mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 22:59:47 +00:00
Revert "Move most of the code from u/b/kernel_worker.dart to p/frontend_server"
This reverts commit 659fdae898
.
Reason for revert: Broke the HHH bots.
The fix according to zra@ should be:
"The new dependencies should be added as dependency overrides to flutter_frontend_server's pubspec.yaml: https://github.com/flutter/engine/blob/master/flutter_frontend_server/pubspec.yaml"
Original change's description:
> Move most of the code from u/b/kernel_worker.dart to p/frontend_server
>
> This should allow us to import the code in cases when we want to
> implement the Bazel worker loop ourselves. This opens some possibilities
> of extra optimizations for internal use cases.
>
> I'm leaving some of the code (e.g., `main`) in the old location, so that
> we do not break anything that depends on it.
>
> The only thing that I've done is to pass the input-digest map to
> `computeKernel` instead of using `Input` objects directly, this way
> we can avoid the dependency on `package:bazel_worker` from
> `frontend_server`. So now, if we're using the worker loop, the map will
> be computed by `kernel_worker.dart` (in non-worker mode, we rely on
> the default empty map).
>
> Bug: http://b/187910394
> Change-Id: I408407ebdebc17c7898552c053a8e1c269cf2df5
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/201187
> Commit-Queue: Michal Terepeta <michalt@google.com>
> Reviewed-by: Johnni Winther <johnniwinther@google.com>
# Not skipping CQ checks because original CL landed > 1 day ago.
Bug: http://b/187910394
Change-Id: Ieb44d61b668a7d4629a27eef5a6ef61d0db9deaf
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/201880
Reviewed-by: Jake Macdonald <jakemac@google.com>
Reviewed-by: Alexander Aprelev <aam@google.com>
Reviewed-by: Phil Quitslund <pquitslund@google.com>
Auto-Submit: Jake Macdonald <jakemac@google.com>
Commit-Queue: Jake Macdonald <jakemac@google.com>
Commit-Queue: Alexander Aprelev <aam@google.com>
Commit-Queue: Phil Quitslund <pquitslund@google.com>
This commit is contained in:
parent
8580370162
commit
e2cd8be814
|
@ -1,426 +0,0 @@
|
|||
// Copyright (c) 2021, 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.
|
||||
|
||||
// @dart = 2.8
|
||||
|
||||
/// A library to invoke the CFE to compute kernel summary files.
|
||||
///
|
||||
/// Used by `utils/bazel/kernel_worker.dart`.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:args/args.dart';
|
||||
import 'package:build_integration/file_system/multi_root.dart';
|
||||
import 'package:compiler/src/kernel/dart2js_target.dart';
|
||||
import 'package:dev_compiler/src/kernel/target.dart';
|
||||
import 'package:front_end/src/api_unstable/bazel_worker.dart' as fe;
|
||||
import 'package:kernel/ast.dart' show Component, Library, Reference;
|
||||
import 'package:kernel/target/targets.dart';
|
||||
import 'package:vm/target/flutter.dart';
|
||||
import 'package:vm/target/flutter_runner.dart';
|
||||
import 'package:vm/target/vm.dart';
|
||||
|
||||
/// 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 (individual 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, abbr: 'h')
|
||||
..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('target',
|
||||
allowed: const [
|
||||
'vm',
|
||||
'flutter',
|
||||
'flutter_runner',
|
||||
'dart2js',
|
||||
'ddc',
|
||||
],
|
||||
help: 'Build kernel for the vm, flutter, flutter_runner, dart2js or ddc')
|
||||
..addOption('dart-sdk-summary')
|
||||
..addMultiOption('input-summary')
|
||||
..addMultiOption('input-linked')
|
||||
..addMultiOption('multi-root')
|
||||
..addOption('multi-root-scheme', defaultsTo: 'org-dartlang-multi-root')
|
||||
..addOption('libraries-file')
|
||||
..addOption('packages-file')
|
||||
..addMultiOption('source')
|
||||
..addOption('output')
|
||||
..addFlag('reuse-compiler-result', defaultsTo: false)
|
||||
..addFlag('use-incremental-compiler', defaultsTo: false)
|
||||
..addOption('used-inputs')
|
||||
..addFlag('track-widget-creation', defaultsTo: false)
|
||||
..addMultiOption('enable-experiment',
|
||||
help: 'Enable a language experiment when invoking the CFE.')
|
||||
..addMultiOption('define', abbr: 'D')
|
||||
..addFlag('verbose', defaultsTo: false)
|
||||
..addFlag('sound-null-safety', defaultsTo: false)
|
||||
..addOption('verbosity',
|
||||
defaultsTo: fe.Verbosity.defaultValue,
|
||||
help: 'Sets the verbosity level used for filtering messages during '
|
||||
'compilation.',
|
||||
allowed: fe.Verbosity.allowedValues,
|
||||
allowedHelp: fe.Verbosity.allowedValuesHelp);
|
||||
|
||||
class ComputeKernelResult {
|
||||
final bool succeeded;
|
||||
final fe.InitializedCompilerState previousState;
|
||||
|
||||
ComputeKernelResult(this.succeeded, this.previousState);
|
||||
}
|
||||
|
||||
/// 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<ComputeKernelResult> computeKernel(List<String> args,
|
||||
{bool isWorker: false,
|
||||
StringBuffer outputBuffer,
|
||||
Map<Uri, List<int>> inputDigests,
|
||||
fe.InitializedCompilerState previousState}) async {
|
||||
inputDigests ??= <Uri, List<int>>{};
|
||||
dynamic out = outputBuffer ?? stderr;
|
||||
bool succeeded = true;
|
||||
|
||||
var parsedArgs = summaryArgsParser.parse(args);
|
||||
|
||||
if (parsedArgs['help']) {
|
||||
out.writeln(summaryArgsParser.usage);
|
||||
if (!isWorker) exit(0);
|
||||
return new ComputeKernelResult(false, previousState);
|
||||
}
|
||||
|
||||
// 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(toUri).toList();
|
||||
var excludeNonSources = parsedArgs['exclude-non-sources'] as bool;
|
||||
|
||||
var nnbdMode = parsedArgs['sound-null-safety'] as bool
|
||||
? fe.NnbdMode.Strong
|
||||
: fe.NnbdMode.Weak;
|
||||
var summaryOnly = parsedArgs['summary-only'] as bool;
|
||||
var trackWidgetCreation = parsedArgs['track-widget-creation'] as bool;
|
||||
|
||||
// TODO(sigmund,jakemac): make target mandatory. We allow null to be backwards
|
||||
// compatible while we migrate existing clients of this tool.
|
||||
var targetName =
|
||||
(parsedArgs['target'] as String) ?? (summaryOnly ? 'ddc' : 'vm');
|
||||
var targetFlags = new TargetFlags(
|
||||
trackWidgetCreation: trackWidgetCreation,
|
||||
enableNullSafety: nnbdMode == fe.NnbdMode.Strong);
|
||||
Target target;
|
||||
switch (targetName) {
|
||||
case 'vm':
|
||||
target = new VmTarget(targetFlags);
|
||||
if (summaryOnly) {
|
||||
out.writeln('error: --summary-only not supported for the vm target');
|
||||
}
|
||||
break;
|
||||
case 'flutter':
|
||||
target = new FlutterTarget(targetFlags);
|
||||
if (summaryOnly) {
|
||||
throw new ArgumentError(
|
||||
'error: --summary-only not supported for the flutter target');
|
||||
}
|
||||
break;
|
||||
case 'flutter_runner':
|
||||
target = new FlutterRunnerTarget(targetFlags);
|
||||
if (summaryOnly) {
|
||||
throw new ArgumentError('error: --summary-only not supported for the '
|
||||
'flutter_runner target');
|
||||
}
|
||||
break;
|
||||
case 'dart2js':
|
||||
target = new Dart2jsTarget('dart2js', targetFlags);
|
||||
if (summaryOnly) {
|
||||
out.writeln(
|
||||
'error: --summary-only not supported for the dart2js target');
|
||||
}
|
||||
break;
|
||||
case 'ddc':
|
||||
// TODO(jakemac):If `generateKernel` changes to return a summary
|
||||
// component, process the component instead.
|
||||
target =
|
||||
new DevCompilerSummaryTarget(sources, excludeNonSources, targetFlags);
|
||||
if (!summaryOnly) {
|
||||
out.writeln('error: --no-summary-only not supported for the '
|
||||
'ddc target');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
out.writeln('error: unsupported target: $targetName');
|
||||
}
|
||||
|
||||
List<Uri> linkedInputs =
|
||||
(parsedArgs['input-linked'] as List<String>).map(toUri).toList();
|
||||
|
||||
List<Uri> summaryInputs =
|
||||
(parsedArgs['input-summary'] as List<String>).map(toUri).toList();
|
||||
|
||||
fe.InitializedCompilerState state;
|
||||
bool usingIncrementalCompiler = false;
|
||||
bool recordUsedInputs = parsedArgs["used-inputs"] != null;
|
||||
var environmentDefines = _parseEnvironmentDefines(parsedArgs['define']);
|
||||
var verbose = parsedArgs['verbose'] as bool;
|
||||
var verbosity = fe.Verbosity.parseArgument(parsedArgs['verbosity']);
|
||||
|
||||
if (parsedArgs['use-incremental-compiler']) {
|
||||
usingIncrementalCompiler = true;
|
||||
|
||||
// If digests weren't given and if not in worker mode, create fake data and
|
||||
// ensure we don't have a previous state (as that wouldn't be safe with
|
||||
// fake input digests).
|
||||
if (!isWorker && inputDigests.isEmpty) {
|
||||
previousState = null;
|
||||
inputDigests[toUri(parsedArgs['dart-sdk-summary'])] = const [0];
|
||||
for (Uri uri in summaryInputs) {
|
||||
inputDigests[uri] = const [0];
|
||||
}
|
||||
for (Uri uri in linkedInputs) {
|
||||
inputDigests[uri] = const [0];
|
||||
}
|
||||
}
|
||||
|
||||
state = await fe.initializeIncrementalCompiler(
|
||||
previousState,
|
||||
{
|
||||
"target=$targetName",
|
||||
"trackWidgetCreation=$trackWidgetCreation",
|
||||
"multiRootScheme=${fileSystem.markerScheme}",
|
||||
"multiRootRoots=${fileSystem.roots}",
|
||||
},
|
||||
toUri(parsedArgs['dart-sdk-summary']),
|
||||
toUri(parsedArgs['packages-file']),
|
||||
toUri(parsedArgs['libraries-file']),
|
||||
[...summaryInputs, ...linkedInputs],
|
||||
inputDigests,
|
||||
target,
|
||||
fileSystem,
|
||||
(parsedArgs['enable-experiment'] as List<String>),
|
||||
summaryOnly,
|
||||
environmentDefines,
|
||||
trackNeededDillLibraries: recordUsedInputs,
|
||||
verbose: verbose,
|
||||
nnbdMode: nnbdMode);
|
||||
} else {
|
||||
state = await fe.initializeCompiler(
|
||||
// TODO(sigmund): pass an old state once we can make use of it.
|
||||
null,
|
||||
toUri(parsedArgs['dart-sdk-summary']),
|
||||
toUri(parsedArgs['libraries-file']),
|
||||
toUri(parsedArgs['packages-file']),
|
||||
[...summaryInputs, ...linkedInputs],
|
||||
target,
|
||||
fileSystem,
|
||||
parsedArgs['enable-experiment'] as List<String>,
|
||||
environmentDefines,
|
||||
verbose: verbose,
|
||||
nnbdMode: nnbdMode);
|
||||
}
|
||||
|
||||
void onDiagnostic(fe.DiagnosticMessage message) {
|
||||
if (fe.Verbosity.shouldPrint(verbosity, message)) {
|
||||
fe.printDiagnosticMessage(message, out.writeln);
|
||||
}
|
||||
if (message.severity == fe.Severity.error) {
|
||||
succeeded = false;
|
||||
}
|
||||
}
|
||||
|
||||
List<int> kernel;
|
||||
bool wroteUsedDills = false;
|
||||
if (usingIncrementalCompiler) {
|
||||
state.options.onDiagnostic = onDiagnostic;
|
||||
Component incrementalComponent = await state.incrementalCompiler
|
||||
.computeDelta(entryPoints: sources, fullComponent: true);
|
||||
|
||||
if (recordUsedInputs) {
|
||||
Set<Uri> usedOutlines = {};
|
||||
for (Library lib in state.incrementalCompiler.neededDillLibraries) {
|
||||
if (lib.importUri.scheme == "dart") continue;
|
||||
Uri uri = state.libraryToInputDill[lib.importUri];
|
||||
if (uri == null) {
|
||||
throw new StateError("Library ${lib.importUri} was recorded as used, "
|
||||
"but was not in the list of known libraries.");
|
||||
}
|
||||
usedOutlines.add(uri);
|
||||
}
|
||||
var outputUsedFile = new File(parsedArgs["used-inputs"]);
|
||||
outputUsedFile.createSync(recursive: true);
|
||||
outputUsedFile.writeAsStringSync(usedOutlines.join("\n"));
|
||||
wroteUsedDills = true;
|
||||
}
|
||||
|
||||
kernel = await state.incrementalCompiler.context.runInContext((_) {
|
||||
if (summaryOnly) {
|
||||
incrementalComponent.uriToSource.clear();
|
||||
incrementalComponent.problemsAsJson = null;
|
||||
incrementalComponent.setMainMethodAndMode(
|
||||
null, true, incrementalComponent.mode);
|
||||
target.performOutlineTransformations(incrementalComponent);
|
||||
makeStable(incrementalComponent);
|
||||
return Future.value(fe.serializeComponent(incrementalComponent,
|
||||
includeSources: false, includeOffsets: false));
|
||||
}
|
||||
|
||||
makeStable(incrementalComponent);
|
||||
|
||||
return Future.value(fe.serializeComponent(incrementalComponent,
|
||||
filter: excludeNonSources
|
||||
? (library) => sources.contains(library.importUri)
|
||||
: null,
|
||||
includeOffsets: true));
|
||||
});
|
||||
} else if (summaryOnly) {
|
||||
kernel = await fe.compileSummary(state, sources, onDiagnostic,
|
||||
includeOffsets: false);
|
||||
} else {
|
||||
Component component =
|
||||
await fe.compileComponent(state, sources, onDiagnostic);
|
||||
kernel = fe.serializeComponent(component,
|
||||
filter: excludeNonSources
|
||||
? (library) => sources.contains(library.importUri)
|
||||
: null,
|
||||
includeOffsets: true);
|
||||
}
|
||||
state.options.onDiagnostic = null; // See http://dartbug.com/36983.
|
||||
|
||||
if (!wroteUsedDills && recordUsedInputs) {
|
||||
// The path taken didn't record inputs used: Say we used everything.
|
||||
var outputUsedFile = new File(parsedArgs["used-inputs"]);
|
||||
outputUsedFile.createSync(recursive: true);
|
||||
Set<Uri> allFiles = {...summaryInputs, ...linkedInputs};
|
||||
outputUsedFile.writeAsStringSync(allFiles.join("\n"));
|
||||
wroteUsedDills = true;
|
||||
}
|
||||
|
||||
if (kernel != null) {
|
||||
var outputFile = new File(parsedArgs['output']);
|
||||
outputFile.createSync(recursive: true);
|
||||
outputFile.writeAsBytesSync(kernel);
|
||||
} else {
|
||||
assert(!succeeded);
|
||||
}
|
||||
|
||||
return new ComputeKernelResult(succeeded, state);
|
||||
}
|
||||
|
||||
/// Make sure the output is stable by sorting libraries and additional exports.
|
||||
void makeStable(Component c) {
|
||||
// Make sure the output is stable.
|
||||
c.libraries.sort((l1, l2) {
|
||||
return "${l1.fileUri}".compareTo("${l2.fileUri}");
|
||||
});
|
||||
c.problemsAsJson?.sort();
|
||||
c.computeCanonicalNames();
|
||||
for (Library library in c.libraries) {
|
||||
library.additionalExports.sort((Reference r1, Reference r2) {
|
||||
return "${r1.canonicalName}".compareTo("${r2.canonicalName}");
|
||||
});
|
||||
library.problemsAsJson?.sort();
|
||||
}
|
||||
}
|
||||
|
||||
/// Extends the DevCompilerTarget to transform 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 DevCompilerSummaryTarget extends DevCompilerTarget {
|
||||
final List<Uri> sources;
|
||||
final bool excludeNonSources;
|
||||
|
||||
DevCompilerSummaryTarget(
|
||||
this.sources, this.excludeNonSources, TargetFlags targetFlags)
|
||||
: super(targetFlags);
|
||||
|
||||
@override
|
||||
void performOutlineTransformations(Component component) {
|
||||
super.performOutlineTransformations(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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Uri toUri(String uriString) {
|
||||
if (uriString == null) return null;
|
||||
// Windows-style paths use '\', so convert them to '/' in case they've been
|
||||
// concatenated with Unix-style paths.
|
||||
return Uri.base.resolve(uriString.replaceAll("\\", "/"));
|
||||
}
|
||||
|
||||
Map<String, String> _parseEnvironmentDefines(List<String> args) {
|
||||
var environment = <String, String>{};
|
||||
|
||||
for (var arg in args) {
|
||||
var eq = arg.indexOf('=');
|
||||
if (eq <= 0) {
|
||||
var kind = eq == 0 ? 'name' : 'value';
|
||||
throw FormatException('no $kind given to -D option `$arg`');
|
||||
}
|
||||
var name = arg.substring(0, eq);
|
||||
var value = arg.substring(eq + 1);
|
||||
environment[name] = value;
|
||||
}
|
||||
|
||||
return environment;
|
||||
}
|
|
@ -8,10 +8,6 @@ environment:
|
|||
|
||||
dependencies:
|
||||
args: ^1.4.4
|
||||
build_integration:
|
||||
path: ../build_integration
|
||||
compiler:
|
||||
path: ../compiler
|
||||
dev_compiler:
|
||||
path: ../dev_compiler
|
||||
front_end:
|
||||
|
|
|
@ -15,9 +15,17 @@ import 'dart:async';
|
|||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:args/args.dart';
|
||||
import 'package:bazel_worker/bazel_worker.dart';
|
||||
import 'package:build_integration/file_system/multi_root.dart';
|
||||
import 'package:dev_compiler/src/kernel/target.dart';
|
||||
import 'package:front_end/src/api_unstable/bazel_worker.dart' as fe;
|
||||
import 'package:frontend_server/compute_kernel.dart';
|
||||
import 'package:kernel/ast.dart' show Component, Library, Reference;
|
||||
import 'package:kernel/target/targets.dart';
|
||||
import 'package:vm/target/vm.dart';
|
||||
import 'package:vm/target/flutter.dart';
|
||||
import 'package:vm/target/flutter_runner.dart';
|
||||
import 'package:compiler/src/kernel/dart2js_target.dart';
|
||||
|
||||
/// [sendPort] may be passed in when started in an isolate. If provided, it is
|
||||
/// used for bazel worker communication instead of stdin/stdout.
|
||||
|
@ -60,17 +68,10 @@ class KernelWorker extends AsyncWorkerLoop {
|
|||
} else {
|
||||
previousState = null;
|
||||
}
|
||||
|
||||
/// Build a map of uris to digests.
|
||||
final inputDigests = <Uri, List<int>>{};
|
||||
for (var input in request.inputs) {
|
||||
inputDigests[toUri(input.path)] = input.digest;
|
||||
}
|
||||
|
||||
var result = await computeKernel(request.arguments,
|
||||
isWorker: true,
|
||||
outputBuffer: outputBuffer,
|
||||
inputDigests: inputDigests,
|
||||
inputs: request.inputs,
|
||||
previousState: previousStateToPass);
|
||||
previousState = result.previousState;
|
||||
if (!result.succeeded) {
|
||||
|
@ -85,3 +86,413 @@ class KernelWorker extends AsyncWorkerLoop {
|
|||
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, abbr: 'h')
|
||||
..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('target',
|
||||
allowed: const [
|
||||
'vm',
|
||||
'flutter',
|
||||
'flutter_runner',
|
||||
'dart2js',
|
||||
'ddc',
|
||||
],
|
||||
help: 'Build kernel for the vm, flutter, flutter_runner, dart2js or ddc')
|
||||
..addOption('dart-sdk-summary')
|
||||
..addMultiOption('input-summary')
|
||||
..addMultiOption('input-linked')
|
||||
..addMultiOption('multi-root')
|
||||
..addOption('multi-root-scheme', defaultsTo: 'org-dartlang-multi-root')
|
||||
..addOption('libraries-file')
|
||||
..addOption('packages-file')
|
||||
..addMultiOption('source')
|
||||
..addOption('output')
|
||||
..addFlag('reuse-compiler-result', defaultsTo: false)
|
||||
..addFlag('use-incremental-compiler', defaultsTo: false)
|
||||
..addOption('used-inputs')
|
||||
..addFlag('track-widget-creation', defaultsTo: false)
|
||||
..addMultiOption('enable-experiment',
|
||||
help: 'Enable a language experiment when invoking the CFE.')
|
||||
..addMultiOption('define', abbr: 'D')
|
||||
..addFlag('verbose', defaultsTo: false)
|
||||
..addFlag('sound-null-safety', defaultsTo: false)
|
||||
..addOption('verbosity',
|
||||
defaultsTo: fe.Verbosity.defaultValue,
|
||||
help: 'Sets the verbosity level used for filtering messages during '
|
||||
'compilation.',
|
||||
allowed: fe.Verbosity.allowedValues,
|
||||
allowedHelp: fe.Verbosity.allowedValuesHelp);
|
||||
|
||||
class ComputeKernelResult {
|
||||
final bool succeeded;
|
||||
final fe.InitializedCompilerState previousState;
|
||||
|
||||
ComputeKernelResult(this.succeeded, this.previousState);
|
||||
}
|
||||
|
||||
/// 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<ComputeKernelResult> computeKernel(List<String> args,
|
||||
{bool isWorker: false,
|
||||
StringBuffer outputBuffer,
|
||||
Iterable<Input> inputs,
|
||||
fe.InitializedCompilerState previousState}) 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 new ComputeKernelResult(false, previousState);
|
||||
}
|
||||
|
||||
// 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(_toUri).toList();
|
||||
var excludeNonSources = parsedArgs['exclude-non-sources'] as bool;
|
||||
|
||||
var nnbdMode = parsedArgs['sound-null-safety'] as bool
|
||||
? fe.NnbdMode.Strong
|
||||
: fe.NnbdMode.Weak;
|
||||
var summaryOnly = parsedArgs['summary-only'] as bool;
|
||||
var trackWidgetCreation = parsedArgs['track-widget-creation'] as bool;
|
||||
|
||||
// TODO(sigmund,jakemac): make target mandatory. We allow null to be backwards
|
||||
// compatible while we migrate existing clients of this tool.
|
||||
var targetName =
|
||||
(parsedArgs['target'] as String) ?? (summaryOnly ? 'ddc' : 'vm');
|
||||
var targetFlags = new TargetFlags(
|
||||
trackWidgetCreation: trackWidgetCreation,
|
||||
enableNullSafety: nnbdMode == fe.NnbdMode.Strong);
|
||||
Target target;
|
||||
switch (targetName) {
|
||||
case 'vm':
|
||||
target = new VmTarget(targetFlags);
|
||||
if (summaryOnly) {
|
||||
out.writeln('error: --summary-only not supported for the vm target');
|
||||
}
|
||||
break;
|
||||
case 'flutter':
|
||||
target = new FlutterTarget(targetFlags);
|
||||
if (summaryOnly) {
|
||||
throw new ArgumentError(
|
||||
'error: --summary-only not supported for the flutter target');
|
||||
}
|
||||
break;
|
||||
case 'flutter_runner':
|
||||
target = new FlutterRunnerTarget(targetFlags);
|
||||
if (summaryOnly) {
|
||||
throw new ArgumentError('error: --summary-only not supported for the '
|
||||
'flutter_runner target');
|
||||
}
|
||||
break;
|
||||
case 'dart2js':
|
||||
target = new Dart2jsTarget('dart2js', targetFlags);
|
||||
if (summaryOnly) {
|
||||
out.writeln(
|
||||
'error: --summary-only not supported for the dart2js target');
|
||||
}
|
||||
break;
|
||||
case 'ddc':
|
||||
// TODO(jakemac):If `generateKernel` changes to return a summary
|
||||
// component, process the component instead.
|
||||
target =
|
||||
new DevCompilerSummaryTarget(sources, excludeNonSources, targetFlags);
|
||||
if (!summaryOnly) {
|
||||
out.writeln('error: --no-summary-only not supported for the '
|
||||
'ddc target');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
out.writeln('error: unsupported target: $targetName');
|
||||
}
|
||||
|
||||
List<Uri> linkedInputs =
|
||||
(parsedArgs['input-linked'] as List<String>).map(_toUri).toList();
|
||||
|
||||
List<Uri> summaryInputs =
|
||||
(parsedArgs['input-summary'] as List<String>).map(_toUri).toList();
|
||||
|
||||
fe.InitializedCompilerState state;
|
||||
bool usingIncrementalCompiler = false;
|
||||
bool recordUsedInputs = parsedArgs["used-inputs"] != null;
|
||||
var environmentDefines = _parseEnvironmentDefines(parsedArgs['define']);
|
||||
var verbose = parsedArgs['verbose'] as bool;
|
||||
var verbosity = fe.Verbosity.parseArgument(parsedArgs['verbosity']);
|
||||
|
||||
if (parsedArgs['use-incremental-compiler']) {
|
||||
usingIncrementalCompiler = true;
|
||||
|
||||
/// Build a map of uris to digests.
|
||||
final inputDigests = <Uri, List<int>>{};
|
||||
if (inputs != null) {
|
||||
for (var input in inputs) {
|
||||
inputDigests[_toUri(input.path)] = input.digest;
|
||||
}
|
||||
}
|
||||
|
||||
// If digests weren't given and if not in worker mode, create fake data and
|
||||
// ensure we don't have a previous state (as that wouldn't be safe with
|
||||
// fake input digests).
|
||||
if (!isWorker && inputDigests.isEmpty) {
|
||||
previousState = null;
|
||||
inputDigests[_toUri(parsedArgs['dart-sdk-summary'])] = const [0];
|
||||
for (Uri uri in summaryInputs) {
|
||||
inputDigests[uri] = const [0];
|
||||
}
|
||||
for (Uri uri in linkedInputs) {
|
||||
inputDigests[uri] = const [0];
|
||||
}
|
||||
}
|
||||
|
||||
state = await fe.initializeIncrementalCompiler(
|
||||
previousState,
|
||||
{
|
||||
"target=$targetName",
|
||||
"trackWidgetCreation=$trackWidgetCreation",
|
||||
"multiRootScheme=${fileSystem.markerScheme}",
|
||||
"multiRootRoots=${fileSystem.roots}",
|
||||
},
|
||||
_toUri(parsedArgs['dart-sdk-summary']),
|
||||
_toUri(parsedArgs['packages-file']),
|
||||
_toUri(parsedArgs['libraries-file']),
|
||||
[...summaryInputs, ...linkedInputs],
|
||||
inputDigests,
|
||||
target,
|
||||
fileSystem,
|
||||
(parsedArgs['enable-experiment'] as List<String>),
|
||||
summaryOnly,
|
||||
environmentDefines,
|
||||
trackNeededDillLibraries: recordUsedInputs,
|
||||
verbose: verbose,
|
||||
nnbdMode: nnbdMode);
|
||||
} else {
|
||||
state = await fe.initializeCompiler(
|
||||
// TODO(sigmund): pass an old state once we can make use of it.
|
||||
null,
|
||||
_toUri(parsedArgs['dart-sdk-summary']),
|
||||
_toUri(parsedArgs['libraries-file']),
|
||||
_toUri(parsedArgs['packages-file']),
|
||||
[...summaryInputs, ...linkedInputs],
|
||||
target,
|
||||
fileSystem,
|
||||
parsedArgs['enable-experiment'] as List<String>,
|
||||
environmentDefines,
|
||||
verbose: verbose,
|
||||
nnbdMode: nnbdMode);
|
||||
}
|
||||
|
||||
void onDiagnostic(fe.DiagnosticMessage message) {
|
||||
if (fe.Verbosity.shouldPrint(verbosity, message)) {
|
||||
fe.printDiagnosticMessage(message, out.writeln);
|
||||
}
|
||||
if (message.severity == fe.Severity.error) {
|
||||
succeeded = false;
|
||||
}
|
||||
}
|
||||
|
||||
List<int> kernel;
|
||||
bool wroteUsedDills = false;
|
||||
if (usingIncrementalCompiler) {
|
||||
state.options.onDiagnostic = onDiagnostic;
|
||||
Component incrementalComponent = await state.incrementalCompiler
|
||||
.computeDelta(entryPoints: sources, fullComponent: true);
|
||||
|
||||
if (recordUsedInputs) {
|
||||
Set<Uri> usedOutlines = {};
|
||||
for (Library lib in state.incrementalCompiler.neededDillLibraries) {
|
||||
if (lib.importUri.scheme == "dart") continue;
|
||||
Uri uri = state.libraryToInputDill[lib.importUri];
|
||||
if (uri == null) {
|
||||
throw new StateError("Library ${lib.importUri} was recorded as used, "
|
||||
"but was not in the list of known libraries.");
|
||||
}
|
||||
usedOutlines.add(uri);
|
||||
}
|
||||
var outputUsedFile = new File(parsedArgs["used-inputs"]);
|
||||
outputUsedFile.createSync(recursive: true);
|
||||
outputUsedFile.writeAsStringSync(usedOutlines.join("\n"));
|
||||
wroteUsedDills = true;
|
||||
}
|
||||
|
||||
kernel = await state.incrementalCompiler.context.runInContext((_) {
|
||||
if (summaryOnly) {
|
||||
incrementalComponent.uriToSource.clear();
|
||||
incrementalComponent.problemsAsJson = null;
|
||||
incrementalComponent.setMainMethodAndMode(
|
||||
null, true, incrementalComponent.mode);
|
||||
target.performOutlineTransformations(incrementalComponent);
|
||||
makeStable(incrementalComponent);
|
||||
return Future.value(fe.serializeComponent(incrementalComponent,
|
||||
includeSources: false, includeOffsets: false));
|
||||
}
|
||||
|
||||
makeStable(incrementalComponent);
|
||||
|
||||
return Future.value(fe.serializeComponent(incrementalComponent,
|
||||
filter: excludeNonSources
|
||||
? (library) => sources.contains(library.importUri)
|
||||
: null,
|
||||
includeOffsets: true));
|
||||
});
|
||||
} else if (summaryOnly) {
|
||||
kernel = await fe.compileSummary(state, sources, onDiagnostic,
|
||||
includeOffsets: false);
|
||||
} else {
|
||||
Component component =
|
||||
await fe.compileComponent(state, sources, onDiagnostic);
|
||||
kernel = fe.serializeComponent(component,
|
||||
filter: excludeNonSources
|
||||
? (library) => sources.contains(library.importUri)
|
||||
: null,
|
||||
includeOffsets: true);
|
||||
}
|
||||
state.options.onDiagnostic = null; // See http://dartbug.com/36983.
|
||||
|
||||
if (!wroteUsedDills && recordUsedInputs) {
|
||||
// The path taken didn't record inputs used: Say we used everything.
|
||||
var outputUsedFile = new File(parsedArgs["used-inputs"]);
|
||||
outputUsedFile.createSync(recursive: true);
|
||||
Set<Uri> allFiles = {...summaryInputs, ...linkedInputs};
|
||||
outputUsedFile.writeAsStringSync(allFiles.join("\n"));
|
||||
wroteUsedDills = true;
|
||||
}
|
||||
|
||||
if (kernel != null) {
|
||||
var outputFile = new File(parsedArgs['output']);
|
||||
outputFile.createSync(recursive: true);
|
||||
outputFile.writeAsBytesSync(kernel);
|
||||
} else {
|
||||
assert(!succeeded);
|
||||
}
|
||||
|
||||
return new ComputeKernelResult(succeeded, state);
|
||||
}
|
||||
|
||||
/// Make sure the output is stable by sorting libraries and additional exports.
|
||||
void makeStable(Component c) {
|
||||
// Make sure the output is stable.
|
||||
c.libraries.sort((l1, l2) {
|
||||
return "${l1.fileUri}".compareTo("${l2.fileUri}");
|
||||
});
|
||||
c.problemsAsJson?.sort();
|
||||
c.computeCanonicalNames();
|
||||
for (Library library in c.libraries) {
|
||||
library.additionalExports.sort((Reference r1, Reference r2) {
|
||||
return "${r1.canonicalName}".compareTo("${r2.canonicalName}");
|
||||
});
|
||||
library.problemsAsJson?.sort();
|
||||
}
|
||||
}
|
||||
|
||||
/// Extends the DevCompilerTarget to transform 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 DevCompilerSummaryTarget extends DevCompilerTarget {
|
||||
final List<Uri> sources;
|
||||
final bool excludeNonSources;
|
||||
|
||||
DevCompilerSummaryTarget(
|
||||
this.sources, this.excludeNonSources, TargetFlags targetFlags)
|
||||
: super(targetFlags);
|
||||
|
||||
@override
|
||||
void performOutlineTransformations(Component component) {
|
||||
super.performOutlineTransformations(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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Uri _toUri(String uriString) {
|
||||
if (uriString == null) return null;
|
||||
// Windows-style paths use '\', so convert them to '/' in case they've been
|
||||
// concatenated with Unix-style paths.
|
||||
return Uri.base.resolve(uriString.replaceAll("\\", "/"));
|
||||
}
|
||||
|
||||
Map<String, String> _parseEnvironmentDefines(List<String> args) {
|
||||
var environment = <String, String>{};
|
||||
|
||||
for (var arg in args) {
|
||||
var eq = arg.indexOf('=');
|
||||
if (eq <= 0) {
|
||||
var kind = eq == 0 ? 'name' : 'value';
|
||||
throw FormatException('no $kind given to -D option `$arg`');
|
||||
}
|
||||
var name = arg.substring(0, eq);
|
||||
var value = arg.substring(eq + 1);
|
||||
environment[name] = value;
|
||||
}
|
||||
|
||||
return environment;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue