blob: 432682e6e27e3c6c7f673c5150c084c2151d3dde [file] [log] [blame]
Adam Barthbdd20662015-10-11 19:42:50 -07001// Copyright 2015 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'dart:async';
Devon Carew494d1e02015-10-28 20:57:51 -07006import 'dart:io';
Adam Barthbdd20662015-10-11 19:42:50 -07007
8import 'package:args/command_runner.dart';
9
10import '../application_package.dart';
Devon Carew2dbceaf2016-02-06 19:19:50 -080011import '../build_configuration.dart';
Adam Barth2e062df2016-03-28 13:07:42 -070012import '../dart/pub.dart';
Adam Barthbdd20662015-10-11 19:42:50 -070013import '../device.dart';
Devon Carew5ad6a572016-03-11 08:49:55 -080014import '../flx.dart' as flx;
Devon Carewd5a6fce2016-02-17 00:59:56 -080015import '../globals.dart';
Adam Barth2e062df2016-03-28 13:07:42 -070016import '../package_map.dart';
Adam Barth12f75812015-10-13 10:00:06 -070017import '../toolchain.dart';
Adam Barthbdd20662015-10-11 19:42:50 -070018import 'flutter_command_runner.dart';
19
Collin Jacksonbeaffec2016-01-29 12:54:15 -080020typedef bool Validator();
21
Adam Barthbdd20662015-10-11 19:42:50 -070022abstract class FlutterCommand extends Command {
Devon Carewf8374cd2016-03-16 14:37:46 -070023 FlutterCommand() {
24 commandValidator = _commandValidator;
25 }
26
Hixie797e27e2016-03-14 13:31:43 -070027 @override
Adam Barthbdd20662015-10-11 19:42:50 -070028 FlutterCommandRunner get runner => super.runner;
29
Devon Carew494d1e02015-10-28 20:57:51 -070030 /// Whether this command needs to be run from the root of a project.
Steve Messick913315b2016-03-07 05:58:02 -080031 bool get requiresProjectRoot => true;
Devon Carew494d1e02015-10-28 20:57:51 -070032
Devon Carew37290d82016-02-24 10:06:59 -080033 /// Whether this command requires a (single) Flutter target device to be connected.
34 bool get requiresDevice => false;
35
36 /// Whether this command only applies to Android devices.
37 bool get androidOnly => false;
38
Devon Carewf8374cd2016-03-16 14:37:46 -070039 /// Whether this command uses the 'target' option.
40 bool _usesTargetOption = false;
41
42 bool _usesPubOption = false;
Steve Messick913315b2016-03-07 05:58:02 -080043
Devon Carewb172dd52016-04-13 19:55:28 -070044 bool get shouldRunPub => _usesPubOption && argResults['pub'];
45
Ian Hickson34190682015-11-09 00:30:45 -080046 List<BuildConfiguration> get buildConfigurations => runner.buildConfigurations;
47
Devon Carewf8374cd2016-03-16 14:37:46 -070048 void usesTargetOption() {
49 argParser.addOption('target',
50 abbr: 't',
51 defaultsTo: flx.defaultMainPath,
52 help: 'Target app path / main entry-point file.');
53 _usesTargetOption = true;
54 }
55
56 void usesPubOption() {
57 argParser.addFlag('pub',
58 defaultsTo: true,
59 help: 'Whether to run "pub get" before executing this command.');
60 _usesPubOption = true;
61 }
62
Devon Carew1c0a9662016-04-18 16:41:15 -070063 void addBuildModeFlags() {
64 argParser.addFlag('debug',
65 negatable: false,
66 help: 'Build a debug version of your app (the default).');
Jason Simmons07951ee2016-04-25 13:36:31 -070067 argParser.addFlag('profile',
68 hide: true,
69 negatable: false,
70 help: 'Build a profile (ahead of time compilation) version of your app.');
Devon Carew6f0bb202016-04-19 11:17:00 -070071 argParser.addFlag('release',
Devon Carew1c0a9662016-04-18 16:41:15 -070072 negatable: false,
Devon Carew6f0bb202016-04-19 11:17:00 -070073 help: 'Build a release version of your app.');
Devon Carew1c0a9662016-04-18 16:41:15 -070074 }
75
76 BuildMode getBuildMode() {
Jason Simmons07951ee2016-04-25 13:36:31 -070077 List<bool> modeFlags = [argResults['debug'], argResults['profile'], argResults['release']];
78 if (modeFlags.where((bool flag) => flag).length > 1)
79 throw new UsageException('Only one of --debug, --profile, or --release should be specified.', null);
Devon Carew1c0a9662016-04-18 16:41:15 -070080
81 BuildMode mode = BuildMode.debug;
Jason Simmons07951ee2016-04-25 13:36:31 -070082 if (argResults['profile'])
83 mode = BuildMode.profile;
Devon Carew6f0bb202016-04-19 11:17:00 -070084 if (argResults['release'])
85 mode = BuildMode.release;
Devon Carew1c0a9662016-04-18 16:41:15 -070086 return mode;
87 }
88
Devon Carew653566d2016-04-04 13:10:56 -070089 void _setupToolchain() {
90 toolchain ??= Toolchain.forConfigs(buildConfigurations);
Adam Barthbdd20662015-10-11 19:42:50 -070091 }
92
Devon Carew653566d2016-04-04 13:10:56 -070093 void _setupApplicationPackages() {
Devon Carewf132aca2016-04-15 21:08:03 -070094 applicationPackages ??= new ApplicationPackageStore();
Devon Carew37290d82016-02-24 10:06:59 -080095 }
96
Hixie797e27e2016-03-14 13:31:43 -070097 @override
Devon Carew4d93c372016-02-26 23:54:18 -080098 Future<int> run() {
99 Stopwatch stopwatch = new Stopwatch()..start();
100
101 return _run().then((int exitCode) {
Devon Carew4daee0c2016-03-14 09:41:00 -0700102 int ms = stopwatch.elapsedMilliseconds;
103 printTrace("'flutter $name' took ${ms}ms; exiting with code $exitCode.");
Devon Carew4d93c372016-02-26 23:54:18 -0800104 return exitCode;
105 });
106 }
107
108 Future<int> _run() async {
Devon Carewf8374cd2016-03-16 14:37:46 -0700109 if (requiresProjectRoot && !commandValidator())
Adam Barthc7e00442015-11-23 13:31:24 -0800110 return 1;
Devon Carew37290d82016-02-24 10:06:59 -0800111
112 // Ensure at least one toolchain is installed.
113 if (requiresDevice && !doctor.canLaunchAnything) {
114 printError("Unable to locate a development device; please run 'flutter doctor' "
115 "for information about installing additional components.");
116 return 1;
117 }
118
119 // Validate devices.
120 if (requiresDevice) {
121 List<Device> devices = await deviceManager.getDevices();
122
123 if (devices.isEmpty && deviceManager.hasSpecifiedDeviceId) {
124 printError("No device found with id '${deviceManager.specifiedDeviceId}'.");
125 return 1;
126 } else if (devices.isEmpty) {
127 printStatus('No connected devices.');
128 return 1;
129 }
130
131 devices = devices.where((Device device) => device.isSupported()).toList();
132
133 if (androidOnly)
Devon Carewe939b152016-03-12 11:08:21 -0800134 devices = devices.where((Device device) => device.platform == TargetPlatform.android_arm).toList();
Devon Carew37290d82016-02-24 10:06:59 -0800135
Devon Carew37290d82016-02-24 10:06:59 -0800136 if (devices.isEmpty) {
137 printStatus('No supported devices connected.');
138 return 1;
Devon Carew5ad6a572016-03-11 08:49:55 -0800139 } else if (devices.length > 1) {
140 printStatus("More than one device connected; please specify a device with "
141 "the '-d <deviceId>' flag.");
142 printStatus('');
143 devices = await deviceManager.getAllConnectedDevices();
Devon Carew4daee0c2016-03-14 09:41:00 -0700144 Device.printDevices(devices);
Devon Carew5ad6a572016-03-11 08:49:55 -0800145 return 1;
146 } else {
147 _deviceForCommand = devices.single;
Devon Carew37290d82016-02-24 10:06:59 -0800148 }
Devon Carew37290d82016-02-24 10:06:59 -0800149 }
150
Devon Carewb172dd52016-04-13 19:55:28 -0700151 if (shouldRunPub) {
Devon Carewf8374cd2016-03-16 14:37:46 -0700152 int exitCode = await pubGet();
153 if (exitCode != 0)
154 return exitCode;
155 }
156
Devon Carew40598442016-04-07 09:15:58 -0700157 // Populate the cache.
Devon Carewa729b022016-04-07 13:31:44 -0700158 await cache.updateAll();
Devon Carew40598442016-04-07 09:15:58 -0700159
Devon Carew653566d2016-04-04 13:10:56 -0700160 _setupToolchain();
161 _setupApplicationPackages();
162
Hixiea0227ca2015-11-11 10:29:05 -0800163 return await runInProject();
Devon Carew494d1e02015-10-28 20:57:51 -0700164 }
165
Collin Jacksonbeaffec2016-01-29 12:54:15 -0800166 // This is a field so that you can modify the value for testing.
Devon Carewf8374cd2016-03-16 14:37:46 -0700167 Validator commandValidator;
168
169 bool _commandValidator() {
Adam Barthc7e00442015-11-23 13:31:24 -0800170 if (!FileSystemEntity.isFileSync('pubspec.yaml')) {
Collin Jacksonbeaffec2016-01-29 12:54:15 -0800171 printError('Error: No pubspec.yaml file found.\n'
172 'This command should be run from the root of your Flutter project.\n'
173 'Do not run this command from the root of your git clone of Flutter.');
Adam Barthc7e00442015-11-23 13:31:24 -0800174 return false;
175 }
Devon Carewf8374cd2016-03-16 14:37:46 -0700176
177 if (_usesTargetOption) {
178 String targetPath = argResults['target'];
179 if (!FileSystemEntity.isFileSync(targetPath)) {
180 printError('Target file "$targetPath" not found.');
181 return false;
182 }
183 }
184
Jason Simmonsf7b17992016-04-08 09:34:42 -0700185 // Validate the current package map only if we will not be running "pub get" later.
186 if (!(_usesPubOption && argResults['pub'])) {
187 String error = PackageMap.instance.checkValid();
188 if (error != null) {
189 printError(error);
190 return false;
191 }
Adam Barth2e062df2016-03-28 13:07:42 -0700192 }
193
Adam Barthc7e00442015-11-23 13:31:24 -0800194 return true;
Devon Carewf8374cd2016-03-16 14:37:46 -0700195 }
Adam Barthc7e00442015-11-23 13:31:24 -0800196
Devon Carew494d1e02015-10-28 20:57:51 -0700197 Future<int> runInProject();
198
Devon Carew37290d82016-02-24 10:06:59 -0800199 // This is caculated in run() if the command has [requiresDevice] specified.
Devon Carew5ad6a572016-03-11 08:49:55 -0800200 Device _deviceForCommand;
201
202 Device get deviceForCommand => _deviceForCommand;
Devon Carew37290d82016-02-24 10:06:59 -0800203
Adam Barthbdd20662015-10-11 19:42:50 -0700204 ApplicationPackageStore applicationPackages;
Adam Barth12f75812015-10-13 10:00:06 -0700205 Toolchain toolchain;
Adam Barthbdd20662015-10-11 19:42:50 -0700206}