[vm/kernel] Move functionality of Fuchsia's compiler.dart into pkg/vm
This includes:
* Selecting front-end target.
* Support for multi-root virtual file system.
* Not linking platform into resulting kernel file.
* Specifying input as URI (instead of file path) on command line.
* Automatically converting input script URI to package URI.
* Writing ninja dependencies file.
* Writing package-split kernel binaries.
After this change Fuchsia's compiler.dart will become a small wrapper
over pkg/vm, sharing most logic and even most command line options
with pkg/vm gen_kernel tool.
Also, this CL attempts to share some pieces of code between frontend
server and gen_kernel.
In addition, seperate bytecode generation for package-split binaries
is implemented (needed for https://dart-review.googlesource.com/c/sdk/+/85469).
Corresponding Fuchsia CL: https://fuchsia-review.googlesource.com/c/topaz/+/229964
Change-Id: I12d7b2f6401357b3c9df2e31bc736af5a9dc5fd2
Reviewed-on: https://dart-review.googlesource.com/c/85721
Reviewed-by: Alexander Aprelev <aam@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
diff --git a/pkg/vm/bin/gen_kernel.dart b/pkg/vm/bin/gen_kernel.dart
index bdb5df6..eefe915 100644
--- a/pkg/vm/bin/gen_kernel.dart
+++ b/pkg/vm/bin/gen_kernel.dart
@@ -3,53 +3,21 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
-import 'dart:io';
+import 'dart:io' show exit;
-import 'package:args/args.dart' show ArgParser, ArgResults;
-import 'package:front_end/src/api_unstable/vm.dart';
-import 'package:kernel/binary/ast_to_binary.dart';
-import 'package:kernel/src/tool/batch_util.dart' as batch_util;
-import 'package:kernel/target/targets.dart' show TargetFlags;
+import 'package:args/args.dart' show ArgParser;
import 'package:kernel/text/ast_to_text.dart'
show globalDebuggingNames, NameSystem;
+import 'package:kernel/src/tool/batch_util.dart' as batch_util;
import 'package:vm/kernel_front_end.dart'
- show compileToKernel, ErrorDetector, ErrorPrinter, parseCommandLineDefines;
-import 'package:vm/target/vm.dart' show VmTarget;
+ show
+ createCompilerArgParser,
+ runCompiler,
+ successExitCode,
+ compileTimeErrorExitCode,
+ badUsageExitCode;
-final ArgParser _argParser = new ArgParser(allowTrailingOptions: true)
- ..addOption('platform',
- help: 'Path to vm_platform_strong.dill file', defaultsTo: null)
- ..addOption('packages', help: 'Path to .packages file', defaultsTo: null)
- ..addOption('output',
- abbr: 'o', help: 'Path to resulting dill file', defaultsTo: null)
- ..addFlag('aot',
- help:
- 'Produce kernel file for AOT compilation (enables global transformations).',
- defaultsTo: false)
- ..addFlag('sync-async',
- help: 'Start `async` functions synchronously', defaultsTo: true)
- ..addFlag('embed-sources',
- help: 'Embed source files in the generated kernel component',
- defaultsTo: true)
- ..addFlag('tfa',
- help:
- 'Enable global type flow analysis and related transformations in AOT mode.',
- defaultsTo: true)
- ..addMultiOption('define',
- abbr: 'D',
- help: 'The values for the environment constants (e.g. -Dkey=value).')
- ..addFlag('enable-asserts',
- help: 'Whether asserts will be enabled.', defaultsTo: false)
- ..addFlag('enable-constant-evaluation',
- help: 'Whether kernel constant evaluation will be enabled.',
- defaultsTo: true)
- ..addFlag('gen-bytecode', help: 'Generate bytecode', defaultsTo: false)
- ..addFlag('emit-bytecode-source-positions',
- help: 'Emit source positions in bytecode', defaultsTo: false)
- ..addFlag('drop-ast',
- help: 'Drop AST for members with bytecode', defaultsTo: false)
- ..addFlag('use-future-bytecode-format',
- help: 'Generate bytecode in the bleeding edge format', defaultsTo: false);
+final ArgParser _argParser = createCompilerArgParser();
final String _usage = '''
Usage: dart pkg/vm/bin/gen_kernel.dart --platform vm_platform_strong.dill [options] input.dart
@@ -59,9 +27,6 @@
${_argParser.usage}
''';
-const int _badUsageExitCode = 1;
-const int _compileTimeErrorExitCode = 254;
-
main(List<String> arguments) async {
if (arguments.isNotEmpty && arguments.last == '--batch') {
await runBatchModeCompiler();
@@ -71,72 +36,7 @@
}
Future<int> compile(List<String> arguments) async {
- final ArgResults options = _argParser.parse(arguments);
- final String platformKernel = options['platform'];
-
- if ((options.rest.length != 1) || (platformKernel == null)) {
- print(_usage);
- return _badUsageExitCode;
- }
-
- final String filename = options.rest.single;
- final String kernelBinaryFilename = options['output'] ?? "$filename.dill";
- final String packages = options['packages'];
- final bool aot = options['aot'];
- final bool tfa = options['tfa'];
- final bool genBytecode = options['gen-bytecode'];
- final bool emitBytecodeSourcePositions =
- options['emit-bytecode-source-positions'];
- final bool dropAST = options['drop-ast'];
- final bool useFutureBytecodeFormat = options['use-future-bytecode-format'];
- final bool enableAsserts = options['enable-asserts'];
- final bool enableConstantEvaluation = options['enable-constant-evaluation'];
- final Map<String, String> environmentDefines = {};
-
- if (!parseCommandLineDefines(options['define'], environmentDefines, _usage)) {
- return _badUsageExitCode;
- }
-
- final errorPrinter = new ErrorPrinter();
- final errorDetector = new ErrorDetector(previousErrorHandler: errorPrinter);
-
- final CompilerOptions compilerOptions = new CompilerOptions()
- ..target = new VmTarget(new TargetFlags(syncAsync: true))
- ..linkedDependencies = <Uri>[
- Uri.base.resolveUri(new Uri.file(platformKernel))
- ]
- ..packagesFileUri =
- packages != null ? Uri.base.resolveUri(new Uri.file(packages)) : null
- ..onDiagnostic = (DiagnosticMessage m) {
- errorDetector(m);
- }
- ..embedSourceText = options['embed-sources'];
-
- final inputUri = new Uri.file(filename);
- final component = await compileToKernel(
- Uri.base.resolveUri(inputUri), compilerOptions,
- aot: aot,
- useGlobalTypeFlowAnalysis: tfa,
- environmentDefines: environmentDefines,
- genBytecode: genBytecode,
- emitBytecodeSourcePositions: emitBytecodeSourcePositions,
- dropAST: dropAST,
- useFutureBytecodeFormat: useFutureBytecodeFormat,
- enableAsserts: enableAsserts,
- enableConstantEvaluation: enableConstantEvaluation);
-
- errorPrinter.printCompilationMessages(inputUri);
-
- if (errorDetector.hasCompilationErrors || (component == null)) {
- return _compileTimeErrorExitCode;
- }
-
- final IOSink sink = new File(kernelBinaryFilename).openWrite();
- final BinaryPrinter printer = new BinaryPrinter(sink);
- printer.writeComponentFile(component);
- await sink.close();
-
- return 0;
+ return runCompiler(_argParser.parse(arguments), _usage);
}
Future runBatchModeCompiler() async {
@@ -160,10 +60,10 @@
globalDebuggingNames = new NameSystem();
switch (exitCode) {
- case 0:
+ case successExitCode:
return batch_util.CompilerOutcome.Ok;
- case _compileTimeErrorExitCode:
- case _badUsageExitCode:
+ case compileTimeErrorExitCode:
+ case badUsageExitCode:
return batch_util.CompilerOutcome.Fail;
default:
throw 'Could not obtain correct exit code from compiler.';
diff --git a/pkg/vm/lib/bytecode/gen_bytecode.dart b/pkg/vm/lib/bytecode/gen_bytecode.dart
index 9c49962..6dd7960 100644
--- a/pkg/vm/lib/bytecode/gen_bytecode.dart
+++ b/pkg/vm/lib/bytecode/gen_bytecode.dart
@@ -39,13 +39,16 @@
// exception.
const String symbolForTypeCast = ' in type cast';
-void generateBytecode(Component component,
- {bool dropAST: false,
- bool emitSourcePositions: false,
- bool omitAssertSourcePositions: false,
- bool useFutureBytecodeFormat: false,
- Map<String, String> environmentDefines,
- ErrorReporter errorReporter}) {
+void generateBytecode(
+ Component component, {
+ bool dropAST: false,
+ bool emitSourcePositions: false,
+ bool omitAssertSourcePositions: false,
+ bool useFutureBytecodeFormat: false,
+ Map<String, String> environmentDefines,
+ ErrorReporter errorReporter,
+ List<Library> libraries,
+}) {
final coreTypes = new CoreTypes(component);
void ignoreAmbiguousSupertypes(Class cls, Supertype a, Supertype b) {}
final hierarchy = new ClassHierarchy(component,
@@ -55,19 +58,25 @@
final constantsBackend =
new VmConstantsBackend(environmentDefines, coreTypes);
final errorReporter = new ForwardConstantEvaluationErrors(typeEnvironment);
- new BytecodeGenerator(
- component,
- coreTypes,
- hierarchy,
- typeEnvironment,
- constantsBackend,
- emitSourcePositions,
- omitAssertSourcePositions,
- useFutureBytecodeFormat,
- errorReporter)
- .visitComponent(component);
+ libraries ??= component.libraries;
+ final bytecodeGenerator = new BytecodeGenerator(
+ component,
+ coreTypes,
+ hierarchy,
+ typeEnvironment,
+ constantsBackend,
+ emitSourcePositions,
+ omitAssertSourcePositions,
+ useFutureBytecodeFormat,
+ errorReporter);
+ for (var library in libraries) {
+ bytecodeGenerator.visitLibrary(library);
+ }
if (dropAST) {
- new DropAST().visitComponent(component);
+ final astRemover = new DropAST(component);
+ for (var library in libraries) {
+ astRemover.visitLibrary(library);
+ }
}
}
@@ -141,9 +150,6 @@
}
@override
- visitComponent(Component node) => node.visitChildren(this);
-
- @override
visitLibrary(Library node) {
if (node.isExternal) {
return;
@@ -3116,14 +3122,8 @@
class DropAST extends Transformer {
BytecodeMetadataRepository metadata;
- @override
- TreeNode visitComponent(Component node) {
- metadata = node.metadata[new BytecodeMetadataRepository().tag];
- if (metadata != null) {
- return super.visitComponent(node);
- }
- return node;
- }
+ DropAST(Component component)
+ : metadata = component.metadata[new BytecodeMetadataRepository().tag];
@override
TreeNode defaultMember(Member node) {
@@ -3152,7 +3152,8 @@
return node;
}
- bool _hasBytecode(Member node) => metadata.mapping.containsKey(node);
+ bool _hasBytecode(Member node) =>
+ metadata != null && metadata.mapping.containsKey(node);
}
typedef void GenerateContinuation();
diff --git a/pkg/vm/lib/frontend_server.dart b/pkg/vm/lib/frontend_server.dart
index fc88b69..99e6976 100644
--- a/pkg/vm/lib/frontend_server.dart
+++ b/pkg/vm/lib/frontend_server.dart
@@ -9,7 +9,6 @@
import 'dart:io' hide FileSystemEntity;
import 'package:args/args.dart';
-import 'package:build_integration/file_system/multi_root.dart';
// front_end/src imports below that require lint `ignore_for_file`
// are a temporary state of things until frontend team builds better api
// that would replace api used below. This api was made private in
@@ -21,14 +20,18 @@
import 'package:kernel/binary/limited_ast_to_binary.dart';
import 'package:kernel/kernel.dart'
show Component, loadComponentSourceFromBytes;
-import 'package:kernel/target/targets.dart';
import 'package:path/path.dart' as path;
import 'package:usage/uuid/uuid.dart';
import 'package:vm/incremental_compiler.dart' show IncrementalCompiler;
import 'package:vm/kernel_front_end.dart'
- show compileToKernel, parseCommandLineDefines;
-import 'package:vm/target/install.dart' show installAdditionalTargets;
+ show
+ compileToKernel,
+ parseCommandLineDefines,
+ convertFileOrUriArgumentToUri,
+ createFrontEndTarget,
+ createFrontEndFileSystem,
+ writeDepfile;
ArgParser argParser = new ArgParser(allowTrailingOptions: true)
..addFlag('train',
@@ -223,6 +226,7 @@
bool unsafePackageSerialization;
CompilerOptions _compilerOptions;
+ FileSystem _fileSystem;
Uri _mainSource;
ArgResults _options;
@@ -248,6 +252,8 @@
IncrementalCompiler generator,
}) async {
_options = options;
+ _fileSystem = createFrontEndFileSystem(
+ options['filesystem-scheme'], options['filesystem-root']);
setMainSourceFilename(filename);
_kernelBinaryFilenameFull = _options['output-dill'] ?? '$filename.dill';
_kernelBinaryFilenameIncremental = _options['output-incremental-dill'] ??
@@ -264,6 +270,7 @@
options['platform'] ?? 'platform_strong.dill';
final CompilerOptions compilerOptions = new CompilerOptions()
..sdkRoot = sdkRoot
+ ..fileSystem = _fileSystem
..packagesFileUri = _getFileOrUri(_options['packages'])
..sdkSummary = sdkRoot.resolve(platformKernelDill)
..verbose = options['verbose']
@@ -288,14 +295,8 @@
printDiagnosticMessage(message, _outputStream.writeln);
}
};
- if (options.wasParsed('filesystem-root')) {
- List<Uri> rootUris = <Uri>[];
- for (String root in options['filesystem-root']) {
- rootUris.add(Uri.base.resolveUri(new Uri.file(root)));
- }
- compilerOptions.fileSystem = new MultiRootFileSystem(
- options['filesystem-scheme'], rootUris, compilerOptions.fileSystem);
+ if (options.wasParsed('filesystem-root')) {
if (_options['output-dill'] == null) {
print('When --filesystem-root is specified it is required to specify'
' --output-dill option that points to physical file system location'
@@ -310,11 +311,7 @@
return false;
}
- // Ensure that Flutter and VM targets are added to targets dictionary.
- installAdditionalTargets();
-
- final TargetFlags targetFlags = new TargetFlags(syncAsync: true);
- compilerOptions.target = getTarget(options['target'], targetFlags);
+ compilerOptions.target = createFrontEndTarget(options['target']);
if (compilerOptions.target == null) {
print('Failed to create front-end target ${options['target']}.');
return false;
@@ -360,7 +357,8 @@
.writeln('$boundaryKey $_kernelBinaryFilename ${errors.length}');
final String depfile = options['depfile'];
if (depfile != null) {
- await _writeDepfile(component, _kernelBinaryFilename, depfile);
+ await writeDepfile(compilerOptions.fileSystem, component,
+ _kernelBinaryFilename, depfile);
}
_kernelBinaryFilename = _kernelBinaryFilenameIncremental;
@@ -614,23 +612,8 @@
_kernelBinaryFilename = _kernelBinaryFilenameFull;
}
- Uri _getFileOrUri(String fileOrUri) {
- if (fileOrUri == null) {
- return null;
- }
- if (_options.wasParsed('filesystem-root')) {
- // This is a hack.
- // Only expect uri when filesystem-root option is specified. It has to
- // be uri for filesystem-root use case because mapping is done on
- // scheme-basis.
- // This is so that we don't deal with Windows files paths that can not
- // be processed as uris.
- return Uri.base.resolve(fileOrUri);
- }
- Uri uri = Uri.parse(fileOrUri);
- if (uri.scheme == 'package') return uri;
- return Uri.base.resolveUri(new Uri.file(fileOrUri));
- }
+ Uri _getFileOrUri(String fileOrUri) =>
+ convertFileOrUriArgumentToUri(_fileSystem, fileOrUri);
IncrementalCompiler _createGenerator(Uri initializeFromDillUri) {
return new IncrementalCompiler(_compilerOptions, _mainSource,
@@ -666,25 +649,6 @@
void close() {}
}
-String _escapePath(String path) {
- return path.replaceAll(r'\', r'\\').replaceAll(r' ', r'\ ');
-}
-
-// https://ninja-build.org/manual.html#_depfile
-_writeDepfile(Component component, String output, String depfile) async {
- final IOSink file = new File(depfile).openWrite();
- file.write(_escapePath(output));
- file.write(':');
- for (Uri dep in component.uriToSource.keys) {
- // Skip empty or corelib dependencies.
- if (dep == null || dep.scheme == 'org-dartlang-sdk') continue;
- file.write(' ');
- file.write(_escapePath(dep.toFilePath()));
- }
- file.write('\n');
- await file.close();
-}
-
class _CompileExpressionRequest {
String expression;
// Note that FE will reject a compileExpression command by returning a null
diff --git a/pkg/vm/lib/kernel_front_end.dart b/pkg/vm/lib/kernel_front_end.dart
index be0184f..ffb74bf 100644
--- a/pkg/vm/lib/kernel_front_end.dart
+++ b/pkg/vm/lib/kernel_front_end.dart
@@ -6,6 +6,12 @@
library vm.kernel_front_end;
import 'dart:async';
+import 'dart:io' show File, IOSink, IOException;
+
+import 'package:args/args.dart' show ArgParser, ArgResults;
+
+import 'package:build_integration/file_system/multi_root.dart'
+ show MultiRootFileSystem, MultiRootFileSystemEntity;
import 'package:front_end/src/api_unstable/vm.dart'
show
@@ -13,22 +19,31 @@
CompilerOptions,
DiagnosticMessage,
DiagnosticMessageHandler,
+ FileSystem,
+ FileSystemEntity,
ProcessedOptions,
Severity,
+ StandardFileSystem,
getMessageUri,
kernelForProgram,
printDiagnosticMessage;
import 'package:kernel/type_environment.dart' show TypeEnvironment;
import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
-import 'package:kernel/ast.dart' show Component, Field, StaticGet;
+import 'package:kernel/ast.dart'
+ show Component, Field, Library, Reference, StaticGet;
+import 'package:kernel/binary/ast_to_binary.dart' show BinaryPrinter;
+import 'package:kernel/binary/limited_ast_to_binary.dart'
+ show LimitedBinaryPrinter;
import 'package:kernel/core_types.dart' show CoreTypes;
+import 'package:kernel/target/targets.dart' show Target, TargetFlags, getTarget;
import 'package:kernel/transformations/constants.dart' as constants;
import 'package:kernel/vm/constants_native_effects.dart' as vm_constants;
import 'bytecode/gen_bytecode.dart' show generateBytecode;
import 'constants_error_reporter.dart' show ForwardConstantEvaluationErrors;
+import 'target/install.dart' show installAdditionalTargets;
import 'transformations/devirtualization.dart' as devirtualization
show transformComponent;
import 'transformations/mixin_deduplication.dart' as mixin_deduplication
@@ -40,6 +55,183 @@
import 'transformations/obfuscation_prohibitions_annotator.dart'
as obfuscationProhibitions;
+/// Declare options consumed by [runCompiler].
+void declareCompilerOptions(ArgParser args) {
+ args.addOption('platform',
+ help: 'Path to vm_platform_strong.dill file', defaultsTo: null);
+ args.addOption('packages', help: 'Path to .packages file', defaultsTo: null);
+ args.addOption('output',
+ abbr: 'o', help: 'Path to resulting dill file', defaultsTo: null);
+ args.addFlag('aot',
+ help:
+ 'Produce kernel file for AOT compilation (enables global transformations).',
+ defaultsTo: false);
+ args.addOption('depfile', help: 'Path to output Ninja depfile');
+ args.addFlag('link-platform',
+ help: 'Include platform into resulting kernel file.', defaultsTo: true);
+ args.addFlag('sync-async',
+ help: 'Start `async` functions synchronously', defaultsTo: true);
+ args.addFlag('embed-sources',
+ help: 'Embed source files in the generated kernel component',
+ defaultsTo: true);
+ args.addMultiOption('filesystem-root',
+ help: 'A base path for the multi-root virtual file system.'
+ ' If multi-root file system is used, the input script should be specified using URI.');
+ args.addOption('filesystem-scheme',
+ help: 'The URI scheme for the multi-root virtual filesystem.');
+ args.addOption('target',
+ help: 'Target model that determines what core libraries are available',
+ allowed: <String>['vm', 'flutter', 'flutter_runner', 'dart_runner'],
+ defaultsTo: 'vm');
+ args.addFlag('tfa',
+ help:
+ 'Enable global type flow analysis and related transformations in AOT mode.',
+ defaultsTo: true);
+ args.addMultiOption('define',
+ abbr: 'D',
+ help: 'The values for the environment constants (e.g. -Dkey=value).');
+ args.addFlag('enable-asserts',
+ help: 'Whether asserts will be enabled.', defaultsTo: false);
+ args.addFlag('enable-constant-evaluation',
+ help: 'Whether kernel constant evaluation will be enabled.',
+ defaultsTo: true);
+ args.addFlag('split-output-by-packages',
+ help:
+ 'Split resulting kernel file into multiple files (one per package).',
+ defaultsTo: false);
+ args.addFlag('gen-bytecode', help: 'Generate bytecode', defaultsTo: false);
+ args.addFlag('emit-bytecode-source-positions',
+ help: 'Emit source positions in bytecode', defaultsTo: false);
+ args.addFlag('drop-ast',
+ help: 'Drop AST for members with bytecode', defaultsTo: false);
+ args.addFlag('use-future-bytecode-format',
+ help: 'Generate bytecode in the bleeding edge format', defaultsTo: false);
+}
+
+/// Create ArgParser and populate it with options consumed by [runCompiler].
+ArgParser createCompilerArgParser() {
+ final ArgParser argParser = new ArgParser(allowTrailingOptions: true);
+ declareCompilerOptions(argParser);
+ return argParser;
+}
+
+const int successExitCode = 0;
+const int badUsageExitCode = 1;
+const int compileTimeErrorExitCode = 254;
+
+/// Run kernel compiler tool with given [options] and [usage]
+/// and return exit code.
+Future<int> runCompiler(ArgResults options, String usage) async {
+ final String platformKernel = options['platform'];
+
+ if ((options.rest.length != 1) || (platformKernel == null)) {
+ print(usage);
+ return badUsageExitCode;
+ }
+
+ final String input = options.rest.single;
+ final String outputFileName = options['output'] ?? "$input.dill";
+ final String packages = options['packages'];
+ final String targetName = options['target'];
+ final String fileSystemScheme = options['filesystem-scheme'];
+ final String depfile = options['depfile'];
+ final List<String> fileSystemRoots = options['filesystem-root'];
+ final bool aot = options['aot'];
+ final bool tfa = options['tfa'];
+ final bool linkPlatform = options['link-platform'];
+ final bool genBytecode = options['gen-bytecode'];
+ final bool emitBytecodeSourcePositions =
+ options['emit-bytecode-source-positions'];
+ final bool dropAST = options['drop-ast'];
+ final bool useFutureBytecodeFormat = options['use-future-bytecode-format'];
+ final bool enableAsserts = options['enable-asserts'];
+ final bool enableConstantEvaluation = options['enable-constant-evaluation'];
+ final bool splitOutputByPackages = options['split-output-by-packages'];
+ final Map<String, String> environmentDefines = {};
+
+ if (!parseCommandLineDefines(options['define'], environmentDefines, usage)) {
+ return badUsageExitCode;
+ }
+
+ final target = createFrontEndTarget(targetName);
+ if (target == null) {
+ print('Failed to create front-end target $targetName.');
+ return badUsageExitCode;
+ }
+
+ final fileSystem =
+ createFrontEndFileSystem(fileSystemScheme, fileSystemRoots);
+
+ final Uri packagesUri =
+ packages != null ? Uri.base.resolveUri(new Uri.file(packages)) : null;
+
+ final platformKernelUri = Uri.base.resolveUri(new Uri.file(platformKernel));
+ final List<Uri> linkedDependencies = <Uri>[];
+ if (aot || linkPlatform) {
+ linkedDependencies.add(platformKernelUri);
+ }
+
+ Uri mainUri = convertFileOrUriArgumentToUri(fileSystem, input);
+ if (packagesUri != null) {
+ mainUri = await convertToPackageUri(fileSystem, mainUri, packagesUri);
+ }
+
+ final errorPrinter = new ErrorPrinter();
+ final errorDetector = new ErrorDetector(previousErrorHandler: errorPrinter);
+
+ final CompilerOptions compilerOptions = new CompilerOptions()
+ ..sdkSummary = platformKernelUri
+ ..target = target
+ ..fileSystem = fileSystem
+ ..linkedDependencies = linkedDependencies
+ ..packagesFileUri = packagesUri
+ ..onDiagnostic = (DiagnosticMessage m) {
+ errorDetector(m);
+ }
+ ..embedSourceText = options['embed-sources'];
+
+ final component = await compileToKernel(mainUri, compilerOptions,
+ aot: aot,
+ useGlobalTypeFlowAnalysis: tfa,
+ environmentDefines: environmentDefines,
+ genBytecode: genBytecode,
+ emitBytecodeSourcePositions: emitBytecodeSourcePositions,
+ dropAST: dropAST && !splitOutputByPackages,
+ useFutureBytecodeFormat: useFutureBytecodeFormat,
+ enableAsserts: enableAsserts,
+ enableConstantEvaluation: enableConstantEvaluation);
+
+ errorPrinter.printCompilationMessages();
+
+ if (errorDetector.hasCompilationErrors || (component == null)) {
+ return compileTimeErrorExitCode;
+ }
+
+ final IOSink sink = new File(outputFileName).openWrite();
+ final BinaryPrinter printer = new BinaryPrinter(sink);
+ printer.writeComponentFile(component);
+ await sink.close();
+
+ if (depfile != null) {
+ await writeDepfile(fileSystem, component, outputFileName, depfile);
+ }
+
+ if (splitOutputByPackages) {
+ await writeOutputSplitByPackages(
+ mainUri,
+ compilerOptions,
+ component,
+ outputFileName,
+ environmentDefines: environmentDefines,
+ genBytecode: genBytecode,
+ emitBytecodeSourcePositions: emitBytecodeSourcePositions,
+ dropAST: dropAST,
+ );
+ }
+
+ return successExitCode;
+}
+
/// Generates a kernel representation of the program whose main library is in
/// the given [source]. Intended for whole program (non-modular) compilation.
///
@@ -235,7 +427,7 @@
previousErrorHandler?.call(message);
}
- void printCompilationMessages(Uri baseUri) {
+ void printCompilationMessages() {
final sortedUris = compilationMessages.keys.toList()
..sort((a, b) => '$a'.compareTo('$b'));
for (final Uri sourceUri in sortedUris) {
@@ -264,3 +456,205 @@
}
return true;
}
+
+/// Create front-end target with given name.
+Target createFrontEndTarget(String targetName) {
+ // Make sure VM-specific targets are available.
+ installAdditionalTargets();
+
+ final TargetFlags targetFlags = new TargetFlags(syncAsync: true);
+ return getTarget(targetName, targetFlags);
+}
+
+/// Create a front-end file system.
+/// If requested, create a virtual mutli-root file system.
+FileSystem createFrontEndFileSystem(
+ String multiRootFileSystemScheme, List<String> multiRootFileSystemRoots) {
+ FileSystem fileSystem = StandardFileSystem.instance;
+ if (multiRootFileSystemRoots != null &&
+ multiRootFileSystemRoots.isNotEmpty &&
+ multiRootFileSystemScheme != null) {
+ final rootUris = <Uri>[];
+ for (String root in multiRootFileSystemRoots) {
+ rootUris.add(Uri.base.resolveUri(new Uri.file(root)));
+ }
+ fileSystem = new MultiRootFileSystem(
+ multiRootFileSystemScheme, rootUris, fileSystem);
+ }
+ return fileSystem;
+}
+
+/// Convert command line argument [input] which is a file or URI to an
+/// absolute URI.
+///
+/// If virtual multi-root file system is used, or [input] can be parsed to a
+/// URI with 'package' scheme, then [input] is interpreted as URI.
+/// Otherwise [input] is interpreted as a file path.
+Uri convertFileOrUriArgumentToUri(FileSystem fileSystem, String input) {
+ if (input == null) {
+ return null;
+ }
+ // If using virtual multi-root file system, input source argument should be
+ // specified as URI.
+ if (fileSystem is MultiRootFileSystem) {
+ return Uri.base.resolve(input);
+ }
+ try {
+ Uri uri = Uri.parse(input);
+ if (uri.scheme == 'package') {
+ return uri;
+ }
+ } on FormatException {
+ // Ignore, treat input argument as file path.
+ }
+ return Uri.base.resolveUri(new Uri.file(input));
+}
+
+/// Convert a URI which may use virtual file system schema to a real file URI.
+Future<Uri> asFileUri(FileSystem fileSystem, Uri uri) async {
+ FileSystemEntity fse = fileSystem.entityForUri(uri);
+ if (fse is MultiRootFileSystemEntity) {
+ fse = await (fse as MultiRootFileSystemEntity).delegate;
+ }
+ return fse.uri;
+}
+
+/// Convert URI to a package URI if it is inside one of the packages.
+Future<Uri> convertToPackageUri(
+ FileSystem fileSystem, Uri uri, Uri packagesUri) async {
+ // Convert virtual URI to a real file URI.
+ String uriString = (await asFileUri(fileSystem, uri)).toString();
+ List<String> packages;
+ try {
+ packages = await new File(packagesUri.toFilePath()).readAsLines();
+ } on IOException {
+ // Can't read packages file - silently give up.
+ return uri;
+ }
+ // file:///a/b/x/y/main.dart -> package:x.y/main.dart
+ for (var line in packages) {
+ final colon = line.indexOf(':');
+ if (colon == -1) {
+ continue;
+ }
+ final packageName = line.substring(0, colon);
+ final packagePath = line.substring(colon + 1);
+ if (uriString.startsWith(packagePath)) {
+ return Uri.parse(
+ 'package:$packageName/${uriString.substring(packagePath.length)}');
+ }
+ }
+ return uri;
+}
+
+/// Write a separate kernel binary for each package. The name of the
+/// output kernel binary is '[outputFileName]-$package.dilp'.
+/// The list of package names is written into a file '[outputFileName]-packages'.
+///
+/// Generates bytecode for each package if requested.
+Future writeOutputSplitByPackages(
+ Uri source,
+ CompilerOptions compilerOptions,
+ Component component,
+ String outputFileName, {
+ Map<String, String> environmentDefines,
+ bool genBytecode: false,
+ bool emitBytecodeSourcePositions: false,
+ bool dropAST: false,
+ bool useFutureBytecodeFormat: false,
+}) async {
+ // Package sharing: make the encoding not depend on the order in which parts
+ // of a package are loaded.
+ component.libraries.sort((Library a, Library b) {
+ return a.importUri.toString().compareTo(b.importUri.toString());
+ });
+ component.computeCanonicalNames();
+ for (Library lib in component.libraries) {
+ lib.additionalExports.sort((Reference a, Reference b) {
+ return a.canonicalName.toString().compareTo(b.canonicalName.toString());
+ });
+ }
+
+ final packagesSet = new Set<String>();
+ for (Library lib in component.libraries) {
+ packagesSet.add(packageFor(lib));
+ }
+ packagesSet.remove('main');
+ packagesSet.remove(null);
+
+ final List<String> packages = packagesSet.toList();
+ packages.add('main'); // Make sure main package is last.
+
+ await runWithFrontEndCompilerContext(source, compilerOptions, component,
+ () async {
+ for (String package in packages) {
+ final String filename = '$outputFileName-$package.dilp';
+ final IOSink sink = new File(filename).openWrite();
+
+ final main = component.mainMethod;
+ if (package != 'main') {
+ component.mainMethod = null;
+ }
+
+ if (genBytecode) {
+ final List<Library> libraries = component.libraries
+ .where((lib) => packageFor(lib) == package)
+ .toList();
+ generateBytecode(component,
+ libraries: libraries,
+ dropAST: dropAST,
+ emitSourcePositions: emitBytecodeSourcePositions,
+ useFutureBytecodeFormat: useFutureBytecodeFormat,
+ environmentDefines: environmentDefines);
+ }
+
+ final BinaryPrinter printer = new LimitedBinaryPrinter(sink,
+ (lib) => packageFor(lib) == package, false /* excludeUriToSource */);
+ printer.writeComponentFile(component);
+ component.mainMethod = main;
+
+ await sink.close();
+ }
+ });
+
+ final IOSink packagesList = new File('$outputFileName-packages').openWrite();
+ for (String package in packages) {
+ packagesList.writeln(package);
+ }
+ await packagesList.close();
+}
+
+String packageFor(Library lib) {
+ // Core libraries are not written into any package kernel binaries.
+ if (lib.isExternal) return null;
+
+ // Packages are written into their own kernel binaries.
+ Uri uri = lib.importUri;
+ if (uri.scheme == 'package') return uri.pathSegments.first;
+
+ // Everything else (e.g., file: or data: imports) is lumped into the main
+ // kernel binary.
+ return 'main';
+}
+
+String _escapePath(String path) {
+ return path.replaceAll('\\', '\\\\').replaceAll(' ', '\\ ');
+}
+
+/// Create ninja dependencies file, as described in
+/// https://ninja-build.org/manual.html#_depfile
+Future<void> writeDepfile(FileSystem fileSystem, Component component,
+ String output, String depfile) async {
+ final IOSink file = new File(depfile).openWrite();
+ file.write(_escapePath(output));
+ file.write(':');
+ for (Uri dep in component.uriToSource.keys) {
+ // Skip empty or corelib dependencies.
+ if (dep == null || dep.scheme == 'org-dartlang-sdk') continue;
+ Uri uri = await asFileUri(fileSystem, dep);
+ file.write(' ');
+ file.write(_escapePath(uri.toFilePath()));
+ }
+ file.write('\n');
+ await file.close();
+}
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index bd548ba..699874f 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -150,6 +150,7 @@
"tests/standalone/",
"tests/standalone_2/",
"pkg/async_helper/",
+ "pkg/build_integration/",
"pkg/dart_internal/",
"pkg/expect/",
"pkg/front_end/",