| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 121 /// Creates a temp directory whose name will be based on [dir] with a unique | 121 /// Creates a temp directory whose name will be based on [dir] with a unique |
| 122 /// suffix appended to it. If [dir] is not provided, a temp directory will be | 122 /// suffix appended to it. If [dir] is not provided, a temp directory will be |
| 123 /// created in a platform-dependent temporary location. Returns the path of the | 123 /// created in a platform-dependent temporary location. Returns the path of the |
| 124 /// created directory. | 124 /// created directory. |
| 125 String createTempDir([dir = '']) { | 125 String createTempDir([dir = '']) { |
| 126 var tempDir = new Directory(dir).createTempSync(); | 126 var tempDir = new Directory(dir).createTempSync(); |
| 127 log.io("Created temp directory ${tempDir.path}"); | 127 log.io("Created temp directory ${tempDir.path}"); |
| 128 return tempDir.path; | 128 return tempDir.path; |
| 129 } | 129 } |
| 130 | 130 |
| 131 // TODO(nweiz): Remove this when issue 9252 is fixed. | 131 /// Recursively deletes [dir]. |
| 132 /// Asynchronously recursively deletes [dir]. Returns a [Future] that completes | 132 void deleteDir(String dir) { |
| 133 /// when the deletion is done. | 133 log.io("Deleting directory $dir."); |
| 134 Future<String> deleteDir(String dir) { | 134 new Directory(dir).deleteSync(recursive: true); |
| 135 return _attemptRetryable(() => log.ioAsync("delete directory $dir", | |
| 136 new Directory(dir).delete(recursive: true).then((_) => dir))); | |
| 137 } | 135 } |
| 138 | 136 |
| 139 /// Asynchronously lists the contents of [dir]. If [recursive] is `true`, lists | 137 /// Asynchronously lists the contents of [dir]. If [recursive] is `true`, lists |
| 140 /// subdirectory contents (defaults to `false`). If [includeHiddenFiles] is | 138 /// subdirectory contents (defaults to `false`). If [includeHiddenFiles] is |
| 141 /// `true`, includes files and directories beginning with `.` (defaults to | 139 /// `true`, includes files and directories beginning with `.` (defaults to |
| 142 /// `false`). | 140 /// `false`). |
| 143 /// | 141 /// |
| 144 /// If [dir] is a string, the returned paths are guaranteed to begin with it. | 142 /// If [dir] is a string, the returned paths are guaranteed to begin with it. |
| 145 Future<List<String>> listDir(String dir, | 143 Future<List<String>> listDir(String dir, |
| 146 {bool recursive: false, bool includeHiddenFiles: false}) { | 144 {bool recursive: false, bool includeHiddenFiles: false}) { |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 201 }); | 199 }); |
| 202 } | 200 } |
| 203 | 201 |
| 204 return doList(dir, new Set<String>()); | 202 return doList(dir, new Set<String>()); |
| 205 } | 203 } |
| 206 | 204 |
| 207 /// Determines if [dir] exists on the file system. | 205 /// Determines if [dir] exists on the file system. |
| 208 bool dirExists(String dir) => new Directory(dir).existsSync(); | 206 bool dirExists(String dir) => new Directory(dir).existsSync(); |
| 209 | 207 |
| 210 /// "Cleans" [dir]. If that directory already exists, it will be deleted. Then a | 208 /// "Cleans" [dir]. If that directory already exists, it will be deleted. Then a |
| 211 /// new empty directory will be created. Returns a [Future] that completes when | 209 /// new empty directory will be created. |
| 212 /// the new clean directory is created. | 210 void cleanDir(String dir) { |
| 213 Future<String> cleanDir(String dir) { | 211 if (dirExists(dir)) { |
| 214 return defer(() { | 212 // Delete it first. |
| 215 if (dirExists(dir)) { | 213 deleteDir(dir); |
| 216 // Delete it first. | 214 } else if (fileExists(dir)) { |
| 217 return deleteDir(dir).then((_) => createDir(dir)); | 215 // If there is a non-directory there (file or symlink), delete it. |
| 218 } | 216 deleteFile(dir); |
| 217 } |
| 219 | 218 |
| 220 if (fileExists(dir)) { | 219 // Just create it. |
| 221 // If there is a non-directory there (file or symlink), delete it. | 220 createDir(dir); |
| 222 deleteFile(dir); | |
| 223 return createDir(dir); | |
| 224 } | |
| 225 | |
| 226 // Just create it. | |
| 227 return createDir(dir); | |
| 228 }); | |
| 229 } | 221 } |
| 230 | 222 |
| 231 // TODO(nweiz): remove this when issue 9253 is fixed. | 223 /// Renames (i.e. moves) the directory [from] to [to]. |
| 232 /// Renames (i.e. moves) the directory [from] to [to]. Returns a [Future] with | 224 void renameDir(String from, String to) { |
| 233 /// the destination directory. | |
| 234 Future<String> renameDir(String from, String to) { | |
| 235 log.io("Renaming directory $from to $to."); | 225 log.io("Renaming directory $from to $to."); |
| 236 | 226 new Directory(from).renameSync(to); |
| 237 return _attemptRetryable(() => new Directory(from).rename(to)).then((dir) { | |
| 238 log.fine("Renamed directory $from to $to."); | |
| 239 return to; | |
| 240 }); | |
| 241 } | |
| 242 | |
| 243 /// On Windows, we sometimes get failures where the directory is still in use | |
| 244 /// when we try to do something with it. This is usually because the OS hasn't | |
| 245 /// noticed yet that a process using that directory has closed. To be a bit | |
| 246 /// more resilient, we wait and retry a few times. | |
| 247 /// | |
| 248 /// Takes a [callback] which returns a future for the operation being attempted. | |
| 249 /// If that future completes with an error, it will slepp and then [callback] | |
| 250 /// will be invoked again to retry the operation. It will try a few times before | |
| 251 /// giving up. | |
| 252 Future _attemptRetryable(Future callback()) { | |
| 253 // Only do lame retry logic on Windows. | |
| 254 if (Platform.operatingSystem != 'windows') return callback(); | |
| 255 | |
| 256 var attempts = 0; | |
| 257 makeAttempt(_) { | |
| 258 attempts++; | |
| 259 return callback().catchError((e) { | |
| 260 if (attempts >= 10) { | |
| 261 throw 'Could not complete operation. Gave up after $attempts attempts.'; | |
| 262 } | |
| 263 | |
| 264 // Wait a bit and try again. | |
| 265 log.fine("Operation failed, retrying (attempt $attempts)."); | |
| 266 return sleep(500).then(makeAttempt); | |
| 267 }); | |
| 268 } | |
| 269 | |
| 270 return makeAttempt(null); | |
| 271 } | 227 } |
| 272 | 228 |
| 273 /// Creates a new symlink at path [symlink] that points to [target]. Returns a | 229 /// Creates a new symlink at path [symlink] that points to [target]. Returns a |
| 274 /// [Future] which completes to the path to the symlink file. | 230 /// [Future] which completes to the path to the symlink file. |
| 275 /// | 231 /// |
| 276 /// If [relative] is true, creates a symlink with a relative path from the | 232 /// If [relative] is true, creates a symlink with a relative path from the |
| 277 /// symlink to the target. Otherwise, uses the [target] path unmodified. | 233 /// symlink to the target. Otherwise, uses the [target] path unmodified. |
| 278 /// | 234 /// |
| 279 /// Note that on Windows, only directories may be symlinked to. | 235 /// Note that on Windows, only directories may be symlinked to. |
| 280 Future<String> createSymlink(String target, String symlink, | 236 Future<String> createSymlink(String target, String symlink, |
| (...skipping 332 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 613 }).catchError((e) { | 569 }).catchError((e) { |
| 614 if (completer.isCompleted) return; | 570 if (completer.isCompleted) return; |
| 615 timer.cancel(); | 571 timer.cancel(); |
| 616 completer.completeError(e); | 572 completer.completeError(e); |
| 617 }); | 573 }); |
| 618 return completer.future; | 574 return completer.future; |
| 619 } | 575 } |
| 620 | 576 |
| 621 /// Creates a temporary directory and passes its path to [fn]. Once the [Future] | 577 /// Creates a temporary directory and passes its path to [fn]. Once the [Future] |
| 622 /// returned by [fn] completes, the temporary directory and all its contents | 578 /// returned by [fn] completes, the temporary directory and all its contents |
| 623 /// will be deleted. | 579 /// will be deleted. [fn] can also return `null`, in which case the temporary |
| 580 /// directory is deleted immediately afterwards. |
| 624 /// | 581 /// |
| 625 /// Returns a future that completes to the value that the future returned from | 582 /// Returns a future that completes to the value that the future returned from |
| 626 /// [fn] completes to. | 583 /// [fn] completes to. |
| 627 Future withTempDir(Future fn(String path)) { | 584 Future withTempDir(Future fn(String path)) { |
| 628 return defer(() { | 585 return defer(() { |
| 629 var tempDir = createTempDir(); | 586 var tempDir = createTempDir(); |
| 630 return fn(tempDir).whenComplete(() { | 587 return new Future.of(() => fn(tempDir)) |
| 631 return deleteDir(tempDir); | 588 .whenComplete(() => deleteDir(tempDir)); |
| 632 }); | |
| 633 }); | 589 }); |
| 634 } | 590 } |
| 635 | 591 |
| 636 /// Extracts a `.tar.gz` file from [stream] to [destination]. Returns whether | 592 /// Extracts a `.tar.gz` file from [stream] to [destination]. Returns whether |
| 637 /// or not the extraction was successful. | 593 /// or not the extraction was successful. |
| 638 Future<bool> extractTarGz(Stream<List<int>> stream, String destination) { | 594 Future<bool> extractTarGz(Stream<List<int>> stream, String destination) { |
| 639 log.fine("Extracting .tar.gz stream to $destination."); | 595 log.fine("Extracting .tar.gz stream to $destination."); |
| 640 | 596 |
| 641 if (Platform.operatingSystem == "windows") { | 597 if (Platform.operatingSystem == "windows") { |
| 642 return _extractTarGzWindows(stream, destination); | 598 return _extractTarGzWindows(stream, destination); |
| (...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 812 const PubProcessResult(this.stdout, this.stderr, this.exitCode); | 768 const PubProcessResult(this.stdout, this.stderr, this.exitCode); |
| 813 | 769 |
| 814 bool get success => exitCode == 0; | 770 bool get success => exitCode == 0; |
| 815 } | 771 } |
| 816 | 772 |
| 817 /// Gets a [Uri] for [uri], which can either already be one, or be a [String]. | 773 /// Gets a [Uri] for [uri], which can either already be one, or be a [String]. |
| 818 Uri _getUri(uri) { | 774 Uri _getUri(uri) { |
| 819 if (uri is Uri) return uri; | 775 if (uri is Uri) return uri; |
| 820 return Uri.parse(uri); | 776 return Uri.parse(uri); |
| 821 } | 777 } |
| OLD | NEW |