fuchsia_build.dart 7.65 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
// @dart = 2.8

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

9
import '../artifacts.dart';
10
import '../asset.dart';
11
import '../base/common.dart';
12
import '../base/file_system.dart';
13
import '../base/logger.dart';
14
import '../base/utils.dart';
15
import '../build_info.dart';
16
import '../bundle_builder.dart';
17
import '../convert.dart';
18
import '../devfs.dart';
19
import '../globals_null_migrated.dart' as globals;
20 21 22
import '../project.dart';

import 'fuchsia_pm.dart';
23
import 'fuchsia_sdk.dart';
24

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

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

41 42 43 44 45 46
// 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.
47 48
Future<void> buildFuchsia({
  @required FuchsiaProject fuchsiaProject,
49
  @required TargetPlatform targetPlatform,
50 51
  @required String target, // E.g., lib/main.dart
  BuildInfo buildInfo = BuildInfo.debug,
52
  String runnerPackageSource = FuchsiaPackageServer.toolHost,
53
}) async {
54
  await _validateCmxFile(fuchsiaProject);
55
  final Directory outDir = globals.fs.directory(getFuchsiaBuildDirectory());
56 57 58 59
  if (!outDir.existsSync()) {
    outDir.createSync(recursive: true);
  }

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

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

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

75 76 77 78 79 80 81 82
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;
83
  final String dilPath = globals.fs.path.join(outDir, '$appName.dil');
84

85
  final String elf = globals.fs.path.join(outDir, 'elf.aotsnapshot');
86

87
  final String genSnapshot = globals.artifacts.getArtifactPath(
88 89 90 91 92 93 94 95
    Artifact.genSnapshot,
    platform: targetPlatform,
    mode: buildInfo.mode,
  );

  final List<String> command = <String>[
    genSnapshot,
    '--deterministic',
96 97
    '--snapshot_kind=app-aot-elf',
    '--elf=$elf',
98 99 100 101
    if (buildInfo.isDebug) '--enable-asserts',
    dilPath,
  ];
  int result;
102
  final Status status = globals.logger.startProgress(
103 104 105
    'Compiling Fuchsia application to native code...',
  );
  try {
106
    result = await globals.processUtils.stream(command, trace: true);
107 108 109 110 111 112 113 114
  } finally {
    status.cancel();
  }
  if (result != 0) {
    throwToolExit('Build process failed');
  }
}

115
Future<void> _buildAssets(
116 117 118 119
  FuchsiaProject fuchsiaProject,
  String target, // lib/main.dart
  BuildInfo buildInfo,
) async {
120 121 122 123 124 125 126 127
  final String assetDir = getAssetBuildDirectory();
  final AssetBundle assets = await buildAssets(
    manifestPath: fuchsiaProject.project.pubspecFile.path,
    packagesPath: fuchsiaProject.project.packagesFile.path,
    assetDirPath: assetDir,
  );

  final Map<String, DevFSContent> assetEntries =
128
      Map<String, DevFSContent>.of(assets.entries);
129
  await writeBundle(globals.fs.directory(assetDir), assetEntries);
130 131 132

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

135
  final File destFile = globals.fs.file(assetManifest);
136 137 138
  await destFile.create(recursive: true);
  final IOSink outFile = destFile.openWrite();

139
  for (final String path in assets.entries.keys) {
140 141 142 143 144 145
    outFile.write('data/$appName/$path=$assetDir/$path\n');
  }
  await outFile.flush();
  await outFile.close();
}

146
void _rewriteCmx(BuildMode mode, String runnerPackageSource, File src, File dst) {
147
  final Map<String, dynamic> cmx = castStringKeyedMap(json.decode(src.readAsStringSync()));
148 149 150 151 152 153 154 155 156
  // 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:
157
      runner = 'flutter_jit_runner';
158
      break;
159 160 161 162
    case BuildMode.profile:
      runner = 'flutter_aot_runner';
      break;
    case BuildMode.jitRelease:
163
      runner = 'flutter_jit_product_runner';
164
      break;
165 166 167
    case BuildMode.release:
      runner = 'flutter_aot_product_runner';
      break;
168 169 170 171
    default:
      throwToolExit('Fuchsia does not support build mode "$mode"');
      break;
  }
172
  cmx['runner'] = 'fuchsia-pkg://$runnerPackageSource/$runner#meta/$runner.cmx';
173 174 175
  dst.writeAsStringSync(json.encode(cmx));
}

176 177
// TODO(zra): Allow supplying a signing key.
Future<void> _buildPackage(
178 179 180
  FuchsiaProject fuchsiaProject,
  String target, // lib/main.dart
  BuildInfo buildInfo,
181
  String runnerPackageSource,
182
) async {
183
  final String outDir = getFuchsiaBuildDirectory();
184
  final String pkgDir = globals.fs.path.join(outDir, 'pkg');
185
  final String appName = fuchsiaProject.project.manifest.appName;
186 187
  final String pkgassets = globals.fs.path.join(outDir, '${appName}_pkgassets');
  final String packageManifest = globals.fs.path.join(pkgDir, 'package_manifest');
188

189
  final Directory pkg = globals.fs.directory(pkgDir);
190 191 192 193
  if (!pkg.existsSync()) {
    pkg.createSync(recursive: true);
  }

194
  final File srcCmx =
195 196
      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'));
197
  _rewriteCmx(buildInfo.mode, runnerPackageSource, srcCmx, dstCmx);
198

199
  final File manifestFile = globals.fs.file(packageManifest);
200 201

  if (buildInfo.usesAot) {
202
    final String elf = globals.fs.path.join(outDir, 'elf.aotsnapshot');
203
    manifestFile.writeAsStringSync(
204
      'data/$appName/app_aot_snapshot.so=$elf\n');
205
  } else {
206 207
    final String dilpmanifest = globals.fs.path.join(outDir, '$appName.dilpmanifest');
    manifestFile.writeAsStringSync(globals.fs.file(dilpmanifest).readAsStringSync());
208 209
  }

210
  manifestFile.writeAsStringSync(globals.fs.file(pkgassets).readAsStringSync(),
211
      mode: FileMode.append);
212
  manifestFile.writeAsStringSync('meta/$appName.cmx=${dstCmx.path}\n',
213 214 215 216
      mode: FileMode.append);
  manifestFile.writeAsStringSync('meta/package=$pkgDir/meta/package\n',
      mode: FileMode.append);

217
  final FuchsiaPM fuchsiaPM = fuchsiaSdk.fuchsiaPM;
218 219 220
  if (!await fuchsiaPM.init(pkgDir, appName)) {
    return;
  }
221
  if (!await fuchsiaPM.build(pkgDir, packageManifest)) {
222 223
    return;
  }
224
  if (!await fuchsiaPM.archive(pkgDir, packageManifest)) {
225 226 227
    return;
  }
}