blob: 8cff1b20a279fb902a0a25a92e826eb12e56dc7c [file] [log] [blame]
// Copyright 2015 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 'package:meta/meta.dart';
import 'artifacts.dart';
import 'asset.dart';
import 'base/build.dart';
import 'base/common.dart';
import 'base/file_system.dart';
import 'build_info.dart';
import 'compile.dart';
import 'dart/package_map.dart';
import 'devfs.dart';
import 'globals.dart';
import 'project.dart';
const String defaultMainPath = 'lib/main.dart';
const String defaultAssetBasePath = '.';
const String defaultManifestPath = 'pubspec.yaml';
String get defaultDepfilePath => fs.path.join(getBuildDirectory(), 'snapshot_blob.bin.d');
String getDefaultApplicationKernelPath({ @required bool trackWidgetCreation }) {
return getKernelPathForTransformerOptions(
fs.path.join(getBuildDirectory(), 'app.dill'),
trackWidgetCreation: trackWidgetCreation,
);
}
String getKernelPathForTransformerOptions(
String path, {
@required bool trackWidgetCreation,
}) {
if (trackWidgetCreation) {
path += '.track.dill';
}
return path;
}
const String defaultPrivateKeyPath = 'privatekey.der';
const String _kKernelKey = 'kernel_blob.bin';
const String _kVMSnapshotData = 'vm_snapshot_data';
const String _kIsolateSnapshotData = 'isolate_snapshot_data';
const String _kIsolateSnapshotInstr = 'isolate_snapshot_instr';
Future<void> build({
TargetPlatform platform,
BuildMode buildMode,
String mainPath = defaultMainPath,
String manifestPath = defaultManifestPath,
String applicationKernelFilePath,
String depfilePath,
String privateKeyPath = defaultPrivateKeyPath,
String assetDirPath,
String packagesPath,
bool precompiledSnapshot = false,
bool reportLicensedPackages = false,
bool trackWidgetCreation = false,
String compilationTraceFilePath,
bool createPatch = false,
String buildNumber,
String baselineDir,
List<String> extraFrontEndOptions = const <String>[],
List<String> extraGenSnapshotOptions = const <String>[],
List<String> fileSystemRoots,
String fileSystemScheme,
}) async {
depfilePath ??= defaultDepfilePath;
assetDirPath ??= getAssetBuildDirectory();
packagesPath ??= fs.path.absolute(PackageMap.globalPackagesPath);
applicationKernelFilePath ??= getDefaultApplicationKernelPath(trackWidgetCreation: trackWidgetCreation);
final FlutterProject flutterProject = await FlutterProject.current();
if (compilationTraceFilePath != null) {
if (buildMode != BuildMode.dynamicProfile && buildMode != BuildMode.dynamicRelease) {
// Silently ignore JIT snapshotting for those builds that don't support it.
compilationTraceFilePath = null;
} else if (compilationTraceFilePath.isEmpty) {
// Disable JIT snapshotting if flag is empty.
printStatus('Code snapshot will be disabled for this build.');
compilationTraceFilePath = null;
} else if (!fs.file(compilationTraceFilePath).existsSync()) {
// Be forgiving if compilation trace file is missing.
printStatus('No compilation trace available. To optimize performance, consider using --train.');
final File tmp = fs.systemTempDirectory.childFile('flutterEmptyCompilationTrace.txt');
compilationTraceFilePath = (tmp..createSync(recursive: true)).path;
} else {
printStatus('Code snapshot will use compilation training file $compilationTraceFilePath.');
}
}
DevFSContent kernelContent;
if (!precompiledSnapshot) {
if ((extraFrontEndOptions != null) && extraFrontEndOptions.isNotEmpty)
printTrace('Extra front-end options: $extraFrontEndOptions');
ensureDirectoryExists(applicationKernelFilePath);
final KernelCompiler kernelCompiler = await kernelCompilerFactory.create(flutterProject);
final CompilerOutput compilerOutput = await kernelCompiler.compile(
sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath),
incrementalCompilerByteStorePath: compilationTraceFilePath != null ? null :
fs.path.absolute(getIncrementalCompilerByteStoreDirectory()),
mainPath: fs.file(mainPath).absolute.path,
outputFilePath: applicationKernelFilePath,
depFilePath: depfilePath,
trackWidgetCreation: trackWidgetCreation,
extraFrontEndOptions: extraFrontEndOptions,
fileSystemRoots: fileSystemRoots,
fileSystemScheme: fileSystemScheme,
packagesPath: packagesPath,
linkPlatformKernelIn: compilationTraceFilePath != null,
);
if (compilerOutput?.outputFilename == null) {
throwToolExit('Compiler failed on $mainPath');
}
kernelContent = DevFSFileContent(fs.file(compilerOutput.outputFilename));
await fs.directory(getBuildDirectory()).childFile('frontend_server.d')
.writeAsString('frontend_server.d: ${artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk)}\n');
if (compilationTraceFilePath != null) {
final JITSnapshotter snapshotter = JITSnapshotter();
final int snapshotExitCode = await snapshotter.build(
platform: platform,
buildMode: buildMode,
mainPath: applicationKernelFilePath,
outputPath: getBuildDirectory(),
packagesPath: packagesPath,
compilationTraceFilePath: compilationTraceFilePath,
extraGenSnapshotOptions: extraGenSnapshotOptions,
createPatch: createPatch,
buildNumber: buildNumber,
baselineDir: baselineDir,
);
if (snapshotExitCode != 0) {
throwToolExit('Snapshotting exited with non-zero exit code: $snapshotExitCode');
}
}
}
final AssetBundle assets = await buildAssets(
manifestPath: manifestPath,
assetDirPath: assetDirPath,
packagesPath: packagesPath,
reportLicensedPackages: reportLicensedPackages,
);
if (assets == null)
throwToolExit('Error building assets', exitCode: 1);
await assemble(
buildMode: buildMode,
assetBundle: assets,
kernelContent: kernelContent,
privateKeyPath: privateKeyPath,
assetDirPath: assetDirPath,
compilationTraceFilePath: compilationTraceFilePath,
);
}
Future<AssetBundle> buildAssets({
String manifestPath,
String assetDirPath,
String packagesPath,
bool includeDefaultFonts = true,
bool reportLicensedPackages = false,
}) async {
assetDirPath ??= getAssetBuildDirectory();
packagesPath ??= fs.path.absolute(PackageMap.globalPackagesPath);
// Build the asset bundle.
final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle();
final int result = await assetBundle.build(
manifestPath: manifestPath,
assetDirPath: assetDirPath,
packagesPath: packagesPath,
includeDefaultFonts: includeDefaultFonts,
reportLicensedPackages: reportLicensedPackages,
);
if (result != 0)
return null;
return assetBundle;
}
Future<void> assemble({
BuildMode buildMode,
AssetBundle assetBundle,
DevFSContent kernelContent,
String privateKeyPath = defaultPrivateKeyPath,
String assetDirPath,
String compilationTraceFilePath,
}) async {
assetDirPath ??= getAssetBuildDirectory();
printTrace('Building bundle');
final Map<String, DevFSContent> assetEntries = Map<String, DevFSContent>.from(assetBundle.entries);
if (kernelContent != null) {
if (compilationTraceFilePath != null) {
final String vmSnapshotData = artifacts.getArtifactPath(Artifact.vmSnapshotData, null, buildMode);
final String isolateSnapshotData = fs.path.join(getBuildDirectory(), _kIsolateSnapshotData);
final String isolateSnapshotInstr = fs.path.join(getBuildDirectory(), _kIsolateSnapshotInstr);
assetEntries[_kVMSnapshotData] = DevFSFileContent(fs.file(vmSnapshotData));
assetEntries[_kIsolateSnapshotData] = DevFSFileContent(fs.file(isolateSnapshotData));
assetEntries[_kIsolateSnapshotInstr] = DevFSFileContent(fs.file(isolateSnapshotInstr));
} else {
final String vmSnapshotData = artifacts.getArtifactPath(Artifact.vmSnapshotData, null, buildMode);
final String isolateSnapshotData = artifacts.getArtifactPath(Artifact.isolateSnapshotData, null, buildMode);
assetEntries[_kKernelKey] = kernelContent;
assetEntries[_kVMSnapshotData] = DevFSFileContent(fs.file(vmSnapshotData));
assetEntries[_kIsolateSnapshotData] = DevFSFileContent(fs.file(isolateSnapshotData));
}
}
printTrace('Writing asset files to $assetDirPath');
ensureDirectoryExists(assetDirPath);
await writeBundle(fs.directory(assetDirPath), assetEntries);
printTrace('Wrote $assetDirPath');
}
Future<void> writeBundle(
Directory bundleDir,
Map<String, DevFSContent> assetEntries,
) async {
if (bundleDir.existsSync())
bundleDir.deleteSync(recursive: true);
bundleDir.createSync(recursive: true);
await Future.wait<void>(
assetEntries.entries.map<Future<void>>((MapEntry<String, DevFSContent> entry) async {
final File file = fs.file(fs.path.join(bundleDir.path, entry.key));
file.parent.createSync(recursive: true);
await file.writeAsBytes(await entry.value.contentsAsBytes());
}));
}