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

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

Issue 13332009: Make listDir and createSymlink synchronous in pub. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 8 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) 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 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
118 /// Creates a temp directory whose name will be based on [dir] with a unique 118 /// Creates a temp directory whose name will be based on [dir] with a unique
119 /// suffix appended to it. If [dir] is not provided, a temp directory will be 119 /// suffix appended to it. If [dir] is not provided, a temp directory will be
120 /// created in a platform-dependent temporary location. Returns the path of the 120 /// created in a platform-dependent temporary location. Returns the path of the
121 /// created directory. 121 /// created directory.
122 String createTempDir([dir = '']) { 122 String createTempDir([dir = '']) {
123 var tempDir = new Directory(dir).createTempSync(); 123 var tempDir = new Directory(dir).createTempSync();
124 log.io("Created temp directory ${tempDir.path}"); 124 log.io("Created temp directory ${tempDir.path}");
125 return tempDir.path; 125 return tempDir.path;
126 } 126 }
127 127
128 /// Asynchronously lists the contents of [dir]. If [recursive] is `true`, lists 128 /// Lists the contents of [dir]. If [recursive] is `true`, lists subdirectory
129 /// subdirectory contents (defaults to `false`). If [includeHiddenFiles] is 129 /// contents (defaults to `false`). If [includeHiddenFiles] is `true`, includes
Bob Nystrom 2013/03/29 21:22:06 includeHiddenFiles -> includeHidden
nweiz 2013/03/29 22:09:02 That's out of scope of this change, but I'll add a
130 /// `true`, includes files and directories beginning with `.` (defaults to 130 /// files and directories beginning with `.` (defaults to `false`).
131 /// `false`).
132 /// 131 ///
133 /// If [dir] is a string, the returned paths are guaranteed to begin with it. 132 /// The returned paths are guaranteed to begin with [dir].
134 Future<List<String>> listDir(String dir, 133 List<String> listDir(String dir, {bool recursive: false,
135 {bool recursive: false, bool includeHiddenFiles: false}) { 134 bool includeHiddenFiles: false}) {
136 Future<List<String>> doList(String dir, Set<String> listedDirectories) { 135 List<String> doList(String dir, Set<String> listedDirectories) {
137 var contents = <String>[]; 136 var contents = <String>[];
138 var completer = new Completer<List<String>>();
139 137
140 // Avoid recursive symlinks. 138 // Avoid recursive symlinks.
141 var resolvedPath = new File(dir).fullPathSync(); 139 var resolvedPath = new File(dir).fullPathSync();
142 if (listedDirectories.contains(resolvedPath)) { 140 if (listedDirectories.contains(resolvedPath)) return [];
143 return new Future.immediate([]);
144 }
145 141
146 listedDirectories = new Set<String>.from(listedDirectories); 142 listedDirectories = new Set<String>.from(listedDirectories);
147 listedDirectories.add(resolvedPath); 143 listedDirectories.add(resolvedPath);
148 144
149 log.io("Listing directory $dir."); 145 log.io("Listing directory $dir.");
150 var lister = new Directory(dir).list();
151 146
152 var children = []; 147 var children = [];
153 lister.listen( 148 for (var entity in new Directory(dir).listSync()) {
154 (entity) { 149 if (entity is File) {
155 if (entity is File) { 150 var file = entity.path;
156 var file = entity.path; 151 if (!includeHiddenFiles && path.basename(file).startsWith('.')) {
Bob Nystrom 2013/03/29 21:22:06 These can probably be one-liners if you shorten it
nweiz 2013/03/29 22:09:02 See above.
157 if (!includeHiddenFiles && path.basename(file).startsWith('.')) { 152 continue;
158 return; 153 }
159 } 154 contents.add(file);
160 contents.add(file); 155 } else if (entity is Directory) {
161 } else if (entity is Directory) { 156 var file = entity.path;
162 var file = entity.path; 157 if (!includeHiddenFiles && path.basename(file).startsWith('.')) {
163 if (!includeHiddenFiles && path.basename(file).startsWith('.')) { 158 continue;
164 return; 159 }
165 } 160 contents.add(file);
166 contents.add(file); 161 // TODO(nweiz): don't manually recurse once issue 4794 is fixed.
167 // TODO(nweiz): don't manually recurse once issue 4794 is fixed. 162 // Note that once we remove the manual recursion, we'll need to
168 // Note that once we remove the manual recursion, we'll need to 163 // explicitly filter out files in hidden directories.
169 // explicitly filter out files in hidden directories. 164 if (recursive) {
170 if (recursive) { 165 children.addAll(doList(file, listedDirectories));
171 children.add(doList(file, listedDirectories)); 166 }
172 } 167 }
173 } 168 }
174 },
175 onDone: () {
176 // TODO(rnystrom): May need to sort here if it turns out
177 // onDir and onFile aren't guaranteed to be called in a
178 // certain order. So far, they seem to.
179 log.fine("Listed directory $dir:\n${contents.join('\n')}");
180 completer.complete(contents);
181 },
182 onError: (error) => completer.completeError(error));
183 169
184 return completer.future.then((contents) { 170 log.fine("Listed directory $dir:\n${contents.join('\n')}");
185 return Future.wait(children).then((childContents) { 171 contents.addAll(children);
186 contents.addAll(flatten(childContents)); 172 return contents;
187 return contents;
188 });
189 });
190 } 173 }
191 174
192 return doList(dir, new Set<String>()); 175 return doList(dir, new Set<String>());
193 } 176 }
194 177
195 /// Returns whether [dir] exists on the file system. This will return `true` for 178 /// Returns whether [dir] exists on the file system. This will return `true` for
196 /// a symlink only if that symlink is unbroken and points to a directory. 179 /// a symlink only if that symlink is unbroken and points to a directory.
197 bool dirExists(String dir) => new Directory(dir).existsSync(); 180 bool dirExists(String dir) => new Directory(dir).existsSync();
198 181
199 /// Deletes whatever's at [path], whether it's a file, directory, or symlink. If 182 /// Deletes whatever's at [path], whether it's a file, directory, or symlink. If
200 /// it's a directory, it will be deleted recursively. 183 /// it's a directory, it will be deleted recursively.
201 void deleteEntry(String path) { 184 void deleteEntry(String path) {
202 if (linkExists(path)) { 185 if (linkExists(path)) {
203 log.io("Deleting link $path."); 186 log.io("Deleting link $path.");
204 if (Platform.operatingSystem == 'windows') { 187 if (Platform.operatingSystem == 'windows') {
205 // TODO(nweiz): remove this when issue 9278 is fixed. 188 // TODO(nweiz): remove this when issue 9278 is fixed.
206 new Directory(path).deleteSync(); 189 new Directory(path).deleteSync();
207 } else { 190 } else {
208 new Link(path).deleteSync(); 191 new Link(path).deleteSync();
209 } 192 }
210 } else if (dirExists(path)) { 193 } else if (dirExists(path)) {
211 log.io("Deleting directory $path."); 194 log.io("Deleting directory $path.");
212 new Directory(path).deleteSync(recursive: true); 195 new Directory(path).deleteSync(recursive: true);
213 } else { 196 } else if (fileExists(path)) {
214 log.io("Deleting file $path."); 197 log.io("Deleting file $path.");
215 new File(path).deleteSync(); 198 new File(path).deleteSync();
216 } 199 }
217 } 200 }
218 201
219 /// "Cleans" [dir]. If that directory already exists, it will be deleted. Then a 202 /// "Cleans" [dir]. If that directory already exists, it will be deleted. Then a
220 /// new empty directory will be created. 203 /// new empty directory will be created.
221 void cleanDir(String dir) { 204 void cleanDir(String dir) {
222 if (entryExists(dir)) deleteEntry(dir); 205 if (entryExists(dir)) deleteEntry(dir);
223 createDir(dir); 206 createDir(dir);
224 } 207 }
225 208
226 /// Renames (i.e. moves) the directory [from] to [to]. 209 /// Renames (i.e. moves) the directory [from] to [to].
227 void renameDir(String from, String to) { 210 void renameDir(String from, String to) {
228 log.io("Renaming directory $from to $to."); 211 log.io("Renaming directory $from to $to.");
229 new Directory(from).renameSync(to); 212 new Directory(from).renameSync(to);
230 } 213 }
231 214
232 /// Creates a new symlink at path [symlink] that points to [target]. Returns a 215 /// Creates a new symlink at path [symlink] that points to [target]. Returns a
233 /// [Future] which completes to the path to the symlink file. 216 /// [Future] which completes to the path to the symlink file.
234 /// 217 ///
235 /// If [relative] is true, creates a symlink with a relative path from the 218 /// If [relative] is true, creates a symlink with a relative path from the
236 /// symlink to the target. Otherwise, uses the [target] path unmodified. 219 /// symlink to the target. Otherwise, uses the [target] path unmodified.
237 /// 220 ///
238 /// Note that on Windows, only directories may be symlinked to. 221 /// Note that on Windows, only directories may be symlinked to.
239 Future<String> createSymlink(String target, String symlink, 222 void createSymlink(String target, String symlink,
240 {bool relative: false}) { 223 {bool relative: false}) {
241 if (relative) { 224 if (relative) {
242 // Relative junction points are not supported on Windows. Instead, just 225 // Relative junction points are not supported on Windows. Instead, just
243 // make sure we have a clean absolute path because it will interpret a 226 // make sure we have a clean absolute path because it will interpret a
244 // relative path to be relative to the cwd, not the symlink, and will be 227 // relative path to be relative to the cwd, not the symlink, and will be
245 // confused by forward slashes. 228 // confused by forward slashes.
246 if (Platform.operatingSystem == 'windows') { 229 if (Platform.operatingSystem == 'windows') {
247 target = path.normalize(path.absolute(target)); 230 target = path.normalize(path.absolute(target));
248 } else { 231 } else {
249 target = path.normalize( 232 target = path.normalize(
250 path.relative(target, from: path.dirname(symlink))); 233 path.relative(target, from: path.dirname(symlink)));
251 } 234 }
252 } 235 }
253 236
254 log.fine("Creating $symlink pointing to $target"); 237 log.fine("Creating $symlink pointing to $target");
255 238 new Link(symlink).createSync(target);
Bob Nystrom 2013/03/29 21:22:06 Nice!
256 var command = 'ln';
257 var args = ['-s', target, symlink];
258
259 if (Platform.operatingSystem == 'windows') {
260 // Call mklink on Windows to create an NTFS junction point. Only works on
261 // Vista or later. (Junction points are available earlier, but the "mklink"
262 // command is not.) I'm using a junction point (/j) here instead of a soft
263 // link (/d) because the latter requires some privilege shenanigans that
264 // I'm not sure how to specify from the command line.
265 command = 'mklink';
266 args = ['/j', symlink, target];
267 }
268
269 // TODO(rnystrom): Check exit code and output?
270 return runProcess(command, args).then((result) => symlink);
271 } 239 }
272 240
273 /// Creates a new symlink that creates an alias at [symlink] that points to the 241 /// Creates a new symlink that creates an alias at [symlink] that points to the
274 /// `lib` directory of package [target]. Returns a [Future] which completes to 242 /// `lib` directory of package [target]. If [target] does not have a `lib`
275 /// the path to the symlink file. If [target] does not have a `lib` directory, 243 /// directory, this shows a warning if appropriate and then does nothing.
276 /// this shows a warning if appropriate and then does nothing.
277 /// 244 ///
278 /// If [relative] is true, creates a symlink with a relative path from the 245 /// If [relative] is true, creates a symlink with a relative path from the
279 /// symlink to the target. Otherwise, uses the [target] path unmodified. 246 /// symlink to the target. Otherwise, uses the [target] path unmodified.
280 Future<String> createPackageSymlink(String name, String target, String symlink, 247 void createPackageSymlink(String name, String target, String symlink,
281 {bool isSelfLink: false, bool relative: false}) { 248 {bool isSelfLink: false, bool relative: false}) {
282 return defer(() { 249 // See if the package has a "lib" directory.
283 // See if the package has a "lib" directory. 250 target = path.join(target, 'lib');
284 target = path.join(target, 'lib'); 251 log.fine("Creating ${isSelfLink ? "self" : ""}link for package '$name'.");
285 log.fine("Creating ${isSelfLink ? "self" : ""}link for package '$name'."); 252 if (dirExists(target)) {
286 if (dirExists(target)) { 253 createSymlink(target, symlink, relative: relative);
287 return createSymlink(target, symlink, relative: relative); 254 return;
288 } 255 }
289 256
290 // It's OK for the self link (i.e. the root package) to not have a lib 257 // It's OK for the self link (i.e. the root package) to not have a lib
291 // directory since it may just be a leaf application that only has 258 // directory since it may just be a leaf application that only has
292 // code in bin or web. 259 // code in bin or web.
293 if (!isSelfLink) { 260 if (!isSelfLink) {
294 log.warning('Warning: Package "$name" does not have a "lib" directory so ' 261 log.warning('Warning: Package "$name" does not have a "lib" directory so '
295 'you will not be able to import any libraries from it.'); 262 'you will not be able to import any libraries from it.');
296 } 263 }
297
298 return symlink;
299 });
300 } 264 }
301 265
302 /// Resolves [target] relative to the location of pub.dart. 266 /// Resolves [target] relative to the location of pub.dart.
303 String relativeToPub(String target) { 267 String relativeToPub(String target) {
304 var scriptPath = new File(new Options().script).fullPathSync(); 268 var scriptPath = new File(new Options().script).fullPathSync();
305 269
306 // Walk up until we hit the "util(s)" directory. This lets us figure out where 270 // Walk up until we hit the "util(s)" directory. This lets us figure out where
307 // we are if this function is called from pub.dart, or one of the tests, 271 // we are if this function is called from pub.dart, or one of the tests,
308 // which also live under "utils", or from the SDK where pub is in "util". 272 // which also live under "utils", or from the SDK where pub is in "util".
309 var utilDir = path.dirname(scriptPath); 273 var utilDir = path.dirname(scriptPath);
(...skipping 334 matching lines...) Expand 10 before | Expand all | Expand 10 after
644 // first we un-gzip it to a tar file. 608 // first we un-gzip it to a tar file.
645 // Note: Setting the working directory instead of passing in a full file 609 // Note: Setting the working directory instead of passing in a full file
646 // path because 7zip says "A full path is not allowed here." 610 // path because 7zip says "A full path is not allowed here."
647 return runProcess(command, ['e', 'data.tar.gz'], workingDir: tempDir); 611 return runProcess(command, ['e', 'data.tar.gz'], workingDir: tempDir);
648 }).then((result) { 612 }).then((result) {
649 if (result.exitCode != 0) { 613 if (result.exitCode != 0) {
650 throw 'Could not un-gzip (exit code ${result.exitCode}). Error:\n' 614 throw 'Could not un-gzip (exit code ${result.exitCode}). Error:\n'
651 '${result.stdout.join("\n")}\n' 615 '${result.stdout.join("\n")}\n'
652 '${result.stderr.join("\n")}'; 616 '${result.stderr.join("\n")}';
653 } 617 }
618
654 // Find the tar file we just created since we don't know its name. 619 // Find the tar file we just created since we don't know its name.
655 return listDir(tempDir); 620 var tarFile = listDir(tempDir).firstWhere((file) =>
Bob Nystrom 2013/03/29 21:22:06 Nit, but how about moving (file) => to the next li
nweiz 2013/03/29 22:09:02 Done.
656 }).then((files) { 621 path.extension(file) == '.tar', orElse: () {
657 var tarFile; 622 throw 'The gzip file did not contain a tar file.';
658 for (var file in files) { 623 });
659 if (path.extension(file) == '.tar') {
660 tarFile = file;
661 break;
662 }
663 }
664
665 if (tarFile == null) throw 'The gzip file did not contain a tar file.';
666 624
667 // Untar the archive into the destination directory. 625 // Untar the archive into the destination directory.
668 return runProcess(command, ['x', tarFile], workingDir: destination); 626 return runProcess(command, ['x', tarFile], workingDir: destination);
669 }).then((result) { 627 }).then((result) {
670 if (result.exitCode != 0) { 628 if (result.exitCode != 0) {
671 throw 'Could not un-tar (exit code ${result.exitCode}). Error:\n' 629 throw 'Could not un-tar (exit code ${result.exitCode}). Error:\n'
672 '${result.stdout.join("\n")}\n' 630 '${result.stdout.join("\n")}\n'
673 '${result.stderr.join("\n")}'; 631 '${result.stderr.join("\n")}';
674 } 632 }
675 return true; 633 return true;
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
771 const PubProcessResult(this.stdout, this.stderr, this.exitCode); 729 const PubProcessResult(this.stdout, this.stderr, this.exitCode);
772 730
773 bool get success => exitCode == 0; 731 bool get success => exitCode == 0;
774 } 732 }
775 733
776 /// Gets a [Uri] for [uri], which can either already be one, or be a [String]. 734 /// Gets a [Uri] for [uri], which can either already be one, or be a [String].
777 Uri _getUri(uri) { 735 Uri _getUri(uri) {
778 if (uri is Uri) return uri; 736 if (uri is Uri) return uri;
779 return Uri.parse(uri); 737 return Uri.parse(uri);
780 } 738 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698