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 15 matching lines...) Expand all Loading... |
26 /// platform-specific path separators. Parts can be [String], [Directory], or | 26 /// platform-specific path separators. Parts can be [String], [Directory], or |
27 /// [File] objects. | 27 /// [File] objects. |
28 String join(part1, [part2, part3, part4, part5, part6, part7, part8]) { | 28 String join(part1, [part2, part3, part4, part5, part6, part7, part8]) { |
29 var parts = [part1, part2, part3, part4, part5, part6, part7, part8] | 29 var parts = [part1, part2, part3, part4, part5, part6, part7, part8] |
30 .map((part) => part == null ? null : _getPath(part)).toList(); | 30 .map((part) => part == null ? null : _getPath(part)).toList(); |
31 | 31 |
32 return path.join(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5], | 32 return path.join(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5], |
33 parts[6], parts[7]); | 33 parts[6], parts[7]); |
34 } | 34 } |
35 | 35 |
36 /// Gets the basename, the file name without any leading directory path, for | |
37 /// [file], which can either be a [String], [File], or [Directory]. | |
38 String basename(file) => path.basename(_getPath(file)); | |
39 | |
40 /// Gets the the leading directory path for [file], which can either be a | |
41 /// [String], [File], or [Directory]. | |
42 String dirname(file) => path.dirname(_getPath(file)); | |
43 | |
44 /// Splits [entry] into its individual components. | |
45 List<String> splitPath(entry) => path.split(_getPath(entry)); | |
46 | |
47 /// Returns whether or not [entry] is nested somewhere within [dir]. This just | 36 /// Returns whether or not [entry] is nested somewhere within [dir]. This just |
48 /// performs a path comparison; it doesn't look at the actual filesystem. | 37 /// performs a path comparison; it doesn't look at the actual filesystem. |
49 bool isBeneath(entry, dir) { | 38 bool isBeneath(String entry, String dir) { |
50 var relative = relativeTo(entry, dir); | 39 var relative = path.relative(entry, from: dir); |
51 return !path.isAbsolute(relative) && splitPath(relative)[0] != '..'; | 40 return !path.isAbsolute(relative) && path.split(relative)[0] != '..'; |
52 } | 41 } |
53 | 42 |
54 /// Returns the path to [target] from [base]. | |
55 String relativeTo(target, base) => path.relative(target, from: base); | |
56 | |
57 /// Determines if [path], which can be a [String] file path, a [File], or a | 43 /// Determines if [path], which can be a [String] file path, a [File], or a |
58 /// [Directory] exists on the file system. | 44 /// [Directory] exists on the file system. |
59 bool entryExists(path) => fileExists(path) || dirExists(path); | 45 bool entryExists(path) => fileExists(path) || dirExists(path); |
60 | 46 |
61 /// Determines if [file], which can be a [String] file path or a [File], exists | 47 /// Determines if [file], which can be a [String] file path or a [File], exists |
62 /// on the file system. | 48 /// on the file system. |
63 bool fileExists(file) => _getFile(file).existsSync(); | 49 bool fileExists(file) => _getFile(file).existsSync(); |
64 | 50 |
65 /// Reads the contents of the text file [file], which can either be a [String] | 51 /// Reads the contents of the text file [file], which can either be a [String] |
66 /// or a [File]. | 52 /// or a [File]. |
(...skipping 19 matching lines...) Expand all Loading... |
86 | 72 |
87 // Sanity check: don't spew a huge file. | 73 // Sanity check: don't spew a huge file. |
88 log.io("Writing ${contents.length} characters to text file $path."); | 74 log.io("Writing ${contents.length} characters to text file $path."); |
89 if (!dontLogContents && contents.length < 1024 * 1024) { | 75 if (!dontLogContents && contents.length < 1024 * 1024) { |
90 log.fine("Contents:\n$contents"); | 76 log.fine("Contents:\n$contents"); |
91 } | 77 } |
92 | 78 |
93 return file..writeAsStringSync(contents); | 79 return file..writeAsStringSync(contents); |
94 } | 80 } |
95 | 81 |
96 /// Deletes [file], which can be a [String] or a [File]. Returns a [Future] | 82 /// Deletes [file], which can be a [String] or a [File]. |
97 /// that completes when the deletion is done. | |
98 File deleteFile(file) => _getFile(file)..delete(); | 83 File deleteFile(file) => _getFile(file)..delete(); |
99 | 84 |
100 /// Creates [file] (which can either be a [String] or a [File]), and writes | 85 /// Creates [file] (which can either be a [String] or a [File]), and writes |
101 /// [contents] to it. | 86 /// [contents] to it. |
102 File writeBinaryFile(file, List<int> contents) { | 87 File writeBinaryFile(file, List<int> contents) { |
103 var path = _getPath(file); | 88 var path = _getPath(file); |
104 file = new File(path); | 89 file = new File(path); |
105 | 90 |
106 log.io("Writing ${contents.length} bytes to binary file $path."); | 91 log.io("Writing ${contents.length} bytes to binary file $path."); |
107 file.openSync(FileMode.WRITE) | 92 file.openSync(FileMode.WRITE) |
(...skipping 13 matching lines...) Expand all Loading... |
121 | 106 |
122 var file = new File(path); | 107 var file = new File(path); |
123 return stream.pipe(wrapOutputStream(file.openOutputStream())).then((_) { | 108 return stream.pipe(wrapOutputStream(file.openOutputStream())).then((_) { |
124 log.fine("Created $path from stream."); | 109 log.fine("Created $path from stream."); |
125 }); | 110 }); |
126 } | 111 } |
127 | 112 |
128 /// Creates a directory [dir]. | 113 /// Creates a directory [dir]. |
129 Directory createDir(dir) => _getDirectory(dir)..createSync(); | 114 Directory createDir(dir) => _getDirectory(dir)..createSync(); |
130 | 115 |
131 /// Ensures that [path] and all its parent directories exist. If they don't | 116 /// Ensures that [dirPath] and all its parent directories exist. If they don't |
132 /// exist, creates them. | 117 /// exist, creates them. |
133 Directory ensureDir(path) { | 118 Directory ensureDir(dirPath) { |
134 path = _getPath(path); | 119 dirPath = _getPath(dirPath); |
135 | 120 |
136 log.fine("Ensuring directory $path exists."); | 121 log.fine("Ensuring directory $dirPath exists."); |
137 var dir = new Directory(path); | 122 var dir = new Directory(dirPath); |
138 if (path == '.' || dirExists(path)) return dir; | 123 if (dirPath == '.' || dirExists(dirPath)) return dir; |
139 | 124 |
140 ensureDir(dirname(path)); | 125 ensureDir(path.dirname(dirPath)); |
141 | 126 |
142 try { | 127 try { |
143 createDir(dir); | 128 createDir(dir); |
144 } on DirectoryIOException catch (ex) { | 129 } on DirectoryIOException catch (ex) { |
145 // Error 17 means the directory already exists (or 183 on Windows). | 130 // Error 17 means the directory already exists (or 183 on Windows). |
146 if (ex.osError.errorCode == 17 || ex.osError.errorCode == 183) { | 131 if (ex.osError.errorCode == 17 || ex.osError.errorCode == 183) { |
147 log.fine("Got 'already exists' error when creating directory."); | 132 log.fine("Got 'already exists' error when creating directory."); |
148 } else { | 133 } else { |
149 throw ex; | 134 throw ex; |
150 } | 135 } |
151 } | 136 } |
152 | 137 |
153 return dir; | 138 return dir; |
154 } | 139 } |
155 | 140 |
156 /// Creates a temp directory whose name will be based on [dir] with a unique | 141 /// 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 | 142 /// 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 | 143 /// created in a platform-dependent temporary location. Returns the path of the |
159 /// completes when the directory is created. | 144 /// created directory. |
160 Directory createTempDir([dir = '']) { | 145 String createTempDir([dir = '']) { |
161 var tempDir = _getDirectory(dir).createTempSync(); | 146 var tempDir = _getDirectory(dir).createTempSync(); |
162 log.io("Created temp directory ${tempDir.path}"); | 147 log.io("Created temp directory ${tempDir.path}"); |
163 return tempDir; | 148 return tempDir.path; |
164 } | 149 } |
165 | 150 |
166 /// Asynchronously recursively deletes [dir], which can be a [String] or a | 151 /// Asynchronously recursively deletes [dir], which can be a [String] or a |
167 /// [Directory]. Returns a [Future] that completes when the deletion is done. | 152 /// [Directory]. Returns a [Future] that completes when the deletion is done. |
168 Future<Directory> deleteDir(dir) { | 153 Future<Directory> deleteDir(dir) { |
169 dir = _getDirectory(dir); | 154 dir = _getDirectory(dir); |
170 | 155 |
171 return _attemptRetryable(() => log.ioAsync("delete directory ${dir.path}", | 156 return _attemptRetryable(() => log.ioAsync("delete directory ${dir.path}", |
172 dir.delete(recursive: true))); | 157 dir.delete(recursive: true))); |
173 } | 158 } |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
211 var stackTrace; | 196 var stackTrace; |
212 try { | 197 try { |
213 throw ""; | 198 throw ""; |
214 } catch (_, localStackTrace) { | 199 } catch (_, localStackTrace) { |
215 stackTrace = localStackTrace; | 200 stackTrace = localStackTrace; |
216 } | 201 } |
217 | 202 |
218 var children = []; | 203 var children = []; |
219 lister.onError = (error) => completer.completeError(error, stackTrace); | 204 lister.onError = (error) => completer.completeError(error, stackTrace); |
220 lister.onDir = (file) { | 205 lister.onDir = (file) { |
221 if (!includeHiddenFiles && basename(file).startsWith('.')) return; | 206 if (!includeHiddenFiles && path.basename(file).startsWith('.')) return; |
222 file = join(dir, basename(file)); | 207 file = join(dir, path.basename(file)); |
223 contents.add(file); | 208 contents.add(file); |
224 // TODO(nweiz): don't manually recurse once issue 7358 is fixed. Note that | 209 // TODO(nweiz): don't manually recurse once issue 7358 is fixed. Note that |
225 // once we remove the manual recursion, we'll need to explicitly filter | 210 // once we remove the manual recursion, we'll need to explicitly filter |
226 // out files in hidden directories. | 211 // out files in hidden directories. |
227 if (recursive) { | 212 if (recursive) { |
228 children.add(doList(new Directory(file), listedDirectories)); | 213 children.add(doList(new Directory(file), listedDirectories)); |
229 } | 214 } |
230 }; | 215 }; |
231 | 216 |
232 lister.onFile = (file) { | 217 lister.onFile = (file) { |
233 if (!includeHiddenFiles && basename(file).startsWith('.')) return; | 218 if (!includeHiddenFiles && path.basename(file).startsWith('.')) return; |
234 contents.add(join(dir, basename(file))); | 219 contents.add(join(dir, path.basename(file))); |
235 }; | 220 }; |
236 | 221 |
237 return completer.future.then((contents) { | 222 return completer.future.then((contents) { |
238 return Future.wait(children).then((childContents) { | 223 return Future.wait(children).then((childContents) { |
239 contents.addAll(flatten(childContents)); | 224 contents.addAll(flatten(childContents)); |
240 return contents; | 225 return contents; |
241 }); | 226 }); |
242 }); | 227 }); |
243 } | 228 } |
244 | 229 |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
354 // code in bin or web. | 339 // code in bin or web. |
355 if (!isSelfLink) { | 340 if (!isSelfLink) { |
356 log.warning('Warning: Package "$name" does not have a "lib" directory so ' | 341 log.warning('Warning: Package "$name" does not have a "lib" directory so ' |
357 'you will not be able to import any libraries from it.'); | 342 'you will not be able to import any libraries from it.'); |
358 } | 343 } |
359 | 344 |
360 return _getFile(to); | 345 return _getFile(to); |
361 }); | 346 }); |
362 } | 347 } |
363 | 348 |
364 /// Given [entry] which may be a [String], [File], or [Directory] relative to | |
365 /// the current working directory, returns its full canonicalized path. | |
366 String getFullPath(entry) => path.absolute(_getPath(entry)); | |
367 | |
368 /// Returns whether or not [entry] is an absolute path. | |
369 bool isAbsolute(entry) => path.isAbsolute(_getPath(entry)); | |
370 | |
371 /// Resolves [target] relative to the location of pub.dart. | 349 /// Resolves [target] relative to the location of pub.dart. |
372 String relativeToPub(String target) { | 350 String relativeToPub(String target) { |
373 var scriptPath = new File(new Options().script).fullPathSync(); | 351 var scriptPath = new File(new Options().script).fullPathSync(); |
374 | 352 |
375 // Walk up until we hit the "util(s)" directory. This lets us figure out where | 353 // Walk up until we hit the "util(s)" directory. This lets us figure out where |
376 // we are if this function is called from pub.dart, or one of the tests, | 354 // we are if this function is called from pub.dart, or one of the tests, |
377 // which also live under "utils", or from the SDK where pub is in "util". | 355 // which also live under "utils", or from the SDK where pub is in "util". |
378 var utilDir = dirname(scriptPath); | 356 var utilDir = path.dirname(scriptPath); |
379 while (basename(utilDir) != 'utils' && basename(utilDir) != 'util') { | 357 while (path.basename(utilDir) != 'utils' && |
380 if (basename(utilDir) == '') throw 'Could not find path to pub.'; | 358 path.basename(utilDir) != 'util') { |
381 utilDir = dirname(utilDir); | 359 if (path.basename(utilDir) == '') throw 'Could not find path to pub.'; |
| 360 utilDir = path.dirname(utilDir); |
382 } | 361 } |
383 | 362 |
384 return path.normalize(join(utilDir, 'pub', target)); | 363 return path.normalize(join(utilDir, 'pub', target)); |
385 } | 364 } |
386 | 365 |
387 // TODO(nweiz): add a ByteSink wrapper to make writing strings to stdout/stderr | 366 // TODO(nweiz): add a ByteSink wrapper to make writing strings to stdout/stderr |
388 // nicer. | 367 // nicer. |
389 | 368 |
390 /// A sink that writes to standard output. Errors piped to this stream will be | 369 /// A sink that writes to standard output. Errors piped to this stream will be |
391 /// surfaced to the top-level error handler. | 370 /// surfaced to the top-level error handler. |
(...skipping 329 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
721 | 700 |
722 /// Creates a temporary directory and passes its path to [fn]. Once the [Future] | 701 /// 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 | 702 /// returned by [fn] completes, the temporary directory and all its contents |
724 /// will be deleted. | 703 /// will be deleted. |
725 /// | 704 /// |
726 /// Returns a future that completes to the value that the future returned from | 705 /// Returns a future that completes to the value that the future returned from |
727 /// [fn] completes to. | 706 /// [fn] completes to. |
728 Future withTempDir(Future fn(String path)) { | 707 Future withTempDir(Future fn(String path)) { |
729 return defer(() { | 708 return defer(() { |
730 var tempDir = createTempDir(); | 709 var tempDir = createTempDir(); |
731 return fn(tempDir.path).whenComplete(() { | 710 return fn(tempDir).whenComplete(() { |
732 return deleteDir(tempDir); | 711 return deleteDir(tempDir); |
733 }); | 712 }); |
734 }); | 713 }); |
735 } | 714 } |
736 | 715 |
737 /// Extracts a `.tar.gz` file from [stream] to [destination], which can be a | 716 /// Extracts a `.tar.gz` file from [stream] to [destination], which can be a |
738 /// directory or a path. Returns whether or not the extraction was successful. | 717 /// directory or a path. Returns whether or not the extraction was successful. |
739 Future<bool> extractTarGz(Stream<List<int>> stream, destination) { | 718 Future<bool> extractTarGz(Stream<List<int>> stream, destination) { |
740 destination = _getPath(destination); | 719 destination = _getPath(destination); |
741 | 720 |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
828 var buffer = new StringBuffer(); | 807 var buffer = new StringBuffer(); |
829 buffer.add('Creating .tag.gz stream containing:\n'); | 808 buffer.add('Creating .tag.gz stream containing:\n'); |
830 contents.forEach((file) => buffer.add('$file\n')); | 809 contents.forEach((file) => buffer.add('$file\n')); |
831 log.fine(buffer.toString()); | 810 log.fine(buffer.toString()); |
832 | 811 |
833 // TODO(nweiz): Propagate errors to the returned stream (including non-zero | 812 // TODO(nweiz): Propagate errors to the returned stream (including non-zero |
834 // exit codes). See issue 3657. | 813 // exit codes). See issue 3657. |
835 var controller = new StreamController<List<int>>(); | 814 var controller = new StreamController<List<int>>(); |
836 | 815 |
837 if (baseDir == null) baseDir = path.current; | 816 if (baseDir == null) baseDir = path.current; |
838 baseDir = getFullPath(baseDir); | 817 baseDir = path.absolute(baseDir); |
839 contents = contents.map((entry) { | 818 contents = contents.map((entry) { |
840 entry = getFullPath(entry); | 819 entry = path.absolute(_getPath(entry)); |
841 if (!isBeneath(entry, baseDir)) { | 820 if (!isBeneath(entry, baseDir)) { |
842 throw 'Entry $entry is not inside $baseDir.'; | 821 throw 'Entry $entry is not inside $baseDir.'; |
843 } | 822 } |
844 return relativeTo(entry, baseDir); | 823 return path.relative(entry, from: baseDir); |
845 }).toList(); | 824 }).toList(); |
846 | 825 |
847 if (Platform.operatingSystem != "windows") { | 826 if (Platform.operatingSystem != "windows") { |
848 var args = ["--create", "--gzip", "--directory", baseDir]; | 827 var args = ["--create", "--gzip", "--directory", baseDir]; |
849 args.addAll(contents.map(_getPath)); | 828 args.addAll(contents.map(_getPath)); |
850 // TODO(nweiz): It's possible that enough command-line arguments will make | 829 // TODO(nweiz): It's possible that enough command-line arguments will make |
851 // the process choke, so at some point we should save the arguments to a | 830 // the process choke, so at some point we should save the arguments to a |
852 // file and pass them in via --files-from for tar and -i@filename for 7zip. | 831 // file and pass them in via --files-from for tar and -i@filename for 7zip. |
853 startProcess("tar", args).then((process) { | 832 startProcess("tar", args).then((process) { |
854 store(process.stdout, controller); | 833 store(process.stdout, controller); |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
941 Directory _getDirectory(entry) { | 920 Directory _getDirectory(entry) { |
942 if (entry is Directory) return entry; | 921 if (entry is Directory) return entry; |
943 return new Directory(entry); | 922 return new Directory(entry); |
944 } | 923 } |
945 | 924 |
946 /// Gets a [Uri] for [uri], which can either already be one, or be a [String]. | 925 /// Gets a [Uri] for [uri], which can either already be one, or be a [String]. |
947 Uri _getUri(uri) { | 926 Uri _getUri(uri) { |
948 if (uri is Uri) return uri; | 927 if (uri is Uri) return uri; |
949 return Uri.parse(uri); | 928 return Uri.parse(uri); |
950 } | 929 } |
OLD | NEW |