Index: utils/pub/io.dart |
diff --git a/utils/pub/io.dart b/utils/pub/io.dart |
index 859edb30683ee97da62e32911e9164f346c5fb86..84cc1aa6cc7c755c94a9a296394cb6b6e850d8ca 100644 |
--- a/utils/pub/io.dart |
+++ b/utils/pub/io.dart |
@@ -263,8 +263,9 @@ Future<Directory> createTempDir([dir = '']) { |
*/ |
Future<Directory> deleteDir(dir) { |
dir = _getDirectory(dir); |
- return log.ioAsync("delete directory ${dir.path}", |
- dir.delete(recursive: true)); |
+ |
+ return _attemptRetryable(() => log.ioAsync("delete directory ${dir.path}", |
+ dir.delete(recursive: true))); |
} |
/** |
@@ -349,37 +350,40 @@ Future<Directory> renameDir(from, String to) { |
from = _getDirectory(from); |
log.io("Renaming directory ${from.path} to $to."); |
- if (Platform.operatingSystem != 'windows') { |
- return from.rename(to).transform((dir) { |
- log.fine("Renamed directory ${from.path} to $to."); |
- return dir; |
- }); |
- } |
+ return _attemptRetryable(() => from.rename(to)).transform((dir) { |
+ log.fine("Renamed directory ${from.path} to $to."); |
+ return dir; |
+ }); |
+} |
+ |
+/// On Windows, we sometimes get failures where the directory is still in use |
+/// when we try to do something with it. This is usually because the OS hasn't |
+/// noticed yet that a process using that directory has closed. To be a bit |
+/// more resilient, we wait and retry a few times. |
+/// |
+/// Takes a [callback] which returns a future for the operation being attempted. |
+/// If that future completes with an error, it will slepp and then [callback] |
+/// will be invoked again to retry the operation. It will try a few times before |
+/// giving up. |
+Future _attemptRetryable(Future callback()) { |
+ // Only do lame retry logic on Windows. |
+ if (Platform.operatingSystem != 'windows') return callback(); |
- // On Windows, we sometimes get failures where the directory is still in use |
- // when we try to move it. To be a bit more resilient, we wait and retry a |
- // few times. |
var attempts = 0; |
- attemptRename(_) { |
+ makeAttempt(_) { |
attempts++; |
- return from.rename(to).transform((dir) { |
- log.fine("Renamed directory ${from.path} to $to."); |
- return dir; |
- }).transformException((e) { |
+ return callback().transformException((e) { |
if (attempts >= 10) { |
- throw 'Could not move directory "${from.path}" to "$to". Gave up ' |
- 'after $attempts attempts.'; |
+ throw 'Could not complete operation. Gave up after $attempts attempts.'; |
} |
// Wait a bit and try again. |
- log.fine("Rename ${from.path} failed, retrying (attempt $attempts)."); |
- return sleep(500).chain(attemptRename); |
+ log.fine("Operation failed, retrying (attempt $attempts)."); |
+ return sleep(500).chain(makeAttempt); |
}); |
- |
- return from; |
} |
- return attemptRename(null); |
+ return makeAttempt(null); |
} |
/** |
@@ -998,7 +1002,12 @@ InputStream createTarGz(List contents, {baseDir}) { |
// file and pass them in via --files-from for tar and -i@filename for 7zip. |
startProcess("tar", args).then((process) { |
pipeInputToInput(process.stdout, stream); |
- process.stderr.pipe(stderr, close: false); |
+ |
+ // Drain and discard 7zip's stderr. 7zip writes its normal output to |
+ // stderr. We don't want to show that since it's meaningless. |
+ // TODO(rnystrom): Should log this and display it if an actual error |
+ // occurs. |
+ consumeInputStream(process.stderr); |
}); |
return stream; |
} |
@@ -1024,7 +1033,11 @@ InputStream createTarGz(List contents, {baseDir}) { |
args = ["a", "unused", "-tgzip", "-so", tarFile]; |
return startProcess(command, args); |
}).chain((process) { |
- process.stderr.pipe(stderr, close: false); |
+ // Drain and discard 7zip's stderr. 7zip writes its normal output to |
+ // stderr. We don't want to show that since it's meaningless. |
+ // TODO(rnystrom): Should log this and display it if an actual error |
+ // occurs. |
+ consumeInputStream(process.stderr); |
return pipeInputToInput(process.stdout, stream); |
}); |
}); |