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

Side by Side Diff: utils/pub/io.dart

Issue 10937019: First pass at getting git and tar.gz working on Windows. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Don't enable all of the Windows tests yet. Created 8 years, 3 months 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 /** 5 /**
6 * Helper functionality to make working with IO easier. 6 * Helper functionality to make working with IO easier.
7 */ 7 */
8 #library('io'); 8 #library('io');
9 9
10 #import('dart:io'); 10 #import('dart:io');
11 #import('dart:uri'); 11 #import('dart:uri');
12 12
13 #import('utils.dart');
14
15 bool _isGitInstalledCache;
16
13 /** Gets the current working directory. */ 17 /** Gets the current working directory. */
14 String get workingDir => new File('.').fullPathSync(); 18 String get workingDir => new File('.').fullPathSync();
15 19
16 /** 20 /**
17 * Prints the given string to `stderr` on its own line. 21 * Prints the given string to `stderr` on its own line.
18 */ 22 */
19 void printError(value) { 23 void printError(value) {
20 stderr.writeString(value.toString()); 24 stderr.writeString(value.toString());
21 stderr.writeString('\n'); 25 stderr.writeString('\n');
22 } 26 }
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
121 return opened.writeString(contents).chain((ignore) { 125 return opened.writeString(contents).chain((ignore) {
122 return opened.close().transform((ignore) => file); 126 return opened.close().transform((ignore) => file);
123 }); 127 });
124 }); 128 });
125 } 129 }
126 130
127 /** 131 /**
128 * Asynchronously deletes [file], which can be a [String] or a [File]. Returns a 132 * Asynchronously deletes [file], which can be a [String] or a [File]. Returns a
129 * [Future] that completes when the deletion is done. 133 * [Future] that completes when the deletion is done.
130 */ 134 */
131 Future<Directory> deleteFile(file) { 135 Future<File> deleteFile(file) {
132 return new File(_getPath(file)).delete(); 136 return new File(_getPath(file)).delete();
133 } 137 }
134 138
135 /** 139 /**
136 * Creates a directory [dir]. Returns a [Future] that completes when the 140 * Creates a directory [dir]. Returns a [Future] that completes when the
137 * directory is created. 141 * directory is created.
138 */ 142 */
139 Future<Directory> createDir(dir) { 143 Future<Directory> createDir(dir) {
140 dir = _getDirectory(dir); 144 dir = _getDirectory(dir);
141 return dir.create(); 145 return dir.create();
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after
261 265
262 var command = 'ln'; 266 var command = 'ln';
263 var args = ['-s', from, to]; 267 var args = ['-s', from, to];
264 268
265 if (Platform.operatingSystem == 'windows') { 269 if (Platform.operatingSystem == 'windows') {
266 // Call mklink on Windows to create an NTFS junction point. Only works on 270 // Call mklink on Windows to create an NTFS junction point. Only works on
267 // Vista or later. (Junction points are available earlier, but the "mklink" 271 // Vista or later. (Junction points are available earlier, but the "mklink"
268 // command is not.) I'm using a junction point (/j) here instead of a soft 272 // command is not.) I'm using a junction point (/j) here instead of a soft
269 // link (/d) because the latter requires some privilege shenanigans that 273 // link (/d) because the latter requires some privilege shenanigans that
270 // I'm not sure how to specify from the command line. 274 // I'm not sure how to specify from the command line.
271 command = 'cmd'; 275 command = 'mklink';
272 args = ['/c', 'mklink', '/j', to, from]; 276 args = ['/j', to, from];
273 } 277 }
274 278
275 return runProcess(command, args).transform((result) { 279 return runProcess(command, args).transform((result) {
276 // TODO(rnystrom): Check exit code and output? 280 // TODO(rnystrom): Check exit code and output?
277 return new File(to); 281 return new File(to);
278 }); 282 });
279 } 283 }
280 284
281 /** 285 /**
282 * Given [entry] which may be a [String], [File], or [Directory] relative to 286 * Given [entry] which may be a [String], [File], or [Directory] relative to
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
343 * 347 *
344 * If [pipeStdout] and/or [pipeStderr] are set, all output from the subprocess's 348 * If [pipeStdout] and/or [pipeStderr] are set, all output from the subprocess's
345 * output streams are sent to the parent process's output streams. Output from 349 * output streams are sent to the parent process's output streams. Output from
346 * piped streams won't be available in the result object. 350 * piped streams won't be available in the result object.
347 */ 351 */
348 Future<PubProcessResult> runProcess(String executable, List<String> args, 352 Future<PubProcessResult> runProcess(String executable, List<String> args,
349 [workingDir, Map<String, String> environment, bool pipeStdout = false, 353 [workingDir, Map<String, String> environment, bool pipeStdout = false,
350 bool pipeStderr = false]) { 354 bool pipeStderr = false]) {
351 int exitCode; 355 int exitCode;
352 356
357 // TODO(rnystrom): Should dart:io just handle this?
358 // Spawning a process on Windows will not look for the executable in the
359 // system path. So, if executable looks like it needs that (i.e. it doesn't
360 // have any path separators in it), then spawn it through a shell.
361 if ((Platform.operatingSystem == "windows") &&
362 (executable.indexOf('\\') == -1)) {
363 args = flatten(["/c", executable, args]);
364 executable = "cmd";
365 }
366
353 final options = new ProcessOptions(); 367 final options = new ProcessOptions();
354 if (workingDir != null) { 368 if (workingDir != null) {
355 options.workingDirectory = _getDirectory(workingDir).path; 369 options.workingDirectory = _getDirectory(workingDir).path;
356 } 370 }
357 options.environment = environment; 371 options.environment = environment;
358 372
359 final process = Process.start(executable, args, options); 373 final process = Process.start(executable, args, options);
360 374
361 final outStream = new StringInputStream(process.stdout); 375 final outStream = new StringInputStream(process.stdout);
362 final processStdout = <String>[]; 376 final processStdout = <String>[];
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
399 413
400 process.onError = (error) => completer.completeException(error); 414 process.onError = (error) => completer.completeException(error);
401 415
402 return completer.future; 416 return completer.future;
403 } 417 }
404 418
405 /** 419 /**
406 * Tests whether or not the git command-line app is available for use. 420 * Tests whether or not the git command-line app is available for use.
407 */ 421 */
408 Future<bool> get isGitInstalled { 422 Future<bool> get isGitInstalled {
409 // TODO(rnystrom): We could cache this after the first check. We aren't right 423 if (_isGitInstalledCache != null) {
410 // now because Future.immediate() will invoke its callback synchronously. 424 // TODO(rnystrom): The sleep is to pump the message queue. Can use
411 // That does bad things in cases where the caller expects futures to always 425 // Future.immediate() when #3356 is fixed.
412 // be async. In particular, withGit() in the pub tests which calls 426 return sleep(0).transform((_) => _isGitInstalledCache);
413 // expectAsync() will fail horribly if the test isn't actually async. 427 }
414 428
415 var completer = new Completer<bool>(); 429 var completer = new Completer<bool>();
416 430
417 // If "git --version" prints something familiar, git is working. 431 // If "git --version" prints something familiar, git is working.
418 var future = runProcess("git", ["--version"]); 432 var future = runProcess("git", ["--version"]);
419 433
420 future.then((results) { 434 future.then((results) {
421 var regex = new RegExp("^git version"); 435 var regex = new RegExp("^git version");
422 completer.complete(results.stdout.length == 1 && 436 completer.complete(results.stdout.length == 1 &&
423 regex.hasMatch(results.stdout[0])); 437 regex.hasMatch(results.stdout[0]));
424 }); 438 });
425 439
426 future.handleException((err) { 440 future.handleException((err) {
427 // If the process failed, they probably don't have it. 441 // If the process failed, they probably don't have it.
428 completer.complete(false); 442 completer.complete(false);
429 return true; 443 return true;
430 }); 444 });
431 445
432 return completer.future; 446 return completer.future;
433 } 447 }
434 448
435 /** 449 /**
436 * Extracts a `.tar.gz` file from [stream] to [destination], which can be a 450 * Extracts a `.tar.gz` file from [stream] to [destination], which can be a
437 * directory or a path. Returns whether or not the extraction was successful. 451 * directory or a path. Returns whether or not the extraction was successful.
438 */ 452 */
439 Future<bool> extractTarGz(InputStream stream, destination) { 453 Future<bool> extractTarGz(InputStream stream, destination) {
454 if (Platform.operatingSystem == "windows") {
455 return _extractTarGzWindows(stream, destination);
456 }
457
458 destination = _getPath(destination);
nweiz 2012/09/18 23:07:12 Why not do this before passing it in to _extractTa
Bob Nystrom 2012/09/19 15:57:54 Done.
459
440 var process = Process.start("tar", 460 var process = Process.start("tar",
441 ["--extract", "--gunzip", "--directory", _getPath(destination)]); 461 ["--extract", "--gunzip", "--directory", destination]);
442 var completer = new Completer<int>(); 462 var completer = new Completer<int>();
443 463
444 stream.pipe(process.stdin); 464 stream.pipe(process.stdin);
445 process.stdout.pipe(stdout, close: false); 465 process.stdout.pipe(stdout, close: false);
446 process.stderr.pipe(stderr, close: false); 466 process.stderr.pipe(stderr, close: false);
447 467
448 process.onExit = completer.complete; 468 process.onExit = completer.complete;
449 process.onError = completer.completeException; 469 process.onError = completer.completeException;
450 return completer.future.transform((exitCode) => exitCode == 0); 470 return completer.future.transform((exitCode) => exitCode == 0);
451 } 471 }
452 472
473 Future<bool> _extractTarGzWindows(InputStream stream, dest) {
474 dest = _getPath(dest);
475
476 // Find 7zip.
477 var scriptDir = new Path(new Options().script).directoryPath;
478
479 // Note: This line of code gets munged by create_sdk.py to be the correct
480 // relative path to 7zip in the SDK.
481 var pathTo7zip = '../../third_party/7zip/7za.exe';
482
483 var command = scriptDir.append(pathTo7zip).canonicalize().toNativePath();
484
485 // 7zip can't unarchive from gzip -> tar -> destination all in one step so
486 // we spawn it twice and pipe them together.
487 var completer = new Completer<int>();
488 var gzipProcess = Process.start(command, ["e", "-si", "-tgzip", '-so']);
489 var tarProcess = Process.start(command, ["e", "-si", "-ttar", '-o"$dest"']);
490
491 stream.pipe(gzipProcess.stdin);
492 gzipProcess.stdout.pipe(tarProcess.stdin);
493
494 tarProcess.onExit = completer.complete;
495 gzipProcess.onError = completer.completeException;
496 gzipProcess.onError = completer.completeException;
497
498 return completer.future.transform((exitCode) => exitCode == 0);
499 }
500
453 /** 501 /**
454 * Contains the results of invoking a [Process] and waiting for it to complete. 502 * Contains the results of invoking a [Process] and waiting for it to complete.
455 */ 503 */
456 class PubProcessResult { 504 class PubProcessResult {
457 final List<String> stdout; 505 final List<String> stdout;
458 final List<String> stderr; 506 final List<String> stderr;
459 final int exitCode; 507 final int exitCode;
460 508
461 const PubProcessResult(this.stdout, this.stderr, this.exitCode); 509 const PubProcessResult(this.stdout, this.stderr, this.exitCode);
462 510
(...skipping 21 matching lines...) Expand all
484 return new Directory(entry); 532 return new Directory(entry);
485 } 533 }
486 534
487 /** 535 /**
488 * Gets a [Uri] for [uri], which can either already be one, or be a [String]. 536 * Gets a [Uri] for [uri], which can either already be one, or be a [String].
489 */ 537 */
490 Uri _getUri(uri) { 538 Uri _getUri(uri) {
491 if (uri is Uri) return uri; 539 if (uri is Uri) return uri;
492 return new Uri.fromString(uri); 540 return new Uri.fromString(uri);
493 } 541 }
OLDNEW
« no previous file with comments | « tools/create_sdk.py ('k') | utils/tests/pub/pub.status » ('j') | utils/tests/pub/test_pub.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698