blob: 0a62819419206b367eccf7fa81278034e72a396a [file] [log] [blame]
// 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:io' show ProcessStartMode;
import 'package:engine_build_configs/engine_build_configs.dart';
import 'package:process_runner/process_runner.dart';
import '../build_utils.dart';
import '../run_utils.dart';
import 'command.dart';
import 'flags.dart';
/// The root 'run' command.
final class RunCommand extends CommandBase {
/// Constructs the 'run' command.
RunCommand({
required super.environment,
required Map<String, BuilderConfig> configs,
}) {
builds = runnableBuilds(environment, configs);
debugCheckBuilds(builds);
argParser.addOption(
configFlag,
abbr: 'c',
defaultsTo: '',
help:
'Specify the build config to use for the target build (usually auto detected)',
allowed: <String>[
for (final Build build in runnableBuilds(environment, configs))
build.name,
],
allowedHelp: <String, String>{
for (final Build build in runnableBuilds(environment, configs))
build.name: build.gn.join(' '),
},
);
}
/// List of compatible builds.
late final List<Build> builds;
@override
String get name => 'run';
@override
String get description => 'Run a flutter app with a local engine build'
'All arguments after -- are forwarded to flutter run, e.g.: '
'et run -- --profile'
'et run -- -d macos'
'See `flutter run --help` for a listing';
Build? _lookup(String configName) {
return builds.where((Build build) => build.name == configName).firstOrNull;
}
Build? _findHostBuild(Build? targetBuild) {
if (targetBuild == null) {
return null;
}
final String name = targetBuild.name;
if (name.startsWith('host_')) {
return targetBuild;
}
// TODO(johnmccutchan): This is brittle, it would be better if we encoded
// the host config name in the target config.
if (name.contains('_debug')) {
return _lookup('host_debug');
} else if (name.contains('_profile')) {
return _lookup('host_profile');
} else if (name.contains('_release')) {
return _lookup('host_release');
}
return null;
}
String _getDeviceId() {
if (argResults!.rest.contains('-d')) {
final int index = argResults!.rest.indexOf('-d') + 1;
if (index < argResults!.rest.length) {
return argResults!.rest[index];
}
}
if (argResults!.rest.contains('--device-id')) {
final int index = argResults!.rest.indexOf('--device-id') + 1;
if (index < argResults!.rest.length) {
return argResults!.rest[index];
}
}
return '';
}
String _getMode() {
// Sniff the build mode from the args that will be passed to flutter run.
String mode = 'debug';
if (argResults!.rest.contains('--profile')) {
mode = 'profile';
} else if (argResults!.rest.contains('--release')) {
mode = 'release';
}
return mode;
}
Future<String?> _selectTargetConfig() async {
final String configName = argResults![configFlag] as String;
if (configName.isNotEmpty) {
return configName;
}
final String deviceId = _getDeviceId();
final RunTarget? target =
await detectAndSelectRunTarget(environment, deviceId);
if (target == null) {
return 'host_debug';
}
environment.logger.status(
'Building to run on "${target.name}" running ${target.targetPlatform}');
return target.buildConfigFor(_getMode());
}
@override
Future<int> run() async {
if (!environment.processRunner.processManager.canRun('flutter')) {
environment.logger.error('Cannot find flutter command in your path');
return 1;
}
final String? configName = await _selectTargetConfig();
if (configName == null) {
environment.logger.error('Could not find target config');
return 1;
}
final Build? build = _lookup(configName);
final Build? hostBuild = _findHostBuild(build);
if (build == null) {
environment.logger.error('Could not find build $configName');
return 1;
}
if (hostBuild == null) {
environment.logger.error('Could not find host build for $configName');
return 1;
}
// First build the host.
int r = await runBuild(environment, hostBuild);
if (r != 0) {
return r;
}
// Now build the target if it isn't the same.
if (hostBuild.name != build.name) {
r = await runBuild(environment, build);
if (r != 0) {
return r;
}
}
// TODO(johnmccutchan): Be smart and if the user requested a profile
// config, add the '--profile' flag when invoking flutter run.
final ProcessRunnerResult result =
await environment.processRunner.runProcess(
<String>[
'flutter',
'run',
'--local-engine-src-path',
environment.engine.srcDir.path,
'--local-engine',
build.name,
'--local-engine-host',
hostBuild.name,
...argResults!.rest,
],
runInShell: true,
startMode: ProcessStartMode.inheritStdio,
);
return result.exitCode;
}
}