mirror of
https://github.com/dart-lang/sdk
synced 2024-10-03 10:32:04 +00:00
bde6bbca96
Similar to how dart2js keeps its own target in package compiler. This allows VmTarget to use package vm specific transformations and metadata. Change-Id: I41dd2ae241b828224fb2c9a51e6ad5073b6fdea8 Reviewed-on: https://dart-review.googlesource.com/69160 Commit-Queue: Vyacheslav Egorov <vegorov@google.com> Reviewed-by: Dmitry Stefantsov <dmitryas@google.com> Reviewed-by: Kevin Millikin <kmillikin@google.com>
218 lines
7.6 KiB
Dart
218 lines
7.6 KiB
Dart
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
/// A tool that invokes the CFE to compute kernel summary files.
|
|
///
|
|
/// This script can be used as a command-line command or a persistent server.
|
|
/// The server is implemented using the bazel worker protocol, so it can be used
|
|
/// within bazel as is. Other tools (like pub-build and package-build) also
|
|
/// use this persistent worker via the same protocol.
|
|
|
|
import 'dart:async';
|
|
import 'dart:io';
|
|
|
|
import 'package:args/args.dart';
|
|
import 'package:bazel_worker/bazel_worker.dart';
|
|
import 'package:build_integration/file_system/multi_root.dart';
|
|
import 'package:front_end/src/api_unstable/bazel_worker.dart' as fe;
|
|
import 'package:kernel/ast.dart' show Component, Library;
|
|
import 'package:kernel/target/targets.dart';
|
|
import 'package:vm/target/vm.dart';
|
|
|
|
main(List<String> args) async {
|
|
args = preprocessArgs(args);
|
|
|
|
if (args.contains('--persistent_worker')) {
|
|
if (args.length != 1) {
|
|
throw new StateError(
|
|
"unexpected args, expected only --persistent-worker but got: $args");
|
|
}
|
|
await new KernelWorker().run();
|
|
} else {
|
|
var succeeded = await computeKernel(args);
|
|
if (!succeeded) {
|
|
exitCode = 15;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A bazel worker loop that can compute full or summary kernel files.
|
|
class KernelWorker extends AsyncWorkerLoop {
|
|
Future<WorkResponse> performRequest(WorkRequest request) async {
|
|
var outputBuffer = new StringBuffer();
|
|
var response = new WorkResponse()..exitCode = 0;
|
|
try {
|
|
var succeeded = await computeKernel(request.arguments,
|
|
isWorker: true, outputBuffer: outputBuffer);
|
|
if (!succeeded) {
|
|
response.exitCode = 15;
|
|
}
|
|
} catch (e, s) {
|
|
outputBuffer.writeln(e);
|
|
outputBuffer.writeln(s);
|
|
response.exitCode = 15;
|
|
}
|
|
response.output = outputBuffer.toString();
|
|
return response;
|
|
}
|
|
}
|
|
|
|
/// If the last arg starts with `@`, this reads the file it points to and treats
|
|
/// each line as an additional arg.
|
|
///
|
|
/// This is how individual work request args are differentiated from startup
|
|
/// args in bazel (inidividual work request args go in that file).
|
|
List<String> preprocessArgs(List<String> args) {
|
|
args = new List.from(args);
|
|
if (args.isEmpty) {
|
|
return args;
|
|
}
|
|
String lastArg = args.last;
|
|
if (lastArg.startsWith('@')) {
|
|
File argsFile = new File(lastArg.substring(1));
|
|
try {
|
|
args.removeLast();
|
|
args.addAll(argsFile.readAsLinesSync());
|
|
} on FileSystemException catch (e) {
|
|
throw new Exception('Failed to read file specified by $lastArg : $e');
|
|
}
|
|
}
|
|
return args;
|
|
}
|
|
|
|
/// An [ArgParser] for generating kernel summaries.
|
|
final summaryArgsParser = new ArgParser()
|
|
..addFlag('help', negatable: false)
|
|
..addFlag('exclude-non-sources',
|
|
negatable: false,
|
|
help: 'Whether source files loaded implicitly should be included as '
|
|
'part of the summary.')
|
|
..addFlag('summary-only',
|
|
defaultsTo: true,
|
|
negatable: true,
|
|
help: 'Whether to only build summary files.')
|
|
..addOption('dart-sdk-summary')
|
|
..addMultiOption('input-summary')
|
|
..addMultiOption('input-linked')
|
|
..addMultiOption('multi-root')
|
|
..addOption('multi-root-scheme', defaultsTo: 'org-dartlang-multi-root')
|
|
..addOption('packages-file')
|
|
..addMultiOption('source')
|
|
..addOption('output');
|
|
|
|
/// Computes a kernel file based on [args].
|
|
///
|
|
/// If [isWorker] is true then exit codes will not be set on failure.
|
|
///
|
|
/// If [outputBuffer] is provided then messages will be written to that buffer
|
|
/// instead of printed to the console.
|
|
///
|
|
/// Returns whether or not the summary was successfully output.
|
|
Future<bool> computeKernel(List<String> args,
|
|
{bool isWorker: false, StringBuffer outputBuffer}) async {
|
|
dynamic out = outputBuffer ?? stderr;
|
|
bool succeeded = true;
|
|
var parsedArgs = summaryArgsParser.parse(args);
|
|
|
|
if (parsedArgs['help']) {
|
|
out.writeln(summaryArgsParser.usage);
|
|
if (!isWorker) exit(0);
|
|
return false;
|
|
}
|
|
|
|
// Bazel creates an overlay file system where some files may be located in the
|
|
// source tree, some in a gendir, and some in a bindir. The multi-root file
|
|
// system hides this from the front end.
|
|
var multiRoots = parsedArgs['multi-root'].map(Uri.base.resolve).toList();
|
|
if (multiRoots.isEmpty) multiRoots.add(Uri.base);
|
|
var fileSystem = new MultiRootFileSystem(parsedArgs['multi-root-scheme'],
|
|
multiRoots, fe.StandardFileSystem.instance);
|
|
var sources = (parsedArgs['source'] as List<String>).map(Uri.parse).toList();
|
|
Target target;
|
|
var summaryOnly = parsedArgs['summary-only'] as bool;
|
|
var excludeNonSources = parsedArgs['exclude-non-sources'] as bool;
|
|
if (summaryOnly) {
|
|
target = new SummaryTarget(
|
|
sources, excludeNonSources, new TargetFlags(strongMode: true));
|
|
} else {
|
|
target = new VmTarget(new TargetFlags(strongMode: true));
|
|
}
|
|
var state = await fe.initializeCompiler(
|
|
// TODO(sigmund): pass an old state once we can make use of it.
|
|
null,
|
|
Uri.base.resolve(parsedArgs['dart-sdk-summary']),
|
|
Uri.base.resolve(parsedArgs['packages-file']),
|
|
(parsedArgs['input-summary'] as List<String>)
|
|
.map(Uri.base.resolve)
|
|
.toList(),
|
|
(parsedArgs['input-linked'] as List<String>)
|
|
.map(Uri.base.resolve)
|
|
.toList(),
|
|
target,
|
|
fileSystem);
|
|
|
|
void onProblem(fe.FormattedMessage message, severity,
|
|
List<fe.FormattedMessage> context) {
|
|
out.writeln(message.formatted);
|
|
for (fe.FormattedMessage message in context) {
|
|
out.writeln(message.formatted);
|
|
}
|
|
succeeded = false;
|
|
}
|
|
|
|
var kernel =
|
|
await fe.compile(state, sources, onProblem, summaryOnly: summaryOnly);
|
|
|
|
if (kernel != null) {
|
|
var outputFile = new File(parsedArgs['output']);
|
|
outputFile.createSync(recursive: true);
|
|
outputFile.writeAsBytesSync(kernel);
|
|
} else {
|
|
assert(!succeeded);
|
|
}
|
|
|
|
return succeeded;
|
|
}
|
|
|
|
/// A target that transforms outlines to meet the requirements of summaries in
|
|
/// bazel and package-build.
|
|
///
|
|
/// Build systems like package-build may provide the same input file twice to
|
|
/// the summary worker, but only intends to have it in one output summary. The
|
|
/// convention is that if it is listed as a source, it is intended to be part of
|
|
/// the output, if the source file was loaded as a dependency, then it was
|
|
/// already included in a different summary. The transformation below ensures
|
|
/// that the output summary doesn't include those implicit inputs.
|
|
///
|
|
/// Note: this transformation is destructive and is only intended to be used
|
|
/// when generating summaries.
|
|
class SummaryTarget extends NoneTarget {
|
|
final List<Uri> sources;
|
|
final bool excludeNonSources;
|
|
|
|
SummaryTarget(this.sources, this.excludeNonSources, TargetFlags flags)
|
|
: super(flags);
|
|
|
|
@override
|
|
void performOutlineTransformations(Component component) {
|
|
if (!excludeNonSources) return;
|
|
|
|
List<Library> libraries = new List.from(component.libraries);
|
|
component.libraries.clear();
|
|
Set<Uri> include = sources.toSet();
|
|
for (var lib in libraries) {
|
|
if (include.contains(lib.importUri)) {
|
|
component.libraries.add(lib);
|
|
} else {
|
|
// Excluding the library also means that their canonical names will not
|
|
// be computed as part of serialization, so we need to do that
|
|
// preemtively here to avoid errors when serializing references to
|
|
// elements of these libraries.
|
|
component.root.getChildFromUri(lib.importUri).bindTo(lib.reference);
|
|
lib.computeCanonicalNames();
|
|
}
|
|
}
|
|
}
|
|
}
|