| // 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:async'; |
| |
| import 'package:args/command_runner.dart'; |
| import 'package:path/path.dart' as path; |
| import 'package:watcher/watcher.dart'; |
| |
| import 'environment.dart'; |
| import 'exceptions.dart'; |
| import 'pipeline.dart'; |
| import 'utils.dart'; |
| |
| const Map<String, String> targetAliases = <String, String>{ |
| 'sdk': 'flutter/web_sdk', |
| 'web_sdk': 'flutter/web_sdk', |
| 'canvaskit': 'flutter/third_party/canvaskit:canvaskit_group', |
| 'canvaskit_chromium': 'flutter/third_party/canvaskit:canvaskit_chromium_group', |
| 'skwasm': 'flutter/third_party/canvaskit:skwasm_group', |
| 'archive': 'flutter/web_sdk:flutter_web_sdk_archive', |
| }; |
| |
| class BuildCommand extends Command<bool> with ArgUtils<bool> { |
| BuildCommand() { |
| argParser.addFlag( |
| 'watch', |
| abbr: 'w', |
| help: 'Run the build in watch mode so it rebuilds whenever a change is ' |
| 'made. Disabled by default.', |
| ); |
| argParser.addFlag( |
| 'host', |
| help: 'Build the host build instead of the wasm build, which is ' |
| 'currently needed for `flutter run --local-engine` to work.' |
| ); |
| argParser.addFlag( |
| 'profile', |
| help: 'Build in profile mode instead of release mode. In this mode, the ' |
| 'output will be located at "out/wasm_profile".\nThis only applies to ' |
| 'the wasm build. The host build is always built in release mode.', |
| ); |
| argParser.addFlag( |
| 'debug', |
| help: 'Build in debug mode instead of release mode. In this mode, the ' |
| 'output will be located at "out/wasm_debug".\nThis only applies to ' |
| 'the wasm build. The host build is always built in release mode.', |
| ); |
| argParser.addFlag( |
| 'dwarf', |
| help: 'Embed DWARF debugging info into the output wasm modules. This is ' |
| 'only valid in debug mode.', |
| ); |
| } |
| |
| @override |
| String get name => 'build'; |
| |
| @override |
| String get description => 'Build the Flutter web engine.'; |
| |
| bool get isWatchMode => boolArg('watch'); |
| |
| bool get host => boolArg('host'); |
| |
| List<String> get targets => argResults?.rest ?? <String>[]; |
| bool get embedDwarf => boolArg('dwarf'); |
| |
| @override |
| FutureOr<bool> run() async { |
| if (embedDwarf && runtimeMode != RuntimeMode.debug) { |
| throw ToolExit('Embedding DWARF data requires debug runtime mode.'); |
| } |
| final FilePath libPath = FilePath.fromWebUi('lib'); |
| final List<PipelineStep> steps = <PipelineStep>[ |
| GnPipelineStep( |
| host: host, |
| runtimeMode: runtimeMode, |
| embedDwarf: embedDwarf, |
| ), |
| NinjaPipelineStep( |
| host: host, |
| runtimeMode: runtimeMode, |
| targets: targets.map((String target) => targetAliases[target] ?? target), |
| ), |
| ]; |
| final Pipeline buildPipeline = Pipeline(steps: steps); |
| await buildPipeline.run(); |
| |
| if (isWatchMode) { |
| print('Initial build done!'); |
| print('Watching directory: ${libPath.relativeToCwd}/'); |
| await PipelineWatcher( |
| dir: libPath.absolute, |
| pipeline: buildPipeline, |
| // Ignore font files that are copied whenever tests run. |
| ignore: (WatchEvent event) => event.path.endsWith('.ttf'), |
| ).start(); |
| } |
| return true; |
| } |
| } |
| |
| /// Runs `gn`. |
| /// |
| /// Not safe to interrupt as it may leave the `out/` directory in a corrupted |
| /// state. GN is pretty quick though, so it's OK to not support interruption. |
| class GnPipelineStep extends ProcessStep { |
| GnPipelineStep({ |
| required this.host, |
| required this.runtimeMode, |
| required this.embedDwarf, |
| }); |
| |
| final bool host; |
| final RuntimeMode runtimeMode; |
| final bool embedDwarf; |
| |
| @override |
| String get description => 'gn'; |
| |
| @override |
| bool get isSafeToInterrupt => false; |
| |
| List<String> get _gnArgs { |
| if (host) { |
| return <String>[ |
| '--unoptimized', |
| '--full-dart-sdk', |
| ]; |
| } else { |
| return <String>[ |
| '--web', |
| '--runtime-mode=${runtimeMode.name}', |
| if (runtimeMode == RuntimeMode.debug) |
| '--unoptimized', |
| if (embedDwarf) |
| '--wasm-use-dwarf', |
| ]; |
| } |
| } |
| |
| @override |
| Future<ProcessManager> createProcess() { |
| print('Running gn...'); |
| return startProcess( |
| path.join(environment.flutterDirectory.path, 'tools', 'gn'), |
| _gnArgs, |
| ); |
| } |
| } |
| |
| /// Runs `autoninja`. |
| /// |
| /// Can be safely interrupted. |
| class NinjaPipelineStep extends ProcessStep { |
| NinjaPipelineStep({ |
| required this.host, |
| required this.runtimeMode, |
| required this.targets, |
| }); |
| |
| @override |
| String get description => 'ninja'; |
| |
| @override |
| bool get isSafeToInterrupt => true; |
| |
| final bool host; |
| final Iterable<String> targets; |
| final RuntimeMode runtimeMode; |
| |
| String get buildDirectory { |
| if (host) { |
| return environment.hostDebugUnoptDir.path; |
| } |
| return getBuildDirectoryForRuntimeMode(runtimeMode).path; |
| } |
| |
| @override |
| Future<ProcessManager> createProcess() { |
| print('Running autoninja...'); |
| return startProcess( |
| 'autoninja', |
| <String>[ |
| '-C', |
| buildDirectory, |
| ...targets, |
| ], |
| ); |
| } |
| } |