• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2015 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'dart:async';
6
7import 'package:meta/meta.dart';
8import 'package:pool/pool.dart';
9
10import 'artifacts.dart';
11import 'asset.dart';
12import 'base/common.dart';
13import 'base/file_system.dart';
14import 'build_info.dart';
15import 'compile.dart';
16import 'dart/package_map.dart';
17import 'devfs.dart';
18import 'globals.dart';
19import 'project.dart';
20
21String get defaultMainPath => fs.path.join('lib', 'main.dart');
22const String defaultAssetBasePath = '.';
23const String defaultManifestPath = 'pubspec.yaml';
24String get defaultDepfilePath => fs.path.join(getBuildDirectory(), 'snapshot_blob.bin.d');
25
26String getDefaultApplicationKernelPath({ @required bool trackWidgetCreation }) {
27  return getKernelPathForTransformerOptions(
28    fs.path.join(getBuildDirectory(), 'app.dill'),
29    trackWidgetCreation: trackWidgetCreation,
30  );
31}
32
33String getKernelPathForTransformerOptions(
34  String path, {
35  @required bool trackWidgetCreation,
36}) {
37  if (trackWidgetCreation) {
38    path += '.track.dill';
39  }
40  return path;
41}
42
43const String defaultPrivateKeyPath = 'privatekey.der';
44
45const String _kKernelKey = 'kernel_blob.bin';
46const String _kVMSnapshotData = 'vm_snapshot_data';
47const String _kIsolateSnapshotData = 'isolate_snapshot_data';
48
49/// Provides a `build` method that builds the bundle.
50class BundleBuilder {
51  /// Builds the bundle for the given target platform.
52  ///
53  /// The default `mainPath` is `lib/main.dart`.
54  /// The default  `manifestPath` is `pubspec.yaml`
55  Future<void> build({
56    TargetPlatform platform,
57    BuildMode buildMode,
58    String mainPath,
59    String manifestPath = defaultManifestPath,
60    String applicationKernelFilePath,
61    String depfilePath,
62    String privateKeyPath = defaultPrivateKeyPath,
63    String assetDirPath,
64    String packagesPath,
65    bool precompiledSnapshot = false,
66    bool reportLicensedPackages = false,
67    bool trackWidgetCreation = false,
68    List<String> extraFrontEndOptions = const <String>[],
69    List<String> extraGenSnapshotOptions = const <String>[],
70    List<String> fileSystemRoots,
71    String fileSystemScheme,
72  }) async {
73    mainPath ??= defaultMainPath;
74    depfilePath ??= defaultDepfilePath;
75    assetDirPath ??= getAssetBuildDirectory();
76    packagesPath ??= fs.path.absolute(PackageMap.globalPackagesPath);
77    applicationKernelFilePath ??= getDefaultApplicationKernelPath(trackWidgetCreation: trackWidgetCreation);
78    final FlutterProject flutterProject = FlutterProject.current();
79
80    DevFSContent kernelContent;
81    if (!precompiledSnapshot) {
82      if ((extraFrontEndOptions != null) && extraFrontEndOptions.isNotEmpty)
83        printTrace('Extra front-end options: $extraFrontEndOptions');
84      ensureDirectoryExists(applicationKernelFilePath);
85      final KernelCompiler kernelCompiler = await kernelCompilerFactory.create(flutterProject);
86      final CompilerOutput compilerOutput = await kernelCompiler.compile(
87        sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath, mode: buildMode),
88        mainPath: fs.file(mainPath).absolute.path,
89        outputFilePath: applicationKernelFilePath,
90        depFilePath: depfilePath,
91        trackWidgetCreation: trackWidgetCreation,
92        extraFrontEndOptions: extraFrontEndOptions,
93        fileSystemRoots: fileSystemRoots,
94        fileSystemScheme: fileSystemScheme,
95        packagesPath: packagesPath,
96      );
97      if (compilerOutput?.outputFilename == null) {
98        throwToolExit('Compiler failed on $mainPath');
99      }
100      kernelContent = DevFSFileContent(fs.file(compilerOutput.outputFilename));
101
102      await fs.directory(getBuildDirectory()).childFile('frontend_server.d')
103          .writeAsString('frontend_server.d: ${artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk)}\n');
104    }
105
106    final AssetBundle assets = await buildAssets(
107      manifestPath: manifestPath,
108      assetDirPath: assetDirPath,
109      packagesPath: packagesPath,
110      reportLicensedPackages: reportLicensedPackages,
111    );
112    if (assets == null)
113      throwToolExit('Error building assets', exitCode: 1);
114
115    await assemble(
116      buildMode: buildMode,
117      assetBundle: assets,
118      kernelContent: kernelContent,
119      privateKeyPath: privateKeyPath,
120      assetDirPath: assetDirPath,
121    );
122  }
123}
124
125Future<AssetBundle> buildAssets({
126  String manifestPath,
127  String assetDirPath,
128  String packagesPath,
129  bool includeDefaultFonts = true,
130  bool reportLicensedPackages = false,
131}) async {
132  assetDirPath ??= getAssetBuildDirectory();
133  packagesPath ??= fs.path.absolute(PackageMap.globalPackagesPath);
134
135  // Build the asset bundle.
136  final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle();
137  final int result = await assetBundle.build(
138    manifestPath: manifestPath,
139    assetDirPath: assetDirPath,
140    packagesPath: packagesPath,
141    includeDefaultFonts: includeDefaultFonts,
142    reportLicensedPackages: reportLicensedPackages,
143  );
144  if (result != 0)
145    return null;
146
147  return assetBundle;
148}
149
150Future<void> assemble({
151  BuildMode buildMode,
152  AssetBundle assetBundle,
153  DevFSContent kernelContent,
154  String privateKeyPath = defaultPrivateKeyPath,
155  String assetDirPath,
156}) async {
157  assetDirPath ??= getAssetBuildDirectory();
158  printTrace('Building bundle');
159
160  final Map<String, DevFSContent> assetEntries = Map<String, DevFSContent>.from(assetBundle.entries);
161  if (kernelContent != null) {
162    final String vmSnapshotData = artifacts.getArtifactPath(Artifact.vmSnapshotData, mode: buildMode);
163    final String isolateSnapshotData = artifacts.getArtifactPath(Artifact.isolateSnapshotData, mode: buildMode);
164    assetEntries[_kKernelKey] = kernelContent;
165    assetEntries[_kVMSnapshotData] = DevFSFileContent(fs.file(vmSnapshotData));
166    assetEntries[_kIsolateSnapshotData] = DevFSFileContent(fs.file(isolateSnapshotData));
167  }
168
169  printTrace('Writing asset files to $assetDirPath');
170  ensureDirectoryExists(assetDirPath);
171
172  await writeBundle(fs.directory(assetDirPath), assetEntries);
173  printTrace('Wrote $assetDirPath');
174}
175
176Future<void> writeBundle(
177  Directory bundleDir,
178  Map<String, DevFSContent> assetEntries,
179) async {
180  if (bundleDir.existsSync())
181    bundleDir.deleteSync(recursive: true);
182  bundleDir.createSync(recursive: true);
183
184  // Limit number of open files to avoid running out of file descriptors.
185  final Pool pool = Pool(64);
186  await Future.wait<void>(
187    assetEntries.entries.map<Future<void>>((MapEntry<String, DevFSContent> entry) async {
188      final PoolResource resource = await pool.request();
189      try {
190        final File file = fs.file(fs.path.join(bundleDir.path, entry.key));
191        file.parent.createSync(recursive: true);
192        await file.writeAsBytes(await entry.value.contentsAsBytes());
193      } finally {
194        resource.release();
195      }
196    }));
197}
198