| 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'); |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 133 } | 133 } |
| 134 | 134 |
| 135 /** | 135 /** |
| 136 * Asynchronously deletes [file], which can be a [String] or a [File]. Returns a | 136 * Asynchronously deletes [file], which can be a [String] or a [File]. Returns a |
| 137 * [Future] that completes when the deletion is done. | 137 * [Future] that completes when the deletion is done. |
| 138 */ | 138 */ |
| 139 Future<File> deleteFile(file) { | 139 Future<File> deleteFile(file) { |
| 140 return new File(_getPath(file)).delete(); | 140 return new File(_getPath(file)).delete(); |
| 141 } | 141 } |
| 142 | 142 |
| 143 /// Writes [stream] to a new file at [path], which may be a [String] or a |
| 144 /// [File]. Will replace any file already at that path. Completes when the file |
| 145 /// is done being written. |
| 146 Future<File> createFileFromStream(InputStream stream, path) { |
| 147 path = _getPath(path); |
| 148 |
| 149 var completer = new Completer<File>(); |
| 150 var file = new File(path); |
| 151 var outputStream = file.openOutputStream(); |
| 152 stream.pipe(outputStream); |
| 153 |
| 154 outputStream.onClosed = () { |
| 155 completer.complete(file); |
| 156 }; |
| 157 |
| 158 completeError(error) { |
| 159 if (!completer.isComplete) completer.completeException(error); |
| 160 } |
| 161 |
| 162 stream.onError = completeError; |
| 163 outputStream.onError = completeError; |
| 164 |
| 165 return completer.future; |
| 166 } |
| 167 |
| 143 /** | 168 /** |
| 144 * Creates a directory [dir]. Returns a [Future] that completes when the | 169 * Creates a directory [dir]. Returns a [Future] that completes when the |
| 145 * directory is created. | 170 * directory is created. |
| 146 */ | 171 */ |
| 147 Future<Directory> createDir(dir) { | 172 Future<Directory> createDir(dir) { |
| 148 dir = _getDirectory(dir); | 173 dir = _getDirectory(dir); |
| 149 return dir.create(); | 174 return dir.create(); |
| 150 } | 175 } |
| 151 | 176 |
| 152 /** | 177 /** |
| (...skipping 467 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 620 process.onStart = () { | 645 process.onStart = () { |
| 621 stream.pipe(process.stdin); | 646 stream.pipe(process.stdin); |
| 622 process.stdout.pipe(stdout, close: false); | 647 process.stdout.pipe(stdout, close: false); |
| 623 process.stderr.pipe(stderr, close: false); | 648 process.stderr.pipe(stderr, close: false); |
| 624 }; | 649 }; |
| 625 | 650 |
| 626 return completer.future.transform((exitCode) => exitCode == 0); | 651 return completer.future.transform((exitCode) => exitCode == 0); |
| 627 } | 652 } |
| 628 | 653 |
| 629 Future<bool> _extractTarGzWindows(InputStream stream, String destination) { | 654 Future<bool> _extractTarGzWindows(InputStream stream, String destination) { |
| 655 // TODO(rnystrom): In the repo's history, there is an older implementation of |
| 656 // this that does everything in memory by piping streams directly together |
| 657 // instead of writing out temp files. The code is simpler, but unfortunately, |
| 658 // 7zip seems to periodically fail when we invoke it from Dart and tell it to |
| 659 // read from stdin instead of a file. Consider resurrecting that version if |
| 660 // we can figure out why it fails. |
| 661 |
| 630 // Find 7zip. | 662 // Find 7zip. |
| 631 var scriptPath = new File(new Options().script).fullPathSync(); | 663 var scriptPath = new File(new Options().script).fullPathSync(); |
| 632 var scriptDir = new Path.fromNative(scriptPath).directoryPath; | 664 var scriptDir = new Path.fromNative(scriptPath).directoryPath; |
| 633 | 665 |
| 634 // Note: This line of code gets munged by create_sdk.py to be the correct | 666 // Note: This line of code gets munged by create_sdk.py to be the correct |
| 635 // relative path to 7zip in the SDK. | 667 // relative path to 7zip in the SDK. |
| 636 var pathTo7zip = '../../third_party/7zip/7za.exe'; | 668 var pathTo7zip = '../../third_party/7zip/7za.exe'; |
| 637 | |
| 638 var command = scriptDir.append(pathTo7zip).canonicalize().toNativePath(); | 669 var command = scriptDir.append(pathTo7zip).canonicalize().toNativePath(); |
| 639 | 670 |
| 640 // 7zip can't unarchive from gzip -> tar -> destination all in one step so | 671 var tempDir; |
| 641 // we spawn it twice and pipe them together. | |
| 642 var completer = new Completer<bool>(); | |
| 643 var gzipProcess = Process.start(command, ['e', '-si', '-tgzip', '-so']); | |
| 644 | 672 |
| 645 // TODO(rnystrom): Even though we are quoting the destination directory here, | 673 return createTempDir().chain((temp) { |
| 646 // 7zip still seems to barf if there is a space in the path. For now we'll | 674 // Write the archive to a temp file. |
| 647 // just avoid spaces. | 675 tempDir = temp; |
| 648 var tarProcess = Process.start(command, | 676 return createFileFromStream(stream, join(tempDir, 'data.tar.gz')); |
| 649 ['x', '-si', '-ttar', '-o"$destination"']); | 677 }).chain((tarGz) { |
| 678 // 7zip can't unarchive from gzip -> tar -> destination all in one step |
| 679 // first we un-gzip it to a tar file. |
| 680 // Note: Setting the working directory instead of passing in a full file |
| 681 // path because 7zip says "A full path is not allowed here." |
| 682 return runProcess(command, ['e', 'data.tar.gz'], workingDir: tempDir); |
| 683 }).chain((result) { |
| 684 if (result.exitCode != 0) { |
| 685 throw 'Could not un-gzip (exit code ${result.exitCode}). Error:\n' |
| 686 '${Strings.join(result.stderr, "\n")}'; |
| 687 } |
| 650 | 688 |
| 651 // 7zip writes to stderr even when things are going OK, so we'll capture it | 689 // Find the tar file we just created since we don't know its name. |
| 652 // here and only print it if the exit code is bad. | 690 return listDir(tempDir); |
| 653 var errorStream = new ListOutputStream(); | 691 }).chain((files) { |
| 692 var tarFile; |
| 693 for (var file in files) { |
| 694 if (new Path(file).extension == 'tar') { |
| 695 tarFile = file; |
| 696 break; |
| 697 } |
| 698 } |
| 654 | 699 |
| 655 // Wait for the process to be fully started before writing to its | 700 if (tarFile == null) throw 'The gzip file did not contain a tar file.'; |
| 656 // stdin stream. | |
| 657 gzipProcess.onStart = () { | |
| 658 stream.pipe(gzipProcess.stdin); | |
| 659 gzipProcess.stderr.pipe(errorStream, close: false); | |
| 660 }; | |
| 661 | 701 |
| 662 tarProcess.onStart = () { | 702 // Untar the archive into the destination directory. |
| 663 gzipProcess.stdout.pipe(tarProcess.stdin); | 703 return runProcess(command, ['x', '-o"$destination"', tarFile], |
| 664 tarProcess.stderr.pipe(errorStream, close: false); | 704 workingDir: tempDir); |
| 705 }).chain((result) { |
| 706 if (result.exitCode != 0) { |
| 707 throw 'Could not un-tar (exit code ${result.exitCode}). Error:\n' |
| 708 '${Strings.join(result.stderr, "\n")}'; |
| 709 } |
| 665 | 710 |
| 666 // TODO(rnystrom): For some mysterious reason, the extract hangs if you | 711 // Clean up the temp directory. |
| 667 // don't do this. Look into why. | 712 // TODO(rnystrom): Should also delete this if anything fails. |
| 668 tarProcess.stdout.pipe(new ListOutputStream()); | 713 return deleteDir(tempDir); |
| 669 }; | 714 }).transform((_) => true); |
| 670 | |
| 671 tarProcess.onExit = (exitCode) { | |
| 672 if (exitCode != 0) { | |
| 673 printError(new String.fromCharCodes(errorStream.read())); | |
| 674 completer.completeException('Could not extract archive to $destination.'); | |
| 675 } else { | |
| 676 completer.complete(true); | |
| 677 } | |
| 678 }; | |
| 679 | |
| 680 tarProcess.onError = completer.completeException; | |
| 681 gzipProcess.onError = completer.completeException; | |
| 682 | |
| 683 return completer.future; | |
| 684 } | 715 } |
| 685 | 716 |
| 686 /** | 717 /** |
| 687 * Exception thrown when an HTTP operation fails. | 718 * Exception thrown when an HTTP operation fails. |
| 688 */ | 719 */ |
| 689 class PubHttpException implements Exception { | 720 class PubHttpException implements Exception { |
| 690 final int statusCode; | 721 final int statusCode; |
| 691 final String reason; | 722 final String reason; |
| 692 | 723 |
| 693 const PubHttpException(this.statusCode, this.reason); | 724 const PubHttpException(this.statusCode, this.reason); |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 736 return new Directory(entry); | 767 return new Directory(entry); |
| 737 } | 768 } |
| 738 | 769 |
| 739 /** | 770 /** |
| 740 * Gets a [Uri] for [uri], which can either already be one, or be a [String]. | 771 * Gets a [Uri] for [uri], which can either already be one, or be a [String]. |
| 741 */ | 772 */ |
| 742 Uri _getUri(uri) { | 773 Uri _getUri(uri) { |
| 743 if (uri is Uri) return uri; | 774 if (uri is Uri) return uri; |
| 744 return new Uri.fromString(uri); | 775 return new Uri.fromString(uri); |
| 745 } | 776 } |
| OLD | NEW |