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

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

Issue 11031015: Extract archives by writing .tar.gz to temp file on Windows. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Respond to review. Created 8 years, 2 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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');
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698