Commit f33596aa authored by Adam Barth's avatar Adam Barth

Teach flutter to build app.a (#4141)

On iOS, we use Xcode to build a static library that contains the precompiled
code. This code is currently unused, but it will be used by the new Xcode
harness to factor out as much complexity as possible into the flutter tool.
parent ff1d3b81
...@@ -38,6 +38,7 @@ class BuildAotCommand extends FlutterCommand { ...@@ -38,6 +38,7 @@ class BuildAotCommand extends FlutterCommand {
Future<int> runInProject() async { Future<int> runInProject() async {
String outputPath = buildAotSnapshot( String outputPath = buildAotSnapshot(
findMainDartFile(argResults['target']), findMainDartFile(argResults['target']),
TargetPlatform.android_arm,
getBuildMode(), getBuildMode(),
outputPath: argResults['output-dir'] outputPath: argResults['output-dir']
); );
...@@ -56,6 +57,7 @@ String _getSdkExtensionPath(String packagesPath, String package) { ...@@ -56,6 +57,7 @@ String _getSdkExtensionPath(String packagesPath, String package) {
String buildAotSnapshot( String buildAotSnapshot(
String mainPath, String mainPath,
TargetPlatform platform,
BuildMode buildMode, { BuildMode buildMode, {
String outputPath: _kDefaultAotOutputDir String outputPath: _kDefaultAotOutputDir
}) { }) {
...@@ -69,14 +71,15 @@ String buildAotSnapshot( ...@@ -69,14 +71,15 @@ String buildAotSnapshot(
String engineSrc = tools.engineSrcPath; String engineSrc = tools.engineSrcPath;
if (engineSrc != null) { if (engineSrc != null) {
entryPointsDir = path.join(engineSrc, 'sky', 'engine', 'bindings'); entryPointsDir = path.join(engineSrc, 'sky', 'engine', 'bindings');
String engineOut = tools.getEngineArtifactsDirectory( String engineOut = tools.getEngineArtifactsDirectory(platform, buildMode).path;
TargetPlatform.android_arm, buildMode).path; if (platform == TargetPlatform.ios) {
genSnapshot = path.join(engineOut, 'clang_x64', 'gen_snapshot');
} else {
String host32BitToolchain = getCurrentHostPlatform() == HostPlatform.darwin_x64 ? 'clang_i386' : 'clang_x86'; String host32BitToolchain = getCurrentHostPlatform() == HostPlatform.darwin_x64 ? 'clang_i386' : 'clang_x86';
genSnapshot = path.join(engineOut, host32BitToolchain, 'gen_snapshot'); genSnapshot = path.join(engineOut, host32BitToolchain, 'gen_snapshot');
}
} else { } else {
String artifactsDir = tools.getEngineArtifactsDirectory( String artifactsDir = tools.getEngineArtifactsDirectory(platform, buildMode).path;
TargetPlatform.android_arm, buildMode).path;
entryPointsDir = artifactsDir; entryPointsDir = artifactsDir;
String hostToolsDir = path.join(artifactsDir, getNameForHostPlatform(getCurrentHostPlatform())); String hostToolsDir = path.join(artifactsDir, getNameForHostPlatform(getCurrentHostPlatform()));
genSnapshot = path.join(hostToolsDir, 'gen_snapshot'); genSnapshot = path.join(hostToolsDir, 'gen_snapshot');
...@@ -90,7 +93,6 @@ String buildAotSnapshot( ...@@ -90,7 +93,6 @@ String buildAotSnapshot(
String rodataBlob = path.join(outputDir.path, 'snapshot_aot_rodata'); String rodataBlob = path.join(outputDir.path, 'snapshot_aot_rodata');
String vmEntryPoints = path.join(entryPointsDir, 'dart_vm_entry_points.txt'); String vmEntryPoints = path.join(entryPointsDir, 'dart_vm_entry_points.txt');
String vmEntryPointsAndroid = path.join(entryPointsDir, 'dart_vm_entry_points_android.txt');
String packagesPath = path.absolute(Directory.current.path, 'packages'); String packagesPath = path.absolute(Directory.current.path, 'packages');
if (!FileSystemEntity.isDirectorySync(packagesPath)) { if (!FileSystemEntity.isDirectorySync(packagesPath)) {
...@@ -107,11 +109,46 @@ String buildAotSnapshot( ...@@ -107,11 +109,46 @@ String buildAotSnapshot(
String skyEngineSdkExt = _getSdkExtensionPath(packagesPath, 'sky_engine'); String skyEngineSdkExt = _getSdkExtensionPath(packagesPath, 'sky_engine');
String uiPath = path.join(skyEngineSdkExt, 'dart_ui.dart'); String uiPath = path.join(skyEngineSdkExt, 'dart_ui.dart');
String vmServicePath = path.join(skyEngineSdkExt, 'dart', 'runtime', 'bin', 'vmservice', 'vmservice_io.dart'); String vmServicePath = path.join(skyEngineSdkExt, 'dart', 'runtime', 'bin', 'vmservice', 'vmservice_io.dart');
String jniPath = path.join(skyEngineSdkExt, 'dart_jni', 'jni.dart');
List<String> filePaths = <String>[ List<String> filePaths = <String>[
genSnapshot, vmEntryPoints, vmEntryPointsAndroid, mojoInternalPath, uiPath, vmServicePath, jniPath genSnapshot,
vmEntryPoints,
mojoInternalPath,
uiPath,
vmServicePath,
]; ];
// These paths are used only on Android.
String vmEntryPointsAndroid;
String jniPathAndroid;
// These paths are used only on iOS.
String snapshotDartIOS;
String assembly;
switch (platform) {
case TargetPlatform.android_arm:
case TargetPlatform.android_x64:
case TargetPlatform.android_x86:
vmEntryPointsAndroid = path.join(entryPointsDir, 'dart_vm_entry_points_android.txt');
jniPathAndroid = path.join(skyEngineSdkExt, 'dart_jni', 'jni.dart');
filePaths.addAll(<String>[
vmEntryPointsAndroid,
jniPathAndroid,
]);
break;
case TargetPlatform.ios:
snapshotDartIOS = path.join(entryPointsDir, 'snapshot.dart');
assembly = path.join(outputDir.path, 'snapshot_assembly.S');
filePaths.addAll(<String>[
snapshotDartIOS,
]);
break;
case TargetPlatform.darwin_x64:
case TargetPlatform.linux_x64:
assert(false);
}
List<String> missingFiles = filePaths.where((String p) => !FileSystemEntity.isFileSync(p)).toList(); List<String> missingFiles = filePaths.where((String p) => !FileSystemEntity.isFileSync(p)).toList();
if (missingFiles.isNotEmpty) { if (missingFiles.isNotEmpty) {
printError('Missing files: $missingFiles'); printError('Missing files: $missingFiles');
...@@ -122,18 +159,35 @@ String buildAotSnapshot( ...@@ -122,18 +159,35 @@ String buildAotSnapshot(
genSnapshot, genSnapshot,
'--vm_isolate_snapshot=$vmIsolateSnapshot', '--vm_isolate_snapshot=$vmIsolateSnapshot',
'--isolate_snapshot=$isolateSnapshot', '--isolate_snapshot=$isolateSnapshot',
'--instructions_blob=$instructionsBlob',
'--rodata_blob=$rodataBlob',
'--embedder_entry_points_manifest=$vmEntryPoints', '--embedder_entry_points_manifest=$vmEntryPoints',
'--embedder_entry_points_manifest=$vmEntryPointsAndroid',
'--package_root=$packagesPath', '--package_root=$packagesPath',
'--url_mapping=dart:mojo.internal,$mojoInternalPath', '--url_mapping=dart:mojo.internal,$mojoInternalPath',
'--url_mapping=dart:ui,$uiPath', '--url_mapping=dart:ui,$uiPath',
'--url_mapping=dart:vmservice_sky,$vmServicePath', '--url_mapping=dart:vmservice_sky,$vmServicePath',
'--url_mapping=dart:jni,$jniPath',
'--no-sim-use-hardfp',
]; ];
switch (platform) {
case TargetPlatform.android_arm:
case TargetPlatform.android_x64:
case TargetPlatform.android_x86:
genSnapshotCmd.addAll(<String>[
'--rodata_blob=$rodataBlob',
'--instructions_blob=$instructionsBlob',
'--embedder_entry_points_manifest=$vmEntryPointsAndroid',
'--url_mapping=dart:jni,$jniPathAndroid',
'--no-sim-use-hardfp',
]);
break;
case TargetPlatform.ios:
genSnapshotCmd.addAll(<String>[
'--assembly=$assembly'
]);
break;
case TargetPlatform.darwin_x64:
case TargetPlatform.linux_x64:
assert(false);
}
if (buildMode != BuildMode.release) { if (buildMode != BuildMode.release) {
genSnapshotCmd.addAll(<String>[ genSnapshotCmd.addAll(<String>[
'--no-checked', '--no-checked',
...@@ -146,5 +200,44 @@ String buildAotSnapshot( ...@@ -146,5 +200,44 @@ String buildAotSnapshot(
printStatus('Building snapshot...'); printStatus('Building snapshot...');
runCheckedSync(genSnapshotCmd); runCheckedSync(genSnapshotCmd);
// On iOS, we use Xcode to compile the snapshot into a static library that the
// end-developer can link into their app.
if (platform == TargetPlatform.ios) {
printStatus('Building app.a...');
// These names are known to from the engine.
const String kDartVmIsolateSnapshotBuffer = 'kDartVmIsolateSnapshotBuffer';
const String kDartIsolateSnapshotBuffer = 'kDartIsolateSnapshotBuffer';
runCheckedSync(<String>['mv', vmIsolateSnapshot, path.join(outputDir.path, kDartVmIsolateSnapshotBuffer)]);
runCheckedSync(<String>['mv', isolateSnapshot, path.join(outputDir.path, kDartIsolateSnapshotBuffer)]);
String kDartVmIsolateSnapshotBufferC = path.join(outputDir.path, '$kDartVmIsolateSnapshotBuffer.c');
String kDartIsolateSnapshotBufferC = path.join(outputDir.path, '$kDartIsolateSnapshotBuffer.c');
runCheckedSync(<String>[
'xxd', '--include', kDartVmIsolateSnapshotBuffer, path.basename(kDartVmIsolateSnapshotBufferC)
], workingDirectory: outputDir.path);
runCheckedSync(<String>[
'xxd', '--include', kDartIsolateSnapshotBuffer, path.basename(kDartIsolateSnapshotBufferC)
], workingDirectory: outputDir.path);
String assemblyO = path.join(outputDir.path, 'snapshot_assembly.o');
String kDartVmIsolateSnapshotBufferO = path.join(outputDir.path, '$kDartVmIsolateSnapshotBuffer.o');
String kDartIsolateSnapshotBufferO = path.join(outputDir.path, '$kDartIsolateSnapshotBuffer.o');
runCheckedSync(<String>['xcrun', 'cc', '-c', assembly, '-o', assemblyO]);
runCheckedSync(<String>['xcrun', 'cc', '-c', kDartVmIsolateSnapshotBufferC, '-o', kDartVmIsolateSnapshotBufferO]);
runCheckedSync(<String>['xcrun', 'cc', '-c', kDartIsolateSnapshotBufferC, '-o', kDartIsolateSnapshotBufferO]);
String appLib = path.join(outputDir.path, 'app.a');
runCheckedSync(<String>['rm', '-f', appLib]);
runCheckedSync(<String>[
'xcrun', 'ar', 'rcs', appLib,
assemblyO, kDartVmIsolateSnapshotBufferO, kDartIsolateSnapshotBufferO,
]);
}
return outputPath; return outputPath;
} }
...@@ -513,7 +513,7 @@ Future<int> buildAndroid( ...@@ -513,7 +513,7 @@ Future<int> buildAndroid(
// Build an AOT snapshot if needed. // Build an AOT snapshot if needed.
if (isAotBuildMode(buildMode) && aotPath == null) { if (isAotBuildMode(buildMode) && aotPath == null) {
aotPath = buildAotSnapshot(findMainDartFile(target), buildMode); aotPath = buildAotSnapshot(findMainDartFile(target), platform, buildMode);
if (aotPath == null) { if (aotPath == null) {
printError('Failed to build AOT snapshot'); printError('Failed to build AOT snapshot');
return 1; return 1;
......
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