Unverified Commit 54c10f44 authored by Stanislav Baranov's avatar Stanislav Baranov Committed by GitHub

Implement build flow for hot updates on Android (#22391)

This also involves switching from Core JIT to App JIT snapshot, and replacing per-isolate VM snapshot with the shared VM snapshot.

For now there is no separate update bundle file, as the generated update gets packaged directly into the APK for testing purposes.
parent b65d3bce
...@@ -307,6 +307,10 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -307,6 +307,10 @@ class FlutterPlugin implements Plugin<Project> {
if (project.hasProperty('precompile')) { if (project.hasProperty('precompile')) {
compilationTraceFilePathValue = project.property('precompile') compilationTraceFilePathValue = project.property('precompile')
} }
Boolean buildHotUpdateValue = false
if (project.hasProperty('hotupdate')) {
buildHotUpdateValue = project.property('hotupdate').toBoolean()
}
String extraFrontEndOptionsValue = null String extraFrontEndOptionsValue = null
if (project.hasProperty('extra-front-end-options')) { if (project.hasProperty('extra-front-end-options')) {
extraFrontEndOptionsValue = project.property('extra-front-end-options') extraFrontEndOptionsValue = project.property('extra-front-end-options')
...@@ -349,6 +353,7 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -349,6 +353,7 @@ class FlutterPlugin implements Plugin<Project> {
fileSystemScheme fileSystemSchemeValue fileSystemScheme fileSystemSchemeValue
trackWidgetCreation trackWidgetCreationValue trackWidgetCreation trackWidgetCreationValue
compilationTraceFilePath compilationTraceFilePathValue compilationTraceFilePath compilationTraceFilePathValue
buildHotUpdate buildHotUpdateValue
buildSharedLibrary buildSharedLibraryValue buildSharedLibrary buildSharedLibraryValue
targetPlatform targetPlatformValue targetPlatform targetPlatformValue
sourceDir project.file(project.flutter.source) sourceDir project.file(project.flutter.source)
...@@ -398,6 +403,8 @@ abstract class BaseFlutterTask extends DefaultTask { ...@@ -398,6 +403,8 @@ abstract class BaseFlutterTask extends DefaultTask {
@Optional @Input @Optional @Input
String compilationTraceFilePath String compilationTraceFilePath
@Optional @Input @Optional @Input
Boolean buildHotUpdate
@Optional @Input
Boolean buildSharedLibrary Boolean buildSharedLibrary
@Optional @Input @Optional @Input
String targetPlatform String targetPlatform
...@@ -491,6 +498,9 @@ abstract class BaseFlutterTask extends DefaultTask { ...@@ -491,6 +498,9 @@ abstract class BaseFlutterTask extends DefaultTask {
if (compilationTraceFilePath != null) { if (compilationTraceFilePath != null) {
args "--precompile", compilationTraceFilePath args "--precompile", compilationTraceFilePath
} }
if (buildHotUpdate) {
args "--hotupdate"
}
if (extraFrontEndOptions != null) { if (extraFrontEndOptions != null) {
args "--extra-front-end-options", "${extraFrontEndOptions}" args "--extra-front-end-options", "${extraFrontEndOptions}"
} }
......
...@@ -365,6 +365,8 @@ Future<Null> _buildGradleProjectV2( ...@@ -365,6 +365,8 @@ Future<Null> _buildGradleProjectV2(
command.add('-Ptrack-widget-creation=true'); command.add('-Ptrack-widget-creation=true');
if (buildInfo.compilationTraceFilePath != null) if (buildInfo.compilationTraceFilePath != null)
command.add('-Pprecompile=${buildInfo.compilationTraceFilePath}'); command.add('-Pprecompile=${buildInfo.compilationTraceFilePath}');
if (buildInfo.buildHotUpdate)
command.add('-Photupdate=true');
if (buildInfo.extraFrontEndOptions != null) if (buildInfo.extraFrontEndOptions != null)
command.add('-Pextra-front-end-options=${buildInfo.extraFrontEndOptions}'); command.add('-Pextra-front-end-options=${buildInfo.extraFrontEndOptions}');
if (buildInfo.extraGenSnapshotOptions != null) if (buildInfo.extraGenSnapshotOptions != null)
......
...@@ -338,10 +338,9 @@ class AOTSnapshotter { ...@@ -338,10 +338,9 @@ class AOTSnapshotter {
} }
} }
class CoreJITSnapshotter { class JITSnapshotter {
/// Builds a "Core JIT" VM snapshot of the specified kernel. This snapshot /// Builds a JIT VM snapshot of the specified kernel. This snapshot includes
/// includes data as well as either machine code or DBC, depending on build /// data as well as either machine code or DBC, depending on build configuration.
/// configuration.
Future<int> build({ Future<int> build({
@required TargetPlatform platform, @required TargetPlatform platform,
@required BuildMode buildMode, @required BuildMode buildMode,
...@@ -349,18 +348,28 @@ class CoreJITSnapshotter { ...@@ -349,18 +348,28 @@ class CoreJITSnapshotter {
@required String packagesPath, @required String packagesPath,
@required String outputPath, @required String outputPath,
@required String compilationTraceFilePath, @required String compilationTraceFilePath,
@required bool buildHotUpdate,
List<String> extraGenSnapshotOptions = const <String>[], List<String> extraGenSnapshotOptions = const <String>[],
}) async { }) async {
if (!_isValidCoreJitPlatform(platform)) { if (!_isValidJitPlatform(platform)) {
printError('${getNameForTargetPlatform(platform)} does not support Core JIT compilation.'); printError('${getNameForTargetPlatform(platform)} does not support JIT snapshotting.');
return 1; return 1;
} }
final Directory outputDir = fs.directory(outputPath); final Directory outputDir = fs.directory(outputPath);
outputDir.createSync(recursive: true); outputDir.createSync(recursive: true);
final List<String> inputPaths = <String>[mainPath, compilationTraceFilePath]; final String engineVmSnapshotData = artifacts.getArtifactPath(Artifact.vmSnapshotData);
final Set<String> outputPaths = Set<String>(); final String engineIsolateSnapshotData = artifacts.getArtifactPath(Artifact.isolateSnapshotData);
final String isolateSnapshotData = fs.path.join(outputDir.path, 'isolate_snapshot_data');
final String isolateSnapshotInstructions = fs.path.join(outputDir.path, 'isolate_snapshot_instr');
final List<String> inputPaths = <String>[
mainPath, compilationTraceFilePath, engineVmSnapshotData, engineIsolateSnapshotData,
];
if (buildHotUpdate) {
inputPaths.add(isolateSnapshotInstructions);
}
final String depfilePath = fs.path.join(outputDir.path, 'snapshot.d'); final String depfilePath = fs.path.join(outputDir.path, 'snapshot.d');
final List<String> genSnapshotArgs = <String>[ final List<String> genSnapshotArgs = <String>[
...@@ -376,21 +385,26 @@ class CoreJITSnapshotter { ...@@ -376,21 +385,26 @@ class CoreJITSnapshotter {
genSnapshotArgs.addAll(extraGenSnapshotOptions); genSnapshotArgs.addAll(extraGenSnapshotOptions);
} }
// Blob Core JIT snapshot. final Set<String> outputPaths = Set<String>();
final String vmSnapshotData = fs.path.join(outputDir.path, 'vm_snapshot_data'); outputPaths.addAll(<String>[isolateSnapshotData]);
final String isolateSnapshotData = fs.path.join(outputDir.path, 'isolate_snapshot_data'); if (!buildHotUpdate) {
final String vmSnapshotInstructions = fs.path.join(outputDir.path, 'vm_snapshot_instr'); outputPaths.add(isolateSnapshotInstructions);
final String isolateSnapshotInstructions = fs.path.join(outputDir.path, 'isolate_snapshot_instr'); }
outputPaths.addAll(<String>[vmSnapshotData, isolateSnapshotData, vmSnapshotInstructions, isolateSnapshotInstructions]);
genSnapshotArgs.addAll(<String>[ genSnapshotArgs.addAll(<String>[
'--snapshot_kind=core-jit', '--snapshot_kind=app-jit',
'--vm_snapshot_data=$vmSnapshotData',
'--isolate_snapshot_data=$isolateSnapshotData',
'--vm_snapshot_instructions=$vmSnapshotInstructions',
'--isolate_snapshot_instructions=$isolateSnapshotInstructions',
'--load_compilation_trace=$compilationTraceFilePath', '--load_compilation_trace=$compilationTraceFilePath',
'--load_vm_snapshot_data=$engineVmSnapshotData',
'--load_isolate_snapshot_data=$engineIsolateSnapshotData',
'--isolate_snapshot_data=$isolateSnapshotData',
]); ]);
if (!buildHotUpdate) {
genSnapshotArgs.add('--isolate_snapshot_instructions=$isolateSnapshotInstructions');
} else {
genSnapshotArgs.add('--reused_instructions=$isolateSnapshotInstructions');
}
if (platform == TargetPlatform.android_arm) { if (platform == TargetPlatform.android_arm) {
// Use softfp for Android armv7 devices. // Use softfp for Android armv7 devices.
// TODO(cbracken): eliminate this when we fix https://github.com/flutter/flutter/issues/17489 // TODO(cbracken): eliminate this when we fix https://github.com/flutter/flutter/issues/17489
...@@ -417,12 +431,13 @@ class CoreJITSnapshotter { ...@@ -417,12 +431,13 @@ class CoreJITSnapshotter {
'buildMode': buildMode.toString(), 'buildMode': buildMode.toString(),
'targetPlatform': platform.toString(), 'targetPlatform': platform.toString(),
'entryPoint': mainPath, 'entryPoint': mainPath,
'buildHotUpdate': buildHotUpdate.toString(),
'extraGenSnapshotOptions': extraGenSnapshotOptions.join(' '), 'extraGenSnapshotOptions': extraGenSnapshotOptions.join(' '),
}, },
depfilePaths: <String>[], depfilePaths: <String>[],
); );
if (await fingerprinter.doesFingerprintMatch()) { if (await fingerprinter.doesFingerprintMatch()) {
printTrace('Skipping Core JIT snapshot build. Fingerprint match.'); printTrace('Skipping JIT snapshot build. Fingerprint match.');
return 0; return 0;
} }
...@@ -447,7 +462,7 @@ class CoreJITSnapshotter { ...@@ -447,7 +462,7 @@ class CoreJITSnapshotter {
return 0; return 0;
} }
bool _isValidCoreJitPlatform(TargetPlatform platform) { bool _isValidJitPlatform(TargetPlatform platform) {
return const <TargetPlatform>[ return const <TargetPlatform>[
TargetPlatform.android_arm, TargetPlatform.android_arm,
TargetPlatform.android_arm64, TargetPlatform.android_arm64,
......
...@@ -13,6 +13,7 @@ class BuildInfo { ...@@ -13,6 +13,7 @@ class BuildInfo {
const BuildInfo(this.mode, this.flavor, { const BuildInfo(this.mode, this.flavor, {
this.trackWidgetCreation = false, this.trackWidgetCreation = false,
this.compilationTraceFilePath, this.compilationTraceFilePath,
this.buildHotUpdate,
this.extraFrontEndOptions, this.extraFrontEndOptions,
this.extraGenSnapshotOptions, this.extraGenSnapshotOptions,
this.buildSharedLibrary, this.buildSharedLibrary,
...@@ -42,6 +43,9 @@ class BuildInfo { ...@@ -42,6 +43,9 @@ class BuildInfo {
/// Dart compilation trace file to use for JIT VM snapshot. /// Dart compilation trace file to use for JIT VM snapshot.
final String compilationTraceFilePath; final String compilationTraceFilePath;
/// Build differential snapshot.
final bool buildHotUpdate;
/// Extra command-line options for front-end. /// Extra command-line options for front-end.
final String extraFrontEndOptions; final String extraFrontEndOptions;
...@@ -97,6 +101,7 @@ class BuildInfo { ...@@ -97,6 +101,7 @@ class BuildInfo {
BuildInfo(mode, flavor, BuildInfo(mode, flavor,
trackWidgetCreation: trackWidgetCreation, trackWidgetCreation: trackWidgetCreation,
compilationTraceFilePath: compilationTraceFilePath, compilationTraceFilePath: compilationTraceFilePath,
buildHotUpdate: buildHotUpdate,
extraFrontEndOptions: extraFrontEndOptions, extraFrontEndOptions: extraFrontEndOptions,
extraGenSnapshotOptions: extraGenSnapshotOptions, extraGenSnapshotOptions: extraGenSnapshotOptions,
buildSharedLibrary: buildSharedLibrary, buildSharedLibrary: buildSharedLibrary,
......
...@@ -25,7 +25,6 @@ const String defaultPrivateKeyPath = 'privatekey.der'; ...@@ -25,7 +25,6 @@ const String defaultPrivateKeyPath = 'privatekey.der';
const String _kKernelKey = 'kernel_blob.bin'; const String _kKernelKey = 'kernel_blob.bin';
const String _kVMSnapshotData = 'vm_snapshot_data'; const String _kVMSnapshotData = 'vm_snapshot_data';
const String _kVMSnapshotInstr = 'vm_snapshot_instr';
const String _kIsolateSnapshotData = 'isolate_snapshot_data'; const String _kIsolateSnapshotData = 'isolate_snapshot_data';
const String _kIsolateSnapshotInstr = 'isolate_snapshot_instr'; const String _kIsolateSnapshotInstr = 'isolate_snapshot_instr';
const String _kDylibKey = 'libapp.so'; const String _kDylibKey = 'libapp.so';
...@@ -46,6 +45,7 @@ Future<void> build({ ...@@ -46,6 +45,7 @@ Future<void> build({
bool reportLicensedPackages = false, bool reportLicensedPackages = false,
bool trackWidgetCreation = false, bool trackWidgetCreation = false,
String compilationTraceFilePath, String compilationTraceFilePath,
bool buildHotUpdate = false,
List<String> extraFrontEndOptions = const <String>[], List<String> extraFrontEndOptions = const <String>[],
List<String> extraGenSnapshotOptions = const <String>[], List<String> extraGenSnapshotOptions = const <String>[],
List<String> fileSystemRoots, List<String> fileSystemRoots,
...@@ -85,7 +85,7 @@ Future<void> build({ ...@@ -85,7 +85,7 @@ Future<void> build({
.writeAsString('frontend_server.d: ${artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk)}\n'); .writeAsString('frontend_server.d: ${artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk)}\n');
if (compilationTraceFilePath != null) { if (compilationTraceFilePath != null) {
final CoreJITSnapshotter snapshotter = CoreJITSnapshotter(); final JITSnapshotter snapshotter = JITSnapshotter();
final int snapshotExitCode = await snapshotter.build( final int snapshotExitCode = await snapshotter.build(
platform: platform, platform: platform,
buildMode: buildMode, buildMode: buildMode,
...@@ -94,6 +94,7 @@ Future<void> build({ ...@@ -94,6 +94,7 @@ Future<void> build({
packagesPath: packagesPath, packagesPath: packagesPath,
compilationTraceFilePath: compilationTraceFilePath, compilationTraceFilePath: compilationTraceFilePath,
extraGenSnapshotOptions: extraGenSnapshotOptions, extraGenSnapshotOptions: extraGenSnapshotOptions,
buildHotUpdate: buildHotUpdate,
); );
if (snapshotExitCode != 0) { if (snapshotExitCode != 0) {
throwToolExit('Snapshotting exited with non-zero exit code: $snapshotExitCode'); throwToolExit('Snapshotting exited with non-zero exit code: $snapshotExitCode');
...@@ -160,12 +161,10 @@ Future<void> assemble({ ...@@ -160,12 +161,10 @@ Future<void> assemble({
final Map<String, DevFSContent> assetEntries = Map<String, DevFSContent>.from(assetBundle.entries); final Map<String, DevFSContent> assetEntries = Map<String, DevFSContent>.from(assetBundle.entries);
if (kernelContent != null) { if (kernelContent != null) {
if (compilationTraceFilePath != null) { if (compilationTraceFilePath != null) {
final String vmSnapshotData = fs.path.join(getBuildDirectory(), _kVMSnapshotData); final String vmSnapshotData = artifacts.getArtifactPath(Artifact.vmSnapshotData);
final String vmSnapshotInstr = fs.path.join(getBuildDirectory(), _kVMSnapshotInstr);
final String isolateSnapshotData = fs.path.join(getBuildDirectory(), _kIsolateSnapshotData); final String isolateSnapshotData = fs.path.join(getBuildDirectory(), _kIsolateSnapshotData);
final String isolateSnapshotInstr = fs.path.join(getBuildDirectory(), _kIsolateSnapshotInstr); final String isolateSnapshotInstr = fs.path.join(getBuildDirectory(), _kIsolateSnapshotInstr);
assetEntries[_kVMSnapshotData] = DevFSFileContent(fs.file(vmSnapshotData)); assetEntries[_kVMSnapshotData] = DevFSFileContent(fs.file(vmSnapshotData));
assetEntries[_kVMSnapshotInstr] = DevFSFileContent(fs.file(vmSnapshotInstr));
assetEntries[_kIsolateSnapshotData] = DevFSFileContent(fs.file(isolateSnapshotData)); assetEntries[_kIsolateSnapshotData] = DevFSFileContent(fs.file(isolateSnapshotData));
assetEntries[_kIsolateSnapshotInstr] = DevFSFileContent(fs.file(isolateSnapshotInstr)); assetEntries[_kIsolateSnapshotInstr] = DevFSFileContent(fs.file(isolateSnapshotInstr));
} else { } else {
......
...@@ -40,7 +40,15 @@ class BuildBundleCommand extends BuildSubCommand { ...@@ -40,7 +40,15 @@ class BuildBundleCommand extends BuildSubCommand {
'file produced by the training run of the application. With this\n' 'file produced by the training run of the application. With this\n'
'flag, instead of using default Dart VM snapshot provided by the\n' 'flag, instead of using default Dart VM snapshot provided by the\n'
'engine, the application will use its own snapshot that includes\n' 'engine, the application will use its own snapshot that includes\n'
'additional functions.' 'additional compiled functions.'
)
..addFlag('hotupdate',
hide: !verboseHelp,
help: 'Build differential snapshot based on the last state of the build\n'
'tree and any changes to the application source code since then.\n'
'This flag is only allowed when using --dynamic. With this flag,\n'
'a partial VM snapshot is generated that is loaded on top of the\n'
'original VM snapshot that contains precompiled code.'
) )
..addMultiOption(FlutterOptions.kExtraFrontEndOptions, ..addMultiOption(FlutterOptions.kExtraFrontEndOptions,
splitCommas: true, splitCommas: true,
...@@ -94,6 +102,7 @@ class BuildBundleCommand extends BuildSubCommand { ...@@ -94,6 +102,7 @@ class BuildBundleCommand extends BuildSubCommand {
reportLicensedPackages: argResults['report-licensed-packages'], reportLicensedPackages: argResults['report-licensed-packages'],
trackWidgetCreation: argResults['track-widget-creation'], trackWidgetCreation: argResults['track-widget-creation'],
compilationTraceFilePath: argResults['precompile'], compilationTraceFilePath: argResults['precompile'],
buildHotUpdate: argResults['hotupdate'],
extraFrontEndOptions: argResults[FlutterOptions.kExtraFrontEndOptions], extraFrontEndOptions: argResults[FlutterOptions.kExtraFrontEndOptions],
extraGenSnapshotOptions: argResults[FlutterOptions.kExtraGenSnapshotOptions], extraGenSnapshotOptions: argResults[FlutterOptions.kExtraGenSnapshotOptions],
fileSystemScheme: argResults['filesystem-scheme'], fileSystemScheme: argResults['filesystem-scheme'],
......
...@@ -129,6 +129,14 @@ class RunCommand extends RunCommandBase { ...@@ -129,6 +129,14 @@ class RunCommand extends RunCommandBase {
'engine, the application will use its own snapshot that includes\n' 'engine, the application will use its own snapshot that includes\n'
'additional functions.' 'additional functions.'
) )
..addFlag('hotupdate',
hide: !verboseHelp,
help: 'Build differential snapshot based on the last state of the build\n'
'tree and any changes to the application source code since then.\n'
'This flag is only allowed when using --dynamic. With this flag,\n'
'a partial VM snapshot is generated that is loaded on top of the\n'
'original VM snapshot that contains precompiled code.'
)
..addFlag('track-widget-creation', ..addFlag('track-widget-creation',
hide: !verboseHelp, hide: !verboseHelp,
help: 'Track widget creation locations. Requires Dart 2.0 functionality.', help: 'Track widget creation locations. Requires Dart 2.0 functionality.',
......
...@@ -243,6 +243,9 @@ abstract class FlutterCommand extends Command<Null> { ...@@ -243,6 +243,9 @@ abstract class FlutterCommand extends Command<Null> {
compilationTraceFilePath: argParser.options.containsKey('precompile') compilationTraceFilePath: argParser.options.containsKey('precompile')
? argResults['precompile'] ? argResults['precompile']
: null, : null,
buildHotUpdate: argParser.options.containsKey('hotupdate')
? argResults['hotupdate']
: false,
extraFrontEndOptions: argParser.options.containsKey(FlutterOptions.kExtraFrontEndOptions) extraFrontEndOptions: argParser.options.containsKey(FlutterOptions.kExtraFrontEndOptions)
? argResults[FlutterOptions.kExtraFrontEndOptions] ? argResults[FlutterOptions.kExtraFrontEndOptions]
: null, : null,
...@@ -495,10 +498,15 @@ abstract class FlutterCommand extends Command<Null> { ...@@ -495,10 +498,15 @@ abstract class FlutterCommand extends Command<Null> {
? argResults['dynamic'] : false; ? argResults['dynamic'] : false;
final String compilationTraceFilePath = argParser.options.containsKey('precompile') final String compilationTraceFilePath = argParser.options.containsKey('precompile')
? argResults['precompile'] : null; ? argResults['precompile'] : null;
final bool buildHotUpdate = argParser.options.containsKey('hotupdate')
? argResults['hotupdate'] : false;
if (compilationTraceFilePath != null && getBuildMode() == BuildMode.debug) if (compilationTraceFilePath != null && getBuildMode() == BuildMode.debug)
throw ToolExit('Error: --precompile is not allowed when --debug is specified.'); throw ToolExit('Error: --precompile is not allowed when --debug is specified.');
if (compilationTraceFilePath != null && !dynamicFlag) if (compilationTraceFilePath != null && !dynamicFlag)
throw ToolExit('Error: --precompile is allowed only when --dynamic is specified.'); throw ToolExit('Error: --precompile is allowed only when --dynamic is specified.');
if (buildHotUpdate && compilationTraceFilePath == null)
throw ToolExit('Error: --hotupdate is allowed only when --precompile is specified.');
} }
ApplicationPackageStore applicationPackages; ApplicationPackageStore applicationPackages;
......
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