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

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

Powered by Google App Engine
This is Rietveld 408576698