Skip to content

Commit

Permalink
Support Package Resolution Configuration files.
Browse files Browse the repository at this point in the history
BUG=http://dartbug.com/23371
R=johnniwinther@google.com

Committed: 36c29d0

Review URL: https://codereview.chromium.org//1162363004.
  • Loading branch information
harryterkelsen committed Jun 9, 2015
1 parent d2f80cf commit 63681d5
Show file tree
Hide file tree
Showing 14 changed files with 191 additions and 65 deletions.
2 changes: 1 addition & 1 deletion DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ vars = {
"oauth2_rev": "@1bff41f4d54505c36f2d1a001b83b8b745c452f5",
"observe_rev": "@eee2b8ec34236fa46982575fbccff84f61202ac6",
"observatory_pub_packages_rev": "@45565",
"package_config_tag": "@0.0.2+4",
"package_config_tag": "@0.0.3+1",
"path_rev": "@93b3e2aa1db0ac0c8bab9d341588d77acda60320",
"petitparser_rev" : "@37878",
"ply_rev": "@604b32590ffad5cbb82e4afef1d305512d06ae93",
Expand Down
17 changes: 14 additions & 3 deletions pkg/compiler/lib/compiler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
library compiler;

import 'dart:async';
import 'package:package_config/packages.dart';
import 'src/apiimpl.dart';

// Unless explicitly allowed, passing [:null:] for any argument to the
Expand Down Expand Up @@ -58,13 +59,19 @@ typedef EventSink<String> CompilerOutputProvider(String name,
* [:null:]. If [uri] is not [:null:], neither are [begin] and
* [end]. [uri] indicates the compilation unit from where the
* diagnostic originates. [begin] and [end] are zero-based character
* offsets from the beginning of the compilaton unit. [message] is the
* offsets from the beginning of the compilation unit. [message] is the
* diagnostic message, and [kind] indicates indicates what kind of
* diagnostic it is.
*/
typedef void DiagnosticHandler(Uri uri, int begin, int end,
String message, Diagnostic kind);

/**
* Provides a package lookup mechanism in the case that no package root or
* package resolution configuration file are explicitly specified.
*/
typedef Future<Packages> PackagesDiscoveryProvider(Uri uri);

/// Information resulting from the compilation.
class CompilationResult {
/// `true` if the compilation succeeded, that is, compilation didn't fail due
Expand Down Expand Up @@ -103,7 +110,9 @@ Future<CompilationResult> compile(
DiagnosticHandler handler,
[List<String> options = const [],
CompilerOutputProvider outputProvider,
Map<String, dynamic> environment = const {}]) {
Map<String, dynamic> environment = const {},
Uri packageConfig,
PackagesDiscoveryProvider packagesDiscoveryProvider]) {
if (!libraryRoot.path.endsWith("/")) {
throw new ArgumentError("libraryRoot must end with a /");
}
Expand All @@ -118,7 +127,9 @@ Future<CompilationResult> compile(
libraryRoot,
packageRoot,
options,
environment);
environment,
packageConfig,
packagesDiscoveryProvider);
return compiler.run(script).then((bool success) {
return new CompilationResult(compiler, isSuccess: success);
});
Expand Down
118 changes: 91 additions & 27 deletions pkg/compiler/lib/src/apiimpl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
library leg_apiimpl;

import 'dart:async';
import 'dart:convert';

import '../compiler.dart' as api;
import 'dart2jslib.dart' as leg;
Expand All @@ -13,6 +14,11 @@ import 'elements/elements.dart' as elements;
import 'package:_internal/libraries.dart' hide LIBRARIES;
import 'package:_internal/libraries.dart' as library_info show LIBRARIES;
import 'io/source_file.dart';
import 'package:package_config/packages.dart';
import 'package:package_config/packages_file.dart' as pkgs;
import 'package:package_config/src/packages_impl.dart'
show NonFilePackagesDirectoryPackages, MapPackages;
import 'package:package_config/src/util.dart' show checkValidPackageUri;

const bool forceIncrementalSupport =
const bool.fromEnvironment('DART2JS_EXPERIMENTAL_INCREMENTAL_SUPPORT');
Expand All @@ -21,22 +27,28 @@ class Compiler extends leg.Compiler {
api.CompilerInputProvider provider;
api.DiagnosticHandler handler;
final Uri libraryRoot;
final Uri packageConfig;
final Uri packageRoot;
final api.PackagesDiscoveryProvider packagesDiscoveryProvider;
Packages packages;
List<String> options;
Map<String, dynamic> environment;
bool mockableLibraryUsed = false;
final Set<String> allowedLibraryCategories;

leg.GenericTask userHandlerTask;
leg.GenericTask userProviderTask;
leg.GenericTask userPackagesDiscoveryTask;

Compiler(this.provider,
api.CompilerOutputProvider outputProvider,
this.handler,
this.libraryRoot,
this.packageRoot,
List<String> options,
this.environment)
this.environment,
[this.packageConfig,
this.packagesDiscoveryProvider])
: this.options = options,
this.allowedLibraryCategories = getAllowedLibraryCategories(options),
super(
Expand Down Expand Up @@ -96,17 +108,20 @@ class Compiler extends leg.Compiler {
tasks.addAll([
userHandlerTask = new leg.GenericTask('Diagnostic handler', this),
userProviderTask = new leg.GenericTask('Input provider', this),
userPackagesDiscoveryTask =
new leg.GenericTask('Package discovery', this),
]);
if (libraryRoot == null) {
throw new ArgumentError("[libraryRoot] is null.");
}
if (!libraryRoot.path.endsWith("/")) {
throw new ArgumentError("[libraryRoot] must end with a /.");
}
if (packageRoot == null) {
throw new ArgumentError("[packageRoot] is null.");
if (packageRoot != null && packageConfig != null) {
throw new ArgumentError("Only one of [packageRoot] or [packageConfig] "
"may be given.");
}
if (!packageRoot.path.endsWith("/")) {
if (packageRoot != null && !packageRoot.path.endsWith("/")) {
throw new ArgumentError("[packageRoot] must end with a /.");
}
if (!analyzeOnly) {
Expand Down Expand Up @@ -160,8 +175,7 @@ class Compiler extends leg.Compiler {

// TODO(johnniwinther): Merge better with [translateDartUri] when
// [scanBuiltinLibrary] is removed.
String lookupLibraryPath(String dartLibraryName) {
LibraryInfo info = lookupLibraryInfo(dartLibraryName);
String lookupLibraryPath(LibraryInfo info) {
if (info == null) return null;
if (!info.isDart2jsLibrary) return null;
if (!allowedLibraryCategories.contains(info.category)) return null;
Expand Down Expand Up @@ -225,6 +239,7 @@ class Compiler extends leg.Compiler {
}

Uri resourceUri = translateUri(node, readableUri);
if (resourceUri == null) return synthesizeScript(node, readableUri);
if (resourceUri.scheme == 'dart-ext') {
if (!allowNativeExtensions) {
withCurrentElement(element, () {
Expand Down Expand Up @@ -260,12 +275,11 @@ class Compiler extends leg.Compiler {
}

Future<leg.Script> synthesizeScript(leg.Spannable node, Uri readableUri) {
Uri resourceUri = translateUri(node, readableUri);
return new Future.value(
new leg.Script(
readableUri, resourceUri,
readableUri, readableUri,
new StringSourceFile.fromUri(
resourceUri,
readableUri,
"// Synthetic source file generated for '$readableUri'."),
isSynthesized: true));
}
Expand All @@ -285,7 +299,7 @@ class Compiler extends leg.Compiler {
Uri translateDartUri(elements.LibraryElement importingLibrary,
Uri resolvedUri, tree.Node node) {
LibraryInfo libraryInfo = lookupLibraryInfo(resolvedUri.path);
String path = lookupLibraryPath(resolvedUri.path);
String path = lookupLibraryPath(libraryInfo);
if (libraryInfo != null &&
libraryInfo.category == "Internal") {
bool allowInternalLibraryAccess = false;
Expand Down Expand Up @@ -333,25 +347,66 @@ class Compiler extends leg.Compiler {
}

Uri translatePackageUri(leg.Spannable node, Uri uri) {
return packageRoot.resolve(uri.path);
try {
checkValidPackageUri(uri);
} on ArgumentError catch (e) {
reportError(
node,
leg.MessageKind.INVALID_PACKAGE_URI,
{'uri': uri, 'exception': e.message});
return null;
}
return packages.resolve(uri,
notFound: (Uri notFound) {
reportError(
node,
leg.MessageKind.LIBRARY_NOT_FOUND,
{'resolvedUri': uri}
);
return null;
});
}

Future setupPackages(Uri uri) async {
if (packageRoot != null) {
// Use "non-file" packages because the file version requires a [Directory]
// and we can't depend on 'dart:io' classes.
packages = new NonFilePackagesDirectoryPackages(packageRoot);
} else if (packageConfig != null) {
var packageConfigContents = await provider(packageConfig);
if (packageConfigContents is String) {
packageConfigContents = UTF8.encode(packageConfigContents);
}
packages =
new MapPackages(pkgs.parse(packageConfigContents, packageConfig));
} else {
if (packagesDiscoveryProvider == null) {
packages = Packages.noPackages;
} else {
packages = await callUserPackagesDiscovery(uri);
}
}
}

Future<bool> run(Uri uri) {
Future<bool> run(Uri uri) async {
log('Allowed library categories: $allowedLibraryCategories');
return super.run(uri).then((bool success) {
int cumulated = 0;
for (final task in tasks) {
int elapsed = task.timing;
if (elapsed != 0) {
cumulated += elapsed;
log('${task.name} took ${elapsed}msec');
}

await setupPackages(uri);
assert(packages != null);

bool success = await super.run(uri);
int cumulated = 0;
for (final task in tasks) {
int elapsed = task.timing;
if (elapsed != 0) {
cumulated += elapsed;
log('${task.name} took ${elapsed}msec');
}
int total = totalCompileTime.elapsedMilliseconds;
log('Total compile-time ${total}msec;'
' unaccounted ${total - cumulated}msec');
return success;
});
}
int total = totalCompileTime.elapsedMilliseconds;
log('Total compile-time ${total}msec;'
' unaccounted ${total - cumulated}msec');
return success;
}

void reportDiagnostic(leg.Spannable node,
Expand All @@ -368,8 +423,7 @@ class Compiler extends leg.Compiler {
if (span == null || span.uri == null) {
callUserHandler(null, null, null, '$message', kind);
} else {
callUserHandler(
translateUri(null, span.uri), span.begin, span.end, '$message', kind);
callUserHandler(span.uri, span.begin, span.end, '$message', kind);
}
}

Expand Down Expand Up @@ -400,6 +454,16 @@ class Compiler extends leg.Compiler {
}
}

Future<Packages> callUserPackagesDiscovery(Uri uri) {
try {
return userPackagesDiscoveryTask.measure(
() => packagesDiscoveryProvider(uri));
} catch (ex, s) {
diagnoseCrashInUserCode('Uncaught exception in package discovery', ex, s);
rethrow;
}
}

void diagnoseCrashInUserCode(String message, exception, stackTrace) {
hasCrashed = true;
print('$message: ${tryToString(exception)}');
Expand Down
28 changes: 19 additions & 9 deletions pkg/compiler/lib/src/dart2js.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import 'util/uri_extras.dart';
import 'util/util.dart' show stackTraceFilePrefix;
import 'util/command_line.dart';
import 'package:_internal/libraries.dart';
import 'package:package_config/discovery.dart' show findPackages;

const String LIBRARY_ROOT = '../../../../../sdk';
const String OUTPUT_LANGUAGE_DART = 'Dart';
Expand Down Expand Up @@ -105,6 +106,7 @@ Future<api.CompilationResult> compile(List<String> argv) {
Uri libraryRoot = currentDirectory;
Uri out = currentDirectory.resolve('out.js');
Uri sourceMapOut = currentDirectory.resolve('out.js.map');
Uri packageConfig = null;
Uri packageRoot = null;
List<String> options = new List<String>();
bool explicitOut = false;
Expand Down Expand Up @@ -140,6 +142,10 @@ Future<api.CompilationResult> compile(List<String> argv) {
packageRoot = currentDirectory.resolve(extractPath(argument));
}

setPackageConfig(String argument) {
packageConfig = currentDirectory.resolve(extractPath(argument));
}

setOutput(Iterator<String> arguments) {
optionsImplyCompilation.add(arguments.current);
String path;
Expand Down Expand Up @@ -329,6 +335,7 @@ Future<api.CompilationResult> compile(List<String> argv) {
(_) => setTrustPrimitives(
'--trust-primitives')),
new OptionHandler(r'--help|/\?|/h', (_) => wantHelp = true),
new OptionHandler('--packages=.+', setPackageConfig),
new OptionHandler('--package-root=.+|-p.+', setPackageRoot),
new OptionHandler('--analyze-all', setAnalyzeAll),
new OptionHandler('--analyze-only', setAnalyzeOnly),
Expand Down Expand Up @@ -404,9 +411,8 @@ Future<api.CompilationResult> compile(List<String> argv) {
"checked mode.");
}

Uri uri = currentDirectory.resolve(arguments[0]);
if (packageRoot == null) {
packageRoot = uri.resolve('./packages/');
if (packageRoot != null && packageConfig != null) {
helpAndFail("Cannot specify both '--package-root' and '--packages.");
}

if ((analyzeOnly || analyzeAll) && !optionsImplyCompilation.isEmpty) {
Expand All @@ -432,8 +438,6 @@ Future<api.CompilationResult> compile(List<String> argv) {
"combination with the '--output-type=dart' option.");
}

diagnosticHandler.info('Package root is $packageRoot');

options.add('--out=$out');
options.add('--source-map=$sourceMapOut');

Expand Down Expand Up @@ -468,9 +472,10 @@ Future<api.CompilationResult> compile(List<String> argv) {
return result;
}

return compileFunc(uri, libraryRoot, packageRoot,
inputProvider, diagnosticHandler,
options, outputProvider, environment)
Uri uri = currentDirectory.resolve(arguments[0]);
return compileFunc(uri, libraryRoot, packageRoot, inputProvider,
diagnosticHandler, options, outputProvider, environment,
packageConfig, findPackages)
.then(compilationDone);
}

Expand Down Expand Up @@ -552,7 +557,12 @@ Supported options:
Display version information.
-p<path>, --package-root=<path>
Where to find packages, that is, "package:..." imports.
Where to find packages, that is, "package:..." imports. This option cannot
be used with --packages.
--packages=<path>
Path to the package resolution configuration file, which supplies a mapping
of package names to paths. This option cannot be used with --package-root.
--analyze-all
Analyze all code. Without this option, the compiler only analyzes
Expand Down
11 changes: 8 additions & 3 deletions pkg/compiler/lib/src/mirrors/analyze.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ Future<MirrorSystem> analyze(List<Uri> libraries,
Uri packageRoot,
api.CompilerInputProvider inputProvider,
api.DiagnosticHandler diagnosticHandler,
[List<String> options = const <String>[]]) {
[List<String> options = const <String>[],
Uri packageConfig,
api.PackagesDiscoveryProvider findPackages]) {
if (!libraryRoot.path.endsWith("/")) {
throw new ArgumentError("libraryRoot must end with a /");
}
Expand Down Expand Up @@ -54,9 +56,12 @@ Future<MirrorSystem> analyze(List<Uri> libraries,
Compiler compiler = new apiimpl.Compiler(inputProvider,
null,
internalDiagnosticHandler,
libraryRoot, packageRoot,
libraryRoot,
packageRoot,
options,
const {});
const {},
packageConfig,
findPackages);
compiler.librariesToAnalyzeWhenRun = libraries;
return compiler.run(null).then((bool success) {
if (success && !compilationFailed) {
Expand Down
Loading

0 comments on commit 63681d5

Please sign in to comment.