| 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 |