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

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

Issue 12079112: Make a bunch of stuff in pub synchronous. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 10 months 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
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 /// 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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698