mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
[flutter_tool] Adds support for 'run' for Fuchsia devices (#32849)
This commit is contained in:
parent
3265e15925
commit
94ce956f0a
@ -17,6 +17,7 @@ import 'base/os.dart' show os;
|
||||
import 'base/process.dart';
|
||||
import 'base/user_messages.dart';
|
||||
import 'build_info.dart';
|
||||
import 'fuchsia/application_package.dart';
|
||||
import 'globals.dart';
|
||||
import 'ios/ios_workflow.dart';
|
||||
import 'ios/plist_utils.dart' as plist;
|
||||
@ -66,7 +67,9 @@ class ApplicationPackageFactory {
|
||||
? WindowsApp.fromWindowsProject(FlutterProject.current().windows)
|
||||
: WindowsApp.fromPrebuiltApp(applicationBinary);
|
||||
case TargetPlatform.fuchsia:
|
||||
return null;
|
||||
return applicationBinary == null
|
||||
? FuchsiaApp.fromFuchsiaProject(FlutterProject.current().fuchsia)
|
||||
: FuchsiaApp.fromPrebuiltApp(applicationBinary);
|
||||
}
|
||||
assert(platform != null);
|
||||
return null;
|
||||
|
@ -3,6 +3,8 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:archive/archive.dart';
|
||||
|
||||
import '../globals.dart';
|
||||
import 'context.dart';
|
||||
import 'file_system.dart';
|
||||
import 'io.dart';
|
||||
@ -72,6 +74,37 @@ abstract class OperatingSystemUtils {
|
||||
|
||||
/// Returns the separator between items in the PATH environment variable.
|
||||
String get pathVarSeparator;
|
||||
|
||||
/// Returns an unused network port.
|
||||
///
|
||||
/// Returns 0 if an unused port cannot be found.
|
||||
///
|
||||
/// The port returned by this function may become used before it is bound by
|
||||
/// its intended user.
|
||||
Future<int> findFreePort({bool ipv6 = false}) async {
|
||||
int port = 0;
|
||||
ServerSocket serverSocket;
|
||||
final InternetAddress loopback =
|
||||
ipv6 ? InternetAddress.loopbackIPv6 : InternetAddress.loopbackIPv4;
|
||||
try {
|
||||
serverSocket = await ServerSocket.bind(loopback, 0);
|
||||
port = serverSocket.port;
|
||||
} on SocketException catch (e) {
|
||||
// If ipv4 loopback bind fails, try ipv6.
|
||||
if (!ipv6) {
|
||||
return findFreePort(ipv6: true);
|
||||
}
|
||||
printTrace('findFreePort failed: $e');
|
||||
} catch (e) {
|
||||
// Failures are signaled by a return value of 0 from this function.
|
||||
printTrace('findFreePort failed: $e');
|
||||
} finally {
|
||||
if (serverSocket != null) {
|
||||
await serverSocket.close();
|
||||
}
|
||||
}
|
||||
return port;
|
||||
}
|
||||
}
|
||||
|
||||
class _PosixUtils extends OperatingSystemUtils {
|
||||
|
@ -27,10 +27,9 @@ import 'devfs.dart';
|
||||
import 'device.dart';
|
||||
import 'doctor.dart';
|
||||
import 'emulator.dart';
|
||||
import 'fuchsia/fuchsia_kernel_compiler.dart';
|
||||
import 'fuchsia/fuchsia_pm.dart';
|
||||
import 'fuchsia/fuchsia_sdk.dart';
|
||||
import 'fuchsia/fuchsia_workflow.dart';
|
||||
import 'fuchsia/fuchsia_device.dart' show FuchsiaDeviceTools;
|
||||
import 'fuchsia/fuchsia_sdk.dart' show FuchsiaSdk, FuchsiaArtifacts;
|
||||
import 'fuchsia/fuchsia_workflow.dart' show FuchsiaWorkflow;
|
||||
import 'ios/cocoapods.dart';
|
||||
import 'ios/ios_workflow.dart';
|
||||
import 'ios/mac.dart';
|
||||
@ -76,8 +75,7 @@ Future<T> runInContext<T>(
|
||||
Flags: () => const EmptyFlags(),
|
||||
FlutterVersion: () => FlutterVersion(const SystemClock()),
|
||||
FuchsiaArtifacts: () => FuchsiaArtifacts.find(),
|
||||
FuchsiaKernelCompiler: () => FuchsiaKernelCompiler(),
|
||||
FuchsiaPM: () => FuchsiaPM(),
|
||||
FuchsiaDeviceTools: () => FuchsiaDeviceTools(),
|
||||
FuchsiaSdk: () => FuchsiaSdk(),
|
||||
FuchsiaWorkflow: () => FuchsiaWorkflow(),
|
||||
GenSnapshot: () => const GenSnapshot(),
|
||||
|
76
packages/flutter_tools/lib/src/fuchsia/amber_ctl.dart
Normal file
76
packages/flutter_tools/lib/src/fuchsia/amber_ctl.dart
Normal file
@ -0,0 +1,76 @@
|
||||
// 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/process.dart';
|
||||
|
||||
import 'fuchsia_device.dart';
|
||||
import 'fuchsia_pm.dart';
|
||||
|
||||
// usage: amber_ctl <command> [opts]
|
||||
// Commands
|
||||
// get_up - get an update for a package
|
||||
// Options
|
||||
// -n: name of the package
|
||||
// -v: version of the package to retrieve, if none is supplied any
|
||||
// package instance could match
|
||||
// -m: merkle root of the package to retrieve, if none is supplied
|
||||
// any package instance could match
|
||||
//
|
||||
// get_blob - get the specified content blob
|
||||
// -i: content ID of the blob
|
||||
//
|
||||
// add_src - add a source to the list we can use
|
||||
// -n: name of the update source (optional, with URL)
|
||||
// -f: file path or url to a source config file
|
||||
// -h: SHA256 hash of source config file (optional, with URL)
|
||||
// -x: do not disable other active sources (if the provided source is
|
||||
// enabled)
|
||||
//
|
||||
// rm_src - remove a source, if it exists
|
||||
// -n: name of the update source
|
||||
//
|
||||
// list_srcs - list the set of sources we can use
|
||||
//
|
||||
// enable_src
|
||||
// -n: name of the update source
|
||||
// -x: do not disable other active sources
|
||||
//
|
||||
// disable_src
|
||||
// -n: name of the update source
|
||||
//
|
||||
// system_update - check for, download, and apply any available system
|
||||
// update
|
||||
//
|
||||
// gc - trigger a garbage collection
|
||||
//
|
||||
// print_state - print go routine state of amber process
|
||||
|
||||
/// Simple wrapper for interacting with the 'amber_ctl' tool running on the
|
||||
/// Fuchsia device.
|
||||
class FuchsiaAmberCtl {
|
||||
/// Teaches the amber instance running on [device] about the Fuchsia package
|
||||
/// server accessible via [configUrl].
|
||||
Future<bool> addSrc(FuchsiaDevice device, FuchsiaPackageServer server) async {
|
||||
final String configUrl = '${server.url}/config.json';
|
||||
final RunResult result =
|
||||
await device.shell('amber_ctl add_src -x -f $configUrl');
|
||||
return result.exitCode == 0;
|
||||
}
|
||||
|
||||
/// Instructs the amber instance running on [device] to forget about the
|
||||
/// Fuchsia package server that it was accessing via [serverUrl].
|
||||
Future<bool> rmSrc(FuchsiaDevice device, FuchsiaPackageServer server) async {
|
||||
final RunResult result =
|
||||
await device.shell('amber_ctl rm_src -n ${server.url}');
|
||||
return result.exitCode == 0;
|
||||
}
|
||||
|
||||
/// Instructs the amber instance running on [device] to prefetch the package
|
||||
/// [packageName].
|
||||
Future<bool> getUp(FuchsiaDevice device, String packageName) async {
|
||||
final RunResult result =
|
||||
await device.shell('amber_ctl get_up -n $packageName');
|
||||
return result.exitCode == 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
// 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 '../application_package.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../build_info.dart';
|
||||
import '../project.dart';
|
||||
|
||||
abstract class FuchsiaApp extends ApplicationPackage {
|
||||
FuchsiaApp({@required String projectBundleId}) : super(id: projectBundleId);
|
||||
|
||||
/// Creates a new [FuchsiaApp] from a fuchsia sub project.
|
||||
factory FuchsiaApp.fromFuchsiaProject(FuchsiaProject project) {
|
||||
return BuildableFuchsiaApp(
|
||||
project: project,
|
||||
);
|
||||
}
|
||||
|
||||
/// Creates a new [FuchsiaApp] from an existing .far archive.
|
||||
///
|
||||
/// [applicationBinary] is the path to the .far archive.
|
||||
factory FuchsiaApp.fromPrebuiltApp(FileSystemEntity applicationBinary) {
|
||||
return PrebuiltFuchsiaApp(
|
||||
farArchive: applicationBinary.path,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String get displayName => id;
|
||||
|
||||
/// The location of the 'far' archive containing the built app.
|
||||
File farArchive(BuildMode buildMode);
|
||||
}
|
||||
|
||||
class PrebuiltFuchsiaApp extends FuchsiaApp {
|
||||
PrebuiltFuchsiaApp({
|
||||
@required String farArchive,
|
||||
}) : _farArchive = farArchive,
|
||||
// TODO(zra): Extract the archive and extract the id from meta/package.
|
||||
super(projectBundleId: farArchive);
|
||||
|
||||
final String _farArchive;
|
||||
|
||||
@override
|
||||
File farArchive(BuildMode buildMode) => fs.file(_farArchive);
|
||||
|
||||
@override
|
||||
String get name => _farArchive;
|
||||
}
|
||||
|
||||
class BuildableFuchsiaApp extends FuchsiaApp {
|
||||
BuildableFuchsiaApp({this.project}) :
|
||||
super(projectBundleId: project.project.manifest.appName);
|
||||
|
||||
final FuchsiaProject project;
|
||||
|
||||
@override
|
||||
File farArchive(BuildMode buildMode) {
|
||||
// TODO(zra): Distinguish among build modes.
|
||||
final String outDir = getFuchsiaBuildDirectory();
|
||||
final String pkgDir = fs.path.join(outDir, 'pkg');
|
||||
final String appName = project.project.manifest.appName;
|
||||
return fs.file(fs.path.join(pkgDir, '$appName-0.far'));
|
||||
}
|
||||
|
||||
@override
|
||||
String get name => project.project.manifest.appName;
|
||||
}
|
@ -12,8 +12,8 @@ import '../bundle.dart';
|
||||
import '../devfs.dart';
|
||||
import '../project.dart';
|
||||
|
||||
import 'fuchsia_kernel_compiler.dart';
|
||||
import 'fuchsia_pm.dart';
|
||||
import 'fuchsia_sdk.dart';
|
||||
|
||||
// Building a Fuchsia package has a few steps:
|
||||
// 1. Do the custom kernel compile using the kernel compiler from the Fuchsia
|
||||
@ -30,7 +30,7 @@ Future<void> buildFuchsia(
|
||||
outDir.createSync(recursive: true);
|
||||
}
|
||||
|
||||
await fuchsiaKernelCompiler.build(
|
||||
await fuchsiaSdk.fuchsiaKernelCompiler.build(
|
||||
fuchsiaProject: fuchsiaProject, target: target, buildInfo: buildInfo);
|
||||
await _buildAssets(fuchsiaProject, target, buildInfo);
|
||||
await _buildPackage(fuchsiaProject, target, buildInfo);
|
||||
@ -96,6 +96,7 @@ Future<void> _buildPackage(
|
||||
manifestFile.writeAsStringSync('meta/package=$pkgDir/meta/package\n',
|
||||
mode: FileMode.append);
|
||||
|
||||
final FuchsiaPM fuchsiaPM = fuchsiaSdk.fuchsiaPM;
|
||||
if (!await fuchsiaPM.init(pkgDir, appName)) {
|
||||
return;
|
||||
}
|
||||
|
@ -0,0 +1,56 @@
|
||||
// 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/common.dart';
|
||||
import '../base/process.dart';
|
||||
import 'fuchsia_sdk.dart';
|
||||
|
||||
// Usage: dev_finder <flags> <subcommand> <subcommand args>
|
||||
//
|
||||
// Subcommands:
|
||||
// commands list all command names
|
||||
// flags describe all known top-level flags
|
||||
// help describe subcommands and their syntax
|
||||
// list lists all Fuchsia devices on the network
|
||||
// resolve attempts to resolve all passed Fuchsia domain names on the
|
||||
// network
|
||||
|
||||
/// A simple wrapper for the Fuchsia SDK's 'dev_finder' tool.
|
||||
class FuchsiaDevFinder {
|
||||
/// Returns a list of attached devices as a list of strings with entries
|
||||
/// formatted as follows:
|
||||
/// 192.168.42.172 scare-cable-skip-joy
|
||||
Future<List<String>> list() async {
|
||||
if (fuchsiaArtifacts.devFinder == null) {
|
||||
throwToolExit('Fuchsia dev_finder tool not found.');
|
||||
}
|
||||
final List<String> command = <String>[
|
||||
fuchsiaArtifacts.devFinder.path,
|
||||
'list',
|
||||
'-full'
|
||||
];
|
||||
final RunResult result = await runAsync(command);
|
||||
return (result.exitCode == 0) ? result.stdout.split('\n') : null;
|
||||
}
|
||||
|
||||
/// Returns the host address by which the device [deviceName] should use for
|
||||
/// the host.
|
||||
///
|
||||
/// The string [deviceName] should be the name of the device from the
|
||||
/// 'list' command, e.g. 'scare-cable-skip-joy'.
|
||||
Future<String> resolve(String deviceName) async {
|
||||
if (fuchsiaArtifacts.devFinder == null) {
|
||||
throwToolExit('Fuchsia dev_finder tool not found.');
|
||||
}
|
||||
final List<String> command = <String>[
|
||||
fuchsiaArtifacts.devFinder.path,
|
||||
'resolve',
|
||||
'-local',
|
||||
'-device-limit', '1',
|
||||
deviceName
|
||||
];
|
||||
final RunResult result = await runAsync(command);
|
||||
return (result.exitCode == 0) ? result.stdout.trim() : null;
|
||||
}
|
||||
}
|
@ -9,8 +9,11 @@ import 'package:meta/meta.dart';
|
||||
import '../application_package.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/os.dart';
|
||||
import '../base/platform.dart';
|
||||
import '../base/process.dart';
|
||||
import '../base/process_manager.dart';
|
||||
@ -21,8 +24,28 @@ import '../globals.dart';
|
||||
import '../project.dart';
|
||||
import '../vmservice.dart';
|
||||
|
||||
import 'amber_ctl.dart';
|
||||
import 'application_package.dart';
|
||||
import 'fuchsia_build.dart';
|
||||
import 'fuchsia_pm.dart';
|
||||
import 'fuchsia_sdk.dart';
|
||||
import 'fuchsia_workflow.dart';
|
||||
import 'tiles_ctl.dart';
|
||||
|
||||
/// The [FuchsiaDeviceTools] instance.
|
||||
FuchsiaDeviceTools get fuchsiaDeviceTools => context.get<FuchsiaDeviceTools>();
|
||||
|
||||
/// Fuchsia device-side tools.
|
||||
class FuchsiaDeviceTools {
|
||||
FuchsiaAmberCtl _amberCtl;
|
||||
FuchsiaAmberCtl get amberCtl => _amberCtl ??= FuchsiaAmberCtl();
|
||||
|
||||
FuchsiaTilesCtl _tilesCtl;
|
||||
FuchsiaTilesCtl get tilesCtl => _tilesCtl ??= FuchsiaTilesCtl();
|
||||
}
|
||||
|
||||
final FuchsiaAmberCtl _amberCtl = fuchsiaDeviceTools.amberCtl;
|
||||
final FuchsiaTilesCtl _tilesCtl = fuchsiaDeviceTools.tilesCtl;
|
||||
|
||||
final String _ipv4Loopback = InternetAddress.loopbackIPv4.address;
|
||||
final String _ipv6Loopback = InternetAddress.loopbackIPv6.address;
|
||||
@ -48,11 +71,15 @@ class _FuchsiaLogReader extends DeviceLogReader {
|
||||
Stream<String> _logLines;
|
||||
@override
|
||||
Stream<String> get logLines {
|
||||
_logLines ??= _processLogs(fuchsiaSdk.syslogs(_device.id));
|
||||
final Stream<String> logStream = fuchsiaSdk.syslogs(_device.id);
|
||||
_logLines ??= _processLogs(logStream);
|
||||
return _logLines;
|
||||
}
|
||||
|
||||
Stream<String> _processLogs(Stream<String> lines) {
|
||||
if (lines == null) {
|
||||
return null;
|
||||
}
|
||||
// Get the starting time of the log processor to filter logs from before
|
||||
// the process attached.
|
||||
final DateTime startTime = systemClock.now();
|
||||
@ -60,7 +87,7 @@ class _FuchsiaLogReader extends DeviceLogReader {
|
||||
// the correct fuchsia module.
|
||||
final RegExp matchRegExp = _app == null
|
||||
? _flutterLogOutput
|
||||
: RegExp('INFO: ${_app.name}\\(flutter\\): ');
|
||||
: RegExp('INFO: ${_app.name}(\.cmx)?\\(flutter\\): ');
|
||||
return Stream<String>.eventTransformed(
|
||||
lines,
|
||||
(Sink<String> outout) => _FuchsiaLogSink(outout, matchRegExp, startTime),
|
||||
@ -188,7 +215,7 @@ class FuchsiaDevice extends Device {
|
||||
|
||||
@override
|
||||
Future<LaunchResult> startApp(
|
||||
ApplicationPackage package, {
|
||||
covariant FuchsiaApp package, {
|
||||
String mainPath,
|
||||
String route,
|
||||
DebuggingOptions debuggingOptions,
|
||||
@ -196,13 +223,110 @@ class FuchsiaDevice extends Device {
|
||||
bool prebuiltApplication = false,
|
||||
bool usesTerminalUi = true,
|
||||
bool ipv6 = false,
|
||||
}) =>
|
||||
Future<void>.error('unimplemented');
|
||||
}) async {
|
||||
if (!prebuiltApplication) {
|
||||
await buildFuchsia(fuchsiaProject: FlutterProject.current().fuchsia,
|
||||
target: mainPath,
|
||||
buildInfo: debuggingOptions.buildInfo);
|
||||
}
|
||||
// Stop the app if it's currently running.
|
||||
await stopApp(package);
|
||||
// Find out who the device thinks we are.
|
||||
final String host = await fuchsiaSdk.fuchsiaDevFinder.resolve(name);
|
||||
final int port = await os.findFreePort();
|
||||
if (port == 0) {
|
||||
printError('Failed to find a free port');
|
||||
return LaunchResult.failed();
|
||||
}
|
||||
final Directory packageRepo =
|
||||
fs.directory(fs.path.join(getFuchsiaBuildDirectory(), '.pkg-repo'));
|
||||
packageRepo.createSync(recursive: true);
|
||||
|
||||
final String appName = FlutterProject.current().manifest.appName;
|
||||
|
||||
final Status status = logger.startProgress(
|
||||
'Starting Fuchsia application...',
|
||||
timeout: null,
|
||||
);
|
||||
FuchsiaPackageServer fuchsiaPackageServer;
|
||||
bool serverRegistered = false;
|
||||
try {
|
||||
// Start up a package server.
|
||||
fuchsiaPackageServer = FuchsiaPackageServer(packageRepo.path, host, port);
|
||||
if (!await fuchsiaPackageServer.start()) {
|
||||
printError('Failed to start the Fuchsia package server');
|
||||
return LaunchResult.failed();
|
||||
}
|
||||
final File farArchive = package.farArchive(
|
||||
debuggingOptions.buildInfo.mode);
|
||||
if (!await fuchsiaPackageServer.addPackage(farArchive)) {
|
||||
printError('Failed to add package to the package server');
|
||||
return LaunchResult.failed();
|
||||
}
|
||||
|
||||
// Teach amber about the package server.
|
||||
if (!await _amberCtl.addSrc(this, fuchsiaPackageServer)) {
|
||||
printError('Failed to teach amber about the package server');
|
||||
return LaunchResult.failed();
|
||||
}
|
||||
serverRegistered = true;
|
||||
|
||||
// Tell amber to prefetch the app.
|
||||
if (!await _amberCtl.getUp(this, appName)) {
|
||||
printError('Failed to get amber to prefetch the package');
|
||||
return LaunchResult.failed();
|
||||
}
|
||||
|
||||
// Ensure tiles_ctl is started, and start the app.
|
||||
if (!await FuchsiaTilesCtl.ensureStarted(this)) {
|
||||
printError('Failed to ensure that tiles is started on the device');
|
||||
return LaunchResult.failed();
|
||||
}
|
||||
|
||||
// Instruct tiles_ctl to start the app.
|
||||
final String fuchsiaUrl =
|
||||
'fuchsia-pkg://fuchsia.com/$appName#meta/$appName.cmx';
|
||||
if (!await _tilesCtl.add(this, fuchsiaUrl, <String>[])) {
|
||||
printError('Failed to add the app to tiles');
|
||||
return LaunchResult.failed();
|
||||
}
|
||||
} finally {
|
||||
// Try to un-teach amber about the package server if needed.
|
||||
if (serverRegistered) {
|
||||
await _amberCtl.rmSrc(this, fuchsiaPackageServer);
|
||||
}
|
||||
// Shutdown the package server and delete the package repo;
|
||||
fuchsiaPackageServer.stop();
|
||||
packageRepo.deleteSync(recursive: true);
|
||||
status.cancel();
|
||||
}
|
||||
|
||||
if (!debuggingOptions.buildInfo.isDebug &&
|
||||
!debuggingOptions.buildInfo.isProfile) {
|
||||
return LaunchResult.succeeded();
|
||||
}
|
||||
|
||||
// In a debug or profile build, try to find the observatory uri.
|
||||
final FuchsiaIsolateDiscoveryProtocol discovery =
|
||||
FuchsiaIsolateDiscoveryProtocol(this, appName);
|
||||
try {
|
||||
final Uri observatoryUri = await discovery.uri;
|
||||
return LaunchResult.succeeded(observatoryUri: observatoryUri);
|
||||
} finally {
|
||||
discovery.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> stopApp(ApplicationPackage app) async {
|
||||
// Currently we don't have a way to stop an app running on Fuchsia.
|
||||
return false;
|
||||
Future<bool> stopApp(covariant FuchsiaApp app) async {
|
||||
final int appKey = await FuchsiaTilesCtl.findAppKey(this, app.id);
|
||||
if (appKey != -1) {
|
||||
if (!await _tilesCtl.remove(this, appKey)) {
|
||||
printError('tiles_ctl remove on ${app.id} failed.');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -250,7 +374,13 @@ class FuchsiaDevice extends Device {
|
||||
|
||||
/// List the ports currently running a dart observatory.
|
||||
Future<List<int>> servicePorts() async {
|
||||
final String findOutput = await shell('find /hub -name vmservice-port');
|
||||
const String findCommand = 'find /hub -name vmservice-port';
|
||||
final RunResult findResult = await shell(findCommand);
|
||||
if (findResult.exitCode != 0) {
|
||||
throwToolExit("'$findCommand' on device $id failed");
|
||||
return null;
|
||||
}
|
||||
final String findOutput = findResult.stdout;
|
||||
if (findOutput.trim() == '') {
|
||||
throwToolExit(
|
||||
'No Dart Observatories found. Are you running a debug build?');
|
||||
@ -261,7 +391,13 @@ class FuchsiaDevice extends Device {
|
||||
if (path == '') {
|
||||
continue;
|
||||
}
|
||||
final String lsOutput = await shell('ls $path');
|
||||
final String lsCommand = 'ls $path';
|
||||
final RunResult lsResult = await shell(lsCommand);
|
||||
if (lsResult.exitCode != 0) {
|
||||
throwToolExit("'$lsCommand' on device $id failed");
|
||||
return null;
|
||||
}
|
||||
final String lsOutput = lsResult.stdout;
|
||||
for (String line in lsOutput.split('\n')) {
|
||||
if (line == '') {
|
||||
continue;
|
||||
@ -276,20 +412,18 @@ class FuchsiaDevice extends Device {
|
||||
}
|
||||
|
||||
/// Run `command` on the Fuchsia device shell.
|
||||
Future<String> shell(String command) async {
|
||||
final RunResult result = await runAsync(<String>[
|
||||
Future<RunResult> shell(String command) async {
|
||||
if (fuchsiaArtifacts.sshConfig == null) {
|
||||
throwToolExit('Cannot interact with device. No ssh config.\n'
|
||||
'Try setting FUCHSIA_SSH_CONFIG or FUCHSIA_BUILD_DIR.');
|
||||
}
|
||||
return await runAsync(<String>[
|
||||
'ssh',
|
||||
'-F',
|
||||
fuchsiaArtifacts.sshConfig.absolute.path,
|
||||
id,
|
||||
command
|
||||
]);
|
||||
if (result.exitCode != 0) {
|
||||
throwToolExit(
|
||||
'Command failed: $command\nstdout: ${result.stdout}\nstderr: ${result.stderr}');
|
||||
return null;
|
||||
}
|
||||
return result.stdout;
|
||||
}
|
||||
|
||||
/// Finds the first port running a VM matching `isolateName` from the
|
||||
@ -332,7 +466,9 @@ class FuchsiaDevice extends Device {
|
||||
FuchsiaIsolateDiscoveryProtocol(this, isolateName);
|
||||
|
||||
@override
|
||||
bool isSupportedForProject(FlutterProject flutterProject) => true;
|
||||
bool isSupportedForProject(FlutterProject flutterProject) {
|
||||
return flutterProject.fuchsia.existsSync();
|
||||
}
|
||||
}
|
||||
|
||||
class FuchsiaIsolateDiscoveryProtocol {
|
||||
@ -431,7 +567,10 @@ class _FuchsiaPortForwarder extends DevicePortForwarder {
|
||||
|
||||
@override
|
||||
Future<int> forward(int devicePort, {int hostPort}) async {
|
||||
hostPort ??= await _findPort();
|
||||
hostPort ??= await os.findFreePort();
|
||||
if (hostPort == 0) {
|
||||
throwToolExit('Failed to forward port $devicePort. No free host-side ports');
|
||||
}
|
||||
// Note: the provided command works around a bug in -N, see US-515
|
||||
// for more explanation.
|
||||
final List<String> command = <String>[
|
||||
@ -483,27 +622,4 @@ class _FuchsiaPortForwarder extends DevicePortForwarder {
|
||||
throwToolExit(result.stderr);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<int> _findPort() async {
|
||||
int port = 0;
|
||||
ServerSocket serverSocket;
|
||||
try {
|
||||
serverSocket = await ServerSocket.bind(_ipv4Loopback, 0);
|
||||
port = serverSocket.port;
|
||||
} catch (e) {
|
||||
// Failures are signaled by a return value of 0 from this function.
|
||||
printTrace('_findPort failed: $e');
|
||||
}
|
||||
if (serverSocket != null) {
|
||||
await serverSocket.close();
|
||||
}
|
||||
return port;
|
||||
}
|
||||
}
|
||||
|
||||
class FuchsiaModulePackage extends ApplicationPackage {
|
||||
FuchsiaModulePackage({@required this.name}) : super(id: name);
|
||||
|
||||
@override
|
||||
final String name;
|
||||
}
|
||||
|
@ -6,11 +6,10 @@ 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 '../base/process.dart';
|
||||
import '../build_info.dart';
|
||||
import '../convert.dart';
|
||||
import '../globals.dart';
|
||||
@ -18,10 +17,8 @@ import '../project.dart';
|
||||
|
||||
import 'fuchsia_sdk.dart';
|
||||
|
||||
/// The [FuchsiaKernelCompiler] instance.
|
||||
FuchsiaKernelCompiler get fuchsiaKernelCompiler =>
|
||||
context.get<FuchsiaKernelCompiler>();
|
||||
|
||||
/// This is a simple wrapper around the custom kernel compiler from the Fuchsia
|
||||
/// SDK.
|
||||
class FuchsiaKernelCompiler {
|
||||
/// Compiles the [fuchsiaProject] with entrypoint [target] to a collection of
|
||||
/// .dilp files (consisting of the app split along package: boundaries, but
|
||||
@ -33,6 +30,9 @@ class FuchsiaKernelCompiler {
|
||||
BuildInfo buildInfo = BuildInfo.debug,
|
||||
}) async {
|
||||
// TODO(zra): Use filesystem root and scheme information from buildInfo.
|
||||
if (fuchsiaArtifacts.kernelCompiler == null) {
|
||||
throwToolExit('Fuchisa kernel compiler not found');
|
||||
}
|
||||
const String multiRootScheme = 'main-root';
|
||||
final String packagesFile = fuchsiaProject.project.packagesFile.path;
|
||||
final String outDir = getFuchsiaBuildDirectory();
|
||||
@ -87,8 +87,7 @@ class FuchsiaKernelCompiler {
|
||||
artifacts.getArtifactPath(Artifact.engineDartBinary),
|
||||
fuchsiaArtifacts.kernelCompiler.path,
|
||||
]..addAll(flags);
|
||||
printTrace("Running: '${command.join(' ')}'");
|
||||
final Process process = await processManager.start(command);
|
||||
final Process process = await runCommand(command);
|
||||
final Status status = logger.startProgress(
|
||||
'Building Fuchsia application...',
|
||||
timeout: null,
|
||||
|
@ -2,16 +2,15 @@
|
||||
// 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/common.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../base/io.dart';
|
||||
import '../base/process_manager.dart';
|
||||
import '../base/process.dart';
|
||||
import '../convert.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
|
||||
@ -21,47 +20,27 @@ class FuchsiaPM {
|
||||
///
|
||||
/// 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,
|
||||
Future<bool> init(String buildPath, String appName) {
|
||||
return _runPMCommand(<String>[
|
||||
'-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,
|
||||
Future<bool> genkey(String buildPath, String outKeyPath) {
|
||||
return _runPMCommand(<String>[
|
||||
'-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.
|
||||
@ -80,9 +59,8 @@ class FuchsiaPM {
|
||||
/// 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,
|
||||
String buildPath, String keyPath, String manifestPath) {
|
||||
return _runPMCommand(<String>[
|
||||
'-o',
|
||||
buildPath,
|
||||
'-k',
|
||||
@ -90,16 +68,7 @@ class FuchsiaPM {
|
||||
'-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.
|
||||
@ -109,10 +78,8 @@ class FuchsiaPM {
|
||||
///
|
||||
/// [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,
|
||||
Future<bool> archive(String buildPath, String keyPath, String manifestPath) {
|
||||
return _runPMCommand(<String>[
|
||||
'-o',
|
||||
buildPath,
|
||||
'-k',
|
||||
@ -120,15 +87,155 @@ class FuchsiaPM {
|
||||
'-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;
|
||||
]);
|
||||
}
|
||||
|
||||
/// Initializes a new package repository at [repoPath] to be later served by
|
||||
/// the 'serve' command.
|
||||
Future<bool> newrepo(String repoPath) {
|
||||
return _runPMCommand(<String>[
|
||||
'newrepo',
|
||||
'-repo',
|
||||
repoPath,
|
||||
]);
|
||||
}
|
||||
|
||||
/// Spawns an http server in a new process for serving Fuchisa packages.
|
||||
///
|
||||
/// The arguemnt [repoPath] should have previously been an arguemnt to
|
||||
/// [newrepo]. The [host] should be the host reported by
|
||||
/// [FuchsiaDevFinder.resolve], and [port] should be an unused port for the
|
||||
/// http server to bind.
|
||||
Future<Process> serve(String repoPath, String host, int port) async {
|
||||
if (fuchsiaArtifacts.pm == null) {
|
||||
throwToolExit('Fuchsia pm tool not found');
|
||||
}
|
||||
return true;
|
||||
final List<String> command = <String>[
|
||||
fuchsiaArtifacts.pm.path,
|
||||
'serve',
|
||||
'-repo',
|
||||
repoPath,
|
||||
'-l',
|
||||
'$host:$port',
|
||||
];
|
||||
final Process process = await runCommand(command);
|
||||
process.stdout
|
||||
.transform(utf8.decoder)
|
||||
.transform(const LineSplitter())
|
||||
.listen(printTrace);
|
||||
process.stderr
|
||||
.transform(utf8.decoder)
|
||||
.transform(const LineSplitter())
|
||||
.listen(printError);
|
||||
return process;
|
||||
}
|
||||
|
||||
/// Publishes a Fuchsia package to a served package repository.
|
||||
///
|
||||
/// For a package repo initialized with [newrepo] at [repoPath] and served
|
||||
/// by [serve], this call publishes the `far` package at [packagePath] to
|
||||
/// the repo such that it will be visible to devices connecting to the
|
||||
/// package server.
|
||||
Future<bool> publish(String repoPath, String packagePath) {
|
||||
return _runPMCommand(<String>[
|
||||
'publish',
|
||||
'-a',
|
||||
'-r',
|
||||
repoPath,
|
||||
'-f',
|
||||
packagePath,
|
||||
]);
|
||||
}
|
||||
|
||||
Future<bool> _runPMCommand(List<String> args) async {
|
||||
if (fuchsiaArtifacts.pm == null) {
|
||||
throwToolExit('Fuchsia pm tool not found');
|
||||
}
|
||||
final List<String> command = <String>[fuchsiaArtifacts.pm.path] + args;
|
||||
final RunResult result = await runAsync(command);
|
||||
return result.exitCode == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// A class for running and retaining state for a Fuchsia package server.
|
||||
///
|
||||
/// [FuchsiaPackageServer] takes care of initializing the package repository,
|
||||
/// spinning up the package server, publishing packages, and shutting down the
|
||||
/// the server.
|
||||
///
|
||||
/// Example usage:
|
||||
/// var server = FuchsiaPackageServer(
|
||||
/// '/path/to/repo',
|
||||
/// await FuchsiaDevFinder.resolve(deviceName),
|
||||
/// await freshPort());
|
||||
/// try {
|
||||
/// await server.start();
|
||||
/// await server.addPackage(farArchivePath);
|
||||
/// ...
|
||||
/// } finally {
|
||||
/// server.stop();
|
||||
/// }
|
||||
class FuchsiaPackageServer {
|
||||
FuchsiaPackageServer(this._repo, this._host, this._port);
|
||||
|
||||
final String _repo;
|
||||
final String _host;
|
||||
final int _port;
|
||||
|
||||
Process _process;
|
||||
|
||||
/// The url that can be used by the device to access this package server.
|
||||
String get url => 'http://$_host:$_port';
|
||||
|
||||
/// Usees [FuchiaPM.newrepo] and [FuchsiaPM.serve] to spin up a new Fuchsia
|
||||
/// package server.
|
||||
///
|
||||
/// Returns false if ther repo could not be created or the server could not
|
||||
/// be spawned, and true otherwise.
|
||||
Future<bool> start() async {
|
||||
if (_process != null) {
|
||||
printError('$this already started!');
|
||||
return false;
|
||||
}
|
||||
// initialize a new repo.
|
||||
if (!await fuchsiaSdk.fuchsiaPM.newrepo(_repo)) {
|
||||
printError('Failed to create a new package server repo');
|
||||
return false;
|
||||
}
|
||||
_process = await fuchsiaSdk.fuchsiaPM.serve(_repo, _host, _port);
|
||||
// Put a completer on _process.exitCode to watch for error.
|
||||
unawaited(_process.exitCode.whenComplete(() {
|
||||
// If _process is null, then the server was stopped deliberately.
|
||||
if (_process != null) {
|
||||
printError('Error running Fuchsia pm tool "serve" command');
|
||||
}
|
||||
}));
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Forcefully stops the package server process by sending it SIGTERM.
|
||||
void stop() {
|
||||
if (_process != null) {
|
||||
_process.kill();
|
||||
_process = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Uses [FuchsiaPM.publish] to add the Fuchsia 'far' package at
|
||||
/// [packagePath] to the package server.
|
||||
///
|
||||
/// Returns true on success and false if the server wasn't started or the
|
||||
/// publish command failed.
|
||||
Future<bool> addPackage(File package) async {
|
||||
if (_process == null) {
|
||||
return false;
|
||||
}
|
||||
return await fuchsiaSdk.fuchsiaPM.publish(_repo, package.path);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
final String p = (_process == null) ? 'stopped' : 'running ${_process.pid}';
|
||||
return 'FuchsiaPackageServer at $_host:$_port ($p)';
|
||||
}
|
||||
}
|
||||
|
@ -8,12 +8,15 @@ import '../base/context.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../base/io.dart';
|
||||
import '../base/platform.dart';
|
||||
import '../base/process.dart';
|
||||
import '../base/process_manager.dart';
|
||||
import '../cache.dart';
|
||||
import '../convert.dart';
|
||||
import '../globals.dart';
|
||||
|
||||
import 'fuchsia_dev_finder.dart';
|
||||
import 'fuchsia_kernel_compiler.dart';
|
||||
import 'fuchsia_pm.dart';
|
||||
|
||||
/// The [FuchsiaSdk] instance.
|
||||
FuchsiaSdk get fuchsiaSdk => context.get<FuchsiaSdk>();
|
||||
|
||||
@ -25,23 +28,32 @@ FuchsiaArtifacts get fuchsiaArtifacts => context.get<FuchsiaArtifacts>();
|
||||
/// This workflow assumes development within the fuchsia source tree,
|
||||
/// including a working fx command-line tool in the user's PATH.
|
||||
class FuchsiaSdk {
|
||||
/// Interface to the 'pm' tool.
|
||||
FuchsiaPM get fuchsiaPM => _fuchsiaPM ??= FuchsiaPM();
|
||||
FuchsiaPM _fuchsiaPM;
|
||||
|
||||
/// Interface to the 'dev_finder' tool.
|
||||
FuchsiaDevFinder _fuchsiaDevFinder;
|
||||
FuchsiaDevFinder get fuchsiaDevFinder =>
|
||||
_fuchsiaDevFinder ??= FuchsiaDevFinder();
|
||||
|
||||
/// Interface to the 'kernel_compiler' tool.
|
||||
FuchsiaKernelCompiler _fuchsiaKernelCompiler;
|
||||
FuchsiaKernelCompiler get fuchsiaKernelCompiler =>
|
||||
_fuchsiaKernelCompiler ??= FuchsiaKernelCompiler();
|
||||
|
||||
/// Example output:
|
||||
/// $ dev_finder list -full
|
||||
/// > 192.168.42.56 paper-pulp-bush-angel
|
||||
Future<String> listDevices() async {
|
||||
try {
|
||||
final String path = fuchsiaArtifacts.devFinder.absolute.path;
|
||||
final RunResult process = await runAsync(<String>[path, 'list', '-full']);
|
||||
return process.stdout.trim();
|
||||
} catch (exception) {
|
||||
printTrace('$exception');
|
||||
if (fuchsiaArtifacts.devFinder == null) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
final List<String> devices = await fuchsiaDevFinder.list();
|
||||
return devices.isNotEmpty ? devices[0] : null;
|
||||
}
|
||||
|
||||
/// Returns the fuchsia system logs for an attached device.
|
||||
///
|
||||
/// Does not currently support multiple attached devices.
|
||||
Stream<String> syslogs(String id) {
|
||||
Process process;
|
||||
try {
|
||||
@ -50,6 +62,8 @@ class FuchsiaSdk {
|
||||
process.kill();
|
||||
});
|
||||
if (fuchsiaArtifacts.sshConfig == null) {
|
||||
printError('Cannot read device logs: No ssh config.');
|
||||
printError('Have you set FUCHSIA_SSH_CONFIG or FUCHSIA_BUILD_DIR?');
|
||||
return null;
|
||||
}
|
||||
const String remoteCommand = 'log_listener --clock Local';
|
||||
@ -101,6 +115,15 @@ class FuchsiaArtifacts {
|
||||
final String tools = fs.path.join(fuchsia, 'tools');
|
||||
final String dartPrebuilts = fs.path.join(tools, 'dart_prebuilts');
|
||||
|
||||
final File devFinder = fs.file(fs.path.join(tools, 'dev_finder'));
|
||||
final File platformDill = fs.file(fs.path.join(
|
||||
dartPrebuilts, 'flutter_runner', 'platform_strong.dill'));
|
||||
final File patchedSdk = fs.file(fs.path.join(
|
||||
dartPrebuilts, 'flutter_runner'));
|
||||
final File kernelCompiler = fs.file(fs.path.join(
|
||||
dartPrebuilts, 'kernel_compiler.snapshot'));
|
||||
final File pm = fs.file(fs.path.join(tools, 'pm'));
|
||||
|
||||
// If FUCHSIA_BUILD_DIR is defined, then look for the ssh_config dir
|
||||
// relative to it. Next, if FUCHSIA_SSH_CONFIG is defined, then use it.
|
||||
// TODO(zra): Consider passing the ssh config path in with a flag.
|
||||
@ -113,14 +136,11 @@ class FuchsiaArtifacts {
|
||||
}
|
||||
return FuchsiaArtifacts(
|
||||
sshConfig: sshConfig,
|
||||
devFinder: fs.file(fs.path.join(tools, 'dev_finder')),
|
||||
platformKernelDill: fs.file(fs.path.join(
|
||||
dartPrebuilts, 'flutter_runner', 'platform_strong.dill')),
|
||||
flutterPatchedSdk: fs.file(fs.path.join(
|
||||
dartPrebuilts, 'flutter_runner')),
|
||||
kernelCompiler: fs.file(fs.path.join(
|
||||
dartPrebuilts, 'kernel_compiler.snapshot')),
|
||||
pm: fs.file(fs.path.join(tools, 'pm')),
|
||||
devFinder: devFinder.existsSync() ? devFinder : null,
|
||||
platformKernelDill: platformDill.existsSync() ? platformDill : null,
|
||||
flutterPatchedSdk: patchedSdk.existsSync() ? patchedSdk : null,
|
||||
kernelCompiler: kernelCompiler.existsSync() ? kernelCompiler : null,
|
||||
pm: pm.existsSync() ? pm : null,
|
||||
);
|
||||
}
|
||||
|
||||
|
113
packages/flutter_tools/lib/src/fuchsia/tiles_ctl.dart
Normal file
113
packages/flutter_tools/lib/src/fuchsia/tiles_ctl.dart
Normal file
@ -0,0 +1,113 @@
|
||||
// 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/process.dart';
|
||||
import '../globals.dart';
|
||||
|
||||
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) {
|
||||
printTrace('tiles_ctl is not running');
|
||||
return -1;
|
||||
}
|
||||
for (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 (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]);
|
||||
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 arguemnts 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;
|
||||
}
|
||||
}
|
@ -313,9 +313,15 @@ class FlutterDevice {
|
||||
}
|
||||
|
||||
void startEchoingDeviceLog() {
|
||||
if (_loggingSubscription != null)
|
||||
if (_loggingSubscription != null) {
|
||||
return;
|
||||
_loggingSubscription = device.getLogReader(app: package).logLines.listen((String line) {
|
||||
}
|
||||
final Stream<String> logStream = device.getLogReader(app: package).logLines;
|
||||
if (logStream == null) {
|
||||
printError('Failed to read device log stream');
|
||||
return;
|
||||
}
|
||||
_loggingSubscription = logStream.listen((String line) {
|
||||
if (!line.contains('Observatory listening on http'))
|
||||
printStatus(line, wrap: false);
|
||||
});
|
||||
|
@ -11,6 +11,7 @@ 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/fuchsia/fuchsia_sdk.dart';
|
||||
import 'package:flutter_tools/src/project.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
@ -25,69 +26,99 @@ void main() {
|
||||
MemoryFileSystem memoryFileSystem;
|
||||
MockPlatform linuxPlatform;
|
||||
MockPlatform windowsPlatform;
|
||||
MockFuchsiaPM fuchsiaPM;
|
||||
MockFuchsiaKernelCompiler fuchsiaKernelCompiler;
|
||||
MockFuchsiaSdk fuchsiaSdk;
|
||||
MockFuchsiaArtifacts fuchsiaArtifacts;
|
||||
MockFuchsiaArtifacts fuchsiaArtifactsNoCompiler;
|
||||
|
||||
setUp(() {
|
||||
memoryFileSystem = MemoryFileSystem();
|
||||
linuxPlatform = MockPlatform();
|
||||
windowsPlatform = MockPlatform();
|
||||
fuchsiaPM = MockFuchsiaPM();
|
||||
fuchsiaKernelCompiler = MockFuchsiaKernelCompiler();
|
||||
fuchsiaSdk = MockFuchsiaSdk();
|
||||
fuchsiaArtifacts = MockFuchsiaArtifacts();
|
||||
fuchsiaArtifactsNoCompiler = MockFuchsiaArtifacts();
|
||||
|
||||
when(linuxPlatform.isLinux).thenReturn(true);
|
||||
when(windowsPlatform.isWindows).thenReturn(true);
|
||||
when(windowsPlatform.isLinux).thenReturn(false);
|
||||
when(windowsPlatform.isMacOS).thenReturn(false);
|
||||
when(fuchsiaArtifacts.kernelCompiler).thenReturn(MockFile());
|
||||
when(fuchsiaArtifactsNoCompiler.kernelCompiler).thenReturn(null);
|
||||
});
|
||||
|
||||
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,
|
||||
});
|
||||
group('Fuchsia build fails gracefully when', () {
|
||||
testUsingContext('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,
|
||||
FuchsiaArtifacts: () => fuchsiaArtifacts,
|
||||
});
|
||||
|
||||
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();
|
||||
testUsingContext('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,
|
||||
});
|
||||
expect(
|
||||
createTestCommandRunner(command)
|
||||
.run(const <String>['build', 'fuchsia']),
|
||||
throwsA(isInstanceOf<ToolExit>()));
|
||||
}, overrides: <Type, Generator>{
|
||||
Platform: () => linuxPlatform,
|
||||
FileSystem: () => memoryFileSystem,
|
||||
FuchsiaArtifacts: () => fuchsiaArtifacts,
|
||||
});
|
||||
|
||||
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');
|
||||
testUsingContext('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,
|
||||
expect(
|
||||
createTestCommandRunner(command)
|
||||
.run(const <String>['build', 'fuchsia']),
|
||||
throwsA(isInstanceOf<ToolExit>()));
|
||||
}, overrides: <Type, Generator>{
|
||||
Platform: () => windowsPlatform,
|
||||
FileSystem: () => memoryFileSystem,
|
||||
FuchsiaArtifacts: () => fuchsiaArtifacts,
|
||||
});
|
||||
|
||||
testUsingContext('there is no Fuchsia kernel compiler', () 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');
|
||||
expect(
|
||||
createTestCommandRunner(command)
|
||||
.run(const <String>['build', 'fuchsia']),
|
||||
throwsA(isInstanceOf<ToolExit>()));
|
||||
}, overrides: <Type, Generator>{
|
||||
Platform: () => linuxPlatform,
|
||||
FileSystem: () => memoryFileSystem,
|
||||
FuchsiaArtifacts: () => fuchsiaArtifactsNoCompiler,
|
||||
});
|
||||
});
|
||||
|
||||
testUsingContext('Fuchsia build parts fit together right', () async {
|
||||
@ -110,8 +141,7 @@ void main() {
|
||||
}, overrides: <Type, Generator>{
|
||||
Platform: () => linuxPlatform,
|
||||
FileSystem: () => memoryFileSystem,
|
||||
FuchsiaPM: () => fuchsiaPM,
|
||||
FuchsiaKernelCompiler: () => fuchsiaKernelCompiler,
|
||||
FuchsiaSdk: () => fuchsiaSdk,
|
||||
});
|
||||
}
|
||||
|
||||
@ -189,3 +219,16 @@ class MockFuchsiaKernelCompiler extends Mock implements FuchsiaKernelCompiler {
|
||||
fs.file(manifestPath).createSync(recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
class MockFuchsiaSdk extends Mock implements FuchsiaSdk {
|
||||
@override
|
||||
final FuchsiaPM fuchsiaPM = MockFuchsiaPM();
|
||||
|
||||
@override
|
||||
final FuchsiaKernelCompiler fuchsiaKernelCompiler =
|
||||
MockFuchsiaKernelCompiler();
|
||||
}
|
||||
|
||||
class MockFile extends Mock implements File {}
|
||||
|
||||
class MockFuchsiaArtifacts extends Mock implements FuchsiaArtifacts {}
|
||||
|
@ -5,26 +5,42 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/vmservice.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:process/process.dart';
|
||||
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:flutter_tools/src/application_package.dart';
|
||||
import 'package:flutter_tools/src/artifacts.dart';
|
||||
import 'package:flutter_tools/src/base/common.dart';
|
||||
import 'package:flutter_tools/src/base/context.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/io.dart';
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/base/os.dart';
|
||||
import 'package:flutter_tools/src/base/time.dart';
|
||||
import 'package:flutter_tools/src/build_info.dart';
|
||||
import 'package:flutter_tools/src/device.dart';
|
||||
import 'package:flutter_tools/src/fuchsia/application_package.dart';
|
||||
import 'package:flutter_tools/src/fuchsia/amber_ctl.dart';
|
||||
import 'package:flutter_tools/src/fuchsia/fuchsia_device.dart';
|
||||
import 'package:flutter_tools/src/fuchsia/fuchsia_dev_finder.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/fuchsia/tiles_ctl.dart';
|
||||
import 'package:flutter_tools/src/project.dart';
|
||||
import 'package:flutter_tools/src/vmservice.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:process/process.dart';
|
||||
|
||||
import '../src/common.dart';
|
||||
import '../src/context.dart';
|
||||
|
||||
void main() {
|
||||
group('fuchsia device', () {
|
||||
MemoryFileSystem memoryFileSystem;
|
||||
setUp(() {
|
||||
memoryFileSystem = MemoryFileSystem();
|
||||
});
|
||||
|
||||
testUsingContext('stores the requested id and name', () {
|
||||
const String deviceId = 'e80::0000:a00a:f00f:2002/3';
|
||||
const String name = 'halfbaked';
|
||||
@ -49,14 +65,34 @@ void main() {
|
||||
expect(names.length, 0);
|
||||
});
|
||||
|
||||
test('default capabilities', () async {
|
||||
testUsingContext('default capabilities', () async {
|
||||
final FuchsiaDevice device = FuchsiaDevice('123');
|
||||
fs.directory('fuchsia').createSync(recursive: true);
|
||||
fs.file('pubspec.yaml').createSync();
|
||||
|
||||
expect(device.supportsHotReload, true);
|
||||
expect(device.supportsHotRestart, false);
|
||||
expect(device.supportsStopApp, false);
|
||||
expect(device.isSupportedForProject(null), true);
|
||||
expect(await device.stopApp(null), false);
|
||||
expect(device.isSupportedForProject(FlutterProject.current()), true);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => memoryFileSystem,
|
||||
});
|
||||
|
||||
testUsingContext('supported for project', () async {
|
||||
final FuchsiaDevice device = FuchsiaDevice('123');
|
||||
fs.directory('fuchsia').createSync(recursive: true);
|
||||
fs.file('pubspec.yaml').createSync();
|
||||
expect(device.isSupportedForProject(FlutterProject.current()), true);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => memoryFileSystem,
|
||||
});
|
||||
|
||||
testUsingContext('not supported for project', () async {
|
||||
final FuchsiaDevice device = FuchsiaDevice('123');
|
||||
fs.file('pubspec.yaml').createSync();
|
||||
expect(device.isSupportedForProject(FlutterProject.current()), false);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => memoryFileSystem,
|
||||
});
|
||||
});
|
||||
|
||||
@ -215,7 +251,7 @@ void main() {
|
||||
testUsingContext('can be parsed for an app', () async {
|
||||
final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester');
|
||||
final DeviceLogReader reader = device.getLogReader(
|
||||
app: FuchsiaModulePackage(name: 'example_app.cmx'));
|
||||
app: FuchsiaModulePackage(name: 'example_app'));
|
||||
final List<String> logLines = <String>[];
|
||||
final Completer<void> lock = Completer<void>();
|
||||
reader.logLines.listen((String line) {
|
||||
@ -244,7 +280,7 @@ void main() {
|
||||
testUsingContext('cuts off prior logs', () async {
|
||||
final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester');
|
||||
final DeviceLogReader reader = device.getLogReader(
|
||||
app: FuchsiaModulePackage(name: 'example_app.cmx'));
|
||||
app: FuchsiaModulePackage(name: 'example_app'));
|
||||
final List<String> logLines = <String>[];
|
||||
final Completer<void> lock = Completer<void>();
|
||||
reader.logLines.listen((String line) {
|
||||
@ -367,16 +403,119 @@ void main() {
|
||||
Logger: () => StdoutLogger(),
|
||||
});
|
||||
});
|
||||
|
||||
group('fuchsia app start and stop: ', () {
|
||||
MemoryFileSystem memoryFileSystem;
|
||||
MockOperatingSystemUtils osUtils;
|
||||
MockFuchsiaDeviceTools fuchsiaDeviceTools;
|
||||
MockFuchsiaSdk fuchsiaSdk;
|
||||
setUp(() {
|
||||
memoryFileSystem = MemoryFileSystem();
|
||||
osUtils = MockOperatingSystemUtils();
|
||||
fuchsiaDeviceTools = MockFuchsiaDeviceTools();
|
||||
fuchsiaSdk = MockFuchsiaSdk();
|
||||
|
||||
when(osUtils.findFreePort()).thenAnswer((_) => Future<int>.value(12345));
|
||||
});
|
||||
|
||||
testUsingContext('start prebuilt app in release mode', () async {
|
||||
const String appName = 'app_name';
|
||||
final FuchsiaDevice device = FuchsiaDevice('123');
|
||||
fs.directory('fuchsia').createSync(recursive: true);
|
||||
final File pubspecFile = fs.file('pubspec.yaml')..createSync();
|
||||
pubspecFile.writeAsStringSync('name: $appName');
|
||||
final File far = fs.file('app_name-0.far')..createSync();
|
||||
|
||||
final FuchsiaApp app = FuchsiaApp.fromPrebuiltApp(far);
|
||||
final DebuggingOptions debuggingOptions =
|
||||
DebuggingOptions.disabled(const BuildInfo(BuildMode.release, null));
|
||||
final LaunchResult launchResult = await device.startApp(app,
|
||||
prebuiltApplication: true,
|
||||
debuggingOptions: debuggingOptions);
|
||||
|
||||
expect(launchResult.started, isTrue);
|
||||
expect(launchResult.hasObservatory, isFalse);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => memoryFileSystem,
|
||||
FuchsiaDeviceTools: () => fuchsiaDeviceTools,
|
||||
FuchsiaSdk: () => fuchsiaSdk,
|
||||
OperatingSystemUtils: () => osUtils,
|
||||
});
|
||||
|
||||
testUsingContext('start and stop prebuilt app in release mode', () async {
|
||||
const String appName = 'app_name';
|
||||
final FuchsiaDevice device = FuchsiaDevice('123');
|
||||
fs.directory('fuchsia').createSync(recursive: true);
|
||||
final File pubspecFile = fs.file('pubspec.yaml')..createSync();
|
||||
pubspecFile.writeAsStringSync('name: $appName');
|
||||
final File far = fs.file('app_name-0.far')..createSync();
|
||||
|
||||
final FuchsiaApp app = FuchsiaApp.fromPrebuiltApp(far);
|
||||
final DebuggingOptions debuggingOptions =
|
||||
DebuggingOptions.disabled(const BuildInfo(BuildMode.release, null));
|
||||
final LaunchResult launchResult = await device.startApp(app,
|
||||
prebuiltApplication: true,
|
||||
debuggingOptions: debuggingOptions);
|
||||
|
||||
expect(launchResult.started, isTrue);
|
||||
expect(launchResult.hasObservatory, isFalse);
|
||||
expect(await device.stopApp(app), isTrue);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => memoryFileSystem,
|
||||
FuchsiaDeviceTools: () => fuchsiaDeviceTools,
|
||||
FuchsiaSdk: () => fuchsiaSdk,
|
||||
OperatingSystemUtils: () => osUtils,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class FuchsiaModulePackage extends ApplicationPackage {
|
||||
FuchsiaModulePackage({@required this.name}) : super(id: name);
|
||||
|
||||
@override
|
||||
final String name;
|
||||
}
|
||||
|
||||
class MockProcessManager extends Mock implements ProcessManager {}
|
||||
|
||||
class MockProcessResult extends Mock implements ProcessResult {}
|
||||
|
||||
class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils {}
|
||||
|
||||
class MockFile extends Mock implements File {}
|
||||
|
||||
class MockProcess extends Mock implements Process {}
|
||||
|
||||
Process _createMockProcess({
|
||||
int exitCode = 0,
|
||||
String stdout = '',
|
||||
String stderr = '',
|
||||
bool persistent = false,
|
||||
}) {
|
||||
final Stream<List<int>> stdoutStream = Stream<List<int>>.fromIterable(<List<int>>[
|
||||
utf8.encode(stdout),
|
||||
]);
|
||||
final Stream<List<int>> stderrStream = Stream<List<int>>.fromIterable(<List<int>>[
|
||||
utf8.encode(stderr),
|
||||
]);
|
||||
final Process process = MockProcess();
|
||||
|
||||
when(process.stdout).thenAnswer((_) => stdoutStream);
|
||||
when(process.stderr).thenAnswer((_) => stderrStream);
|
||||
|
||||
if (persistent) {
|
||||
final Completer<int> exitCodeCompleter = Completer<int>();
|
||||
when(process.kill()).thenAnswer((_) {
|
||||
exitCodeCompleter.complete(-11);
|
||||
return true;
|
||||
});
|
||||
when(process.exitCode).thenAnswer((_) => exitCodeCompleter.future);
|
||||
} else {
|
||||
when(process.exitCode).thenAnswer((_) => Future<int>.value(exitCode));
|
||||
}
|
||||
return process;
|
||||
}
|
||||
|
||||
class MockFuchsiaDevice extends Mock implements FuchsiaDevice {
|
||||
MockFuchsiaDevice(this.id, this.portForwarder, this.ipv6);
|
||||
|
||||
@ -419,3 +558,192 @@ class MockIsolate extends Mock implements Isolate {
|
||||
@override
|
||||
final String name;
|
||||
}
|
||||
|
||||
class MockFuchsiaAmberCtl extends Mock implements FuchsiaAmberCtl {
|
||||
@override
|
||||
Future<bool> addSrc(FuchsiaDevice device, FuchsiaPackageServer server) async {
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> rmSrc(FuchsiaDevice device, FuchsiaPackageServer server) async {
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> getUp(FuchsiaDevice device, String packageName) async {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class MockFuchsiaTilesCtl extends Mock implements FuchsiaTilesCtl {
|
||||
final Map<int, String> _runningApps = <int, String>{};
|
||||
bool _started = false;
|
||||
int _nextAppId = 1;
|
||||
|
||||
@override
|
||||
Future<bool> start(FuchsiaDevice device) async {
|
||||
_started = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<int, String>> list(FuchsiaDevice device) async {
|
||||
if (!_started) {
|
||||
return null;
|
||||
}
|
||||
return _runningApps;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> add(FuchsiaDevice device, String url, List<String> args) async {
|
||||
if (!_started) {
|
||||
return false;
|
||||
}
|
||||
_runningApps[_nextAppId] = url;
|
||||
_nextAppId++;
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> remove(FuchsiaDevice device, int key) async {
|
||||
if (!_started) {
|
||||
return false;
|
||||
}
|
||||
_runningApps.remove(key);
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> quit(FuchsiaDevice device) async {
|
||||
if (!_started) {
|
||||
return false;
|
||||
}
|
||||
_started = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class MockFuchsiaDeviceTools extends Mock implements FuchsiaDeviceTools {
|
||||
@override
|
||||
final FuchsiaAmberCtl amberCtl = MockFuchsiaAmberCtl();
|
||||
|
||||
@override
|
||||
final FuchsiaTilesCtl tilesCtl = MockFuchsiaTilesCtl();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> newrepo(String repoPath) async {
|
||||
if (!fs.directory(repoPath).existsSync()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Process> serve(String repoPath, String host, int port) async {
|
||||
return _createMockProcess(persistent: true);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> publish(String repoPath, String packagePath) async {
|
||||
if (!fs.directory(repoPath).existsSync()) {
|
||||
return false;
|
||||
}
|
||||
if (!fs.file(packagePath).existsSync()) {
|
||||
return false;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
class MockFuchsiaDevFinder extends Mock implements FuchsiaDevFinder {
|
||||
@override
|
||||
Future<List<String>> list() async {
|
||||
return <String>['192.168.42.172 scare-cable-skip-joy'];
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> resolve(String deviceName) async {
|
||||
return '192.168.42.10';
|
||||
}
|
||||
}
|
||||
|
||||
class MockFuchsiaSdk extends Mock implements FuchsiaSdk {
|
||||
@override
|
||||
final FuchsiaPM fuchsiaPM = MockFuchsiaPM();
|
||||
|
||||
@override
|
||||
final FuchsiaKernelCompiler fuchsiaKernelCompiler =
|
||||
MockFuchsiaKernelCompiler();
|
||||
|
||||
@override
|
||||
final FuchsiaDevFinder fuchsiaDevFinder = MockFuchsiaDevFinder();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user