| // 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:ffi' as ffi; |
| 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:path/path.dart' as p; |
| import 'package:platform/platform.dart'; |
| import 'package:process_runner/process_runner.dart'; |
| |
| // This is an example of how to use the APIs of this library to parse and |
| // execute the build configurations json files under ci/builders. |
| // |
| // Usage: |
| // $ dart bin/run.dart [build config name] [build name] |
| // For example: |
| // $ dart bin/run.dart mac_unopt host_debug_unopt |
| // |
| // The build config names are the names of the json files under ci/builders |
| // The build names are the "name" fields of the maps in the list of "builds". |
| |
| void main(List<String> args) async { |
| final String? configName; |
| final String? buildName; |
| if (args.length >= 2) { |
| configName = args[0]; |
| buildName = args[1]; |
| } else { |
| io.stderr.writeln(r''' |
| Usage: |
| $ dart bin/run.dart [build config name] [build name] |
| |
| For example: |
| |
| $ dart bin/run.dart mac_unopt host_debug_unopt |
| |
| The build config names are the names of the json files under ci/builders. |
| The build names are the "name" fields of the maps in the list of "builds". |
| '''); |
| io.exitCode = 1; |
| return; |
| } |
| |
| // Find the engine repo. |
| final Engine engine; |
| try { |
| engine = Engine.findWithin(); |
| } catch (e) { |
| io.stderr.writeln(e); |
| io.exitCode = 1; |
| return; |
| } |
| |
| // Find and parse the engine build configs. |
| final io.Directory buildConfigsDir = io.Directory(p.join( |
| engine.flutterDir.path, |
| 'ci', |
| 'builders', |
| )); |
| final BuildConfigLoader loader = BuildConfigLoader( |
| buildConfigsDir: buildConfigsDir, |
| ); |
| |
| // Treat it as an error if no build configs were found. The caller likely |
| // expected to find some. |
| final Map<String, BuilderConfig> configs = loader.configs; |
| if (configs.isEmpty) { |
| io.stderr.writeln( |
| 'Error: No build configs found under ${buildConfigsDir.path}', |
| ); |
| io.exitCode = 1; |
| return; |
| } |
| if (loader.errors.isNotEmpty) { |
| loader.errors.forEach(io.stderr.writeln); |
| io.exitCode = 1; |
| } |
| |
| // Check the parsed build configs for validity. |
| final BuilderConfig? targetConfig = configs[configName]; |
| if (targetConfig == null) { |
| io.stderr.writeln('Build config "$configName" not found.'); |
| io.exitCode = 1; |
| return; |
| } |
| final List<String> buildConfigErrors = targetConfig.check(configName); |
| if (buildConfigErrors.isNotEmpty) { |
| io.stderr.writeln('Errors in "$configName":'); |
| for (final String error in buildConfigErrors) { |
| io.stderr.writeln(' $error'); |
| } |
| io.exitCode = 1; |
| return; |
| } |
| |
| Build? targetBuild; |
| for (int i = 0; i < targetConfig.builds.length; i++) { |
| final Build build = targetConfig.builds[i]; |
| if (build.name == buildName) { |
| targetBuild = build; |
| } |
| } |
| if (targetBuild == null) { |
| io.stderr.writeln( |
| 'Target build not found. No build called $buildName in $configName', |
| ); |
| io.exitCode = 1; |
| return; |
| } |
| |
| // If RBE config files aren't in the tree, then disable RBE. |
| final String rbeConfigPath = p.join( |
| engine.srcDir.path, |
| 'flutter', |
| 'build', |
| 'rbe', |
| ); |
| final List<String> extraGnArgs = <String>[ |
| if (!io.Directory(rbeConfigPath).existsSync()) '--no-rbe', |
| ]; |
| |
| final BuildRunner buildRunner = BuildRunner( |
| platform: const LocalPlatform(), |
| processRunner: ProcessRunner(), |
| abi: ffi.Abi.current(), |
| engineSrcDir: engine.srcDir, |
| build: targetBuild, |
| extraGnArgs: extraGnArgs, |
| runGenerators: false, |
| runTests: false, |
| ); |
| void handler(RunnerEvent event) { |
| switch (event) { |
| case RunnerStart(): |
| io.stdout.writeln('$event: ${event.command.join(' ')}'); |
| case RunnerProgress(done: true): |
| io.stdout.writeln(event); |
| case RunnerProgress(done: false): |
| { |
| final int width = io.stdout.terminalColumns; |
| final String percent = '${event.percent.toStringAsFixed(1)}%'; |
| final String fraction = '(${event.completed}/${event.total})'; |
| final String prefix = '[${event.name}] $percent $fraction '; |
| final int remainingSpace = width - prefix.length; |
| final String what; |
| if (remainingSpace >= event.what.length) { |
| what = event.what; |
| } else { |
| what = event.what.substring(event.what.length - remainingSpace + 1); |
| } |
| final String spaces = ' ' * width; |
| io.stdout.write('$spaces\r'); // Erase the old line. |
| io.stdout.write('$prefix$what\r'); // Print the new line. |
| } |
| default: |
| io.stdout.writeln(event); |
| } |
| } |
| |
| final bool buildResult = await buildRunner.run(handler); |
| io.exitCode = buildResult ? 0 : 1; |
| } |