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
diff --git a/packages/flutter_tools/lib/src/build_configuration.dart b/packages/flutter_tools/lib/src/build_configuration.dart index cce4248..38bb16408 100644 --- a/packages/flutter_tools/lib/src/build_configuration.dart +++ b/packages/flutter_tools/lib/src/build_configuration.dart
@@ -14,6 +14,20 @@ 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 { mac, linux,
diff --git a/packages/flutter_tools/lib/src/commands/build_apk.dart b/packages/flutter_tools/lib/src/commands/build_apk.dart index 529e913..224ca17 100644 --- a/packages/flutter_tools/lib/src/commands/build_apk.dart +++ b/packages/flutter_tools/lib/src/commands/build_apk.dart
@@ -8,14 +8,12 @@ import 'package:path/path.dart' as path; import '../android/android_sdk.dart'; -import '../application_package.dart'; import '../artifacts.dart'; import '../base/file_system.dart' show ensureDirectoryExists; import '../base/os.dart'; import '../base/process.dart'; import '../base/utils.dart'; import '../build_configuration.dart'; -import '../device.dart'; import '../flx.dart' as flx; import '../globals.dart'; import '../runner/flutter_command.dart'; @@ -23,6 +21,8 @@ import '../toolchain.dart'; import 'run.dart'; +export '../android/android_device.dart' show AndroidDevice; + const String _kDefaultAndroidManifestPath = 'android/AndroidManifest.xml'; const String _kDefaultOutputPath = 'build/app.apk'; const String _kDefaultResourcesPath = 'android/res'; @@ -138,37 +138,48 @@ class BuildApkCommand extends FlutterCommand { 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(); - argParser.addOption('manifest', - abbr: 'm', - defaultsTo: _kDefaultAndroidManifestPath, - help: 'Android manifest XML file.'); - argParser.addOption('resources', - abbr: 'r', - help: 'Resources directory path.'); - argParser.addOption('output-file', - abbr: 'o', - defaultsTo: _kDefaultOutputPath, - help: 'Output APK file.'); - argParser.addOption('flx', - abbr: 'f', - help: 'Path to the FLX file. If this is not provided, an FLX will be built.'); - argParser.addOption('keystore', - help: 'Path to the keystore used to sign the app.'); - argParser.addOption('keystore-password', - help: 'Password used to access the keystore.'); - argParser.addOption('keystore-key-alias', - help: 'Alias of the entry within the keystore.'); - argParser.addOption('keystore-key-password', - help: 'Password for the entry within the keystore.'); usesPubOption(); + + argParser.addOption('manifest', + abbr: 'm', + defaultsTo: _kDefaultAndroidManifestPath, + help: 'Android manifest XML file.'); + argParser.addOption('resources', + abbr: 'r', + help: 'Resources directory path.'); + argParser.addOption('output-file', + abbr: 'o', + defaultsTo: _kDefaultOutputPath, + help: 'Output APK file.'); + argParser.addOption('flx', + abbr: 'f', + help: 'Path to the FLX file. If this is not provided, an FLX will be built.'); + argParser.addOption('keystore', + help: 'Path to the keystore used to sign the app.'); + argParser.addOption('keystore-password', + help: 'Password used to access the keystore.'); + argParser.addOption('keystore-key-alias', + help: 'Alias of the entry within the keystore.'); + argParser.addOption('keystore-key-password', + help: 'Password for the entry within the keystore.'); } @override final String name = 'apk'; @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 Future<int> runInProject() async { @@ -185,10 +196,16 @@ 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). return await buildAndroid( TargetPlatform.android_arm, + variant, toolchain: toolchain, configs: buildConfigurations, enginePath: runner.enginePath, @@ -213,8 +230,11 @@ BuildConfiguration config, String enginePath, String manifest, - String resources + String resources, + BuildVariant buildVariant ) async { + // TODO(devoncarew): Get the right artifacts for [buildVariant]. + List<String> artifactPaths; if (enginePath != null) { String abiDir = platform == TargetPlatform.android_arm ? 'armeabi-v7a' : 'x86_64'; @@ -231,13 +251,12 @@ ArtifactType.androidLibSkyShell, ArtifactType.androidKeystore, ]; - Iterable<String> pathFutures = artifactTypes.map((ArtifactType type) { + artifactPaths = artifactTypes.map((ArtifactType type) { return ArtifactStore.getPath(ArtifactStore.getArtifact( type: type, targetPlatform: config.targetPlatform )); - }); - artifactPaths = pathFutures.toList(); + }).toList(); } _ApkComponents components = new _ApkComponents(); @@ -264,13 +283,19 @@ int _buildApk( TargetPlatform platform, + BuildVariant buildVariant, _ApkComponents components, String flxPath, ApkKeystoreInfo keystore, String outputFile ) { + assert(platform != null); + assert(buildVariant != null); + Directory tempDir = Directory.systemTemp.createTempSync('flutter_tools'); + printTrace('Building APK; buildVariant: ${getVariantName(buildVariant)}.'); + try { _ApkBuilder builder = new _ApkBuilder(androidSdk.latestVersion); @@ -375,7 +400,8 @@ } Future<int> buildAndroid( - TargetPlatform platform, { + TargetPlatform platform, + BuildVariant buildVariant, { Toolchain toolchain, List<BuildConfiguration> configs, String enginePath, @@ -416,14 +442,16 @@ } 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) { printError('Failure building APK. Unable to find components.'); return 1; } - printStatus('Building APK...'); + printStatus('Building APK in ${getVariantName(buildVariant)} mode...'); if (flxPath != null && flxPath.isNotEmpty) { if (!FileSystemEntity.isFileSync(flxPath)) { @@ -432,7 +460,7 @@ return 1; } - return _buildApk(platform, components, flxPath, keystore, outputFile); + return _buildApk(platform, buildVariant, components, flxPath, keystore, outputFile); } else { // Find the path to the main Dart file; build the FLX. String mainPath = findMainDartFile(target); @@ -441,40 +469,17 @@ mainPath: mainPath, includeRobotoFonts: false); - return _buildApk(platform, components, localBundlePath, keystore, outputFile); + return _buildApk(platform, buildVariant, components, localBundlePath, keystore, outputFile); } } -// TODO(mpcomplete): move this to Device? -/// 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( +Future<int> buildApk( TargetPlatform platform, Toolchain toolchain, List<BuildConfiguration> configs, { String enginePath, - String target + String target, + BuildVariant buildVariant: BuildVariant.develop }) async { if (!FileSystemEntity.isFileSync(_kDefaultAndroidManifestPath)) { printError('Cannot build APK. Missing $_kDefaultAndroidManifestPath.'); @@ -483,6 +488,7 @@ int result = await buildAndroid( platform, + buildVariant, toolchain: toolchain, configs: configs, enginePath: enginePath,
diff --git a/packages/flutter_tools/lib/src/commands/drive.dart b/packages/flutter_tools/lib/src/commands/drive.dart index 23809a5..704507f 100644 --- a/packages/flutter_tools/lib/src/commands/drive.dart +++ b/packages/flutter_tools/lib/src/commands/drive.dart
@@ -242,7 +242,7 @@ // TODO(devoncarew): We should remove the need to special case here. if (command.device is AndroidDevice) { printTrace('Building an APK.'); - int result = await build_apk.build( + int result = await build_apk.buildApk( command.device.platform, command.toolchain, command.buildConfigurations, enginePath: command.runner.enginePath, target: command.target );
diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart index e78f11c..7299fe5 100644 --- a/packages/flutter_tools/lib/src/commands/run.dart +++ b/packages/flutter_tools/lib/src/commands/run.dart
@@ -162,13 +162,17 @@ return 1; } - if (install) { + // TODO(devoncarew): We shouldn't have to do type checks here. + if (install && device is AndroidDevice) { printTrace('Running build command.'); - int result = await buildForDevice( - device, applicationPackages, toolchain, configs, + + int result = await buildApk( + device.platform, toolchain, configs, enginePath: enginePath, - target: target + target: target, + buildVariant: BuildVariant.develop ); + if (result != 0) return result; }