bundle.dart 6.22 KB
Newer Older
1 2 3 4 5 6
// 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';

7
import 'package:meta/meta.dart';
8
import 'package:pool/pool.dart';
9

10
import 'asset.dart';
11
import 'base/common.dart';
12
import 'base/file_system.dart';
13
import 'build_info.dart';
14
import 'build_system/build_system.dart';
15
import 'build_system/depfile.dart';
16
import 'build_system/targets/dart.dart';
17
import 'dart/package_map.dart';
18
import 'devfs.dart';
19
import 'globals.dart';
20
import 'project.dart';
21

22
String get defaultMainPath => fs.path.join('lib', 'main.dart');
23
const String defaultAssetBasePath = '.';
24
const String defaultManifestPath = 'pubspec.yaml';
25
String get defaultDepfilePath => fs.path.join(getBuildDirectory(), 'snapshot_blob.bin.d');
26

27
String getDefaultApplicationKernelPath({ @required bool trackWidgetCreation }) {
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
  return getKernelPathForTransformerOptions(
    fs.path.join(getBuildDirectory(), 'app.dill'),
    trackWidgetCreation: trackWidgetCreation,
  );
}

String getKernelPathForTransformerOptions(
  String path, {
  @required bool trackWidgetCreation,
}) {
  if (trackWidgetCreation) {
    path += '.track.dill';
  }
  return path;
}

44 45
const String defaultPrivateKeyPath = 'privatekey.der';

46 47 48 49 50 51 52
/// Provides a `build` method that builds the bundle.
class BundleBuilder {
  /// Builds the bundle for the given target platform.
  ///
  /// The default `mainPath` is `lib/main.dart`.
  /// The default  `manifestPath` is `pubspec.yaml`
  Future<void> build({
53
    @required TargetPlatform platform,
54
    BuildMode buildMode,
55
    String mainPath,
56 57 58 59 60 61 62 63 64 65 66 67 68 69
    String manifestPath = defaultManifestPath,
    String applicationKernelFilePath,
    String depfilePath,
    String privateKeyPath = defaultPrivateKeyPath,
    String assetDirPath,
    String packagesPath,
    bool precompiledSnapshot = false,
    bool reportLicensedPackages = false,
    bool trackWidgetCreation = false,
    List<String> extraFrontEndOptions = const <String>[],
    List<String> extraGenSnapshotOptions = const <String>[],
    List<String> fileSystemRoots,
    String fileSystemScheme,
  }) async {
70
    mainPath ??= defaultMainPath;
71 72 73 74
    depfilePath ??= defaultDepfilePath;
    assetDirPath ??= getAssetBuildDirectory();
    packagesPath ??= fs.path.absolute(PackageMap.globalPackagesPath);
    final FlutterProject flutterProject = FlutterProject.current();
75 76 77 78 79 80 81 82
    await buildWithAssemble(
      buildMode: buildMode ?? BuildMode.debug,
      targetPlatform: platform,
      mainPath: mainPath,
      flutterProject: flutterProject,
      outputDir: assetDirPath,
      depfilePath: depfilePath,
      precompiled: precompiledSnapshot,
83
      trackWidgetCreation: trackWidgetCreation,
84
    );
85 86 87 88 89 90
    // Work around for flutter_tester placing kernel artifacts in odd places.
    if (applicationKernelFilePath != null) {
      final File outputDill = fs.directory(assetDirPath).childFile('kernel_blob.bin');
      if (outputDill.existsSync()) {
        outputDill.copySync(applicationKernelFilePath);
      }
91
    }
92
    return;
93
  }
94 95
}

96 97 98 99 100 101 102 103 104 105
/// Build an application bundle using flutter assemble.
///
/// This is a temporary shim to migrate the build implementations.
Future<void> buildWithAssemble({
  @required FlutterProject flutterProject,
  @required BuildMode buildMode,
  @required TargetPlatform targetPlatform,
  @required String mainPath,
  @required String outputDir,
  @required String depfilePath,
106
  @required bool precompiled,
107
  bool trackWidgetCreation,
108
}) async {
109 110
  // If the precompiled flag was not passed, force us into debug mode.
  buildMode = precompiled ? buildMode : BuildMode.debug;
111 112 113 114 115 116 117 118
  final Environment environment = Environment(
    projectDir: flutterProject.directory,
    outputDir: fs.directory(outputDir),
    buildDir: flutterProject.dartTool.childDirectory('flutter_build'),
    defines: <String, String>{
      kTargetFile: mainPath,
      kBuildMode: getNameForBuildMode(buildMode),
      kTargetPlatform: getNameForTargetPlatform(targetPlatform),
119
      kTrackWidgetCreation: trackWidgetCreation?.toString(),
120
    },
121 122 123 124 125 126 127 128 129 130 131 132 133
  );
  final Target target = buildMode == BuildMode.debug
    ? const CopyFlutterBundle()
    : const ReleaseCopyFlutterBundle();
  final BuildResult result = await buildSystem.build(target, environment);

  if (!result.success) {
    for (ExceptionMeasurement measurement in result.exceptions.values) {
      printError(measurement.exception.toString());
      printError(measurement.stackTrace.toString());
    }
    throwToolExit('Failed to build bundle.');
  }
134 135 136 137 138
  if (depfilePath != null) {
    final Depfile depfile = Depfile(result.inputFiles, result.outputFiles);
    final File outputDepfile = fs.file(depfilePath);
    if (!outputDepfile.parent.existsSync()) {
      outputDepfile.parent.createSync(recursive: true);
139
    }
140
    depfile.writeToFile(outputDepfile);
141 142 143
  }
}

144
Future<AssetBundle> buildAssets({
145
  String manifestPath,
146
  String assetDirPath,
147
  String packagesPath,
148
  bool includeDefaultFonts = true,
149
  bool reportLicensedPackages = false,
150
}) async {
151
  assetDirPath ??= getAssetBuildDirectory();
152
  packagesPath ??= fs.path.absolute(PackageMap.globalPackagesPath);
153

154
  // Build the asset bundle.
155
  final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle();
156
  final int result = await assetBundle.build(
157
    manifestPath: manifestPath,
158
    assetDirPath: assetDirPath,
159
    packagesPath: packagesPath,
160
    includeDefaultFonts: includeDefaultFonts,
161
    reportLicensedPackages: reportLicensedPackages,
162
  );
163
  if (result != 0) {
164
    return null;
165
  }
166 167 168 169

  return assetBundle;
}

170
Future<void> writeBundle(
171 172
  Directory bundleDir,
  Map<String, DevFSContent> assetEntries,
173
) async {
174
  if (bundleDir.existsSync()) {
175
    bundleDir.deleteSync(recursive: true);
176
  }
177 178
  bundleDir.createSync(recursive: true);

179 180
  // Limit number of open files to avoid running out of file descriptors.
  final Pool pool = Pool(64);
181 182
  await Future.wait<void>(
    assetEntries.entries.map<Future<void>>((MapEntry<String, DevFSContent> entry) async {
183 184 185 186 187 188 189 190
      final PoolResource resource = await pool.request();
      try {
        final File file = fs.file(fs.path.join(bundleDir.path, entry.key));
        file.parent.createSync(recursive: true);
        await file.writeAsBytes(await entry.value.contentsAsBytes());
      } finally {
        resource.release();
      }
191
    }));
192
}