blob: 408148a7ef2f307e4f5868a02b78f9c2e31266b1 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:io';
import 'package:path/path.dart' as path;
import '../base/process.dart';
import '../build_info.dart';
import '../globals.dart';
import '../runner/flutter_command.dart';
import 'run.dart';
const String _kDefaultAotOutputDir = 'build/aot';
// Files generated by the ahead-of-time snapshot builder.
const List<String> kAotSnapshotFiles = const <String>[
'snapshot_aot_instr', 'snapshot_aot_isolate', 'snapshot_aot_rodata', 'snapshot_aot_vmisolate',
];
class BuildAotCommand extends FlutterCommand {
BuildAotCommand() {
usesTargetOption();
addBuildModeFlags();
usesPubOption();
argParser.addOption('output-dir', defaultsTo: _kDefaultAotOutputDir);
}
@override
final String name = 'aot';
@override
final String description = "Build an ahead-of-time compiled snapshot of your app's Dart code.";
@override
Future<int> runInProject() async {
String outputPath = buildAotSnapshot(
findMainDartFile(argResults['target']),
TargetPlatform.android_arm,
getBuildMode(),
outputPath: argResults['output-dir']
);
if (outputPath == null)
return 1;
printStatus('Built $outputPath.');
return 0;
}
}
String _getSdkExtensionPath(String packagesPath, String package) {
Directory packageDir = new Directory(path.join(packagesPath, package));
return path.join(path.dirname(packageDir.resolveSymbolicLinksSync()), 'sdk_ext');
}
String buildAotSnapshot(
String mainPath,
TargetPlatform platform,
BuildMode buildMode, {
String outputPath: _kDefaultAotOutputDir
}) {
if (!isAotBuildMode(buildMode)) {
printError('${getModeName(buildMode)} mode does not support AOT compilation.');
return null;
}
String entryPointsDir, genSnapshot;
String engineSrc = tools.engineSrcPath;
if (engineSrc != null) {
entryPointsDir = path.join(engineSrc, 'sky', 'engine', 'bindings');
String engineOut = tools.getEngineArtifactsDirectory(platform, 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';
genSnapshot = path.join(engineOut, host32BitToolchain, 'gen_snapshot');
}
} else {
String artifactsDir = tools.getEngineArtifactsDirectory(platform, buildMode).path;
entryPointsDir = artifactsDir;
String hostToolsDir = path.join(artifactsDir, getNameForHostPlatform(getCurrentHostPlatform()));
genSnapshot = path.join(hostToolsDir, 'gen_snapshot');
}
Directory outputDir = new Directory(outputPath);
outputDir.createSync(recursive: true);
String vmIsolateSnapshot = path.join(outputDir.path, 'snapshot_aot_vmisolate');
String isolateSnapshot = path.join(outputDir.path, 'snapshot_aot_isolate');
String instructionsBlob = path.join(outputDir.path, 'snapshot_aot_instr');
String rodataBlob = path.join(outputDir.path, 'snapshot_aot_rodata');
String vmEntryPoints = path.join(entryPointsDir, 'dart_vm_entry_points.txt');
String packagesPath = path.absolute(Directory.current.path, 'packages');
if (!FileSystemEntity.isDirectorySync(packagesPath)) {
printError('Could not find packages directory: $packagesPath\n' +
'Did you run `pub get` in this directory?');
printError('This is needed to work around ' +
'https://github.com/dart-lang/sdk/issues/26362');
return null;
}
String mojoSdkExt = _getSdkExtensionPath(packagesPath, 'mojo');
String mojoInternalPath = path.join(mojoSdkExt, 'internal.dart');
String skyEngineSdkExt = _getSdkExtensionPath(packagesPath, 'sky_engine');
String uiPath = path.join(skyEngineSdkExt, 'dart_ui.dart');
String vmServicePath = path.join(skyEngineSdkExt, 'dart', 'runtime', 'bin', 'vmservice', 'vmservice_io.dart');
List<String> filePaths = <String>[
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();
if (missingFiles.isNotEmpty) {
printError('Missing files: $missingFiles');
return null;
}
List<String> genSnapshotCmd = <String>[
genSnapshot,
'--vm_isolate_snapshot=$vmIsolateSnapshot',
'--isolate_snapshot=$isolateSnapshot',
'--embedder_entry_points_manifest=$vmEntryPoints',
'--package_root=$packagesPath',
'--url_mapping=dart:mojo.internal,$mojoInternalPath',
'--url_mapping=dart:ui,$uiPath',
'--url_mapping=dart:vmservice_sky,$vmServicePath',
];
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) {
genSnapshotCmd.addAll(<String>[
'--no-checked',
'--conditional_directives',
]);
}
genSnapshotCmd.add(mainPath);
printStatus('Building snapshot...');
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;
}