Commit 7b2367ed authored by Jakob Andersen's avatar Jakob Andersen Committed by GitHub

Remove legacy .apk build. (#8793)

* Remove legacy .apk build.

Print out an error message telling the user to upgrade the project if
it's not Gradle-based. Removed all the obvious traces of the legacy
build.

Added support for Dart VM kernel snapshots in Gradle builds.

Fixed Android installs to verify that the app is actually installed, and
not just rely on the presence of the .sha1 file.
parent 3fc7265a
......@@ -149,6 +149,11 @@ class FlutterPlugin implements Plugin<Project> {
project.compileDebugJavaWithJavac.dependsOn project.flutterBuildX86Jar
}
File kernel
if (project.hasProperty('kernel')) {
kernel = project.file(project.property('kernel'))
}
project.android.applicationVariants.all { variant ->
if (!["debug", "profile", "release"].contains(variant.name)) {
throw new GradleException("Build variant must be one of \"debug\", \"profile\", or \"release\" but was \"${variant.name}\"")
......@@ -161,6 +166,7 @@ class FlutterPlugin implements Plugin<Project> {
localEngine this.localEngine
localEngineSrcPath this.localEngineSrcPath
targetPath target
kernelFile kernel
sourceDir project.file(project.flutter.source)
intermediateDir project.file("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter/${variant.name}")
}
......@@ -189,6 +195,8 @@ class FlutterTask extends DefaultTask {
String localEngineSrcPath
@Input
String targetPath
@Optional @InputFile
File kernelFile
File sourceDir
......@@ -282,6 +290,9 @@ class FlutterTask extends DefaultTask {
}
args "build", "flx"
args "--target", targetPath
if (kernelFile != null) {
args "--kernel", kernelFile.absolutePath
}
args "--output-file", "${intermediateDir}/app.flx"
args "--no-include-roboto-fonts"
if (buildMode != "debug") {
......
......@@ -15,7 +15,6 @@ import '../base/process.dart';
import '../base/process_manager.dart';
import '../build_info.dart';
import '../commands/build_apk.dart';
import '../devfs.dart';
import '../device.dart';
import '../globals.dart';
import '../protocol_discovery.dart';
......@@ -263,6 +262,24 @@ class AndroidDevice extends Device {
return true;
}
bool _installLatestApp(ApplicationPackage package) {
if (isAppInstalled(package)) {
if (isLatestBuildInstalled(package)) {
printStatus('Latest build already installed.');
return true;
}
printStatus('Uninstalling old version...');
if (!uninstallApp(package))
printError('Warning: uninstalling old version failed');
}
printTrace('Installing APK.');
if (!installApp(package)) {
printTrace('Error: Failed to install APK.');
return false;
}
return true;
}
@override
Future<LaunchResult> startApp(
ApplicationPackage package,
......@@ -271,7 +288,7 @@ class AndroidDevice extends Device {
String route,
DebuggingOptions debuggingOptions,
Map<String, dynamic> platformArgs,
DevFSContent kernelContent,
String kernelPath,
bool prebuiltApplication: false,
bool applicationNeedsRebuild: false,
}) async {
......@@ -288,8 +305,7 @@ class AndroidDevice extends Device {
await buildApk(targetPlatform,
target: mainPath,
buildMode: debuggingOptions.buildMode,
kernelContent: kernelContent,
applicationNeedsRebuild: applicationNeedsRebuild
kernelPath: kernelPath,
);
// Package has been built, so we can get the updated application ID and
// activity name from the .apk.
......@@ -299,21 +315,8 @@ class AndroidDevice extends Device {
printTrace("Stopping app '${package.name}' on $name.");
await stopApp(package);
if (isLatestBuildInstalled(package)) {
printStatus('Latest build already installed.');
} else {
if (isAppInstalled(package)) {
printStatus('Uninstalling old version...');
if (!uninstallApp(package))
printError('Warning: uninstalling old version failed');
}
printTrace('Installing APK.');
if (!installApp(package)) {
printTrace('Error: Failed to install APK.');
return new LaunchResult.failed();
}
}
if (!_installLatestApp(package))
return new LaunchResult.failed();
final bool traceStartup = platformArgs['trace-startup'] ?? false;
final AndroidApk apk = package;
......
......@@ -141,7 +141,7 @@ File ensureLocalProperties() {
return localProperties;
}
Future<Null> buildGradleProject(BuildMode buildMode, String target) async {
Future<Null> buildGradleProject(BuildMode buildMode, String target, String kernelPath) async {
final File localProperties = ensureLocalProperties();
// Update the local.properties file with the build mode.
// FlutterPlugin v1 reads local.properties to determine build mode. Plugin v2
......@@ -162,7 +162,7 @@ Future<Null> buildGradleProject(BuildMode buildMode, String target) async {
case FlutterPluginVersion.managed:
// Fall through. Managed plugin builds the same way as plugin v2.
case FlutterPluginVersion.v2:
return buildGradleProjectV2(gradle, buildModeName, target);
return buildGradleProjectV2(gradle, buildModeName, target, kernelPath);
}
}
......@@ -185,7 +185,7 @@ Future<Null> buildGradleProjectV1(String gradle) async {
printStatus('Built $gradleAppOutV1 (${getSizeAsMB(apkFile.lengthSync())}).');
}
Future<Null> buildGradleProjectV2(String gradle, String buildModeName, String target) async {
Future<Null> buildGradleProjectV2(String gradle, String buildModeName, String target, String kernelPath) async {
final String assembleTask = "assemble${toTitleCase(buildModeName)}";
// Run 'gradle assemble<BuildMode>'.
......@@ -203,6 +203,8 @@ Future<Null> buildGradleProjectV2(String gradle, String buildModeName, String ta
if (target != null) {
command.add('-Ptarget=$target');
}
if (kernelPath != null)
command.add('-Pkernel=$kernelPath');
command.add(assembleTask);
final int exitcode = await runCommandAndStreamOutput(
command,
......
......@@ -9,8 +9,6 @@ import 'build_info.dart';
import 'globals.dart';
enum Artifact {
chromiumDebugKeyStore,
classesDexJar,
icudtlDat,
libskyShellSo,
dartIoEntriesTxt,
......@@ -26,10 +24,6 @@ enum Artifact {
String _artifactToFileName(Artifact artifact) {
switch (artifact) {
case Artifact.chromiumDebugKeyStore:
return 'chromium-debug.keystore';
case Artifact.classesDexJar:
return 'classes.dex.jar';
case Artifact.icudtlDat:
return 'icudtl.dat';
case Artifact.libskyShellSo:
......@@ -104,8 +98,6 @@ class CachedArtifacts extends Artifacts {
String _getAndroidArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode mode) {
final String engineDir = _getEngineArtifactsPath(platform, mode);
switch (artifact) {
case Artifact.chromiumDebugKeyStore:
case Artifact.classesDexJar:
case Artifact.icudtlDat:
case Artifact.libskyShellSo:
return fs.path.join(engineDir, _artifactToFileName(artifact));
......@@ -207,8 +199,6 @@ class LocalEngineArtifacts extends Artifacts {
@override
String getArtifactPath(Artifact artifact, [TargetPlatform platform, BuildMode mode]) {
switch (artifact) {
case Artifact.chromiumDebugKeyStore:
return fs.path.join(_engineSrcPath, 'build', 'android', 'ant', _artifactToFileName(artifact));
case Artifact.dartIoEntriesTxt:
return fs.path.join(_engineSrcPath, 'dart', 'runtime', 'bin', _artifactToFileName(artifact));
case Artifact.dartVmEntryPointsTxt:
......@@ -216,8 +206,6 @@ class LocalEngineArtifacts extends Artifacts {
return fs.path.join(_engineSrcPath, 'flutter', 'runtime', _artifactToFileName(artifact));
case Artifact.snapshotDart:
return fs.path.join(_engineSrcPath, 'flutter', 'lib', 'snapshot', _artifactToFileName(artifact));
case Artifact.classesDexJar:
return fs.path.join(engineOutPath, 'gen', 'flutter', 'shell', 'platform', 'android', 'android', _artifactToFileName(artifact));
case Artifact.libskyShellSo:
final String abi = _getAbiDirectory(platform);
return fs.path.join(engineOutPath, 'gen', 'flutter', 'shell', 'platform', 'android', 'android', fs.path.join('android', 'libs', abi, _artifactToFileName(artifact)));
......
......@@ -3,151 +3,16 @@
// found in the LICENSE file.
import 'dart:async';
import 'dart:convert' show JSON;
import '../android/android_sdk.dart';
import '../android/gradle.dart';
import '../artifacts.dart';
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/logger.dart';
import '../base/process.dart';
import '../base/process_manager.dart';
import '../base/utils.dart';
import '../build_info.dart';
import '../devfs.dart';
import '../flx.dart' as flx;
import '../globals.dart';
import '../resident_runner.dart';
import '../services.dart';
import 'build.dart';
import 'build_aot.dart';
export '../android/android_device.dart' show AndroidDevice;
const String _kDefaultAndroidManifestPath = 'android/AndroidManifest.xml';
const String _kDefaultResourcesPath = 'android/res';
const String _kDefaultAssetsPath = 'android/assets';
const String _kFlutterManifestPath = 'pubspec.yaml';
const String _kPackagesStatusPath = '.packages';
// Alias of the key provided in the Chromium debug keystore
const String _kDebugKeystoreKeyAlias = "chromiumdebugkey";
// Password for the Chromium debug keystore
const String _kDebugKeystorePassword = "chromium";
// Default APK output fs.path.
String get _defaultOutputPath => fs.path.join(getAndroidBuildDirectory(), 'app.apk');
/// Copies files into a new directory structure.
class _AssetBuilder {
final Directory outDir;
Directory _assetDir;
_AssetBuilder(this.outDir, String assetDirName) {
_assetDir = fs.directory('${outDir.path}/$assetDirName');
_assetDir.createSync(recursive: true);
}
void add(File asset, String relativePath) {
final String destPath = fs.path.join(_assetDir.path, relativePath);
ensureDirectoryExists(destPath);
asset.copySync(destPath);
}
Directory get directory => _assetDir;
}
/// Builds an APK package using Android SDK tools.
class _ApkBuilder {
final AndroidSdkVersion sdk;
File _androidJar;
File _aapt;
File _dx;
File _zipalign;
File _apksigner;
_ApkBuilder(this.sdk) {
_androidJar = fs.file(sdk.androidJarPath);
_aapt = fs.file(sdk.aaptPath);
_dx = fs.file(sdk.dxPath);
_zipalign = fs.file(sdk.zipalignPath);
_apksigner = fs.file(sdk.apksignerPath);
}
String checkDependencies() {
if (!_androidJar.existsSync())
return 'Cannot find android.jar at ${_androidJar.path}';
if (!processManager.canRun(_aapt.path))
return 'Cannot find aapt at ${_aapt.path}';
if (!processManager.canRun(_dx.path))
return 'Cannot find dx at ${_dx.path}';
if (!processManager.canRun(_zipalign.path))
return 'Cannot find zipalign at ${_zipalign.path}';
if (!processManager.canRun(_apksigner.path))
return 'Cannot find apksigner at ${_apksigner.path}';
return null;
}
void compileClassesDex(File classesDex, List<File> jars) {
final List<String> packageArgs = <String>[_dx.path,
'--dex',
'--force-jumbo',
'--output', classesDex.path
];
packageArgs.addAll(jars.map((File f) => f.path));
runCheckedSync(packageArgs);
}
void package(File outputApk, File androidManifest, Directory assets, Directory artifacts, Directory resources, BuildMode buildMode) {
final List<String> packageArgs = <String>[_aapt.path,
'package',
'-M', androidManifest.path,
'-A', assets.path,
'-I', _androidJar.path,
'-F', outputApk.path,
];
if (buildMode == BuildMode.debug)
packageArgs.add('--debug-mode');
if (resources != null)
packageArgs.addAll(<String>['-S', resources.absolute.path]);
packageArgs.add(artifacts.path);
runCheckedSync(packageArgs);
}
void sign(File keystore, String keystorePassword, String keyAlias, String keyPassword, File outputApk) {
assert(_apksigner != null);
runCheckedSync(<String>[_apksigner.path, 'sign',
'--ks', keystore.path,
'--ks-key-alias', keyAlias,
'--ks-pass', 'pass:$keystorePassword',
'--key-pass', 'pass:$keyPassword',
outputApk.path,
]);
}
void align(File unalignedApk, File outputApk) {
runCheckedSync(<String>[_zipalign.path, '-f', '4', unalignedApk.path, outputApk.path]);
}
}
class _ApkComponents {
File manifest;
File icuData;
List<File> jars;
List<Map<String, String>> services = <Map<String, String>>[];
File libSkyShell;
File debugKeystore;
Directory resources;
Map<String, File> extraFiles;
}
class ApkKeystoreInfo {
ApkKeystoreInfo({ this.keystore, this.password, this.keyAlias, this.keyPassword }) {
assert(keystore != null);
......@@ -165,35 +30,10 @@ class BuildApkCommand extends BuildSubCommand {
addBuildModeFlags();
usesPubOption();
argParser.addOption('manifest',
abbr: 'm',
defaultsTo: _kDefaultAndroidManifestPath,
help: 'Android manifest XML file.');
argParser.addOption('resources',
abbr: 'r',
help: 'Resources directory fs.path.');
argParser.addOption('output-file',
abbr: 'o',
defaultsTo: _defaultOutputPath,
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('target-arch',
defaultsTo: 'arm',
allowed: <String>['arm', 'x86', 'x64'],
help: 'Architecture of the target device.');
argParser.addOption('aot-path',
help: 'Path to the ahead-of-time compiled snapshot directory.\n'
'If this is not provided, an AOT snapshot 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
......@@ -224,358 +64,25 @@ class BuildApkCommand extends BuildSubCommand {
final TargetPlatform targetPlatform = _getTargetPlatform(argResults['target-arch']);
final BuildMode buildMode = getBuildMode();
if (targetPlatform != TargetPlatform.android_arm && buildMode != BuildMode.debug)
throwToolExit('Profile and release builds are only supported on ARM targets.');
if (isProjectUsingGradle()) {
await buildAndroidWithGradle(
targetPlatform,
buildMode,
target: targetFile
);
} else {
await buildAndroid(
targetPlatform,
buildMode,
force: true,
manifest: argResults['manifest'],
resources: argResults['resources'],
outputFile: argResults['output-file'],
target: targetFile,
flxPath: argResults['flx'],
aotPath: argResults['aot-path'],
keystore: (argResults['keystore'] ?? '').isEmpty ? null : new ApkKeystoreInfo(
keystore: argResults['keystore'],
password: argResults['keystore-password'],
keyAlias: argResults['keystore-key-alias'],
keyPassword: argResults['keystore-key-password']
)
);
}
}
}
// Return the directory name within the APK that is used for native code libraries
// on the given platform.
String getAbiDirectory(TargetPlatform platform) {
switch (platform) {
case TargetPlatform.android_arm:
return 'armeabi-v7a';
case TargetPlatform.android_x64:
return 'x86_64';
case TargetPlatform.android_x86:
return 'x86';
default:
throw new Exception('Unsupported platform.');
}
}
Future<_ApkComponents> _findApkComponents(
TargetPlatform platform,
BuildMode buildMode,
String manifest,
String resources,
Map<String, File> extraFiles
) async {
final _ApkComponents components = new _ApkComponents();
components.manifest = fs.file(manifest);
components.resources = resources == null ? null : fs.directory(resources);
components.extraFiles = extraFiles != null ? extraFiles : <String, File>{};
components.icuData = fs.file(artifacts.getArtifactPath(Artifact.icudtlDat, platform, buildMode));
components.jars = <File>[
fs.file(artifacts.getArtifactPath(Artifact.classesDexJar, platform, buildMode))
];
components.libSkyShell = fs.file(artifacts.getArtifactPath(Artifact.libskyShellSo, platform, buildMode));
components.debugKeystore = fs.file(artifacts.getArtifactPath(Artifact.chromiumDebugKeyStore, platform, buildMode));
await parseServiceConfigs(components.services, jars: components.jars);
final List<File> allFiles = <File>[
components.manifest, components.icuData, components.libSkyShell, components.debugKeystore
]..addAll(components.jars)
..addAll(components.extraFiles.values);
for (File file in allFiles) {
if (!file.existsSync()) {
printError('Cannot locate file: ${file.path}');
return null;
}
}
return components;
}
int _buildApk(
TargetPlatform platform,
BuildMode buildMode,
_ApkComponents components,
String flxPath,
ApkKeystoreInfo keystore,
String outputFile
) {
assert(platform != null);
assert(buildMode != null);
final Directory tempDir = fs.systemTempDirectory.createTempSync('flutter_tools');
printTrace('Building APK; buildMode: ${getModeName(buildMode)}.');
try {
final _ApkBuilder builder = new _ApkBuilder(androidSdk.latestVersion);
final String error = builder.checkDependencies();
if (error != null) {
printError(error);
return 1;
}
final File classesDex = fs.file('${tempDir.path}/classes.dex');
builder.compileClassesDex(classesDex, components.jars);
final File servicesConfig =
generateServiceDefinitions(tempDir.path, components.services);
final _AssetBuilder assetBuilder = new _AssetBuilder(tempDir, 'assets');
assetBuilder.add(components.icuData, 'icudtl.dat');
assetBuilder.add(fs.file(flxPath), 'app.flx');
assetBuilder.add(servicesConfig, 'services.json');
final _AssetBuilder artifactBuilder = new _AssetBuilder(tempDir, 'artifacts');
artifactBuilder.add(classesDex, 'classes.dex');
final String abiDir = getAbiDirectory(platform);
artifactBuilder.add(components.libSkyShell, 'lib/$abiDir/libsky_shell.so');
for (String relativePath in components.extraFiles.keys)
artifactBuilder.add(components.extraFiles[relativePath], relativePath);
final File unalignedApk = fs.file('${tempDir.path}/app.apk.unaligned');
builder.package(
unalignedApk, components.manifest, assetBuilder.directory,
artifactBuilder.directory, components.resources, buildMode
);
final File finalApk = fs.file(outputFile);
ensureDirectoryExists(finalApk.path);
builder.align(unalignedApk, finalApk);
final int signResult = _signApk(builder, components, finalApk, keystore, buildMode);
if (signResult != 0)
return signResult;
printTrace('calculateSha: $outputFile');
final File apkShaFile = fs.file('$outputFile.sha1');
apkShaFile.writeAsStringSync(calculateSha(finalApk));
return 0;
} finally {
tempDir.deleteSync(recursive: true);
}
}
int _signApk(
_ApkBuilder builder,
_ApkComponents components,
File apk,
ApkKeystoreInfo keystoreInfo,
BuildMode buildMode,
) {
File keystore;
String keystorePassword;
String keyAlias;
String keyPassword;
if (keystoreInfo == null) {
if (buildMode == BuildMode.release) {
printStatus('Warning! Signing the APK using the debug keystore.');
printStatus('You will need a real keystore to distribute your application.');
} else {
printTrace('Signing the APK using the debug keystore.');
}
keystore = components.debugKeystore;
keystorePassword = _kDebugKeystorePassword;
keyAlias = _kDebugKeystoreKeyAlias;
keyPassword = _kDebugKeystorePassword;
} else {
keystore = fs.file(keystoreInfo.keystore);
keystorePassword = keystoreInfo.password ?? '';
keyAlias = keystoreInfo.keyAlias ?? '';
if (keystorePassword.isEmpty || keyAlias.isEmpty) {
printError('You must provide a keystore password and a key alias.');
return 1;
}
keyPassword = keystoreInfo.keyPassword ?? '';
if (keyPassword.isEmpty)
keyPassword = keystorePassword;
await buildApk(targetPlatform, buildMode: buildMode, target: targetFile);
}
builder.sign(keystore, keystorePassword, keyAlias, keyPassword, apk);
return 0;
}
// Returns true if the apk is out of date and needs to be rebuilt.
bool _needsRebuild(
String apkPath,
String manifest,
TargetPlatform platform,
BuildMode buildMode,
Map<String, File> extraFiles
) {
final FileStat apkStat = fs.statSync(apkPath);
// Note: This list of dependencies is imperfect, but will do for now. We
// purposely don't include the .dart files, because we can load those
// over the network without needing to rebuild (at least on Android).
final List<String> dependencies = <String>[
manifest,
_kFlutterManifestPath,
_kPackagesStatusPath
];
dependencies.addAll(extraFiles.values.map((File file) => file.path));
final Iterable<FileStat> dependenciesStat =
dependencies.map((String path) => fs.statSync(path));
if (apkStat.type == FileSystemEntityType.NOT_FOUND)
return true;
for (FileStat dep in dependenciesStat) {
if (dep.modified == null || dep.modified.isAfter(apkStat.modified))
return true;
}
if (!fs.isFileSync('$apkPath.sha1'))
return true;
final String lastBuildType = _readBuildMeta(fs.path.dirname(apkPath))['targetBuildType'];
final String targetBuildType = _getTargetBuildTypeToken(platform, buildMode, fs.file(apkPath));
if (lastBuildType != targetBuildType)
return true;
return false;
}
Future<Null> buildAndroid(
TargetPlatform platform,
BuildMode buildMode, {
bool force: false,
String manifest: _kDefaultAndroidManifestPath,
String resources,
String outputFile,
Future<Null> buildApk(
TargetPlatform platform, {
String target,
String flxPath,
String aotPath,
DevFSContent kernelContent,
ApkKeystoreInfo keystore,
bool applicationNeedsRebuild: false
BuildMode buildMode: BuildMode.debug,
String kernelPath,
}) async {
outputFile ??= _defaultOutputPath;
// Validate that we can find an android sdk.
if (androidSdk == null)
throwToolExit('No Android SDK found. Try setting the ANDROID_HOME environment variable.');
final List<String> validationResult = androidSdk.validateSdkWellFormed();
if (validationResult.isNotEmpty) {
validationResult.forEach(printError);
throwToolExit('Try re-installing or updating your Android SDK.');
}
final Map<String, File> extraFiles = <String, File>{};
if (fs.isDirectorySync(_kDefaultAssetsPath)) {
final Directory assetsDir = fs.directory(_kDefaultAssetsPath);
for (FileSystemEntity entity in assetsDir.listSync(recursive: true)) {
if (entity is File) {
final String targetPath = entity.path.substring(assetsDir.path.length);
extraFiles["assets/$targetPath"] = entity;
}
}
}
final bool needRebuild =
applicationNeedsRebuild ||
_needsRebuild(outputFile, manifest, platform, buildMode, extraFiles);
// In debug (JIT) mode, the snapshot lives in the FLX, and we can skip the APK
// rebuild if none of the resources in the APK are stale.
// In AOT modes, the snapshot lives in the APK, so the APK must be rebuilt.
if (!isAotBuildMode(buildMode) && !force && !needRebuild) {
printTrace('APK up to date; skipping build step.');
return;
}
if (resources != null) {
if (!fs.isDirectorySync(resources))
throwToolExit('Resources directory "$resources" not found.');
} else {
if (fs.isDirectorySync(_kDefaultResourcesPath))
resources = _kDefaultResourcesPath;
}
final _ApkComponents components = await _findApkComponents(platform, buildMode, manifest, resources, extraFiles);
if (components == null)
throwToolExit('Failure building APK: unable to find components.');
final String typeName = artifacts.getEngineType(platform, buildMode);
final Status status = logger.startProgress('Building APK in ${getModeName(buildMode)} mode ($typeName)...',
expectSlowOperation: true);
if (flxPath != null && flxPath.isNotEmpty) {
if (!fs.isFileSync(flxPath)) {
throwToolExit('FLX does not exist: $flxPath\n'
'(Omit the --flx option to build the FLX automatically)');
}
} else {
// Build the FLX.
flxPath = await flx.buildFlx(
mainPath: findMainDartFile(target),
kernelContent: kernelContent,
precompiledSnapshot: isAotBuildMode(buildMode),
includeRobotoFonts: false);
if (flxPath == null)
throwToolExit(null);
}
// Build an AOT snapshot if needed.
if (isAotBuildMode(buildMode) && aotPath == null) {
aotPath = await buildAotSnapshot(findMainDartFile(target), platform, buildMode);
if (aotPath == null)
throwToolExit('Failed to build AOT snapshot');
}
if (aotPath != null) {
if (!isAotBuildMode(buildMode))
throwToolExit('AOT snapshot can not be used in build mode $buildMode');
if (!fs.isDirectorySync(aotPath))
throwToolExit('AOT snapshot does not exist: $aotPath');
for (String aotFilename in kAotSnapshotFiles) {
final String aotFilePath = fs.path.join(aotPath, aotFilename);
if (!fs.isFileSync(aotFilePath))
throwToolExit('Missing AOT snapshot file: $aotFilePath');
components.extraFiles['assets/$aotFilename'] = fs.file(aotFilePath);
}
if (!isProjectUsingGradle()) {
throwToolExit(
'The build process for Android has changed, and the current project configuration\n'
'is no longer valid. Please consult\n\n'
' https://github.com/flutter/flutter/wiki/Upgrading-Flutter-projects-to-build-with-gradle\n\n'
'for details on how to upgrade the project.'
);
}
final int result = _buildApk(platform, buildMode, components, flxPath, keystore, outputFile);
status.stop();
if (result != 0)
throwToolExit('Build APK failed ($result)', exitCode: result);
final File apkFile = fs.file(outputFile);
printTrace('Built $outputFile (${getSizeAsMB(apkFile.lengthSync())}).');
_writeBuildMetaEntry(
fs.path.dirname(outputFile),
'targetBuildType',
_getTargetBuildTypeToken(platform, buildMode, fs.file(outputFile))
);
}
Future<Null> buildAndroidWithGradle(
TargetPlatform platform,
BuildMode buildMode, {
bool force: false,
String target
}) async {
if (platform != TargetPlatform.android_arm && buildMode != BuildMode.debug) {
throwToolExit('Profile and release builds are only supported on ARM targets.');
}
......@@ -589,59 +96,5 @@ Future<Null> buildAndroidWithGradle(
throwToolExit('Try re-installing or updating your Android SDK.');
}
return buildGradleProject(buildMode, target);
}
Future<Null> buildApk(
TargetPlatform platform, {
String target,
BuildMode buildMode: BuildMode.debug,
DevFSContent kernelContent,
bool applicationNeedsRebuild: false,
}) async {
if (isProjectUsingGradle()) {
return await buildAndroidWithGradle(
platform,
buildMode,
force: false,
target: target
);
} else {
if (!fs.isFileSync(_kDefaultAndroidManifestPath))
throwToolExit('Cannot build APK: missing $_kDefaultAndroidManifestPath.');
return await buildAndroid(
platform,
buildMode,
force: false,
target: target,
kernelContent: kernelContent,
applicationNeedsRebuild: applicationNeedsRebuild,
);
}
}
Map<String, dynamic> _readBuildMeta(String buildDirectoryPath) {
final File buildMetaFile = fs.file(fs.path.join(buildDirectoryPath, 'build_meta.json'));
if (buildMetaFile.existsSync())
return JSON.decode(buildMetaFile.readAsStringSync());
return <String, dynamic>{};
}
void _writeBuildMetaEntry(String buildDirectoryPath, String key, dynamic value) {
final Map<String, dynamic> meta = _readBuildMeta(buildDirectoryPath);
meta[key] = value;
final File buildMetaFile = fs.file(fs.path.join(buildDirectoryPath, 'build_meta.json'));
buildMetaFile.writeAsStringSync(toPrettyJson(meta));
}
String _getTargetBuildTypeToken(TargetPlatform platform, BuildMode buildMode, File outputBinary) {
String buildType = getNameForTargetPlatform(platform) + '-' + getModeName(buildMode);
if (artifacts is LocalEngineArtifacts) {
final LocalEngineArtifacts localEngineArtifacts = artifacts;
buildType += ' [${localEngineArtifacts.engineOutPath}]';
}
if (outputBinary.existsSync())
buildType += ' [${outputBinary.lastModifiedSync().millisecondsSinceEpoch}]';
return buildType;
return buildGradleProject(buildMode, target, kernelPath);
}
......@@ -20,6 +20,7 @@ class BuildFlxCommand extends BuildSubCommand {
argParser.addOption('output-file', abbr: 'o', defaultsTo: defaultFlxOutputPath);
argParser.addOption('snapshot', defaultsTo: defaultSnapshotPath);
argParser.addOption('depfile', defaultsTo: defaultDepfilePath);
argParser.addOption('kernel');
argParser.addOption('working-dir', defaultsTo: getAssetBuildDirectory());
argParser.addFlag('include-roboto-fonts', defaultsTo: true);
argParser.addFlag('report-licensed-packages', help: 'Whether to report the names of all the packages that are included in the application\'s LICENSE file.', defaultsTo: false);
......@@ -49,6 +50,7 @@ class BuildFlxCommand extends BuildSubCommand {
depfilePath: argResults['depfile'],
privateKeyPath: argResults['private-key'],
workingDirPath: argResults['working-dir'],
kernelPath: argResults['kernel'],
precompiledSnapshot: argResults['precompiled'],
includeRobotoFonts: argResults['include-roboto-fonts'],
reportLicensedPackages: argResults['report-licensed-packages']
......
......@@ -13,7 +13,6 @@ import 'base/file_system.dart';
import 'base/port_scanner.dart';
import 'base/utils.dart';
import 'build_info.dart';
import 'devfs.dart';
import 'globals.dart';
import 'ios/devices.dart';
import 'ios/simulators.dart';
......@@ -209,7 +208,7 @@ abstract class Device {
String route,
DebuggingOptions debuggingOptions,
Map<String, dynamic> platformArgs,
DevFSContent kernelContent,
String kernelPath,
bool prebuiltApplication: false,
bool applicationNeedsRebuild: false
});
......
......@@ -21,7 +21,6 @@ const String defaultManifestPath = 'pubspec.yaml';
String get defaultFlxOutputPath => fs.path.join(getBuildDirectory(), 'app.flx');
String get defaultSnapshotPath => fs.path.join(getBuildDirectory(), 'snapshot_blob.bin');
String get defaultDepfilePath => fs.path.join(getBuildDirectory(), 'snapshot_blob.bin.d');
String get defaultKernelPath => fs.path.join(getBuildDirectory(), 'kernel_blob.bin');
const String defaultPrivateKeyPath = 'privatekey.der';
const String _kKernelKey = 'kernel_blob.bin';
......@@ -56,26 +55,6 @@ Future<int> createSnapshot({
return runCommandAndStreamOutput(args);
}
/// Build the flx in the build directory and return `localBundlePath` on success.
///
/// Return `null` on failure.
Future<String> buildFlx({
String mainPath: defaultMainPath,
DevFSContent kernelContent,
bool precompiledSnapshot: false,
bool includeRobotoFonts: true
}) async {
await build(
snapshotPath: defaultSnapshotPath,
outputPath: defaultFlxOutputPath,
mainPath: mainPath,
kernelContent: kernelContent,
precompiledSnapshot: precompiledSnapshot,
includeRobotoFonts: includeRobotoFonts
);
return defaultFlxOutputPath;
}
Future<Null> build({
String mainPath: defaultMainPath,
String manifestPath: defaultManifestPath,
......@@ -86,27 +65,17 @@ Future<Null> build({
String workingDirPath,
String packagesPath,
String kernelPath,
DevFSContent kernelContent,
bool precompiledSnapshot: false,
bool includeRobotoFonts: true,
bool reportLicensedPackages: false
}) async {
outputPath ??= defaultFlxOutputPath;
kernelPath ??= defaultKernelPath;
snapshotPath ??= defaultSnapshotPath;
depfilePath ??= defaultDepfilePath;
workingDirPath ??= getAssetBuildDirectory();
packagesPath ??= fs.path.absolute(PackageMap.globalPackagesPath);
File snapshotFile;
File kernelFile;
if (kernelContent != null) {
// TODO(danrubel) in the future, call the VM to generate this file
kernelFile = fs.file(kernelPath);
final IOSink sink = kernelFile.openWrite();
await sink.addStream(kernelContent.contentsAsStream());
sink.close();
}
if (!precompiledSnapshot) {
ensureDirectoryExists(snapshotPath);
......@@ -124,9 +93,13 @@ Future<Null> build({
snapshotFile = fs.file(snapshotPath);
}
DevFSContent kernelContent;
if (kernelPath != null)
kernelContent = new DevFSFileContent(fs.file(kernelPath));
return assemble(
manifestPath: manifestPath,
kernelFile: kernelFile,
kernelContent: kernelContent,
snapshotFile: snapshotFile,
outputPath: outputPath,
privateKeyPath: privateKeyPath,
......@@ -139,7 +112,7 @@ Future<Null> build({
Future<Null> assemble({
String manifestPath,
File kernelFile,
DevFSContent kernelContent,
File snapshotFile,
String outputPath,
String privateKeyPath: defaultPrivateKeyPath,
......@@ -172,8 +145,8 @@ Future<Null> assemble({
// Add all entries from the asset bundle.
zipBuilder.entries.addAll(assetBundle.entries);
if (kernelFile != null)
zipBuilder.entries[_kKernelKey] = new DevFSFileContent(kernelFile);
if (kernelContent != null)
zipBuilder.entries[_kKernelKey] = kernelContent;
if (snapshotFile != null)
zipBuilder.entries[_kSnapshotKey] = new DevFSFileContent(snapshotFile);
......
......@@ -6,7 +6,6 @@ import 'dart:async';
import '../application_package.dart';
import '../build_info.dart';
import '../devfs.dart';
import '../device.dart';
/// Read the log for a particular device.
......@@ -67,7 +66,7 @@ class FuchsiaDevice extends Device {
DebuggingOptions debuggingOptions,
Map<String, dynamic> platformArgs,
bool prebuiltApplication: false,
DevFSContent kernelContent,
String kernelPath,
bool applicationNeedsRebuild: false,
}) => new Future<Null>.error('unimplemented');
......
......@@ -13,7 +13,6 @@ import '../base/port_scanner.dart';
import '../base/process.dart';
import '../base/process_manager.dart';
import '../build_info.dart';
import '../devfs.dart';
import '../device.dart';
import '../doctor.dart';
import '../globals.dart';
......@@ -194,7 +193,7 @@ class IOSDevice extends Device {
DebuggingOptions debuggingOptions,
Map<String, dynamic> platformArgs,
bool prebuiltApplication: false,
DevFSContent kernelContent,
String kernelPath,
bool applicationNeedsRebuild: false,
}) async {
if (!prebuiltApplication) {
......
......@@ -15,7 +15,6 @@ import '../base/platform.dart';
import '../base/process.dart';
import '../base/process_manager.dart';
import '../build_info.dart';
import '../devfs.dart';
import '../device.dart';
import '../flx.dart' as flx;
import '../globals.dart';
......@@ -422,7 +421,7 @@ class IOSSimulator extends Device {
String route,
DebuggingOptions debuggingOptions,
Map<String, dynamic> platformArgs,
DevFSContent kernelContent,
String kernelPath,
bool prebuiltApplication: false,
bool applicationNeedsRebuild: false,
}) async {
......
......@@ -196,11 +196,6 @@ class HotRunner extends ResidentRunner {
final String modeName = getModeName(debuggingOptions.buildMode);
printStatus('Launching ${getDisplayPath(mainPath)} on ${device.name} in $modeName mode...');
// Include kernel code
DevFSContent kernelContent;
if (kernelFilePath != null)
kernelContent = new DevFSFileContent(fs.file(kernelFilePath));
// Start the application.
final Future<LaunchResult> futureResult = device.startApp(
package,
......@@ -210,7 +205,7 @@ class HotRunner extends ResidentRunner {
platformArgs: platformArgs,
route: route,
prebuiltApplication: prebuiltMode,
kernelContent: kernelContent,
kernelPath: kernelFilePath,
applicationNeedsRebuild: shouldBuild || hasDirtyDependencies()
);
......
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