Use URIs rather than paths in front end API.
This carries a number of benefits: - It allows the front end to trivially support schemes other than "file:" (e.g. "http:") by allowing the client to supply a FileSystem implementation that handles them. - It is more consistent with the functionality of the ".packages" file (which allows packages to map to any kind of URI). - It allows the "bazel root" feature to be rewritten to use a magic scheme rather than a magic path. (This eliminates concerns about the magic path overlapping with a user's use case). Note that this feature has been renamed to "multi root" since it is sufficiently generic to be applicable to build systems other than Bazel. - It reduces the risk of forgetting to use the front end's FileSystem abstraction to access the file system, since the native file system interfaces do not accept URIs. R=danrubel@google.com Review-Url: https://codereview.chromium.org/2614063007 .
This commit is contained in:
@@ -18,13 +18,13 @@ typedef void ErrorHandler(CompilationError error);
|
||||
///
|
||||
/// Not intended to be implemented or extended by clients.
|
||||
class CompilerOptions {
|
||||
/// The path to the Dart SDK.
|
||||
/// The URI of the root of the Dart SDK (typically a "file:" URI).
|
||||
///
|
||||
/// If `null`, the SDK will be searched for using
|
||||
/// [Platform.resolvedExecutable] as a starting point.
|
||||
///
|
||||
/// This option is mutually exclusive with [sdkSummary].
|
||||
String sdkPath;
|
||||
Uri sdkRoot;
|
||||
|
||||
/// Callback to which compilation errors should be delivered.
|
||||
///
|
||||
@@ -32,56 +32,58 @@ class CompilerOptions {
|
||||
/// type [CompilationError].
|
||||
ErrorHandler onError = defaultErrorHandler;
|
||||
|
||||
/// Path to the ".packages" file.
|
||||
/// URI of the ".packages" file (typically a "file:" URI).
|
||||
///
|
||||
/// If `null`, the ".packages" file will be found via the standard
|
||||
/// package_config search algorithm.
|
||||
///
|
||||
/// If the empty string, no packages file will be used.
|
||||
String packagesFilePath;
|
||||
/// If the URI's path component is empty (e.g. `new Uri()`), no packages file
|
||||
/// will be used.
|
||||
Uri packagesFileUri;
|
||||
|
||||
/// Paths to the input summary files (excluding the SDK summary). These files
|
||||
/// should all be linked summaries. They should also be closed, in the sense
|
||||
/// that any libraries they reference should also appear in [inputSummaries]
|
||||
/// or [sdkSummary].
|
||||
List<String> inputSummaries = [];
|
||||
/// URIs of input summary files (excluding the SDK summary; typically these
|
||||
/// will be "file:" URIs). These files should all be linked summaries. They
|
||||
/// should also be closed, in the sense that any libraries they reference
|
||||
/// should also appear in [inputSummaries] or [sdkSummary].
|
||||
List<Uri> inputSummaries = [];
|
||||
|
||||
/// Path to the SDK summary file.
|
||||
/// URI of the SDK summary file (typically a "file:" URI).
|
||||
///
|
||||
/// This should be a linked summary. If `null`, the SDK summary will be
|
||||
/// searched for at a default location within [sdkPath].
|
||||
/// searched for at a default location within [sdkRoot].
|
||||
///
|
||||
/// This option is mutually exclusive with [sdkPath]. TODO(paulberry): if the
|
||||
/// This option is mutually exclusive with [sdkRoot]. TODO(paulberry): if the
|
||||
/// VM does not contain a pickled copy of the SDK, we might need to change
|
||||
/// this.
|
||||
String sdkSummary;
|
||||
Uri sdkSummary;
|
||||
|
||||
/// URI override map.
|
||||
///
|
||||
/// This is a map from Uri to file path. Any URI override listed in this map
|
||||
/// takes precedence over the URI resolution that would be implied by the
|
||||
/// packages file (see [packagesFilePath]) and/or [bazelRoots].
|
||||
/// This is a map from URIs that might appear in import/export/part statements
|
||||
/// to URIs that should be used to locate the corresponding files in the
|
||||
/// [fileSystem]. Any URI override listed in this map takes precedence over
|
||||
/// the URI resolution that would be implied by the packages file (see
|
||||
/// [packagesFileUri]) and/or [multiRoots].
|
||||
///
|
||||
/// If a URI is not listed in this map, then the normal URI resolution
|
||||
/// algorithm will be used.
|
||||
///
|
||||
/// TODO(paulberry): transition analyzer and dev_compiler to use the
|
||||
/// "file:///bazel-root" mechanism, and then remove this.
|
||||
/// "multi-root:" mechanism, and then remove this.
|
||||
@deprecated
|
||||
Map<Uri, String> uriOverride = {};
|
||||
Map<Uri, Uri> uriOverride = {};
|
||||
|
||||
/// Bazel roots.
|
||||
/// Multi-roots.
|
||||
///
|
||||
/// Any Uri that resolves to "file:///bazel-root/$rest" will be searched for
|
||||
/// at "$root/$rest" ("$root\\$rest" in Windows), where "$root" is drawn from
|
||||
/// this list. If the file is not found at any of those locations, the URI
|
||||
/// "file:///bazel-root/$rest" will be used directly.
|
||||
/// Any Uri that resolves to "multi-root:///$rest" will be searched for
|
||||
/// at "$root/$rest", where "$root" is drawn from this list.
|
||||
///
|
||||
/// Intended use: if the Bazel workspace is located at path "$workspace", this
|
||||
/// could be set to `['$workspace', '$workspace/bazel-bin',
|
||||
/// '$workspace/bazel-genfiles']`, effectively overlaying source and generated
|
||||
/// files.
|
||||
List<String> bazelRoots = [];
|
||||
/// Intended use: if the user has a Bazel workspace located at path
|
||||
/// "$workspace", this could be set to the file URIs corresponding to the
|
||||
/// paths for "$workspace", "$workspace/bazel-bin",
|
||||
/// and "$workspace/bazel-genfiles", effectively overlaying source and
|
||||
/// generated files.
|
||||
List<Uri> multiRoots = [];
|
||||
|
||||
/// Sets the platform bit, which determines which patch files should be
|
||||
/// applied to the SDK.
|
||||
@@ -99,7 +101,7 @@ class CompilerOptions {
|
||||
///
|
||||
/// All file system access performed by the front end goes through this
|
||||
/// mechanism, with one exception: if no value is specified for
|
||||
/// [packagesFilePath], the packages file is located using the actual physical
|
||||
/// [packagesFileUri], the packages file is located using the actual physical
|
||||
/// file system. TODO(paulberry): fix this.
|
||||
FileSystem fileSystem = PhysicalFileSystem.instance;
|
||||
|
||||
|
||||
@@ -18,22 +18,17 @@ import 'package:path/path.dart' as path;
|
||||
/// Not intended to be implemented or extended by clients.
|
||||
abstract class FileSystem {
|
||||
/// Returns a path context suitable for use with this [FileSystem].
|
||||
///
|
||||
/// TODO(paulberry): try to eliminate all usages of this. Since the
|
||||
/// FileSystem API now uses URIs rather than paths, it should not be needed.
|
||||
path.Context get context;
|
||||
|
||||
/// Returns a [FileSystemEntity] corresponding to the given [path].
|
||||
///
|
||||
/// Uses of `..` and `.` in path are normalized before returning (so, for
|
||||
/// example, `entityForPath('./foo')` and `entityForPath('foo')` are
|
||||
/// equivalent). Relative paths are also converted to absolute paths.
|
||||
///
|
||||
/// Does not check whether a file or folder exists at the given location.
|
||||
FileSystemEntity entityForPath(String path);
|
||||
|
||||
/// Returns a [FileSystemEntity] corresponding to the given [uri].
|
||||
///
|
||||
/// Uses of `..` and `.` in the URI are normalized before returning.
|
||||
///
|
||||
/// If [uri] is not an absolute `file:` URI, an [Error] will be thrown.
|
||||
/// If the URI scheme is not supported by this file system, an [Error] will be
|
||||
/// thrown.
|
||||
///
|
||||
/// Does not check whether a file or folder exists at the given location.
|
||||
FileSystemEntity entityForUri(Uri uri);
|
||||
@@ -46,14 +41,12 @@ abstract class FileSystem {
|
||||
///
|
||||
/// Not intended to be implemented or extended by clients.
|
||||
abstract class FileSystemEntity {
|
||||
/// Returns the absolute normalized path represented by this file system
|
||||
/// Returns the absolute normalized URI represented by this file system
|
||||
/// entity.
|
||||
///
|
||||
/// Note: if the [FileSystemEntity] was created using
|
||||
/// [FileSystem.entityForPath], this is not necessarily the same as the path
|
||||
/// that was used to create the object, since the path might have been
|
||||
/// normalized.
|
||||
String get path;
|
||||
/// Note: this is not necessarily the same as the URI that was passed to
|
||||
/// [FileSystem.entityForUri], since the URI might have been normalized.
|
||||
Uri get uri;
|
||||
|
||||
/// Attempts to access this file system entity as a file and read its contents
|
||||
/// as raw bytes.
|
||||
|
||||
@@ -59,7 +59,7 @@ Future<Program> kernelForProgram(Uri source, CompilerOptions options) async {
|
||||
/// which will be read are those listed in [sources],
|
||||
/// [CompilerOptions.inputSummaries], and [CompilerOptions.sdkSummary]. If a
|
||||
/// source file attempts to refer to a file which is not obtainable from these
|
||||
/// paths, that will result in an error, even if the file exists on the
|
||||
/// URIs, that will result in an error, even if the file exists on the
|
||||
/// filesystem.
|
||||
///
|
||||
/// When [CompilerOptions.chaseDependencies] is true, this default behavior
|
||||
@@ -128,7 +128,8 @@ Future<Program> kernelForBuildUnit(
|
||||
Future<DartLoader> _createLoader(CompilerOptions options,
|
||||
{Repository repository, Uri entry}) async {
|
||||
var kernelOptions = _convertOptions(options);
|
||||
var packages = await createPackages(options.packagesFilePath,
|
||||
var packages = await createPackages(
|
||||
_uriToPath(options.packagesFileUri, options),
|
||||
discoveryPath: entry?.path);
|
||||
return new DartLoader(
|
||||
repository ?? new Repository(), kernelOptions, packages);
|
||||
@@ -137,11 +138,12 @@ Future<DartLoader> _createLoader(CompilerOptions options,
|
||||
DartOptions _convertOptions(CompilerOptions options) {
|
||||
return new DartOptions(
|
||||
strongMode: options.strongMode,
|
||||
sdk: options.sdkPath,
|
||||
sdk: _uriToPath(options.sdkRoot, options),
|
||||
// TODO(sigmund): make it possible to use summaries and still compile the
|
||||
// sdk sources.
|
||||
sdkSummary: options.compileSdk ? null : options.sdkSummary,
|
||||
packagePath: options.packagesFilePath,
|
||||
sdkSummary:
|
||||
options.compileSdk ? null : _uriToPath(options.sdkSummary, options),
|
||||
packagePath: _uriToPath(options.packagesFileUri, options),
|
||||
declaredVariables: options.declaredVariables);
|
||||
}
|
||||
|
||||
@@ -152,6 +154,14 @@ void _reportErrors(List errors, ErrorHandler onError) {
|
||||
}
|
||||
}
|
||||
|
||||
String _uriToPath(Uri uri, CompilerOptions options) {
|
||||
if (uri == null) return null;
|
||||
if (uri.scheme != 'file') {
|
||||
throw new StateError('Only file URIs are supported');
|
||||
}
|
||||
return options.fileSystem.context.fromUri(uri);
|
||||
}
|
||||
|
||||
// TODO(sigmund): delete this class. Dartk should not format errors itself, we
|
||||
// should just pass them along.
|
||||
class _DartkError implements CompilationError {
|
||||
|
||||
@@ -20,26 +20,29 @@ class MemoryFileSystem implements FileSystem {
|
||||
@override
|
||||
final p.Context context;
|
||||
|
||||
final Map<String, Uint8List> _files = {};
|
||||
final Map<Uri, Uint8List> _files = {};
|
||||
|
||||
/// The "current directory" in the in-memory virtual file system.
|
||||
///
|
||||
/// This is used to convert relative paths to absolute paths.
|
||||
String currentDirectory;
|
||||
/// This is used to convert relative URIs to absolute URIs.
|
||||
///
|
||||
/// Always ends in a trailing '/'.
|
||||
Uri currentDirectory;
|
||||
|
||||
MemoryFileSystem(this.context, this.currentDirectory);
|
||||
|
||||
@override
|
||||
MemoryFileSystemEntity entityForPath(String path) =>
|
||||
new MemoryFileSystemEntity._(
|
||||
this, context.normalize(context.join(currentDirectory, path)));
|
||||
MemoryFileSystem(this.context, Uri currentDirectory)
|
||||
: currentDirectory = _addTrailingSlash(currentDirectory);
|
||||
|
||||
@override
|
||||
MemoryFileSystemEntity entityForUri(Uri uri) {
|
||||
if (uri.scheme != 'file') throw new ArgumentError('File URI expected');
|
||||
// Note: we don't have to verify that the URI's path is absolute, because
|
||||
// URIs with non-empty schemes always have absolute paths.
|
||||
return entityForPath(context.fromUri(uri));
|
||||
return new MemoryFileSystemEntity._(
|
||||
this, currentDirectory.resolveUri(uri).normalizePath());
|
||||
}
|
||||
|
||||
static Uri _addTrailingSlash(Uri uri) {
|
||||
if (!uri.path.endsWith('/')) {
|
||||
uri = uri.replace(path: uri.path + '/');
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,22 +52,22 @@ class MemoryFileSystemEntity implements FileSystemEntity {
|
||||
final MemoryFileSystem _fileSystem;
|
||||
|
||||
@override
|
||||
final String path;
|
||||
final Uri uri;
|
||||
|
||||
MemoryFileSystemEntity._(this._fileSystem, this.path);
|
||||
MemoryFileSystemEntity._(this._fileSystem, this.uri);
|
||||
|
||||
@override
|
||||
int get hashCode => path.hashCode;
|
||||
int get hashCode => uri.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
other is MemoryFileSystemEntity &&
|
||||
other.path == path &&
|
||||
other.uri == uri &&
|
||||
identical(other._fileSystem, _fileSystem);
|
||||
|
||||
@override
|
||||
Future<List<int>> readAsBytes() async {
|
||||
List<int> contents = _fileSystem._files[path];
|
||||
List<int> contents = _fileSystem._files[uri];
|
||||
if (contents != null) {
|
||||
return contents.toList();
|
||||
}
|
||||
@@ -82,7 +85,7 @@ class MemoryFileSystemEntity implements FileSystemEntity {
|
||||
/// If no file exists, one is created. If a file exists already, it is
|
||||
/// overwritten.
|
||||
void writeAsBytesSync(List<int> bytes) {
|
||||
_fileSystem._files[path] = new Uint8List.fromList(bytes);
|
||||
_fileSystem._files[uri] = new Uint8List.fromList(bytes);
|
||||
}
|
||||
|
||||
/// Writes the given string to this file system entity.
|
||||
@@ -95,6 +98,6 @@ class MemoryFileSystemEntity implements FileSystemEntity {
|
||||
// Note: the return type of UTF8.encode is List<int>, but in practice it
|
||||
// always returns Uint8List. We rely on that for efficiency, so that we
|
||||
// don't have to make an extra copy.
|
||||
_fileSystem._files[path] = UTF8.encode(s) as Uint8List;
|
||||
_fileSystem._files[uri] = UTF8.encode(s) as Uint8List;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,37 +23,39 @@ class PhysicalFileSystem implements FileSystem {
|
||||
@override
|
||||
p.Context get context => p.context;
|
||||
|
||||
@override
|
||||
FileSystemEntity entityForPath(String path) =>
|
||||
new _PhysicalFileSystemEntity(context.normalize(context.absolute(path)));
|
||||
|
||||
@override
|
||||
FileSystemEntity entityForUri(Uri uri) {
|
||||
if (uri.scheme != 'file') throw new ArgumentError('File URI expected');
|
||||
if (uri.scheme != 'file' && uri.scheme != '') {
|
||||
throw new ArgumentError('File URI expected');
|
||||
}
|
||||
// Note: we don't have to verify that the URI's path is absolute, because
|
||||
// URIs with non-empty schemes always have absolute paths.
|
||||
return entityForPath(context.fromUri(uri));
|
||||
var path = context.fromUri(uri);
|
||||
return new _PhysicalFileSystemEntity(
|
||||
context.normalize(context.absolute(path)));
|
||||
}
|
||||
}
|
||||
|
||||
/// Concrete implementation of [FileSystemEntity] for use by
|
||||
/// [PhysicalFileSystem].
|
||||
class _PhysicalFileSystemEntity implements FileSystemEntity {
|
||||
@override
|
||||
final String path;
|
||||
final String _path;
|
||||
|
||||
_PhysicalFileSystemEntity(this.path);
|
||||
_PhysicalFileSystemEntity(this._path);
|
||||
|
||||
@override
|
||||
int get hashCode => path.hashCode;
|
||||
int get hashCode => _path.hashCode;
|
||||
|
||||
@override
|
||||
Uri get uri => p.toUri(_path);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
other is _PhysicalFileSystemEntity && other.path == path;
|
||||
other is _PhysicalFileSystemEntity && other._path == _path;
|
||||
|
||||
@override
|
||||
Future<List<int>> readAsBytes() => new io.File(path).readAsBytes();
|
||||
Future<List<int>> readAsBytes() => new io.File(_path).readAsBytes();
|
||||
|
||||
@override
|
||||
Future<String> readAsString() => new io.File(path).readAsString();
|
||||
Future<String> readAsString() => new io.File(_path).readAsString();
|
||||
}
|
||||
|
||||
@@ -39,11 +39,11 @@ class ProcessedOptions {
|
||||
/// Get the [FileSystem] which should be used by the front end to access
|
||||
/// files.
|
||||
///
|
||||
/// If the client supplied bazel roots using [CompilerOptions.bazelRoots], the
|
||||
/// If the client supplied roots using [CompilerOptions.multiRoots], the
|
||||
/// returned [FileSystem] will automatically perform the appropriate mapping.
|
||||
FileSystem get fileSystem {
|
||||
// TODO(paulberry): support bazelRoots.
|
||||
assert(_raw.bazelRoots.isEmpty);
|
||||
// TODO(paulberry): support multiRoots.
|
||||
assert(_raw.multiRoots.isEmpty);
|
||||
return _raw.fileSystem;
|
||||
}
|
||||
|
||||
@@ -56,8 +56,7 @@ class ProcessedOptions {
|
||||
await _getPackages();
|
||||
var sdkLibraries =
|
||||
<String, Uri>{}; // TODO(paulberry): support SDK libraries
|
||||
_uriResolver =
|
||||
new UriResolver(_packages, sdkLibraries, fileSystem.context);
|
||||
_uriResolver = new UriResolver(_packages, sdkLibraries);
|
||||
}
|
||||
return _uriResolver;
|
||||
}
|
||||
@@ -68,15 +67,14 @@ class ProcessedOptions {
|
||||
/// required to locate/read the packages file.
|
||||
Future<Map<String, Uri>> _getPackages() async {
|
||||
if (_packages == null) {
|
||||
if (_raw.packagesFilePath == null) {
|
||||
if (_raw.packagesFileUri == null) {
|
||||
throw new UnimplementedError(); // TODO(paulberry): search for .packages
|
||||
} else if (_raw.packagesFilePath.isEmpty) {
|
||||
} else if (_raw.packagesFileUri.path.isEmpty) {
|
||||
_packages = {};
|
||||
} else {
|
||||
var contents =
|
||||
await fileSystem.entityForPath(_raw.packagesFilePath).readAsBytes();
|
||||
var baseLocation = fileSystem.context.toUri(_raw.packagesFilePath);
|
||||
_packages = package_config.parse(contents, baseLocation);
|
||||
await fileSystem.entityForUri(_raw.packagesFileUri).readAsBytes();
|
||||
_packages = package_config.parse(contents, _raw.packagesFileUri);
|
||||
}
|
||||
}
|
||||
return _packages;
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
// 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.
|
||||
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
/// The class `UriResolver` implements the rules for resolving URIs to file
|
||||
/// paths.
|
||||
///
|
||||
/// TODO(paulberry): Is it necessary to support the "http" scheme?
|
||||
/// The class `UriResolver` implements the rules for resolving "dart:" and
|
||||
/// "package:" URIs.
|
||||
class UriResolver {
|
||||
/// The URI scheme used for "package" URIs.
|
||||
static const PACKAGE_SCHEME = 'package';
|
||||
|
||||
/// The URI scheme used for "dart" URIs.
|
||||
static const DART_SCHEME = 'dart';
|
||||
|
||||
/// A map from package name to the file URI of the "lib" directory of the
|
||||
/// corresponding package. This is equivalent to the format returned by
|
||||
/// the "package_config" package's parse() function.
|
||||
@@ -18,36 +20,27 @@ class UriResolver {
|
||||
/// of the defining compilation unit of the SDK library.
|
||||
final Map<String, Uri> sdkLibraries;
|
||||
|
||||
/// The path context which should be used to convert from file URIs to file
|
||||
/// paths.
|
||||
final p.Context pathContext;
|
||||
UriResolver(this.packages, this.sdkLibraries);
|
||||
|
||||
/// The URI scheme used for "package" URIs.
|
||||
static const PACKAGE_SCHEME = 'package';
|
||||
|
||||
/// The URI scheme used for "dart" URIs.
|
||||
static const DART_SCHEME = 'dart';
|
||||
|
||||
/// The URI scheme used for "file" URIs.
|
||||
static const FILE_SCHEME = 'file';
|
||||
|
||||
UriResolver(this.packages, this.sdkLibraries, this.pathContext);
|
||||
|
||||
/// Converts a URI to a file path.
|
||||
/// Converts "package:" and "dart:" URIs to the locations of the corresponding
|
||||
/// files.
|
||||
///
|
||||
/// If the given URI is valid, and of a recognized form, returns the file path
|
||||
/// it corresponds to. Otherwise returns `null`. It is not necessary for the
|
||||
/// URI to be absolute (relative URIs will be converted to relative file
|
||||
/// paths).
|
||||
/// If the given URI is a "package:" or "dart:" URI, is well formed, and names
|
||||
/// a package or dart library that is recognized, returns the URI it resolves
|
||||
/// to. If the given URI is a "package:" or "dart:" URI, and is ill-formed
|
||||
/// or names a package or dart library that is not recognized, returns `null`.
|
||||
///
|
||||
/// Note that no I/O is performed; the file path that is returned will be
|
||||
/// If the given URI has any scheme other than "package:" or "dart:", it is
|
||||
/// returned unchanged.
|
||||
///
|
||||
/// It is not necessary for the URI to be absolute (relative URIs will be
|
||||
/// passed through unchanged).
|
||||
///
|
||||
/// Note that no I/O is performed; the URI that is returned will be
|
||||
/// independent of whether or not any particular file exists on the file
|
||||
/// system.
|
||||
String resolve(Uri uri) {
|
||||
Uri fileUri;
|
||||
if (uri.scheme == FILE_SCHEME) {
|
||||
fileUri = uri;
|
||||
} else {
|
||||
Uri resolve(Uri uri) {
|
||||
if (uri.scheme == DART_SCHEME || uri.scheme == PACKAGE_SCHEME) {
|
||||
var path = uri.path;
|
||||
var slashIndex = path.indexOf('/');
|
||||
String prefix;
|
||||
@@ -67,9 +60,9 @@ class UriResolver {
|
||||
libUri = sdkLibraries[prefix];
|
||||
}
|
||||
if (libUri == null) return null;
|
||||
fileUri = libUri.resolve(rest);
|
||||
if (fileUri.scheme != FILE_SCHEME) return null;
|
||||
return libUri.resolve(rest);
|
||||
} else {
|
||||
return uri;
|
||||
}
|
||||
return pathContext.fromUri(fileUri);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,13 +99,14 @@ class _WalkerNode extends Node<_WalkerNode> {
|
||||
Future<List<_WalkerNode>> computeDependencies() async {
|
||||
var dependencies = <_WalkerNode>[];
|
||||
// TODO(paulberry): add error recovery if the file can't be read.
|
||||
var path = walker.uriResolver.resolve(uri);
|
||||
if (path == null) {
|
||||
var resolvedUri = walker.uriResolver.resolve(uri);
|
||||
if (resolvedUri == null) {
|
||||
// TODO(paulberry): If an error reporter was provided, report the error
|
||||
// in the proper way and continue.
|
||||
throw new StateError('Invalid URI: $uri');
|
||||
}
|
||||
var contents = await walker.fileSystem.entityForPath(path).readAsString();
|
||||
var contents =
|
||||
await walker.fileSystem.entityForUri(resolvedUri).readAsString();
|
||||
var scanner = new _Scanner(contents);
|
||||
var token = scanner.tokenize();
|
||||
// TODO(paulberry): report errors.
|
||||
|
||||
@@ -35,15 +35,17 @@ class DependencyGrapherTest {
|
||||
// If no starting points given, assume the first entry in [contents] is the
|
||||
// single starting point.
|
||||
startingPoints ??= [contents.keys.first];
|
||||
var fileSystem = new MemoryFileSystem(pathos.posix, '/');
|
||||
var fileSystem = new MemoryFileSystem(pathos.posix, Uri.parse('file:///'));
|
||||
contents.forEach((path, text) {
|
||||
fileSystem.entityForPath(path).writeAsStringSync(text);
|
||||
fileSystem.entityForUri(pathos.posix.toUri(path)).writeAsStringSync(text);
|
||||
});
|
||||
// TODO(paulberry): implement and test other option possibilities.
|
||||
var options = new CompilerOptions()
|
||||
..fileSystem = fileSystem
|
||||
..chaseDependencies = true
|
||||
..packagesFilePath = packagesFilePath;
|
||||
..packagesFileUri = packagesFilePath == ''
|
||||
? new Uri()
|
||||
: pathos.posix.toUri(packagesFilePath);
|
||||
var graph = await graphForProgram(
|
||||
startingPoints.map(pathos.posix.toUri).toList(), options);
|
||||
return graph.topologicallySortedCycles;
|
||||
|
||||
@@ -30,26 +30,23 @@ class FileTest extends _BaseTestNative {
|
||||
setUp() {
|
||||
super.setUp();
|
||||
path = join(tempPath, 'file.txt');
|
||||
file = fileSystem.entityForPath(path);
|
||||
file = entityForPath(path);
|
||||
}
|
||||
|
||||
test_equals_differentPaths() {
|
||||
expect(
|
||||
file == fileSystem.entityForPath(join(tempPath, 'file2.txt')), isFalse);
|
||||
expect(file == entityForPath(join(tempPath, 'file2.txt')), isFalse);
|
||||
}
|
||||
|
||||
test_equals_samePath() {
|
||||
expect(
|
||||
file == fileSystem.entityForPath(join(tempPath, 'file.txt')), isTrue);
|
||||
expect(file == entityForPath(join(tempPath, 'file.txt')), isTrue);
|
||||
}
|
||||
|
||||
test_hashCode_samePath() {
|
||||
expect(file.hashCode,
|
||||
fileSystem.entityForPath(join(tempPath, 'file.txt')).hashCode);
|
||||
expect(file.hashCode, entityForPath(join(tempPath, 'file.txt')).hashCode);
|
||||
}
|
||||
|
||||
test_path() {
|
||||
expect(file.path, path);
|
||||
expect(file.uri, fileSystem.context.toUri(path));
|
||||
}
|
||||
|
||||
test_readAsBytes_badUtf8() async {
|
||||
@@ -128,40 +125,44 @@ abstract class MemoryFileSystemTestMixin extends _BaseTest {
|
||||
tempUri = fileSystem.context.toUri(tempPath);
|
||||
}
|
||||
|
||||
test_entityForPath() {
|
||||
var path = join(tempPath, 'file.txt');
|
||||
expect(fileSystem.entityForPath(path).path, path);
|
||||
test_currentDirectory_trailingSlash() {
|
||||
// The currentDirectory should already end in a trailing slash.
|
||||
expect(fileSystem.currentDirectory.path, endsWith('/'));
|
||||
// A trailing slash should automatically be appended when creating a
|
||||
// MemoryFileSystem.
|
||||
var path = fileSystem.currentDirectory.path;
|
||||
var currentDirectoryWithoutSlash = fileSystem.currentDirectory
|
||||
.replace(path: path.substring(0, path.length - 1));
|
||||
expect(
|
||||
new MemoryFileSystem(fileSystem.context, currentDirectoryWithoutSlash)
|
||||
.currentDirectory,
|
||||
fileSystem.currentDirectory);
|
||||
// If the currentDirectory supplied to the MemoryFileSystem constructor
|
||||
// already has a trailing slash, no further trailing slash should be added.
|
||||
expect(
|
||||
new MemoryFileSystem(fileSystem.context, fileSystem.currentDirectory)
|
||||
.currentDirectory,
|
||||
fileSystem.currentDirectory);
|
||||
}
|
||||
|
||||
test_entityForPath_absolutize() {
|
||||
expect(fileSystem.entityForPath('file.txt').path,
|
||||
join(fileSystem.currentDirectory, 'file.txt'));
|
||||
expect(entityForPath('file.txt').uri,
|
||||
fileSystem.currentDirectory.resolve('file.txt'));
|
||||
}
|
||||
|
||||
test_entityForPath_normalize_dot() {
|
||||
expect(fileSystem.entityForPath(join(tempPath, '.', 'file.txt')).path,
|
||||
join(tempPath, 'file.txt'));
|
||||
expect(entityForPath(join(tempPath, '.', 'file.txt')).uri,
|
||||
Uri.parse('$tempUri/file.txt'));
|
||||
}
|
||||
|
||||
test_entityForPath_normalize_dotDot() {
|
||||
expect(
|
||||
fileSystem.entityForPath(join(tempPath, 'foo', '..', 'file.txt')).path,
|
||||
join(tempPath, 'file.txt'));
|
||||
expect(entityForPath(join(tempPath, 'foo', '..', 'file.txt')).uri,
|
||||
Uri.parse('$tempUri/file.txt'));
|
||||
}
|
||||
|
||||
test_entityForUri() {
|
||||
expect(fileSystem.entityForUri(Uri.parse('$tempUri/file.txt')).path,
|
||||
join(tempPath, 'file.txt'));
|
||||
}
|
||||
|
||||
test_entityForUri_bareUri_absolute() {
|
||||
expect(() => fileSystem.entityForUri(Uri.parse('/file.txt')),
|
||||
throwsA(new isInstanceOf<Error>()));
|
||||
}
|
||||
|
||||
test_entityForUri_bareUri_relative() {
|
||||
expect(() => fileSystem.entityForUri(Uri.parse('file.txt')),
|
||||
throwsA(new isInstanceOf<Error>()));
|
||||
expect(fileSystem.entityForUri(Uri.parse('$tempUri/file.txt')).uri,
|
||||
Uri.parse('$tempUri/file.txt'));
|
||||
}
|
||||
|
||||
test_entityForUri_fileUri_relative() {
|
||||
@@ -184,18 +185,18 @@ abstract class MemoryFileSystemTestMixin extends _BaseTest {
|
||||
}
|
||||
|
||||
test_entityForUri_nonFileUri() {
|
||||
expect(() => fileSystem.entityForUri(Uri.parse('package:foo/bar.dart')),
|
||||
throwsA(new isInstanceOf<Error>()));
|
||||
var uri = Uri.parse('package:foo/bar.dart');
|
||||
expect(fileSystem.entityForUri(uri).uri, uri);
|
||||
}
|
||||
|
||||
test_entityForUri_normalize_dot() {
|
||||
expect(fileSystem.entityForUri(Uri.parse('$tempUri/./file.txt')).path,
|
||||
join(tempPath, 'file.txt'));
|
||||
expect(fileSystem.entityForUri(Uri.parse('$tempUri/./file.txt')).uri,
|
||||
Uri.parse('$tempUri/file.txt'));
|
||||
}
|
||||
|
||||
test_entityForUri_normalize_dotDot() {
|
||||
expect(fileSystem.entityForUri(Uri.parse('$tempUri/foo/../file.txt')).path,
|
||||
join(tempPath, 'file.txt'));
|
||||
expect(fileSystem.entityForUri(Uri.parse('$tempUri/foo/../file.txt')).uri,
|
||||
Uri.parse('$tempUri/file.txt'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,8 +214,14 @@ class MemoryFileSystemTestWindows extends _BaseTestWindows
|
||||
|
||||
abstract class _BaseTest {
|
||||
MemoryFileSystem get fileSystem;
|
||||
|
||||
String get tempPath;
|
||||
|
||||
MemoryFileSystemEntity entityForPath(String path) =>
|
||||
fileSystem.entityForUri(fileSystem.context.toUri(path));
|
||||
|
||||
String join(String path1, String path2, [String path3, String path4]);
|
||||
|
||||
void setUp();
|
||||
}
|
||||
|
||||
@@ -227,8 +234,8 @@ class _BaseTestNative extends _BaseTest {
|
||||
|
||||
setUp() {
|
||||
tempPath = pathos.join(io.Directory.systemTemp.path, 'test_file_system');
|
||||
fileSystem =
|
||||
new MemoryFileSystem(pathos.context, io.Directory.current.path);
|
||||
fileSystem = new MemoryFileSystem(
|
||||
pathos.context, pathos.toUri(io.Directory.current.path));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,7 +248,7 @@ class _BaseTestPosix extends _BaseTest {
|
||||
|
||||
void setUp() {
|
||||
tempPath = '/test_file_system';
|
||||
fileSystem = new MemoryFileSystem(pathos.posix, '/cwd');
|
||||
fileSystem = new MemoryFileSystem(pathos.posix, Uri.parse('file:///cwd'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,6 +261,7 @@ class _BaseTestWindows extends _BaseTest {
|
||||
|
||||
void setUp() {
|
||||
tempPath = r'c:\test_file_system';
|
||||
fileSystem = new MemoryFileSystem(pathos.windows, r'c:\cwd');
|
||||
fileSystem =
|
||||
new MemoryFileSystem(pathos.windows, Uri.parse('file:///c:/cwd'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,35 +30,19 @@ class FileTest extends _BaseTest {
|
||||
setUp() {
|
||||
super.setUp();
|
||||
path = p.join(tempPath, 'file.txt');
|
||||
file = PhysicalFileSystem.instance.entityForPath(path);
|
||||
file = PhysicalFileSystem.instance.entityForUri(p.toUri(path));
|
||||
}
|
||||
|
||||
test_equals_differentPaths() {
|
||||
expect(
|
||||
file ==
|
||||
PhysicalFileSystem.instance
|
||||
.entityForPath(p.join(tempPath, 'file2.txt')),
|
||||
isFalse);
|
||||
expect(file == entityForPath(p.join(tempPath, 'file2.txt')), isFalse);
|
||||
}
|
||||
|
||||
test_equals_samePath() {
|
||||
expect(
|
||||
file ==
|
||||
PhysicalFileSystem.instance
|
||||
.entityForPath(p.join(tempPath, 'file.txt')),
|
||||
isTrue);
|
||||
expect(file == entityForPath(p.join(tempPath, 'file.txt')), isTrue);
|
||||
}
|
||||
|
||||
test_hashCode_samePath() {
|
||||
expect(
|
||||
file.hashCode,
|
||||
PhysicalFileSystem.instance
|
||||
.entityForPath(p.join(tempPath, 'file.txt'))
|
||||
.hashCode);
|
||||
}
|
||||
|
||||
test_path() {
|
||||
expect(file.path, path);
|
||||
expect(file.hashCode, entityForPath(p.join(tempPath, 'file.txt')).hashCode);
|
||||
}
|
||||
|
||||
test_readAsBytes_badUtf8() async {
|
||||
@@ -98,6 +82,10 @@ class FileTest extends _BaseTest {
|
||||
new io.File(path).writeAsBytesSync(bytes);
|
||||
expect(await file.readAsString(), '\u20ac');
|
||||
}
|
||||
|
||||
test_uri() {
|
||||
expect(file.uri, p.toUri(path));
|
||||
}
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
@@ -111,48 +99,35 @@ class PhysicalFileSystemTest extends _BaseTest {
|
||||
|
||||
test_entityForPath() {
|
||||
var path = p.join(tempPath, 'file.txt');
|
||||
expect(PhysicalFileSystem.instance.entityForPath(path).path, path);
|
||||
expect(entityForPath(path).uri, p.toUri(path));
|
||||
}
|
||||
|
||||
test_entityForPath_absolutize() {
|
||||
expect(PhysicalFileSystem.instance.entityForPath('file.txt').path,
|
||||
new io.File('file.txt').absolute.path);
|
||||
expect(entityForPath('file.txt').uri,
|
||||
p.toUri(new io.File('file.txt').absolute.path));
|
||||
}
|
||||
|
||||
test_entityForPath_normalize_dot() {
|
||||
expect(
|
||||
PhysicalFileSystem.instance
|
||||
.entityForPath(p.join(tempPath, '.', 'file.txt'))
|
||||
.path,
|
||||
p.join(tempPath, 'file.txt'));
|
||||
expect(entityForPath(p.join(tempPath, '.', 'file.txt')).uri,
|
||||
p.toUri(p.join(tempPath, 'file.txt')));
|
||||
}
|
||||
|
||||
test_entityForPath_normalize_dotDot() {
|
||||
expect(
|
||||
PhysicalFileSystem.instance
|
||||
.entityForPath(p.join(tempPath, 'foo', '..', 'file.txt'))
|
||||
.path,
|
||||
p.join(tempPath, 'file.txt'));
|
||||
expect(entityForPath(p.join(tempPath, 'foo', '..', 'file.txt')).uri,
|
||||
p.toUri(p.join(tempPath, 'file.txt')));
|
||||
}
|
||||
|
||||
test_entityForUri() {
|
||||
expect(
|
||||
PhysicalFileSystem.instance
|
||||
.entityForUri(Uri.parse('$tempUri/file.txt'))
|
||||
.path,
|
||||
p.join(tempPath, 'file.txt'));
|
||||
.uri,
|
||||
p.toUri(p.join(tempPath, 'file.txt')));
|
||||
}
|
||||
|
||||
test_entityForUri_bareUri_absolute() {
|
||||
expect(
|
||||
() => PhysicalFileSystem.instance.entityForUri(Uri.parse('/file.txt')),
|
||||
throwsA(new isInstanceOf<Error>()));
|
||||
}
|
||||
|
||||
test_entityForUri_bareUri_relative() {
|
||||
expect(
|
||||
() => PhysicalFileSystem.instance.entityForUri(Uri.parse('file.txt')),
|
||||
throwsA(new isInstanceOf<Error>()));
|
||||
expect(PhysicalFileSystem.instance.entityForUri(Uri.parse('/file.txt')).uri,
|
||||
p.toUri(p.fromUri('/file.txt')));
|
||||
}
|
||||
|
||||
test_entityForUri_fileUri_relative() {
|
||||
@@ -185,16 +160,16 @@ class PhysicalFileSystemTest extends _BaseTest {
|
||||
expect(
|
||||
PhysicalFileSystem.instance
|
||||
.entityForUri(Uri.parse('$tempUri/./file.txt'))
|
||||
.path,
|
||||
p.join(tempPath, 'file.txt'));
|
||||
.uri,
|
||||
p.toUri(p.join(tempPath, 'file.txt')));
|
||||
}
|
||||
|
||||
test_entityForUri_normalize_dotDot() {
|
||||
expect(
|
||||
PhysicalFileSystem.instance
|
||||
.entityForUri(Uri.parse('$tempUri/foo/../file.txt'))
|
||||
.path,
|
||||
p.join(tempPath, 'file.txt'));
|
||||
.uri,
|
||||
p.toUri(p.join(tempPath, 'file.txt')));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,6 +177,9 @@ class _BaseTest {
|
||||
io.Directory tempDirectory;
|
||||
String tempPath;
|
||||
|
||||
FileSystemEntity entityForPath(String path) =>
|
||||
PhysicalFileSystem.instance.entityForUri(p.toUri(path));
|
||||
|
||||
setUp() {
|
||||
tempDirectory = io.Directory.systemTemp.createTempSync('test_file_system');
|
||||
tempPath = tempDirectory.absolute.path;
|
||||
|
||||
@@ -17,7 +17,7 @@ main() {
|
||||
|
||||
@reflectiveTest
|
||||
class ProcessedOptionsTest {
|
||||
final fileSystem = new MemoryFileSystem(pathos.posix, '/');
|
||||
final fileSystem = new MemoryFileSystem(pathos.posix, Uri.parse('file:///'));
|
||||
|
||||
test_compileSdk_false() {
|
||||
for (var value in [false, true]) {
|
||||
@@ -37,46 +37,49 @@ class ProcessedOptionsTest {
|
||||
|
||||
test_getUriResolver_explicitPackagesFile() async {
|
||||
// This .packages file should be ignored.
|
||||
fileSystem.entityForPath('/.packages').writeAsStringSync('foo:bar\n');
|
||||
fileSystem
|
||||
.entityForUri(Uri.parse('file:///.packages'))
|
||||
.writeAsStringSync('foo:bar\n');
|
||||
// This one should be used.
|
||||
fileSystem
|
||||
.entityForPath('/explicit.packages')
|
||||
.entityForUri(Uri.parse('file:///explicit.packages'))
|
||||
.writeAsStringSync('foo:baz\n');
|
||||
var raw = new CompilerOptions()
|
||||
..fileSystem = fileSystem
|
||||
..packagesFilePath = '/explicit.packages';
|
||||
..packagesFileUri = Uri.parse('file:///explicit.packages');
|
||||
var processed = new ProcessedOptions(raw);
|
||||
var uriResolver = await processed.getUriResolver();
|
||||
expect(uriResolver.packages, {'foo': Uri.parse('file:///baz/')});
|
||||
expect(uriResolver.pathContext, same(fileSystem.context));
|
||||
}
|
||||
|
||||
test_getUriResolver_explicitPackagesFile_withBaseLocation() async {
|
||||
// This .packages file should be ignored.
|
||||
fileSystem.entityForPath('/.packages').writeAsStringSync('foo:bar\n');
|
||||
fileSystem
|
||||
.entityForUri(Uri.parse('file:///.packages'))
|
||||
.writeAsStringSync('foo:bar\n');
|
||||
// This one should be used.
|
||||
fileSystem
|
||||
.entityForPath('/base/location/explicit.packages')
|
||||
.entityForUri(Uri.parse('file:///base/location/explicit.packages'))
|
||||
.writeAsStringSync('foo:baz\n');
|
||||
var raw = new CompilerOptions()
|
||||
..fileSystem = fileSystem
|
||||
..packagesFilePath = '/base/location/explicit.packages';
|
||||
..packagesFileUri = Uri.parse('file:///base/location/explicit.packages');
|
||||
var processed = new ProcessedOptions(raw);
|
||||
var uriResolver = await processed.getUriResolver();
|
||||
expect(
|
||||
uriResolver.packages, {'foo': Uri.parse('file:///base/location/baz/')});
|
||||
expect(uriResolver.pathContext, same(fileSystem.context));
|
||||
}
|
||||
|
||||
test_getUriResolver_noPackages() async {
|
||||
// .packages file should be ignored.
|
||||
fileSystem.entityForPath('/.packages').writeAsStringSync('foo:bar\n');
|
||||
fileSystem
|
||||
.entityForUri(Uri.parse('file:///.packages'))
|
||||
.writeAsStringSync('foo:bar\n');
|
||||
var raw = new CompilerOptions()
|
||||
..fileSystem = fileSystem
|
||||
..packagesFilePath = '';
|
||||
..packagesFileUri = new Uri();
|
||||
var processed = new ProcessedOptions(raw);
|
||||
var uriResolver = await processed.getUriResolver();
|
||||
expect(uriResolver.packages, isEmpty);
|
||||
expect(uriResolver.pathContext, same(fileSystem.context));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ abstract class UriResolverTest {
|
||||
p.Context get pathContext;
|
||||
|
||||
void test_badScheme() {
|
||||
_expectResolution('foo:bar/baz.dart', null);
|
||||
_expectResolutionUri('foo:bar/baz.dart', Uri.parse('foo:bar/baz.dart'));
|
||||
}
|
||||
|
||||
void test_dart() {
|
||||
@@ -54,11 +54,11 @@ abstract class UriResolverTest {
|
||||
}
|
||||
|
||||
void test_noSchemeAbsolute() {
|
||||
_expectResolution('/foo.dart', null);
|
||||
_expectResolutionUri('/foo.dart', Uri.parse('/foo.dart'));
|
||||
}
|
||||
|
||||
void test_noSchemeRelative() {
|
||||
_expectResolution('foo.dart', null);
|
||||
_expectResolution('foo.dart', 'foo.dart');
|
||||
}
|
||||
|
||||
void test_package() {
|
||||
@@ -102,6 +102,13 @@ abstract class UriResolverTest {
|
||||
/// Verifies that the resolution of [uriString] produces the path
|
||||
/// [expectedResult].
|
||||
void _expectResolution(String uriString, String expectedResult) {
|
||||
_expectResolutionUri(uriString,
|
||||
expectedResult == null ? null : pathContext.toUri(expectedResult));
|
||||
}
|
||||
|
||||
/// Verifies that the resolution of [uriString] produces the URI
|
||||
/// [expectedResult].
|
||||
void _expectResolutionUri(String uriString, Uri expectedResult) {
|
||||
var packages = {
|
||||
'foo': _u('packages/foo/lib/'),
|
||||
'bar': _u('packages/bar/lib/')
|
||||
@@ -110,7 +117,7 @@ abstract class UriResolverTest {
|
||||
'core': _u('sdk/lib/core/core.dart'),
|
||||
'async': _u('sdk/lib/async/async.dart')
|
||||
};
|
||||
var uriResolver = new UriResolver(packages, sdkLibraries, pathContext);
|
||||
var uriResolver = new UriResolver(packages, sdkLibraries);
|
||||
expect(uriResolver.resolve(Uri.parse(uriString)), expectedResult);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,17 +11,19 @@ Future dumpToSink(Program program, StreamSink<List<int>> sink) {
|
||||
}
|
||||
|
||||
Future kernelToSink(Uri entry, StreamSink<List<int>> sink) async {
|
||||
var program = await kernelForProgram(entry,
|
||||
var program = await kernelForProgram(
|
||||
entry,
|
||||
new CompilerOptions()
|
||||
..sdkPath = 'sdk'
|
||||
..packagesFilePath = '.packages'
|
||||
..sdkRoot = new Uri.file('sdk')
|
||||
..packagesFileUri = new Uri.file('.packages')
|
||||
..onError = (e) => print(e.message));
|
||||
|
||||
await dumpToSink(program, sink);
|
||||
}
|
||||
|
||||
main(args) async {
|
||||
kernelToSink(Uri.base.resolve(args[0]),
|
||||
kernelToSink(
|
||||
Uri.base.resolve(args[0]),
|
||||
// TODO(sigmund,hausner): define memory type where to dump binary data.
|
||||
new StreamController<List<int>>.broadcast().sink);
|
||||
}
|
||||
|
||||
@@ -115,15 +115,16 @@ Future<Program> generateKernel(Uri entryUri,
|
||||
var options = new CompilerOptions()
|
||||
..strongMode = false
|
||||
..compileSdk = compileSdk
|
||||
..packagesFilePath = '.packages'
|
||||
..packagesFileUri = new Uri.file('.packages')
|
||||
..onError = ((e) => print('${e.message}'));
|
||||
if (useSdkSummary) {
|
||||
// TODO(sigmund): adjust path based on the benchmark runner architecture.
|
||||
// Possibly let the runner make the file available at an architecture
|
||||
// independent location.
|
||||
options.sdkSummary = 'out/ReleaseX64/dart-sdk/lib/_internal/spec.sum';
|
||||
options.sdkSummary =
|
||||
new Uri.file('out/ReleaseX64/dart-sdk/lib/_internal/spec.sum');
|
||||
} else {
|
||||
options.sdkPath = 'sdk';
|
||||
options.sdkRoot = new Uri.file('sdk');
|
||||
}
|
||||
Program program = await kernelForProgram(entryUri, options);
|
||||
dartkTimer.stop();
|
||||
|
||||
Reference in New Issue
Block a user