Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(28)

Side by Side Diff: utils/pub/io.dart

Issue 11522006: Get pub_lish tests running on Windows. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | utils/tests/pub/pub.status » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 /** 5 /**
6 * Helper functionality to make working with IO easier. 6 * Helper functionality to make working with IO easier.
7 */ 7 */
8 library io; 8 library io;
9 9
10 import 'dart:io'; 10 import 'dart:io';
(...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after
256 return log.ioAsync("create temp directory ${dir.path}", 256 return log.ioAsync("create temp directory ${dir.path}",
257 dir.createTemp()); 257 dir.createTemp());
258 } 258 }
259 259
260 /** 260 /**
261 * Asynchronously recursively deletes [dir], which can be a [String] or a 261 * Asynchronously recursively deletes [dir], which can be a [String] or a
262 * [Directory]. Returns a [Future] that completes when the deletion is done. 262 * [Directory]. Returns a [Future] that completes when the deletion is done.
263 */ 263 */
264 Future<Directory> deleteDir(dir) { 264 Future<Directory> deleteDir(dir) {
265 dir = _getDirectory(dir); 265 dir = _getDirectory(dir);
266 return log.ioAsync("delete directory ${dir.path}", 266
267 dir.delete(recursive: true)); 267 return _attemptRetryable(() => log.ioAsync("delete directory ${dir.path}",
268 dir.delete(recursive: true)));
268 } 269 }
269 270
270 /** 271 /**
271 * Asynchronously lists the contents of [dir], which can be a [String] directory 272 * Asynchronously lists the contents of [dir], which can be a [String] directory
272 * path or a [Directory]. If [recursive] is `true`, lists subdirectory contents 273 * path or a [Directory]. If [recursive] is `true`, lists subdirectory contents
273 * (defaults to `false`). If [includeHiddenFiles] is `true`, includes files and 274 * (defaults to `false`). If [includeHiddenFiles] is `true`, includes files and
274 * directories beginning with `.` (defaults to `false`). 275 * directories beginning with `.` (defaults to `false`).
275 */ 276 */
276 Future<List<String>> listDir(dir, 277 Future<List<String>> listDir(dir,
277 {bool recursive: false, bool includeHiddenFiles: false}) { 278 {bool recursive: false, bool includeHiddenFiles: false}) {
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
342 } 343 }
343 }); 344 });
344 } 345 }
345 346
346 /// Renames (i.e. moves) the directory [from] to [to]. Returns a [Future] with 347 /// Renames (i.e. moves) the directory [from] to [to]. Returns a [Future] with
347 /// the destination directory. 348 /// the destination directory.
348 Future<Directory> renameDir(from, String to) { 349 Future<Directory> renameDir(from, String to) {
349 from = _getDirectory(from); 350 from = _getDirectory(from);
350 log.io("Renaming directory ${from.path} to $to."); 351 log.io("Renaming directory ${from.path} to $to.");
351 352
352 if (Platform.operatingSystem != 'windows') { 353 return _attemptRetryable(() => from.rename(to)).transform((dir) {
353 return from.rename(to).transform((dir) { 354 log.fine("Renamed directory ${from.path} to $to.");
354 log.fine("Renamed directory ${from.path} to $to."); 355 return dir;
355 return dir; 356 });
357 }
358
359 /// On Windows, we sometimes get failures where the directory is still in use
360 /// when we try to do something with it. This is usually because the OS hasn't
361 /// noticed yet that a process using that directory has closed. To be a bit
362 /// more resilient, we wait and retry a few times.
363 ///
364 /// Takes a [callback] which returns a future for the operation being attempted.
365 /// If that future completes with an error, it will slepp and then [callback]
366 /// will be invoked again to retry the operation. It will try a few times before
367 /// giving up.
368 Future _attemptRetryable(Future callback()) {
369 // Only do lame retry logic on Windows.
370 if (Platform.operatingSystem != 'windows') return callback();
371
372 var attempts = 0;
373 makeAttempt(_) {
374 attempts++;
375 return callback().transformException((e) {
376 if (attempts >= 10) {
377 throw 'Could not complete operation. Gave up after $attempts attempts.';
378 }
379
380 // Wait a bit and try again.
381 log.fine("Operation failed, retrying (attempt $attempts).");
382 return sleep(500).chain(makeAttempt);
356 }); 383 });
357 } 384 }
358 385
359 // On Windows, we sometimes get failures where the directory is still in use 386 return makeAttempt(null);
360 // when we try to move it. To be a bit more resilient, we wait and retry a
361 // few times.
362 var attempts = 0;
363 attemptRename(_) {
364 attempts++;
365 return from.rename(to).transform((dir) {
366 log.fine("Renamed directory ${from.path} to $to.");
367 return dir;
368 }).transformException((e) {
369 if (attempts >= 10) {
370 throw 'Could not move directory "${from.path}" to "$to". Gave up '
371 'after $attempts attempts.';
372 }
373
374 // Wait a bit and try again.
375 log.fine("Rename ${from.path} failed, retrying (attempt $attempts).");
376 return sleep(500).chain(attemptRename);
377 });
378
379 return from;
380 }
381
382 return attemptRename(null);
383 } 387 }
384 388
385 /** 389 /**
386 * Creates a new symlink that creates an alias from [from] to [to], both of 390 * Creates a new symlink that creates an alias from [from] to [to], both of
387 * which can be a [String], [File], or [Directory]. Returns a [Future] which 391 * which can be a [String], [File], or [Directory]. Returns a [Future] which
388 * completes to the symlink file (i.e. [to]). 392 * completes to the symlink file (i.e. [to]).
389 */ 393 */
390 Future<File> createSymlink(from, to) { 394 Future<File> createSymlink(from, to) {
391 from = _getPath(from); 395 from = _getPath(from);
392 to = _getPath(to); 396 to = _getPath(to);
(...skipping 598 matching lines...) Expand 10 before | Expand all | Expand 10 after
991 }); 995 });
992 996
993 if (Platform.operatingSystem != "windows") { 997 if (Platform.operatingSystem != "windows") {
994 var args = ["--create", "--gzip", "--directory", baseDir]; 998 var args = ["--create", "--gzip", "--directory", baseDir];
995 args.addAll(contents.map(_getPath)); 999 args.addAll(contents.map(_getPath));
996 // TODO(nweiz): It's possible that enough command-line arguments will make 1000 // TODO(nweiz): It's possible that enough command-line arguments will make
997 // the process choke, so at some point we should save the arguments to a 1001 // the process choke, so at some point we should save the arguments to a
998 // file and pass them in via --files-from for tar and -i@filename for 7zip. 1002 // file and pass them in via --files-from for tar and -i@filename for 7zip.
999 startProcess("tar", args).then((process) { 1003 startProcess("tar", args).then((process) {
1000 pipeInputToInput(process.stdout, stream); 1004 pipeInputToInput(process.stdout, stream);
1001 process.stderr.pipe(stderr, close: false); 1005
1006 // Drain and discard 7zip's stderr. 7zip writes its normal output to
1007 // stderr. We don't want to show that since it's meaningless.
1008 // TODO(rnystrom): Should log this and display it if an actual error
1009 // occurs.
1010 consumeInputStream(process.stderr);
1002 }); 1011 });
1003 return stream; 1012 return stream;
1004 } 1013 }
1005 1014
1006 withTempDir((tempDir) { 1015 withTempDir((tempDir) {
1007 // Create the tar file. 1016 // Create the tar file.
1008 var tarFile = join(tempDir, "intermediate.tar"); 1017 var tarFile = join(tempDir, "intermediate.tar");
1009 var args = ["a", "-w$baseDir", tarFile]; 1018 var args = ["a", "-w$baseDir", tarFile];
1010 args.addAll(contents.map((entry) => '-i!"$entry"')); 1019 args.addAll(contents.map((entry) => '-i!"$entry"'));
1011 1020
1012 // Note: This line of code gets munged by create_sdk.py to be the correct 1021 // Note: This line of code gets munged by create_sdk.py to be the correct
1013 // relative path to 7zip in the SDK. 1022 // relative path to 7zip in the SDK.
1014 var pathTo7zip = '../../third_party/7zip/7za.exe'; 1023 var pathTo7zip = '../../third_party/7zip/7za.exe';
1015 var command = relativeToPub(pathTo7zip); 1024 var command = relativeToPub(pathTo7zip);
1016 1025
1017 // We're passing 'baseDir' both as '-w' and setting it as the working 1026 // We're passing 'baseDir' both as '-w' and setting it as the working
1018 // directory explicitly here intentionally. The former ensures that the 1027 // directory explicitly here intentionally. The former ensures that the
1019 // files added to the archive have the correct relative path in the archive. 1028 // files added to the archive have the correct relative path in the archive.
1020 // The latter enables relative paths in the "-i" args to be resolved. 1029 // The latter enables relative paths in the "-i" args to be resolved.
1021 return runProcess(command, args, workingDir: baseDir).chain((_) { 1030 return runProcess(command, args, workingDir: baseDir).chain((_) {
1022 // GZIP it. 7zip doesn't support doing both as a single operation. Send 1031 // GZIP it. 7zip doesn't support doing both as a single operation. Send
1023 // the output to stdout. 1032 // the output to stdout.
1024 args = ["a", "unused", "-tgzip", "-so", tarFile]; 1033 args = ["a", "unused", "-tgzip", "-so", tarFile];
1025 return startProcess(command, args); 1034 return startProcess(command, args);
1026 }).chain((process) { 1035 }).chain((process) {
1027 process.stderr.pipe(stderr, close: false); 1036 // Drain and discard 7zip's stderr. 7zip writes its normal output to
1037 // stderr. We don't want to show that since it's meaningless.
1038 // TODO(rnystrom): Should log this and display it if an actual error
1039 // occurs.
1040 consumeInputStream(process.stderr);
1028 return pipeInputToInput(process.stdout, stream); 1041 return pipeInputToInput(process.stdout, stream);
1029 }); 1042 });
1030 }); 1043 });
1031 return stream; 1044 return stream;
1032 } 1045 }
1033 1046
1034 /** 1047 /**
1035 * Exception thrown when an HTTP operation fails. 1048 * Exception thrown when an HTTP operation fails.
1036 */ 1049 */
1037 class PubHttpException implements Exception { 1050 class PubHttpException implements Exception {
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
1122 return new Directory(entry); 1135 return new Directory(entry);
1123 } 1136 }
1124 1137
1125 /** 1138 /**
1126 * Gets a [Uri] for [uri], which can either already be one, or be a [String]. 1139 * Gets a [Uri] for [uri], which can either already be one, or be a [String].
1127 */ 1140 */
1128 Uri _getUri(uri) { 1141 Uri _getUri(uri) {
1129 if (uri is Uri) return uri; 1142 if (uri is Uri) return uri;
1130 return new Uri.fromString(uri); 1143 return new Uri.fromString(uri);
1131 } 1144 }
OLDNEW
« no previous file with comments | « no previous file | utils/tests/pub/pub.status » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698