fuchsia_build.dart 9.14 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
import 'dart:async';

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

9
import '../artifacts.dart';
10
import '../asset.dart';
11
import '../base/common.dart';
12 13
import '../base/file_system.dart';
import '../base/io.dart';
14 15
import '../base/logger.dart';
import '../base/process.dart';
16
import '../base/utils.dart';
17 18
import '../build_info.dart';
import '../bundle.dart';
19
import '../convert.dart';
20
import '../devfs.dart';
21
import '../globals.dart' as globals;
22
import '../project.dart';
23
import '../reporting/reporting.dart';
24 25

import 'fuchsia_pm.dart';
26
import 'fuchsia_sdk.dart';
27

28 29 30
Future<void> _timedBuildStep(String name, Future<void> Function() action) async {
  final Stopwatch sw = Stopwatch()..start();
  await action();
31
  globals.printTrace('$name: ${sw.elapsedMilliseconds} ms.');
32 33 34
  flutterUsage.sendTiming('build', name, Duration(milliseconds: sw.elapsedMilliseconds));
}

35 36
Future<void> _validateCmxFile(FuchsiaProject fuchsiaProject) async {
  final String appName = fuchsiaProject.project.manifest.appName;
37 38
  final String cmxPath = globals.fs.path.join(fuchsiaProject.meta.path, '$appName.cmx');
  final File cmxFile = globals.fs.file(cmxPath);
39 40 41 42 43
  if (!await cmxFile.exists()) {
    throwToolExit('The Fuchsia build requires a .cmx file at $cmxPath for the app: $appName.');
  }
}

44 45 46 47 48 49
// Building a Fuchsia package has a few steps:
// 1. Do the custom kernel compile using the kernel compiler from the Fuchsia
//    SDK. This produces .dilp files (among others) and a manifest file.
// 2. Create a manifest file for assets.
// 3. Using these manifests, use the Fuchsia SDK 'pm' tool to create the
//    Fuchsia package.
50 51
Future<void> buildFuchsia({
  @required FuchsiaProject fuchsiaProject,
52
  @required TargetPlatform targetPlatform,
53 54
  @required String target, // E.g., lib/main.dart
  BuildInfo buildInfo = BuildInfo.debug,
55
  String runnerPackageSource = FuchsiaPackageServer.toolHost,
56
}) async {
57
  await _validateCmxFile(fuchsiaProject);
58
  final Directory outDir = globals.fs.directory(getFuchsiaBuildDirectory());
59 60 61 62
  if (!outDir.existsSync()) {
    outDir.createSync(recursive: true);
  }

63 64 65
  await _timedBuildStep('fuchsia-kernel-compile',
    () => fuchsiaSdk.fuchsiaKernelCompiler.build(
      fuchsiaProject: fuchsiaProject, target: target, buildInfo: buildInfo));
66 67 68 69 70 71

  if (buildInfo.usesAot) {
    await _timedBuildStep('fuchsia-gen-snapshot',
      () => _genSnapshot(fuchsiaProject, target, buildInfo, targetPlatform));
  }

72 73 74
  await _timedBuildStep('fuchsia-build-assets',
    () => _buildAssets(fuchsiaProject, target, buildInfo));
  await _timedBuildStep('fuchsia-build-package',
75
    () => _buildPackage(fuchsiaProject, target, buildInfo, runnerPackageSource));
76 77
}

78 79 80 81 82 83 84 85
Future<void> _genSnapshot(
  FuchsiaProject fuchsiaProject,
  String target, // lib/main.dart
  BuildInfo buildInfo,
  TargetPlatform targetPlatform,
) async {
  final String outDir = getFuchsiaBuildDirectory();
  final String appName = fuchsiaProject.project.manifest.appName;
86
  final String dilPath = globals.fs.path.join(outDir, '$appName.dil');
87

88 89 90 91
  final String vmSnapshotData = globals.fs.path.join(outDir, 'vm_data.aotsnapshot');
  final String vmSnapshotInstructions = globals.fs.path.join(outDir, 'vm_instructions.aotsnapshot');
  final String snapshotData = globals.fs.path.join(outDir, 'data.aotsnapshot');
  final String snapshotInstructions = globals.fs.path.join(outDir, 'instructions.aotsnapshot');
92

93
  final String genSnapshot = globals.artifacts.getArtifactPath(
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
    Artifact.genSnapshot,
    platform: targetPlatform,
    mode: buildInfo.mode,
  );

  final List<String> command = <String>[
    genSnapshot,
    '--no_causal_async_stacks',
    '--deterministic',
    '--snapshot_kind=app-aot-blobs',
    '--vm_snapshot_data=$vmSnapshotData',
    '--vm_snapshot_instructions=$vmSnapshotInstructions',
    '--isolate_snapshot_data=$snapshotData',
    '--isolate_snapshot_instructions=$snapshotInstructions',
    if (buildInfo.isDebug) '--enable-asserts',
    dilPath,
  ];
  int result;
112
  final Status status = globals.logger.startProgress(
113 114 115 116 117 118 119 120 121 122 123 124 125
    'Compiling Fuchsia application to native code...',
    timeout: null,
  );
  try {
    result = await processUtils.stream(command, trace: true);
  } finally {
    status.cancel();
  }
  if (result != 0) {
    throwToolExit('Build process failed');
  }
}

126
Future<void> _buildAssets(
127 128 129 130
  FuchsiaProject fuchsiaProject,
  String target, // lib/main.dart
  BuildInfo buildInfo,
) async {
131 132 133 134 135 136 137 138 139 140
  final String assetDir = getAssetBuildDirectory();
  final AssetBundle assets = await buildAssets(
    manifestPath: fuchsiaProject.project.pubspecFile.path,
    packagesPath: fuchsiaProject.project.packagesFile.path,
    assetDirPath: assetDir,
    includeDefaultFonts: false,
  );

  final Map<String, DevFSContent> assetEntries =
      Map<String, DevFSContent>.from(assets.entries);
141
  await writeBundle(globals.fs.directory(assetDir), assetEntries);
142 143 144

  final String appName = fuchsiaProject.project.manifest.appName;
  final String outDir = getFuchsiaBuildDirectory();
145
  final String assetManifest = globals.fs.path.join(outDir, '${appName}_pkgassets');
146

147
  final File destFile = globals.fs.file(assetManifest);
148 149 150
  await destFile.create(recursive: true);
  final IOSink outFile = destFile.openWrite();

151
  for (final String path in assets.entries.keys) {
152 153 154 155 156 157
    outFile.write('data/$appName/$path=$assetDir/$path\n');
  }
  await outFile.flush();
  await outFile.close();
}

158
void _rewriteCmx(BuildMode mode, String runnerPackageSource, File src, File dst) {
159
  final Map<String, dynamic> cmx = castStringKeyedMap(json.decode(src.readAsStringSync()));
160 161 162 163 164 165 166 167 168
  // If the app author has already specified the runner in the cmx file, then
  // do not override it with something else.
  if (cmx.containsKey('runner')) {
    dst.writeAsStringSync(json.encode(cmx));
    return;
  }
  String runner;
  switch (mode) {
    case BuildMode.debug:
169
      runner = 'flutter_jit_runner';
170
      break;
171 172 173 174
    case BuildMode.profile:
      runner = 'flutter_aot_runner';
      break;
    case BuildMode.jitRelease:
175
      runner = 'flutter_jit_product_runner';
176
      break;
177 178 179
    case BuildMode.release:
      runner = 'flutter_aot_product_runner';
      break;
180 181 182 183
    default:
      throwToolExit('Fuchsia does not support build mode "$mode"');
      break;
  }
184
  cmx['runner'] = 'fuchsia-pkg://$runnerPackageSource/$runner#meta/$runner.cmx';
185 186 187
  dst.writeAsStringSync(json.encode(cmx));
}

188 189
// TODO(zra): Allow supplying a signing key.
Future<void> _buildPackage(
190 191 192
  FuchsiaProject fuchsiaProject,
  String target, // lib/main.dart
  BuildInfo buildInfo,
193
  String runnerPackageSource,
194
) async {
195
  final String outDir = getFuchsiaBuildDirectory();
196
  final String pkgDir = globals.fs.path.join(outDir, 'pkg');
197
  final String appName = fuchsiaProject.project.manifest.appName;
198 199 200
  final String pkgassets = globals.fs.path.join(outDir, '${appName}_pkgassets');
  final String packageManifest = globals.fs.path.join(pkgDir, 'package_manifest');
  final String devKeyPath = globals.fs.path.join(pkgDir, 'development.key');
201

202
  final Directory pkg = globals.fs.directory(pkgDir);
203 204 205 206
  if (!pkg.existsSync()) {
    pkg.createSync(recursive: true);
  }

207
  final File srcCmx =
208 209
      globals.fs.file(globals.fs.path.join(fuchsiaProject.meta.path, '$appName.cmx'));
  final File dstCmx = globals.fs.file(globals.fs.path.join(outDir, '$appName.cmx'));
210
  _rewriteCmx(buildInfo.mode, runnerPackageSource, srcCmx, dstCmx);
211

212
  final File manifestFile = globals.fs.file(packageManifest);
213 214

  if (buildInfo.usesAot) {
215 216 217 218
    final String vmSnapshotData = globals.fs.path.join(outDir, 'vm_data.aotsnapshot');
    final String vmSnapshotInstructions = globals.fs.path.join(outDir, 'vm_instructions.aotsnapshot');
    final String snapshotData = globals.fs.path.join(outDir, 'data.aotsnapshot');
    final String snapshotInstructions = globals.fs.path.join(outDir, 'instructions.aotsnapshot');
219 220 221 222 223 224 225 226 227 228 229 230
    manifestFile.writeAsStringSync(
      'data/$appName/vm_snapshot_data.bin=$vmSnapshotData\n');
    manifestFile.writeAsStringSync(
      'data/$appName/vm_snapshot_instructions.bin=$vmSnapshotInstructions\n',
      mode: FileMode.append);
    manifestFile.writeAsStringSync(
      'data/$appName/isolate_snapshot_data.bin=$snapshotData\n',
      mode: FileMode.append);
    manifestFile.writeAsStringSync(
      'data/$appName/isolate_snapshot_instructions.bin=$snapshotInstructions\n',
      mode: FileMode.append);
  } else {
231 232
    final String dilpmanifest = globals.fs.path.join(outDir, '$appName.dilpmanifest');
    manifestFile.writeAsStringSync(globals.fs.file(dilpmanifest).readAsStringSync());
233 234
  }

235
  manifestFile.writeAsStringSync(globals.fs.file(pkgassets).readAsStringSync(),
236
      mode: FileMode.append);
237
  manifestFile.writeAsStringSync('meta/$appName.cmx=${dstCmx.path}\n',
238 239 240 241
      mode: FileMode.append);
  manifestFile.writeAsStringSync('meta/package=$pkgDir/meta/package\n',
      mode: FileMode.append);

242
  final FuchsiaPM fuchsiaPM = fuchsiaSdk.fuchsiaPM;
243 244 245 246 247 248 249 250 251 252 253 254 255
  if (!await fuchsiaPM.init(pkgDir, appName)) {
    return;
  }
  if (!await fuchsiaPM.genkey(pkgDir, devKeyPath)) {
    return;
  }
  if (!await fuchsiaPM.build(pkgDir, devKeyPath, packageManifest)) {
    return;
  }
  if (!await fuchsiaPM.archive(pkgDir, devKeyPath, packageManifest)) {
    return;
  }
}