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 /// Helper functionality to make working with IO easier. | 5 /// Helper functionality to make working with IO easier. |
6 library io; | 6 library io; |
7 | 7 |
8 import 'dart:async'; | 8 import 'dart:async'; |
9 import 'dart:io'; | 9 import 'dart:io'; |
10 import 'dart:isolate'; | 10 import 'dart:isolate'; |
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
150 } | 150 } |
151 } | 151 } |
152 | 152 |
153 return dir; | 153 return dir; |
154 } | 154 } |
155 | 155 |
156 /// Creates a temp directory whose name will be based on [dir] with a unique | 156 /// Creates a temp directory whose name will be based on [dir] with a unique |
157 /// suffix appended to it. If [dir] is not provided, a temp directory will be | 157 /// suffix appended to it. If [dir] is not provided, a temp directory will be |
158 /// created in a platform-dependent temporary location. Returns a [Future] that | 158 /// created in a platform-dependent temporary location. Returns a [Future] that |
159 /// completes when the directory is created. | 159 /// completes when the directory is created. |
160 Future<Directory> createTempDir([dir = '']) { | 160 Directory createTempDir([dir = '']) { |
161 dir = _getDirectory(dir); | 161 var tempDir = _getDirectory(dir).createTempSync(); |
162 return log.ioAsync("create temp directory ${dir.path}", | 162 log.io("Created temp directory ${tempDir.path}"); |
163 dir.createTemp()); | 163 return tempDir; |
164 } | 164 } |
165 | 165 |
166 /// Asynchronously recursively deletes [dir], which can be a [String] or a | 166 /// Asynchronously recursively deletes [dir], which can be a [String] or a |
167 /// [Directory]. Returns a [Future] that completes when the deletion is done. | 167 /// [Directory]. Returns a [Future] that completes when the deletion is done. |
168 Future<Directory> deleteDir(dir) { | 168 Future<Directory> deleteDir(dir) { |
169 dir = _getDirectory(dir); | 169 dir = _getDirectory(dir); |
170 | 170 |
171 return _attemptRetryable(() => log.ioAsync("delete directory ${dir.path}", | 171 return _attemptRetryable(() => log.ioAsync("delete directory ${dir.path}", |
172 dir.delete(recursive: true))); | 172 dir.delete(recursive: true))); |
173 } | 173 } |
(...skipping 541 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
715 if (completed) return; | 715 if (completed) return; |
716 timer.cancel(); | 716 timer.cancel(); |
717 completer.completeError(e.error, e.stackTrace); | 717 completer.completeError(e.error, e.stackTrace); |
718 }); | 718 }); |
719 return completer.future; | 719 return completer.future; |
720 } | 720 } |
721 | 721 |
722 /// Creates a temporary directory and passes its path to [fn]. Once the [Future] | 722 /// Creates a temporary directory and passes its path to [fn]. Once the [Future] |
723 /// returned by [fn] completes, the temporary directory and all its contents | 723 /// returned by [fn] completes, the temporary directory and all its contents |
724 /// will be deleted. | 724 /// will be deleted. |
| 725 /// |
| 726 /// Returns a future that completes to the value that the future returned from |
| 727 /// [fn] completes to. |
725 Future withTempDir(Future fn(String path)) { | 728 Future withTempDir(Future fn(String path)) { |
726 var tempDir; | 729 return defer(() { |
727 return createTempDir().then((dir) { | 730 var tempDir = createTempDir(); |
728 tempDir = dir; | 731 return fn(tempDir.path).whenComplete(() { |
729 return fn(tempDir.path); | 732 return deleteDir(tempDir); |
730 }).whenComplete(() { | 733 }); |
731 log.fine('Cleaning up temp directory ${tempDir.path}.'); | |
732 return deleteDir(tempDir); | |
733 }); | 734 }); |
734 } | 735 } |
735 | 736 |
736 /// Extracts a `.tar.gz` file from [stream] to [destination], which can be a | 737 /// Extracts a `.tar.gz` file from [stream] to [destination], which can be a |
737 /// directory or a path. Returns whether or not the extraction was successful. | 738 /// directory or a path. Returns whether or not the extraction was successful. |
738 Future<bool> extractTarGz(Stream<List<int>> stream, destination) { | 739 Future<bool> extractTarGz(Stream<List<int>> stream, destination) { |
739 destination = _getPath(destination); | 740 destination = _getPath(destination); |
740 | 741 |
741 log.fine("Extracting .tar.gz stream to $destination."); | 742 log.fine("Extracting .tar.gz stream to $destination."); |
742 | 743 |
(...skipping 29 matching lines...) Expand all Loading... |
772 // instead of writing out temp files. The code is simpler, but unfortunately, | 773 // instead of writing out temp files. The code is simpler, but unfortunately, |
773 // 7zip seems to periodically fail when we invoke it from Dart and tell it to | 774 // 7zip seems to periodically fail when we invoke it from Dart and tell it to |
774 // read from stdin instead of a file. Consider resurrecting that version if | 775 // read from stdin instead of a file. Consider resurrecting that version if |
775 // we can figure out why it fails. | 776 // we can figure out why it fails. |
776 | 777 |
777 // Note: This line of code gets munged by create_sdk.py to be the correct | 778 // Note: This line of code gets munged by create_sdk.py to be the correct |
778 // relative path to 7zip in the SDK. | 779 // relative path to 7zip in the SDK. |
779 var pathTo7zip = '../../third_party/7zip/7za.exe'; | 780 var pathTo7zip = '../../third_party/7zip/7za.exe'; |
780 var command = relativeToPub(pathTo7zip); | 781 var command = relativeToPub(pathTo7zip); |
781 | 782 |
782 var tempDir; | 783 return withTempDir((tempDir) { |
| 784 // Write the archive to a temp file. |
| 785 return createFileFromStream(stream, join(tempDir, 'data.tar.gz')).then((_) { |
| 786 // 7zip can't unarchive from gzip -> tar -> destination all in one step |
| 787 // first we un-gzip it to a tar file. |
| 788 // Note: Setting the working directory instead of passing in a full file |
| 789 // path because 7zip says "A full path is not allowed here." |
| 790 return runProcess(command, ['e', 'data.tar.gz'], workingDir: tempDir); |
| 791 }).then((result) { |
| 792 if (result.exitCode != 0) { |
| 793 throw 'Could not un-gzip (exit code ${result.exitCode}). Error:\n' |
| 794 '${Strings.join(result.stdout, "\n")}\n' |
| 795 '${Strings.join(result.stderr, "\n")}'; |
| 796 } |
| 797 // Find the tar file we just created since we don't know its name. |
| 798 return listDir(tempDir); |
| 799 }).then((files) { |
| 800 var tarFile; |
| 801 for (var file in files) { |
| 802 if (path.extension(file) == '.tar') { |
| 803 tarFile = file; |
| 804 break; |
| 805 } |
| 806 } |
783 | 807 |
784 // TODO(rnystrom): Use withTempDir(). | 808 if (tarFile == null) throw 'The gzip file did not contain a tar file.'; |
785 return createTempDir().then((temp) { | 809 |
786 // Write the archive to a temp file. | 810 // Untar the archive into the destination directory. |
787 tempDir = temp; | 811 return runProcess(command, ['x', tarFile], workingDir: destination); |
788 return createFileFromStream(stream, join(tempDir, 'data.tar.gz')); | 812 }).then((result) { |
789 }).then((_) { | 813 if (result.exitCode != 0) { |
790 // 7zip can't unarchive from gzip -> tar -> destination all in one step | 814 throw 'Could not un-tar (exit code ${result.exitCode}). Error:\n' |
791 // first we un-gzip it to a tar file. | 815 '${Strings.join(result.stdout, "\n")}\n' |
792 // Note: Setting the working directory instead of passing in a full file | 816 '${Strings.join(result.stderr, "\n")}'; |
793 // path because 7zip says "A full path is not allowed here." | |
794 return runProcess(command, ['e', 'data.tar.gz'], workingDir: tempDir); | |
795 }).then((result) { | |
796 if (result.exitCode != 0) { | |
797 throw 'Could not un-gzip (exit code ${result.exitCode}). Error:\n' | |
798 '${Strings.join(result.stdout, "\n")}\n' | |
799 '${Strings.join(result.stderr, "\n")}'; | |
800 } | |
801 // Find the tar file we just created since we don't know its name. | |
802 return listDir(tempDir); | |
803 }).then((files) { | |
804 var tarFile; | |
805 for (var file in files) { | |
806 if (path.extension(file) == '.tar') { | |
807 tarFile = file; | |
808 break; | |
809 } | 817 } |
810 } | 818 return true; |
811 | 819 }); |
812 if (tarFile == null) throw 'The gzip file did not contain a tar file.'; | 820 }); |
813 | |
814 // Untar the archive into the destination directory. | |
815 return runProcess(command, ['x', tarFile], workingDir: destination); | |
816 }).then((result) { | |
817 if (result.exitCode != 0) { | |
818 throw 'Could not un-tar (exit code ${result.exitCode}). Error:\n' | |
819 '${Strings.join(result.stdout, "\n")}\n' | |
820 '${Strings.join(result.stderr, "\n")}'; | |
821 } | |
822 | |
823 log.fine('Clean up 7zip temp directory ${tempDir.path}.'); | |
824 // TODO(rnystrom): Should also delete this if anything fails. | |
825 return deleteDir(tempDir); | |
826 }).then((_) => true); | |
827 } | 821 } |
828 | 822 |
829 /// Create a .tar.gz archive from a list of entries. Each entry can be a | 823 /// Create a .tar.gz archive from a list of entries. Each entry can be a |
830 /// [String], [Directory], or [File] object. The root of the archive is | 824 /// [String], [Directory], or [File] object. The root of the archive is |
831 /// considered to be [baseDir], which defaults to the current working directory. | 825 /// considered to be [baseDir], which defaults to the current working directory. |
832 /// Returns a [ByteStream] that will emit the contents of the archive. | 826 /// Returns a [ByteStream] that will emit the contents of the archive. |
833 ByteStream createTarGz(List contents, {baseDir}) { | 827 ByteStream createTarGz(List contents, {baseDir}) { |
834 var buffer = new StringBuffer(); | 828 var buffer = new StringBuffer(); |
835 buffer.add('Creating .tag.gz stream containing:\n'); | 829 buffer.add('Creating .tag.gz stream containing:\n'); |
836 contents.forEach((file) => buffer.add('$file\n')); | 830 contents.forEach((file) => buffer.add('$file\n')); |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
947 Directory _getDirectory(entry) { | 941 Directory _getDirectory(entry) { |
948 if (entry is Directory) return entry; | 942 if (entry is Directory) return entry; |
949 return new Directory(entry); | 943 return new Directory(entry); |
950 } | 944 } |
951 | 945 |
952 /// Gets a [Uri] for [uri], which can either already be one, or be a [String]. | 946 /// Gets a [Uri] for [uri], which can either already be one, or be a [String]. |
953 Uri _getUri(uri) { | 947 Uri _getUri(uri) { |
954 if (uri is Uri) return uri; | 948 if (uri is Uri) return uri; |
955 return Uri.parse(uri); | 949 return Uri.parse(uri); |
956 } | 950 } |
OLD | NEW |