| Index: sdk/lib/_internal/pub_generated/lib/src/io.dart
|
| diff --git a/sdk/lib/_internal/pub_generated/lib/src/io.dart b/sdk/lib/_internal/pub_generated/lib/src/io.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..dd0d560305196bc9220cec811d27dff73068f5f2
|
| --- /dev/null
|
| +++ b/sdk/lib/_internal/pub_generated/lib/src/io.dart
|
| @@ -0,0 +1,550 @@
|
| +library pub.io;
|
| +import 'dart:async';
|
| +import 'dart:collection';
|
| +import 'dart:convert';
|
| +import 'dart:io';
|
| +import 'package:path/path.dart' as path;
|
| +import 'package:pool/pool.dart';
|
| +import 'package:http/http.dart' show ByteStream;
|
| +import 'package:http_multi_server/http_multi_server.dart';
|
| +import 'package:stack_trace/stack_trace.dart';
|
| +import 'exit_codes.dart' as exit_codes;
|
| +import 'exceptions.dart';
|
| +import 'error_group.dart';
|
| +import 'log.dart' as log;
|
| +import 'sdk.dart' as sdk;
|
| +import 'utils.dart';
|
| +export 'package:http/http.dart' show ByteStream;
|
| +final _descriptorPool = new Pool(32);
|
| +bool entryExists(String path) =>
|
| + dirExists(path) || fileExists(path) || linkExists(path);
|
| +bool linkExists(String link) => new Link(link).existsSync();
|
| +bool fileExists(String file) => new File(file).existsSync();
|
| +String canonicalize(String pathString) {
|
| + var seen = new Set<String>();
|
| + var components =
|
| + new Queue<String>.from(path.split(path.normalize(path.absolute(pathString))));
|
| + var newPath = components.removeFirst();
|
| + while (!components.isEmpty) {
|
| + seen.add(path.join(newPath, path.joinAll(components)));
|
| + var resolvedPath =
|
| + resolveLink(path.join(newPath, components.removeFirst()));
|
| + var relative = path.relative(resolvedPath, from: newPath);
|
| + if (relative == '.') continue;
|
| + var relativeComponents = new Queue<String>.from(path.split(relative));
|
| + if (path.isAbsolute(relative)) {
|
| + if (seen.contains(relative)) {
|
| + newPath = relative;
|
| + } else {
|
| + newPath = relativeComponents.removeFirst();
|
| + relativeComponents.addAll(components);
|
| + components = relativeComponents;
|
| + }
|
| + continue;
|
| + }
|
| + while (relativeComponents.first == '..') {
|
| + newPath = path.dirname(newPath);
|
| + relativeComponents.removeFirst();
|
| + }
|
| + if (relativeComponents.length == 1) {
|
| + newPath = path.join(newPath, relativeComponents.single);
|
| + continue;
|
| + }
|
| + var newSubPath = path.join(newPath, path.joinAll(relativeComponents));
|
| + if (seen.contains(newSubPath)) {
|
| + newPath = newSubPath;
|
| + continue;
|
| + }
|
| + relativeComponents.addAll(components);
|
| + components = relativeComponents;
|
| + }
|
| + return newPath;
|
| +}
|
| +String resolveLink(String link) {
|
| + var seen = new Set<String>();
|
| + while (linkExists(link) && !seen.contains(link)) {
|
| + seen.add(link);
|
| + link =
|
| + path.normalize(path.join(path.dirname(link), new Link(link).targetSync()));
|
| + }
|
| + return link;
|
| +}
|
| +String readTextFile(String file) =>
|
| + new File(file).readAsStringSync(encoding: UTF8);
|
| +List<int> readBinaryFile(String file) {
|
| + log.io("Reading binary file $file.");
|
| + var contents = new File(file).readAsBytesSync();
|
| + log.io("Read ${contents.length} bytes from $file.");
|
| + return contents;
|
| +}
|
| +String writeTextFile(String file, String contents, {bool dontLogContents:
|
| + false}) {
|
| + log.io("Writing ${contents.length} characters to text file $file.");
|
| + if (!dontLogContents && contents.length < 1024 * 1024) {
|
| + log.fine("Contents:\n$contents");
|
| + }
|
| + new File(file).writeAsStringSync(contents);
|
| + return file;
|
| +}
|
| +String writeBinaryFile(String file, List<int> contents) {
|
| + log.io("Writing ${contents.length} bytes to binary file $file.");
|
| + new File(file).openSync(mode: FileMode.WRITE)
|
| + ..writeFromSync(contents)
|
| + ..closeSync();
|
| + log.fine("Wrote text file $file.");
|
| + return file;
|
| +}
|
| +Future<String> createFileFromStream(Stream<List<int>> stream, String file) {
|
| + log.io("Creating $file from stream.");
|
| + return _descriptorPool.withResource(() {
|
| + return Chain.track(stream.pipe(new File(file).openWrite())).then((_) {
|
| + log.fine("Created $file from stream.");
|
| + return file;
|
| + });
|
| + });
|
| +}
|
| +void copyFiles(Iterable<String> files, String baseDir, String destination) {
|
| + for (var file in files) {
|
| + var newPath = path.join(destination, path.relative(file, from: baseDir));
|
| + ensureDir(path.dirname(newPath));
|
| + copyFile(file, newPath);
|
| + }
|
| +}
|
| +void copyFile(String source, String destination) {
|
| + writeBinaryFile(destination, readBinaryFile(source));
|
| +}
|
| +String createDir(String dir) {
|
| + new Directory(dir).createSync();
|
| + return dir;
|
| +}
|
| +String ensureDir(String dir) {
|
| + new Directory(dir).createSync(recursive: true);
|
| + return dir;
|
| +}
|
| +String createTempDir(String base, String prefix) {
|
| + var tempDir = new Directory(base).createTempSync(prefix);
|
| + log.io("Created temp directory ${tempDir.path}");
|
| + return tempDir.path;
|
| +}
|
| +String createSystemTempDir() {
|
| + var tempDir = Directory.systemTemp.createTempSync('pub_');
|
| + log.io("Created temp directory ${tempDir.path}");
|
| + return tempDir.path;
|
| +}
|
| +List<String> listDir(String dir, {bool recursive: false, bool includeHidden:
|
| + false, bool includeDirs: true, Iterable<String> whitelist}) {
|
| + if (whitelist == null) whitelist = [];
|
| + var whitelistFilter = createFileFilter(whitelist);
|
| + return new Directory(
|
| + dir).listSync(recursive: recursive, followLinks: true).where((entity) {
|
| + if (!includeDirs && entity is Directory) return false;
|
| + if (entity is Link) return false;
|
| + if (includeHidden) return true;
|
| + assert(entity.path.startsWith(dir));
|
| + var pathInDir = entity.path.substring(dir.length);
|
| + var whitelistedBasename =
|
| + whitelistFilter.firstWhere(pathInDir.contains, orElse: () => null);
|
| + if (whitelistedBasename != null) {
|
| + pathInDir =
|
| + pathInDir.substring(0, pathInDir.length - whitelistedBasename.length);
|
| + }
|
| + if (pathInDir.contains("/.")) return false;
|
| + if (Platform.operatingSystem != "windows") return true;
|
| + return !pathInDir.contains("\\.");
|
| + }).map((entity) => entity.path).toList();
|
| +}
|
| +bool dirExists(String dir) => new Directory(dir).existsSync();
|
| +void _attempt(String description, void operation()) {
|
| + if (Platform.operatingSystem != 'windows') {
|
| + operation();
|
| + return;
|
| + }
|
| + getErrorReason(error) {
|
| + if (error.osError.errorCode == 5) {
|
| + return "access was denied";
|
| + }
|
| + if (error.osError.errorCode == 32) {
|
| + return "it was in use by another process";
|
| + }
|
| + return null;
|
| + }
|
| + for (var i = 0; i < 2; i++) {
|
| + try {
|
| + operation();
|
| + return;
|
| + } on FileSystemException catch (error) {
|
| + var reason = getErrorReason(error);
|
| + if (reason == null) rethrow;
|
| + log.io("Failed to $description because $reason. " "Retrying in 50ms.");
|
| + sleep(new Duration(milliseconds: 50));
|
| + }
|
| + }
|
| + try {
|
| + operation();
|
| + } on FileSystemException catch (error) {
|
| + var reason = getErrorReason(error);
|
| + if (reason == null) rethrow;
|
| + fail(
|
| + "Failed to $description because $reason.\n"
|
| + "This may be caused by a virus scanner or having a file\n"
|
| + "in the directory open in another application.");
|
| + }
|
| +}
|
| +void deleteEntry(String path) {
|
| + _attempt("delete entry", () {
|
| + if (linkExists(path)) {
|
| + log.io("Deleting link $path.");
|
| + new Link(path).deleteSync();
|
| + } else if (dirExists(path)) {
|
| + log.io("Deleting directory $path.");
|
| + new Directory(path).deleteSync(recursive: true);
|
| + } else if (fileExists(path)) {
|
| + log.io("Deleting file $path.");
|
| + new File(path).deleteSync();
|
| + }
|
| + });
|
| +}
|
| +void cleanDir(String dir) {
|
| + if (entryExists(dir)) deleteEntry(dir);
|
| + ensureDir(dir);
|
| +}
|
| +void renameDir(String from, String to) {
|
| + _attempt("rename directory", () {
|
| + log.io("Renaming directory $from to $to.");
|
| + try {
|
| + new Directory(from).renameSync(to);
|
| + } on IOException catch (error) {
|
| + if (entryExists(to)) deleteEntry(to);
|
| + rethrow;
|
| + }
|
| + });
|
| +}
|
| +void createSymlink(String target, String symlink, {bool relative: false}) {
|
| + if (relative) {
|
| + if (Platform.operatingSystem == 'windows') {
|
| + target = path.normalize(path.absolute(target));
|
| + } else {
|
| + var symlinkDir = canonicalize(path.dirname(symlink));
|
| + target = path.normalize(path.relative(target, from: symlinkDir));
|
| + }
|
| + }
|
| + log.fine("Creating $symlink pointing to $target");
|
| + new Link(symlink).createSync(target);
|
| +}
|
| +void createPackageSymlink(String name, String target, String symlink,
|
| + {bool isSelfLink: false, bool relative: false}) {
|
| + target = path.join(target, 'lib');
|
| + if (!dirExists(target)) return;
|
| + log.fine("Creating ${isSelfLink ? "self" : ""}link for package '$name'.");
|
| + createSymlink(target, symlink, relative: relative);
|
| +}
|
| +final bool runningFromSdk = Platform.script.path.endsWith('.snapshot');
|
| +String assetPath(String target) {
|
| + if (runningFromSdk) {
|
| + return path.join(
|
| + sdk.rootDirectory,
|
| + 'lib',
|
| + '_internal',
|
| + 'pub',
|
| + 'asset',
|
| + target);
|
| + } else {
|
| + return path.join(
|
| + path.dirname(libraryPath('pub.io')),
|
| + '..',
|
| + '..',
|
| + 'asset',
|
| + target);
|
| + }
|
| +}
|
| +String get repoRoot {
|
| + if (runningFromSdk) {
|
| + throw new StateError("Can't get the repo root from the SDK.");
|
| + }
|
| + var libDir = path.dirname(libraryPath('pub.io'));
|
| + if (libDir.contains('pub_async')) {
|
| + return path.normalize(path.join(libDir, '..', '..', '..', '..', '..'));
|
| + }
|
| + return path.normalize(path.join(libDir, '..', '..', '..', '..', '..', '..'));
|
| +}
|
| +final Stream<String> stdinLines =
|
| + streamToLines(new ByteStream(Chain.track(stdin)).toStringStream());
|
| +Future<bool> confirm(String message) {
|
| + log.fine('Showing confirm message: $message');
|
| + if (runningAsTest) {
|
| + log.message("$message (y/n)?");
|
| + } else {
|
| + stdout.write(log.format("$message (y/n)? "));
|
| + }
|
| + return streamFirst(
|
| + stdinLines).then((line) => new RegExp(r"^[yY]").hasMatch(line));
|
| +}
|
| +Future drainStream(Stream stream) {
|
| + return stream.fold(null, (x, y) {});
|
| +}
|
| +Future flushThenExit(int status) {
|
| + return Future.wait(
|
| + [
|
| + Chain.track(stdout.close()),
|
| + Chain.track(stderr.close())]).then((_) => exit(status));
|
| +}
|
| +Pair<EventSink, Future> consumerToSink(StreamConsumer consumer) {
|
| + var controller = new StreamController(sync: true);
|
| + var done = controller.stream.pipe(consumer);
|
| + return new Pair<EventSink, Future>(controller.sink, done);
|
| +}
|
| +Future store(Stream stream, EventSink sink, {bool cancelOnError: true,
|
| + bool closeSink: true}) {
|
| + var completer = new Completer();
|
| + stream.listen(sink.add, onError: (e, stackTrace) {
|
| + sink.addError(e, stackTrace);
|
| + if (cancelOnError) {
|
| + completer.complete();
|
| + if (closeSink) sink.close();
|
| + }
|
| + }, onDone: () {
|
| + if (closeSink) sink.close();
|
| + completer.complete();
|
| + }, cancelOnError: cancelOnError);
|
| + return completer.future;
|
| +}
|
| +Future<PubProcessResult> runProcess(String executable, List<String> args,
|
| + {workingDir, Map<String, String> environment}) {
|
| + return _descriptorPool.withResource(() {
|
| + return _doProcess(
|
| + Process.run,
|
| + executable,
|
| + args,
|
| + workingDir,
|
| + environment).then((result) {
|
| + var pubResult =
|
| + new PubProcessResult(result.stdout, result.stderr, result.exitCode);
|
| + log.processResult(executable, pubResult);
|
| + return pubResult;
|
| + });
|
| + });
|
| +}
|
| +Future<PubProcess> startProcess(String executable, List<String> args,
|
| + {workingDir, Map<String, String> environment}) {
|
| + return _descriptorPool.request().then((resource) {
|
| + return _doProcess(
|
| + Process.start,
|
| + executable,
|
| + args,
|
| + workingDir,
|
| + environment).then((ioProcess) {
|
| + var process = new PubProcess(ioProcess);
|
| + process.exitCode.whenComplete(resource.release);
|
| + return process;
|
| + });
|
| + });
|
| +}
|
| +PubProcessResult runProcessSync(String executable, List<String> args,
|
| + {String workingDir, Map<String, String> environment}) {
|
| + var result =
|
| + _doProcess(Process.runSync, executable, args, workingDir, environment);
|
| + var pubResult =
|
| + new PubProcessResult(result.stdout, result.stderr, result.exitCode);
|
| + log.processResult(executable, pubResult);
|
| + return pubResult;
|
| +}
|
| +class PubProcess {
|
| + final Process _process;
|
| + EventSink<List<int>> _stdin;
|
| + Future _stdinClosed;
|
| + ByteStream _stdout;
|
| + ByteStream _stderr;
|
| + Future<int> _exitCode;
|
| + EventSink<List<int>> get stdin => _stdin;
|
| + Future get stdinClosed => _stdinClosed;
|
| + ByteStream get stdout => _stdout;
|
| + ByteStream get stderr => _stderr;
|
| + Future<int> get exitCode => _exitCode;
|
| + PubProcess(Process process) : _process = process {
|
| + var errorGroup = new ErrorGroup();
|
| + var pair = consumerToSink(process.stdin);
|
| + _stdin = pair.first;
|
| + _stdinClosed = errorGroup.registerFuture(Chain.track(pair.last));
|
| + _stdout =
|
| + new ByteStream(errorGroup.registerStream(Chain.track(process.stdout)));
|
| + _stderr =
|
| + new ByteStream(errorGroup.registerStream(Chain.track(process.stderr)));
|
| + var exitCodeCompleter = new Completer();
|
| + _exitCode =
|
| + errorGroup.registerFuture(Chain.track(exitCodeCompleter.future));
|
| + _process.exitCode.then((code) => exitCodeCompleter.complete(code));
|
| + }
|
| + bool kill([ProcessSignal signal = ProcessSignal.SIGTERM]) =>
|
| + _process.kill(signal);
|
| +}
|
| +_doProcess(Function fn, String executable, List<String> args, String workingDir,
|
| + Map<String, String> environment) {
|
| + if ((Platform.operatingSystem == "windows") &&
|
| + (executable.indexOf('\\') == -1)) {
|
| + args = flatten(["/c", executable, args]);
|
| + executable = "cmd";
|
| + }
|
| + log.process(executable, args, workingDir == null ? '.' : workingDir);
|
| + return fn(
|
| + executable,
|
| + args,
|
| + workingDirectory: workingDir,
|
| + environment: environment);
|
| +}
|
| +Future timeout(Future input, int milliseconds, Uri url, String description) {
|
| + var completer = new Completer();
|
| + var duration = new Duration(milliseconds: milliseconds);
|
| + var timer = new Timer(duration, () {
|
| + var message =
|
| + 'Timed out after ${niceDuration(duration)} while ' '$description.';
|
| + if (url.host == "pub.dartlang.org" ||
|
| + url.host == "storage.googleapis.com") {
|
| + message += "\nThis is likely a transient error. Please try again later.";
|
| + }
|
| + completer.completeError(new TimeoutException(message), new Chain.current());
|
| + });
|
| + input.then((value) {
|
| + if (completer.isCompleted) return;
|
| + timer.cancel();
|
| + completer.complete(value);
|
| + }).catchError((e, stackTrace) {
|
| + if (completer.isCompleted) return;
|
| + timer.cancel();
|
| + completer.completeError(e, stackTrace);
|
| + });
|
| + return completer.future;
|
| +}
|
| +Future withTempDir(Future fn(String path)) {
|
| + return syncFuture(() {
|
| + var tempDir = createSystemTempDir();
|
| + return syncFuture(
|
| + () => fn(tempDir)).whenComplete(() => deleteEntry(tempDir));
|
| + });
|
| +}
|
| +Future<HttpServer> bindServer(String host, int port) {
|
| + if (host == 'localhost') return HttpMultiServer.loopback(port);
|
| + return HttpServer.bind(host, port);
|
| +}
|
| +Future<bool> extractTarGz(Stream<List<int>> stream, String destination) {
|
| + log.fine("Extracting .tar.gz stream to $destination.");
|
| + if (Platform.operatingSystem == "windows") {
|
| + return _extractTarGzWindows(stream, destination);
|
| + }
|
| + var args = ["--extract", "--gunzip", "--directory", destination];
|
| + if (_noUnknownKeyword) {
|
| + args.insert(0, "--warning=no-unknown-keyword");
|
| + }
|
| + return startProcess("tar", args).then((process) {
|
| + store(process.stdout.handleError((_) {}), stdout, closeSink: false);
|
| + store(process.stderr.handleError((_) {}), stderr, closeSink: false);
|
| + return Future.wait([store(stream, process.stdin), process.exitCode]);
|
| + }).then((results) {
|
| + var exitCode = results[1];
|
| + if (exitCode != exit_codes.SUCCESS) {
|
| + throw new Exception(
|
| + "Failed to extract .tar.gz stream to $destination " "(exit code $exitCode).");
|
| + }
|
| + log.fine("Extracted .tar.gz stream to $destination. Exit code $exitCode.");
|
| + });
|
| +}
|
| +final bool _noUnknownKeyword = _computeNoUnknownKeyword();
|
| +bool _computeNoUnknownKeyword() {
|
| + if (!Platform.isLinux) return false;
|
| + var result = Process.runSync("tar", ["--version"]);
|
| + if (result.exitCode != 0) {
|
| + throw new ApplicationException(
|
| + "Failed to run tar (exit code ${result.exitCode}):\n${result.stderr}");
|
| + }
|
| + var match =
|
| + new RegExp(r"^tar \(GNU tar\) (\d+).(\d+)\n").firstMatch(result.stdout);
|
| + if (match == null) return false;
|
| + var major = int.parse(match[1]);
|
| + var minor = int.parse(match[2]);
|
| + return major >= 2 || (major == 1 && minor >= 23);
|
| +}
|
| +String get pathTo7zip {
|
| + if (runningFromSdk) return assetPath(path.join('7zip', '7za.exe'));
|
| + return path.join(repoRoot, 'third_party', '7zip', '7za.exe');
|
| +}
|
| +Future<bool> _extractTarGzWindows(Stream<List<int>> stream, String destination)
|
| + {
|
| + return withTempDir((tempDir) {
|
| + var dataFile = path.join(tempDir, 'data.tar.gz');
|
| + return createFileFromStream(stream, dataFile).then((_) {
|
| + return runProcess(pathTo7zip, ['e', 'data.tar.gz'], workingDir: tempDir);
|
| + }).then((result) {
|
| + if (result.exitCode != exit_codes.SUCCESS) {
|
| + throw new Exception(
|
| + 'Could not un-gzip (exit code ${result.exitCode}). ' 'Error:\n'
|
| + '${result.stdout.join("\n")}\n' '${result.stderr.join("\n")}');
|
| + }
|
| + var tarFile = listDir(
|
| + tempDir).firstWhere((file) => path.extension(file) == '.tar', orElse: () {
|
| + throw new FormatException('The gzip file did not contain a tar file.');
|
| + });
|
| + return runProcess(pathTo7zip, ['x', tarFile], workingDir: destination);
|
| + }).then((result) {
|
| + if (result.exitCode != exit_codes.SUCCESS) {
|
| + throw new Exception(
|
| + 'Could not un-tar (exit code ${result.exitCode}). ' 'Error:\n'
|
| + '${result.stdout.join("\n")}\n' '${result.stderr.join("\n")}');
|
| + }
|
| + return true;
|
| + });
|
| + });
|
| +}
|
| +ByteStream createTarGz(List contents, {baseDir}) {
|
| + return new ByteStream(futureStream(syncFuture(() {
|
| + var buffer = new StringBuffer();
|
| + buffer.write('Creating .tag.gz stream containing:\n');
|
| + contents.forEach((file) => buffer.write('$file\n'));
|
| + log.fine(buffer.toString());
|
| + if (baseDir == null) baseDir = path.current;
|
| + baseDir = path.absolute(baseDir);
|
| + contents = contents.map((entry) {
|
| + entry = path.absolute(entry);
|
| + if (!path.isWithin(baseDir, entry)) {
|
| + throw new ArgumentError('Entry $entry is not inside $baseDir.');
|
| + }
|
| + return path.relative(entry, from: baseDir);
|
| + }).toList();
|
| + if (Platform.operatingSystem != "windows") {
|
| + var args = ["--create", "--gzip", "--directory", baseDir];
|
| + args.addAll(contents);
|
| + return startProcess("tar", args).then((process) => process.stdout);
|
| + }
|
| + var tempDir = createSystemTempDir();
|
| + return syncFuture(() {
|
| + var tarFile = path.join(tempDir, "intermediate.tar");
|
| + var args = ["a", "-w$baseDir", tarFile];
|
| + args.addAll(contents.map((entry) => '-i!$entry'));
|
| + return runProcess(pathTo7zip, args, workingDir: baseDir).then((_) {
|
| + args = ["a", "unused", "-tgzip", "-so", tarFile];
|
| + return startProcess(pathTo7zip, args);
|
| + }).then((process) => process.stdout);
|
| + }).then((stream) {
|
| + return stream.transform(onDoneTransformer(() => deleteEntry(tempDir)));
|
| + }).catchError((e) {
|
| + deleteEntry(tempDir);
|
| + throw e;
|
| + });
|
| + })));
|
| +}
|
| +class PubProcessResult {
|
| + final List<String> stdout;
|
| + final List<String> stderr;
|
| + final int exitCode;
|
| + PubProcessResult(String stdout, String stderr, this.exitCode)
|
| + : this.stdout = _toLines(stdout),
|
| + this.stderr = _toLines(stderr);
|
| + static List<String> _toLines(String output) {
|
| + var lines = splitLines(output);
|
| + if (!lines.isEmpty && lines.last == "") lines.removeLast();
|
| + return lines;
|
| + }
|
| + bool get success => exitCode == exit_codes.SUCCESS;
|
| +}
|
| +Uri _getUri(uri) {
|
| + if (uri is Uri) return uri;
|
| + return Uri.parse(uri);
|
| +}
|
|
|