-
Notifications
You must be signed in to change notification settings - Fork 6k
Add an initial 'build' command to engine_tool #50681
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| // Copyright 2013 The Flutter Authors. All rights reserved. | ||
| // Use of this source code is governed by a BSD-style license that can be | ||
| // found in the LICENSE file. | ||
|
|
||
| import 'package:engine_build_configs/engine_build_configs.dart'; | ||
|
|
||
| import 'environment.dart'; | ||
|
|
||
| /// A function that returns true or false when given a [BuildConfig] and its | ||
| /// name. | ||
| typedef ConfigFilter = bool Function(String name, BuildConfig config); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Personally (here and below) I'd just inline the function definition. That avoids having to explain these generically "returns true or false" and you can just document how it's used in the (single?) function below. |
||
|
|
||
| /// A function that returns true or false when given a [BuildConfig] name | ||
| /// and a [GlobalBuild]. | ||
| typedef BuildFilter = bool Function(String configName, GlobalBuild build); | ||
|
|
||
| /// Returns a filtered copy of [input] filtering out configs where test | ||
| /// returns false. | ||
| Map<String, BuildConfig> filterBuildConfigs( | ||
| Map<String, BuildConfig> input, ConfigFilter test) { | ||
| return <String, BuildConfig>{ | ||
| for (final MapEntry<String, BuildConfig> 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, BuildConfig> runnableBuildConfigs( | ||
| Environment env, Map<String, BuildConfig> input) { | ||
| return filterBuildConfigs(input, (String name, BuildConfig config) { | ||
| return config.canRunOn(env.platform); | ||
| }); | ||
| } | ||
|
|
||
| /// Returns a List of [GlobalBuild] that match test. | ||
| List<GlobalBuild> filterBuilds( | ||
| Map<String, BuildConfig> input, BuildFilter test) { | ||
| return <GlobalBuild>[ | ||
| for (final MapEntry<String, BuildConfig> entry in input.entries) | ||
| for (final GlobalBuild build in entry.value.builds) | ||
| if (test(entry.key, build)) build, | ||
| ]; | ||
| } | ||
|
|
||
| /// Returns a list of runnable builds. | ||
| List<GlobalBuild> runnableBuilds( | ||
| Environment env, Map<String, BuildConfig> input) { | ||
| return filterBuilds(input, (String configName, GlobalBuild build) { | ||
| return build.canRunOn(env.platform); | ||
| }); | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,92 @@ | ||||||||||||||||||||||||||||||||||||||||||
| // Copyright 2013 The Flutter Authors. All rights reserved. | ||||||||||||||||||||||||||||||||||||||||||
| // Use of this source code is governed by a BSD-style license that can be | ||||||||||||||||||||||||||||||||||||||||||
| // found in the LICENSE file. | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| import 'package:engine_build_configs/engine_build_configs.dart'; | ||||||||||||||||||||||||||||||||||||||||||
johnmccutchan marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| import '../build_utils.dart'; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| import 'command.dart'; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| const String _configFlag = 'config'; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| // TODO(johnmccutchan): Should BuildConfig be BuilderConfig and GlobalBuild be BuildConfig? | ||||||||||||||||||||||||||||||||||||||||||
| // TODO(johnmccutchan): List all available build targets and allow the user | ||||||||||||||||||||||||||||||||||||||||||
| // to specify which one(s) we should build on the cli. | ||||||||||||||||||||||||||||||||||||||||||
| // TODO(johnmccutchan): Can we show a progress indicator like 'running gn...'? | ||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I think we can copy some code from flutter_tool for this. |
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| /// The root 'build' command. | ||||||||||||||||||||||||||||||||||||||||||
| final class BuildCommand extends CommandBase { | ||||||||||||||||||||||||||||||||||||||||||
| /// Constructs the 'build' command. | ||||||||||||||||||||||||||||||||||||||||||
| BuildCommand({ | ||||||||||||||||||||||||||||||||||||||||||
| required super.environment, | ||||||||||||||||||||||||||||||||||||||||||
| required Map<String, BuildConfig> configs, | ||||||||||||||||||||||||||||||||||||||||||
| }) { | ||||||||||||||||||||||||||||||||||||||||||
| builds = runnableBuilds(environment, configs); | ||||||||||||||||||||||||||||||||||||||||||
| // Add options here that are common to all queries. | ||||||||||||||||||||||||||||||||||||||||||
| argParser.addOption( | ||||||||||||||||||||||||||||||||||||||||||
| _configFlag, | ||||||||||||||||||||||||||||||||||||||||||
| abbr: 'c', | ||||||||||||||||||||||||||||||||||||||||||
| defaultsTo: 'host_debug', | ||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Definitely a TODO (unless you feel inspired) but I have the perfect default for you here: https://github.com/flutter/engine/tree/main/tools/pkg/engine_repo_tools Specifically: engine/tools/pkg/engine_repo_tools/lib/engine_repo_tools.dart Lines 156 to 169 in df0dc1f
You can see a recent example of this being used here (not exactly the same, but close): engine/testing/scenario_app/bin/android_integration_tests.dart Lines 44 to 49 in df0dc1f
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Personally I'd rather have a deterministic default. For Crank, you can set your default builder in a This config file also lets you add your own custom builders, which is useful if the CI builders don't support your desired scenario like Android from a macOS host. See: https://github.com/loic-sharma/crank#custom-builders
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hardcoding a default for now sgtm. I suspect we'll want a config file with defaults at some point, but that's big enough for a separate PR. |
||||||||||||||||||||||||||||||||||||||||||
| help: 'Specify the build config to use', | ||||||||||||||||||||||||||||||||||||||||||
| allowed: <String>[ | ||||||||||||||||||||||||||||||||||||||||||
| for (final GlobalBuild config in runnableBuilds(environment, configs)) | ||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
| config.name, | ||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||
| allowedHelp: <String, String>{ | ||||||||||||||||||||||||||||||||||||||||||
| for (final GlobalBuild config in runnableBuilds(environment, configs)) | ||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
| config.name: config.gn.join(' '), | ||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| /// List of compatible builds. | ||||||||||||||||||||||||||||||||||||||||||
| late final List<GlobalBuild> builds; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||
| String get name => 'build'; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||
| String get description => 'Builds the engine'; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||
| Future<int> run() async { | ||||||||||||||||||||||||||||||||||||||||||
| final String configName = argResults![_configFlag] as String; | ||||||||||||||||||||||||||||||||||||||||||
| final GlobalBuild? build = builds | ||||||||||||||||||||||||||||||||||||||||||
| .where((GlobalBuild build) => build.name == configName) | ||||||||||||||||||||||||||||||||||||||||||
| .firstOrNull; | ||||||||||||||||||||||||||||||||||||||||||
| if (build == null) { | ||||||||||||||||||||||||||||||||||||||||||
| environment.logger.error('Could not find config $configName'); | ||||||||||||||||||||||||||||||||||||||||||
| return 1; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| final GlobalBuildRunner buildRunner = GlobalBuildRunner( | ||||||||||||||||||||||||||||||||||||||||||
| platform: environment.platform, | ||||||||||||||||||||||||||||||||||||||||||
| processRunner: environment.processRunner, | ||||||||||||||||||||||||||||||||||||||||||
| abi: environment.abi, | ||||||||||||||||||||||||||||||||||||||||||
| engineSrcDir: environment.engine.srcDir, | ||||||||||||||||||||||||||||||||||||||||||
| build: build); | ||||||||||||||||||||||||||||||||||||||||||
| void handler(RunnerEvent event) { | ||||||||||||||||||||||||||||||||||||||||||
| switch (event) { | ||||||||||||||||||||||||||||||||||||||||||
| case RunnerStart(): | ||||||||||||||||||||||||||||||||||||||||||
| environment.logger.info('$event: ${event.command.join(' ')}'); | ||||||||||||||||||||||||||||||||||||||||||
| case RunnerProgress(done: true): | ||||||||||||||||||||||||||||||||||||||||||
| environment.logger.clearLine(); | ||||||||||||||||||||||||||||||||||||||||||
| environment.logger.status(event); | ||||||||||||||||||||||||||||||||||||||||||
| case RunnerProgress(done: false): | ||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||
| final String percent = '${event.percent.toStringAsFixed(1)}%'; | ||||||||||||||||||||||||||||||||||||||||||
| final String fraction = '(${event.completed}/${event.total})'; | ||||||||||||||||||||||||||||||||||||||||||
| final String prefix = '[${event.name}] $percent $fraction '; | ||||||||||||||||||||||||||||||||||||||||||
| final String what = event.what; | ||||||||||||||||||||||||||||||||||||||||||
| environment.logger.clearLine(); | ||||||||||||||||||||||||||||||||||||||||||
| environment.logger.status('$prefix$what'); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| default: | ||||||||||||||||||||||||||||||||||||||||||
| environment.logger.status(event); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| final bool buildResult = await buildRunner.run(handler); | ||||||||||||||||||||||||||||||||||||||||||
| return buildResult ? 0 : 1; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| // Copyright 2013 The Flutter Authors. 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:convert' as convert; | ||
| import 'dart:ffi' as ffi show Abi; | ||
| import 'dart:io' as io; | ||
|
|
||
| import 'package:engine_build_configs/engine_build_configs.dart'; | ||
| import 'package:engine_repo_tools/engine_repo_tools.dart'; | ||
| import 'package:engine_tool/src/build_utils.dart'; | ||
| import 'package:engine_tool/src/commands/command_runner.dart'; | ||
| import 'package:engine_tool/src/environment.dart'; | ||
| import 'package:engine_tool/src/logger.dart'; | ||
| import 'package:litetest/litetest.dart'; | ||
| import 'package:platform/platform.dart'; | ||
| import 'package:process_fakes/process_fakes.dart'; | ||
| import 'package:process_runner/process_runner.dart'; | ||
|
|
||
| import 'fixtures.dart' as fixtures; | ||
|
|
||
| void main() { | ||
| final Engine engine; | ||
| try { | ||
| engine = Engine.findWithin(); | ||
| } catch (e) { | ||
| io.stderr.writeln(e); | ||
| io.exitCode = 1; | ||
| return; | ||
| } | ||
|
|
||
| final BuildConfig linuxTestConfig = BuildConfig.fromJson( | ||
| path: 'ci/builders/linux_test_config.json', | ||
| map: convert.jsonDecode(fixtures.testConfig('Linux')) | ||
| as Map<String, Object?>, | ||
| ); | ||
|
|
||
| final BuildConfig macTestConfig = BuildConfig.fromJson( | ||
| path: 'ci/builders/mac_test_config.json', | ||
| map: convert.jsonDecode(fixtures.testConfig('Mac-12')) | ||
| as Map<String, Object?>, | ||
| ); | ||
|
|
||
| final BuildConfig winTestConfig = BuildConfig.fromJson( | ||
| path: 'ci/builders/win_test_config.json', | ||
| map: convert.jsonDecode(fixtures.testConfig('Windows-11')) | ||
| as Map<String, Object?>, | ||
| ); | ||
|
|
||
| final Map<String, BuildConfig> configs = <String, BuildConfig>{ | ||
| 'linux_test_config': linuxTestConfig, | ||
| 'linux_test_config2': linuxTestConfig, | ||
| 'mac_test_config': macTestConfig, | ||
| 'win_test_config': winTestConfig, | ||
| }; | ||
|
|
||
| (Environment, List<List<String>>) linuxEnv(Logger logger) { | ||
| final List<List<String>> runHistory = <List<String>>[]; | ||
| return ( | ||
| Environment( | ||
| abi: ffi.Abi.linuxX64, | ||
| engine: engine, | ||
| platform: FakePlatform(operatingSystem: Platform.linux), | ||
| processRunner: ProcessRunner( | ||
| processManager: FakeProcessManager(onStart: (List<String> command) { | ||
| runHistory.add(command); | ||
| return FakeProcess(); | ||
| }, onRun: (List<String> command) { | ||
| runHistory.add(command); | ||
| return io.ProcessResult(81, 0, '', ''); | ||
| }), | ||
| ), | ||
| logger: logger, | ||
| ), | ||
| runHistory | ||
| ); | ||
| } | ||
|
|
||
| test('can find host runnable build', () async { | ||
| final Logger logger = Logger.test(); | ||
| final (Environment env, _) = linuxEnv(logger); | ||
| final List<GlobalBuild> result = runnableBuilds(env, configs); | ||
| expect(result.length, equals(2)); | ||
| expect(result[0].name, equals('build_name')); | ||
| }); | ||
|
|
||
| test('build command invokes gn', () async { | ||
| final Logger logger = Logger.test(); | ||
| final (Environment env, List<List<String>> runHistory) = linuxEnv(logger); | ||
| final ToolCommandRunner runner = ToolCommandRunner( | ||
| environment: env, | ||
| configs: configs, | ||
| ); | ||
| final int result = await runner.run(<String>[ | ||
| 'build', | ||
| '--config', | ||
| 'build_name', | ||
| ]); | ||
| expect(result, equals(0)); | ||
| expect(runHistory.length, greaterThanOrEqualTo(1)); | ||
| expect(runHistory[0].length, greaterThanOrEqualTo(1)); | ||
| expect(runHistory[0][0], contains('gn')); | ||
| }); | ||
|
|
||
| test('build command invokes ninja', () async { | ||
| final Logger logger = Logger.test(); | ||
| final (Environment env, List<List<String>> runHistory) = linuxEnv(logger); | ||
| final ToolCommandRunner runner = ToolCommandRunner( | ||
| environment: env, | ||
| configs: configs, | ||
| ); | ||
| final int result = await runner.run(<String>[ | ||
| 'build', | ||
| '--config', | ||
| 'build_name', | ||
| ]); | ||
| expect(result, equals(0)); | ||
| expect(runHistory.length, greaterThanOrEqualTo(2)); | ||
| expect(runHistory[1].length, greaterThanOrEqualTo(1)); | ||
| expect(runHistory[1][0], contains('ninja')); | ||
| }); | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.