blob: 771d8f0b726c4bca7f0fc10d7f1f993a7e9d9c41 [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 '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';
const String defaultMainPath = 'lib/main.dart';
const String defaultAssetBasePath = '.';
const String defaultManifestPath = 'pubspec.yaml';
String get defaultSnapshotPath => fs.path.join(getBuildDirectory(), 'snapshot_blob.bin');
String get defaultDepfilePath => fs.path.join(getBuildDirectory(), 'snapshot_blob.bin.d');
String get defaultApplicationKernelPath => fs.path.join(getBuildDirectory(), 'app.dill');
const String defaultPrivateKeyPath = 'privatekey.der';
const String _kKernelKey = 'kernel_blob.bin';
const String _kSnapshotKey = 'snapshot_blob.bin';
const String _kDylibKey = 'libapp.so';
const String _kPlatformKernelKey = 'platform.dill';
Future<void> build({
String mainPath: defaultMainPath,
String manifestPath: defaultManifestPath,
String snapshotPath,
String applicationKernelFilePath,
String depfilePath,
String privateKeyPath: defaultPrivateKeyPath,
String assetDirPath,
String packagesPath,
bool previewDart2 : false,
bool precompiledSnapshot: false,
bool reportLicensedPackages: false,
bool trackWidgetCreation: false,
List<String> fileSystemRoots,
String fileSystemScheme,
}) async {
snapshotPath ??= defaultSnapshotPath;
depfilePath ??= defaultDepfilePath;
assetDirPath ??= getAssetBuildDirectory();
packagesPath ??= fs.path.absolute(PackageMap.globalPackagesPath);
applicationKernelFilePath ??= defaultApplicationKernelPath;
File snapshotFile;
if (!precompiledSnapshot && !previewDart2) {
ensureDirectoryExists(snapshotPath);
// In a precompiled snapshot, the instruction buffer contains script
// content equivalents
final Snapshotter snapshotter = new Snapshotter();
final int result = await snapshotter.buildScriptSnapshot(
mainPath: mainPath,
snapshotPath: snapshotPath,
depfilePath: depfilePath,
packagesPath: packagesPath,
);
if (result != 0)
throwToolExit('Failed to run the Flutter compiler. Exit code: $result', exitCode: result);
snapshotFile = fs.file(snapshotPath);
}
DevFSContent kernelContent;
if (!precompiledSnapshot && previewDart2) {
final File fingerprintFile = fs.file('$depfilePath.fingerprint');
final List<String> inputPaths = <String>[
mainPath,
];
bool needBuild = true;
final List<File> fingerprintFiles = <File>[fingerprintFile, fs.file(depfilePath)]
..addAll(inputPaths.map(fs.file));
Future<Fingerprint> makeFingerprint() async {
final Set<String> compilerInputPaths = await readDepfile(depfilePath)
..add(mainPath);
final Map<String, String> properties = <String, String>{
'entryPoint': mainPath,
'trackWidgetCreation': trackWidgetCreation.toString(),
};
return new Fingerprint.fromBuildInputs(properties, compilerInputPaths);
}
if (fingerprintFiles.every((File file) => file.existsSync())) {
try {
final String json = await fingerprintFile.readAsString();
final Fingerprint oldFingerprint = new Fingerprint.fromJson(json);
if (oldFingerprint == await makeFingerprint()) {
needBuild = false;
printStatus('Skipping compilation. Fingerprint match.');
}
} catch (e) {
printTrace('Rebuilding kernel file due to fingerprint check error: $e');
}
}
String kernelBinaryFilename;
if (needBuild) {
ensureDirectoryExists(applicationKernelFilePath);
final CompilerOutput compilerOutput = await kernelCompiler.compile(
sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath),
incrementalCompilerByteStorePath: fs.path.absolute(getIncrementalCompilerByteStoreDirectory()),
mainPath: fs.file(mainPath).absolute.path,
outputFilePath: applicationKernelFilePath,
depFilePath: depfilePath,
trackWidgetCreation: trackWidgetCreation,
fileSystemRoots: fileSystemRoots,
fileSystemScheme: fileSystemScheme,
packagesPath: packagesPath,
);
kernelBinaryFilename = compilerOutput?.outputFilename;
if (kernelBinaryFilename == null) {
throwToolExit('Compiler failed on $mainPath');
}
// Compute and record build fingerprint.
try {
final Fingerprint fingerprint = await makeFingerprint();
await fingerprintFile.writeAsString(fingerprint.toJson());
} catch (e, s) {
// Log exception and continue, this step is a performance improvement only.
printStatus('Error during compilation output fingerprinting: $e\n$s');
}
} else {
kernelBinaryFilename = applicationKernelFilePath;
}
kernelContent = new DevFSFileContent(fs.file(kernelBinaryFilename));
await fs.directory(getBuildDirectory()).childFile('frontend_server.d')
.writeAsString('frontend_server.d: ${artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk)}\n');
}
final AssetBundle assets = await buildAssets(
manifestPath: manifestPath,
assetDirPath: assetDirPath,
packagesPath: packagesPath,
reportLicensedPackages: reportLicensedPackages,
);
if (assets == null)
throwToolExit('Error building assets', exitCode: 1);
await assemble(
assetBundle: assets,
kernelContent: kernelContent,
snapshotFile: snapshotFile,
privateKeyPath: privateKeyPath,
assetDirPath: assetDirPath,
);
}
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({
AssetBundle assetBundle,
DevFSContent kernelContent,
File snapshotFile,
File dylibFile,
String privateKeyPath: defaultPrivateKeyPath,
String assetDirPath,
}) async {
assetDirPath ??= getAssetBuildDirectory();
printTrace('Building bundle');
final Map<String, DevFSContent> assetEntries = new Map<String, DevFSContent>.from(assetBundle.entries);
if (kernelContent != null) {
final String platformKernelDill = artifacts.getArtifactPath(Artifact.platformKernelDill);
assetEntries[_kKernelKey] = kernelContent;
assetEntries[_kPlatformKernelKey] = new DevFSFileContent(fs.file(platformKernelDill));
}
if (snapshotFile != null)
assetEntries[_kSnapshotKey] = new DevFSFileContent(snapshotFile);
if (dylibFile != null)
assetEntries[_kDylibKey] = new DevFSFileContent(dylibFile);
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(
assetEntries.entries.map((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());
}));
}