Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 } |
| OLD | NEW |