Unverified Commit 549f70c9 authored by Rich Kadel's avatar Rich Kadel Committed by GitHub

Remove outdated Fuchsia concepts (#107335)

Fuchsia will soon remove all support for Component Framework version 1
components (recognized by component manifests ending in `.cmx`).
Notably, some of the `flutter` tool commands for Fuchsia devices--
notably, but not limited to, those related to CFv1--are outdated, and
either do not work today or soon won't work.

This PR removes the outdated components and commands, replacing some
with the newer version, or simply removing the non-working features,
in some cases.
parent a3b531dd
{
"program": {
"data": "data/complex_layout"
},
"sandbox": {
"services": [
"fuchsia.cobalt.LoggerFactory",
"fuchsia.fonts.Provider",
"fuchsia.logger.LogSink",
"fuchsia.modular.Clipboard",
"fuchsia.modular.ContextWriter",
"fuchsia.modular.DeviceMap",
"fuchsia.modular.ModuleContext",
"fuchsia.sys.Environment",
"fuchsia.sys.Launcher",
"fuchsia.testing.runner.TestRunner",
"fuchsia.ui.input.ImeService",
"fuchsia.ui.policy.Presenter",
"fuchsia.ui.scenic.Scenic"
]
}
}
{
"program": {
"data": "data/stocks"
},
"sandbox": {
"services": [
"fuchsia.cobalt.LoggerFactory",
"fuchsia.fonts.Provider",
"fuchsia.logger.LogSink",
"fuchsia.modular.Clipboard",
"fuchsia.modular.ContextWriter",
"fuchsia.modular.DeviceMap",
"fuchsia.modular.ModuleContext",
"fuchsia.sys.Environment",
"fuchsia.sys.Launcher",
"fuchsia.testing.runner.TestRunner",
"fuchsia.ui.input.ImeService",
"fuchsia.ui.policy.Presenter",
"fuchsia.ui.scenic.Scenic"
]
}
}
{
"program": {
"data": "data/flutter_gallery"
},
"sandbox": {
"services": [
"fuchsia.cobalt.LoggerFactory",
"fuchsia.fonts.Provider",
"fuchsia.logger.LogSink",
"fuchsia.modular.Clipboard",
"fuchsia.sys.Environment",
"fuchsia.sys.Launcher",
"fuchsia.ui.input.ImeService",
"fuchsia.ui.policy.Presenter",
"fuchsia.ui.scenic.Scenic"
]
}
}
{
"program": {
"data": "data/flutter_gallery"
},
"sandbox": {
"services": [
"fuchsia.cobalt.LoggerFactory",
"fuchsia.fonts.Provider",
"fuchsia.logger.LogSink",
"fuchsia.modular.Clipboard",
"fuchsia.modular.ContextWriter",
"fuchsia.modular.DeviceMap",
"fuchsia.modular.ModuleContext",
"fuchsia.sys.Environment",
"fuchsia.sys.Launcher",
"fuchsia.testing.runner.TestRunner",
"fuchsia.ui.input.ImeService",
"fuchsia.ui.policy.Presenter",
"fuchsia.ui.scenic.Scenic"
]
}
}
{
"program": {
"data": "data/integration_ui"
},
"sandbox": {
"services": [
"fuchsia.cobalt.LoggerFactory",
"fuchsia.fonts.Provider",
"fuchsia.logger.LogSink",
"fuchsia.modular.Clipboard",
"fuchsia.modular.ContextWriter",
"fuchsia.modular.DeviceMap",
"fuchsia.modular.ModuleContext",
"fuchsia.sys.Environment",
"fuchsia.sys.Launcher",
"fuchsia.testing.runner.TestRunner",
"fuchsia.ui.input.ImeService",
"fuchsia.ui.policy.Presenter",
"fuchsia.ui.scenic.Scenic"
]
}
}
{
"program": {
"data": "data/hello_world"
},
"sandbox": {
"services": [
"fuchsia.cobalt.LoggerFactory",
"fuchsia.fonts.Provider",
"fuchsia.logger.LogSink",
"fuchsia.modular.Clipboard",
"fuchsia.sys.Environment",
"fuchsia.sys.Launcher",
"fuchsia.ui.input.ImeService",
"fuchsia.ui.policy.Presenter",
"fuchsia.ui.scenic.Scenic"
]
}
}
......@@ -14,7 +14,6 @@ import 'build_aar.dart';
import 'build_apk.dart';
import 'build_appbundle.dart';
import 'build_bundle.dart';
import 'build_fuchsia.dart';
import 'build_ios.dart';
import 'build_ios_framework.dart';
import 'build_macos_framework.dart';
......@@ -43,7 +42,6 @@ class BuildCommand extends FlutterCommand {
verboseHelp: verboseHelp
));
_addSubcommand(BuildWindowsCommand(verboseHelp: verboseHelp));
_addSubcommand(BuildFuchsiaCommand(verboseHelp: verboseHelp));
}
void _addSubcommand(BuildSubCommand command) {
......@@ -83,8 +81,11 @@ abstract class BuildSubCommand extends FlutterCommand {
@protected
void displayNullSafetyMode(BuildInfo buildInfo) {
globals.printStatus('');
if (buildInfo.nullSafetyMode == NullSafetyMode.sound) {
globals.printStatus('💪 Building with sound null safety 💪', emphasis: true);
if (buildInfo.nullSafetyMode == NullSafetyMode.sound) {
globals.printStatus(
'💪 Building with sound null safety 💪',
emphasis: true,
);
} else {
globals.printStatus(
'Building without sound null safety',
......
// Copyright 2014 The Flutter 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/common.dart';
import '../build_info.dart';
import '../cache.dart';
import '../features.dart';
import '../fuchsia/fuchsia_build.dart';
import '../fuchsia/fuchsia_pm.dart';
import '../globals.dart' as globals;
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({
required bool verboseHelp,
}) : super(verboseHelp: verboseHelp) {
addTreeShakeIconsFlag();
usesTargetOption();
usesDartDefineOption();
addBuildModeFlags(verboseHelp: verboseHelp);
addNullSafetyModeOptions(hide: !verboseHelp);
addEnableExperimentation(hide: !verboseHelp);
argParser.addOption(
'runner-source',
help: 'The package source to use for the flutter_runner. '
'"${FuchsiaPackageServer.deviceHost}" implies using a runner already on the device. '
'"${FuchsiaPackageServer.toolHost}" implies using a runner distributed with Flutter.',
allowed: <String>[
FuchsiaPackageServer.deviceHost,
FuchsiaPackageServer.toolHost,
],
defaultsTo: FuchsiaPackageServer.toolHost,
);
argParser.addOption('target-platform',
defaultsTo: 'fuchsia-x64',
allowed: <String>['fuchsia-arm64', 'fuchsia-x64'],
help: 'The target platform for which the app is compiled.',
);
}
@override
final String name = 'fuchsia';
@override
bool hidden = true;
@override
Future<Set<DevelopmentArtifact>> get requiredArtifacts async => <DevelopmentArtifact>{
DevelopmentArtifact.fuchsia,
};
@override
String get description => 'Build the Fuchsia target (Experimental).';
@override
bool get supported => globals.platform.isLinux || globals.platform.isMacOS;
@override
Future<FlutterCommandResult> runCommand() async {
if (!featureFlags.isFuchsiaEnabled) {
throwToolExit(
'"build fuchsia" is currently disabled. See "flutter config" for more '
'information.'
);
}
final BuildInfo buildInfo = await getBuildInfo();
final FlutterProject flutterProject = FlutterProject.current();
if (!supported) {
throwToolExit('"build fuchsia" is only supported on Linux and MacOS hosts.');
}
if (!flutterProject.fuchsia.existsSync()) {
throwToolExit('No Fuchsia project is configured.');
}
displayNullSafetyMode(buildInfo);
await buildFuchsia(
fuchsiaProject: flutterProject.fuchsia,
target: targetFile,
targetPlatform: getTargetPlatformForName(stringArgDeprecated('target-platform')!),
buildInfo: buildInfo,
runnerPackageSource: stringArgDeprecated('runner-source')!,
);
return FlutterCommandResult.success();
}
}
// Copyright 2014 The Flutter 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 '../artifacts.dart';
import '../asset.dart';
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/logger.dart';
import '../base/utils.dart';
import '../build_info.dart';
import '../bundle_builder.dart';
import '../convert.dart';
import '../devfs.dart';
import '../globals.dart' as globals;
import '../project.dart';
import 'fuchsia_pm.dart';
Future<void> _timedBuildStep(String name, Future<void> Function() action) async {
final Stopwatch sw = Stopwatch()..start();
await action();
globals.printTrace('$name: ${sw.elapsedMilliseconds} ms.');
globals.flutterUsage.sendTiming('build', name, Duration(milliseconds: sw.elapsedMilliseconds));
}
Future<void> _validateCmxFile(FuchsiaProject fuchsiaProject) async {
final String appName = fuchsiaProject.project.manifest.appName;
final String cmxPath = globals.fs.path.join(fuchsiaProject.meta.path, '$appName.cmx');
final File cmxFile = globals.fs.file(cmxPath);
if (!await cmxFile.exists()) {
throwToolExit('The Fuchsia build requires a .cmx file at $cmxPath for the app: $appName.');
}
}
// 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 TargetPlatform targetPlatform,
String? target, // E.g., lib/main.dart
BuildInfo buildInfo = BuildInfo.debug,
String runnerPackageSource = FuchsiaPackageServer.toolHost,
}) async {
final String targetPath = target ??= 'lib/main.dart';
await _validateCmxFile(fuchsiaProject);
final Directory outDir = globals.fs.directory(getFuchsiaBuildDirectory());
if (!outDir.existsSync()) {
outDir.createSync(recursive: true);
}
await _timedBuildStep('fuchsia-kernel-compile',
() => globals.fuchsiaSdk!.fuchsiaKernelCompiler.build(
fuchsiaProject: fuchsiaProject, target: targetPath, buildInfo: buildInfo));
if (buildInfo.usesAot) {
await _timedBuildStep('fuchsia-gen-snapshot',
() => _genSnapshot(fuchsiaProject, targetPath, buildInfo, targetPlatform));
}
await _timedBuildStep('fuchsia-build-assets',
() => _buildAssets(fuchsiaProject, targetPath, buildInfo));
await _timedBuildStep('fuchsia-build-package',
() => _buildPackage(fuchsiaProject, targetPath, buildInfo, runnerPackageSource));
}
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;
final String dilPath = globals.fs.path.join(outDir, '$appName.dil');
final String elf = globals.fs.path.join(outDir, 'elf.aotsnapshot');
final String genSnapshot = globals.artifacts!.getArtifactPath(
Artifact.genSnapshot,
platform: targetPlatform,
mode: buildInfo.mode,
);
final List<String> command = <String>[
genSnapshot,
'--deterministic',
'--snapshot_kind=app-aot-elf',
'--elf=$elf',
if (buildInfo.isDebug) '--enable-asserts',
dilPath,
];
int result;
final Status status = globals.logger.startProgress(
'Compiling Fuchsia application to native code...',
);
try {
result = await globals.processUtils.stream(command, trace: true);
} finally {
status.cancel();
}
if (result != 0) {
throwToolExit('Build process failed');
}
}
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,
);
if (assets == null) {
throwToolExit('Unable to find assets.', exitCode: 1);
}
final Map<String, DevFSContent> assetEntries = Map<String, DevFSContent>.of(
assets.entries,
);
await writeBundle(globals.fs.directory(assetDir), assetEntries, assets.entryKinds);
final String appName = fuchsiaProject.project.manifest.appName;
final String outDir = getFuchsiaBuildDirectory();
final String assetManifest = globals.fs.path.join(outDir, '${appName}_pkgassets');
final File destFile = globals.fs.file(assetManifest);
await destFile.create(recursive: true);
final IOSink outFile = destFile.openWrite();
for (final String path in assets.entries.keys) {
outFile.write('data/$appName/$path=$assetDir/$path\n');
}
await outFile.flush();
await outFile.close();
}
void _rewriteCmx(BuildMode mode, String runnerPackageSource, File src, File dst) {
final Map<String, Object?> cmx = castStringKeyedMap(json.decode(src.readAsStringSync())) ?? <String, Object?>{};
// 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:
runner = 'flutter_jit_runner';
break;
case BuildMode.profile:
runner = 'flutter_aot_runner';
break;
case BuildMode.jitRelease:
runner = 'flutter_jit_product_runner';
break;
case BuildMode.release:
runner = 'flutter_aot_product_runner';
break;
default:
throwToolExit('Fuchsia does not support build mode "$mode"');
}
cmx['runner'] = 'fuchsia-pkg://$runnerPackageSource/$runner#meta/$runner.cmx';
dst.writeAsStringSync(json.encode(cmx));
}
// TODO(zanderso): Allow supplying a signing key.
Future<void> _buildPackage(
FuchsiaProject fuchsiaProject,
String target, // lib/main.dart
BuildInfo buildInfo,
String runnerPackageSource,
) async {
final String outDir = getFuchsiaBuildDirectory();
final String pkgDir = globals.fs.path.join(outDir, 'pkg');
final String appName = fuchsiaProject.project.manifest.appName;
final String pkgassets = globals.fs.path.join(outDir, '${appName}_pkgassets');
final String packageManifest = globals.fs.path.join(pkgDir, 'package_manifest');
final Directory pkg = globals.fs.directory(pkgDir);
if (!pkg.existsSync()) {
pkg.createSync(recursive: true);
}
final File srcCmx =
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'));
_rewriteCmx(buildInfo.mode, runnerPackageSource, srcCmx, dstCmx);
final File manifestFile = globals.fs.file(packageManifest);
if (buildInfo.usesAot) {
final String elf = globals.fs.path.join(outDir, 'elf.aotsnapshot');
manifestFile.writeAsStringSync(
'data/$appName/app_aot_snapshot.so=$elf\n');
} else {
final String dilpmanifest = globals.fs.path.join(outDir, '$appName.dilpmanifest');
manifestFile.writeAsStringSync(globals.fs.file(dilpmanifest).readAsStringSync());
}
manifestFile.writeAsStringSync(globals.fs.file(pkgassets).readAsStringSync(),
mode: FileMode.append);
manifestFile.writeAsStringSync('meta/$appName.cmx=${dstCmx.path}\n',
mode: FileMode.append);
manifestFile.writeAsStringSync('meta/package=$pkgDir/meta/package\n',
mode: FileMode.append);
final FuchsiaPM? fuchsiaPM = globals.fuchsiaSdk?.fuchsiaPM;
if (fuchsiaPM == null) {
return;
}
if (!await fuchsiaPM.init(pkgDir, appName)) {
return;
}
if (!await fuchsiaPM.build(pkgDir, packageManifest)) {
return;
}
if (!await fuchsiaPM.archive(pkgDir, packageManifest)) {
return;
}
}
......@@ -8,6 +8,7 @@ import 'package:process/process.dart';
import '../base/common.dart';
import '../base/logger.dart';
import '../base/process.dart';
import '../globals.dart' as globals;
import 'fuchsia_sdk.dart';
// Usage: ffx [-c <config>] [-e <env>] [-t <target>] [-T <timeout>] [-v] [<command>] [<args>]
......@@ -26,17 +27,21 @@ import 'fuchsia_sdk.dart';
// config View and switch default and user configurations
// daemon Interact with/control the ffx daemon
// target Interact with a target device or emulator
// session Control the current session. See
// https://fuchsia.dev/fuchsia-src/concepts/session/introduction
// for details.
/// A simple wrapper for the Fuchsia SDK's 'ffx' tool.
class FuchsiaFfx {
FuchsiaFfx({
required FuchsiaArtifacts? fuchsiaArtifacts,
required Logger logger,
required ProcessManager processManager,
}) : _fuchsiaArtifacts = fuchsiaArtifacts,
_logger = logger,
_processUtils =
ProcessUtils(logger: logger, processManager: processManager);
FuchsiaArtifacts? fuchsiaArtifacts,
Logger? logger,
ProcessManager? processManager,
}) : _fuchsiaArtifacts = fuchsiaArtifacts ?? globals.fuchsiaArtifacts,
_logger = logger ?? globals.logger,
_processUtils = ProcessUtils(
logger: logger ?? globals.logger,
processManager: processManager ?? globals.processManager);
final FuchsiaArtifacts? _fuchsiaArtifacts;
final Logger _logger;
......@@ -53,8 +58,7 @@ class FuchsiaFfx {
}
final List<String> command = <String>[
ffx.path,
if (timeout != null)
...<String>['-T', '${timeout.inSeconds}'],
if (timeout != null) ...<String>['-T', '${timeout.inSeconds}'],
'target',
'list',
// TODO(akbiggs): Revert -f back to --format once we've verified that
......@@ -97,4 +101,50 @@ class FuchsiaFfx {
}
return result.stdout.trim();
}
/// Show information about the current session
///
/// Returns `null` if the command failed, which can be interpreted as there is
/// no usable session.
Future<String?> sessionShow() async {
final File? ffx = _fuchsiaArtifacts?.ffx;
if (ffx == null || !ffx.existsSync()) {
throwToolExit('Fuchsia ffx tool not found.');
}
final List<String> command = <String>[
ffx.path,
'session',
'show',
];
final RunResult result = await _processUtils.run(command);
if (result.exitCode != 0) {
_logger.printError('ffx failed: ${result.stderr}');
return null;
}
return result.stdout;
}
/// Add an element to the current session
///
/// [url] should be formatted as a Fuchsia-style package URL, e.g.:
/// fuchsia-pkg://fuchsia.com/flutter_gallery#meta/flutter_gallery.cmx
/// Returns true on success and false on failure.
Future<bool> sessionAdd(String url) async {
final File? ffx = _fuchsiaArtifacts?.ffx;
if (ffx == null || !ffx.existsSync()) {
throwToolExit('Fuchsia ffx tool not found.');
}
final List<String> command = <String>[
ffx.path,
'session',
'add',
url,
];
final RunResult result = await _processUtils.run(command);
if (result.exitCode != 0) {
_logger.printError('ffx failed: ${result.stderr}');
return false;
}
return true;
}
}
......@@ -41,7 +41,7 @@ class FuchsiaPM {
/// 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/$APPNAME.cm=/path/to/cm/on/the/host/$APPNAME.cm
/// meta/package=/path/to/package/file/from/init/package
///
/// where $APPNAME is the same [appName] passed to [init], and meta/package
......@@ -232,12 +232,14 @@ class FuchsiaPackageServer {
if (_process == null) {
return false;
}
return (await globals.fuchsiaSdk?.fuchsiaPM.publish(_repo, package.path)) ?? false;
return (await globals.fuchsiaSdk?.fuchsiaPM.publish(_repo, package.path)) ??
false;
}
@override
String toString() {
final String p = (_process == null) ? 'stopped' : 'running ${_process?.pid}';
final String p =
(_process == null) ? 'stopped' : 'running ${_process?.pid}';
return 'FuchsiaPackageServer at $_host:$_port ($p)';
}
}
......@@ -31,11 +31,7 @@ class FuchsiaSdk {
late final FuchsiaKernelCompiler fuchsiaKernelCompiler = FuchsiaKernelCompiler();
/// Interface to the 'ffx' tool.
late final FuchsiaFfx fuchsiaFfx = FuchsiaFfx(
fuchsiaArtifacts: globals.fuchsiaArtifacts,
logger: globals.logger,
processManager: globals.processManager,
);
late final FuchsiaFfx fuchsiaFfx = FuchsiaFfx();
/// Returns any attached devices is a newline-denominated String.
///
......
// Copyright 2014 The Flutter 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/process.dart';
import 'fuchsia_device.dart';
// Usage: session_control <command> [<args>]
//
// Various operations to control sessions.
//
// Options:
// --help display usage information
//
// Commands:
// launch Launch a new session.
// restart Restart the current session.
// add Add an element to the current session.
//
// Usage: session_control launch <session_url>
//
// Launch a new session.
//
// Options:
// --help display usage information
//
// Usage: session_control restart
//
// Restart the current session.
//
// Options:
// --help display usage information
//
//
// Usage: session_control add <element_url>
//
// Add an element to the current session.
//
// Options:
// --help display usage information
/// A simple wrapper around the 'session_control' tool running on the Fuchsia device.
class FuchsiaSessionControl {
/// Instructs session_control on the device to add the app at [url] as an element.
///
/// [url] should be formatted as a Fuchsia-style package URL, e.g.:
/// fuchsia-pkg://fuchsia.com/flutter_gallery#meta/flutter_gallery.cmx
/// Returns true on success and false on failure.
Future<bool> add(FuchsiaDevice device, String url) async {
final RunResult result = await device.shell('session_control add $url');
return result.exitCode == 0;
}
}
// Copyright 2014 The Flutter 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/process.dart';
import '../globals.dart' as globals;
import 'fuchsia_device.dart';
// Usage: tiles_ctl <command>
// Supported commands:
// start
// add [--disable-focus] <url> [<args>...]
// remove <key>
// list
// quit
/// A simple wrapper around the 'tiles_ctl' tool running on the Fuchsia device.
class FuchsiaTilesCtl {
/// Finds the key for the app called [appName], or returns -1 if it can't be
/// found.
static Future<int> findAppKey(FuchsiaDevice device, String appName) async {
final FuchsiaTilesCtl tilesCtl = fuchsiaDeviceTools.tilesCtl;
final Map<int, String>? runningApps = await tilesCtl.list(device);
if (runningApps == null) {
globals.printTrace('tiles_ctl is not running');
return -1;
}
for (final MapEntry<int, String> entry in runningApps.entries) {
if (entry.value.contains('$appName#meta')) {
return entry.key;
}
}
return -1;
}
/// Ensures that tiles is running on the device.
static Future<bool> ensureStarted(FuchsiaDevice device) async {
final FuchsiaTilesCtl tilesCtl = fuchsiaDeviceTools.tilesCtl;
final Map<int, String>? runningApps = await tilesCtl.list(device);
if (runningApps == null) {
return tilesCtl.start(device);
}
return true;
}
/// Instructs 'tiles' to start on the device.
///
/// Returns true on success and false on failure.
Future<bool> start(FuchsiaDevice device) async {
final RunResult result = await device.shell('tiles_ctl start');
return result.exitCode == 0;
}
/// Returns a mapping of tile keys to app URLs.
///
/// Returns an empty mapping if tiles_ctl is running but no apps are running.
/// Returns null if tiles_ctl is not running.
Future<Map<int, String>?> list(FuchsiaDevice device) async {
// Output of tiles_ctl list has the format:
// Found 1 tiles:
// Tile key 1 url fuchsia-pkg://fuchsia.com/stocks#meta/stocks.cmx ...
final Map<int, String> tiles = <int, String>{};
final RunResult result = await device.shell('tiles_ctl list');
if (result.exitCode != 0) {
return null;
}
// Look for evidence that tiles_ctl is not running.
if (result.stdout.contains("Couldn't find tiles component in realm")) {
return null;
}
// Find lines beginning with 'Tile'
for (final String line in result.stdout.split('\n')) {
final List<String> words = line.split(' ');
if (words.isNotEmpty && words[0] == 'Tile') {
final int? key = int.tryParse(words[2]);
if (key != null) {
final String url = words[4];
tiles[key] = url;
}
}
}
return tiles;
}
/// Instructs tiles on the device to begin running the app at [url] in a new
/// tile.
///
/// The app is passed the arguments in [args]. Flutter apps receive these
/// arguments as arguments to `main()`. [url] should be formatted as a
/// Fuchsia-style package URL, e.g.:
/// fuchsia-pkg://fuchsia.com/flutter_gallery#meta/flutter_gallery.cmx
/// Returns true on success and false on failure.
Future<bool> add(FuchsiaDevice device, String url, List<String> args) async {
final RunResult result = await device.shell(
'tiles_ctl add $url ${args.join(" ")}');
return result.exitCode == 0;
}
/// Instructs tiles on the device to remove the app with key [key].
///
/// Returns true on success and false on failure.
Future<bool> remove(FuchsiaDevice device, int key) async {
final RunResult result = await device.shell('tiles_ctl remove $key');
return result.exitCode == 0;
}
/// Instructs tiles on the device to quit.
///
/// Returns true on success and false on failure.
Future<bool> quit(FuchsiaDevice device) async {
final RunResult result = await device.shell('tiles_ctl quit');
return result.exitCode == 0;
}
}
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import 'package:args/command_runner.dart';
import 'package:file/memory.dart';
import 'package:file_testing/file_testing.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/commands/build_fuchsia.dart';
import 'package:flutter_tools/src/features.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/fuchsia/fuchsia_sdk.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:meta/meta.dart';
import 'package:test/fake.dart';
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/fakes.dart';
import '../../src/test_flutter_command_runner.dart';
// Defined globally for fakes to use.
FileSystem fileSystem;
void main() {
Cache.disableLocking();
final Platform linuxPlatform = FakePlatform(
environment: const <String, String>{
'FLUTTER_ROOT': '/',
},
);
final Platform windowsPlatform = FakePlatform(
operatingSystem: 'windows',
environment: const <String, String>{
'FLUTTER_ROOT': '/',
},
);
FakeFuchsiaSdk fuchsiaSdk;
setUp(() {
fuchsiaSdk = FakeFuchsiaSdk();
fileSystem = MemoryFileSystem.test();
});
group('Fuchsia build fails gracefully when', () {
testUsingContext('The feature is disabled', () async {
final BuildCommand command = BuildCommand();
fileSystem.directory('fuchsia').createSync(recursive: true);
fileSystem.file('.packages').createSync();
fileSystem.file('pubspec.yaml').createSync();
fileSystem.file('lib/main.dart').createSync(recursive: true);
expect(
createTestCommandRunner(command).run(const <String>['build', 'fuchsia']),
throwsToolExit(message: '"build fuchsia" is currently disabled'),
);
}, overrides: <Type, Generator>{
Platform: () => linuxPlatform,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
FeatureFlags: () => TestFeatureFlags(),
});
testUsingContext('there is no Fuchsia project', () async {
final BuildCommand command = BuildCommand();
expect(
createTestCommandRunner(command).run(const <String>['build', 'fuchsia']),
throwsToolExit(),
);
}, overrides: <Type, Generator>{
Platform: () => linuxPlatform,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
FeatureFlags: () => TestFeatureFlags(isFuchsiaEnabled: true),
});
testUsingContext('there is no cmx file', () async {
final BuildCommand command = BuildCommand();
fileSystem.directory('fuchsia').createSync(recursive: true);
fileSystem.file('.packages').createSync();
fileSystem.file('pubspec.yaml').createSync();
expect(
createTestCommandRunner(command).run(const <String>['build', 'fuchsia']),
throwsToolExit(),
);
}, overrides: <Type, Generator>{
Platform: () => linuxPlatform,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
FeatureFlags: () => TestFeatureFlags(isFuchsiaEnabled: true),
});
testUsingContext('on Windows platform', () async {
final BuildCommand command = BuildCommand();
const String appName = 'app_name';
fileSystem
.file(fileSystem.path.join('fuchsia', 'meta', '$appName.cmx'))
..createSync(recursive: true)
..writeAsStringSync('{}');
fileSystem.file('.packages').createSync();
final File pubspecFile = fileSystem.file('pubspec.yaml')..createSync();
pubspecFile.writeAsStringSync('name: $appName');
final bool supported = BuildFuchsiaCommand(verboseHelp: false).supported;
expect(
createTestCommandRunner(command).run(const <String>['build', 'fuchsia']),
supported ? throwsToolExit() : throwsA(isA<UsageException>()),
);
}, overrides: <Type, Generator>{
Platform: () => windowsPlatform,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
FeatureFlags: () => TestFeatureFlags(isFuchsiaEnabled: true),
});
testUsingContext('there is no Fuchsia kernel compiler', () async {
final BuildCommand command = BuildCommand();
const String appName = 'app_name';
fileSystem
.file(fileSystem.path.join('fuchsia', 'meta', '$appName.cmx'))
..createSync(recursive: true)
..writeAsStringSync('{}');
fileSystem.file('.packages').createSync();
fileSystem.file(fileSystem.path.join('lib', 'main.dart')).createSync(recursive: true);
final File pubspecFile = fileSystem.file('pubspec.yaml')..createSync();
pubspecFile.writeAsStringSync('name: $appName');
expect(
createTestCommandRunner(command).run(const <String>['build', 'fuchsia']),
throwsToolExit(),
);
}, overrides: <Type, Generator>{
Platform: () => linuxPlatform,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
FeatureFlags: () => TestFeatureFlags(isFuchsiaEnabled: true),
});
});
testUsingContext('Fuchsia build parts fit together right', () async {
final BuildCommand command = BuildCommand();
const String appName = 'app_name';
fileSystem
.file(fileSystem.path.join('fuchsia', 'meta', '$appName.cmx'))
..createSync(recursive: true)
..writeAsStringSync('{}');
fileSystem.file('.packages').createSync();
fileSystem.file(fileSystem.path.join('lib', 'main.dart')).createSync(recursive: true);
final File pubspecFile = fileSystem.file('pubspec.yaml')..createSync();
pubspecFile.writeAsStringSync('name: $appName');
await createTestCommandRunner(command)
.run(const <String>['build', 'fuchsia']);
final String farPath = fileSystem.path.join(
getFuchsiaBuildDirectory(), 'pkg', 'app_name-0.far',
);
expect(fileSystem.file(farPath), exists);
}, overrides: <Type, Generator>{
Platform: () => linuxPlatform,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
FuchsiaSdk: () => fuchsiaSdk,
FeatureFlags: () => TestFeatureFlags(isFuchsiaEnabled: true),
});
}
class FakeFuchsiaPM extends Fake implements FuchsiaPM {
String _appName;
@override
Future<bool> init(String buildPath, String appName) async {
if (!fileSystem.directory(buildPath).existsSync()) {
return false;
}
fileSystem
.file(fileSystem.path.join(buildPath, 'meta', 'package'))
.createSync(recursive: true);
_appName = appName;
return true;
}
@override
Future<bool> build(String buildPath, String manifestPath) async {
if (!fileSystem.file(fileSystem.path.join(buildPath, 'meta', 'package')).existsSync() ||
!fileSystem.file(manifestPath).existsSync()) {
return false;
}
fileSystem.file(fileSystem.path.join(buildPath, 'meta.far')).createSync(recursive: true);
return true;
}
@override
Future<bool> archive(String buildPath, String manifestPath) async {
if (!fileSystem.file(fileSystem.path.join(buildPath, 'meta', 'package')).existsSync() ||
!fileSystem.file(manifestPath).existsSync()) {
return false;
}
if (_appName == null) {
return false;
}
fileSystem
.file(fileSystem.path.join(buildPath, '$_appName-0.far'))
.createSync(recursive: true);
return true;
}
}
class FakeFuchsiaKernelCompiler extends Fake 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 = fileSystem.path.join(outDir, '$appName.dilpmanifest');
fileSystem.file(manifestPath).createSync(recursive: true);
}
}
class FakeFuchsiaSdk extends Fake implements FuchsiaSdk {
@override
final FuchsiaPM fuchsiaPM = FakeFuchsiaPM();
@override
final FuchsiaKernelCompiler fuchsiaKernelCompiler =
FakeFuchsiaKernelCompiler();
}
......@@ -12,7 +12,6 @@ import 'package:flutter_tools/src/commands/build.dart';
import 'package:flutter_tools/src/commands/build_aar.dart';
import 'package:flutter_tools/src/commands/build_apk.dart';
import 'package:flutter_tools/src/commands/build_appbundle.dart';
import 'package:flutter_tools/src/commands/build_fuchsia.dart';
import 'package:flutter_tools/src/commands/build_ios.dart';
import 'package:flutter_tools/src/commands/build_ios_framework.dart';
import 'package:flutter_tools/src/commands/build_linux.dart';
......@@ -36,9 +35,11 @@ void main() {
BuildIOSCommand(verboseHelp: false),
BuildIOSArchiveCommand(verboseHelp: false),
BuildAppBundleCommand(),
BuildFuchsiaCommand(verboseHelp: false),
BuildAarCommand(verboseHelp: false),
BuildIOSFrameworkCommand(verboseHelp: false, buildSystem: globals.buildSystem),
BuildIOSFrameworkCommand(
verboseHelp: false,
buildSystem: globals.buildSystem,
),
AttachCommand(),
];
......@@ -53,7 +54,8 @@ void main() {
}
});
testUsingContext('BuildSubCommand displays current null safety mode', () async {
testUsingContext('BuildSubCommand displays current null safety mode',
() async {
const BuildInfo unsound = BuildInfo(
BuildMode.debug,
'',
......@@ -67,11 +69,13 @@ void main() {
);
FakeBuildSubCommand().test(unsound);
expect(testLogger.statusText, contains('Building without sound null safety'));
expect(
testLogger.statusText, contains('Building without sound null safety'));
testLogger.clear();
FakeBuildSubCommand().test(sound);
expect(testLogger.statusText, contains('💪 Building with sound null safety 💪'));
expect(testLogger.statusText,
contains('💪 Building with sound null safety 💪'));
});
testUsingContext('Include only supported sub commands', () {
......
......@@ -43,14 +43,14 @@ void main() {
]);
const String expected = '\n'
'┌────────────────────────────────────────────────────────────────────────────\n'
'│ General Info \n'
'│ [✓] App Name: flutter_gallery \n'
'│ [✓] Supported Platforms: android, ios, web, macos, linux, windows, fuchsia\n'
'│ [✓] Is Flutter Package: yes \n'
'│ [✓] Uses Material Design: yes \n'
'│ [✓] Is Plugin: no \n'
'└────────────────────────────────────────────────────────────────────────────\n';
'┌───────────────────────────────────────────────────────────────────┐\n'
'│ General Info │\n'
'│ [✓] App Name: flutter_gallery │\n'
'│ [✓] Supported Platforms: android, ios, web, macos, linux, windows │\n'
'│ [✓] Is Flutter Package: yes │\n'
'│ [✓] Uses Material Design: yes │\n'
'│ [✓] Is Plugin: no │\n'
'└───────────────────────────────────────────────────────────────────┘\n';
expect(loggerTest.statusText, contains(expected));
});
......
......@@ -252,9 +252,6 @@ class FakeStdin extends Fake implements Stdin {
@override
bool echoMode = true;
@override
bool echoNewlineMode = true;
@override
bool lineMode = true;
......
......@@ -25,7 +25,8 @@ void main() {
}
fuchsiaVmServiceConnectionFunction = fakeServiceFunction;
final DartVm vm = await DartVm.connect(Uri.parse('http://this.whatever/ws'));
final DartVm vm =
await DartVm.connect(Uri.parse('http://this.whatever/ws'));
expect(vm, isNot(null));
await vm.stop();
expect(service.disposed, true);
......@@ -79,12 +80,14 @@ void main() {
Uri uri, {
Duration? timeout,
}) {
fakeService.flutterListViews = vms.Response.parse(flutterViewCannedResponses);
fakeService.flutterListViews =
vms.Response.parse(flutterViewCannedResponses);
return Future<vms.VmService>(() => fakeService);
}
fuchsiaVmServiceConnectionFunction = fakeVmConnectionFunction;
final DartVm vm = await DartVm.connect(Uri.parse('http://whatever.com/ws'));
final DartVm vm =
await DartVm.connect(Uri.parse('http://whatever.com/ws'));
expect(vm, isNot(null));
final List<FlutterView> views = await vm.getAllFlutterViews();
expect(views.length, 3);
......@@ -135,12 +138,14 @@ void main() {
Uri uri, {
Duration? timeout,
}) {
fakeService.flutterListViews = vms.Response.parse(flutterViewCannedResponses);
fakeService.flutterListViews =
vms.Response.parse(flutterViewCannedResponses);
return Future<vms.VmService>(() => fakeService);
}
fuchsiaVmServiceConnectionFunction = fakeVmConnectionFunction;
final DartVm vm = await DartVm.connect(Uri.parse('http://whatever.com/ws'));
final DartVm vm =
await DartVm.connect(Uri.parse('http://whatever.com/ws'));
expect(vm, isNot(null));
final List<FlutterView> views = await vm.getAllFlutterViews();
expect(views.length, 3);
......@@ -156,7 +161,8 @@ void main() {
});
test('invalid flutter view missing ID', () async {
final Map<String, dynamic> flutterViewCannedResponseMissingId = <String, dynamic>{
final Map<String, dynamic> flutterViewCannedResponseMissingId =
<String, dynamic>{
'views': <Map<String, dynamic>>[
// Valid flutter view.
<String, dynamic>{
......@@ -182,12 +188,14 @@ void main() {
Uri uri, {
Duration? timeout,
}) {
fakeService.flutterListViews = vms.Response.parse(flutterViewCannedResponseMissingId);
fakeService.flutterListViews =
vms.Response.parse(flutterViewCannedResponseMissingId);
return Future<vms.VmService>(() => fakeService);
}
fuchsiaVmServiceConnectionFunction = fakeVmConnectionFunction;
final DartVm vm = await DartVm.connect(Uri.parse('http://whatever.com/ws'));
final DartVm vm =
await DartVm.connect(Uri.parse('http://whatever.com/ws'));
expect(vm, isNot(null));
Future<void> failingFunction() async {
await vm.getAllFlutterViews();
......@@ -217,7 +225,7 @@ void main() {
'type': '@Isolate',
'fixedId': 'true',
'id': 'isolates/3',
'name': 'flutterBinary.cmx',
'name': 'flutterBinary.cm',
'number': '3',
})!,
vms.IsolateRef.parse(<String, dynamic>{
......@@ -238,16 +246,20 @@ void main() {
}
fuchsiaVmServiceConnectionFunction = fakeVmConnectionFunction;
final DartVm vm = await DartVm.connect(Uri.parse('http://whatever.com/ws'));
final DartVm vm =
await DartVm.connect(Uri.parse('http://whatever.com/ws'));
expect(vm, isNot(null));
final List<IsolateRef> matchingFlutterIsolates = await vm.getMainIsolatesByPattern('flutterBinary.cmx');
final List<IsolateRef> matchingFlutterIsolates =
await vm.getMainIsolatesByPattern('flutterBinary.cm');
expect(matchingFlutterIsolates.length, 1);
final List<IsolateRef> allIsolates = await vm.getMainIsolatesByPattern('');
final List<IsolateRef> allIsolates =
await vm.getMainIsolatesByPattern('');
expect(allIsolates.length, 4);
});
test('invalid flutter view missing ID', () async {
final Map<String, dynamic> flutterViewCannedResponseMissingIsolateName = <String, dynamic>{
final Map<String, dynamic> flutterViewCannedResponseMissingIsolateName =
<String, dynamic>{
'views': <Map<String, dynamic>>[
// Missing isolate name.
<String, dynamic>{
......@@ -267,12 +279,14 @@ void main() {
Uri uri, {
Duration? timeout,
}) {
fakeService.flutterListViews = vms.Response.parse(flutterViewCannedResponseMissingIsolateName);
fakeService.flutterListViews =
vms.Response.parse(flutterViewCannedResponseMissingIsolateName);
return Future<vms.VmService>(() => fakeService);
}
fuchsiaVmServiceConnectionFunction = fakeVmConnectionFunction;
final DartVm vm = await DartVm.connect(Uri.parse('http://whatever.com/ws'));
final DartVm vm =
await DartVm.connect(Uri.parse('http://whatever.com/ws'));
expect(vm, isNot(null));
Future<void> failingFunction() async {
await vm.getAllFlutterViews();
......@@ -298,7 +312,8 @@ class FakeVmService extends Fake implements vms.VmService {
}
@override
Future<vms.Response> callMethod(String method, {String? isolateId, Map<String, dynamic>? args}) async {
Future<vms.Response> callMethod(String method,
{String? isolateId, Map<String, dynamic>? args}) async {
if (method == '_flutter.listViews') {
return flutterListViews!;
}
......
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