blob: 55bf077ec5a106cb57897a584171e90479013d42 [file] [log] [blame]
// Copyright (c) 2014, 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.
import 'dart:io';
import 'command.dart';
import 'configuration.dart';
import 'path.dart';
import 'repository.dart';
import 'runtime_configuration.dart';
import 'test_file.dart';
import 'utils.dart';
List<String> _replaceDartFiles(List<String> list, String replacement) {
return list
.map((file) => file.endsWith(".dart") ? replacement : file)
.toList();
}
/// Generates a list with a single `--enable-experiment=` option that includes
/// all experiments enabled by [configuration] and [testFile].
///
/// Returns an empty list if there are no experiments to enable. Returning a
/// list allows the result of calling this to be spread into another list.
List<String> _experimentsArgument(
TestConfiguration configuration, TestFile testFile) {
var experiments = {
...configuration.experiments,
...testFile.experiments,
};
if (experiments.isEmpty) {
return const [];
}
return ['--enable-experiment=${experiments.join(',')}'];
}
List<String> _nnbdModeArgument(TestConfiguration configuration) {
switch (configuration.nnbdMode) {
case NnbdMode.legacy:
return [];
case NnbdMode.strong:
return ['--sound-null-safety'];
case NnbdMode.weak:
return ['--no-sound-null-safety'];
}
throw 'unreachable';
}
/// Grouping of a command with its expected result.
class CommandArtifact {
final List<Command> commands;
/// Expected result of running [commands].
final String filename;
/// MIME type of [filename].
final String mimeType;
CommandArtifact(this.commands, this.filename, this.mimeType);
}
abstract class CompilerConfiguration {
final TestConfiguration _configuration;
bool get _isDebug => _configuration.mode.isDebug;
bool get _isHostChecked => _configuration.isHostChecked;
bool get _useSdk => _configuration.useSdk;
bool get _enableAsserts => _configuration.enableAsserts;
/// Whether to run the runtime on the compilation result of a test which
/// expects a compile-time error and the compiler did not emit one.
bool get runRuntimeDespiteMissingCompileTimeError => false;
factory CompilerConfiguration(TestConfiguration configuration) {
switch (configuration.compiler) {
case Compiler.dart2analyzer:
return AnalyzerCompilerConfiguration(configuration);
case Compiler.compareAnalyzerCfe:
return CompareAnalyzerCfeCompilerConfiguration(configuration);
case Compiler.dart2js:
return Dart2jsCompilerConfiguration(configuration);
case Compiler.dartdevc:
return DevCompilerConfiguration(configuration);
case Compiler.dartdevk:
return DevCompilerConfiguration(configuration);
case Compiler.appJitk:
return AppJitCompilerConfiguration(configuration);
case Compiler.dartk:
if (configuration.architecture == Architecture.simarm ||
configuration.architecture == Architecture.simarm64 ||
configuration.architecture == Architecture.simarm64c ||
configuration.system == System.android) {
return VMKernelCompilerConfiguration(configuration);
}
return NoneCompilerConfiguration(configuration);
case Compiler.dartkp:
return PrecompilerCompilerConfiguration(configuration);
case Compiler.specParser:
return SpecParserCompilerConfiguration(configuration);
case Compiler.fasta:
return FastaCompilerConfiguration(configuration);
case Compiler.none:
return NoneCompilerConfiguration(configuration);
}
throw "unreachable";
}
CompilerConfiguration._subclass(this._configuration);
/// A multiplier used to give tests longer time to run.
int get timeoutMultiplier {
if (_configuration.configuration.vmOptions
.any((s) => s.contains("optimization-counter-threshold"))) {
return 2;
} else {
return 1;
}
}
String computeCompilerPath() {
throw "Unknown compiler for: $runtimeType";
}
bool get hasCompiler => true;
List<Uri> bootstrapDependencies() => const <Uri>[];
CommandArtifact computeCompilationArtifact(
/// Each test has its own temporary directory to avoid name collisions.
String tempDir,
List<String> arguments,
Map<String, String> environmentOverrides) {
return CommandArtifact([], null, null);
}
List<String> computeCompilerArguments(
TestFile testFile, List<String> vmOptions, List<String> args) {
return [
...testFile.sharedOptions,
..._configuration.sharedOptions,
..._experimentsArgument(_configuration, testFile),
...args
];
}
List<String> computeRuntimeArguments(
RuntimeConfiguration runtimeConfiguration,
TestFile testFile,
List<String> vmOptions,
List<String> originalArguments,
CommandArtifact artifact) {
return [artifact.filename];
}
}
/// The "none" compiler.
class NoneCompilerConfiguration extends CompilerConfiguration {
NoneCompilerConfiguration(TestConfiguration configuration)
: super._subclass(configuration);
bool get hasCompiler => false;
List<String> computeRuntimeArguments(
RuntimeConfiguration runtimeConfiguration,
TestFile testFile,
List<String> vmOptions,
List<String> originalArguments,
CommandArtifact artifact) {
return [
if (_enableAsserts) '--enable_asserts',
if (_configuration.hotReload)
'--hot-reload-test-mode'
else if (_configuration.hotReloadRollback)
'--hot-reload-rollback-test-mode',
...vmOptions,
..._nnbdModeArgument(_configuration),
...testFile.sharedOptions,
..._configuration.sharedOptions,
..._experimentsArgument(_configuration, testFile),
...originalArguments,
...testFile.dartOptions
];
}
}
class VMKernelCompilerConfiguration extends CompilerConfiguration
with VMKernelCompilerMixin {
VMKernelCompilerConfiguration(TestConfiguration configuration)
: super._subclass(configuration);
bool get _isAot => false;
// Issue(http://dartbug.com/29840): Currently fasta sometimes does not emit a
// compile-time error (even though it should). The VM will emit some of these
// compile-time errors (e.g. in constant evaluator, class finalizer, ...).
//
// => Since this distinction between fasta and vm reported compile-time
// errors do not exist when running dart with the kernel-service, we will
// also not make this distinction when compiling to .dill and then run.
//
// The corresponding http://dartbug.com/29840 tracks to get the frontend to
// emit all necessary compile-time errors (and *additionally* encode them
// in the AST in certain cases).
bool get runRuntimeDespiteMissingCompileTimeError => true;
CommandArtifact computeCompilationArtifact(String tempDir,
List<String> arguments, Map<String, String> environmentOverrides) {
final commands = <Command>[
computeCompileToKernelCommand(tempDir, arguments, environmentOverrides),
];
return CommandArtifact(commands, tempKernelFile(tempDir),
'application/kernel-ir-fully-linked');
}
@override
List<String> computeCompilerArguments(
TestFile testFile, List<String> vmOptions, List<String> args) {
return [
...testFile.sharedOptions,
..._configuration.sharedOptions,
..._experimentsArgument(_configuration, testFile),
...vmOptions,
..._nnbdModeArgument(_configuration),
...args
];
}
List<String> computeRuntimeArguments(
RuntimeConfiguration runtimeConfiguration,
TestFile testFile,
List<String> vmOptions,
List<String> originalArguments,
CommandArtifact artifact) {
var filename = artifact.filename;
if (runtimeConfiguration is DartkAdbRuntimeConfiguration) {
// On Android the Dill file will be pushed to a different directory on the
// device. Use that one instead.
filename = "${DartkAdbRuntimeConfiguration.deviceTestDir}/out.dill";
}
return [
if (_enableAsserts) '--enable_asserts',
if (_configuration.hotReload)
'--hot-reload-test-mode'
else if (_configuration.hotReloadRollback)
'--hot-reload-rollback-test-mode',
...vmOptions,
..._nnbdModeArgument(_configuration),
...testFile.sharedOptions,
..._configuration.sharedOptions,
..._experimentsArgument(_configuration, testFile),
..._replaceDartFiles(originalArguments, filename),
...testFile.dartOptions
];
}
}
typedef CompilerArgumentsFunction = List<String> Function(
List<String> globalArguments, String previousCompilerOutput);
class PipelineCommand {
final CompilerConfiguration compilerConfiguration;
final CompilerArgumentsFunction _argumentsFunction;
PipelineCommand._(this.compilerConfiguration, this._argumentsFunction);
factory PipelineCommand.runWithGlobalArguments(
CompilerConfiguration configuration) {
return PipelineCommand._(configuration,
(List<String> globalArguments, String previousOutput) {
assert(previousOutput == null);
return globalArguments;
});
}
factory PipelineCommand.runWithDartOrKernelFile(
CompilerConfiguration configuration) {
return PipelineCommand._(configuration,
(List<String> globalArguments, String previousOutput) {
var filtered = globalArguments
.where((name) => name.endsWith('.dart') || name.endsWith('.dill'))
.toList();
assert(filtered.length == 1);
return filtered;
});
}
factory PipelineCommand.runWithPreviousKernelOutput(
CompilerConfiguration configuration) {
return PipelineCommand._(configuration,
(List<String> globalArguments, String previousOutput) {
assert(previousOutput.endsWith('.dill'));
return _replaceDartFiles(globalArguments, previousOutput);
});
}
List<String> extractArguments(
List<String> globalArguments, String previousOutput) {
return _argumentsFunction(globalArguments, previousOutput);
}
}
class ComposedCompilerConfiguration extends CompilerConfiguration {
final List<PipelineCommand> pipelineCommands;
ComposedCompilerConfiguration(
TestConfiguration configuration, this.pipelineCommands)
: super._subclass(configuration);
CommandArtifact computeCompilationArtifact(String tempDir,
List<String> arguments, Map<String, String> environmentOverrides) {
var allCommands = <Command>[];
// The first compilation command is as usual.
var compileArguments =
pipelineCommands[0].extractArguments(arguments, null);
var artifact = pipelineCommands[0]
.compilerConfiguration
.computeCompilationArtifact(
tempDir, compileArguments, environmentOverrides);
allCommands.addAll(artifact.commands);
// The following compilation commands are based on the output of the
// previous one.
for (var i = 1; i < pipelineCommands.length; i++) {
var command = pipelineCommands[i];
compileArguments = command.extractArguments(arguments, artifact.filename);
artifact = command.compilerConfiguration.computeCompilationArtifact(
tempDir, compileArguments, environmentOverrides);
allCommands.addAll(artifact.commands);
}
return CommandArtifact(allCommands, artifact.filename, artifact.mimeType);
}
List<String> computeCompilerArguments(
TestFile testFile, List<String> vmOptions, List<String> args) {
// The result will be passed as an input to [extractArguments]
// (i.e. the arguments to the [PipelineCommand]).
return [
...vmOptions,
...testFile.sharedOptions,
..._configuration.sharedOptions,
..._experimentsArgument(_configuration, testFile),
...args
];
}
List<String> computeRuntimeArguments(
RuntimeConfiguration runtimeConfiguration,
TestFile testFile,
List<String> vmOptions,
List<String> originalArguments,
CommandArtifact artifact) {
var lastCompilerConfiguration = pipelineCommands.last.compilerConfiguration;
return lastCompilerConfiguration.computeRuntimeArguments(
runtimeConfiguration, testFile, vmOptions, originalArguments, artifact);
}
}
/// Common configuration for dart2js-based tools, such as dart2js.
class Dart2xCompilerConfiguration extends CompilerConfiguration {
static final Map<String, List<Uri>> _bootstrapDependenciesCache = {};
final String moniker;
Dart2xCompilerConfiguration(this.moniker, TestConfiguration configuration)
: super._subclass(configuration);
String computeCompilerPath() {
var prefix = 'sdk/bin';
var suffix = shellScriptExtension;
if (_isHostChecked) {
if (_useSdk) {
// Note: when [_useSdk] is true, dart2js is run from a snapshot that was
// built without checked mode. The VM cannot make such snapshot run in
// checked mode later. These two flags could be used together if we also
// build an sdk with checked snapshots.
throw "--host-checked and --use-sdk cannot be used together";
}
// The script dart2js_developer is not included in the
// shipped SDK, that is the script is not installed in
// "$buildDir/dart-sdk/bin/"
return '$prefix/dart2js_developer$suffix';
}
if (_useSdk) {
prefix = '${_configuration.buildDirectory}/dart-sdk/bin';
}
return '$prefix/dart2js$suffix';
}
Command computeCompilationCommand(String outputFileName,
List<String> arguments, Map<String, String> environmentOverrides) {
arguments = arguments.toList();
arguments.add('--out=$outputFileName');
return CompilationCommand(moniker, outputFileName, bootstrapDependencies(),
computeCompilerPath(), arguments, environmentOverrides,
alwaysCompile: !_useSdk);
}
List<Uri> bootstrapDependencies() {
if (!_useSdk) return const <Uri>[];
return _bootstrapDependenciesCache.putIfAbsent(
_configuration.buildDirectory,
() => [
Uri.base
.resolveUri(Uri.directory(_configuration.buildDirectory))
.resolve('dart-sdk/bin/snapshots/dart2js.dart.snapshot')
]);
}
}
/// Configuration for dart2js.
class Dart2jsCompilerConfiguration extends Dart2xCompilerConfiguration {
Dart2jsCompilerConfiguration(TestConfiguration configuration)
: super('dart2js', configuration);
List<String> computeCompilerArguments(
TestFile testFile, List<String> vmOptions, List<String> args) {
return [
...testFile.sharedOptions,
..._configuration.sharedOptions,
..._experimentsArgument(_configuration, testFile),
...testFile.dart2jsOptions,
..._nnbdModeArgument(_configuration),
...args
];
}
CommandArtifact computeCompilationArtifact(String tempDir,
List<String> arguments, Map<String, String> environmentOverrides) {
var compilerArguments = [
...arguments,
..._configuration.dart2jsOptions,
..._nnbdModeArgument(_configuration),
];
// TODO(athom): input filename extraction is copied from DDC. Maybe this
// should be passed to computeCompilationArtifact, instead?
var inputFile = arguments.last;
var inputFilename = Uri.file(inputFile).pathSegments.last;
var out = "$tempDir/${inputFilename.replaceAll('.dart', '.js')}";
var babel = _configuration.babel;
var babelOut = out;
if (babel != null && babel.isNotEmpty) {
out = out.replaceAll('.js', '.raw.js');
}
var commands = [
computeCompilationCommand(out, compilerArguments, environmentOverrides),
if (babel != null && babel.isNotEmpty)
computeBabelCommand(out, babelOut, babel)
];
return CommandArtifact(commands, babelOut, 'application/javascript');
}
List<String> computeRuntimeArguments(
RuntimeConfiguration runtimeConfiguration,
TestFile testFile,
List<String> vmOptions,
List<String> originalArguments,
CommandArtifact artifact) {
var sdk = _useSdk
? Uri.directory(_configuration.buildDirectory).resolve('dart-sdk/')
: Uri.directory(Repository.dir.toNativePath()).resolve('sdk/');
var preambleDir = sdk.resolve('lib/_internal/js_runtime/lib/preambles/');
return runtimeConfiguration.dart2jsPreambles(preambleDir)
..add(artifact.filename);
}
Command computeBabelCommand(String input, String output, String options) {
var uri = Repository.uri;
var babelTransform =
uri.resolve('pkg/test_runner/lib/src/babel_transform.js').toFilePath();
var babelStandalone =
uri.resolve('third_party/babel/babel.min.js').toFilePath();
return CompilationCommand(
'babel',
output,
[],
_configuration.runtimeConfiguration.d8FileName,
[babelTransform, "--", babelStandalone, options, input],
{},
alwaysCompile: true); // TODO(athom): ensure dependency tracking works.
}
}
/// Configuration for `dartdevc` and `dartdevk` (DDC with Kernel)
class DevCompilerConfiguration extends CompilerConfiguration {
DevCompilerConfiguration(TestConfiguration configuration)
: super._subclass(configuration);
bool get useKernel => _configuration.compiler == Compiler.dartdevk;
String computeCompilerPath() {
var dir = _useSdk ? "${_configuration.buildDirectory}/dart-sdk" : "sdk";
return "$dir/bin/dartdevc$shellScriptExtension";
}
List<String> computeCompilerArguments(
TestFile testFile, List<String> vmOptions, List<String> args) {
return [
...testFile.sharedOptions,
..._configuration.sharedOptions,
..._experimentsArgument(_configuration, testFile),
...testFile.ddcOptions,
if (_configuration.nnbdMode == NnbdMode.strong) '--sound-null-safety',
// The file being compiled is the last argument.
args.last
];
}
Command _createCommand(String inputFile, String outputFile,
List<String> sharedOptions, Map<String, String> environment) {
var args = <String>[];
// Remove option for generating non-null assertions for non-nullable
// method parameters in weak mode. DDC treats this as a runtime flag for
// the bootstrapping code, instead of a compiler option.
var options = sharedOptions.toList();
options.remove('--null-assertions');
if (!_useSdk) {
// If we're testing a built SDK, DDC will find its own summary.
//
// For local development we don't have a built SDK yet, so point directly
// at the built summary file location.
var sdkSummaryFile = _configuration.nnbdMode == NnbdMode.strong
? 'ddc_outline_sound.dill'
: 'ddc_outline.dill';
var sdkSummary = Path(_configuration.buildDirectory)
.append(sdkSummaryFile)
.absolute
.toNativePath();
args.addAll(["--dart-sdk-summary", sdkSummary]);
}
args.addAll(options);
args.addAll(_configuration.sharedOptions);
var d8Runtime = _configuration.runtime == Runtime.d8;
args.addAll([
"--ignore-unrecognized-flags",
"--no-summarize",
if (d8Runtime) "--modules=legacy",
"-o",
outputFile,
inputFile,
]);
if (!d8Runtime) {
// TODO(sigmund): allow caching of shared packages in legacy mode too.
// Link to the summaries for the available packages, so that they don't
// get recompiled into the test's own module.
var packageSummaryDir = _configuration.nnbdMode == NnbdMode.strong
? 'pkg_sound'
: 'pkg_kernel';
for (var package in testPackages) {
args.add("-s");
// Since the summaries for the packages are not near the tests, we give
// dartdevc explicit module paths for each one. When the test is run, we
// will tell require.js where to find each package's compiled JS.
var summary = Path(_configuration.buildDirectory)
.append("/gen/utils/dartdevc/$packageSummaryDir/$package.dill")
.absolute
.toNativePath();
args.add("$summary=$package");
}
}
var inputDir = Path(inputFile).append("..").canonicalize().toNativePath();
var displayName = useKernel ? 'dartdevk' : 'dartdevc';
return CompilationCommand(displayName, outputFile, bootstrapDependencies(),
computeCompilerPath(), args, environment,
workingDirectory: inputDir);
}
CommandArtifact computeCompilationArtifact(String tempDir,
List<String> arguments, Map<String, String> environmentOverrides) {
// The list of arguments comes from a call to our own
// computeCompilerArguments(). It contains the shared options followed by
// the input file path.
// TODO(rnystrom): Jamming these into a list in order to pipe them from
// computeCompilerArguments() to here seems hacky. Is there a cleaner way?
var sharedOptions = arguments.sublist(0, arguments.length - 1);
var inputFile = arguments.last;
var inputUri = Uri.file(inputFile);
var inputFilename = inputUri.pathSegments.last;
var moduleName =
inputFilename.substring(0, inputFilename.length - ".dart".length);
var outputFile = "$tempDir/$moduleName.js";
var runFile = outputFile;
if (_configuration.runtime == Runtime.d8) {
// TODO(sigmund): ddc should have a flag to emit an entrypoint file like
// the one below, otherwise it is succeptible to break, for example, if
// library naming conventions were to change in the future.
runFile = "$tempDir/$moduleName.d8.js";
var nonNullAsserts = arguments.contains('--null-assertions');
var nativeNonNullAsserts = arguments.contains('--native-null-assertions');
var weakNullSafetyErrors =
arguments.contains('--weak-null-safety-errors');
var soundNullSafety = _configuration.nnbdMode == NnbdMode.strong;
var weakNullSafetyWarnings = !(weakNullSafetyErrors || soundNullSafety);
var repositoryUri = Uri.directory(Repository.dir.toNativePath());
var dartLibraryPath = repositoryUri
.resolve('pkg/dev_compiler/lib/js/legacy/dart_library.js')
.path;
var sdkJsDir = Uri.directory(_configuration.buildDirectory)
.resolve('gen/utils/dartdevc/');
var sdkJsPath = soundNullSafety
? 'sound/legacy/dart_sdk.js'
: 'kernel/legacy/dart_sdk.js';
var libraryName = inputUri.path
.substring(repositoryUri.path.length)
.replaceAll("/", "__")
.replaceAll("-", "_")
.replaceAll(".dart", "");
// Note: this assumes that d8 is invoked with the dart2js d8.js preamble.
// TODO(sigmund): to support other runtimes like js-shell, we may want to
// remove the `load` statements here and instead provide those files
// through the runtime command-line arguments.
File(runFile).writeAsStringSync('''
load("$dartLibraryPath");
load("$sdkJsDir/$sdkJsPath");
load("$outputFile");
let sdk = dart_library.import("dart_sdk");
sdk.dart.weakNullSafetyWarnings($weakNullSafetyWarnings);
sdk.dart.weakNullSafetyErrors($weakNullSafetyErrors);
sdk.dart.nonNullAsserts($nonNullAsserts);
sdk.dart.nativeNonNullAsserts($nativeNonNullAsserts);
// Invoke main through the d8 preamble to ensure the code is running
// within the fake event loop.
self.dartMainRunner(function () {
dart_library.start("$moduleName", "$libraryName");
});
'''
.replaceAll("\n ", "\n"));
}
return CommandArtifact([
_createCommand(inputFile, outputFile, sharedOptions, environmentOverrides)
], runFile, "application/javascript");
}
List<String> computeRuntimeArguments(
RuntimeConfiguration runtimeConfiguration,
TestFile testFile,
List<String> vmOptions,
List<String> originalArguments,
CommandArtifact artifact) {
var sdkDir = _useSdk
? Uri.directory(_configuration.buildDirectory).resolve('dart-sdk/')
: Uri.directory(Repository.dir.toNativePath()).resolve('sdk/');
var preambleDir = sdkDir.resolve('lib/_internal/js_runtime/lib/preambles/');
return runtimeConfiguration.dart2jsPreambles(preambleDir)
..add(artifact.filename);
}
}
class PrecompilerCompilerConfiguration extends CompilerConfiguration
with VMKernelCompilerMixin {
bool get _isAndroid => _configuration.system == System.android;
bool get _isArm => _configuration.architecture == Architecture.arm;
bool get _isSimArm => _configuration.architecture == Architecture.simarm;
bool get _isSimArm64 =>
_configuration.architecture == Architecture.simarm64 ||
_configuration.architecture == Architecture.simarm64c;
bool get _isArmX64 => _configuration.architecture == Architecture.arm_x64;
bool get _isArm64 =>
_configuration.architecture == Architecture.arm64 ||
_configuration.architecture == Architecture.arm64c;
bool get _isX64 =>
_configuration.architecture == Architecture.x64 ||
_configuration.architecture == Architecture.x64c;
bool get _isIA32 => _configuration.architecture == Architecture.ia32;
bool get _isAot => true;
PrecompilerCompilerConfiguration(TestConfiguration configuration)
: super._subclass(configuration);
int get timeoutMultiplier {
var multiplier = 2;
if (_isDebug) multiplier *= 4;
if (_enableAsserts) multiplier *= 2;
return multiplier;
}
CommandArtifact computeCompilationArtifact(String tempDir,
List<String> arguments, Map<String, String> environmentOverrides) {
var commands = <Command>[];
commands.add(computeCompileToKernelCommand(
tempDir, arguments, environmentOverrides));
commands.add(
computeDartBootstrapCommand(tempDir, arguments, environmentOverrides));
if (!_configuration.keepGeneratedFiles) {
commands.add(computeRemoveKernelFileCommand(
tempDir, arguments, environmentOverrides));
}
if (!_configuration.useElf) {
commands.add(
computeAssembleCommand(tempDir, arguments, environmentOverrides));
if (!_configuration.keepGeneratedFiles) {
commands.add(computeRemoveAssemblyCommand(
tempDir, arguments, environmentOverrides));
}
}
if (_configuration.useElf && _isAndroid) {
// On Android, run the NDK's "strip" tool with "--strip-unneeded" to copy
// Flutter's workflow. Skip this step on tests for DWARF (which may get
// stripped).
if (!arguments.last.contains("dwarf")) {
commands.add(computeStripCommand(tempDir, environmentOverrides));
}
}
return CommandArtifact(
commands, '$tempDir', 'application/dart-precompiled');
}
/// Creates a command to clean up large temporary kernel files.
///
/// Warning: this command removes temporary file and violates tracking of
/// dependencies between commands, which may cause problems if multiple
/// almost identical configurations are tested simultaneously.
Command computeRemoveKernelFileCommand(String tempDir, List arguments,
Map<String, String> environmentOverrides) {
String exec;
List<String> args;
if (Platform.isWindows) {
exec = 'cmd.exe';
args = ['/c', 'del', tempKernelFile(tempDir)];
} else {
exec = 'rm';
args = [tempKernelFile(tempDir)];
}
return CompilationCommand('remove_kernel_file', tempDir,
bootstrapDependencies(), exec, args, environmentOverrides,
alwaysCompile: !_useSdk);
}
Command computeDartBootstrapCommand(String tempDir, List<String> arguments,
Map<String, String> environmentOverrides) {
var buildDir = _configuration.buildDirectory;
var exec = _configuration.genSnapshotPath;
if (exec == null) {
if (_isAndroid) {
if (_isArm || _isIA32) {
exec = "$buildDir/clang_x86/gen_snapshot";
} else if (_isArm64 || _isX64 || _isArmX64) {
exec = "$buildDir/clang_x64/gen_snapshot";
} else {
// Guaranteed by package:test_runner/src/configuration.dart's
// TestConfiguration.validate().
assert(false);
}
} else if (_configuration.builderTag == "crossword") {
exec = "${buildDir}_X64/gen_snapshot";
} else if (_isArm && _configuration.useQemu) {
// DebugXARM --> DebugSIMARM_X64
final simBuildDir = buildDir.replaceAll("XARM", "SIMARM_X64");
exec = "$simBuildDir/gen_snapshot";
} else if (_isArm64 && _configuration.useQemu) {
exec = "$buildDir/clang_x64/gen_snapshot";
} else {
exec = "$buildDir/gen_snapshot";
}
}
var args = [
if (_configuration.useElf) ...[
"--snapshot-kind=app-aot-elf",
"--elf=$tempDir/out.aotsnapshot",
// Only splitting with a ELF to avoid having to setup compilation of
// multiple assembly files in the test harness.
"--loading-unit-manifest=$tempDir/ignored.json",
] else ...[
"--snapshot-kind=app-aot-assembly",
"--assembly=$tempDir/out.S",
],
if (_isAndroid && _isArm) '--no-sim-use-hardfp',
if (_configuration.isMinified) '--obfuscate',
// The SIMARM precompiler assumes support for integer division, but the
// Qemu arm cpus do not support integer division.
if (_configuration.useQemu) '--no-use-integer-division',
..._replaceDartFiles(arguments, tempKernelFile(tempDir)),
];
return CompilationCommand('precompiler', tempDir, bootstrapDependencies(),
exec, args, environmentOverrides,
alwaysCompile: !_useSdk);
}
static const String ndkPath = "third_party/android_tools/ndk";
String get abiTriple => _isArm || _isArmX64
? "arm-linux-androideabi"
: _isArm64
? "aarch64-linux-android"
: null;
String get host => Platform.isLinux
? "linux"
: Platform.isMacOS
? "darwin"
: null;
Command computeAssembleCommand(String tempDir, List arguments,
Map<String, String> environmentOverrides) {
String cc, shared, ldFlags;
if (_isAndroid) {
cc = "$ndkPath/toolchains/$abiTriple-4.9/prebuilt/"
"$host-x86_64/bin/$abiTriple-gcc";
shared = '-shared';
} else if (Platform.isLinux) {
if (_isSimArm || (_isArm && _configuration.useQemu)) {
cc = 'arm-linux-gnueabihf-gcc';
} else if (_isSimArm64 || (_isArm64 && _configuration.useQemu)) {
cc = 'aarch64-linux-gnu-gcc';
} else {
cc = 'gcc';
}
shared = '-shared';
} else if (Platform.isMacOS) {
cc = 'clang';
shared = '-dynamiclib';
// Tell Mac linker to give up generating eh_frame from dwarf.
ldFlags = '-Wl,-no_compact_unwind';
} else {
throw "Platform not supported: ${Platform.operatingSystem}";
}
String ccFlags;
switch (_configuration.architecture) {
case Architecture.x64:
case Architecture.x64c:
ccFlags = "-m64";
break;
case Architecture.simarm64:
case Architecture.simarm64c:
case Architecture.ia32:
case Architecture.simarm:
case Architecture.arm:
case Architecture.arm_x64:
case Architecture.arm64:
case Architecture.arm64c:
ccFlags = null;
break;
default:
throw "Architecture not supported: ${_configuration.architecture.name}";
}
var args = [
if (ccFlags != null) ccFlags,
if (ldFlags != null) ldFlags,
shared,
'-nostdlib',
'-o',
'$tempDir/out.aotsnapshot',
'$tempDir/out.S'
];
return CompilationCommand('assemble', tempDir, bootstrapDependencies(), cc,
args, environmentOverrides,
alwaysCompile: !_useSdk);
}
Command computeStripCommand(
String tempDir, Map<String, String> environmentOverrides) {
var stripTool = "$ndkPath/toolchains/$abiTriple-4.9/prebuilt/"
"$host-x86_64/bin/$abiTriple-strip";
var args = ['--strip-unneeded', "$tempDir/out.aotsnapshot"];
return CompilationCommand('strip', tempDir, bootstrapDependencies(),
stripTool, args, environmentOverrides,
alwaysCompile: !_useSdk);
}
/// Creates a command to clean up large temporary assembly files.
///
/// This step reduces the amount of space needed to run the precompilation
/// tests by 60%.
/// Warning: this command removes temporary file and violates tracking of
/// dependencies between commands, which may cause problems if multiple
/// almost identical configurations are tested simultaneously.
Command computeRemoveAssemblyCommand(String tempDir, List arguments,
Map<String, String> environmentOverrides) {
return CompilationCommand('remove_assembly', tempDir,
bootstrapDependencies(), 'rm', ['$tempDir/out.S'], environmentOverrides,
alwaysCompile: !_useSdk);
}
List<String> filterVmOptions(List<String> vmOptions) {
var filtered = vmOptions.toList();
filtered.removeWhere(
(option) => option.startsWith("--optimization-counter-threshold"));
filtered.removeWhere(
(option) => option.startsWith("--optimization_counter_threshold"));
return filtered;
}
List<String> computeCompilerArguments(
TestFile testFile, List<String> vmOptions, List<String> args) {
return [
if (_enableAsserts) '--enable_asserts',
...filterVmOptions(vmOptions),
...testFile.sharedOptions,
..._configuration.sharedOptions,
..._experimentsArgument(_configuration, testFile),
...args
];
}
List<String> computeRuntimeArguments(
RuntimeConfiguration runtimeConfiguration,
TestFile testFile,
List<String> vmOptions,
List<String> originalArguments,
CommandArtifact artifact) {
var dir = artifact.filename;
if (runtimeConfiguration is DartPrecompiledAdbRuntimeConfiguration) {
// On android the precompiled snapshot will be pushed to a different
// directory on the device, use that one instead.
dir = DartPrecompiledAdbRuntimeConfiguration.deviceTestDir;
}
originalArguments =
_replaceDartFiles(originalArguments, "$dir/out.aotsnapshot");
return [
if (_enableAsserts) '--enable_asserts',
...vmOptions,
..._nnbdModeArgument(_configuration),
...testFile.sharedOptions,
..._configuration.sharedOptions,
..._experimentsArgument(_configuration, testFile),
...originalArguments,
...testFile.dartOptions
];
}
}
class AppJitCompilerConfiguration extends CompilerConfiguration {
AppJitCompilerConfiguration(TestConfiguration configuration)
: super._subclass(configuration);
int get timeoutMultiplier {
var multiplier = 1;
if (_isDebug) multiplier *= 2;
if (_enableAsserts) multiplier *= 2;
return multiplier;
}
CommandArtifact computeCompilationArtifact(String tempDir,
List<String> arguments, Map<String, String> environmentOverrides) {
var snapshot = "$tempDir/out.jitsnapshot";
return CommandArtifact(
[computeCompilationCommand(tempDir, arguments, environmentOverrides)],
snapshot,
'application/dart-snapshot');
}
Command computeCompilationCommand(String tempDir, List<String> arguments,
Map<String, String> environmentOverrides) {
var snapshot = "$tempDir/out.jitsnapshot";
return CompilationCommand(
'app_jit',
tempDir,
bootstrapDependencies(),
"${_configuration.buildDirectory}/dart",
["--snapshot=$snapshot", "--snapshot-kind=app-jit", ...arguments],
environmentOverrides,
alwaysCompile: !_useSdk);
}
List<String> computeCompilerArguments(
TestFile testFile, List<String> vmOptions, List<String> args) {
return [
if (_enableAsserts) '--enable_asserts',
...vmOptions,
..._nnbdModeArgument(_configuration),
...testFile.sharedOptions,
..._configuration.sharedOptions,
..._experimentsArgument(_configuration, testFile),
...args,
...testFile.dartOptions
];
}
List<String> computeRuntimeArguments(
RuntimeConfiguration runtimeConfiguration,
TestFile testFile,
List<String> vmOptions,
List<String> originalArguments,
CommandArtifact artifact) {
return [
if (_enableAsserts) '--enable_asserts',
...vmOptions,
..._nnbdModeArgument(_configuration),
...testFile.sharedOptions,
..._configuration.sharedOptions,
..._experimentsArgument(_configuration, testFile),
..._replaceDartFiles(originalArguments, artifact.filename),
...testFile.dartOptions
];
}
}
/// Configuration for dartanalyzer.
class AnalyzerCompilerConfiguration extends CompilerConfiguration {
AnalyzerCompilerConfiguration(TestConfiguration configuration)
: super._subclass(configuration);
int get timeoutMultiplier => 4;
String computeCompilerPath() {
var prefix = 'sdk/bin';
if (_isHostChecked) {
if (_useSdk) {
throw "--host-checked and --use-sdk cannot be used together";
}
// The script dartanalyzer_developer is not included in the
// shipped SDK, that is the script is not installed in
// "$buildDir/dart-sdk/bin/"
return '$prefix/dartanalyzer_developer$shellScriptExtension';
}
if (_useSdk) {
prefix = '${_configuration.buildDirectory}/dart-sdk/bin';
}
return '$prefix/dartanalyzer$shellScriptExtension';
}
CommandArtifact computeCompilationArtifact(String tempDir,
List<String> arguments, Map<String, String> environmentOverrides) {
const legacyTestDirectories = {
"co19_2",
"corelib_2",
"ffi_2",
"language_2",
"lib_2",
"service_2",
"standalone_2"
};
// If we are running a legacy test with NNBD enabled, tell analyzer to use
// a pre-NNBD language version for the test.
var testPath = arguments.last;
var segments = Path(testPath).relativeTo(Repository.dir).segments();
var setLegacyVersion = segments.any(legacyTestDirectories.contains);
var args = [
...arguments,
if (_configuration.useAnalyzerCfe) '--use-cfe',
if (_configuration.useAnalyzerFastaParser) '--use-fasta-parser',
if (setLegacyVersion) '--default-language-version=2.7',
];
// Since this is not a real compilation, no artifacts are produced.
return CommandArtifact(
[AnalysisCommand(computeCompilerPath(), args, environmentOverrides)],
null,
null);
}
List<String> computeRuntimeArguments(
RuntimeConfiguration runtimeConfiguration,
TestFile testFile,
List<String> vmOptions,
List<String> originalArguments,
CommandArtifact artifact) {
return [];
}
}
/// Configuration for compareAnalyzerCfe.
class CompareAnalyzerCfeCompilerConfiguration extends CompilerConfiguration {
CompareAnalyzerCfeCompilerConfiguration(TestConfiguration configuration)
: super._subclass(configuration);
int get timeoutMultiplier => 4;
String computeCompilerPath() {
if (_useSdk) {
throw "--use-sdk cannot be used with compiler compare_analyzer_cfe";
}
return 'pkg/analyzer_fe_comparison/bin/'
'compare_sdk_tests$shellScriptExtension';
}
CommandArtifact computeCompilationArtifact(String tempDir,
List<String> arguments, Map<String, String> environmentOverrides) {
// Since this is not a real compilation, no artifacts are produced.
return CommandArtifact([
CompareAnalyzerCfeCommand(
computeCompilerPath(), arguments.toList(), environmentOverrides)
], null, null);
}
List<String> computeRuntimeArguments(
RuntimeConfiguration runtimeConfiguration,
TestFile testFile,
List<String> vmOptions,
List<String> originalArguments,
CommandArtifact artifact) {
return [];
}
}
/// Configuration for spec_parser.
class SpecParserCompilerConfiguration extends CompilerConfiguration {
SpecParserCompilerConfiguration(TestConfiguration configuration)
: super._subclass(configuration);
String computeCompilerPath() => 'tools/spec_parse.py';
CommandArtifact computeCompilationArtifact(String tempDir,
List<String> arguments, Map<String, String> environmentOverrides) {
arguments = arguments.toList();
// Since this is not a real compilation, no artifacts are produced.
return CommandArtifact([
SpecParseCommand(computeCompilerPath(), arguments, environmentOverrides)
], null, null);
}
List<String> computeRuntimeArguments(
RuntimeConfiguration runtimeConfiguration,
TestFile testFile,
List<String> vmOptions,
List<String> originalArguments,
CommandArtifact artifact) {
return [];
}
}
abstract class VMKernelCompilerMixin {
TestConfiguration get _configuration;
bool get _useSdk;
bool get _isAot;
bool get _enableAsserts;
List<Uri> bootstrapDependencies();
String tempKernelFile(String tempDir) =>
Path('$tempDir/out.dill').toNativePath();
Command computeCompileToKernelCommand(String tempDir, List<String> arguments,
Map<String, String> environmentOverrides) {
var pkgVmDir = Platform.script.resolve('../../../pkg/vm').toFilePath();
var genKernel = '$pkgVmDir/tool/gen_kernel$shellScriptExtension';
var kernelBinariesFolder = '${_configuration.buildDirectory}';
if (_useSdk) {
kernelBinariesFolder += '/dart-sdk/lib/_internal';
}
var vmPlatform = '$kernelBinariesFolder/vm_platform_strong.dill';
var dillFile = tempKernelFile(tempDir);
var isProductMode = _configuration.configuration.mode == Mode.product;
var args = [
_isAot ? '--aot' : '--no-aot',
'--platform=$vmPlatform',
'-o',
dillFile,
arguments.where((name) => name.endsWith('.dart')).single,
...arguments.where((name) =>
name.startsWith('-D') ||
name.startsWith('--define') ||
name.startsWith('--packages=') ||
name.startsWith('--enable-experiment=')),
'-Ddart.vm.product=$isProductMode',
if (_enableAsserts ||
arguments.contains('--enable-asserts') ||
arguments.contains('--enable_asserts'))
'--enable-asserts',
..._nnbdModeArgument(_configuration),
..._configuration.genKernelOptions,
];
return VMKernelCompilationCommand(dillFile, bootstrapDependencies(),
genKernel, args, environmentOverrides,
alwaysCompile: true);
}
}
class FastaCompilerConfiguration extends CompilerConfiguration {
static final _compilerLocation =
Repository.uri.resolve("pkg/front_end/tool/_fasta/compile.dart");
final Uri _platformDill;
final Uri _vmExecutable;
factory FastaCompilerConfiguration(TestConfiguration configuration) {
var buildDirectory =
Uri.base.resolveUri(Uri.directory(configuration.buildDirectory));
var dillDir = buildDirectory;
if (configuration.useSdk) {
dillDir = buildDirectory.resolve("dart-sdk/lib/_internal/");
}
var platformDill = dillDir.resolve("vm_platform_strong.dill");
var vmExecutable = buildDirectory
.resolve(configuration.useSdk ? "dart-sdk/bin/dart" : "dart");
return FastaCompilerConfiguration._(
platformDill, vmExecutable, configuration);
}
FastaCompilerConfiguration._(
this._platformDill, this._vmExecutable, TestConfiguration configuration)
: super._subclass(configuration);
@override
bool get runRuntimeDespiteMissingCompileTimeError => true;
@override
List<Uri> bootstrapDependencies() => [_platformDill];
@override
CommandArtifact computeCompilationArtifact(String tempDir,
List<String> arguments, Map<String, String> environmentOverrides) {
var output =
Uri.base.resolveUri(Uri.directory(tempDir)).resolve("out.dill");
var outputFileName = output.toFilePath();
var compilerArguments = [
'--verify',
'--skip-platform-verification',
"-o",
outputFileName,
"--platform",
_platformDill.toFilePath(),
...arguments
];
return CommandArtifact([
FastaCompilationCommand(
_compilerLocation,
output.toFilePath(),
bootstrapDependencies(),
_vmExecutable.toFilePath(),
compilerArguments,
environmentOverrides,
Repository.uri.toFilePath())
], outputFileName, "application/x.dill");
}
@override
List<String> computeCompilerArguments(
TestFile testFile, List<String> vmOptions, List<String> args) {
// Remove shared option for generating non-null assertions for non-nullable
// method parameters in weak mode. It's currently unused by the front end.
var options = testFile.sharedOptions.toList();
options.remove('--null-assertions');
var arguments = [
...options,
..._configuration.sharedOptions,
..._experimentsArgument(_configuration, testFile),
if (_configuration.configuration.nnbdMode == NnbdMode.strong) ...[
"--nnbd-strong"
]
];
for (var argument in args) {
if (argument == "--ignore-unrecognized-flags") continue;
arguments.add(argument);
if (!argument.startsWith("-")) {
// Some tests pass arguments to the Dart program; that is, arguments
// after the name of the test file. Such arguments have nothing to do
// with the compiler and should be ignored.
break;
}
}
return arguments;
}
@override
List<String> computeRuntimeArguments(
RuntimeConfiguration runtimeConfiguration,
TestFile testFile,
List<String> vmOptions,
List<String> originalArguments,
CommandArtifact artifact) {
if (runtimeConfiguration is! NoneRuntimeConfiguration) {
throw "--compiler=fasta only supports --runtime=none";
}
return [];
}
}