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 |