Commit 8849cd6b authored by Devon Carew's avatar Devon Carew

add a --deploy flag to build apk (#3249)

* add a --deploy flag to build apk

* update command description

* use an enum instead of a bool param for build variants

* rename buildForDeploy flag to buildVariant

* review comments
parent b4173e20
...@@ -14,6 +14,20 @@ enum BuildType { ...@@ -14,6 +14,20 @@ enum BuildType {
debug, debug,
} }
/// The type of build - `develop` or `deploy`.
///
/// TODO(devoncarew): Add a `profile` variant.
enum BuildVariant {
develop,
deploy
}
String getVariantName(BuildVariant variant) {
String name = '$variant';
int index = name.indexOf('.');
return index == -1 ? name : name.substring(index + 1);
}
enum HostPlatform { enum HostPlatform {
mac, mac,
linux, linux,
......
...@@ -8,14 +8,12 @@ import 'dart:io'; ...@@ -8,14 +8,12 @@ import 'dart:io';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import '../android/android_sdk.dart'; import '../android/android_sdk.dart';
import '../application_package.dart';
import '../artifacts.dart'; import '../artifacts.dart';
import '../base/file_system.dart' show ensureDirectoryExists; import '../base/file_system.dart' show ensureDirectoryExists;
import '../base/os.dart'; import '../base/os.dart';
import '../base/process.dart'; import '../base/process.dart';
import '../base/utils.dart'; import '../base/utils.dart';
import '../build_configuration.dart'; import '../build_configuration.dart';
import '../device.dart';
import '../flx.dart' as flx; import '../flx.dart' as flx;
import '../globals.dart'; import '../globals.dart';
import '../runner/flutter_command.dart'; import '../runner/flutter_command.dart';
...@@ -23,6 +21,8 @@ import '../services.dart'; ...@@ -23,6 +21,8 @@ import '../services.dart';
import '../toolchain.dart'; import '../toolchain.dart';
import 'run.dart'; import 'run.dart';
export '../android/android_device.dart' show AndroidDevice;
const String _kDefaultAndroidManifestPath = 'android/AndroidManifest.xml'; const String _kDefaultAndroidManifestPath = 'android/AndroidManifest.xml';
const String _kDefaultOutputPath = 'build/app.apk'; const String _kDefaultOutputPath = 'build/app.apk';
const String _kDefaultResourcesPath = 'android/res'; const String _kDefaultResourcesPath = 'android/res';
...@@ -138,37 +138,48 @@ class ApkKeystoreInfo { ...@@ -138,37 +138,48 @@ class ApkKeystoreInfo {
class BuildApkCommand extends FlutterCommand { class BuildApkCommand extends FlutterCommand {
BuildApkCommand() { BuildApkCommand() {
argParser.addFlag('develop',
negatable: false,
help: 'Build a development version of your app (the default).');
argParser.addFlag('deploy',
negatable: false,
help: 'Build a deployable version of your app.');
usesTargetOption(); usesTargetOption();
usesPubOption();
argParser.addOption('manifest', argParser.addOption('manifest',
abbr: 'm', abbr: 'm',
defaultsTo: _kDefaultAndroidManifestPath, defaultsTo: _kDefaultAndroidManifestPath,
help: 'Android manifest XML file.'); help: 'Android manifest XML file.');
argParser.addOption('resources', argParser.addOption('resources',
abbr: 'r', abbr: 'r',
help: 'Resources directory path.'); help: 'Resources directory path.');
argParser.addOption('output-file', argParser.addOption('output-file',
abbr: 'o', abbr: 'o',
defaultsTo: _kDefaultOutputPath, defaultsTo: _kDefaultOutputPath,
help: 'Output APK file.'); help: 'Output APK file.');
argParser.addOption('flx', argParser.addOption('flx',
abbr: 'f', abbr: 'f',
help: 'Path to the FLX file. If this is not provided, an FLX will be built.'); help: 'Path to the FLX file. If this is not provided, an FLX will be built.');
argParser.addOption('keystore', argParser.addOption('keystore',
help: 'Path to the keystore used to sign the app.'); help: 'Path to the keystore used to sign the app.');
argParser.addOption('keystore-password', argParser.addOption('keystore-password',
help: 'Password used to access the keystore.'); help: 'Password used to access the keystore.');
argParser.addOption('keystore-key-alias', argParser.addOption('keystore-key-alias',
help: 'Alias of the entry within the keystore.'); help: 'Alias of the entry within the keystore.');
argParser.addOption('keystore-key-password', argParser.addOption('keystore-key-password',
help: 'Password for the entry within the keystore.'); help: 'Password for the entry within the keystore.');
usesPubOption();
} }
@override @override
final String name = 'apk'; final String name = 'apk';
@override @override
final String description = 'Build an Android APK file from your app.'; final String description = 'Build an Android APK file from your app.\n\n'
'This command can build development and deployable versions of your application. \'develop\' builds\n'
'support debugging and a quick development cycle. \'deploy\' builds don\'t support debugging and are\n'
'suitable for deploying to app stores.';
@override @override
Future<int> runInProject() async { Future<int> runInProject() async {
...@@ -185,10 +196,16 @@ class BuildApkCommand extends FlutterCommand { ...@@ -185,10 +196,16 @@ class BuildApkCommand extends FlutterCommand {
return 1; return 1;
} }
BuildVariant variant = BuildVariant.develop;
if (argResults['deploy'])
variant = BuildVariant.deploy;
// TODO(devoncarew): This command should take an arg for the output type (arm / x64). // TODO(devoncarew): This command should take an arg for the output type (arm / x64).
return await buildAndroid( return await buildAndroid(
TargetPlatform.android_arm, TargetPlatform.android_arm,
variant,
toolchain: toolchain, toolchain: toolchain,
configs: buildConfigurations, configs: buildConfigurations,
enginePath: runner.enginePath, enginePath: runner.enginePath,
...@@ -213,8 +230,11 @@ Future<_ApkComponents> _findApkComponents( ...@@ -213,8 +230,11 @@ Future<_ApkComponents> _findApkComponents(
BuildConfiguration config, BuildConfiguration config,
String enginePath, String enginePath,
String manifest, String manifest,
String resources String resources,
BuildVariant buildVariant
) async { ) async {
// TODO(devoncarew): Get the right artifacts for [buildVariant].
List<String> artifactPaths; List<String> artifactPaths;
if (enginePath != null) { if (enginePath != null) {
String abiDir = platform == TargetPlatform.android_arm ? 'armeabi-v7a' : 'x86_64'; String abiDir = platform == TargetPlatform.android_arm ? 'armeabi-v7a' : 'x86_64';
...@@ -231,13 +251,12 @@ Future<_ApkComponents> _findApkComponents( ...@@ -231,13 +251,12 @@ Future<_ApkComponents> _findApkComponents(
ArtifactType.androidLibSkyShell, ArtifactType.androidLibSkyShell,
ArtifactType.androidKeystore, ArtifactType.androidKeystore,
]; ];
Iterable<String> pathFutures = artifactTypes.map((ArtifactType type) { artifactPaths = artifactTypes.map((ArtifactType type) {
return ArtifactStore.getPath(ArtifactStore.getArtifact( return ArtifactStore.getPath(ArtifactStore.getArtifact(
type: type, type: type,
targetPlatform: config.targetPlatform targetPlatform: config.targetPlatform
)); ));
}); }).toList();
artifactPaths = pathFutures.toList();
} }
_ApkComponents components = new _ApkComponents(); _ApkComponents components = new _ApkComponents();
...@@ -264,13 +283,19 @@ Future<_ApkComponents> _findApkComponents( ...@@ -264,13 +283,19 @@ Future<_ApkComponents> _findApkComponents(
int _buildApk( int _buildApk(
TargetPlatform platform, TargetPlatform platform,
BuildVariant buildVariant,
_ApkComponents components, _ApkComponents components,
String flxPath, String flxPath,
ApkKeystoreInfo keystore, ApkKeystoreInfo keystore,
String outputFile String outputFile
) { ) {
assert(platform != null);
assert(buildVariant != null);
Directory tempDir = Directory.systemTemp.createTempSync('flutter_tools'); Directory tempDir = Directory.systemTemp.createTempSync('flutter_tools');
printTrace('Building APK; buildVariant: ${getVariantName(buildVariant)}.');
try { try {
_ApkBuilder builder = new _ApkBuilder(androidSdk.latestVersion); _ApkBuilder builder = new _ApkBuilder(androidSdk.latestVersion);
...@@ -375,7 +400,8 @@ bool _needsRebuild(String apkPath, String manifest) { ...@@ -375,7 +400,8 @@ bool _needsRebuild(String apkPath, String manifest) {
} }
Future<int> buildAndroid( Future<int> buildAndroid(
TargetPlatform platform, { TargetPlatform platform,
BuildVariant buildVariant, {
Toolchain toolchain, Toolchain toolchain,
List<BuildConfiguration> configs, List<BuildConfiguration> configs,
String enginePath, String enginePath,
...@@ -416,14 +442,16 @@ Future<int> buildAndroid( ...@@ -416,14 +442,16 @@ Future<int> buildAndroid(
} }
BuildConfiguration config = configs.firstWhere((BuildConfiguration bc) => bc.targetPlatform == platform); BuildConfiguration config = configs.firstWhere((BuildConfiguration bc) => bc.targetPlatform == platform);
_ApkComponents components = await _findApkComponents(platform, config, enginePath, manifest, resources); _ApkComponents components = await _findApkComponents(
platform, config, enginePath, manifest, resources, buildVariant
);
if (components == null) { if (components == null) {
printError('Failure building APK. Unable to find components.'); printError('Failure building APK. Unable to find components.');
return 1; return 1;
} }
printStatus('Building APK...'); printStatus('Building APK in ${getVariantName(buildVariant)} mode...');
if (flxPath != null && flxPath.isNotEmpty) { if (flxPath != null && flxPath.isNotEmpty) {
if (!FileSystemEntity.isFileSync(flxPath)) { if (!FileSystemEntity.isFileSync(flxPath)) {
...@@ -432,7 +460,7 @@ Future<int> buildAndroid( ...@@ -432,7 +460,7 @@ Future<int> buildAndroid(
return 1; return 1;
} }
return _buildApk(platform, components, flxPath, keystore, outputFile); return _buildApk(platform, buildVariant, components, flxPath, keystore, outputFile);
} else { } else {
// Find the path to the main Dart file; build the FLX. // Find the path to the main Dart file; build the FLX.
String mainPath = findMainDartFile(target); String mainPath = findMainDartFile(target);
...@@ -441,40 +469,17 @@ Future<int> buildAndroid( ...@@ -441,40 +469,17 @@ Future<int> buildAndroid(
mainPath: mainPath, mainPath: mainPath,
includeRobotoFonts: false); includeRobotoFonts: false);
return _buildApk(platform, components, localBundlePath, keystore, outputFile); return _buildApk(platform, buildVariant, components, localBundlePath, keystore, outputFile);
} }
} }
// TODO(mpcomplete): move this to Device? Future<int> buildApk(
/// This is currently Android specific.
Future<int> buildForDevice(
Device device,
ApplicationPackageStore applicationPackages,
Toolchain toolchain,
List<BuildConfiguration> configs, {
String enginePath,
String target: ''
}) async {
ApplicationPackage package = applicationPackages.getPackageForPlatform(device.platform);
if (package == null)
return 0;
// TODO(mpcomplete): Temporary hack. We only support the apk builder atm.
if (package == applicationPackages.android) {
int result = await build(device.platform, toolchain, configs, enginePath: enginePath, target: target);
if (result != 0)
return result;
}
return 0;
}
Future<int> build(
TargetPlatform platform, TargetPlatform platform,
Toolchain toolchain, Toolchain toolchain,
List<BuildConfiguration> configs, { List<BuildConfiguration> configs, {
String enginePath, String enginePath,
String target String target,
BuildVariant buildVariant: BuildVariant.develop
}) async { }) async {
if (!FileSystemEntity.isFileSync(_kDefaultAndroidManifestPath)) { if (!FileSystemEntity.isFileSync(_kDefaultAndroidManifestPath)) {
printError('Cannot build APK. Missing $_kDefaultAndroidManifestPath.'); printError('Cannot build APK. Missing $_kDefaultAndroidManifestPath.');
...@@ -483,6 +488,7 @@ Future<int> build( ...@@ -483,6 +488,7 @@ Future<int> build(
int result = await buildAndroid( int result = await buildAndroid(
platform, platform,
buildVariant,
toolchain: toolchain, toolchain: toolchain,
configs: configs, configs: configs,
enginePath: enginePath, enginePath: enginePath,
......
...@@ -242,7 +242,7 @@ Future<int> startApp(DriveCommand command) async { ...@@ -242,7 +242,7 @@ Future<int> startApp(DriveCommand command) async {
// TODO(devoncarew): We should remove the need to special case here. // TODO(devoncarew): We should remove the need to special case here.
if (command.device is AndroidDevice) { if (command.device is AndroidDevice) {
printTrace('Building an APK.'); printTrace('Building an APK.');
int result = await build_apk.build( int result = await build_apk.buildApk(
command.device.platform, command.toolchain, command.buildConfigurations, command.device.platform, command.toolchain, command.buildConfigurations,
enginePath: command.runner.enginePath, target: command.target enginePath: command.runner.enginePath, target: command.target
); );
......
...@@ -162,13 +162,17 @@ Future<int> startApp( ...@@ -162,13 +162,17 @@ Future<int> startApp(
return 1; return 1;
} }
if (install) { // TODO(devoncarew): We shouldn't have to do type checks here.
if (install && device is AndroidDevice) {
printTrace('Running build command.'); printTrace('Running build command.');
int result = await buildForDevice(
device, applicationPackages, toolchain, configs, int result = await buildApk(
device.platform, toolchain, configs,
enginePath: enginePath, enginePath: enginePath,
target: target target: target,
buildVariant: BuildVariant.develop
); );
if (result != 0) if (result != 0)
return result; return result;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment