Merge pull request #2747 from devoncarew/refactor_build
Refactor the build commands
This commit is contained in:
commit
18c8f57739
@ -12,7 +12,6 @@ import 'src/base/context.dart';
|
||||
import 'src/base/logger.dart';
|
||||
import 'src/base/process.dart';
|
||||
import 'src/commands/analyze.dart';
|
||||
import 'src/commands/apk.dart';
|
||||
import 'src/commands/build.dart';
|
||||
import 'src/commands/create.dart';
|
||||
import 'src/commands/daemon.dart';
|
||||
@ -50,7 +49,6 @@ Future<Null> main(List<String> args) async {
|
||||
|
||||
FlutterCommandRunner runner = new FlutterCommandRunner(verboseHelp: verboseHelp)
|
||||
..addCommand(new AnalyzeCommand())
|
||||
..addCommand(new ApkCommand())
|
||||
..addCommand(new BuildCommand())
|
||||
..addCommand(new CreateCommand())
|
||||
..addCommand(new DaemonCommand(hidden: !verboseHelp))
|
||||
|
@ -1,77 +1,53 @@
|
||||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
// Copyright 2016 The Chromium 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 'dart:io';
|
||||
|
||||
import '../flx.dart';
|
||||
import '../dart/pub.dart';
|
||||
import '../globals.dart';
|
||||
import '../runner/flutter_command.dart';
|
||||
import '../toolchain.dart';
|
||||
import 'build_apk.dart';
|
||||
import 'build_flx.dart';
|
||||
|
||||
class BuildCommand extends FlutterCommand {
|
||||
BuildCommand() {
|
||||
addSubcommand(new BuildApkCommand());
|
||||
addSubcommand(new BuildCleanCommand());
|
||||
addSubcommand(new BuildFlxCommand());
|
||||
}
|
||||
|
||||
@override
|
||||
final String name = 'build';
|
||||
|
||||
@override
|
||||
final String description = 'Package your Flutter app into an FLX.';
|
||||
|
||||
BuildCommand() {
|
||||
argParser.addFlag('precompiled', negatable: false);
|
||||
// This option is still referenced by the iOS build scripts. We should
|
||||
// remove it once we've updated those build scripts.
|
||||
argParser.addOption('asset-base', help: 'Ignored. Will be removed.', hide: true);
|
||||
argParser.addOption('compiler');
|
||||
argParser.addOption('manifest', defaultsTo: defaultManifestPath);
|
||||
argParser.addOption('private-key', defaultsTo: defaultPrivateKeyPath);
|
||||
argParser.addOption('output-file', abbr: 'o', defaultsTo: defaultFlxOutputPath);
|
||||
argParser.addOption('snapshot', defaultsTo: defaultSnapshotPath);
|
||||
argParser.addOption('depfile', defaultsTo: defaultDepfilePath);
|
||||
argParser.addOption('working-dir', defaultsTo: defaultWorkingDirPath);
|
||||
argParser.addFlag('pub',
|
||||
defaultsTo: true,
|
||||
help: 'Whether to run "pub get" before building the app.');
|
||||
addTargetOption();
|
||||
}
|
||||
final String description = 'Flutter build commands.';
|
||||
|
||||
@override
|
||||
Future<int> run() async {
|
||||
if (argResults['pub']) {
|
||||
int exitCode = await pubGet();
|
||||
if (exitCode != 0)
|
||||
return exitCode;
|
||||
}
|
||||
return await super.run();
|
||||
}
|
||||
Future<int> runInProject() => new Future<int>.value(0);
|
||||
}
|
||||
|
||||
class BuildCleanCommand extends FlutterCommand {
|
||||
@override
|
||||
final String name = 'clean';
|
||||
|
||||
@override
|
||||
final String description = 'Delete the build/ directory.';
|
||||
|
||||
@override
|
||||
Future<int> runInProject() async {
|
||||
String compilerPath = argResults['compiler'];
|
||||
Directory buildDir = new Directory('build');
|
||||
printStatus("Deleting '${buildDir.path}${Platform.pathSeparator}'.");
|
||||
|
||||
if (compilerPath == null)
|
||||
await downloadToolchain();
|
||||
else
|
||||
toolchain = new Toolchain(compiler: new Compiler(compilerPath));
|
||||
if (!buildDir.existsSync())
|
||||
return 0;
|
||||
|
||||
String outputPath = argResults['output-file'];
|
||||
|
||||
return await build(
|
||||
toolchain,
|
||||
mainPath: argResults['target'],
|
||||
manifestPath: argResults['manifest'],
|
||||
outputPath: outputPath,
|
||||
snapshotPath: argResults['snapshot'],
|
||||
depfilePath: argResults['depfile'],
|
||||
privateKeyPath: argResults['private-key'],
|
||||
workingDirPath: argResults['working-dir'],
|
||||
precompiledSnapshot: argResults['precompiled']
|
||||
).then((int result) {
|
||||
if (result == 0)
|
||||
printStatus('Built $outputPath.');
|
||||
else
|
||||
printError('Error building $outputPath: $result.');
|
||||
return result;
|
||||
});
|
||||
try {
|
||||
buildDir.deleteSync(recursive: true);
|
||||
return 0;
|
||||
} catch (error) {
|
||||
printError(error.toString());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -127,22 +127,19 @@ class _ApkComponents {
|
||||
}
|
||||
|
||||
class ApkKeystoreInfo {
|
||||
ApkKeystoreInfo({ this.keystore, this.password, this.keyAlias, this.keyPassword });
|
||||
ApkKeystoreInfo({ this.keystore, this.password, this.keyAlias, this.keyPassword }) {
|
||||
assert(keystore != null);
|
||||
}
|
||||
|
||||
String keystore;
|
||||
String password;
|
||||
String keyAlias;
|
||||
String keyPassword;
|
||||
final String keystore;
|
||||
final String password;
|
||||
final String keyAlias;
|
||||
final String keyPassword;
|
||||
}
|
||||
|
||||
class ApkCommand extends FlutterCommand {
|
||||
@override
|
||||
final String name = 'apk';
|
||||
|
||||
@override
|
||||
final String description = 'Build an Android APK package.';
|
||||
|
||||
ApkCommand() {
|
||||
class BuildApkCommand extends FlutterCommand {
|
||||
BuildApkCommand() {
|
||||
usesTargetOption();
|
||||
argParser.addOption('manifest',
|
||||
abbr: 'm',
|
||||
defaultsTo: _kDefaultAndroidManifestPath,
|
||||
@ -157,23 +154,24 @@ class ApkCommand extends FlutterCommand {
|
||||
help: 'Output APK file.');
|
||||
argParser.addOption('flx',
|
||||
abbr: 'f',
|
||||
defaultsTo: '',
|
||||
help: 'Path to the FLX file. If this is not provided, an FLX will be built.');
|
||||
argParser.addOption('keystore',
|
||||
defaultsTo: '',
|
||||
help: 'Path to the keystore used to sign the app.');
|
||||
argParser.addOption('keystore-password',
|
||||
defaultsTo: '',
|
||||
help: 'Password used to access the keystore.');
|
||||
argParser.addOption('keystore-key-alias',
|
||||
defaultsTo: '',
|
||||
help: 'Alias of the entry within the keystore.');
|
||||
argParser.addOption('keystore-key-password',
|
||||
defaultsTo: '',
|
||||
help: 'Password for the entry within the keystore.');
|
||||
addTargetOption();
|
||||
usesPubOption();
|
||||
}
|
||||
|
||||
@override
|
||||
final String name = 'apk';
|
||||
|
||||
@override
|
||||
final String description = 'Build an Android APK file from your app.';
|
||||
|
||||
@override
|
||||
Future<int> runInProject() async {
|
||||
// Validate that we can find an android sdk.
|
||||
@ -199,7 +197,7 @@ class ApkCommand extends FlutterCommand {
|
||||
outputFile: argResults['output-file'],
|
||||
target: argResults['target'],
|
||||
flxPath: argResults['flx'],
|
||||
keystore: argResults['keystore'].isEmpty ? null : new ApkKeystoreInfo(
|
||||
keystore: (argResults['keystore'] ?? '').isEmpty ? null : new ApkKeystoreInfo(
|
||||
keystore: argResults['keystore'],
|
||||
password: argResults['keystore-password'],
|
||||
keyAlias: argResults['keystore-key-alias'],
|
||||
@ -324,13 +322,13 @@ int _signApk(
|
||||
keyPassword = _kDebugKeystorePassword;
|
||||
} else {
|
||||
keystore = new File(keystoreInfo.keystore);
|
||||
keystorePassword = keystoreInfo.password;
|
||||
keyAlias = keystoreInfo.keyAlias;
|
||||
keystorePassword = keystoreInfo.password ?? '';
|
||||
keyAlias = keystoreInfo.keyAlias ?? '';
|
||||
if (keystorePassword.isEmpty || keyAlias.isEmpty) {
|
||||
printError('Must provide a keystore password and a key alias.');
|
||||
return 1;
|
||||
}
|
||||
keyPassword = keystoreInfo.keyPassword;
|
||||
keyPassword = keystoreInfo.keyPassword ?? '';
|
||||
if (keyPassword.isEmpty)
|
||||
keyPassword = keystorePassword;
|
||||
}
|
||||
@ -375,7 +373,7 @@ Future<int> buildAndroid({
|
||||
String resources: _kDefaultResourcesPath,
|
||||
String outputFile: _kDefaultOutputPath,
|
||||
String target: '',
|
||||
String flxPath: '',
|
||||
String flxPath,
|
||||
ApkKeystoreInfo keystore
|
||||
}) async {
|
||||
// Validate that we can find an android sdk.
|
||||
@ -405,7 +403,7 @@ Future<int> buildAndroid({
|
||||
|
||||
printStatus('Building APK...');
|
||||
|
||||
if (flxPath.isNotEmpty) {
|
||||
if (flxPath != null && flxPath.isNotEmpty) {
|
||||
if (!FileSystemEntity.isFileSync(flxPath)) {
|
||||
printError('FLX does not exist: $flxPath');
|
||||
printError('(Omit the --flx option to build the FLX automatically)');
|
68
packages/flutter_tools/lib/src/commands/build_flx.dart
Normal file
68
packages/flutter_tools/lib/src/commands/build_flx.dart
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright 2015 The Chromium 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 '../flx.dart';
|
||||
import '../globals.dart';
|
||||
import '../runner/flutter_command.dart';
|
||||
import '../toolchain.dart';
|
||||
|
||||
class BuildFlxCommand extends FlutterCommand {
|
||||
BuildFlxCommand() {
|
||||
usesTargetOption();
|
||||
argParser.addFlag('precompiled', negatable: false);
|
||||
// This option is still referenced by the iOS build scripts. We should
|
||||
// remove it once we've updated those build scripts.
|
||||
argParser.addOption('asset-base', help: 'Ignored. Will be removed.', hide: true);
|
||||
argParser.addOption('compiler');
|
||||
argParser.addOption('manifest', defaultsTo: defaultManifestPath);
|
||||
argParser.addOption('private-key', defaultsTo: defaultPrivateKeyPath);
|
||||
argParser.addOption('output-file', abbr: 'o', defaultsTo: defaultFlxOutputPath);
|
||||
argParser.addOption('snapshot', defaultsTo: defaultSnapshotPath);
|
||||
argParser.addOption('depfile', defaultsTo: defaultDepfilePath);
|
||||
argParser.addOption('working-dir', defaultsTo: defaultWorkingDirPath);
|
||||
usesPubOption();
|
||||
}
|
||||
|
||||
@override
|
||||
final String name = 'flx';
|
||||
|
||||
@override
|
||||
final String description = 'Build a Flutter FLX file from your app.';
|
||||
|
||||
@override
|
||||
final String usageFooter = 'FLX files are archives of your application code and resources; '
|
||||
'they are used by some Flutter Android and iOS runtimes.';
|
||||
|
||||
@override
|
||||
Future<int> runInProject() async {
|
||||
String compilerPath = argResults['compiler'];
|
||||
|
||||
if (compilerPath == null)
|
||||
await downloadToolchain();
|
||||
else
|
||||
toolchain = new Toolchain(compiler: new Compiler(compilerPath));
|
||||
|
||||
String outputPath = argResults['output-file'];
|
||||
|
||||
return await build(
|
||||
toolchain,
|
||||
mainPath: argResults['target'],
|
||||
manifestPath: argResults['manifest'],
|
||||
outputPath: outputPath,
|
||||
snapshotPath: argResults['snapshot'],
|
||||
depfilePath: argResults['depfile'],
|
||||
privateKeyPath: argResults['private-key'],
|
||||
workingDirPath: argResults['working-dir'],
|
||||
precompiledSnapshot: argResults['precompiled']
|
||||
).then((int result) {
|
||||
if (result == 0)
|
||||
printStatus('Built $outputPath.');
|
||||
else
|
||||
printError('Error building $outputPath: $result.');
|
||||
return result;
|
||||
});
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@ import '../base/os.dart';
|
||||
import '../device.dart';
|
||||
import '../globals.dart';
|
||||
import '../ios/simulators.dart' show SimControl, IOSSimulatorUtils;
|
||||
import 'apk.dart' as apk;
|
||||
import 'build_apk.dart' as build_apk;
|
||||
import 'run.dart';
|
||||
|
||||
/// Runs integration (a.k.a. end-to-end) tests.
|
||||
@ -244,7 +244,7 @@ Future<int> startApp(DriveCommand command) async {
|
||||
|
||||
if (command.device is AndroidDevice) {
|
||||
printTrace('Building an APK.');
|
||||
int result = await apk.build(command.toolchain, command.buildConfigurations,
|
||||
int result = await build_apk.build(command.toolchain, command.buildConfigurations,
|
||||
enginePath: command.runner.enginePath, target: command.target);
|
||||
|
||||
if (result != 0)
|
||||
|
@ -19,7 +19,7 @@ class RefreshCommand extends FlutterCommand {
|
||||
final String description = 'Build and deploy the Dart code in a Flutter app (Android only).';
|
||||
|
||||
RefreshCommand() {
|
||||
addTargetOption();
|
||||
usesTargetOption();
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -15,7 +15,7 @@ import '../device.dart';
|
||||
import '../globals.dart';
|
||||
import '../runner/flutter_command.dart';
|
||||
import '../toolchain.dart';
|
||||
import 'apk.dart';
|
||||
import 'build_apk.dart';
|
||||
import 'install.dart';
|
||||
|
||||
/// Given the value of the --target option, return the path of the Dart file
|
||||
@ -43,7 +43,7 @@ abstract class RunCommandBase extends FlutterCommand {
|
||||
help: 'Start tracing during startup.');
|
||||
argParser.addOption('route',
|
||||
help: 'Which route to load when starting the app.');
|
||||
addTargetOption();
|
||||
usesTargetOption();
|
||||
}
|
||||
|
||||
bool get checked => argResults['checked'];
|
||||
@ -73,12 +73,10 @@ class RunCommand extends RunCommandBase {
|
||||
defaultsTo: false,
|
||||
negatable: false,
|
||||
help: 'Start in a paused mode and wait for a debugger to connect.');
|
||||
argParser.addFlag('pub',
|
||||
defaultsTo: true,
|
||||
help: 'Whether to run "pub get" before running the app.');
|
||||
argParser.addOption('debug-port',
|
||||
defaultsTo: observatoryDefaultPort.toString(),
|
||||
help: 'Listen to the given port for a debug connection.');
|
||||
usesPubOption();
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -7,6 +7,7 @@ import 'dart:io';
|
||||
|
||||
import 'package:args/command_runner.dart';
|
||||
|
||||
import '../dart/pub.dart';
|
||||
import '../application_package.dart';
|
||||
import '../build_configuration.dart';
|
||||
import '../device.dart';
|
||||
@ -18,6 +19,10 @@ import 'flutter_command_runner.dart';
|
||||
typedef bool Validator();
|
||||
|
||||
abstract class FlutterCommand extends Command {
|
||||
FlutterCommand() {
|
||||
commandValidator = _commandValidator;
|
||||
}
|
||||
|
||||
@override
|
||||
FlutterCommandRunner get runner => super.runner;
|
||||
|
||||
@ -30,12 +35,28 @@ abstract class FlutterCommand extends Command {
|
||||
/// Whether this command only applies to Android devices.
|
||||
bool get androidOnly => false;
|
||||
|
||||
/// Whether this command allows usage of the 'target' option.
|
||||
bool get allowsTarget => _targetOptionSpecified;
|
||||
bool _targetOptionSpecified = false;
|
||||
/// Whether this command uses the 'target' option.
|
||||
bool _usesTargetOption = false;
|
||||
|
||||
bool _usesPubOption = false;
|
||||
|
||||
List<BuildConfiguration> get buildConfigurations => runner.buildConfigurations;
|
||||
|
||||
void usesTargetOption() {
|
||||
argParser.addOption('target',
|
||||
abbr: 't',
|
||||
defaultsTo: flx.defaultMainPath,
|
||||
help: 'Target app path / main entry-point file.');
|
||||
_usesTargetOption = true;
|
||||
}
|
||||
|
||||
void usesPubOption() {
|
||||
argParser.addFlag('pub',
|
||||
defaultsTo: true,
|
||||
help: 'Whether to run "pub get" before executing this command.');
|
||||
_usesPubOption = true;
|
||||
}
|
||||
|
||||
Future<Null> downloadToolchain() async {
|
||||
toolchain ??= await Toolchain.forConfigs(buildConfigurations);
|
||||
}
|
||||
@ -56,8 +77,7 @@ abstract class FlutterCommand extends Command {
|
||||
}
|
||||
|
||||
Future<int> _run() async {
|
||||
bool _checkRoot = requiresProjectRoot && allowsTarget && !_targetSpecified;
|
||||
if (_checkRoot && !projectRootValidator())
|
||||
if (requiresProjectRoot && !commandValidator())
|
||||
return 1;
|
||||
|
||||
// Ensure at least one toolchain is installed.
|
||||
@ -99,19 +119,36 @@ abstract class FlutterCommand extends Command {
|
||||
}
|
||||
}
|
||||
|
||||
if (_usesPubOption && argResults['pub']) {
|
||||
int exitCode = await pubGet();
|
||||
if (exitCode != 0)
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
return await runInProject();
|
||||
}
|
||||
|
||||
// This is a field so that you can modify the value for testing.
|
||||
Validator projectRootValidator = () {
|
||||
Validator commandValidator;
|
||||
|
||||
bool _commandValidator() {
|
||||
if (!FileSystemEntity.isFileSync('pubspec.yaml')) {
|
||||
printError('Error: No pubspec.yaml file found.\n'
|
||||
'This command should be run from the root of your Flutter project.\n'
|
||||
'Do not run this command from the root of your git clone of Flutter.');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_usesTargetOption) {
|
||||
String targetPath = argResults['target'];
|
||||
if (!FileSystemEntity.isFileSync(targetPath)) {
|
||||
printError('Target file "$targetPath" not found.');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
Future<int> runInProject();
|
||||
|
||||
@ -122,15 +159,4 @@ abstract class FlutterCommand extends Command {
|
||||
|
||||
ApplicationPackageStore applicationPackages;
|
||||
Toolchain toolchain;
|
||||
|
||||
bool _targetSpecified = false;
|
||||
|
||||
void addTargetOption() {
|
||||
argParser.addOption('target',
|
||||
abbr: 't',
|
||||
callback: (dynamic val) => _targetSpecified = true,
|
||||
defaultsTo: flx.defaultMainPath,
|
||||
help: 'Target app path / main entry-point file.');
|
||||
_targetOptionSpecified = true;
|
||||
}
|
||||
}
|
||||
|
@ -101,5 +101,5 @@ void applyMocksToCommand(FlutterCommand command) {
|
||||
command
|
||||
..applicationPackages = new MockApplicationPackageStore()
|
||||
..toolchain = new MockToolchain()
|
||||
..projectRootValidator = () => true;
|
||||
..commandValidator = () => true;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user