Unverified Commit 8841afeb authored by Zachary Anderson's avatar Zachary Anderson Committed by GitHub

[flutter_tool] Build a Fuchsia package (#32519)

parent 054d9bb2
-F-MfCBXXKddVWXabbdfQ1im71SzFu9dqYMTJs7TmqoC VrOFdEZI8hvuDSy4i4L4RgPff9jy9_5uBViSXQ71axwC
Hc4DxFUkoHicT-ytOLaxzkT4QYS5uiMXXcZO6s9C1qcC 8XAyRTpJ8T1zVXsCPSY3WEEXLzsELLXZ3gsCmJ_LgQsC
...@@ -392,11 +392,16 @@ String getWebBuildDirectory() { ...@@ -392,11 +392,16 @@ String getWebBuildDirectory() {
return fs.path.join(getBuildDirectory(), 'web'); return fs.path.join(getBuildDirectory(), 'web');
} }
/// Returns the linux build output directory. /// Returns the Linux build output directory.
String getLinuxBuildDirectory() { String getLinuxBuildDirectory() {
return fs.path.join(getBuildDirectory(), 'linux'); return fs.path.join(getBuildDirectory(), 'linux');
} }
/// Returns the Fuchsia build output directory.
String getFuchsiaBuildDirectory() {
return fs.path.join(getBuildDirectory(), 'fuchsia');
}
/// Returns directory used by incremental compiler (IKG - incremental kernel /// Returns directory used by incremental compiler (IKG - incremental kernel
/// generator) to store cached intermediate state. /// generator) to store cached intermediate state.
String getIncrementalCompilerByteStoreDirectory() { String getIncrementalCompilerByteStoreDirectory() {
......
...@@ -13,6 +13,7 @@ import 'build_aot.dart'; ...@@ -13,6 +13,7 @@ import 'build_aot.dart';
import 'build_apk.dart'; import 'build_apk.dart';
import 'build_appbundle.dart'; import 'build_appbundle.dart';
import 'build_bundle.dart'; import 'build_bundle.dart';
import 'build_fuchsia.dart';
import 'build_ios.dart'; import 'build_ios.dart';
import 'build_web.dart'; import 'build_web.dart';
...@@ -27,6 +28,7 @@ class BuildCommand extends FlutterCommand { ...@@ -27,6 +28,7 @@ class BuildCommand extends FlutterCommand {
addSubcommand(BuildMacosCommand()); addSubcommand(BuildMacosCommand());
addSubcommand(BuildLinuxCommand()); addSubcommand(BuildLinuxCommand());
addSubcommand(BuildWindowsCommand()); addSubcommand(BuildWindowsCommand());
addSubcommand(BuildFuchsiaCommand(verboseHelp: verboseHelp));
} }
@override @override
......
// Copyright 2019 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';
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/platform.dart';
import '../build_info.dart';
import '../cache.dart';
import '../fuchsia/fuchsia_build.dart';
import '../project.dart';
import '../runner/flutter_command.dart' show FlutterCommandResult;
import 'build.dart';
/// A command to build a Fuchsia target.
class BuildFuchsiaCommand extends BuildSubCommand {
BuildFuchsiaCommand({bool verboseHelp = false}) {
usesTargetOption();
addBuildModeFlags(verboseHelp: verboseHelp);
}
@override
final String name = 'fuchsia';
@override
bool isExperimental = true;
@override
bool hidden = true;
@override
Future<Set<DevelopmentArtifact>> get requiredArtifacts async => <DevelopmentArtifact>{
DevelopmentArtifact.fuchsia,
DevelopmentArtifact.universal,
};
@override
String get description => 'build the Fuchsia target (Experimental).';
@override
Future<FlutterCommandResult> runCommand() async {
Cache.releaseLockEarly();
final BuildInfo buildInfo = getBuildInfo();
final FlutterProject flutterProject = FlutterProject.current();
if (!platform.isLinux && !platform.isMacOS) {
throwToolExit('"build Fuchsia" only supported on Linux and MacOS hosts.');
}
if (!flutterProject.fuchsia.existsSync()) {
throwToolExit('No Fuchsia project configured.');
}
final String appName = flutterProject.fuchsia.project.manifest.appName;
final String cmxPath = fs.path.join(
flutterProject.fuchsia.meta.path, '$appName.cmx');
final File cmxFile = fs.file(cmxPath);
if (!cmxFile.existsSync()) {
throwToolExit('Fuchsia build requires a .cmx file at $cmxPath for the app');
}
await buildFuchsia(
fuchsiaProject: flutterProject.fuchsia,
target: targetFile,
buildInfo: buildInfo);
return null;
}
}
...@@ -27,6 +27,8 @@ import 'devfs.dart'; ...@@ -27,6 +27,8 @@ import 'devfs.dart';
import 'device.dart'; import 'device.dart';
import 'doctor.dart'; import 'doctor.dart';
import 'emulator.dart'; import 'emulator.dart';
import 'fuchsia/fuchsia_kernel_compiler.dart';
import 'fuchsia/fuchsia_pm.dart';
import 'fuchsia/fuchsia_sdk.dart'; import 'fuchsia/fuchsia_sdk.dart';
import 'fuchsia/fuchsia_workflow.dart'; import 'fuchsia/fuchsia_workflow.dart';
import 'ios/cocoapods.dart'; import 'ios/cocoapods.dart';
...@@ -52,36 +54,38 @@ Future<T> runInContext<T>( ...@@ -52,36 +54,38 @@ Future<T> runInContext<T>(
body: runner, body: runner,
overrides: overrides, overrides: overrides,
fallbacks: <Type, Generator>{ fallbacks: <Type, Generator>{
AndroidLicenseValidator: () => AndroidLicenseValidator(),
AndroidSdk: AndroidSdk.locateAndroidSdk, AndroidSdk: AndroidSdk.locateAndroidSdk,
AndroidStudio: AndroidStudio.latestValid, AndroidStudio: AndroidStudio.latestValid,
AndroidWorkflow: () => AndroidWorkflow(),
AndroidValidator: () => AndroidValidator(), AndroidValidator: () => AndroidValidator(),
AndroidLicenseValidator: () => AndroidLicenseValidator(), AndroidWorkflow: () => AndroidWorkflow(),
ApplicationPackageFactory: () => ApplicationPackageFactory(), ApplicationPackageFactory: () => ApplicationPackageFactory(),
Artifacts: () => CachedArtifacts(), Artifacts: () => CachedArtifacts(),
AssetBundleFactory: () => AssetBundleFactory.defaultInstance, AssetBundleFactory: () => AssetBundleFactory.defaultInstance,
BotDetector: () => const BotDetector(), BotDetector: () => const BotDetector(),
Cache: () => Cache(), Cache: () => Cache(),
ChromeLauncher: () => const ChromeLauncher(),
CocoaPods: () => CocoaPods(), CocoaPods: () => CocoaPods(),
CocoaPodsValidator: () => const CocoaPodsValidator(), CocoaPodsValidator: () => const CocoaPodsValidator(),
Config: () => Config(), Config: () => Config(),
ChromeLauncher: () => const ChromeLauncher(),
DevFSConfig: () => DevFSConfig(), DevFSConfig: () => DevFSConfig(),
DeviceManager: () => DeviceManager(), DeviceManager: () => DeviceManager(),
Doctor: () => const Doctor(), Doctor: () => const Doctor(),
DoctorValidatorsProvider: () => DoctorValidatorsProvider.defaultInstance, DoctorValidatorsProvider: () => DoctorValidatorsProvider.defaultInstance,
EmulatorManager: () => EmulatorManager(), EmulatorManager: () => EmulatorManager(),
FuchsiaSdk: () => FuchsiaSdk(),
FuchsiaArtifacts: () => FuchsiaArtifacts.find(),
FuchsiaWorkflow: () => FuchsiaWorkflow(),
Flags: () => const EmptyFlags(), Flags: () => const EmptyFlags(),
FlutterVersion: () => FlutterVersion(const SystemClock()), FlutterVersion: () => FlutterVersion(const SystemClock()),
FuchsiaArtifacts: () => FuchsiaArtifacts.find(),
FuchsiaKernelCompiler: () => FuchsiaKernelCompiler(),
FuchsiaPM: () => FuchsiaPM(),
FuchsiaSdk: () => FuchsiaSdk(),
FuchsiaWorkflow: () => FuchsiaWorkflow(),
GenSnapshot: () => const GenSnapshot(), GenSnapshot: () => const GenSnapshot(),
HotRunnerConfig: () => HotRunnerConfig(), HotRunnerConfig: () => HotRunnerConfig(),
IMobileDevice: () => const IMobileDevice(), IMobileDevice: () => const IMobileDevice(),
IOSSimulatorUtils: () => IOSSimulatorUtils(), IOSSimulatorUtils: () => IOSSimulatorUtils(),
IOSWorkflow: () => const IOSWorkflow(),
IOSValidator: () => const IOSValidator(), IOSValidator: () => const IOSValidator(),
IOSWorkflow: () => const IOSWorkflow(),
KernelCompilerFactory: () => const KernelCompilerFactory(), KernelCompilerFactory: () => const KernelCompilerFactory(),
LinuxWorkflow: () => const LinuxWorkflow(), LinuxWorkflow: () => const LinuxWorkflow(),
Logger: () => platform.isWindows ? WindowsStdoutLogger() : StdoutLogger(), Logger: () => platform.isWindows ? WindowsStdoutLogger() : StdoutLogger(),
...@@ -89,13 +93,13 @@ Future<T> runInContext<T>( ...@@ -89,13 +93,13 @@ Future<T> runInContext<T>(
OperatingSystemUtils: () => OperatingSystemUtils(), OperatingSystemUtils: () => OperatingSystemUtils(),
PlistBuddy: () => const PlistBuddy(), PlistBuddy: () => const PlistBuddy(),
SimControl: () => SimControl(), SimControl: () => SimControl(),
SystemClock: () => const SystemClock(),
Stdio: () => const Stdio(), Stdio: () => const Stdio(),
SystemClock: () => const SystemClock(),
TimeoutConfiguration: () => const TimeoutConfiguration(), TimeoutConfiguration: () => const TimeoutConfiguration(),
Usage: () => Usage(), Usage: () => Usage(),
UserMessages: () => UserMessages(), UserMessages: () => UserMessages(),
WindowsWorkflow: () => const WindowsWorkflow(),
WebCompiler: () => const WebCompiler(), WebCompiler: () => const WebCompiler(),
WindowsWorkflow: () => const WindowsWorkflow(),
Xcode: () => Xcode(), Xcode: () => Xcode(),
XcodeProjectInterpreter: () => XcodeProjectInterpreter(), XcodeProjectInterpreter: () => XcodeProjectInterpreter(),
}, },
......
// Copyright 2019 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 'package:meta/meta.dart';
import '../asset.dart';
import '../base/file_system.dart';
import '../base/io.dart';
import '../build_info.dart';
import '../bundle.dart';
import '../devfs.dart';
import '../project.dart';
import 'fuchsia_kernel_compiler.dart';
import 'fuchsia_pm.dart';
// 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.
Future<void> buildFuchsia(
{@required FuchsiaProject fuchsiaProject,
@required String target, // E.g., lib/main.dart
BuildInfo buildInfo = BuildInfo.debug}) async {
final Directory outDir = fs.directory(getFuchsiaBuildDirectory());
if (!outDir.existsSync()) {
outDir.createSync(recursive: true);
}
await fuchsiaKernelCompiler.build(
fuchsiaProject: fuchsiaProject, target: target, buildInfo: buildInfo);
await _buildAssets(fuchsiaProject, target, buildInfo);
await _buildPackage(fuchsiaProject, target, buildInfo);
}
Future<void> _buildAssets(
FuchsiaProject fuchsiaProject,
String target, // lib/main.dart
BuildInfo buildInfo) async {
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);
await writeBundle(fs.directory(assetDir), assetEntries);
final String appName = fuchsiaProject.project.manifest.appName;
final String outDir = getFuchsiaBuildDirectory();
final String assetManifest = fs.path.join(outDir, '${appName}_pkgassets');
final File destFile = fs.file(assetManifest);
await destFile.create(recursive: true);
final IOSink outFile = destFile.openWrite();
for (String path in assets.entries.keys) {
outFile.write('data/$appName/$path=$assetDir/$path\n');
}
await outFile.flush();
await outFile.close();
}
// TODO(zra): Allow supplying a signing key.
Future<void> _buildPackage(
FuchsiaProject fuchsiaProject,
String target, // lib/main.dart
BuildInfo buildInfo) async {
final String outDir = getFuchsiaBuildDirectory();
final String pkgDir = fs.path.join(outDir, 'pkg');
final String appName = fuchsiaProject.project.manifest.appName;
final String dilpmanifest = fs.path.join(outDir, '$appName.dilpmanifest');
final String pkgassets = fs.path.join(outDir, '${appName}_pkgassets');
final String packageManifest = fs.path.join(pkgDir, 'package_manifest');
final String devKeyPath = fs.path.join(pkgDir, 'development.key');
final Directory pkg = fs.directory(pkgDir);
if (!pkg.existsSync()) {
pkg.createSync(recursive: true);
}
// Concatenate dilpmanifest and pkgassets into package_manifest.
final File manifestFile = fs.file(packageManifest);
manifestFile.writeAsStringSync(fs.file(dilpmanifest).readAsStringSync());
manifestFile.writeAsStringSync(fs.file(pkgassets).readAsStringSync(),
mode: FileMode.append);
manifestFile.writeAsStringSync(
'meta/$appName.cmx=${fuchsiaProject.meta.path}/$appName.cmx\n',
mode: FileMode.append);
manifestFile.writeAsStringSync('meta/package=$pkgDir/meta/package\n',
mode: FileMode.append);
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;
}
}
// Copyright 2019 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 'package:meta/meta.dart';
import '../artifacts.dart';
import '../base/common.dart';
import '../base/context.dart';
import '../base/file_system.dart';
import '../base/io.dart';
import '../base/logger.dart';
import '../base/process_manager.dart';
import '../build_info.dart';
import '../convert.dart';
import '../globals.dart';
import '../project.dart';
import 'fuchsia_sdk.dart';
/// The [FuchsiaKernelCompiler] instance.
FuchsiaKernelCompiler get fuchsiaKernelCompiler =>
context.get<FuchsiaKernelCompiler>();
class FuchsiaKernelCompiler {
/// Compiles the [fuchsiaProject] with entrypoint [target] to a collection of
/// .dilp files (consisting of the app split along package: boundaries, but
/// the Flutter tool should make no use of that fact), and a manifest that
/// refers to them.
Future<void> build({
@required FuchsiaProject fuchsiaProject,
@required String target, // E.g., lib/main.dart
BuildInfo buildInfo = BuildInfo.debug,
}) async {
// TODO(zra): Use filesystem root and scheme information from buildInfo.
const String multiRootScheme = 'main-root';
final String packagesFile = fuchsiaProject.project.packagesFile.path;
final String outDir = getFuchsiaBuildDirectory();
final String appName = fuchsiaProject.project.manifest.appName;
final String fsRoot = fuchsiaProject.project.directory.path;
final String relativePackagesFile =
fs.path.relative(packagesFile, from: fsRoot);
final String manifestPath = fs.path.join(outDir, '$appName.dilpmanifest');
List<String> flags = <String>[
// https://github.com/dart-lang/sdk/issues/36639:
// Remove when new constant eval supports dilp files.
'--enable-experiment=no-constant-update-2018',
'--target', 'flutter_runner',
'--platform', fuchsiaArtifacts.platformKernelDill.path,
'--filesystem-scheme', 'main-root',
'--filesystem-root', fsRoot,
'--packages', '$multiRootScheme:///$relativePackagesFile',
'--output', fs.path.join(outDir, '$appName.dil'),
'--no-link-platform',
'--split-output-by-packages',
'--manifest', manifestPath,
'--component-name', appName,
];
if (buildInfo.isDebug) {
flags += <String>[
'--embed-sources',
];
} else if (buildInfo.isProfile) {
flags += <String>[
'--no-embed-sources',
'-Ddart.vm.profile=true',
'--gen-bytecode',
'--drop-ast',
];
} else if (buildInfo.isRelease) {
flags += <String>[
'--no-embed-sources',
'-Ddart.vm.release=true',
'--gen-bytecode',
'--drop-ast',
];
} else {
throwToolExit('Expected build type to be debug, profile, or release');
}
flags += <String>[
'$multiRootScheme:///$target',
];
final List<String> command = <String>[
artifacts.getArtifactPath(Artifact.engineDartBinary),
fuchsiaArtifacts.kernelCompiler.path,
]..addAll(flags);
printTrace("Running: '${command.join(' ')}'");
final Process process = await processManager.start(command);
final Status status = logger.startProgress(
'Building Fuchsia application...',
timeout: null,
);
int result;
try {
process.stderr
.transform(utf8.decoder)
.transform(const LineSplitter())
.listen(printError);
process.stdout
.transform(utf8.decoder)
.transform(const LineSplitter())
.listen(printTrace);
result = await process.exitCode;
} finally {
status.cancel();
}
if (result != 0) {
throwToolExit('Build process failed');
}
}
}
// Copyright 2019 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 '../base/context.dart';
import '../base/io.dart';
import '../base/process_manager.dart';
import '../globals.dart';
import 'fuchsia_sdk.dart';
/// The [FuchsiaPM] instance.
FuchsiaPM get fuchsiaPM => context.get<FuchsiaPM>();
/// This is a basic wrapper class for the Fuchsia SDK's `pm` tool.
class FuchsiaPM {
/// Initializes the staging area at [buildPath] for creating the Fuchsia
/// package for the app named [appName].
///
/// When successful, this creates a file under [buildPath] at `meta/package`.
///
/// NB: The [buildPath] should probably be e.g. `build/fuchsia/pkg`, and the
/// [appName] should probably be the name of the app from the pubspec file.
Future<bool> init(String buildPath, String appName) async {
final List<String> command = <String>[
fuchsiaArtifacts.pm.path,
'-o',
buildPath,
'-n',
appName,
'init',
];
printTrace("Running: '${command.join(' ')}'");
final ProcessResult result = await processManager.run(command);
if (result.exitCode != 0) {
printError('Error initializing Fuchsia package for $appName: ');
printError(result.stdout);
printError(result.stderr);
return false;
}
return true;
}
/// Generates a new private key to be used to sign a Fuchsia package.
///
/// [buildPath] should be the same [buildPath] passed to [init].
Future<bool> genkey(String buildPath, String outKeyPath) async {
final List<String> command = <String>[
fuchsiaArtifacts.pm.path,
'-o',
buildPath,
'-k',
outKeyPath,
'genkey',
];
printTrace("Running: '${command.join(' ')}'");
final ProcessResult result = await processManager.run(command);
if (result.exitCode != 0) {
printError('Error generating key for Fuchsia package: ');
printError(result.stdout);
printError(result.stderr);
return false;
}
return true;
}
/// Updates, signs, and seals a Fuchsia package.
///
/// [buildPath] should be the same [buildPath] passed to [init].
/// [manifestPath] must be a file containing lines formatted as follows:
///
/// data/path/to/file/in/the/package=/path/to/file/on/the/host
///
/// which describe the contents of the Fuchsia package. It must also contain
/// two other entries:
///
/// meta/$APPNAME.cmx=/path/to/cmx/on/the/host/$APPNAME.cmx
/// meta/package=/path/to/package/file/from/init/package
///
/// where $APPNAME is the same [appName] passed to [init], and meta/package
/// is set up to be the file `meta/package` created by [init].
Future<bool> build(
String buildPath, String keyPath, String manifestPath) async {
final List<String> command = <String>[
fuchsiaArtifacts.pm.path,
'-o',
buildPath,
'-k',
keyPath,
'-m',
manifestPath,
'build',
];
printTrace("Running: '${command.join(' ')}'");
final ProcessResult result = await processManager.run(command);
if (result.exitCode != 0) {
printError('Error building Fuchsia package: ');
printError(result.stdout);
printError(result.stderr);
return false;
}
return true;
}
/// Constructs a .far representation of the Fuchsia package.
///
/// When successful, creates a file `app_name-0.far` under [buildPath], which
/// is the Fuchsia package.
///
/// [buildPath] should be the same path passed to [init], and [manfiestPath]
/// should be the same manifest passed to [build].
Future<bool> archive(
String buildPath, String keyPath, String manifestPath) async {
final List<String> command = <String>[
fuchsiaArtifacts.pm.path,
'-o',
buildPath,
'-k',
keyPath,
'-m',
manifestPath,
'archive',
];
printTrace("Running: '${command.join(' ')}'");
final ProcessResult result = await processManager.run(command);
if (result.exitCode != 0) {
printError('Error archiving Fuchsia package: ');
printError(result.stdout);
printError(result.stderr);
return false;
}
return true;
}
}
...@@ -86,6 +86,8 @@ class FuchsiaArtifacts { ...@@ -86,6 +86,8 @@ class FuchsiaArtifacts {
this.devFinder, this.devFinder,
this.platformKernelDill, this.platformKernelDill,
this.flutterPatchedSdk, this.flutterPatchedSdk,
this.kernelCompiler,
this.pm,
}); });
/// Creates a new [FuchsiaArtifacts] using the cached Fuchsia SDK. /// Creates a new [FuchsiaArtifacts] using the cached Fuchsia SDK.
...@@ -116,6 +118,9 @@ class FuchsiaArtifacts { ...@@ -116,6 +118,9 @@ class FuchsiaArtifacts {
dartPrebuilts, 'flutter_runner', 'platform_strong.dill')), dartPrebuilts, 'flutter_runner', 'platform_strong.dill')),
flutterPatchedSdk: fs.file(fs.path.join( flutterPatchedSdk: fs.file(fs.path.join(
dartPrebuilts, 'flutter_runner')), dartPrebuilts, 'flutter_runner')),
kernelCompiler: fs.file(fs.path.join(
dartPrebuilts, 'kernel_compiler.snapshot')),
pm: fs.file(fs.path.join(tools, 'pm')),
); );
} }
...@@ -135,4 +140,10 @@ class FuchsiaArtifacts { ...@@ -135,4 +140,10 @@ class FuchsiaArtifacts {
/// The directory containing [platformKernelDill]. /// The directory containing [platformKernelDill].
final File flutterPatchedSdk; final File flutterPatchedSdk;
/// The snapshot of the Fuchsia kernel compiler.
final File kernelCompiler;
/// The pm tool.
final File pm;
} }
...@@ -91,22 +91,32 @@ class FlutterProject { ...@@ -91,22 +91,32 @@ class FlutterProject {
} }
/// The iOS sub project of this project. /// The iOS sub project of this project.
IosProject get ios => IosProject.fromFlutter(this); IosProject _ios;
IosProject get ios => _ios ??= IosProject.fromFlutter(this);
/// The Android sub project of this project. /// The Android sub project of this project.
AndroidProject get android => AndroidProject._(this); AndroidProject _android;
AndroidProject get android => _android ??= AndroidProject._(this);
/// The web sub project of this project. /// The web sub project of this project.
WebProject get web => WebProject._(this); WebProject _web;
WebProject get web => _web ??= WebProject._(this);
/// The macos sub project of this project. /// The MacOS sub project of this project.
MacOSProject get macos => MacOSProject._(this); MacOSProject _macos;
MacOSProject get macos => _macos ??= MacOSProject._(this);
/// The linux sub project of this project. /// The Linux sub project of this project.
LinuxProject get linux => LinuxProject._(this); LinuxProject _linux;
LinuxProject get linux => _linux ??= LinuxProject._(this);
/// The windows sub project of this project. /// The Windows sub project of this project.
WindowsProject get windows => WindowsProject._(this); WindowsProject _windows;
WindowsProject get windows => _windows ??= WindowsProject._(this);
/// The Fuchsia sub project of this project.
FuchsiaProject _fuchsia;
FuchsiaProject get fuchsia => _fuchsia ??= FuchsiaProject._(this);
/// The `pubspec.yaml` file of this project. /// The `pubspec.yaml` file of this project.
File get pubspecFile => directory.childFile('pubspec.yaml'); File get pubspecFile => directory.childFile('pubspec.yaml');
...@@ -606,3 +616,20 @@ class LinuxProject { ...@@ -606,3 +616,20 @@ class LinuxProject {
/// The Linux project makefile. /// The Linux project makefile.
File get makeFile => editableHostAppDirectory.childFile('Makefile'); File get makeFile => editableHostAppDirectory.childFile('Makefile');
} }
/// The Fuchisa sub project
class FuchsiaProject {
FuchsiaProject._(this.project);
final FlutterProject project;
Directory _editableHostAppDirectory;
Directory get editableHostAppDirectory =>
_editableHostAppDirectory ??= project.directory.childDirectory('fuchsia');
bool existsSync() => editableHostAppDirectory.existsSync();
Directory _meta;
Directory get meta =>
_meta ??= editableHostAppDirectory.childDirectory('meta');
}
// Copyright 2019 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 'package:file/memory.dart';
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/build.dart';
import 'package:flutter_tools/src/fuchsia/fuchsia_kernel_compiler.dart';
import 'package:flutter_tools/src/fuchsia/fuchsia_pm.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:meta/meta.dart';
import 'package:mockito/mockito.dart';
import '../src/common.dart';
import '../src/context.dart';
import '../src/mocks.dart';
void main() {
Cache.disableLocking();
MemoryFileSystem memoryFileSystem;
MockPlatform linuxPlatform;
MockPlatform windowsPlatform;
MockFuchsiaPM fuchsiaPM;
MockFuchsiaKernelCompiler fuchsiaKernelCompiler;
setUp(() {
memoryFileSystem = MemoryFileSystem();
linuxPlatform = MockPlatform();
windowsPlatform = MockPlatform();
fuchsiaPM = MockFuchsiaPM();
fuchsiaKernelCompiler = MockFuchsiaKernelCompiler();
when(linuxPlatform.isLinux).thenReturn(true);
when(windowsPlatform.isWindows).thenReturn(true);
when(windowsPlatform.isLinux).thenReturn(false);
when(windowsPlatform.isMacOS).thenReturn(false);
});
testUsingContext('Fuchsia build fails when there is no fuchsia project',
() async {
final BuildCommand command = BuildCommand();
applyMocksToCommand(command);
expect(
createTestCommandRunner(command)
.run(const <String>['build', 'fuchsia']),
throwsA(isInstanceOf<ToolExit>()));
}, overrides: <Type, Generator>{
Platform: () => linuxPlatform,
FileSystem: () => memoryFileSystem,
});
testUsingContext('Fuchsia build fails when there is no cmx file', () async {
final BuildCommand command = BuildCommand();
applyMocksToCommand(command);
fs.directory('fuchsia').createSync(recursive: true);
fs.file('.packages').createSync();
fs.file('pubspec.yaml').createSync();
expect(
createTestCommandRunner(command)
.run(const <String>['build', 'fuchsia']),
throwsA(isInstanceOf<ToolExit>()));
}, overrides: <Type, Generator>{
Platform: () => linuxPlatform,
FileSystem: () => memoryFileSystem,
});
testUsingContext('Fuchsia build fails on Windows platform', () async {
final BuildCommand command = BuildCommand();
applyMocksToCommand(command);
const String appName = 'app_name';
fs
.file(fs.path.join('fuchsia', 'meta', '$appName.cmx'))
.createSync(recursive: true);
fs.file('.packages').createSync();
final File pubspecFile = fs.file('pubspec.yaml')..createSync();
pubspecFile.writeAsStringSync('name: $appName');
expect(
createTestCommandRunner(command)
.run(const <String>['build', 'fuchsia']),
throwsA(isInstanceOf<ToolExit>()));
}, overrides: <Type, Generator>{
Platform: () => windowsPlatform,
FileSystem: () => memoryFileSystem,
});
testUsingContext('Fuchsia build parts fit together right', () async {
final BuildCommand command = BuildCommand();
applyMocksToCommand(command);
const String appName = 'app_name';
fs
.file(fs.path.join('fuchsia', 'meta', '$appName.cmx'))
.createSync(recursive: true);
fs.file('.packages').createSync();
fs.file(fs.path.join('lib', 'main.dart')).createSync(recursive: true);
final File pubspecFile = fs.file('pubspec.yaml')..createSync();
pubspecFile.writeAsStringSync('name: $appName');
await createTestCommandRunner(command)
.run(const <String>['build', 'fuchsia']);
final String farPath =
fs.path.join(getFuchsiaBuildDirectory(), 'pkg', 'app_name-0.far');
expect(fs.file(farPath).existsSync(), isTrue);
}, overrides: <Type, Generator>{
Platform: () => linuxPlatform,
FileSystem: () => memoryFileSystem,
FuchsiaPM: () => fuchsiaPM,
FuchsiaKernelCompiler: () => fuchsiaKernelCompiler,
});
}
class MockPlatform extends Mock implements Platform {
@override
Map<String, String> environment = <String, String>{
'FLUTTER_ROOT': '/',
};
}
class MockFuchsiaPM extends Mock implements FuchsiaPM {
String _appName;
@override
Future<bool> init(String buildPath, String appName) async {
if (!fs.directory(buildPath).existsSync()) {
return false;
}
fs
.file(fs.path.join(buildPath, 'meta', 'package'))
.createSync(recursive: true);
_appName = appName;
return true;
}
@override
Future<bool> genkey(String buildPath, String outKeyPath) async {
if (!fs.file(fs.path.join(buildPath, 'meta', 'package')).existsSync()) {
return false;
}
fs.file(outKeyPath).createSync(recursive: true);
return true;
}
@override
Future<bool> build(
String buildPath, String keyPath, String manifestPath) async {
if (!fs.file(fs.path.join(buildPath, 'meta', 'package')).existsSync() ||
!fs.file(keyPath).existsSync() ||
!fs.file(manifestPath).existsSync()) {
return false;
}
fs.file(fs.path.join(buildPath, 'meta.far')).createSync(recursive: true);
return true;
}
@override
Future<bool> archive(
String buildPath, String keyPath, String manifestPath) async {
if (!fs.file(fs.path.join(buildPath, 'meta', 'package')).existsSync() ||
!fs.file(keyPath).existsSync() ||
!fs.file(manifestPath).existsSync()) {
return false;
}
if (_appName == null) {
return false;
}
fs
.file(fs.path.join(buildPath, '$_appName-0.far'))
.createSync(recursive: true);
return true;
}
}
class MockFuchsiaKernelCompiler extends Mock implements FuchsiaKernelCompiler {
@override
Future<void> build({
@required FuchsiaProject fuchsiaProject,
@required String target, // E.g., lib/main.dart
BuildInfo buildInfo = BuildInfo.debug,
}) async {
final String outDir = getFuchsiaBuildDirectory();
final String appName = fuchsiaProject.project.manifest.appName;
final String manifestPath = fs.path.join(outDir, '$appName.dilpmanifest');
fs.file(manifestPath).createSync(recursive: true);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment