Refactor `BuildPlan`, better document and explain `--config`. (#56324)
Closes https://github.com/flutter/flutter/issues/156591.
Closes https://github.com/flutter/flutter/issues/156355.
I also moved some functions only used as implementation details :)
diff --git a/tools/engine_tool/BUILD.gn b/tools/engine_tool/BUILD.gn
index 0171d08..398eb01 100644
--- a/tools/engine_tool/BUILD.gn
+++ b/tools/engine_tool/BUILD.gn
@@ -8,6 +8,7 @@
testonly = true
public_deps = [
":build_command_test",
+ ":build_plan_test",
":entry_point_test",
":fetch_command_test",
":flutter_tools_test",
@@ -40,6 +41,10 @@
main_dart = "test/commands/build_command_test.dart"
}
+dart_test("build_plan_test") {
+ main_dart = "test/commands/build_plan_test.dart"
+}
+
dart_test("entry_point_test") {
main_dart = "test/entry_point_test.dart"
}
diff --git a/tools/engine_tool/lib/src/build_plan.dart b/tools/engine_tool/lib/src/build_plan.dart
index 035e519..3aab6ec 100644
--- a/tools/engine_tool/lib/src/build_plan.dart
+++ b/tools/engine_tool/lib/src/build_plan.dart
@@ -6,6 +6,7 @@
import 'package:collection/collection.dart';
import 'package:engine_build_configs/engine_build_configs.dart';
import 'package:meta/meta.dart';
+import 'package:platform/platform.dart';
import 'build_utils.dart';
import 'environment.dart';
@@ -162,28 +163,40 @@
required Map<String, BuilderConfig> configs,
}) {
// Add --config.
- final builds = runnableBuilds(
- environment,
- configs,
- environment.verbose || !help,
+ final builds = _extractBuilds(
+ environment.platform,
+ runnableConfigs: _runnableBuildConfigs(
+ environment.platform,
+ configsByName: configs,
+ ),
+ hideCiSpecificBuilds: help && !environment.verbose,
);
debugCheckBuilds(builds);
parser.addOption(
_flagConfig,
abbr: 'c',
- defaultsTo: () {
- if (builds.any((b) => b.name == 'host_debug')) {
- return 'host_debug';
- }
- return null;
- }(),
+ help: ''
+ 'Selects a build configuration for the current platform.\n'
+ '\n'
+ 'If omitted, et attempts '
+ 'to default to a suitable target platform. This is typically a '
+ '"host_debug" build when building on a supported desktop OS, or a '
+ 'suitable build when targeting (via "et run") a flutter app.\n'
+ '\n'
+ '${environment.verbose ? ''
+ 'Since verbose mode was selected, both local development '
+ 'configurations and configurations that are typically only '
+ 'used on CI will be visible, including possible duplicates.' : ''
+ 'Configurations include (use --verbose for more details):'}',
allowed: [
for (final config in builds) mangleConfigName(environment, config.name),
- ],
- allowedHelp: {
- for (final config in builds)
- mangleConfigName(environment, config.name): config.description,
- },
+ ]..sort(),
+ allowedHelp: environment.verbose
+ ? {
+ for (final config in builds)
+ mangleConfigName(environment, config.name): config.description,
+ }
+ : null,
);
// Add --lto.
@@ -361,3 +374,41 @@
const BuildStrategy(this._help);
final String _help;
}
+
+typedef _ConfigsByName = Iterable<MapEntry<String, BuilderConfig>>;
+
+/// Computes a list of build configs that can can execute on [environment].
+_ConfigsByName _runnableBuildConfigs(
+ Platform platform, {
+ required Map<String, BuilderConfig> configsByName,
+}) {
+ return configsByName.entries.where((entry) {
+ return entry.value.canRunOn(platform);
+ });
+}
+
+/// Extracts [Build]s from [runnableConfigs] that can execute on [platform].
+///
+/// If [hideCiSpecificBuilds], builds that are unlikely to be picked for local
+/// development (i.e. start with the prefix `ci/` by convention) are not
+/// returned in order to make command-line _help_ text shorter.
+List<Build> _extractBuilds(
+ Platform platform, {
+ required _ConfigsByName runnableConfigs,
+ required bool hideCiSpecificBuilds,
+}) {
+ return [
+ for (final buildConfig in runnableConfigs)
+ ...buildConfig.value.builds.where(
+ (build) {
+ if (!build.canRunOn(platform)) {
+ return false;
+ }
+ if (!hideCiSpecificBuilds) {
+ return true;
+ }
+ return build.name.startsWith(platform.operatingSystem);
+ },
+ ),
+ ];
+}
diff --git a/tools/engine_tool/lib/src/build_utils.dart b/tools/engine_tool/lib/src/build_utils.dart
index 3a25b56..5a99a0f 100644
--- a/tools/engine_tool/lib/src/build_utils.dart
+++ b/tools/engine_tool/lib/src/build_utils.dart
@@ -11,51 +11,6 @@
import 'label.dart';
import 'logger.dart';
-/// A function that returns true or false when given a [BuilderConfig] and its
-/// name.
-typedef ConfigFilter = bool Function(String name, BuilderConfig config);
-
-/// A function that returns true or false when given a [BuilderConfig] name
-/// and a [Build].
-typedef BuildFilter = bool Function(String configName, Build build);
-
-/// Returns a filtered copy of [input] filtering out configs where test
-/// returns false.
-Map<String, BuilderConfig> filterBuilderConfigs(
- Map<String, BuilderConfig> input, ConfigFilter test) {
- return <String, BuilderConfig>{
- for (final MapEntry<String, BuilderConfig> entry in input.entries)
- if (test(entry.key, entry.value)) entry.key: entry.value,
- };
-}
-
-/// Returns a copy of [input] filtering out configs that are not runnable
-/// on the current platform.
-Map<String, BuilderConfig> runnableBuilderConfigs(
- Environment env, Map<String, BuilderConfig> input) {
- return filterBuilderConfigs(input, (String name, BuilderConfig config) {
- return config.canRunOn(env.platform);
- });
-}
-
-/// Returns a List of [Build] that match test.
-List<Build> filterBuilds(Map<String, BuilderConfig> input, BuildFilter test) {
- return <Build>[
- for (final MapEntry<String, BuilderConfig> entry in input.entries)
- for (final Build build in entry.value.builds)
- if (test(entry.key, build)) build,
- ];
-}
-
-/// Returns a list of runnable builds.
-List<Build> runnableBuilds(
- Environment env, Map<String, BuilderConfig> input, bool verbose) {
- return filterBuilds(input, (String configName, Build build) {
- return build.canRunOn(env.platform) &&
- (verbose || build.name.startsWith(env.platform.operatingSystem));
- });
-}
-
/// Validates the list of builds.
/// Calls assert.
void debugCheckBuilds(List<Build> builds) {
@@ -99,8 +54,10 @@
if (_doNotMangle(env, name)) {
return name;
}
- throw ArgumentError(
- 'name argument "$name" must start with a valid platform name or "ci"',
+ throw ArgumentError.value(
+ name,
+ 'name',
+ 'Expected to start with a valid platform name (i.e. $osPrefix) or "ci/"',
);
}
diff --git a/tools/engine_tool/lib/src/commands/command_runner.dart b/tools/engine_tool/lib/src/commands/command_runner.dart
index 454cfa2..d1afdd8 100644
--- a/tools/engine_tool/lib/src/commands/command_runner.dart
+++ b/tools/engine_tool/lib/src/commands/command_runner.dart
@@ -25,7 +25,14 @@
required this.environment,
required this.configs,
this.help = false,
- }) : super(toolName, toolDescription, usageLineLength: _usageLineLength) {
+ }) : super(
+ 'et',
+ ''
+ 'A command line tool for working on '
+ 'the Flutter Engine.\n\nThis is a community supported project, '
+ 'for more information see https://flutter.dev/to/et.',
+ usageLineLength: _usageLineLength,
+ ) {
final List<Command<int>> commands = <Command<int>>[
FetchCommand(
environment: environment,
@@ -73,16 +80,6 @@
);
}
- /// The name of the tool as reported in the tool's usage and help
- /// messages.
- static const String toolName = 'et';
-
- /// The description of the tool reported in the tool's usage and help
- /// messages.
- static const String toolDescription = 'A command line tool for working on '
- 'the Flutter Engine.\n\nThis is a community supported project, file '
- 'a bug or feature request: https://flutter.dev/to/engine-tool-bug.';
-
/// The host system environment.
final Environment environment;
diff --git a/tools/engine_tool/test/build_plan_test.dart b/tools/engine_tool/test/build_plan_test.dart
index 691ddbb..20d8a36 100644
--- a/tools/engine_tool/test/build_plan_test.dart
+++ b/tools/engine_tool/test/build_plan_test.dart
@@ -547,6 +547,75 @@
contains('Additional arguments to provide to "gn"'),
);
});
+
+ /// Returns an [ArgParser] pre-primed with both MacOS and Linux builds.
+ ArgParser createMultiPlatformArgParser({
+ required bool verbose,
+ }) {
+ final testEnv = TestEnvironment.withTestEngine(
+ verbose: verbose,
+ );
+ addTearDown(testEnv.cleanup);
+
+ final linuxConfig = TestBuilderConfig();
+ linuxConfig.addBuild(
+ name: 'linux/host_debug',
+ dimension: TestDroneDimension.linux,
+ description: 'A development build of the Linux host.',
+ );
+ linuxConfig.addBuild(
+ name: 'ci/linux_host_debug',
+ dimension: TestDroneDimension.linux,
+ description: 'A CI-suitable development build of the Linux host.',
+ );
+
+ final macOSConfig = TestBuilderConfig();
+ macOSConfig.addBuild(
+ name: 'macos/host_debug',
+ dimension: TestDroneDimension.mac,
+ description: 'A build we do not expect to see due to filtering.',
+ );
+
+ final parser = ArgParser();
+ final _ = BuildPlan.configureArgParser(
+ parser,
+ testEnv.environment,
+ configs: {
+ 'linux_test_config': linuxConfig.buildConfig(
+ path: 'ci/builders/linux_test_config.json',
+ ),
+ 'macos_test_config': macOSConfig.buildConfig(
+ path: 'ci/builders/macos_test_config.json',
+ ),
+ },
+ help: true,
+ );
+
+ return parser;
+ }
+
+ test('shows only non-CI builds that can run locally', () {
+ final parser = createMultiPlatformArgParser(verbose: false);
+
+ expect(
+ parser.usage,
+ contains('[host_debug]'),
+ );
+ });
+
+ test('shows builds that can run locally, with details', () {
+ final parser = createMultiPlatformArgParser(verbose: true);
+
+ expect(
+ parser.usage,
+ stringContainsInOrder([
+ '[ci/linux_host_debug]',
+ 'A CI-suitable development build of the Linux host.',
+ '[host_debug]',
+ 'A development build of the Linux host.',
+ ]),
+ );
+ });
});
}
diff --git a/tools/engine_tool/test/commands/build_command_test.dart b/tools/engine_tool/test/commands/build_command_test.dart
index 3267ba8..243e75a 100644
--- a/tools/engine_tool/test/commands/build_command_test.dart
+++ b/tools/engine_tool/test/commands/build_command_test.dart
@@ -16,39 +16,6 @@
import '../src/utils.dart';
void main() {
- test('can find host runnable build', () async {
- final testEnv = TestEnvironment.withTestEngine(
- abi: Abi.macosArm64,
- );
- addTearDown(testEnv.cleanup);
-
- final builder = TestBuilderConfig();
- builder.addBuild(
- name: 'macos/host_debug',
- dimension: TestDroneDimension.mac,
- );
- builder.addBuild(
- name: 'mac/host_profile',
- dimension: TestDroneDimension.mac,
- );
- builder.addBuild(
- name: 'linux/host_debug',
- dimension: TestDroneDimension.linux,
- );
-
- final configs = {
- 'mac_test_config': builder.buildConfig(
- path: 'ci/builders/mac_test_config.json',
- ),
- };
-
- final result = runnableBuilds(testEnv.environment, configs, true);
- expect(
- result.map((r) => r.name),
- unorderedEquals(['macos/host_debug', 'mac/host_profile']),
- );
- });
-
test('build command invokes gn', () async {
final testEnv = TestEnvironment.withTestEngine(
abi: Abi.macosArm64,