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

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

Issue 11475046: Add Bob's path library to pub. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Code review changes 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 | « utils/pub/entrypoint.dart ('k') | utils/pub/path.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) 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';
11 import 'dart:isolate'; 11 import 'dart:isolate';
12 import 'dart:uri'; 12 import 'dart:uri';
13 13
14 // TODO(nweiz): Make this import better. 14 // TODO(nweiz): Make this import better.
15 import '../../pkg/http/lib/http.dart' as http; 15 import '../../pkg/http/lib/http.dart' as http;
16 import 'curl_client.dart';
16 import 'utils.dart'; 17 import 'utils.dart';
17 import 'curl_client.dart'; 18 import 'path.dart' as path;
18 19
19 bool _isGitInstalledCache; 20 bool _isGitInstalledCache;
20 21
21 /// The cached Git command. 22 /// The cached Git command.
22 String _gitCommandCache; 23 String _gitCommandCache;
23 24
24 /** Gets the current working directory. */
25 String get currentWorkingDir => new File('.').fullPathSync();
26
27 final NEWLINE_PATTERN = new RegExp("\r\n?|\n\r?"); 25 final NEWLINE_PATTERN = new RegExp("\r\n?|\n\r?");
28 26
29 /** 27 /**
30 * Prints the given string to `stderr` on its own line. 28 * Prints the given string to `stderr` on its own line.
31 */ 29 */
32 void printError(value) { 30 void printError(value) {
33 stderr.writeString(value.toString()); 31 stderr.writeString(value.toString());
34 stderr.writeString('\n'); 32 stderr.writeString('\n');
35 } 33 }
36 34
37 35
38 /** 36 /**
39 * Joins a number of path string parts into a single path. Handles 37 * Joins a number of path string parts into a single path. Handles
40 * platform-specific path separators. Parts can be [String], [Directory], or 38 * platform-specific path separators. Parts can be [String], [Directory], or
41 * [File] objects. 39 * [File] objects.
42 */ 40 */
43 String join(part1, [part2, part3, part4]) { 41 String join(part1, [part2, part3, part4]) {
44 final parts = sanitizePath(part1).split('/'); 42 part1 = _getPath(part1);
43 if (part2 != null) part2 = _getPath(part2);
44 if (part3 != null) part3 = _getPath(part3);
45 if (part4 != null) part4 = _getPath(part4);
45 46
46 for (final part in [part2, part3, part4]) { 47 // TODO(nweiz): Don't use "?part" in path.dart.
47 if (part == null) continue; 48 if (part4 != null) {
48 49 return path.join(part1, part2, part3, part4);
49 for (final piece in _getPath(part).split('/')) { 50 } else if (part3 != null) {
50 if (piece == '..' && parts.length > 0 && 51 return path.join(part1, part2, part3);
51 parts.last != '.' && parts.last != '..') { 52 } else if (part2 != null) {
52 parts.removeLast(); 53 return path.join(part1, part2);
53 } else if (piece != '') { 54 } else {
54 if (parts.length > 0 && parts.last == '.') { 55 return path.join(part1);
55 parts.removeLast();
56 }
57 parts.add(piece);
58 }
59 }
60 } 56 }
61
62 return Strings.join(parts, Platform.pathSeparator);
63 } 57 }
64 58
65 /// Splits [path] into its individual components. 59 /// Gets the basename, the file name without any leading directory path, for
66 List<String> splitPath(path) => sanitizePath(path).split('/'); 60 /// [file], which can either be a [String], [File], or [Directory].
61 String basename(file) => path.filename(_getPath(file));
67 62
68 /** 63 // TODO(nweiz): move this into path.dart.
69 * Gets the basename, the file name without any leading directory path, for 64 /// Gets the the leading directory path for [file], which can either be a
70 * [file], which can either be a [String], [File], or [Directory]. 65 /// [String], [File], or [Directory].
71 */ 66 String dirname(file) {
72 // TODO(rnystrom): Copied from file_system (so that we don't have to add 67 file = _sanitizePath(file);
73 // file_system to the SDK). Should unify.
74 String basename(file) {
75 file = sanitizePath(file);
76 68
77 int lastSlash = file.lastIndexOf('/', file.length); 69 int lastSlash = file.lastIndexOf('/', file.length);
78 if (lastSlash == -1) { 70 if (lastSlash == -1) {
79 return file;
80 } else {
81 return file.substring(lastSlash + 1);
82 }
83 }
84
85 /**
86 * Gets the the leading directory path for [file], which can either be a
87 * [String], [File], or [Directory].
88 */
89 // TODO(nweiz): Copied from file_system (so that we don't have to add
90 // file_system to the SDK). Should unify.
91 String dirname(file) {
92 file = sanitizePath(file);
93
94 int lastSlash = file.lastIndexOf('/', file.length);
95 if (lastSlash == -1) {
96 return '.'; 71 return '.';
97 } else { 72 } else {
98 return file.substring(0, lastSlash); 73 return file.substring(0, lastSlash);
99 } 74 }
100 } 75 }
101 76
77 // TODO(nweiz): move this into path.dart.
78 /// Splits [path] into its individual components.
79 List<String> splitPath(path) => _sanitizePath(path).split('/');
80
102 /// Returns whether or not [entry] is nested somewhere within [dir]. This just 81 /// Returns whether or not [entry] is nested somewhere within [dir]. This just
103 /// performs a path comparison; it doesn't look at the actual filesystem. 82 /// performs a path comparison; it doesn't look at the actual filesystem.
104 bool isBeneath(entry, dir) => 83 bool isBeneath(entry, dir) {
105 sanitizePath(entry).startsWith('${sanitizePath(dir)}/'); 84 var relative = relativeTo(entry, dir);
85 return !path.isAbsolute(relative) && !relative.startsWith('..');
86 }
87
88 // TODO(nweiz): move this into path.dart.
89 /// Returns the path to [target] from [base].
90 String relativeTo(target, base) =>
91 new path.Builder(root: base).relative(target);
106 92
107 /** 93 /**
108 * Asynchronously determines if [path], which can be a [String] file path, a 94 * Asynchronously determines if [path], which can be a [String] file path, a
109 * [File], or a [Directory] exists on the file system. Returns a [Future] that 95 * [File], or a [Directory] exists on the file system. Returns a [Future] that
110 * completes with the result. 96 * completes with the result.
111 */ 97 */
112 Future<bool> exists(path) { 98 Future<bool> exists(path) {
113 path = _getPath(path); 99 path = _getPath(path);
114 return Futures.wait([fileExists(path), dirExists(path)]).transform((results) { 100 return Futures.wait([fileExists(path), dirExists(path)]).transform((results) {
115 return results[0] || results[1]; 101 return results[0] || results[1];
(...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after
392 'Warning: Package "$name" does not have a "lib" directory so you ' 378 'Warning: Package "$name" does not have a "lib" directory so you '
393 'will not be able to import any libraries from it.'); 379 'will not be able to import any libraries from it.');
394 } 380 }
395 381
396 return new Future.immediate(to); 382 return new Future.immediate(to);
397 }); 383 });
398 } 384 }
399 385
400 /// Given [entry] which may be a [String], [File], or [Directory] relative to 386 /// Given [entry] which may be a [String], [File], or [Directory] relative to
401 /// the current working directory, returns its full canonicalized path. 387 /// the current working directory, returns its full canonicalized path.
402 String getFullPath(entry) { 388 String getFullPath(entry) => path.absolute(_getPath(entry));
403 var path = _getPath(entry);
404
405 // Don't do anything if it's already absolute.
406 if (isAbsolute(path)) return path;
407
408 // Using Path.join here instead of File().fullPathSync() because the former
409 // does not require an actual file to exist at that path.
410 return new Path.fromNative(currentWorkingDir).join(new Path(path))
411 .toNativePath();
412 }
413 389
414 /// Returns whether or not [entry] is an absolute path. 390 /// Returns whether or not [entry] is an absolute path.
415 bool isAbsolute(entry) => _splitAbsolute(entry).first != null; 391 bool isAbsolute(entry) => path.isAbsolute(_getPath(entry));
416 392
417 /// Splits [entry] into two components: the absolute path prefix and the 393 /// Resolves [target] relative to the location of pub.dart.
418 /// remaining path. Takes into account Windows' quirky absolute paths syntaxes. 394 String relativeToPub(String target) {
419 Pair<String, String> _splitAbsolute(entry) {
420 var path = _getPath(entry);
421
422 if (Platform.operatingSystem != 'windows') {
423 return !path.startsWith('/') ? new Pair(null, path)
424 : new Pair('/', path.substring(1));
425 }
426
427 // An absolute path on Windows is either UNC (two leading backslashes),
428 // or a drive letter followed by a colon and a slash.
429 var match = new RegExp(r'^(\\\\|[a-zA-Z]:[/\\])').firstMatch(path);
430 return match == null ? new Pair(null, path)
431 : new Pair(match.group(0), path.substring(match.end));
432 }
433
434 /// Resolves [path] relative to the location of pub.dart.
435 String relativeToPub(String path) {
436 var scriptPath = new File(new Options().script).fullPathSync(); 395 var scriptPath = new File(new Options().script).fullPathSync();
437 396
438 // Walk up until we hit the "util(s)" directory. This lets us figure out where 397 // Walk up until we hit the "util(s)" directory. This lets us figure out where
439 // we are if this function is called from pub.dart, or one of the tests, 398 // we are if this function is called from pub.dart, or one of the tests,
440 // which also live under "utils", or from the SDK where pub is in "util". 399 // which also live under "utils", or from the SDK where pub is in "util".
441 var utilDir = new Path.fromNative(scriptPath).directoryPath; 400 var utilDir = dirname(scriptPath);
442 while (utilDir.filename != 'utils' && utilDir.filename != 'util') { 401 while (basename(utilDir) != 'utils' && basename(utilDir) != 'util') {
443 if (utilDir.filename == '') throw 'Could not find path to pub.'; 402 if (basename(utilDir) == '') throw 'Could not find path to pub.';
444 utilDir = utilDir.directoryPath; 403 utilDir = dirname(utilDir);
445 } 404 }
446 405
447 return utilDir.append('pub').append(path).canonicalize().toNativePath(); 406 return path.normalize(join(utilDir, 'pub', target));
448 } 407 }
449 408
450 /// A StringInputStream reading from stdin. 409 /// A StringInputStream reading from stdin.
451 final _stringStdin = new StringInputStream(stdin); 410 final _stringStdin = new StringInputStream(stdin);
452 411
453 /// Returns a single line read from a [StringInputStream]. By default, reads 412 /// Returns a single line read from a [StringInputStream]. By default, reads
454 /// from stdin. 413 /// from stdin.
455 /// 414 ///
456 /// A [StringInputStream] passed to this should have no callbacks registered. 415 /// A [StringInputStream] passed to this should have no callbacks registered.
457 Future<String> readLine([StringInputStream stream]) { 416 Future<String> readLine([StringInputStream stream]) {
(...skipping 397 matching lines...) Expand 10 before | Expand all | Expand 10 after
855 if (result.exitCode != 0) { 814 if (result.exitCode != 0) {
856 throw 'Could not un-gzip (exit code ${result.exitCode}). Error:\n' 815 throw 'Could not un-gzip (exit code ${result.exitCode}). Error:\n'
857 '${Strings.join(result.stdout, "\n")}\n' 816 '${Strings.join(result.stdout, "\n")}\n'
858 '${Strings.join(result.stderr, "\n")}'; 817 '${Strings.join(result.stderr, "\n")}';
859 } 818 }
860 // Find the tar file we just created since we don't know its name. 819 // Find the tar file we just created since we don't know its name.
861 return listDir(tempDir); 820 return listDir(tempDir);
862 }).chain((files) { 821 }).chain((files) {
863 var tarFile; 822 var tarFile;
864 for (var file in files) { 823 for (var file in files) {
865 if (new Path(file).extension == 'tar') { 824 if (path.extension(file) == '.tar') {
866 tarFile = file; 825 tarFile = file;
867 break; 826 break;
868 } 827 }
869 } 828 }
870 829
871 if (tarFile == null) throw 'The gzip file did not contain a tar file.'; 830 if (tarFile == null) throw 'The gzip file did not contain a tar file.';
872 831
873 // Untar the archive into the destination directory. 832 // Untar the archive into the destination directory.
874 return runProcess(command, ['x', tarFile], workingDir: destination); 833 return runProcess(command, ['x', tarFile], workingDir: destination);
875 }).chain((result) { 834 }).chain((result) {
(...skipping 11 matching lines...) Expand all
887 846
888 /// Create a .tar.gz archive from a list of entries. Each entry can be a 847 /// Create a .tar.gz archive from a list of entries. Each entry can be a
889 /// [String], [Directory], or [File] object. The root of the archive is 848 /// [String], [Directory], or [File] object. The root of the archive is
890 /// considered to be [baseDir], which defaults to the current working directory. 849 /// considered to be [baseDir], which defaults to the current working directory.
891 /// Returns an [InputStream] that will emit the contents of the archive. 850 /// Returns an [InputStream] that will emit the contents of the archive.
892 InputStream createTarGz(List contents, {baseDir}) { 851 InputStream createTarGz(List contents, {baseDir}) {
893 // TODO(nweiz): Propagate errors to the returned stream (including non-zero 852 // TODO(nweiz): Propagate errors to the returned stream (including non-zero
894 // exit codes). See issue 3657. 853 // exit codes). See issue 3657.
895 var stream = new ListInputStream(); 854 var stream = new ListInputStream();
896 855
897 if (baseDir == null) baseDir = currentWorkingDir; 856 if (baseDir == null) baseDir = path.current;
898 baseDir = getFullPath(baseDir); 857 baseDir = getFullPath(baseDir);
899 contents = contents.map((entry) { 858 contents = contents.map((entry) {
900 entry = getFullPath(entry); 859 entry = getFullPath(entry);
901 if (!isBeneath(entry, baseDir)) { 860 if (!isBeneath(entry, baseDir)) {
902 throw 'Entry $entry is not inside $baseDir.'; 861 throw 'Entry $entry is not inside $baseDir.';
903 } 862 }
904 return new Path.fromNative(entry).relativeTo(new Path.fromNative(baseDir)) 863 return relativeTo(entry, baseDir);
905 .toNativePath();
906 }); 864 });
907 865
908 if (Platform.operatingSystem != "windows") { 866 if (Platform.operatingSystem != "windows") {
909 var args = ["--create", "--gzip", "--directory", baseDir]; 867 var args = ["--create", "--gzip", "--directory", baseDir];
910 args.addAll(contents.map(_getPath)); 868 args.addAll(contents.map(_getPath));
911 // TODO(nweiz): It's possible that enough command-line arguments will make 869 // TODO(nweiz): It's possible that enough command-line arguments will make
912 // the process choke, so at some point we should save the arguments to a 870 // the process choke, so at some point we should save the arguments to a
913 // file and pass them in via --files-from for tar and -i@filename for 7zip. 871 // file and pass them in via --files-from for tar and -i@filename for 7zip.
914 startProcess("tar", args).then((process) { 872 startProcess("tar", args).then((process) {
915 pipeInputToInput(process.stdout, stream); 873 pipeInputToInput(process.stdout, stream);
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
989 */ 947 */
990 String _getPath(entry) { 948 String _getPath(entry) {
991 if (entry is String) return entry; 949 if (entry is String) return entry;
992 if (entry is File) return entry.name; 950 if (entry is File) return entry.name;
993 if (entry is Directory) return entry.path; 951 if (entry is Directory) return entry.path;
994 throw 'Entry $entry is not a supported type.'; 952 throw 'Entry $entry is not a supported type.';
995 } 953 }
996 954
997 /// Gets the path string for [entry], normalizing backslashes to forward slashes 955 /// Gets the path string for [entry], normalizing backslashes to forward slashes
998 /// on Windows. 956 /// on Windows.
999 String sanitizePath(entry) { 957 String _sanitizePath(entry) {
1000 entry = _getPath(entry); 958 entry = _getPath(entry);
1001 if (Platform.operatingSystem != 'windows') return entry; 959 if (Platform.operatingSystem != 'windows') return entry;
1002 960
1003 var split = _splitAbsolute(entry); 961 var split = _splitAbsolute(entry);
1004 if (split.first == null) return split.last.replaceAll('\\', '/'); 962 if (split.first == null) return split.last.replaceAll('\\', '/');
1005 963
1006 // For absolute Windows paths, we don't want the prefix (either "\\" or e.g. 964 // For absolute Windows paths, we don't want the prefix (either "\\" or e.g.
1007 // "C:\") to look like a normal path component, so we ensure that it only 965 // "C:\") to look like a normal path component, so we ensure that it only
1008 // contains backslashes. 966 // contains backslashes.
1009 return '${split.first.replaceAll('/', '\\')}' 967 return '${split.first.replaceAll('/', '\\')}'
1010 '${split.last.replaceAll('\\', '/')}'; 968 '${split.last.replaceAll('\\', '/')}';
1011 } 969 }
1012 970
971 // TODO(nweiz): Add something like this to path.dart.
972 /// Splits [entry] into two components: the absolute path prefix and the
973 /// remaining path. Takes into account Windows' quirky absolute paths syntaxes.
974 Pair<String, String> _splitAbsolute(entry) {
975 var path = _getPath(entry);
976
977 if (Platform.operatingSystem != 'windows') {
978 return !path.startsWith('/') ? new Pair(null, path)
979 : new Pair('/', path.substring(1));
980 }
981
982 // An absolute path on Windows is either UNC (two leading backslashes),
983 // or a drive letter followed by a colon and a slash.
984 var match = new RegExp(r'^(\\\\|[a-zA-Z]:[/\\])').firstMatch(path);
985 return match == null ? new Pair(null, path)
986 : new Pair(match.group(0), path.substring(match.end));
987 }
988
1013 /** 989 /**
1014 * Gets a [Directory] for [entry], which can either already be one, or be a 990 * Gets a [Directory] for [entry], which can either already be one, or be a
1015 * [String]. 991 * [String].
1016 */ 992 */
1017 Directory _getDirectory(entry) { 993 Directory _getDirectory(entry) {
1018 if (entry is Directory) return entry; 994 if (entry is Directory) return entry;
1019 return new Directory(entry); 995 return new Directory(entry);
1020 } 996 }
1021 997
1022 /** 998 /**
1023 * Gets a [Uri] for [uri], which can either already be one, or be a [String]. 999 * Gets a [Uri] for [uri], which can either already be one, or be a [String].
1024 */ 1000 */
1025 Uri _getUri(uri) { 1001 Uri _getUri(uri) {
1026 if (uri is Uri) return uri; 1002 if (uri is Uri) return uri;
1027 return new Uri.fromString(uri); 1003 return new Uri.fromString(uri);
1028 } 1004 }
OLDNEW
« no previous file with comments | « utils/pub/entrypoint.dart ('k') | utils/pub/path.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698