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:
nweiz@google.com 2012-12-08 03:47:03 +00:00
parent 4b0a76bc2d
commit f813520a0a
10 changed files with 1230 additions and 106 deletions

View file

@ -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;

View file

@ -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

View file

@ -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
View 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();
}
}

View file

@ -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);

View file

@ -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",');
}
});

View 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');
});
}

View 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);
});
}

View 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');
});
}

View file

@ -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);
}
/**