mirror of
https://github.com/dart-lang/sdk
synced 2024-11-05 18:22:09 +00:00
Add Bob's path library to pub.
BUG=7215 Review URL: https://codereview.chromium.org//11475046 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@15881 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
parent
4b0a76bc2d
commit
f813520a0a
10 changed files with 1230 additions and 106 deletions
|
@ -35,7 +35,7 @@ class CurlClient extends http.BaseClient {
|
|||
|
||||
var requestStream = request.finalize();
|
||||
return withTempDir((tempDir) {
|
||||
var headerFile = new Path(tempDir).append("curl-headers").toNativePath();
|
||||
var headerFile = join(tempDir, "curl-headers");
|
||||
var arguments = _argumentsForRequest(request, headerFile);
|
||||
log.process(executable, arguments);
|
||||
var process;
|
||||
|
|
|
@ -10,9 +10,9 @@ import 'log.dart' as log;
|
|||
import 'package.dart';
|
||||
import 'root_source.dart';
|
||||
import 'system_cache.dart';
|
||||
import 'utils.dart';
|
||||
import 'version.dart';
|
||||
import 'version_solver.dart';
|
||||
import 'utils.dart';
|
||||
|
||||
/**
|
||||
* Pub operates over a directed graph of dependencies that starts at a root
|
||||
|
|
|
@ -15,6 +15,7 @@ import 'dart:uri';
|
|||
import '../../pkg/http/lib/http.dart' as http;
|
||||
import 'curl_client.dart';
|
||||
import 'log.dart' as log;
|
||||
import 'path.dart' as path;
|
||||
import 'utils.dart';
|
||||
|
||||
bool _isGitInstalledCache;
|
||||
|
@ -22,9 +23,6 @@ bool _isGitInstalledCache;
|
|||
/// The cached Git command.
|
||||
String _gitCommandCache;
|
||||
|
||||
/** Gets the current working directory. */
|
||||
String get currentWorkingDir => new File('.').fullPathSync();
|
||||
|
||||
final NEWLINE_PATTERN = new RegExp("\r\n?|\n\r?");
|
||||
|
||||
/**
|
||||
|
@ -33,55 +31,32 @@ final NEWLINE_PATTERN = new RegExp("\r\n?|\n\r?");
|
|||
* [File] objects.
|
||||
*/
|
||||
String join(part1, [part2, part3, part4]) {
|
||||
final parts = sanitizePath(part1).split('/');
|
||||
part1 = _getPath(part1);
|
||||
if (part2 != null) part2 = _getPath(part2);
|
||||
if (part3 != null) part3 = _getPath(part3);
|
||||
if (part4 != null) part4 = _getPath(part4);
|
||||
|
||||
for (final part in [part2, part3, part4]) {
|
||||
if (part == null) continue;
|
||||
|
||||
for (final piece in _getPath(part).split('/')) {
|
||||
if (piece == '..' && parts.length > 0 &&
|
||||
parts.last != '.' && parts.last != '..') {
|
||||
parts.removeLast();
|
||||
} else if (piece != '') {
|
||||
if (parts.length > 0 && parts.last == '.') {
|
||||
parts.removeLast();
|
||||
}
|
||||
parts.add(piece);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Strings.join(parts, Platform.pathSeparator);
|
||||
}
|
||||
|
||||
/// Splits [path] into its individual components.
|
||||
List<String> splitPath(path) => sanitizePath(path).split('/');
|
||||
|
||||
/**
|
||||
* Gets the basename, the file name without any leading directory path, for
|
||||
* [file], which can either be a [String], [File], or [Directory].
|
||||
*/
|
||||
// TODO(rnystrom): Copied from file_system (so that we don't have to add
|
||||
// file_system to the SDK). Should unify.
|
||||
String basename(file) {
|
||||
file = sanitizePath(file);
|
||||
|
||||
int lastSlash = file.lastIndexOf('/', file.length);
|
||||
if (lastSlash == -1) {
|
||||
return file;
|
||||
// TODO(nweiz): Don't use "?part" in path.dart.
|
||||
if (part4 != null) {
|
||||
return path.join(part1, part2, part3, part4);
|
||||
} else if (part3 != null) {
|
||||
return path.join(part1, part2, part3);
|
||||
} else if (part2 != null) {
|
||||
return path.join(part1, part2);
|
||||
} else {
|
||||
return file.substring(lastSlash + 1);
|
||||
return path.join(part1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the the leading directory path for [file], which can either be a
|
||||
* [String], [File], or [Directory].
|
||||
*/
|
||||
// TODO(nweiz): Copied from file_system (so that we don't have to add
|
||||
// file_system to the SDK). Should unify.
|
||||
/// Gets the basename, the file name without any leading directory path, for
|
||||
/// [file], which can either be a [String], [File], or [Directory].
|
||||
String basename(file) => path.filename(_getPath(file));
|
||||
|
||||
// TODO(nweiz): move this into path.dart.
|
||||
/// Gets the the leading directory path for [file], which can either be a
|
||||
/// [String], [File], or [Directory].
|
||||
String dirname(file) {
|
||||
file = sanitizePath(file);
|
||||
file = _sanitizePath(file);
|
||||
|
||||
int lastSlash = file.lastIndexOf('/', file.length);
|
||||
if (lastSlash == -1) {
|
||||
|
@ -91,10 +66,21 @@ String dirname(file) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(nweiz): move this into path.dart.
|
||||
/// Splits [path] into its individual components.
|
||||
List<String> splitPath(path) => _sanitizePath(path).split('/');
|
||||
|
||||
/// Returns whether or not [entry] is nested somewhere within [dir]. This just
|
||||
/// performs a path comparison; it doesn't look at the actual filesystem.
|
||||
bool isBeneath(entry, dir) =>
|
||||
sanitizePath(entry).startsWith('${sanitizePath(dir)}/');
|
||||
bool isBeneath(entry, dir) {
|
||||
var relative = relativeTo(entry, dir);
|
||||
return !path.isAbsolute(relative) && !relative.startsWith('..');
|
||||
}
|
||||
|
||||
// TODO(nweiz): move this into path.dart.
|
||||
/// Returns the path to [target] from [base].
|
||||
String relativeTo(target, base) =>
|
||||
new path.Builder(root: base).relative(target);
|
||||
|
||||
/**
|
||||
* Asynchronously determines if [path], which can be a [String] file path, a
|
||||
|
@ -455,52 +441,25 @@ Future<File> createPackageSymlink(String name, from, to,
|
|||
|
||||
/// Given [entry] which may be a [String], [File], or [Directory] relative to
|
||||
/// the current working directory, returns its full canonicalized path.
|
||||
String getFullPath(entry) {
|
||||
var path = _getPath(entry);
|
||||
|
||||
// Don't do anything if it's already absolute.
|
||||
if (isAbsolute(path)) return path;
|
||||
|
||||
// Using Path.join here instead of File().fullPathSync() because the former
|
||||
// does not require an actual file to exist at that path.
|
||||
return new Path.fromNative(currentWorkingDir).join(new Path(path))
|
||||
.toNativePath();
|
||||
}
|
||||
String getFullPath(entry) => path.absolute(_getPath(entry));
|
||||
|
||||
/// Returns whether or not [entry] is an absolute path.
|
||||
bool isAbsolute(entry) => _splitAbsolute(entry).first != null;
|
||||
bool isAbsolute(entry) => path.isAbsolute(_getPath(entry));
|
||||
|
||||
/// Splits [entry] into two components: the absolute path prefix and the
|
||||
/// remaining path. Takes into account Windows' quirky absolute paths syntaxes.
|
||||
Pair<String, String> _splitAbsolute(entry) {
|
||||
var path = _getPath(entry);
|
||||
|
||||
if (Platform.operatingSystem != 'windows') {
|
||||
return !path.startsWith('/') ? new Pair(null, path)
|
||||
: new Pair('/', path.substring(1));
|
||||
}
|
||||
|
||||
// An absolute path on Windows is either UNC (two leading backslashes),
|
||||
// or a drive letter followed by a colon and a slash.
|
||||
var match = new RegExp(r'^(\\\\|[a-zA-Z]:[/\\])').firstMatch(path);
|
||||
return match == null ? new Pair(null, path)
|
||||
: new Pair(match.group(0), path.substring(match.end));
|
||||
}
|
||||
|
||||
/// Resolves [path] relative to the location of pub.dart.
|
||||
String relativeToPub(String path) {
|
||||
/// Resolves [target] relative to the location of pub.dart.
|
||||
String relativeToPub(String target) {
|
||||
var scriptPath = new File(new Options().script).fullPathSync();
|
||||
|
||||
// Walk up until we hit the "util(s)" directory. This lets us figure out where
|
||||
// we are if this function is called from pub.dart, or one of the tests,
|
||||
// which also live under "utils", or from the SDK where pub is in "util".
|
||||
var utilDir = new Path.fromNative(scriptPath).directoryPath;
|
||||
while (utilDir.filename != 'utils' && utilDir.filename != 'util') {
|
||||
if (utilDir.filename == '') throw 'Could not find path to pub.';
|
||||
utilDir = utilDir.directoryPath;
|
||||
var utilDir = dirname(scriptPath);
|
||||
while (basename(utilDir) != 'utils' && basename(utilDir) != 'util') {
|
||||
if (basename(utilDir) == '') throw 'Could not find path to pub.';
|
||||
utilDir = dirname(utilDir);
|
||||
}
|
||||
|
||||
return utilDir.append('pub').append(path).canonicalize().toNativePath();
|
||||
return path.normalize(join(utilDir, 'pub', target));
|
||||
}
|
||||
|
||||
/// A StringInputStream reading from stdin.
|
||||
|
@ -978,7 +937,7 @@ Future<bool> _extractTarGzWindows(InputStream stream, String destination) {
|
|||
}).chain((files) {
|
||||
var tarFile;
|
||||
for (var file in files) {
|
||||
if (new Path(file).extension == 'tar') {
|
||||
if (path.extension(file) == '.tar') {
|
||||
tarFile = file;
|
||||
break;
|
||||
}
|
||||
|
@ -1015,15 +974,14 @@ InputStream createTarGz(List contents, {baseDir}) {
|
|||
// exit codes). See issue 3657.
|
||||
var stream = new ListInputStream();
|
||||
|
||||
if (baseDir == null) baseDir = currentWorkingDir;
|
||||
if (baseDir == null) baseDir = path.current;
|
||||
baseDir = getFullPath(baseDir);
|
||||
contents = contents.map((entry) {
|
||||
entry = getFullPath(entry);
|
||||
if (!isBeneath(entry, baseDir)) {
|
||||
throw 'Entry $entry is not inside $baseDir.';
|
||||
}
|
||||
return new Path.fromNative(entry).relativeTo(new Path.fromNative(baseDir))
|
||||
.toNativePath();
|
||||
return relativeTo(entry, baseDir);
|
||||
});
|
||||
|
||||
if (Platform.operatingSystem != "windows") {
|
||||
|
@ -1117,7 +1075,7 @@ String _getPath(entry) {
|
|||
|
||||
/// Gets the path string for [entry], normalizing backslashes to forward slashes
|
||||
/// on Windows.
|
||||
String sanitizePath(entry) {
|
||||
String _sanitizePath(entry) {
|
||||
entry = _getPath(entry);
|
||||
if (Platform.operatingSystem != 'windows') return entry;
|
||||
|
||||
|
@ -1131,6 +1089,24 @@ String sanitizePath(entry) {
|
|||
'${split.last.replaceAll('\\', '/')}';
|
||||
}
|
||||
|
||||
// TODO(nweiz): Add something like this to path.dart.
|
||||
/// Splits [entry] into two components: the absolute path prefix and the
|
||||
/// remaining path. Takes into account Windows' quirky absolute paths syntaxes.
|
||||
Pair<String, String> _splitAbsolute(entry) {
|
||||
var path = _getPath(entry);
|
||||
|
||||
if (Platform.operatingSystem != 'windows') {
|
||||
return !path.startsWith('/') ? new Pair(null, path)
|
||||
: new Pair('/', path.substring(1));
|
||||
}
|
||||
|
||||
// An absolute path on Windows is either UNC (two leading backslashes),
|
||||
// or a drive letter followed by a colon and a slash.
|
||||
var match = new RegExp(r'^(\\\\|[a-zA-Z]:[/\\])').firstMatch(path);
|
||||
return match == null ? new Pair(null, path)
|
||||
: new Pair(match.group(0), path.substring(match.end));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a [Directory] for [entry], which can either already be one, or be a
|
||||
* [String].
|
||||
|
|
514
utils/pub/path.dart
Normal file
514
utils/pub/path.dart
Normal file
|
@ -0,0 +1,514 @@
|
|||
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
/// A comprehensive, cross-platform path manipulation library.
|
||||
library path;
|
||||
|
||||
import 'dart:io' as io;
|
||||
|
||||
/// An internal builder for the current OS so we can provide a straight
|
||||
/// functional interface and not require users to create one.
|
||||
final _builder = new Builder();
|
||||
|
||||
/// Gets the path to the current working directory.
|
||||
String get current => new io.Directory.current().path;
|
||||
|
||||
/// Gets the path separator for the current platform. On Mac and Linux, this
|
||||
/// is `/`. On Windows, it's `\`.
|
||||
String get separator => _builder.separator;
|
||||
|
||||
/// Converts [path] to an absolute path by resolving it relative to the current
|
||||
/// working directory. If [path] is already an absolute path, just returns it.
|
||||
///
|
||||
/// path.absolute('foo/bar.txt'); // -> /your/current/dir/foo/bar.txt
|
||||
String absolute(String path) => join(current, path);
|
||||
|
||||
/// Gets the file extension of [path]; the portion after the last `.` in the
|
||||
/// [basename] of the path.
|
||||
///
|
||||
/// path.extension('path/to/foo.dart'); // -> '.dart'
|
||||
/// path.extension('path/to/foo'); // -> ''
|
||||
/// path.extension('path.to/foo'); // -> ''
|
||||
/// path.extension('path/to/foo.dart.js'); // -> '.js'
|
||||
///
|
||||
/// If the file name starts with a `.`, then it is not considered an extension:
|
||||
///
|
||||
/// path.extension('~/.bashrc'); // -> ''
|
||||
String extension(String path) => _builder.extension(path);
|
||||
|
||||
/// Gets the part of [path] after the last separator on the current platform.
|
||||
///
|
||||
/// path.filename('path/to/foo.dart'); // -> 'foo.dart'
|
||||
/// path.filename('path/to'); // -> 'to'
|
||||
String filename(String path) => _builder.filename(path);
|
||||
|
||||
/// Gets the part of [path] after the last separator on the current platform,
|
||||
/// and without any trailing file extension.
|
||||
///
|
||||
/// path.filenameWithoutExtension('path/to/foo.dart'); // -> 'foo'
|
||||
String filenameWithoutExtension(String path) =>
|
||||
_builder.filenameWithoutExtension(path);
|
||||
|
||||
/// Returns `true` if [path] is an absolute path and `false` if it is a
|
||||
/// relative path. On Mac and Unix systems, relative paths start with a `/`
|
||||
/// (forward slash). On Windows, an absolute path starts with `\\`, or a drive
|
||||
/// letter followed by `:/` or `:\`.
|
||||
bool isAbsolute(String path) => _builder.isAbsolute(path);
|
||||
|
||||
/// Returns `true` if [path] is a relative path and `false` if it is absolute.
|
||||
/// On Mac and Unix systems, relative paths start with a `/` (forward slash).
|
||||
/// On Windows, an absolute path starts with `\\`, or a drive letter followed
|
||||
/// by `:/` or `:\`.
|
||||
bool isRelative(String path) => _builder.isRelative(path);
|
||||
|
||||
/// Joins the given path parts into a single path using the current platform's
|
||||
/// [separator]. Example:
|
||||
///
|
||||
/// path.join('path', 'to', 'foo'); // -> 'path/to/foo'
|
||||
///
|
||||
/// If any part ends in a path separator, then a redundant separator will not
|
||||
/// be added:
|
||||
///
|
||||
/// path.join('path/', 'to', 'foo'); // -> 'path/to/foo
|
||||
///
|
||||
/// If a part is an absolute path, then anything before that will be ignored:
|
||||
///
|
||||
/// path.join('path', '/to', 'foo'); // -> '/to/foo'
|
||||
///
|
||||
String join(String part1, [String part2, String part3, String part4,
|
||||
String part5, String part6, String part7, String part8]) {
|
||||
if (!?part2) return _builder.join(part1);
|
||||
if (!?part3) return _builder.join(part1, part2);
|
||||
if (!?part4) return _builder.join(part1, part2, part3);
|
||||
if (!?part5) return _builder.join(part1, part2, part3, part4);
|
||||
if (!?part6) return _builder.join(part1, part2, part3, part4, part5);
|
||||
if (!?part7) return _builder.join(part1, part2, part3, part4, part5, part6);
|
||||
if (!?part8) return _builder.join(part1, part2, part3, part4, part5, part6,
|
||||
part7);
|
||||
return _builder.join(part1, part2, part3, part4, part5, part6, part7, part8);
|
||||
}
|
||||
|
||||
/// Normalizes [path], simplifying it by handling `..`, and `.`, and
|
||||
/// removing redundant path separators whenever possible.
|
||||
///
|
||||
/// path.normalize('path/./to/..//file.text'); // -> 'path/file.txt'
|
||||
String normalize(String path) => _builder.normalize(path);
|
||||
|
||||
/// Converts [path] to an equivalent relative path from the current directory.
|
||||
///
|
||||
/// // Given current directory is /root/path:
|
||||
/// path.relative('/root/path/a/b.dart'); // -> 'a/b.dart'
|
||||
/// path.relative('/root/other.dart'); // -> '../other.dart'
|
||||
String relative(String path) => _builder.relative(path);
|
||||
|
||||
/// Removes a trailing extension from the last part of [path].
|
||||
///
|
||||
/// withoutExtension('path/to/foo.dart'); // -> 'path/to/foo'
|
||||
String withoutExtension(String path) => _builder.withoutExtension(path);
|
||||
|
||||
/// An instantiable class for manipulating paths. Unlike the top-level
|
||||
/// functions, this lets you explicitly select what platform the paths will use.
|
||||
class Builder {
|
||||
/// Creates a new path builder for the given style and root directory.
|
||||
///
|
||||
/// If [style] is omitted, it uses the host operating system's path style. If
|
||||
/// [root] is omitted, it defaults to the current working directory.
|
||||
factory Builder({Style style, String root}) {
|
||||
if (style == null) {
|
||||
if (io.Platform.operatingSystem == 'windows') {
|
||||
style = Style.windows;
|
||||
} else {
|
||||
style = Style.posix;
|
||||
}
|
||||
}
|
||||
|
||||
if (root == null) root = new io.Directory.current().path;
|
||||
|
||||
return new Builder._(style, root);
|
||||
}
|
||||
|
||||
Builder._(this.style, this.root);
|
||||
|
||||
/// The style of path that this builder works with.
|
||||
final Style style;
|
||||
|
||||
/// The root directory that relative paths will be relative to.
|
||||
final String root;
|
||||
|
||||
/// Gets the path separator for the builder's [style]. On Mac and Linux,
|
||||
/// this is `/`. On Windows, it's `\`.
|
||||
String get separator => style.separator;
|
||||
|
||||
/// Gets the file extension of [path]; the portion after the last `.` in the
|
||||
/// [basename] of the path.
|
||||
///
|
||||
/// builder.extension('path/to/foo.dart'); // -> '.dart'
|
||||
/// builder.extension('path/to/foo'); // -> ''
|
||||
/// builder.extension('path.to/foo'); // -> ''
|
||||
/// builder.extension('path/to/foo.dart.js'); // -> '.js'
|
||||
///
|
||||
/// If the file name starts with a `.`, then it is not considered an
|
||||
/// extension:
|
||||
///
|
||||
/// builder.extension('~/.bashrc'); // -> ''
|
||||
String extension(String path) => _parse(path).extension;
|
||||
|
||||
/// Gets the part of [path] after the last separator on the builder's
|
||||
/// platform.
|
||||
///
|
||||
/// builder.filename('path/to/foo.dart'); // -> 'foo.dart'
|
||||
/// builder.filename('path/to'); // -> 'to'
|
||||
String filename(String path) => _parse(path).filename;
|
||||
|
||||
/// Gets the part of [path] after the last separator on the builder's
|
||||
/// platform, and without any trailing file extension.
|
||||
///
|
||||
/// builder.filenameWithoutExtension('path/to/foo.dart'); // -> 'foo'
|
||||
String filenameWithoutExtension(String path) =>
|
||||
_parse(path).filenameWithoutExtension;
|
||||
|
||||
/// Returns `true` if [path] is an absolute path and `false` if it is a
|
||||
/// relative path. On Mac and Unix systems, relative paths start with a `/`
|
||||
/// (forward slash). On Windows, an absolute path starts with `\\`, or a drive
|
||||
/// letter followed by `:/` or `:\`.
|
||||
bool isAbsolute(String path) => _parse(path).isAbsolute;
|
||||
|
||||
/// Returns `true` if [path] is a relative path and `false` if it is absolute.
|
||||
/// On Mac and Unix systems, relative paths start with a `/` (forward slash).
|
||||
/// On Windows, an absolute path starts with `\\`, or a drive letter followed
|
||||
/// by `:/` or `:\`.
|
||||
bool isRelative(String path) => !isAbsolute(path);
|
||||
|
||||
/// Joins the given path parts into a single path. Example:
|
||||
///
|
||||
/// builder.join('path', 'to', 'foo'); // -> 'path/to/foo'
|
||||
///
|
||||
/// If any part ends in a path separator, then a redundant separator will not
|
||||
/// be added:
|
||||
///
|
||||
/// builder.join('path/', 'to', 'foo'); // -> 'path/to/foo
|
||||
///
|
||||
/// If a part is an absolute path, then anything before that will be ignored:
|
||||
///
|
||||
/// builder.join('path', '/to', 'foo'); // -> '/to/foo'
|
||||
///
|
||||
String join(String part1, [String part2, String part3, String part4,
|
||||
String part5, String part6, String part7, String part8]) {
|
||||
var buffer = new StringBuffer();
|
||||
var needsSeparator = false;
|
||||
|
||||
addPart(condition, part) {
|
||||
if (!condition) return;
|
||||
|
||||
if (this.isAbsolute(part)) {
|
||||
// An absolute path discards everything before it.
|
||||
buffer.clear();
|
||||
buffer.add(part);
|
||||
} else {
|
||||
if (part.length > 0 && style.separatorPattern.hasMatch(part[0])) {
|
||||
// The part starts with a separator, so we don't need to add one.
|
||||
} else if (needsSeparator) {
|
||||
buffer.add(separator);
|
||||
}
|
||||
|
||||
buffer.add(part);
|
||||
}
|
||||
|
||||
// Unless this part ends with a separator, we'll need to add one before
|
||||
// the next part.
|
||||
needsSeparator = part.length > 0 &&
|
||||
!style.separatorPattern.hasMatch(part[part.length - 1]);
|
||||
}
|
||||
|
||||
addPart(true, part1);
|
||||
addPart(?part2, part2);
|
||||
addPart(?part3, part3);
|
||||
addPart(?part4, part4);
|
||||
addPart(?part5, part5);
|
||||
addPart(?part6, part6);
|
||||
addPart(?part7, part7);
|
||||
addPart(?part8, part8);
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/// Normalizes [path], simplifying it by handling `..`, and `.`, and
|
||||
/// removing redundant path separators whenever possible.
|
||||
///
|
||||
/// builder.normalize('path/./to/..//file.text'); // -> 'path/file.txt'
|
||||
String normalize(String path) {
|
||||
if (path == '') return path;
|
||||
|
||||
var parsed = _parse(path);
|
||||
parsed.normalize();
|
||||
return parsed.toString();
|
||||
}
|
||||
|
||||
/// Creates a new path by appending the given path parts to the [root].
|
||||
/// Equivalent to [join()] with [root] as the first argument. Example:
|
||||
///
|
||||
/// var builder = new Builder(root: 'root');
|
||||
/// builder.join('path', 'to', 'foo'); // -> 'root/path/to/foo'
|
||||
String resolve(String part1, [String part2, String part3, String part4,
|
||||
String part5, String part6, String part7]) {
|
||||
if (!?part2) return join(root, part1);
|
||||
if (!?part3) return join(root, part1, part2);
|
||||
if (!?part4) return join(root, part1, part2, part3);
|
||||
if (!?part5) return join(root, part1, part2, part3, part4);
|
||||
if (!?part6) return join(root, part1, part2, part3, part4, part5);
|
||||
if (!?part7) return join(root, part1, part2, part3, part4, part5, part6);
|
||||
return join(root, part1, part2, part3, part4, part5, part6, part7);
|
||||
}
|
||||
|
||||
/// Converts [path] to an equivalent relative path starting at [root].
|
||||
///
|
||||
/// var builder = new Builder(root: '/root/path');
|
||||
/// builder.relative('/root/path/a/b.dart'); // -> 'a/b.dart'
|
||||
/// builder.relative('/root/other.dart'); // -> '../other.dart'
|
||||
String relative(String path) {
|
||||
// If the base path is relative, resolve it relative to the current
|
||||
// directory.
|
||||
var base = root;
|
||||
if (this.isRelative(base)) base = absolute(base);
|
||||
|
||||
// If the given path is relative, resolve it relative to the base.
|
||||
path = this.join(base, path);
|
||||
|
||||
var baseParsed = _parse(base)..normalize();
|
||||
var pathParsed = _parse(path)..normalize();
|
||||
|
||||
// If the root prefixes don't match (for example, different drive letters
|
||||
// on Windows), then there is no relative path, so just return the absolute
|
||||
// one.
|
||||
if (baseParsed.root != pathParsed.root) return pathParsed.toString();
|
||||
|
||||
// Strip off their common prefix.
|
||||
while (baseParsed.parts.length > 0 && pathParsed.parts.length > 0) {
|
||||
if (baseParsed.parts[0] != pathParsed.parts[0]) break;
|
||||
baseParsed.parts.removeAt(0);
|
||||
baseParsed.separators.removeAt(0);
|
||||
pathParsed.parts.removeAt(0);
|
||||
pathParsed.separators.removeAt(0);
|
||||
}
|
||||
|
||||
// If there are any directories left in the root path, we need to walk up
|
||||
// out of them.
|
||||
pathParsed.parts.insertRange(0, baseParsed.parts.length, '..');
|
||||
pathParsed.separators.insertRange(0, baseParsed.parts.length,
|
||||
style.separator);
|
||||
|
||||
// Corner case: the paths completely collapsed.
|
||||
if (pathParsed.parts.length == 0) return '.';
|
||||
|
||||
// Make it relative.
|
||||
pathParsed.root = '';
|
||||
pathParsed.removeTrailingSeparator();
|
||||
|
||||
return pathParsed.toString();
|
||||
}
|
||||
|
||||
/// Removes a trailing extension from the last part of [path].
|
||||
///
|
||||
/// builder.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo'
|
||||
String withoutExtension(String path) {
|
||||
var lastSeparator = path.lastIndexOf(separator);
|
||||
var lastDot = path.lastIndexOf('.');
|
||||
|
||||
// Ignore '.' in anything but the last component.
|
||||
if (lastSeparator != -1 && lastDot <= lastSeparator + 1) lastDot = -1;
|
||||
|
||||
if (lastDot <= 0) return path;
|
||||
return path.substring(0, lastDot);
|
||||
}
|
||||
|
||||
_ParsedPath _parse(String path) {
|
||||
var before = path;
|
||||
|
||||
// Remove the root prefix, if any.
|
||||
var root = style.getRoot(path);
|
||||
if (root != null) path = path.substring(root.length);
|
||||
|
||||
// Split the parts on path separators.
|
||||
var parts = [];
|
||||
var separators = [];
|
||||
var start = 0;
|
||||
for (var match in style.separatorPattern.allMatches(path)) {
|
||||
parts.add(path.substring(start, match.start));
|
||||
separators.add(match[0]);
|
||||
start = match.end;
|
||||
}
|
||||
|
||||
// Add the final part, if any.
|
||||
if (start < path.length) {
|
||||
parts.add(path.substring(start));
|
||||
separators.add('');
|
||||
}
|
||||
|
||||
// Separate out the file extension.
|
||||
var extension = '';
|
||||
if (parts.length > 0) {
|
||||
var file = parts.last;
|
||||
if (file != '..') {
|
||||
var lastDot = file.lastIndexOf('.');
|
||||
|
||||
// If there is a dot (and it's not the first character, like '.bashrc').
|
||||
if (lastDot > 0) {
|
||||
parts[parts.length - 1] = file.substring(0, lastDot);
|
||||
extension = file.substring(lastDot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new _ParsedPath(style, root, parts, separators, extension);
|
||||
}
|
||||
}
|
||||
|
||||
/// An enum type describing a "flavor" of path.
|
||||
class Style {
|
||||
/// POSIX-style paths use "/" (forward slash) as separators. Absolute paths
|
||||
/// start with "/". Used by UNIX, Linux, Mac OS X, and others.
|
||||
static final posix = new Style._('posix', '/', '/', '/');
|
||||
|
||||
/// Windows paths use "\" (backslash) as separators. Absolute paths start with
|
||||
/// a drive letter followed by a colon (example, "C:") or two backslashes
|
||||
/// ("\\") for UNC paths.
|
||||
static final windows = new Style._('windows', '\\', r'[/\\]',
|
||||
r'\\\\|[a-zA-Z]:[/\\]');
|
||||
|
||||
Style._(this.name, this.separator, String separatorPattern, String rootPattern)
|
||||
: separatorPattern = new RegExp(separatorPattern),
|
||||
_rootPattern = new RegExp('^$rootPattern');
|
||||
|
||||
/// The name of this path style. Will be "posix" or "windows".
|
||||
final String name;
|
||||
|
||||
/// The path separator for this style. On POSIX, this is `/`. On Windows,
|
||||
/// it's `\`.
|
||||
final String separator;
|
||||
|
||||
/// The [Pattern] that can be used to match a separator for a path in this
|
||||
/// style. Windows allows both "/" and "\" as path separators even though
|
||||
/// "\" is the canonical one.
|
||||
final Pattern separatorPattern;
|
||||
|
||||
/// The [Pattern] that can be used to match the root prefix of an absolute
|
||||
/// path in this style.
|
||||
final Pattern _rootPattern;
|
||||
|
||||
/// Gets the root prefix of [path] if path is absolute. If [path] is relative,
|
||||
/// returns `null`.
|
||||
String getRoot(String path) {
|
||||
var match = _rootPattern.firstMatch(path);
|
||||
if (match == null) return null;
|
||||
return match[0];
|
||||
}
|
||||
|
||||
String toString() => name;
|
||||
}
|
||||
|
||||
// TODO(rnystrom): Make this public?
|
||||
class _ParsedPath {
|
||||
/// The [Style] that was used to parse this path.
|
||||
Style style;
|
||||
|
||||
/// The absolute root portion of the path, or `null` if the path is relative.
|
||||
/// On POSIX systems, this will be `null` or "/". On Windows, it can be
|
||||
/// `null`, "//" for a UNC path, or something like "C:\" for paths with drive
|
||||
/// letters.
|
||||
String root;
|
||||
|
||||
/// The path-separated parts of the path. All but the last will be
|
||||
/// directories. The last could be a directory, or could be the file name
|
||||
/// without its extension.
|
||||
List<String> parts;
|
||||
|
||||
/// The path separators following each part. The last one will be an empty
|
||||
/// string unless the path ends with a trailing separator.
|
||||
List<String> separators;
|
||||
|
||||
/// The file's extension, or "" if it doesn't have one.
|
||||
String extension;
|
||||
|
||||
/// `true` if the path ends with a trailing separator.
|
||||
bool get hasTrailingSeparator {
|
||||
if (separators.length == 0) return false;
|
||||
return separators[separators.length - 1] != '';
|
||||
}
|
||||
|
||||
/// `true` if this is an absolute path.
|
||||
bool get isAbsolute => root != null;
|
||||
|
||||
_ParsedPath(this.style, this.root, this.parts, this.separators,
|
||||
this.extension);
|
||||
|
||||
String get filename {
|
||||
if (parts.length == 0) return extension;
|
||||
if (hasTrailingSeparator) return '';
|
||||
return '${parts.last}$extension';
|
||||
}
|
||||
|
||||
String get filenameWithoutExtension {
|
||||
if (parts.length == 0) return '';
|
||||
if (hasTrailingSeparator) return '';
|
||||
return parts.last;
|
||||
}
|
||||
|
||||
void removeTrailingSeparator() {
|
||||
if (separators.length > 0) {
|
||||
separators[separators.length - 1] = '';
|
||||
}
|
||||
}
|
||||
|
||||
void normalize() {
|
||||
// Handle '.', '..', and empty parts.
|
||||
var leadingDoubles = 0;
|
||||
var newParts = [];
|
||||
for (var part in parts) {
|
||||
if (part == '.' || part == '') {
|
||||
// Do nothing. Ignore it.
|
||||
} else if (part == '..') {
|
||||
// Pop the last part off.
|
||||
if (newParts.length > 0) {
|
||||
newParts.removeLast();
|
||||
} else {
|
||||
// Backed out past the beginning, so preserve the "..".
|
||||
leadingDoubles++;
|
||||
}
|
||||
} else {
|
||||
newParts.add(part);
|
||||
}
|
||||
}
|
||||
|
||||
// A relative path can back out from the start directory.
|
||||
if (!isAbsolute) {
|
||||
newParts.insertRange(0, leadingDoubles, '..');
|
||||
}
|
||||
|
||||
// If we collapsed down to nothing, do ".".
|
||||
if (newParts.length == 0 && !isAbsolute) {
|
||||
newParts.add('.');
|
||||
}
|
||||
|
||||
// Canonicalize separators.
|
||||
var newSeparators = [];
|
||||
newSeparators.insertRange(0, newParts.length, style.separator);
|
||||
|
||||
parts = newParts;
|
||||
separators = newSeparators;
|
||||
|
||||
removeTrailingSeparator();
|
||||
}
|
||||
|
||||
String toString() {
|
||||
var builder = new StringBuffer();
|
||||
if (root != null) builder.add(root);
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
builder.add(parts[i]);
|
||||
if (extension != null && i == parts.length - 1) builder.add(extension);
|
||||
builder.add(separators[i]);
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ import 'entrypoint.dart';
|
|||
import 'exit_codes.dart' as exit_codes;
|
||||
import 'log.dart' as log;
|
||||
import 'package.dart';
|
||||
import 'path.dart' as path;
|
||||
import 'pubspec.dart';
|
||||
import 'source.dart';
|
||||
import 'source_registry.dart';
|
||||
|
@ -249,7 +250,7 @@ abstract class PubCommand {
|
|||
// TODO(rnystrom): Will eventually need better logic to walk up
|
||||
// subdirectories until we hit one that looks package-like. For now, just
|
||||
// assume the cwd is it.
|
||||
future = Entrypoint.load(currentWorkingDir, cache);
|
||||
future = Entrypoint.load(path.current, cache);
|
||||
}
|
||||
|
||||
future = future.chain((entrypoint) {
|
||||
|
@ -270,10 +271,10 @@ abstract class PubCommand {
|
|||
future.handleException((e) {
|
||||
if (e is PubspecNotFoundException && e.name == null) {
|
||||
e = 'Could not find a file named "pubspec.yaml" in the directory '
|
||||
'$currentWorkingDir.';
|
||||
'${path.current}.';
|
||||
} else if (e is PubspecHasNoNameException && e.name == null) {
|
||||
e = 'pubspec.yaml is missing the required "name" field (e.g. "name: '
|
||||
'${basename(currentWorkingDir)}").';
|
||||
'${basename(path.current)}").';
|
||||
}
|
||||
|
||||
handleError(e, future.stackTrace);
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'dart:io';
|
|||
|
||||
import '../entrypoint.dart';
|
||||
import '../io.dart';
|
||||
import '../path.dart' as path;
|
||||
import '../validator.dart';
|
||||
|
||||
/// Dart reserved words, from the Dart spec.
|
||||
|
@ -31,11 +32,10 @@ class NameValidator extends Validator {
|
|||
return listDir(libDir, recursive: true);
|
||||
}).transform((files) {
|
||||
for (var file in files) {
|
||||
// TODO(nweiz): Since `file` is absolute, this will break if the package
|
||||
// itself is in a directory named "src" (issue 7215).
|
||||
file = relativeTo(file, libDir);
|
||||
if (splitPath(file).contains("src")) continue;
|
||||
if (new Path(file).extension != 'dart') continue;
|
||||
var libName = new Path(basename(file)).filenameWithoutExtension;
|
||||
if (path.extension(file) != '.dart') continue;
|
||||
var libName = path.filenameWithoutExtension(file);
|
||||
_checkName(libName, 'The name of "$file", "$libName",');
|
||||
}
|
||||
});
|
||||
|
|
278
utils/tests/pub/path/path_posix_test.dart
Normal file
278
utils/tests/pub/path/path_posix_test.dart
Normal file
|
@ -0,0 +1,278 @@
|
|||
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
library path_test;
|
||||
|
||||
import 'dart:io' as io;
|
||||
|
||||
import '../../../../pkg/unittest/lib/unittest.dart';
|
||||
import '../../../pub/path.dart' as path;
|
||||
|
||||
main() {
|
||||
var builder = new path.Builder(style: path.Style.posix, root: '/root/path');
|
||||
|
||||
if (new path.Builder().style == path.Style.posix) {
|
||||
group('absolute', () {
|
||||
expect(path.absolute('a/b.txt'), path.join(path.current, 'a/b.txt'));
|
||||
expect(path.absolute('/a/b.txt'), '/a/b.txt');
|
||||
});
|
||||
}
|
||||
|
||||
test('separator', () {
|
||||
expect(builder.separator, '/');
|
||||
});
|
||||
|
||||
test('extension', () {
|
||||
expect(builder.extension(''), '');
|
||||
expect(builder.extension('foo.dart'), '.dart');
|
||||
expect(builder.extension('foo.dart.js'), '.js');
|
||||
expect(builder.extension('a.b/c'), '');
|
||||
expect(builder.extension('a.b/c.d'), '.d');
|
||||
expect(builder.extension('~/.bashrc'), '');
|
||||
expect(builder.extension(r'a.b\c'), r'.b\c');
|
||||
});
|
||||
|
||||
test('filename', () {
|
||||
expect(builder.filename(''), '');
|
||||
expect(builder.filename('a'), 'a');
|
||||
expect(builder.filename('a/b'), 'b');
|
||||
expect(builder.filename('a/b/c'), 'c');
|
||||
expect(builder.filename('a/b.c'), 'b.c');
|
||||
expect(builder.filename('a/'), '');
|
||||
expect(builder.filename('a/.'), '.');
|
||||
expect(builder.filename(r'a/b\c'), r'b\c');
|
||||
});
|
||||
|
||||
test('filenameWithoutExtension', () {
|
||||
expect(builder.filenameWithoutExtension(''), '');
|
||||
expect(builder.filenameWithoutExtension('a'), 'a');
|
||||
expect(builder.filenameWithoutExtension('a/b'), 'b');
|
||||
expect(builder.filenameWithoutExtension('a/b/c'), 'c');
|
||||
expect(builder.filenameWithoutExtension('a/b.c'), 'b');
|
||||
expect(builder.filenameWithoutExtension('a/'), '');
|
||||
expect(builder.filenameWithoutExtension('a/.'), '.');
|
||||
expect(builder.filenameWithoutExtension(r'a/b\c'), r'b\c');
|
||||
expect(builder.filenameWithoutExtension('a/.bashrc'), '.bashrc');
|
||||
expect(builder.filenameWithoutExtension('a/b/c.d.e'), 'c.d');
|
||||
});
|
||||
|
||||
test('isAbsolute', () {
|
||||
expect(builder.isAbsolute(''), false);
|
||||
expect(builder.isAbsolute('a'), false);
|
||||
expect(builder.isAbsolute('a/b'), false);
|
||||
expect(builder.isAbsolute('/a'), true);
|
||||
expect(builder.isAbsolute('/a/b'), true);
|
||||
expect(builder.isAbsolute('~'), false);
|
||||
expect(builder.isAbsolute('.'), false);
|
||||
expect(builder.isAbsolute('../a'), false);
|
||||
expect(builder.isAbsolute('C:/a'), false);
|
||||
expect(builder.isAbsolute(r'C:\a'), false);
|
||||
expect(builder.isAbsolute(r'\\a'), false);
|
||||
});
|
||||
|
||||
test('isRelative', () {
|
||||
expect(builder.isRelative(''), true);
|
||||
expect(builder.isRelative('a'), true);
|
||||
expect(builder.isRelative('a/b'), true);
|
||||
expect(builder.isRelative('/a'), false);
|
||||
expect(builder.isRelative('/a/b'), false);
|
||||
expect(builder.isRelative('~'), true);
|
||||
expect(builder.isRelative('.'), true);
|
||||
expect(builder.isRelative('../a'), true);
|
||||
expect(builder.isRelative('C:/a'), true);
|
||||
expect(builder.isRelative(r'C:\a'), true);
|
||||
expect(builder.isRelative(r'\\a'), true);
|
||||
});
|
||||
|
||||
group('join', () {
|
||||
test('allows up to eight parts', () {
|
||||
expect(builder.join('a'), 'a');
|
||||
expect(builder.join('a', 'b'), 'a/b');
|
||||
expect(builder.join('a', 'b', 'c'), 'a/b/c');
|
||||
expect(builder.join('a', 'b', 'c', 'd'), 'a/b/c/d');
|
||||
expect(builder.join('a', 'b', 'c', 'd', 'e'), 'a/b/c/d/e');
|
||||
expect(builder.join('a', 'b', 'c', 'd', 'e', 'f'), 'a/b/c/d/e/f');
|
||||
expect(builder.join('a', 'b', 'c', 'd', 'e', 'f', 'g'), 'a/b/c/d/e/f/g');
|
||||
expect(builder.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'),
|
||||
'a/b/c/d/e/f/g/h');
|
||||
});
|
||||
|
||||
test('does not add separator if a part ends in one', () {
|
||||
expect(builder.join('a/', 'b', 'c/', 'd'), 'a/b/c/d');
|
||||
expect(builder.join('a\\', 'b'), r'a\/b');
|
||||
});
|
||||
|
||||
test('ignores parts before an absolute path', () {
|
||||
expect(builder.join('a', '/b', '/c', 'd'), '/c/d');
|
||||
expect(builder.join('a', r'c:\b', 'c', 'd'), r'a/c:\b/c/d');
|
||||
expect(builder.join('a', r'\\b', 'c', 'd'), r'a/\\b/c/d');
|
||||
});
|
||||
});
|
||||
|
||||
group('normalize', () {
|
||||
test('simple cases', () {
|
||||
expect(builder.normalize(''), '');
|
||||
expect(builder.normalize('.'), '.');
|
||||
expect(builder.normalize('..'), '..');
|
||||
expect(builder.normalize('a'), 'a');
|
||||
expect(builder.normalize('/'), '/');
|
||||
expect(builder.normalize(r'\'), r'\');
|
||||
});
|
||||
|
||||
test('collapses redundant separators', () {
|
||||
expect(builder.normalize(r'a/b/c'), r'a/b/c');
|
||||
expect(builder.normalize(r'a//b///c////d'), r'a/b/c/d');
|
||||
});
|
||||
|
||||
test('does not collapse separators for other platform', () {
|
||||
expect(builder.normalize(r'a\\b\\\c'), r'a\\b\\\c');
|
||||
});
|
||||
|
||||
test('eliminates "." parts', () {
|
||||
expect(builder.normalize('./'), '.');
|
||||
expect(builder.normalize('/.'), '/');
|
||||
expect(builder.normalize('/./'), '/');
|
||||
expect(builder.normalize('./.'), '.');
|
||||
expect(builder.normalize('a/./b'), 'a/b');
|
||||
expect(builder.normalize('a/.b/c'), 'a/.b/c');
|
||||
expect(builder.normalize('a/././b/./c'), 'a/b/c');
|
||||
expect(builder.normalize('././a'), 'a');
|
||||
expect(builder.normalize('a/./.'), 'a');
|
||||
});
|
||||
|
||||
test('eliminates ".." parts', () {
|
||||
expect(builder.normalize('..'), '..');
|
||||
expect(builder.normalize('../'), '..');
|
||||
expect(builder.normalize('../../..'), '../../..');
|
||||
expect(builder.normalize('../../../'), '../../..');
|
||||
expect(builder.normalize('/..'), '/');
|
||||
expect(builder.normalize('/../../..'), '/');
|
||||
expect(builder.normalize('/../../../a'), '/a');
|
||||
expect(builder.normalize('a/..'), '.');
|
||||
expect(builder.normalize('a/b/..'), 'a');
|
||||
expect(builder.normalize('a/../b'), 'b');
|
||||
expect(builder.normalize('a/./../b'), 'b');
|
||||
expect(builder.normalize('a/b/c/../../d/e/..'), 'a/d');
|
||||
expect(builder.normalize('a/b/../../../../c'), '../../c');
|
||||
});
|
||||
|
||||
test('does not walk before root on absolute paths', () {
|
||||
expect(builder.normalize('..'), '..');
|
||||
expect(builder.normalize('../'), '..');
|
||||
expect(builder.normalize('/..'), '/');
|
||||
expect(builder.normalize('a/..'), '.');
|
||||
expect(builder.normalize('a/b/..'), 'a');
|
||||
expect(builder.normalize('a/../b'), 'b');
|
||||
expect(builder.normalize('a/./../b'), 'b');
|
||||
expect(builder.normalize('a/b/c/../../d/e/..'), 'a/d');
|
||||
expect(builder.normalize('a/b/../../../../c'), '../../c');
|
||||
});
|
||||
|
||||
test('removes trailing separators', () {
|
||||
expect(builder.normalize('./'), '.');
|
||||
expect(builder.normalize('.//'), '.');
|
||||
expect(builder.normalize('a/'), 'a');
|
||||
expect(builder.normalize('a/b/'), 'a/b');
|
||||
expect(builder.normalize('a/b///'), 'a/b');
|
||||
});
|
||||
});
|
||||
|
||||
group('relative', () {
|
||||
group('from absolute root', () {
|
||||
test('given absolute path in root', () {
|
||||
expect(builder.relative('/'), '../..');
|
||||
expect(builder.relative('/root'), '..');
|
||||
expect(builder.relative('/root/path'), '.');
|
||||
expect(builder.relative('/root/path/a'), 'a');
|
||||
expect(builder.relative('/root/path/a/b.txt'), 'a/b.txt');
|
||||
expect(builder.relative('/root/a/b.txt'), '../a/b.txt');
|
||||
});
|
||||
|
||||
test('given absolute path outside of root', () {
|
||||
expect(builder.relative('/a/b'), '../../a/b');
|
||||
expect(builder.relative('/root/path/a'), 'a');
|
||||
expect(builder.relative('/root/path/a/b.txt'), 'a/b.txt');
|
||||
expect(builder.relative('/root/a/b.txt'), '../a/b.txt');
|
||||
});
|
||||
|
||||
test('given relative path', () {
|
||||
// The path is considered relative to the root, so it basically just
|
||||
// normalizes.
|
||||
expect(builder.relative(''), '.');
|
||||
expect(builder.relative('.'), '.');
|
||||
expect(builder.relative('a'), 'a');
|
||||
expect(builder.relative('a/b.txt'), 'a/b.txt');
|
||||
expect(builder.relative('../a/b.txt'), '../a/b.txt');
|
||||
expect(builder.relative('a/./b/../c.txt'), 'a/c.txt');
|
||||
});
|
||||
});
|
||||
|
||||
group('from relative root', () {
|
||||
var r = new path.Builder(style: path.Style.posix, root: 'foo/bar');
|
||||
|
||||
// These tests rely on the current working directory, so don't do the
|
||||
// right thing if you run them on the wrong platform.
|
||||
if (io.Platform.operatingSystem != 'windows') {
|
||||
test('given absolute path', () {
|
||||
var b = new path.Builder(style: path.Style.posix);
|
||||
expect(r.relative('/'), b.join(b.relative('/'), '../..'));
|
||||
expect(r.relative('/a/b'), b.join(b.relative('/'), '../../a/b'));
|
||||
});
|
||||
}
|
||||
|
||||
test('given relative path', () {
|
||||
// The path is considered relative to the root, so it basically just
|
||||
// normalizes.
|
||||
expect(r.relative(''), '.');
|
||||
expect(r.relative('.'), '.');
|
||||
expect(r.relative('..'), '..');
|
||||
expect(r.relative('a'), 'a');
|
||||
expect(r.relative('a/b.txt'), 'a/b.txt');
|
||||
expect(r.relative('../a/b.txt'), '../a/b.txt');
|
||||
expect(r.relative('a/./b/../c.txt'), 'a/c.txt');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group('resolve', () {
|
||||
test('allows up to seven parts', () {
|
||||
expect(builder.resolve('a'), '/root/path/a');
|
||||
expect(builder.resolve('a', 'b'), '/root/path/a/b');
|
||||
expect(builder.resolve('a', 'b', 'c'), '/root/path/a/b/c');
|
||||
expect(builder.resolve('a', 'b', 'c', 'd'), '/root/path/a/b/c/d');
|
||||
expect(builder.resolve('a', 'b', 'c', 'd', 'e'), '/root/path/a/b/c/d/e');
|
||||
expect(builder.resolve('a', 'b', 'c', 'd', 'e', 'f'),
|
||||
'/root/path/a/b/c/d/e/f');
|
||||
expect(builder.resolve('a', 'b', 'c', 'd', 'e', 'f', 'g'),
|
||||
'/root/path/a/b/c/d/e/f/g');
|
||||
});
|
||||
|
||||
test('does not add separator if a part ends in one', () {
|
||||
expect(builder.resolve('a/', 'b', 'c/', 'd'), '/root/path/a/b/c/d');
|
||||
expect(builder.resolve(r'a\', 'b'), r'/root/path/a\/b');
|
||||
});
|
||||
|
||||
test('ignores parts before an absolute path', () {
|
||||
expect(builder.resolve('a', '/b', '/c', 'd'), '/c/d');
|
||||
expect(builder.resolve('a', r'c:\b', 'c', 'd'), r'/root/path/a/c:\b/c/d');
|
||||
expect(builder.resolve('a', r'\\b', 'c', 'd'), r'/root/path/a/\\b/c/d');
|
||||
});
|
||||
});
|
||||
|
||||
test('withoutExtension', () {
|
||||
expect(builder.withoutExtension(''), '');
|
||||
expect(builder.withoutExtension('a'), 'a');
|
||||
expect(builder.withoutExtension('.a'), '.a');
|
||||
expect(builder.withoutExtension('a.b'), 'a');
|
||||
expect(builder.withoutExtension('a/b.c'), 'a/b');
|
||||
expect(builder.withoutExtension('a/b.c.d'), 'a/b.c');
|
||||
expect(builder.withoutExtension('a/'), 'a/');
|
||||
expect(builder.withoutExtension('a/b/'), 'a/b/');
|
||||
expect(builder.withoutExtension('a/.'), 'a/.');
|
||||
expect(builder.withoutExtension('a/.b'), 'a/.b');
|
||||
expect(builder.withoutExtension('a.b/c'), 'a.b/c');
|
||||
expect(builder.withoutExtension(r'a/b\c'), r'a/b\c');
|
||||
expect(builder.withoutExtension(r'a/b\c.d'), r'a/b\c');
|
||||
});
|
||||
}
|
59
utils/tests/pub/path/path_test.dart
Normal file
59
utils/tests/pub/path/path_test.dart
Normal file
|
@ -0,0 +1,59 @@
|
|||
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
library all_test;
|
||||
|
||||
import 'dart:io' as io;
|
||||
|
||||
import '../../../../pkg/unittest/lib/unittest.dart';
|
||||
import '../../../pub/path.dart' as path;
|
||||
|
||||
main() {
|
||||
group('path.Style', () {
|
||||
test('name', () {
|
||||
expect(path.Style.posix.name, 'posix');
|
||||
expect(path.Style.windows.name, 'windows');
|
||||
});
|
||||
|
||||
test('separator', () {
|
||||
expect(path.Style.posix.separator, '/');
|
||||
expect(path.Style.windows.separator, '\\');
|
||||
});
|
||||
|
||||
test('toString()', () {
|
||||
expect(path.Style.posix.toString(), 'posix');
|
||||
expect(path.Style.windows.toString(), 'windows');
|
||||
});
|
||||
});
|
||||
|
||||
group('new Builder()', () {
|
||||
test('uses the given root directory', () {
|
||||
var builder = new path.Builder(root: '/a/b/c');
|
||||
expect(builder.root, '/a/b/c');
|
||||
});
|
||||
|
||||
test('uses the given style', () {
|
||||
var builder = new path.Builder(style: path.Style.windows);
|
||||
expect(builder.style, path.Style.windows);
|
||||
});
|
||||
|
||||
test('uses the current working directory if root is omitted', () {
|
||||
var builder = new path.Builder();
|
||||
expect(builder.root, new io.Directory.current().path);
|
||||
});
|
||||
|
||||
test('uses the host OS if style is omitted', () {
|
||||
var builder = new path.Builder();
|
||||
if (io.Platform.operatingSystem == 'windows') {
|
||||
expect(builder.style, path.Style.windows);
|
||||
} else {
|
||||
expect(builder.style, path.Style.posix);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('current', () {
|
||||
expect(path.current, new io.Directory.current().path);
|
||||
});
|
||||
}
|
295
utils/tests/pub/path/path_windows_test.dart
Normal file
295
utils/tests/pub/path/path_windows_test.dart
Normal file
|
@ -0,0 +1,295 @@
|
|||
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
library path_test;
|
||||
|
||||
import 'dart:io' as io;
|
||||
|
||||
import '../../../../pkg/unittest/lib/unittest.dart';
|
||||
import '../../../pub/path.dart' as path;
|
||||
|
||||
main() {
|
||||
var builder = new path.Builder(style: path.Style.windows,
|
||||
root: r'C:\root\path');
|
||||
|
||||
if (new path.Builder().style == path.Style.windows) {
|
||||
group('absolute', () {
|
||||
expect(path.absolute(r'a\b.txt'), path.join(path.current, r'a\b.txt'));
|
||||
expect(path.absolute(r'C:\a\b.txt'), r'C:\a\b.txt');
|
||||
expect(path.absolute(r'\\a\b.txt'), r'\\a\b.txt');
|
||||
});
|
||||
}
|
||||
|
||||
group('separator', () {
|
||||
expect(builder.separator, '\\');
|
||||
});
|
||||
|
||||
test('extension', () {
|
||||
expect(builder.extension(''), '');
|
||||
expect(builder.extension('foo.dart'), '.dart');
|
||||
expect(builder.extension('foo.dart.js'), '.js');
|
||||
expect(builder.extension(r'a.b\c'), '');
|
||||
expect(builder.extension('a.b/c.d'), '.d');
|
||||
expect(builder.extension(r'~\.bashrc'), '');
|
||||
expect(builder.extension(r'a.b/c'), r'');
|
||||
});
|
||||
|
||||
test('filename', () {
|
||||
expect(builder.filename(r''), '');
|
||||
expect(builder.filename(r'a'), 'a');
|
||||
expect(builder.filename(r'a\b'), 'b');
|
||||
expect(builder.filename(r'a\b\c'), 'c');
|
||||
expect(builder.filename(r'a\b.c'), 'b.c');
|
||||
expect(builder.filename(r'a\'), '');
|
||||
expect(builder.filename(r'a/'), '');
|
||||
expect(builder.filename(r'a\.'), '.');
|
||||
expect(builder.filename(r'a\b/c'), r'c');
|
||||
});
|
||||
|
||||
test('filenameWithoutExtension', () {
|
||||
expect(builder.filenameWithoutExtension(''), '');
|
||||
expect(builder.filenameWithoutExtension('a'), 'a');
|
||||
expect(builder.filenameWithoutExtension(r'a\b'), 'b');
|
||||
expect(builder.filenameWithoutExtension(r'a\b\c'), 'c');
|
||||
expect(builder.filenameWithoutExtension(r'a\b.c'), 'b');
|
||||
expect(builder.filenameWithoutExtension(r'a\'), '');
|
||||
expect(builder.filenameWithoutExtension(r'a\.'), '.');
|
||||
expect(builder.filenameWithoutExtension(r'a\b/c'), r'c');
|
||||
expect(builder.filenameWithoutExtension(r'a\.bashrc'), '.bashrc');
|
||||
expect(builder.filenameWithoutExtension(r'a\b\c.d.e'), 'c.d');
|
||||
});
|
||||
|
||||
test('isAbsolute', () {
|
||||
expect(builder.isAbsolute(''), false);
|
||||
expect(builder.isAbsolute('a'), false);
|
||||
expect(builder.isAbsolute(r'a\b'), false);
|
||||
expect(builder.isAbsolute(r'\a'), false);
|
||||
expect(builder.isAbsolute(r'\a\b'), false);
|
||||
expect(builder.isAbsolute('~'), false);
|
||||
expect(builder.isAbsolute('.'), false);
|
||||
expect(builder.isAbsolute(r'..\a'), false);
|
||||
expect(builder.isAbsolute(r'a:/a\b'), true);
|
||||
expect(builder.isAbsolute(r'D:/a/b'), true);
|
||||
expect(builder.isAbsolute(r'c:\'), true);
|
||||
expect(builder.isAbsolute(r'B:\'), true);
|
||||
expect(builder.isAbsolute(r'c:\a'), true);
|
||||
expect(builder.isAbsolute(r'C:\a'), true);
|
||||
expect(builder.isAbsolute(r'\\a'), true);
|
||||
expect(builder.isAbsolute(r'\\'), true);
|
||||
});
|
||||
|
||||
test('isRelative', () {
|
||||
expect(builder.isRelative(''), true);
|
||||
expect(builder.isRelative('a'), true);
|
||||
expect(builder.isRelative(r'a\b'), true);
|
||||
expect(builder.isRelative(r'\a'), true);
|
||||
expect(builder.isRelative(r'\a\b'), true);
|
||||
expect(builder.isRelative('~'), true);
|
||||
expect(builder.isRelative('.'), true);
|
||||
expect(builder.isRelative(r'..\a'), true);
|
||||
expect(builder.isRelative(r'a:/a\b'), false);
|
||||
expect(builder.isRelative(r'D:/a/b'), false);
|
||||
expect(builder.isRelative(r'c:\'), false);
|
||||
expect(builder.isRelative(r'B:\'), false);
|
||||
expect(builder.isRelative(r'c:\a'), false);
|
||||
expect(builder.isRelative(r'C:\a'), false);
|
||||
expect(builder.isRelative(r'\\a'), false);
|
||||
expect(builder.isRelative(r'\\'), false);
|
||||
});
|
||||
|
||||
group('join', () {
|
||||
test('allows up to eight parts', () {
|
||||
expect(builder.join('a'), 'a');
|
||||
expect(builder.join('a', 'b'), r'a\b');
|
||||
expect(builder.join('a', 'b', 'c'), r'a\b\c');
|
||||
expect(builder.join('a', 'b', 'c', 'd'), r'a\b\c\d');
|
||||
expect(builder.join('a', 'b', 'c', 'd', 'e'), r'a\b\c\d\e');
|
||||
expect(builder.join('a', 'b', 'c', 'd', 'e', 'f'), r'a\b\c\d\e\f');
|
||||
expect(builder.join('a', 'b', 'c', 'd', 'e', 'f', 'g'), r'a\b\c\d\e\f\g');
|
||||
expect(builder.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'),
|
||||
r'a\b\c\d\e\f\g\h');
|
||||
});
|
||||
|
||||
test('does not add separator if a part ends or begins in one', () {
|
||||
expect(builder.join(r'a\', 'b', r'c\', 'd'), r'a\b\c\d');
|
||||
expect(builder.join('a/', 'b'), r'a/b');
|
||||
expect(builder.join('a', '/b'), 'a/b');
|
||||
expect(builder.join('a', r'\b'), r'a\b');
|
||||
});
|
||||
|
||||
test('ignores parts before an absolute path', () {
|
||||
expect(builder.join('a', '/b', '/c', 'd'), r'a/b/c\d');
|
||||
expect(builder.join('a', r'c:\b', 'c', 'd'), r'c:\b\c\d');
|
||||
expect(builder.join('a', r'\\b', r'\\c', 'd'), r'\\c\d');
|
||||
});
|
||||
});
|
||||
|
||||
group('normalize', () {
|
||||
test('simple cases', () {
|
||||
expect(builder.normalize(''), '');
|
||||
expect(builder.normalize('.'), '.');
|
||||
expect(builder.normalize('..'), '..');
|
||||
expect(builder.normalize('a'), 'a');
|
||||
expect(builder.normalize('C:/'), r'C:/');
|
||||
expect(builder.normalize(r'C:\'), r'C:\');
|
||||
expect(builder.normalize(r'\\'), r'\\');
|
||||
});
|
||||
|
||||
test('collapses redundant separators', () {
|
||||
expect(builder.normalize(r'a\b\c'), r'a\b\c');
|
||||
expect(builder.normalize(r'a\\b\\\c\\\\d'), r'a\b\c\d');
|
||||
});
|
||||
|
||||
test('eliminates "." parts', () {
|
||||
expect(builder.normalize(r'.\'), '.');
|
||||
expect(builder.normalize(r'c:\.'), r'c:\');
|
||||
expect(builder.normalize(r'B:\.\'), r'B:\');
|
||||
expect(builder.normalize(r'\\.'), r'\\');
|
||||
expect(builder.normalize(r'\\.\'), r'\\');
|
||||
expect(builder.normalize(r'.\.'), '.');
|
||||
expect(builder.normalize(r'a\.\b'), r'a\b');
|
||||
expect(builder.normalize(r'a\.b\c'), r'a\.b\c');
|
||||
expect(builder.normalize(r'a\./.\b\.\c'), r'a\b\c');
|
||||
expect(builder.normalize(r'.\./a'), 'a');
|
||||
expect(builder.normalize(r'a/.\.'), 'a');
|
||||
});
|
||||
|
||||
test('eliminates ".." parts', () {
|
||||
expect(builder.normalize('..'), '..');
|
||||
expect(builder.normalize(r'..\'), '..');
|
||||
expect(builder.normalize(r'..\..\..'), r'..\..\..');
|
||||
expect(builder.normalize(r'../..\..\'), r'..\..\..');
|
||||
// TODO(rnystrom): Is this how Python handles absolute paths on Windows?
|
||||
expect(builder.normalize(r'\\..'), r'\\');
|
||||
expect(builder.normalize(r'\\..\..\..'), r'\\');
|
||||
expect(builder.normalize(r'\\..\../..\a'), r'\\a');
|
||||
expect(builder.normalize(r'c:\..'), r'c:\');
|
||||
expect(builder.normalize(r'A:/..\..\..'), r'A:/');
|
||||
expect(builder.normalize(r'b:\..\..\..\a'), r'b:\a');
|
||||
expect(builder.normalize(r'a\..'), '.');
|
||||
expect(builder.normalize(r'a\b\..'), 'a');
|
||||
expect(builder.normalize(r'a\..\b'), 'b');
|
||||
expect(builder.normalize(r'a\.\..\b'), 'b');
|
||||
expect(builder.normalize(r'a\b\c\..\..\d\e\..'), r'a\d');
|
||||
expect(builder.normalize(r'a\b\..\..\..\..\c'), r'..\..\c');
|
||||
});
|
||||
|
||||
test('removes trailing separators', () {
|
||||
expect(builder.normalize(r'.\'), '.');
|
||||
expect(builder.normalize(r'.\\'), '.');
|
||||
expect(builder.normalize(r'a/'), 'a');
|
||||
expect(builder.normalize(r'a\b\'), r'a\b');
|
||||
expect(builder.normalize(r'a\b\\\'), r'a\b');
|
||||
});
|
||||
|
||||
test('normalizes separators', () {
|
||||
expect(builder.normalize(r'a/b\c'), r'a\b\c');
|
||||
});
|
||||
});
|
||||
|
||||
group('relative', () {
|
||||
group('from absolute root', () {
|
||||
test('given absolute path in root', () {
|
||||
expect(builder.relative(r'C:\'), r'..\..');
|
||||
expect(builder.relative(r'C:\root'), '..');
|
||||
expect(builder.relative(r'C:\root\path'), '.');
|
||||
expect(builder.relative(r'C:\root\path\a'), 'a');
|
||||
expect(builder.relative(r'C:\root\path\a\b.txt'), r'a\b.txt');
|
||||
expect(builder.relative(r'C:\root\a\b.txt'), r'..\a\b.txt');
|
||||
});
|
||||
|
||||
test('given absolute path outside of root', () {
|
||||
expect(builder.relative(r'C:\a\b'), r'..\..\a\b');
|
||||
expect(builder.relative(r'C:\root\path\a'), 'a');
|
||||
expect(builder.relative(r'C:\root\path\a\b.txt'), r'a\b.txt');
|
||||
expect(builder.relative(r'C:\root\a\b.txt'), r'..\a\b.txt');
|
||||
});
|
||||
|
||||
test('given relative path', () {
|
||||
// The path is considered relative to the root, so it basically just
|
||||
// normalizes.
|
||||
expect(builder.relative(''), '.');
|
||||
expect(builder.relative('.'), '.');
|
||||
expect(builder.relative('a'), 'a');
|
||||
expect(builder.relative(r'a\b.txt'), r'a\b.txt');
|
||||
expect(builder.relative(r'..\a\b.txt'), r'..\a\b.txt');
|
||||
expect(builder.relative(r'a\.\b\..\c.txt'), r'a\c.txt');
|
||||
});
|
||||
});
|
||||
|
||||
group('from relative root', () {
|
||||
var r = new path.Builder(style: path.Style.windows, root: r'foo\bar');
|
||||
|
||||
// These tests rely on the current working directory, so don't do the
|
||||
// right thing if you run them on the wrong platform.
|
||||
if (io.Platform.operatingSystem == 'windows') {
|
||||
test('given absolute path', () {
|
||||
var b = new path.Builder(style: path.Style.windows);
|
||||
expect(r.relative(r'C:\'), b.join(b.relative(r'C:\'), r'..\..'));
|
||||
expect(r.relative(r'C:\a\b'),
|
||||
b.join(b.relative(r'C:\'), r'..\..\a\b'));
|
||||
});
|
||||
}
|
||||
|
||||
test('given relative path', () {
|
||||
// The path is considered relative to the root, so it basically just
|
||||
// normalizes.
|
||||
expect(r.relative(''), '.');
|
||||
expect(r.relative('.'), '.');
|
||||
expect(r.relative('..'), '..');
|
||||
expect(r.relative('a'), 'a');
|
||||
expect(r.relative(r'a\b.txt'), r'a\b.txt');
|
||||
expect(r.relative(r'..\a/b.txt'), r'..\a\b.txt');
|
||||
expect(r.relative(r'a\./b\../c.txt'), r'a\c.txt');
|
||||
});
|
||||
});
|
||||
|
||||
test('given absolute with differnt root prefix', () {
|
||||
expect(builder.relative(r'D:\a\b'), r'D:\a\b');
|
||||
expect(builder.relative(r'\\a\b'), r'\\a\b');
|
||||
});
|
||||
});
|
||||
|
||||
group('resolve', () {
|
||||
test('allows up to seven parts', () {
|
||||
expect(builder.resolve('a'), r'C:\root\path\a');
|
||||
expect(builder.resolve('a', 'b'), r'C:\root\path\a\b');
|
||||
expect(builder.resolve('a', 'b', 'c'), r'C:\root\path\a\b\c');
|
||||
expect(builder.resolve('a', 'b', 'c', 'd'), r'C:\root\path\a\b\c\d');
|
||||
expect(builder.resolve('a', 'b', 'c', 'd', 'e'),
|
||||
r'C:\root\path\a\b\c\d\e');
|
||||
expect(builder.resolve('a', 'b', 'c', 'd', 'e', 'f'),
|
||||
r'C:\root\path\a\b\c\d\e\f');
|
||||
expect(builder.resolve('a', 'b', 'c', 'd', 'e', 'f', 'g'),
|
||||
r'C:\root\path\a\b\c\d\e\f\g');
|
||||
});
|
||||
|
||||
test('does not add separator if a part ends in one', () {
|
||||
expect(builder.resolve(r'a\', 'b', r'c\', 'd'), r'C:\root\path\a\b\c\d');
|
||||
expect(builder.resolve('a/', 'b'), r'C:\root\path\a/b');
|
||||
});
|
||||
|
||||
test('ignores parts before an absolute path', () {
|
||||
expect(builder.resolve('a', '/b', '/c', 'd'), r'C:\root\path\a/b/c\d');
|
||||
expect(builder.resolve('a', r'c:\b', 'c', 'd'), r'c:\b\c\d');
|
||||
expect(builder.resolve('a', r'\\b', r'\\c', 'd'), r'\\c\d');
|
||||
});
|
||||
});
|
||||
|
||||
test('withoutExtension', () {
|
||||
expect(builder.withoutExtension(''), '');
|
||||
expect(builder.withoutExtension('a'), 'a');
|
||||
expect(builder.withoutExtension('.a'), '.a');
|
||||
expect(builder.withoutExtension('a.b'), 'a');
|
||||
expect(builder.withoutExtension(r'a\b.c'), r'a\b');
|
||||
expect(builder.withoutExtension(r'a\b.c.d'), r'a\b.c');
|
||||
expect(builder.withoutExtension(r'a\'), r'a\');
|
||||
expect(builder.withoutExtension(r'a\b\'), r'a\b\');
|
||||
expect(builder.withoutExtension(r'a\.'), r'a\.');
|
||||
expect(builder.withoutExtension(r'a\.b'), r'a\.b');
|
||||
expect(builder.withoutExtension(r'a.b\c'), r'a.b\c');
|
||||
expect(builder.withoutExtension(r'a\b/c'), r'a\b/c');
|
||||
expect(builder.withoutExtension(r'a\b/c.d'), r'a\b/c');
|
||||
});
|
||||
}
|
|
@ -23,6 +23,7 @@ import '../../pub/entrypoint.dart';
|
|||
import '../../pub/git_source.dart';
|
||||
import '../../pub/hosted_source.dart';
|
||||
import '../../pub/io.dart';
|
||||
import '../../pub/path.dart' as path;
|
||||
import '../../pub/sdk_source.dart';
|
||||
import '../../pub/system_cache.dart';
|
||||
import '../../pub/utils.dart';
|
||||
|
@ -527,15 +528,15 @@ void run() {
|
|||
// If an error occurs during testing, delete the sandbox, throw the error so
|
||||
// that the test framework sees it, then finally call asyncDone so that the
|
||||
// test framework knows we're done doing asynchronous stuff.
|
||||
var future = _runScheduled(createdSandboxDir, _scheduledOnException)
|
||||
var subFuture = _runScheduled(createdSandboxDir, _scheduledOnException)
|
||||
.chain((_) => cleanup());
|
||||
future.handleException((e) {
|
||||
subFuture.handleException((e) {
|
||||
print("Exception while cleaning up: $e");
|
||||
print(future.stackTrace);
|
||||
registerException(error, future.stackTrace);
|
||||
print(subFuture.stackTrace);
|
||||
registerException(error, subFuture.stackTrace);
|
||||
return true;
|
||||
});
|
||||
future.then((_) => registerException(error, future.stackTrace));
|
||||
subFuture.then((_) => registerException(error, future.stackTrace));
|
||||
return true;
|
||||
});
|
||||
|
||||
|
@ -546,10 +547,10 @@ void run() {
|
|||
|
||||
/// Get the path to the root "util/test/pub" directory containing the pub tests.
|
||||
String get testDirectory {
|
||||
var dir = new Path.fromNative(new Options().script);
|
||||
while (dir.filename != 'pub') dir = dir.directoryPath;
|
||||
var dir = new Options().script;
|
||||
while (basename(dir) != 'pub') dir = dirname(dir);
|
||||
|
||||
return new File(dir.toNativePath()).fullPathSync();
|
||||
return getFullPath(dir);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue