Chromium Code Reviews| 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 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 42 /// Returns whether or not [entry] is nested somewhere within [dir]. This just | 42 /// Returns whether or not [entry] is nested somewhere within [dir]. This just |
| 43 /// performs a path comparison; it doesn't look at the actual filesystem. | 43 /// performs a path comparison; it doesn't look at the actual filesystem. |
| 44 bool isBeneath(entry, dir) { | 44 bool isBeneath(entry, dir) { |
| 45 var relative = relativeTo(entry, dir); | 45 var relative = relativeTo(entry, dir); |
| 46 return !path.isAbsolute(relative) && splitPath(relative)[0] != '..'; | 46 return !path.isAbsolute(relative) && splitPath(relative)[0] != '..'; |
| 47 } | 47 } |
| 48 | 48 |
| 49 /// Returns the path to [target] from [base]. | 49 /// Returns the path to [target] from [base]. |
| 50 String relativeTo(target, base) => path.relative(target, from: base); | 50 String relativeTo(target, base) => path.relative(target, from: base); |
| 51 | 51 |
| 52 /// Asynchronously determines if [path], which can be a [String] file path, a | 52 /// Determines if [path], which can be a [String] file path, a [File], or a |
| 53 /// [File], or a [Directory] exists on the file system. Returns a [Future] that | 53 /// [Directory] exists on the file system. |
| 54 /// completes with the result. | 54 bool entryExists(path) => fileExists(path) || dirExists(path); |
| 55 Future<bool> exists(path) { | |
| 56 path = _getPath(path); | |
| 57 return Future.wait([fileExists(path), dirExists(path)]).then((results) { | |
| 58 return results[0] || results[1]; | |
| 59 }); | |
| 60 } | |
| 61 | 55 |
| 62 /// Asynchronously determines if [file], which can be a [String] file path or a | 56 /// Determines if [file], which can be a [String] file path or a [File], exists |
| 63 /// [File], exists on the file system. Returns a [Future] that completes with | 57 /// on the file system. |
| 64 /// the result. | 58 bool fileExists(file) => _getFile(file).existsSync(); |
| 65 Future<bool> fileExists(file) { | |
| 66 var path = _getPath(file); | |
| 67 return log.ioAsync("Seeing if file $path exists.", | |
| 68 new File(path).exists(), | |
| 69 (exists) => "File $path ${exists ? 'exists' : 'does not exist'}."); | |
| 70 } | |
| 71 | 59 |
| 72 /// Reads the contents of the text file [file], which can either be a [String] | 60 /// Reads the contents of the text file [file], which can either be a [String] |
| 73 /// or a [File]. | 61 /// or a [File]. |
| 74 Future<String> readTextFile(file) { | 62 String readTextFile(file) => _getFile(file).readAsStringSync(Encoding.UTF_8); |
| 75 var path = _getPath(file); | |
| 76 return log.ioAsync("Reading text file $path.", | |
| 77 new File(path).readAsString(Encoding.UTF_8), | |
| 78 (contents) { | |
| 79 // Sanity check: don't spew a huge file. | |
| 80 if (contents.length < 1024 * 1024) { | |
| 81 return "Read $path. Contents:\n$contents"; | |
| 82 } else { | |
| 83 return "Read ${contents.length} characters from $path."; | |
| 84 } | |
| 85 }); | |
| 86 } | |
| 87 | 63 |
| 88 /// Creates [file] (which can either be a [String] or a [File]), and writes | 64 /// Creates [file] (which can either be a [String] or a [File]), and writes |
| 89 /// [contents] to it. Completes when the file is written and closed. | 65 /// [contents] to it. |
| 90 /// | 66 /// |
| 91 /// If [dontLogContents] is true, the contents of the file will never be logged. | 67 /// If [dontLogContents] is true, the contents of the file will never be logged. |
| 92 Future<File> writeTextFile(file, String contents, {dontLogContents: false}) { | 68 File writeTextFile(file, String contents, {dontLogContents: false}) { |
| 93 var path = _getPath(file); | 69 var path = _getPath(file); |
| 94 file = new File(path); | 70 file = new File(path); |
| 95 | 71 |
| 96 // Sanity check: don't spew a huge file. | 72 // Sanity check: don't spew a huge file. |
| 97 log.io("Writing ${contents.length} characters to text file $path."); | 73 log.io("Writing ${contents.length} characters to text file $path."); |
| 98 if (!dontLogContents && contents.length < 1024 * 1024) { | 74 if (!dontLogContents && contents.length < 1024 * 1024) { |
| 99 log.fine("Contents:\n$contents"); | 75 log.fine("Contents:\n$contents"); |
| 100 } | 76 } |
| 101 | 77 |
| 102 return file.open(FileMode.WRITE).then((opened) { | 78 file.writeAsStringSync(contents); |
| 103 return opened.writeString(contents).then((ignore) { | 79 return file; |
|
nweiz
2013/02/01 02:05:55
"return file..writeAsStringSync(contents)"? Is tha
Bob Nystrom
2013/02/01 23:17:21
Eh, I'll try it. Let's see if it grows on us.
| |
| 104 return opened.close().then((_) { | |
| 105 log.fine("Wrote text file $path."); | |
| 106 return file; | |
| 107 }); | |
| 108 }); | |
| 109 }); | |
| 110 } | 80 } |
| 111 | 81 |
| 112 /// Asynchronously deletes [file], which can be a [String] or a [File]. Returns | 82 /// Deletes [file], which can be a [String] or a [File]. Returns a [Future] |
| 113 /// a [Future] that completes when the deletion is done. | 83 /// that completes when the deletion is done. |
| 114 Future<File> deleteFile(file) { | 84 File deleteFile(file) => _getFile(file)..delete(); |
| 115 var path = _getPath(file); | |
| 116 return log.ioAsync("delete file $path", | |
| 117 new File(path).delete()); | |
| 118 } | |
| 119 | 85 |
| 120 /// Writes [stream] to a new file at [path], which may be a [String] or a | 86 /// Writes [stream] to a new file at [path], which may be a [String] or a |
| 121 /// [File]. Will replace any file already at that path. Completes when the file | 87 /// [File]. Will replace any file already at that path. Completes when the file |
| 122 /// is done being written. | 88 /// is done being written. |
| 123 Future<File> createFileFromStream(InputStream stream, path) { | 89 Future<File> createFileFromStream(InputStream stream, path) { |
| 124 path = _getPath(path); | 90 path = _getPath(path); |
| 125 | 91 |
| 126 log.io("Creating $path from stream."); | 92 log.io("Creating $path from stream."); |
| 127 | 93 |
| 128 var completer = new Completer<File>(); | 94 var completer = new Completer<File>(); |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 153 log.fine("Got error after stream was closed: $error"); | 119 log.fine("Got error after stream was closed: $error"); |
| 154 } | 120 } |
| 155 } | 121 } |
| 156 | 122 |
| 157 stream.onError = completeError; | 123 stream.onError = completeError; |
| 158 outputStream.onError = completeError; | 124 outputStream.onError = completeError; |
| 159 | 125 |
| 160 return completer.future; | 126 return completer.future; |
| 161 } | 127 } |
| 162 | 128 |
| 163 /// Creates a directory [dir]. Returns a [Future] that completes when the | 129 /// Creates a directory [dir]. |
| 164 /// directory is created. | 130 Directory createDir(dir) => _getDirectory(dir)..createSync(); |
| 165 Future<Directory> createDir(dir) { | |
| 166 dir = _getDirectory(dir); | |
| 167 return log.ioAsync("create directory ${dir.path}", | |
| 168 dir.create()); | |
| 169 } | |
| 170 | 131 |
| 171 /// Ensures that [path] and all its parent directories exist. If they don't | 132 /// Ensures that [path] and all its parent directories exist. If they don't |
| 172 /// exist, creates them. Returns a [Future] that completes once all the | 133 /// exist, creates them. |
| 173 /// directories are created. | 134 Directory ensureDir(path) { |
| 174 Future<Directory> ensureDir(path) { | |
| 175 path = _getPath(path); | 135 path = _getPath(path); |
| 136 | |
| 176 log.fine("Ensuring directory $path exists."); | 137 log.fine("Ensuring directory $path exists."); |
| 177 if (path == '.') return new Future.immediate(new Directory('.')); | 138 var dir = new Directory(path); |
| 139 if (path == '.' || dirExists(path)) return dir; | |
| 178 | 140 |
| 179 return dirExists(path).then((exists) { | 141 ensureDir(dirname(path)); |
| 180 if (exists) { | 142 |
| 181 log.fine("Directory $path already exists."); | 143 try { |
| 182 return new Directory(path); | 144 createDir(dir); |
| 145 } on DirectoryIOException catch (ex) { | |
| 146 // Error 17 means the directory already exists (or 183 on Windows). | |
| 147 if (ex.osError.errorCode == 17 || ex.osError.errorCode == 183) { | |
| 148 log.fine("Got 'already exists' error when creating directory."); | |
| 149 } else { | |
| 150 throw ex; | |
| 183 } | 151 } |
| 152 } | |
| 184 | 153 |
| 185 return ensureDir(dirname(path)).then((_) { | 154 return dir; |
| 186 return createDir(path).catchError((asyncError) { | |
| 187 if (asyncError.error is! DirectoryIOException) throw asyncError; | |
| 188 // Error 17 means the directory already exists (or 183 on Windows). | |
| 189 if (asyncError.error.osError.errorCode == 17 || | |
| 190 asyncError.error.osError.errorCode == 183) { | |
| 191 log.fine("Got 'already exists' error when creating directory."); | |
| 192 return _getDirectory(path); | |
| 193 } | |
| 194 | |
| 195 throw asyncError; | |
| 196 }); | |
| 197 }); | |
| 198 }); | |
| 199 } | 155 } |
| 200 | 156 |
| 201 /// Creates a temp directory whose name will be based on [dir] with a unique | 157 /// Creates a temp directory whose name will be based on [dir] with a unique |
| 202 /// suffix appended to it. If [dir] is not provided, a temp directory will be | 158 /// suffix appended to it. If [dir] is not provided, a temp directory will be |
| 203 /// created in a platform-dependent temporary location. Returns a [Future] that | 159 /// created in a platform-dependent temporary location. Returns a [Future] that |
| 204 /// completes when the directory is created. | 160 /// completes when the directory is created. |
| 205 Future<Directory> createTempDir([dir = '']) { | 161 Future<Directory> createTempDir([dir = '']) { |
| 206 dir = _getDirectory(dir); | 162 dir = _getDirectory(dir); |
| 207 return log.ioAsync("create temp directory ${dir.path}", | 163 return log.ioAsync("create temp directory ${dir.path}", |
| 208 dir.createTemp()); | 164 dir.createTemp()); |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 282 return Future.wait(children).then((childContents) { | 238 return Future.wait(children).then((childContents) { |
| 283 contents.addAll(flatten(childContents)); | 239 contents.addAll(flatten(childContents)); |
| 284 return contents; | 240 return contents; |
| 285 }); | 241 }); |
| 286 }); | 242 }); |
| 287 } | 243 } |
| 288 | 244 |
| 289 return doList(_getDirectory(dir), new Set<String>()); | 245 return doList(_getDirectory(dir), new Set<String>()); |
| 290 } | 246 } |
| 291 | 247 |
| 292 /// Asynchronously determines if [dir], which can be a [String] directory path | 248 /// Determines if [dir], which can be a [String] directory path or a |
| 293 /// or a [Directory], exists on the file system. Returns a [Future] that | 249 /// [Directory], exists on the file system. |
| 294 /// completes with the result. | 250 bool dirExists(dir) => _getDirectory(dir).existsSync(); |
| 295 Future<bool> dirExists(dir) { | |
| 296 dir = _getDirectory(dir); | |
| 297 return log.ioAsync("Seeing if directory ${dir.path} exists.", | |
| 298 dir.exists(), | |
| 299 (exists) => "Directory ${dir.path} " | |
| 300 "${exists ? 'exists' : 'does not exist'}."); | |
| 301 } | |
| 302 | 251 |
| 303 /// "Cleans" [dir]. If that directory already exists, it will be deleted. Then a | 252 /// "Cleans" [dir]. If that directory already exists, it will be deleted. Then a |
| 304 /// new empty directory will be created. Returns a [Future] that completes when | 253 /// new empty directory will be created. Returns a [Future] that completes when |
| 305 /// the new clean directory is created. | 254 /// the new clean directory is created. |
| 306 Future<Directory> cleanDir(dir) { | 255 Future<Directory> cleanDir(dir) { |
| 307 return dirExists(dir).then((exists) { | 256 // Make sure all errors propagate through future. |
| 308 if (exists) { | 257 return defer(() { |
| 258 if (dirExists(dir)) { | |
| 309 // Delete it first. | 259 // Delete it first. |
| 310 return deleteDir(dir).then((_) => createDir(dir)); | 260 return deleteDir(dir).then((_) => createDir(dir)); |
| 311 } else { | 261 } else { |
| 312 // Just create it. | 262 // Just create it. |
| 313 return createDir(dir); | 263 return createDir(dir); |
| 314 } | 264 } |
| 315 }); | 265 }); |
| 316 } | 266 } |
| 317 | 267 |
| 318 /// Renames (i.e. moves) the directory [from] to [to]. Returns a [Future] with | 268 /// Renames (i.e. moves) the directory [from] to [to]. Returns a [Future] with |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 385 }); | 335 }); |
| 386 } | 336 } |
| 387 | 337 |
| 388 /// Creates a new symlink that creates an alias from the `lib` directory of | 338 /// Creates a new symlink that creates an alias from the `lib` directory of |
| 389 /// package [from] to [to], both of which can be a [String], [File], or | 339 /// package [from] to [to], both of which can be a [String], [File], or |
| 390 /// [Directory]. Returns a [Future] which completes to the symlink file (i.e. | 340 /// [Directory]. Returns a [Future] which completes to the symlink file (i.e. |
| 391 /// [to]). If [from] does not have a `lib` directory, this shows a warning if | 341 /// [to]). If [from] does not have a `lib` directory, this shows a warning if |
| 392 /// appropriate and then does nothing. | 342 /// appropriate and then does nothing. |
| 393 Future<File> createPackageSymlink(String name, from, to, | 343 Future<File> createPackageSymlink(String name, from, to, |
| 394 {bool isSelfLink: false}) { | 344 {bool isSelfLink: false}) { |
| 395 // See if the package has a "lib" directory. | 345 // Make sure all errors propagate through future. |
| 396 from = join(from, 'lib'); | 346 return defer(() { |
| 397 return dirExists(from).then((exists) { | 347 // See if the package has a "lib" directory. |
| 348 from = join(from, 'lib'); | |
| 398 log.fine("Creating ${isSelfLink ? "self" : ""}link for package '$name'."); | 349 log.fine("Creating ${isSelfLink ? "self" : ""}link for package '$name'."); |
| 399 if (exists) return createSymlink(from, to); | 350 if (dirExists(from)) return createSymlink(from, to); |
| 400 | 351 |
| 401 // It's OK for the self link (i.e. the root package) to not have a lib | 352 // It's OK for the self link (i.e. the root package) to not have a lib |
| 402 // directory since it may just be a leaf application that only has | 353 // directory since it may just be a leaf application that only has |
| 403 // code in bin or web. | 354 // code in bin or web. |
| 404 if (!isSelfLink) { | 355 if (!isSelfLink) { |
| 405 log.warning('Warning: Package "$name" does not have a "lib" directory so ' | 356 log.warning('Warning: Package "$name" does not have a "lib" directory so ' |
| 406 'you will not be able to import any libraries from it.'); | 357 'you will not be able to import any libraries from it.'); |
| 407 } | 358 } |
| 408 | 359 |
| 409 return to; | 360 return _getFile(to); |
| 410 }); | 361 }); |
| 411 } | 362 } |
| 412 | 363 |
| 413 /// Given [entry] which may be a [String], [File], or [Directory] relative to | 364 /// Given [entry] which may be a [String], [File], or [Directory] relative to |
| 414 /// the current working directory, returns its full canonicalized path. | 365 /// the current working directory, returns its full canonicalized path. |
| 415 String getFullPath(entry) => path.absolute(_getPath(entry)); | 366 String getFullPath(entry) => path.absolute(_getPath(entry)); |
| 416 | 367 |
| 417 /// Returns whether or not [entry] is an absolute path. | 368 /// Returns whether or not [entry] is an absolute path. |
| 418 bool isAbsolute(entry) => path.isAbsolute(_getPath(entry)); | 369 bool isAbsolute(entry) => path.isAbsolute(_getPath(entry)); |
| 419 | 370 |
| (...skipping 524 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 944 class PubProcessResult { | 895 class PubProcessResult { |
| 945 final List<String> stdout; | 896 final List<String> stdout; |
| 946 final List<String> stderr; | 897 final List<String> stderr; |
| 947 final int exitCode; | 898 final int exitCode; |
| 948 | 899 |
| 949 const PubProcessResult(this.stdout, this.stderr, this.exitCode); | 900 const PubProcessResult(this.stdout, this.stderr, this.exitCode); |
| 950 | 901 |
| 951 bool get success => exitCode == 0; | 902 bool get success => exitCode == 0; |
| 952 } | 903 } |
| 953 | 904 |
| 905 /// Gets a dart:io [File] for [entry], which can either already be a File or be | |
| 906 /// a path string. | |
| 907 File _getFile(entry) { | |
| 908 if (entry is File) return entry; | |
| 909 if (entry is String) return new File(entry); | |
| 910 throw 'Entry $entry is not a supported type.'; | |
| 911 } | |
| 912 | |
| 954 /// Gets the path string for [entry], which can either already be a path string, | 913 /// Gets the path string for [entry], which can either already be a path string, |
| 955 /// or be a [File] or [Directory]. Allows working generically with "file-like" | 914 /// or be a [File] or [Directory]. Allows working generically with "file-like" |
| 956 /// objects. | 915 /// objects. |
| 957 String _getPath(entry) { | 916 String _getPath(entry) { |
| 958 if (entry is String) return entry; | 917 if (entry is String) return entry; |
| 959 if (entry is File) return entry.name; | 918 if (entry is File) return entry.name; |
| 960 if (entry is Directory) return entry.path; | 919 if (entry is Directory) return entry.path; |
| 961 throw 'Entry $entry is not a supported type.'; | 920 throw 'Entry $entry is not a supported type.'; |
| 962 } | 921 } |
| 963 | 922 |
| 964 /// Gets a [Directory] for [entry], which can either already be one, or be a | 923 /// Gets a [Directory] for [entry], which can either already be one, or be a |
| 965 /// [String]. | 924 /// [String]. |
| 966 Directory _getDirectory(entry) { | 925 Directory _getDirectory(entry) { |
| 967 if (entry is Directory) return entry; | 926 if (entry is Directory) return entry; |
| 968 return new Directory(entry); | 927 return new Directory(entry); |
| 969 } | 928 } |
| 970 | 929 |
| 971 /// Gets a [Uri] for [uri], which can either already be one, or be a [String]. | 930 /// Gets a [Uri] for [uri], which can either already be one, or be a [String]. |
| 972 Uri _getUri(uri) { | 931 Uri _getUri(uri) { |
| 973 if (uri is Uri) return uri; | 932 if (uri is Uri) return uri; |
| 974 return Uri.parse(uri); | 933 return Uri.parse(uri); |
| 975 } | 934 } |
| OLD | NEW |