Store library paths relative to a given application root folder.

In kernel, library import URIs now support an "app" scheme as an
alternative to the "file" scheme, representing a path relative to
the application root.

dartk takes an --app-root flag giving the application root. If none
is given, file URIs are used instead.

The intention is that kernel binaries should not carry irrelevant
path information, such as the path to the home directory of the
user who compiled a given file.

It is not the intention that end-users should see an app URI.
Import paths are currently not shown to users at all, and if we need
to do this, they should be translated to file paths first.

In theory we could stick to file URIs with relative paths, but the Uri
class from dart:core makes this difficult, as certain operations on it
assume that file paths should be absolute.

Source mapping URIs are not yet affected by this change.

R=kmillikin@google.com

Committed: 60adb852ad

Review URL: https://codereview.chromium.org/2532053005 .

Reverted: bb540416f2
This commit is contained in:
Asger Feldthaus 2016-11-30 10:39:48 +01:00
parent ecf9acd26c
commit 709c1e0b75
8 changed files with 102 additions and 77 deletions

View file

@ -241,7 +241,7 @@ Future<Program> generateKernel(Uri entryUri) async {
var repository = new Repository();
DartLoader loader = new DartLoader(repository, options, packages);
Program program = loader.loadProgram(entryUri.path);
Program program = loader.loadProgram(entryUri);
List errors = loader.errors;
if (errors.isNotEmpty) {
const int errorLimit = 100;

View file

@ -10,6 +10,7 @@ import 'batch_util.dart';
import 'package:args/args.dart';
import 'package:kernel/analyzer/loader.dart';
import 'package:kernel/application_root.dart';
import 'package:kernel/verifier.dart';
import 'package:kernel/kernel.dart';
import 'package:kernel/log.dart';
@ -36,6 +37,11 @@ ArgParser parser = new ArgParser(allowTrailingOptions: true)
..addOption('packages',
abbr: 'p', help: 'Path to the .packages file or packages folder.')
..addOption('package-root', help: 'Deprecated alias for --packages')
..addOption('app-root',
help: 'Store library paths relative to the given directory.\n'
'If none is given, absolute paths are used.\n'
'Application libraries not inside the application root are stored '
'using absolute paths')
..addOption('target',
abbr: 't',
help: 'Tailor the IR to the given target.',
@ -244,6 +250,13 @@ Future<CompilerOutcome> batchMain(
String packagePath = options['packages'] ?? options['package-root'];
checkIsFileOrDirectoryOrNull(packagePath, 'Package root or .packages');
String applicationRootOption = options['app-root'];
checkIsDirectoryOrNull(applicationRootOption, 'Application root');
if (applicationRootOption != null) {
applicationRootOption = new File(applicationRootOption).absolute.path;
}
var applicationRoot = new ApplicationRoot(applicationRootOption);
// Set up logging.
if (options['verbose']) {
log.onRecord.listen((LogRecord rec) {
@ -255,9 +268,10 @@ Future<CompilerOutcome> batchMain(
return fail('Exactly one FILE should be given.');
}
var file = options.rest.single;
String file = options.rest.single;
checkIsFile(file, option: 'Input file');
file = new File(file).absolute.path;
Uri fileUri = new Uri(scheme: 'file', path: file);
String format = options['format'] ?? defaultFormat();
String outputFile = options['out'] ?? defaultOutput();
@ -297,16 +311,18 @@ Future<CompilerOutcome> batchMain(
sdk: options['sdk'],
packagePath: packagePath,
customUriMappings: customUriMappings,
declaredVariables: declaredVariables);
declaredVariables: declaredVariables,
applicationRoot: applicationRoot);
String packageDiscoveryPath = batchModeState.isBatchMode ? null : file;
DartLoader loader = await batchModeState.batch.getLoader(
repository, dartOptions,
packageDiscoveryPath: packageDiscoveryPath);
if (options['link']) {
program = loader.loadProgram(file, target: target);
program = loader.loadProgram(fileUri, target: target);
} else {
var library = loader.loadLibrary(file);
assert(library == repository.getLibrary(file));
var library = loader.loadLibrary(fileUri);
assert(library ==
repository.getLibraryReference(applicationRoot.relativeUri(fileUri)));
program = new Program(repository.libraries);
}
errors = loader.errors;

View file

@ -16,6 +16,7 @@ import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/parser.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/generated/source_io.dart';
import 'package:kernel/application_root.dart';
import 'package:package_config/discovery.dart';
import 'package:package_config/packages.dart';
@ -35,6 +36,7 @@ class DartOptions {
bool strongModeSdk;
String sdk;
String packagePath;
ApplicationRoot applicationRoot;
Map<Uri, Uri> customUriMappings;
Map<String, String> declaredVariables;
@ -43,12 +45,14 @@ class DartOptions {
bool strongModeSdk,
this.sdk,
this.packagePath,
ApplicationRoot applicationRoot,
Map<Uri, Uri> customUriMappings,
Map<String, String> declaredVariables})
: this.customUriMappings = customUriMappings ?? <Uri, Uri>{},
this.declaredVariables = declaredVariables ?? <String, String>{},
this.strongMode = strongMode,
this.strongModeSdk = strongModeSdk ?? strongMode;
this.strongModeSdk = strongModeSdk ?? strongMode,
this.applicationRoot = applicationRoot ?? new ApplicationRoot.none();
}
abstract class ReferenceLevelLoader {
@ -68,6 +72,7 @@ abstract class ReferenceLevelLoader {
class DartLoader implements ReferenceLevelLoader {
final Repository repository;
final ApplicationRoot applicationRoot;
final Bimap<ClassElement, ast.Class> _classes =
new Bimap<ClassElement, ast.Class>();
final Bimap<Element, ast.Member> _members = new Bimap<Element, ast.Member>();
@ -90,19 +95,16 @@ class DartLoader implements ReferenceLevelLoader {
DartLoader(this.repository, DartOptions options, Packages packages,
{DartSdk dartSdk})
: this.context = createContext(options, packages, dartSdk: dartSdk);
LibraryElement getLibraryElement(ast.Library node) {
return context
.getLibraryElement(context.sourceFactory.forUri2(node.importUri));
}
: this.context = createContext(options, packages, dartSdk: dartSdk),
this.applicationRoot = options.applicationRoot;
String getLibraryName(LibraryElement element) {
return element.name.isEmpty ? null : element.name;
}
ast.Library getLibraryReference(LibraryElement element) {
return repository.getLibraryReference(element.source.uri)
var uri = applicationRoot.relativeUri(element.source.uri);
return repository.getLibraryReference(uri)
..name ??= getLibraryName(element)
..fileUri = "file://${element.source.fullName}";
}
@ -579,7 +581,8 @@ class DartLoader implements ReferenceLevelLoader {
void ensureLibraryIsLoaded(ast.Library node) {
if (!node.isExternal) return;
node.isExternal = false;
var source = context.sourceFactory.forUri2(node.importUri);
var source = context.sourceFactory
.forUri2(applicationRoot.absoluteUri(node.importUri));
assert(source != null);
var element = context.computeLibraryElement(source);
context.resolveCompilationUnit(source, element);
@ -623,8 +626,9 @@ class DartLoader implements ReferenceLevelLoader {
List<String> getLoadedFileNames() {
var list = <String>[];
for (var library in repository.libraries) {
LibraryElement element = context.computeLibraryElement(
context.sourceFactory.forUri2(library.importUri));
LibraryElement element = context.computeLibraryElement(context
.sourceFactory
.forUri2(applicationRoot.absoluteUri(library.importUri)));
for (var unit in element.units) {
list.add(unit.source.fullName);
}
@ -639,8 +643,9 @@ class DartLoader implements ReferenceLevelLoader {
}
}
ast.Program loadProgram(String mainLibrary, {Target target}) {
ast.Library library = repository.getLibrary(mainLibrary);
ast.Program loadProgram(Uri mainLibrary, {Target target}) {
ast.Library library = repository
.getLibraryReference(applicationRoot.relativeUri(mainLibrary));
ensureLibraryIsLoaded(library);
loadEverything(target: target);
var program = new ast.Program(repository.libraries);
@ -661,8 +666,9 @@ class DartLoader implements ReferenceLevelLoader {
return program;
}
ast.Library loadLibrary(String mainLibrary) {
ast.Library library = repository.getLibrary(mainLibrary);
ast.Library loadLibrary(Uri uri) {
ast.Library library =
repository.getLibraryReference(applicationRoot.relativeUri(uri));
ensureLibraryIsLoaded(library);
return library;
}

View file

@ -0,0 +1,45 @@
// Copyright (c) 2016, 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 kernel.application_root;
import 'package:path/path.dart' as pathlib;
/// Resolves URIs with the `app` scheme.
///
/// These are used internally in kernel to represent file paths relative to
/// some application root. This is done to avoid storing irrelevant paths, such
/// as the path to the home directory of the user who compiled a given file.
class ApplicationRoot {
static const String scheme = 'app';
final String path;
ApplicationRoot(this.path) {
assert(path == null || pathlib.isAbsolute(path));
}
ApplicationRoot.none() : path = null;
/// Converts `app` URIs to absolute `file` URIs.
Uri absoluteUri(Uri uri) {
if (path == null) return uri;
if (uri.scheme == ApplicationRoot.scheme) {
return new Uri(scheme: 'file', path: pathlib.join(this.path, uri.path));
} else {
return uri;
}
}
/// Converts `file` URIs to `app` URIs.
Uri relativeUri(Uri uri) {
if (path == null) return uri;
if (uri.scheme == 'file' && pathlib.isWithin(this.path, uri.path)) {
return new Uri(
scheme: ApplicationRoot.scheme,
path: pathlib.relative(uri.path, from: this.path));
} else {
return uri;
}
}
}

View file

@ -157,9 +157,11 @@ abstract class TreeNode extends Node {
// ------------------------------------------------------------------------
class Library extends TreeNode implements Comparable<Library> {
/// An absolute import path to this library.
/// An import path to this library.
///
/// The [Uri] should have the `dart`, `package`, or `file` scheme.
/// The [Uri] should have the `dart`, `package`, `app`, or `file` scheme.
///
/// If the URI has the `app` scheme, it is relative to the application root.
Uri importUri;
/// The uri of the source file this library was loaded from.

View file

@ -3,10 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
library kernel.repository;
import 'dart:io';
import 'package:path/path.dart' as pathlib;
import 'ast.dart';
/// Keeps track of which [Library] objects have been created for a given URI.
@ -14,61 +10,16 @@ import 'ast.dart';
/// To load different files into the same IR, pass in the same repository
/// object to the loaders.
class Repository {
final String workingDirectory;
final Map<Uri, Library> _uriToLibrary = <Uri, Library>{};
final List<Library> libraries = <Library>[];
Repository({String workingDirectory})
: this.workingDirectory = workingDirectory ?? Directory.current.path;
/// Get the [Library] object for the library addresesd by [path]; possibly
/// as an external library.
///
/// The [path] may be a relative or absolute file path, or a URI string with a
/// `dart:`, `package:` or `file:` scheme.
///
/// Note that this method does not check if the library can be loaded at all.
Library getLibrary(String path) {
return getLibraryReference(normalizePath(path));
}
String normalizeFileExtension(String path) {
if (path.endsWith('.dill')) {
return path.substring(0, path.length - '.dill'.length) + '.dart';
} else {
return path;
}
}
/// Get the canonical URI for the library addressed by the given [path].
///
/// The [path] may be a relative or absolute file path, or a URI string with a
/// `dart:`, `package:` or `file:` scheme.
Uri normalizePath(String path) {
var uri = Uri.parse(path);
if (!uri.hasScheme) {
if (!pathlib.isAbsolute(path)) {
path = pathlib.join(workingDirectory, path);
}
uri = new Uri(scheme: 'file', path: normalizeFileExtension(path));
} else if (uri.scheme == 'file') {
var path = normalizeFileExtension(uri.path);
if (!uri.hasAbsolutePath) {
uri = uri.replace(path: pathlib.join(workingDirectory, path));
} else {
uri = uri.replace(path: path);
}
}
return uri;
}
Library getLibraryReference(Uri uri) {
assert(uri.hasScheme);
assert(uri.scheme != 'file' || uri.hasAbsolutePath);
return _uriToLibrary.putIfAbsent(uri, () => _buildLibraryReference(uri));
}
Library _buildLibraryReference(Uri uri) {
assert(uri.hasScheme);
var library = new Library(uri, isExternal: true);
libraries.add(library);
return library;

View file

@ -4,6 +4,7 @@
import 'dart:io';
import 'package:kernel/analyzer/loader.dart';
import 'package:kernel/application_root.dart';
import 'package:kernel/kernel.dart';
import 'package:kernel/target/targets.dart';
import 'package:kernel/text/ast_to_text.dart';
@ -30,11 +31,13 @@ void runBaselineTests(String folderName, TestTarget target) {
String outputDirectory = '$testcaseDirectory/$folderName';
var batch = new DartLoaderBatch();
Directory directory = new Directory(inputDirectory);
var applicationRoot = new ApplicationRoot(directory.absolute.path);
for (FileSystemEntity file in directory.listSync()) {
if (file is File && file.path.endsWith('.dart')) {
String name = pathlib.basename(file.path);
test(name, () async {
String dartPath = file.path;
Uri dartPath =
new Uri(scheme: 'file', path: pathlib.absolute(file.path));
String shortName = pathlib.withoutExtension(name);
String filenameOfBaseline = '$outputDirectory/$shortName.baseline.txt';
String filenameOfCurrent = '$outputDirectory/$shortName.current.txt';
@ -45,7 +48,8 @@ void runBaselineTests(String folderName, TestTarget target) {
new DartOptions(
strongMode: target.strongMode,
sdk: sdkDirectory,
declaredVariables: target.extraDeclaredVariables));
declaredVariables: target.extraDeclaredVariables,
applicationRoot: applicationRoot));
var program = loader.loadProgram(dartPath, target: target);
verifyProgram(program);
var errors = target.transformProgram(program);

View file

@ -45,6 +45,7 @@ main(List<String> args) {
bool strongMode = options['strong'];
String path = options.rest.single;
var uri = new Uri(scheme: 'file', path: new File(path).absolute.path);
var packages =
getPackagesDirectory(new Uri(scheme: 'file', path: packagePath));
Repository repository = new Repository();
@ -54,7 +55,7 @@ main(List<String> args) {
new DartOptions(
strongMode: strongMode, sdk: sdk, packagePath: packagePath),
packages)
.loadProgram(path);
.loadProgram(uri);
CacheEntry.recomputedCounts.forEach((key, value) {
print('Recomputed $key $value times');