Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(198)

Unified Diff: utils/pub/io.dart

Issue 11308212: Add an initial "pub lish" command. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: utils/pub/io.dart
diff --git a/utils/pub/io.dart b/utils/pub/io.dart
index ed5dee68be2a1c265dfd56364385e676b3c56f50..e7db79c9505e672d4180f8e8863de0ff67e659c7 100644
--- a/utils/pub/io.dart
+++ b/utils/pub/io.dart
@@ -228,12 +228,11 @@ Future<Directory> deleteDir(dir) {
/**
* Asynchronously lists the contents of [dir], which can be a [String] directory
* path or a [Directory]. If [recursive] is `true`, lists subdirectory contents
- * (defaults to `false`). If [includeSpecialFiles] is `true`, includes
- * hidden `.DS_Store` files (defaults to `false`, other hidden files may be
- * omitted later).
+ * (defaults to `false`). If [includeHiddenFiles] is `true`, includes files
+ * beginning with `.` (defaults to `false`).
*/
Future<List<String>> listDir(dir,
- [bool recursive = false, bool includeSpecialFiles = false]) {
+ {bool recursive: false, bool includeHiddenFiles: false}) {
final completer = new Completer<List<String>>();
final contents = <String>[];
@@ -249,9 +248,7 @@ Future<List<String>> listDir(dir,
lister.onError = (error) => completer.completeException(error);
lister.onDir = (file) => contents.add(file);
lister.onFile = (file) {
- if (!includeSpecialFiles) {
- if (basename(file) == '.DS_Store') return;
- }
+ if (!includeHiddenFiles && basename(file).startsWith('.')) return;
contents.add(file);
};
@@ -372,6 +369,41 @@ String relativeToPub(String path) {
return scriptDir.append(path).canonicalize().toNativePath();
}
+/// A StringInputStream reading from stdin.
+StringInputStream _stringStdin = new StringInputStream(stdin);
Bob Nystrom 2012/11/26 23:39:52 "StringInputStream" -> "final".
nweiz 2012/11/27 20:15:54 Done.
+
+/// Returns a single line read from a [StringInputStream]. By default, reads
+/// from stdin.
+///
+/// A [StringInputStream] passed to this should have no callbacks registered.
+Future<String> readLine([StringInputStream stream]) {
+ if (stream == null) stream = _stringStdin;
+ if (stream.closed) return new Future.immediate('');
+ void removeCallbacks() {
+ stream.onClosed = null;
+ stream.onLine = null;
+ stream.onError = null;
+ }
+
+ var completer = new Completer();
+ stream.onClosed = () {
+ removeCallbacks();
+ completer.complete('');
+ };
+
+ stream.onLine = () {
+ removeCallbacks();
+ completer.complete(stream.readLine());
+ };
+
+ stream.onError = (e) {
+ removeCallbacks();
+ completer.completeException(e);
+ };
+
+ return completer.future;
+}
+
// TODO(nweiz): make this configurable
/**
* The amount of time in milliseconds to allow HTTP requests before assuming
@@ -432,7 +464,7 @@ Future<InputStream> httpGet(uri) {
Future<String> httpGetString(uri) {
var future = httpGet(uri).chain((stream) => consumeInputStream(stream))
.transform((bytes) => new String.fromCharCodes(bytes));
- return timeout(future, HTTP_TIMEOUT, 'Timed out while fetching URL "$uri".');
+ return timeout(future, HTTP_TIMEOUT, 'fetching URL "$uri"');
}
/**
@@ -455,6 +487,8 @@ void pipeInputToInput(InputStream source, ListInputStream sink,
* Buffers all input from an InputStream and returns it as a future.
*/
Future<List<int>> consumeInputStream(InputStream stream) {
+ if (stream.closed) return new Future.immediate(<int>[]);
+
var completer = new Completer<List<int>>();
var buffer = <int>[];
stream.onClosed = () => completer.complete(buffer);
@@ -463,22 +497,56 @@ Future<List<int>> consumeInputStream(InputStream stream) {
return completer.future;
}
+/// Buffers all input from a StringInputStream and returns it as a future.
+Future<String> consumeStringInputStream(StringInputStream stream) {
+ if (stream.closed) return new Future.immediate('');
+
+ var completer = new Completer<String>();
+ var buffer = new StringBuffer();
+ stream.onClosed = () => completer.complete(buffer.toString());
+ stream.onData = () => buffer.add(stream.read());
+ stream.onError = (e) => completer.completeException(e);
+ return completer.future;
+}
+
/// Spawns and runs the process located at [executable], passing in [args].
-/// Returns a [Future] that will complete the results of the process after it
-/// has ended.
+/// Returns a [Future] that will complete with the results of the process after
+/// it has ended.
///
/// The spawned process will inherit its parent's environment variables. If
/// [environment] is provided, that will be used to augment (not replace) the
/// the inherited variables.
-///
-/// If [pipeStdout] and/or [pipeStderr] are set, all output from the
-/// subprocess's output streams are sent to the parent process's output streams.
-/// Output from piped streams won't be available in the result object.
Future<PubProcessResult> runProcess(String executable, List<String> args,
- {workingDir, Map<String, String> environment, bool pipeStdout: false,
- bool pipeStderr: false}) {
- int exitCode;
+ {workingDir, Map<String, String> environment}) {
+ return _doProcess(Process.run, executable, args, workingDir, environment)
+ .transform((result) {
+ // TODO(rnystrom): Remove this and change to returning one string.
+ List<String> toLines(String output) {
+ var lines = output.split(NEWLINE_PATTERN);
+ if (!lines.isEmpty && lines.last == "") lines.removeLast();
+ return lines;
+ }
+ return new PubProcessResult(toLines(result.stdout),
+ toLines(result.stderr),
+ result.exitCode);
+ });
+}
+/// Spawns the process located at [executable], passing in [args]. Returns a
+/// [Future] that will complete with the [Process] once it's been started.
+///
+/// The spawned process will inherit its parent's environment variables. If
+/// [environment] is provided, that will be used to augment (not replace) the
+/// the inherited variables.
+Future<Process> startProcess(String executable, List<String> args,
+ {workingDir, Map<String, String> environment}) =>
+ _doProcess(Process.start, executable, args, workingDir, environment);
+
+/// Calls [fn] with appropriately modified arguments. [fn] should have the same
+/// signature as [Process.start], except that the returned [Future] may have a
+/// type other than [Process].
+Future _doProcess(Function fn, String executable, List<String> args, workingDir,
+ Map<String, String> environment) {
// TODO(rnystrom): Should dart:io just handle this?
// Spawning a process on Windows will not look for the executable in the
// system path. So, if executable looks like it needs that (i.e. it doesn't
@@ -499,34 +567,25 @@ Future<PubProcessResult> runProcess(String executable, List<String> args,
environment.forEach((key, value) => options.environment[key] = value);
}
- var future = Process.run(executable, args, options);
- return future.transform((result) {
- // TODO(rnystrom): Remove this and change to returning one string.
- List<String> toLines(String output) {
- var lines = output.split(NEWLINE_PATTERN);
- if (!lines.isEmpty && lines.last == "") lines.removeLast();
- return lines;
- }
- return new PubProcessResult(toLines(result.stdout),
- toLines(result.stderr),
- result.exitCode);
- });
+ return fn(executable, args, options);
}
/**
* Wraps [input] to provide a timeout. If [input] completes before
* [milliseconds] have passed, then the return value completes in the same way.
* However, if [milliseconds] pass before [input] has completed, it completes
- * with a [TimeoutException] with [message].
+ * with a [TimeoutException] with [description] (which should be a fragment
+ * describing the action that timed out).
*
* Note that timing out will not cancel the asynchronous operation behind
* [input].
*/
-Future timeout(Future input, int milliseconds, String message) {
+Future timeout(Future input, int milliseconds, String description) {
var completer = new Completer();
var timer = new Timer(milliseconds, (_) {
if (completer.future.isComplete) return;
- completer.completeException(new TimeoutException(message));
+ completer.completeException(new TimeoutException(
+ 'Timed out while $description.'));
});
input.handleException((e) {
if (completer.future.isComplete) return false;
@@ -704,6 +763,48 @@ Future<bool> _extractTarGzWindows(InputStream stream, String destination) {
}).transform((_) => true);
}
+/// Create a .tar.gz archive from a list of entries. Each entry can be a
+/// [String], [Directory], or [File] object. Returns an [InputStream] that will
+/// emit the contents of the archive.
+InputStream createTarGz(List contents) {
+ // TODO(nweiz): Propagate errors to the returned stream (including non-zero
+ // exit codes). See issue 3657.
+ var stream = new ListInputStream();
+
+ if (Platform.operatingSystem != "windows") {
+ var args = ["--create", "--gzip"];
+ args.addAll(contents.map(_getPath));
Bob Nystrom 2012/11/26 23:39:52 Do we need to worry about blowing the arg buffer h
nweiz 2012/11/27 20:15:54 I think the buffer is really big on modern systems
+ startProcess("tar", args).then((process) {
+ pipeInputToInput(process.stdout, stream);
+ process.stderr.pipe(stderr, close: false);
+ });
+ return stream;
+ }
+
+ withTempDir((tempDir) {
Bob Nystrom 2012/11/26 23:39:52 Yay Windows support!
nweiz 2012/11/27 20:15:54 I just more or less copied your code from test_pub
+ // Create the tar file.
+ var tarFile = join(tempDir, "intermediate.tar");
+ var args = ["a", tarFile];
+ args.addAll(contents.map((entry) => '-i!"$entry"'));
+
+ // Note: This line of code gets munged by create_sdk.py to be the correct
+ // relative path to 7zip in the SDK.
+ var pathTo7zip = '../../third_party/7zip/7za.exe';
+ var command = relativeToPub(pathTo7zip);
+
+ return runProcess(command, args).chain((_) {
+ // GZIP it. 7zip doesn't support doing both as a single operation. Send
+ // the output to stdout.
+ args = ["a", "not used", "-so", tarFile];
+ return startProcess(command, args);
+ }).transform((process) {
+ pipeInputToInput(process.stdout, stream);
+ process.stderr.pipe(stderr, close: false);
+ });
+ });
+ return stream;
+}
+
/**
* Exception thrown when an HTTP operation fails.
*/

Powered by Google App Engine
This is Rietveld 408576698