mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 01:30:32 +00:00
67e7924365
Change-Id: Icac415945e6aec26d646d4cdf5a29a79051a9127 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/209848 Auto-Submit: Bob Nystrom <rnystrom@google.com> Commit-Queue: Bob Nystrom <rnystrom@google.com> Reviewed-by: William Hesse <whesse@google.com>
207 lines
7.7 KiB
Dart
207 lines
7.7 KiB
Dart
// Copyright (c) 2020, 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 'configuration.dart';
|
|
|
|
/// A step that is run on a builder to build and test certain configurations of
|
|
/// the Dart SDK.
|
|
///
|
|
/// Each step on a builder runs a script the with provided arguments. If the
|
|
/// script is 'tools/test.py' (which is the default if no script is given in
|
|
/// the test matrix), or `testRunner == true`, the step is called a 'test
|
|
/// step'. Test steps must include the '--named_configuration' (for short
|
|
/// '-n') option to select the named [Configuration] to test.
|
|
///
|
|
/// Test steps are expected to produce test results that are collected during
|
|
/// the run of the builder and checked against the expected results to determine
|
|
/// the success or failure of the build.
|
|
class Step {
|
|
final String name;
|
|
final String script;
|
|
final List<String> arguments;
|
|
final Map<String, String> environment;
|
|
final String? fileSet;
|
|
final int? shards;
|
|
final bool isTestRunner;
|
|
final Configuration? testedConfiguration;
|
|
|
|
Step(this.name, String? script, this.arguments, this.environment,
|
|
this.fileSet, this.shards, this.isTestRunner, this.testedConfiguration)
|
|
: script = script ?? testScriptName;
|
|
|
|
static const testScriptName = "tools/test.py";
|
|
|
|
bool get isTestStep => script == testScriptName || isTestRunner;
|
|
|
|
/// Create a [Step] from the 'step template' [map], values for supported
|
|
/// variables [configuration], and the list of supported named configurations.
|
|
static Step parse(Map map, Map<String, String?> configuration,
|
|
List<Configuration> configurations) {
|
|
var arguments = (map["arguments"] as List? ?? [])
|
|
.map((argument) => _expandVariables(argument as String, configuration))
|
|
.toList();
|
|
var testedConfigurations = <Configuration>[];
|
|
var script = map["script"] as String? ?? testScriptName;
|
|
var isTestRunner = map["testRunner"] as bool? ?? false;
|
|
if (script == testScriptName || isTestRunner) {
|
|
// TODO(karlklose): replace with argument parser that can handle all
|
|
// arguments to test.py.
|
|
for (var argument in arguments) {
|
|
var names = <String>[];
|
|
if (argument.startsWith("--named_configuration")) {
|
|
names.addAll(argument
|
|
.substring("--named_configuration".length)
|
|
.split(",")
|
|
.map((s) => s.trim()));
|
|
} else if (argument.startsWith("-n")) {
|
|
names.addAll(
|
|
argument.substring("-n".length).split(",").map((s) => s.trim()));
|
|
} else {
|
|
continue;
|
|
}
|
|
for (var name in names) {
|
|
var matchingConfigurations =
|
|
configurations.where((c) => c.name == name);
|
|
if (matchingConfigurations.isEmpty) {
|
|
throw FormatException("Undefined configuration: $name");
|
|
}
|
|
testedConfigurations.add(matchingConfigurations.single);
|
|
}
|
|
}
|
|
if (testedConfigurations.length > 1) {
|
|
throw FormatException("Step tests multiple configurations: $arguments");
|
|
}
|
|
}
|
|
return Step(
|
|
map["name"] as String,
|
|
script,
|
|
arguments,
|
|
<String, String>{...?map["environment"]},
|
|
map["fileset"] as String?,
|
|
map["shards"] as int?,
|
|
isTestRunner,
|
|
testedConfigurations.isEmpty ? null : testedConfigurations.single);
|
|
}
|
|
}
|
|
|
|
/// A builder runs a list of [Step]s to build and test certain configurations of
|
|
/// the Dart SDK.
|
|
///
|
|
/// Groups of builders are defined in the 'builder_configurations' section of
|
|
/// the test matrix.
|
|
class Builder {
|
|
final String name;
|
|
final String? description;
|
|
final List<Step> steps;
|
|
final System? system;
|
|
final Mode? mode;
|
|
final Architecture? arch;
|
|
final Sanitizer? sanitizer;
|
|
final Runtime? runtime;
|
|
final Set<Configuration> testedConfigurations;
|
|
|
|
Builder(this.name, this.description, this.steps, this.system, this.mode,
|
|
this.arch, this.sanitizer, this.runtime, this.testedConfigurations);
|
|
|
|
/// Create a [Builder] from its name, a list of 'step templates', the
|
|
/// supported named configurations and a description.
|
|
///
|
|
/// The 'step templates' can contain the variables `${system}`, `${mode}`,
|
|
/// `${arch}`, and `${runtime}. The values for these variables are inferred
|
|
/// from the builder's name.
|
|
static Builder parse(String builderName, List<Map> steps,
|
|
List<Configuration> configurations, String? description) {
|
|
var builderParts = builderName.split("-");
|
|
var systemName = _findPart(builderParts, System.names, 'linux');
|
|
var modeName = _findPart(builderParts, Mode.names, 'release');
|
|
var archName = _findPart(builderParts, Architecture.names, 'x64');
|
|
var sanitizerName = _findPart(builderParts, Sanitizer.names);
|
|
var runtimeName = _findPart(builderParts, Runtime.names);
|
|
var parsedSteps = steps
|
|
.map((step) => Step.parse(
|
|
step,
|
|
{
|
|
"system": systemName,
|
|
"mode": modeName,
|
|
"arch": archName,
|
|
"sanitizer": sanitizerName,
|
|
"runtime": runtimeName,
|
|
},
|
|
configurations))
|
|
.toList();
|
|
var testedConfigurations = _getTestedConfigurations(parsedSteps);
|
|
return Builder(
|
|
builderName,
|
|
description,
|
|
parsedSteps,
|
|
_findIfNotNull(System.find, systemName),
|
|
_findIfNotNull(Mode.find, modeName),
|
|
_findIfNotNull(Architecture.find, archName),
|
|
_findIfNotNull(Sanitizer.find, sanitizerName),
|
|
_findIfNotNull(Runtime.find, runtimeName),
|
|
testedConfigurations);
|
|
}
|
|
}
|
|
|
|
/// Tries to replace a variable named [variableName] with [value] and throws
|
|
/// and exception if the variable is used but `value == null`.
|
|
String _tryReplace(String string, String variableName, String? value) {
|
|
var variable = "\${$variableName}";
|
|
if (string.contains(variable)) {
|
|
if (value == null) {
|
|
throw FormatException("Undefined value for '$variableName' in '$string'");
|
|
}
|
|
return string.replaceAll(variable, value);
|
|
} else {
|
|
return string;
|
|
}
|
|
}
|
|
|
|
/// Replace the use of supported variable names with the their value given
|
|
/// in [values] and throws an exception if an unsupported variable name is used.
|
|
String _expandVariables(String string, Map<String, String?> values) {
|
|
for (var variable in ["system", "mode", "arch", "sanitizer", "runtime"]) {
|
|
string = _tryReplace(string, variable, values[variable]);
|
|
}
|
|
return string;
|
|
}
|
|
|
|
Set<Configuration> _getTestedConfigurations(List<Step> steps) {
|
|
return steps
|
|
.where((step) => step.isTestStep)
|
|
.map((step) => step.testedConfiguration)
|
|
.whereType<Configuration>()
|
|
.toSet();
|
|
}
|
|
|
|
T? _findIfNotNull<T>(T Function(String) find, String? name) {
|
|
return name != null ? find(name) : null;
|
|
}
|
|
|
|
String? _findPart(List<String> builderParts, List<String> parts,
|
|
[String? fallback]) {
|
|
return builderParts
|
|
.cast<String?>()
|
|
.firstWhere((part) => parts.contains(part), orElse: () => fallback);
|
|
}
|
|
|
|
List<Builder> parseBuilders(
|
|
List<Map> builderConfigurations, List<Configuration> configurations) {
|
|
var builders = <Builder>[];
|
|
var names = <String>{};
|
|
for (var builderConfiguration in builderConfigurations) {
|
|
var meta = builderConfiguration["meta"] as Map? ?? <String, String>{};
|
|
var builderNames = <String>[...?builderConfiguration["builders"]];
|
|
var steps = <Map>[...?builderConfiguration["steps"]];
|
|
for (var builderName in builderNames) {
|
|
if (!names.add(builderName)) {
|
|
throw FormatException('Duplicate builder name: "$builderName"');
|
|
}
|
|
builders.add(Builder.parse(
|
|
builderName, steps, configurations, meta["description"] as String?));
|
|
}
|
|
}
|
|
return builders;
|
|
}
|