1// Copyright 2019 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'; 8 9import '../asset.dart'; 10import '../base/common.dart'; 11import '../base/file_system.dart'; 12import '../base/io.dart'; 13import '../build_info.dart'; 14import '../bundle.dart'; 15import '../convert.dart'; 16import '../devfs.dart'; 17import '../globals.dart'; 18import '../project.dart'; 19import '../reporting/reporting.dart'; 20 21import 'fuchsia_pm.dart'; 22import 'fuchsia_sdk.dart'; 23 24Future<void> _timedBuildStep(String name, Future<void> Function() action) async { 25 final Stopwatch sw = Stopwatch()..start(); 26 await action(); 27 printTrace('$name: ${sw.elapsedMilliseconds} ms.'); 28 flutterUsage.sendTiming('build', name, Duration(milliseconds: sw.elapsedMilliseconds)); 29} 30 31// Building a Fuchsia package has a few steps: 32// 1. Do the custom kernel compile using the kernel compiler from the Fuchsia 33// SDK. This produces .dilp files (among others) and a manifest file. 34// 2. Create a manifest file for assets. 35// 3. Using these manifests, use the Fuchsia SDK 'pm' tool to create the 36// Fuchsia package. 37Future<void> buildFuchsia( 38 {@required FuchsiaProject fuchsiaProject, 39 @required String target, // E.g., lib/main.dart 40 BuildInfo buildInfo = BuildInfo.debug}) async { 41 final Directory outDir = fs.directory(getFuchsiaBuildDirectory()); 42 if (!outDir.existsSync()) { 43 outDir.createSync(recursive: true); 44 } 45 46 await _timedBuildStep('fuchsia-kernel-compile', 47 () => fuchsiaSdk.fuchsiaKernelCompiler.build( 48 fuchsiaProject: fuchsiaProject, target: target, buildInfo: buildInfo)); 49 await _timedBuildStep('fuchsia-build-assets', 50 () => _buildAssets(fuchsiaProject, target, buildInfo)); 51 await _timedBuildStep('fuchsia-build-package', 52 () => _buildPackage(fuchsiaProject, target, buildInfo)); 53} 54 55Future<void> _buildAssets( 56 FuchsiaProject fuchsiaProject, 57 String target, // lib/main.dart 58 BuildInfo buildInfo) async { 59 final String assetDir = getAssetBuildDirectory(); 60 final AssetBundle assets = await buildAssets( 61 manifestPath: fuchsiaProject.project.pubspecFile.path, 62 packagesPath: fuchsiaProject.project.packagesFile.path, 63 assetDirPath: assetDir, 64 includeDefaultFonts: false, 65 ); 66 67 final Map<String, DevFSContent> assetEntries = 68 Map<String, DevFSContent>.from(assets.entries); 69 await writeBundle(fs.directory(assetDir), assetEntries); 70 71 final String appName = fuchsiaProject.project.manifest.appName; 72 final String outDir = getFuchsiaBuildDirectory(); 73 final String assetManifest = fs.path.join(outDir, '${appName}_pkgassets'); 74 75 final File destFile = fs.file(assetManifest); 76 await destFile.create(recursive: true); 77 final IOSink outFile = destFile.openWrite(); 78 79 for (String path in assets.entries.keys) { 80 outFile.write('data/$appName/$path=$assetDir/$path\n'); 81 } 82 await outFile.flush(); 83 await outFile.close(); 84} 85 86void _rewriteCmx(BuildMode mode, File src, File dst) { 87 final Map<String, dynamic> cmx = json.decode(src.readAsStringSync()); 88 // If the app author has already specified the runner in the cmx file, then 89 // do not override it with something else. 90 if (cmx.containsKey('runner')) { 91 dst.writeAsStringSync(json.encode(cmx)); 92 return; 93 } 94 String runner; 95 switch (mode) { 96 case BuildMode.debug: 97 case BuildMode.profile: 98 runner = 'flutter_jit_runner'; 99 break; 100 case BuildMode.release: 101 runner = 'flutter_jit_product_runner'; 102 break; 103 default: 104 throwToolExit('Fuchsia does not support build mode "$mode"'); 105 break; 106 } 107 cmx['runner'] = 'fuchsia-pkg://fuchsia.com/$runner#meta/$runner.cmx'; 108 dst.writeAsStringSync(json.encode(cmx)); 109} 110 111// TODO(zra): Allow supplying a signing key. 112Future<void> _buildPackage( 113 FuchsiaProject fuchsiaProject, 114 String target, // lib/main.dart 115 BuildInfo buildInfo) async { 116 final String outDir = getFuchsiaBuildDirectory(); 117 final String pkgDir = fs.path.join(outDir, 'pkg'); 118 final String appName = fuchsiaProject.project.manifest.appName; 119 final String dilpmanifest = fs.path.join(outDir, '$appName.dilpmanifest'); 120 final String pkgassets = fs.path.join(outDir, '${appName}_pkgassets'); 121 final String packageManifest = fs.path.join(pkgDir, 'package_manifest'); 122 final String devKeyPath = fs.path.join(pkgDir, 'development.key'); 123 124 final Directory pkg = fs.directory(pkgDir); 125 if (!pkg.existsSync()) { 126 pkg.createSync(recursive: true); 127 } 128 129 final File srcCmx = 130 fs.file(fs.path.join(fuchsiaProject.meta.path, '$appName.cmx')); 131 final File dstCmx = fs.file(fs.path.join(outDir, '$appName.cmx')); 132 _rewriteCmx(buildInfo.mode, srcCmx, dstCmx); 133 134 // Concatenate dilpmanifest and pkgassets into package_manifest. 135 final File manifestFile = fs.file(packageManifest); 136 manifestFile.writeAsStringSync(fs.file(dilpmanifest).readAsStringSync()); 137 manifestFile.writeAsStringSync(fs.file(pkgassets).readAsStringSync(), 138 mode: FileMode.append); 139 manifestFile.writeAsStringSync('meta/$appName.cmx=${dstCmx.path}\n', 140 mode: FileMode.append); 141 manifestFile.writeAsStringSync('meta/package=$pkgDir/meta/package\n', 142 mode: FileMode.append); 143 144 final FuchsiaPM fuchsiaPM = fuchsiaSdk.fuchsiaPM; 145 if (!await fuchsiaPM.init(pkgDir, appName)) { 146 return; 147 } 148 if (!await fuchsiaPM.genkey(pkgDir, devKeyPath)) { 149 return; 150 } 151 if (!await fuchsiaPM.build(pkgDir, devKeyPath, packageManifest)) { 152 return; 153 } 154 if (!await fuchsiaPM.archive(pkgDir, devKeyPath, packageManifest)) { 155 return; 156 } 157} 158