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/process.dart';
|
||||||
import 'base/user_messages.dart';
|
import 'base/user_messages.dart';
|
||||||
import 'build_info.dart';
|
import 'build_info.dart';
|
||||||
|
import 'fuchsia/application_package.dart';
|
||||||
import 'globals.dart';
|
import 'globals.dart';
|
||||||
import 'ios/ios_workflow.dart';
|
import 'ios/ios_workflow.dart';
|
||||||
import 'ios/plist_utils.dart' as plist;
|
import 'ios/plist_utils.dart' as plist;
|
||||||
@ -66,7 +67,9 @@ class ApplicationPackageFactory {
|
|||||||
? WindowsApp.fromWindowsProject(FlutterProject.current().windows)
|
? WindowsApp.fromWindowsProject(FlutterProject.current().windows)
|
||||||
: WindowsApp.fromPrebuiltApp(applicationBinary);
|
: WindowsApp.fromPrebuiltApp(applicationBinary);
|
||||||
case TargetPlatform.fuchsia:
|
case TargetPlatform.fuchsia:
|
||||||
return null;
|
return applicationBinary == null
|
||||||
|
? FuchsiaApp.fromFuchsiaProject(FlutterProject.current().fuchsia)
|
||||||
|
: FuchsiaApp.fromPrebuiltApp(applicationBinary);
|
||||||
}
|
}
|
||||||
assert(platform != null);
|
assert(platform != null);
|
||||||
return null;
|
return null;
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
import 'package:archive/archive.dart';
|
import 'package:archive/archive.dart';
|
||||||
|
|
||||||
|
import '../globals.dart';
|
||||||
import 'context.dart';
|
import 'context.dart';
|
||||||
import 'file_system.dart';
|
import 'file_system.dart';
|
||||||
import 'io.dart';
|
import 'io.dart';
|
||||||
@ -72,6 +74,37 @@ abstract class OperatingSystemUtils {
|
|||||||
|
|
||||||
/// Returns the separator between items in the PATH environment variable.
|
/// Returns the separator between items in the PATH environment variable.
|
||||||
String get pathVarSeparator;
|
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 {
|
class _PosixUtils extends OperatingSystemUtils {
|
||||||
|
@ -27,10 +27,9 @@ import 'devfs.dart';
|
|||||||
import 'device.dart';
|
import 'device.dart';
|
||||||
import 'doctor.dart';
|
import 'doctor.dart';
|
||||||
import 'emulator.dart';
|
import 'emulator.dart';
|
||||||
import 'fuchsia/fuchsia_kernel_compiler.dart';
|
import 'fuchsia/fuchsia_device.dart' show FuchsiaDeviceTools;
|
||||||
import 'fuchsia/fuchsia_pm.dart';
|
import 'fuchsia/fuchsia_sdk.dart' show FuchsiaSdk, FuchsiaArtifacts;
|
||||||
import 'fuchsia/fuchsia_sdk.dart';
|
import 'fuchsia/fuchsia_workflow.dart' show FuchsiaWorkflow;
|
||||||
import 'fuchsia/fuchsia_workflow.dart';
|
|
||||||
import 'ios/cocoapods.dart';
|
import 'ios/cocoapods.dart';
|
||||||
import 'ios/ios_workflow.dart';
|
import 'ios/ios_workflow.dart';
|
||||||
import 'ios/mac.dart';
|
import 'ios/mac.dart';
|
||||||
@ -76,8 +75,7 @@ Future<T> runInContext<T>(
|
|||||||
Flags: () => const EmptyFlags(),
|
Flags: () => const EmptyFlags(),
|
||||||
FlutterVersion: () => FlutterVersion(const SystemClock()),
|
FlutterVersion: () => FlutterVersion(const SystemClock()),
|
||||||
FuchsiaArtifacts: () => FuchsiaArtifacts.find(),
|
FuchsiaArtifacts: () => FuchsiaArtifacts.find(),
|
||||||
FuchsiaKernelCompiler: () => FuchsiaKernelCompiler(),
|
FuchsiaDeviceTools: () => FuchsiaDeviceTools(),
|
||||||
FuchsiaPM: () => FuchsiaPM(),
|
|
||||||
FuchsiaSdk: () => FuchsiaSdk(),
|
FuchsiaSdk: () => FuchsiaSdk(),
|
||||||
FuchsiaWorkflow: () => FuchsiaWorkflow(),
|
FuchsiaWorkflow: () => FuchsiaWorkflow(),
|
||||||
GenSnapshot: () => const GenSnapshot(),
|
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 '../devfs.dart';
|
||||||
import '../project.dart';
|
import '../project.dart';
|
||||||
|
|
||||||
import 'fuchsia_kernel_compiler.dart';
|
|
||||||
import 'fuchsia_pm.dart';
|
import 'fuchsia_pm.dart';
|
||||||
|
import 'fuchsia_sdk.dart';
|
||||||
|
|
||||||
// Building a Fuchsia package has a few steps:
|
// Building a Fuchsia package has a few steps:
|
||||||
// 1. Do the custom kernel compile using the kernel compiler from the Fuchsia
|
// 1. Do the custom kernel compile using the kernel compiler from the Fuchsia
|
||||||
@ -30,7 +30,7 @@ Future<void> buildFuchsia(
|
|||||||
outDir.createSync(recursive: true);
|
outDir.createSync(recursive: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
await fuchsiaKernelCompiler.build(
|
await fuchsiaSdk.fuchsiaKernelCompiler.build(
|
||||||
fuchsiaProject: fuchsiaProject, target: target, buildInfo: buildInfo);
|
fuchsiaProject: fuchsiaProject, target: target, buildInfo: buildInfo);
|
||||||
await _buildAssets(fuchsiaProject, target, buildInfo);
|
await _buildAssets(fuchsiaProject, target, buildInfo);
|
||||||
await _buildPackage(fuchsiaProject, target, buildInfo);
|
await _buildPackage(fuchsiaProject, target, buildInfo);
|
||||||
@ -96,6 +96,7 @@ Future<void> _buildPackage(
|
|||||||
manifestFile.writeAsStringSync('meta/package=$pkgDir/meta/package\n',
|
manifestFile.writeAsStringSync('meta/package=$pkgDir/meta/package\n',
|
||||||
mode: FileMode.append);
|
mode: FileMode.append);
|
||||||
|
|
||||||
|
final FuchsiaPM fuchsiaPM = fuchsiaSdk.fuchsiaPM;
|
||||||
if (!await fuchsiaPM.init(pkgDir, appName)) {
|
if (!await fuchsiaPM.init(pkgDir, appName)) {
|
||||||
return;
|
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 '../application_package.dart';
|
||||||
import '../artifacts.dart';
|
import '../artifacts.dart';
|
||||||
import '../base/common.dart';
|
import '../base/common.dart';
|
||||||
|
import '../base/context.dart';
|
||||||
|
import '../base/file_system.dart';
|
||||||
import '../base/io.dart';
|
import '../base/io.dart';
|
||||||
import '../base/logger.dart';
|
import '../base/logger.dart';
|
||||||
|
import '../base/os.dart';
|
||||||
import '../base/platform.dart';
|
import '../base/platform.dart';
|
||||||
import '../base/process.dart';
|
import '../base/process.dart';
|
||||||
import '../base/process_manager.dart';
|
import '../base/process_manager.dart';
|
||||||
@ -21,8 +24,28 @@ import '../globals.dart';
|
|||||||
import '../project.dart';
|
import '../project.dart';
|
||||||
import '../vmservice.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_sdk.dart';
|
||||||
import 'fuchsia_workflow.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 _ipv4Loopback = InternetAddress.loopbackIPv4.address;
|
||||||
final String _ipv6Loopback = InternetAddress.loopbackIPv6.address;
|
final String _ipv6Loopback = InternetAddress.loopbackIPv6.address;
|
||||||
@ -48,11 +71,15 @@ class _FuchsiaLogReader extends DeviceLogReader {
|
|||||||
Stream<String> _logLines;
|
Stream<String> _logLines;
|
||||||
@override
|
@override
|
||||||
Stream<String> get logLines {
|
Stream<String> get logLines {
|
||||||
_logLines ??= _processLogs(fuchsiaSdk.syslogs(_device.id));
|
final Stream<String> logStream = fuchsiaSdk.syslogs(_device.id);
|
||||||
|
_logLines ??= _processLogs(logStream);
|
||||||
return _logLines;
|
return _logLines;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<String> _processLogs(Stream<String> lines) {
|
Stream<String> _processLogs(Stream<String> lines) {
|
||||||
|
if (lines == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
// Get the starting time of the log processor to filter logs from before
|
// Get the starting time of the log processor to filter logs from before
|
||||||
// the process attached.
|
// the process attached.
|
||||||
final DateTime startTime = systemClock.now();
|
final DateTime startTime = systemClock.now();
|
||||||
@ -60,7 +87,7 @@ class _FuchsiaLogReader extends DeviceLogReader {
|
|||||||
// the correct fuchsia module.
|
// the correct fuchsia module.
|
||||||
final RegExp matchRegExp = _app == null
|
final RegExp matchRegExp = _app == null
|
||||||
? _flutterLogOutput
|
? _flutterLogOutput
|
||||||
: RegExp('INFO: ${_app.name}\\(flutter\\): ');
|
: RegExp('INFO: ${_app.name}(\.cmx)?\\(flutter\\): ');
|
||||||
return Stream<String>.eventTransformed(
|
return Stream<String>.eventTransformed(
|
||||||
lines,
|
lines,
|
||||||
(Sink<String> outout) => _FuchsiaLogSink(outout, matchRegExp, startTime),
|
(Sink<String> outout) => _FuchsiaLogSink(outout, matchRegExp, startTime),
|
||||||
@ -188,7 +215,7 @@ class FuchsiaDevice extends Device {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<LaunchResult> startApp(
|
Future<LaunchResult> startApp(
|
||||||
ApplicationPackage package, {
|
covariant FuchsiaApp package, {
|
||||||
String mainPath,
|
String mainPath,
|
||||||
String route,
|
String route,
|
||||||
DebuggingOptions debuggingOptions,
|
DebuggingOptions debuggingOptions,
|
||||||
@ -196,14 +223,111 @@ class FuchsiaDevice extends Device {
|
|||||||
bool prebuiltApplication = false,
|
bool prebuiltApplication = false,
|
||||||
bool usesTerminalUi = true,
|
bool usesTerminalUi = true,
|
||||||
bool ipv6 = false,
|
bool ipv6 = false,
|
||||||
}) =>
|
}) async {
|
||||||
Future<void>.error('unimplemented');
|
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
|
@override
|
||||||
Future<bool> stopApp(ApplicationPackage app) async {
|
Future<bool> stopApp(covariant FuchsiaApp app) async {
|
||||||
// Currently we don't have a way to stop an app running on Fuchsia.
|
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 false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<TargetPlatform> get targetPlatform async => TargetPlatform.fuchsia;
|
Future<TargetPlatform> get targetPlatform async => TargetPlatform.fuchsia;
|
||||||
@ -250,7 +374,13 @@ class FuchsiaDevice extends Device {
|
|||||||
|
|
||||||
/// List the ports currently running a dart observatory.
|
/// List the ports currently running a dart observatory.
|
||||||
Future<List<int>> servicePorts() async {
|
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() == '') {
|
if (findOutput.trim() == '') {
|
||||||
throwToolExit(
|
throwToolExit(
|
||||||
'No Dart Observatories found. Are you running a debug build?');
|
'No Dart Observatories found. Are you running a debug build?');
|
||||||
@ -261,7 +391,13 @@ class FuchsiaDevice extends Device {
|
|||||||
if (path == '') {
|
if (path == '') {
|
||||||
continue;
|
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')) {
|
for (String line in lsOutput.split('\n')) {
|
||||||
if (line == '') {
|
if (line == '') {
|
||||||
continue;
|
continue;
|
||||||
@ -276,20 +412,18 @@ class FuchsiaDevice extends Device {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Run `command` on the Fuchsia device shell.
|
/// Run `command` on the Fuchsia device shell.
|
||||||
Future<String> shell(String command) async {
|
Future<RunResult> shell(String command) async {
|
||||||
final RunResult result = await runAsync(<String>[
|
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',
|
'ssh',
|
||||||
'-F',
|
'-F',
|
||||||
fuchsiaArtifacts.sshConfig.absolute.path,
|
fuchsiaArtifacts.sshConfig.absolute.path,
|
||||||
id,
|
id,
|
||||||
command
|
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
|
/// Finds the first port running a VM matching `isolateName` from the
|
||||||
@ -332,7 +466,9 @@ class FuchsiaDevice extends Device {
|
|||||||
FuchsiaIsolateDiscoveryProtocol(this, isolateName);
|
FuchsiaIsolateDiscoveryProtocol(this, isolateName);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool isSupportedForProject(FlutterProject flutterProject) => true;
|
bool isSupportedForProject(FlutterProject flutterProject) {
|
||||||
|
return flutterProject.fuchsia.existsSync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FuchsiaIsolateDiscoveryProtocol {
|
class FuchsiaIsolateDiscoveryProtocol {
|
||||||
@ -431,7 +567,10 @@ class _FuchsiaPortForwarder extends DevicePortForwarder {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<int> forward(int devicePort, {int hostPort}) async {
|
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
|
// Note: the provided command works around a bug in -N, see US-515
|
||||||
// for more explanation.
|
// for more explanation.
|
||||||
final List<String> command = <String>[
|
final List<String> command = <String>[
|
||||||
@ -483,27 +622,4 @@ class _FuchsiaPortForwarder extends DevicePortForwarder {
|
|||||||
throwToolExit(result.stderr);
|
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 '../artifacts.dart';
|
||||||
import '../base/common.dart';
|
import '../base/common.dart';
|
||||||
import '../base/context.dart';
|
|
||||||
import '../base/file_system.dart';
|
import '../base/file_system.dart';
|
||||||
import '../base/io.dart';
|
import '../base/io.dart';
|
||||||
import '../base/logger.dart';
|
import '../base/logger.dart';
|
||||||
import '../base/process_manager.dart';
|
import '../base/process.dart';
|
||||||
import '../build_info.dart';
|
import '../build_info.dart';
|
||||||
import '../convert.dart';
|
import '../convert.dart';
|
||||||
import '../globals.dart';
|
import '../globals.dart';
|
||||||
@ -18,10 +17,8 @@ import '../project.dart';
|
|||||||
|
|
||||||
import 'fuchsia_sdk.dart';
|
import 'fuchsia_sdk.dart';
|
||||||
|
|
||||||
/// The [FuchsiaKernelCompiler] instance.
|
/// This is a simple wrapper around the custom kernel compiler from the Fuchsia
|
||||||
FuchsiaKernelCompiler get fuchsiaKernelCompiler =>
|
/// SDK.
|
||||||
context.get<FuchsiaKernelCompiler>();
|
|
||||||
|
|
||||||
class FuchsiaKernelCompiler {
|
class FuchsiaKernelCompiler {
|
||||||
/// Compiles the [fuchsiaProject] with entrypoint [target] to a collection of
|
/// Compiles the [fuchsiaProject] with entrypoint [target] to a collection of
|
||||||
/// .dilp files (consisting of the app split along package: boundaries, but
|
/// .dilp files (consisting of the app split along package: boundaries, but
|
||||||
@ -33,6 +30,9 @@ class FuchsiaKernelCompiler {
|
|||||||
BuildInfo buildInfo = BuildInfo.debug,
|
BuildInfo buildInfo = BuildInfo.debug,
|
||||||
}) async {
|
}) async {
|
||||||
// TODO(zra): Use filesystem root and scheme information from buildInfo.
|
// 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';
|
const String multiRootScheme = 'main-root';
|
||||||
final String packagesFile = fuchsiaProject.project.packagesFile.path;
|
final String packagesFile = fuchsiaProject.project.packagesFile.path;
|
||||||
final String outDir = getFuchsiaBuildDirectory();
|
final String outDir = getFuchsiaBuildDirectory();
|
||||||
@ -87,8 +87,7 @@ class FuchsiaKernelCompiler {
|
|||||||
artifacts.getArtifactPath(Artifact.engineDartBinary),
|
artifacts.getArtifactPath(Artifact.engineDartBinary),
|
||||||
fuchsiaArtifacts.kernelCompiler.path,
|
fuchsiaArtifacts.kernelCompiler.path,
|
||||||
]..addAll(flags);
|
]..addAll(flags);
|
||||||
printTrace("Running: '${command.join(' ')}'");
|
final Process process = await runCommand(command);
|
||||||
final Process process = await processManager.start(command);
|
|
||||||
final Status status = logger.startProgress(
|
final Status status = logger.startProgress(
|
||||||
'Building Fuchsia application...',
|
'Building Fuchsia application...',
|
||||||
timeout: null,
|
timeout: null,
|
||||||
|
@ -2,16 +2,15 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
import '../base/context.dart';
|
import '../base/common.dart';
|
||||||
|
import '../base/file_system.dart';
|
||||||
import '../base/io.dart';
|
import '../base/io.dart';
|
||||||
import '../base/process_manager.dart';
|
import '../base/process.dart';
|
||||||
|
import '../convert.dart';
|
||||||
import '../globals.dart';
|
import '../globals.dart';
|
||||||
|
|
||||||
import 'fuchsia_sdk.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.
|
/// This is a basic wrapper class for the Fuchsia SDK's `pm` tool.
|
||||||
class FuchsiaPM {
|
class FuchsiaPM {
|
||||||
/// Initializes the staging area at [buildPath] for creating the Fuchsia
|
/// 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
|
/// 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.
|
/// [appName] should probably be the name of the app from the pubspec file.
|
||||||
Future<bool> init(String buildPath, String appName) async {
|
Future<bool> init(String buildPath, String appName) {
|
||||||
final List<String> command = <String>[
|
return _runPMCommand(<String>[
|
||||||
fuchsiaArtifacts.pm.path,
|
|
||||||
'-o',
|
'-o',
|
||||||
buildPath,
|
buildPath,
|
||||||
'-n',
|
'-n',
|
||||||
appName,
|
appName,
|
||||||
'init',
|
'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.
|
/// Generates a new private key to be used to sign a Fuchsia package.
|
||||||
///
|
///
|
||||||
/// [buildPath] should be the same [buildPath] passed to [init].
|
/// [buildPath] should be the same [buildPath] passed to [init].
|
||||||
Future<bool> genkey(String buildPath, String outKeyPath) async {
|
Future<bool> genkey(String buildPath, String outKeyPath) {
|
||||||
final List<String> command = <String>[
|
return _runPMCommand(<String>[
|
||||||
fuchsiaArtifacts.pm.path,
|
|
||||||
'-o',
|
'-o',
|
||||||
buildPath,
|
buildPath,
|
||||||
'-k',
|
'-k',
|
||||||
outKeyPath,
|
outKeyPath,
|
||||||
'genkey',
|
'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.
|
/// 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
|
/// where $APPNAME is the same [appName] passed to [init], and meta/package
|
||||||
/// is set up to be the file `meta/package` created by [init].
|
/// is set up to be the file `meta/package` created by [init].
|
||||||
Future<bool> build(
|
Future<bool> build(
|
||||||
String buildPath, String keyPath, String manifestPath) async {
|
String buildPath, String keyPath, String manifestPath) {
|
||||||
final List<String> command = <String>[
|
return _runPMCommand(<String>[
|
||||||
fuchsiaArtifacts.pm.path,
|
|
||||||
'-o',
|
'-o',
|
||||||
buildPath,
|
buildPath,
|
||||||
'-k',
|
'-k',
|
||||||
@ -90,16 +68,7 @@ class FuchsiaPM {
|
|||||||
'-m',
|
'-m',
|
||||||
manifestPath,
|
manifestPath,
|
||||||
'build',
|
'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.
|
/// 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]
|
/// [buildPath] should be the same path passed to [init], and [manfiestPath]
|
||||||
/// should be the same manifest passed to [build].
|
/// should be the same manifest passed to [build].
|
||||||
Future<bool> archive(
|
Future<bool> archive(String buildPath, String keyPath, String manifestPath) {
|
||||||
String buildPath, String keyPath, String manifestPath) async {
|
return _runPMCommand(<String>[
|
||||||
final List<String> command = <String>[
|
|
||||||
fuchsiaArtifacts.pm.path,
|
|
||||||
'-o',
|
'-o',
|
||||||
buildPath,
|
buildPath,
|
||||||
'-k',
|
'-k',
|
||||||
@ -120,15 +87,155 @@ class FuchsiaPM {
|
|||||||
'-m',
|
'-m',
|
||||||
manifestPath,
|
manifestPath,
|
||||||
'archive',
|
'archive',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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');
|
||||||
|
}
|
||||||
|
final List<String> command = <String>[
|
||||||
|
fuchsiaArtifacts.pm.path,
|
||||||
|
'serve',
|
||||||
|
'-repo',
|
||||||
|
repoPath,
|
||||||
|
'-l',
|
||||||
|
'$host:$port',
|
||||||
];
|
];
|
||||||
printTrace("Running: '${command.join(' ')}'");
|
final Process process = await runCommand(command);
|
||||||
final ProcessResult result = await processManager.run(command);
|
process.stdout
|
||||||
if (result.exitCode != 0) {
|
.transform(utf8.decoder)
|
||||||
printError('Error archiving Fuchsia package: ');
|
.transform(const LineSplitter())
|
||||||
printError(result.stdout);
|
.listen(printTrace);
|
||||||
printError(result.stderr);
|
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;
|
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;
|
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/file_system.dart';
|
||||||
import '../base/io.dart';
|
import '../base/io.dart';
|
||||||
import '../base/platform.dart';
|
import '../base/platform.dart';
|
||||||
import '../base/process.dart';
|
|
||||||
import '../base/process_manager.dart';
|
import '../base/process_manager.dart';
|
||||||
import '../cache.dart';
|
import '../cache.dart';
|
||||||
import '../convert.dart';
|
import '../convert.dart';
|
||||||
import '../globals.dart';
|
import '../globals.dart';
|
||||||
|
|
||||||
|
import 'fuchsia_dev_finder.dart';
|
||||||
|
import 'fuchsia_kernel_compiler.dart';
|
||||||
|
import 'fuchsia_pm.dart';
|
||||||
|
|
||||||
/// The [FuchsiaSdk] instance.
|
/// The [FuchsiaSdk] instance.
|
||||||
FuchsiaSdk get fuchsiaSdk => context.get<FuchsiaSdk>();
|
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,
|
/// This workflow assumes development within the fuchsia source tree,
|
||||||
/// including a working fx command-line tool in the user's PATH.
|
/// including a working fx command-line tool in the user's PATH.
|
||||||
class FuchsiaSdk {
|
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:
|
/// Example output:
|
||||||
/// $ dev_finder list -full
|
/// $ dev_finder list -full
|
||||||
/// > 192.168.42.56 paper-pulp-bush-angel
|
/// > 192.168.42.56 paper-pulp-bush-angel
|
||||||
Future<String> listDevices() async {
|
Future<String> listDevices() async {
|
||||||
try {
|
if (fuchsiaArtifacts.devFinder == null) {
|
||||||
final String path = fuchsiaArtifacts.devFinder.absolute.path;
|
|
||||||
final RunResult process = await runAsync(<String>[path, 'list', '-full']);
|
|
||||||
return process.stdout.trim();
|
|
||||||
} catch (exception) {
|
|
||||||
printTrace('$exception');
|
|
||||||
}
|
|
||||||
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.
|
/// Returns the fuchsia system logs for an attached device.
|
||||||
///
|
|
||||||
/// Does not currently support multiple attached devices.
|
|
||||||
Stream<String> syslogs(String id) {
|
Stream<String> syslogs(String id) {
|
||||||
Process process;
|
Process process;
|
||||||
try {
|
try {
|
||||||
@ -50,6 +62,8 @@ class FuchsiaSdk {
|
|||||||
process.kill();
|
process.kill();
|
||||||
});
|
});
|
||||||
if (fuchsiaArtifacts.sshConfig == null) {
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
const String remoteCommand = 'log_listener --clock Local';
|
const String remoteCommand = 'log_listener --clock Local';
|
||||||
@ -101,6 +115,15 @@ class FuchsiaArtifacts {
|
|||||||
final String tools = fs.path.join(fuchsia, 'tools');
|
final String tools = fs.path.join(fuchsia, 'tools');
|
||||||
final String dartPrebuilts = fs.path.join(tools, 'dart_prebuilts');
|
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
|
// 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.
|
// 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.
|
// TODO(zra): Consider passing the ssh config path in with a flag.
|
||||||
@ -113,14 +136,11 @@ class FuchsiaArtifacts {
|
|||||||
}
|
}
|
||||||
return FuchsiaArtifacts(
|
return FuchsiaArtifacts(
|
||||||
sshConfig: sshConfig,
|
sshConfig: sshConfig,
|
||||||
devFinder: fs.file(fs.path.join(tools, 'dev_finder')),
|
devFinder: devFinder.existsSync() ? devFinder : null,
|
||||||
platformKernelDill: fs.file(fs.path.join(
|
platformKernelDill: platformDill.existsSync() ? platformDill : null,
|
||||||
dartPrebuilts, 'flutter_runner', 'platform_strong.dill')),
|
flutterPatchedSdk: patchedSdk.existsSync() ? patchedSdk : null,
|
||||||
flutterPatchedSdk: fs.file(fs.path.join(
|
kernelCompiler: kernelCompiler.existsSync() ? kernelCompiler : null,
|
||||||
dartPrebuilts, 'flutter_runner')),
|
pm: pm.existsSync() ? pm : null,
|
||||||
kernelCompiler: fs.file(fs.path.join(
|
|
||||||
dartPrebuilts, 'kernel_compiler.snapshot')),
|
|
||||||
pm: fs.file(fs.path.join(tools, 'pm')),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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() {
|
void startEchoingDeviceLog() {
|
||||||
if (_loggingSubscription != null)
|
if (_loggingSubscription != null) {
|
||||||
return;
|
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'))
|
if (!line.contains('Observatory listening on http'))
|
||||||
printStatus(line, wrap: false);
|
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/commands/build.dart';
|
||||||
import 'package:flutter_tools/src/fuchsia/fuchsia_kernel_compiler.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_pm.dart';
|
||||||
|
import 'package:flutter_tools/src/fuchsia/fuchsia_sdk.dart';
|
||||||
import 'package:flutter_tools/src/project.dart';
|
import 'package:flutter_tools/src/project.dart';
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mockito/mockito.dart';
|
||||||
@ -25,23 +26,28 @@ void main() {
|
|||||||
MemoryFileSystem memoryFileSystem;
|
MemoryFileSystem memoryFileSystem;
|
||||||
MockPlatform linuxPlatform;
|
MockPlatform linuxPlatform;
|
||||||
MockPlatform windowsPlatform;
|
MockPlatform windowsPlatform;
|
||||||
MockFuchsiaPM fuchsiaPM;
|
MockFuchsiaSdk fuchsiaSdk;
|
||||||
MockFuchsiaKernelCompiler fuchsiaKernelCompiler;
|
MockFuchsiaArtifacts fuchsiaArtifacts;
|
||||||
|
MockFuchsiaArtifacts fuchsiaArtifactsNoCompiler;
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
memoryFileSystem = MemoryFileSystem();
|
memoryFileSystem = MemoryFileSystem();
|
||||||
linuxPlatform = MockPlatform();
|
linuxPlatform = MockPlatform();
|
||||||
windowsPlatform = MockPlatform();
|
windowsPlatform = MockPlatform();
|
||||||
fuchsiaPM = MockFuchsiaPM();
|
fuchsiaSdk = MockFuchsiaSdk();
|
||||||
fuchsiaKernelCompiler = MockFuchsiaKernelCompiler();
|
fuchsiaArtifacts = MockFuchsiaArtifacts();
|
||||||
|
fuchsiaArtifactsNoCompiler = MockFuchsiaArtifacts();
|
||||||
|
|
||||||
when(linuxPlatform.isLinux).thenReturn(true);
|
when(linuxPlatform.isLinux).thenReturn(true);
|
||||||
when(windowsPlatform.isWindows).thenReturn(true);
|
when(windowsPlatform.isWindows).thenReturn(true);
|
||||||
when(windowsPlatform.isLinux).thenReturn(false);
|
when(windowsPlatform.isLinux).thenReturn(false);
|
||||||
when(windowsPlatform.isMacOS).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',
|
group('Fuchsia build fails gracefully when', () {
|
||||||
|
testUsingContext('there is no Fuchsia project',
|
||||||
() async {
|
() async {
|
||||||
final BuildCommand command = BuildCommand();
|
final BuildCommand command = BuildCommand();
|
||||||
applyMocksToCommand(command);
|
applyMocksToCommand(command);
|
||||||
@ -52,9 +58,10 @@ void main() {
|
|||||||
}, overrides: <Type, Generator>{
|
}, overrides: <Type, Generator>{
|
||||||
Platform: () => linuxPlatform,
|
Platform: () => linuxPlatform,
|
||||||
FileSystem: () => memoryFileSystem,
|
FileSystem: () => memoryFileSystem,
|
||||||
|
FuchsiaArtifacts: () => fuchsiaArtifacts,
|
||||||
});
|
});
|
||||||
|
|
||||||
testUsingContext('Fuchsia build fails when there is no cmx file', () async {
|
testUsingContext('there is no cmx file', () async {
|
||||||
final BuildCommand command = BuildCommand();
|
final BuildCommand command = BuildCommand();
|
||||||
applyMocksToCommand(command);
|
applyMocksToCommand(command);
|
||||||
fs.directory('fuchsia').createSync(recursive: true);
|
fs.directory('fuchsia').createSync(recursive: true);
|
||||||
@ -68,9 +75,10 @@ void main() {
|
|||||||
}, overrides: <Type, Generator>{
|
}, overrides: <Type, Generator>{
|
||||||
Platform: () => linuxPlatform,
|
Platform: () => linuxPlatform,
|
||||||
FileSystem: () => memoryFileSystem,
|
FileSystem: () => memoryFileSystem,
|
||||||
|
FuchsiaArtifacts: () => fuchsiaArtifacts,
|
||||||
});
|
});
|
||||||
|
|
||||||
testUsingContext('Fuchsia build fails on Windows platform', () async {
|
testUsingContext('on Windows platform', () async {
|
||||||
final BuildCommand command = BuildCommand();
|
final BuildCommand command = BuildCommand();
|
||||||
applyMocksToCommand(command);
|
applyMocksToCommand(command);
|
||||||
const String appName = 'app_name';
|
const String appName = 'app_name';
|
||||||
@ -88,6 +96,29 @@ void main() {
|
|||||||
}, overrides: <Type, Generator>{
|
}, overrides: <Type, Generator>{
|
||||||
Platform: () => windowsPlatform,
|
Platform: () => windowsPlatform,
|
||||||
FileSystem: () => memoryFileSystem,
|
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 {
|
testUsingContext('Fuchsia build parts fit together right', () async {
|
||||||
@ -110,8 +141,7 @@ void main() {
|
|||||||
}, overrides: <Type, Generator>{
|
}, overrides: <Type, Generator>{
|
||||||
Platform: () => linuxPlatform,
|
Platform: () => linuxPlatform,
|
||||||
FileSystem: () => memoryFileSystem,
|
FileSystem: () => memoryFileSystem,
|
||||||
FuchsiaPM: () => fuchsiaPM,
|
FuchsiaSdk: () => fuchsiaSdk,
|
||||||
FuchsiaKernelCompiler: () => fuchsiaKernelCompiler,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,3 +219,16 @@ class MockFuchsiaKernelCompiler extends Mock implements FuchsiaKernelCompiler {
|
|||||||
fs.file(manifestPath).createSync(recursive: true);
|
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:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter_tools/src/base/logger.dart';
|
import 'package:file/memory.dart';
|
||||||
import 'package:flutter_tools/src/vmservice.dart';
|
import 'package:flutter_tools/src/application_package.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
|
||||||
import 'package:process/process.dart';
|
|
||||||
|
|
||||||
import 'package:flutter_tools/src/artifacts.dart';
|
import 'package:flutter_tools/src/artifacts.dart';
|
||||||
import 'package:flutter_tools/src/base/common.dart';
|
import 'package:flutter_tools/src/base/common.dart';
|
||||||
import 'package:flutter_tools/src/base/context.dart';
|
import 'package:flutter_tools/src/base/context.dart';
|
||||||
import 'package:flutter_tools/src/base/file_system.dart';
|
import 'package:flutter_tools/src/base/file_system.dart';
|
||||||
import 'package:flutter_tools/src/base/io.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/base/time.dart';
|
||||||
|
import 'package:flutter_tools/src/build_info.dart';
|
||||||
import 'package:flutter_tools/src/device.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_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/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/common.dart';
|
||||||
import '../src/context.dart';
|
import '../src/context.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('fuchsia device', () {
|
group('fuchsia device', () {
|
||||||
|
MemoryFileSystem memoryFileSystem;
|
||||||
|
setUp(() {
|
||||||
|
memoryFileSystem = MemoryFileSystem();
|
||||||
|
});
|
||||||
|
|
||||||
testUsingContext('stores the requested id and name', () {
|
testUsingContext('stores the requested id and name', () {
|
||||||
const String deviceId = 'e80::0000:a00a:f00f:2002/3';
|
const String deviceId = 'e80::0000:a00a:f00f:2002/3';
|
||||||
const String name = 'halfbaked';
|
const String name = 'halfbaked';
|
||||||
@ -49,14 +65,34 @@ void main() {
|
|||||||
expect(names.length, 0);
|
expect(names.length, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('default capabilities', () async {
|
testUsingContext('default capabilities', () async {
|
||||||
final FuchsiaDevice device = FuchsiaDevice('123');
|
final FuchsiaDevice device = FuchsiaDevice('123');
|
||||||
|
fs.directory('fuchsia').createSync(recursive: true);
|
||||||
|
fs.file('pubspec.yaml').createSync();
|
||||||
|
|
||||||
expect(device.supportsHotReload, true);
|
expect(device.supportsHotReload, true);
|
||||||
expect(device.supportsHotRestart, false);
|
expect(device.supportsHotRestart, false);
|
||||||
expect(device.supportsStopApp, false);
|
expect(device.supportsStopApp, false);
|
||||||
expect(device.isSupportedForProject(null), true);
|
expect(device.isSupportedForProject(FlutterProject.current()), true);
|
||||||
expect(await device.stopApp(null), false);
|
}, 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 {
|
testUsingContext('can be parsed for an app', () async {
|
||||||
final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester');
|
final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester');
|
||||||
final DeviceLogReader reader = device.getLogReader(
|
final DeviceLogReader reader = device.getLogReader(
|
||||||
app: FuchsiaModulePackage(name: 'example_app.cmx'));
|
app: FuchsiaModulePackage(name: 'example_app'));
|
||||||
final List<String> logLines = <String>[];
|
final List<String> logLines = <String>[];
|
||||||
final Completer<void> lock = Completer<void>();
|
final Completer<void> lock = Completer<void>();
|
||||||
reader.logLines.listen((String line) {
|
reader.logLines.listen((String line) {
|
||||||
@ -244,7 +280,7 @@ void main() {
|
|||||||
testUsingContext('cuts off prior logs', () async {
|
testUsingContext('cuts off prior logs', () async {
|
||||||
final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester');
|
final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester');
|
||||||
final DeviceLogReader reader = device.getLogReader(
|
final DeviceLogReader reader = device.getLogReader(
|
||||||
app: FuchsiaModulePackage(name: 'example_app.cmx'));
|
app: FuchsiaModulePackage(name: 'example_app'));
|
||||||
final List<String> logLines = <String>[];
|
final List<String> logLines = <String>[];
|
||||||
final Completer<void> lock = Completer<void>();
|
final Completer<void> lock = Completer<void>();
|
||||||
reader.logLines.listen((String line) {
|
reader.logLines.listen((String line) {
|
||||||
@ -367,16 +403,119 @@ void main() {
|
|||||||
Logger: () => StdoutLogger(),
|
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 MockProcessManager extends Mock implements ProcessManager {}
|
||||||
|
|
||||||
class MockProcessResult extends Mock implements ProcessResult {}
|
class MockProcessResult extends Mock implements ProcessResult {}
|
||||||
|
|
||||||
|
class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils {}
|
||||||
|
|
||||||
class MockFile extends Mock implements File {}
|
class MockFile extends Mock implements File {}
|
||||||
|
|
||||||
class MockProcess extends Mock implements Process {}
|
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 {
|
class MockFuchsiaDevice extends Mock implements FuchsiaDevice {
|
||||||
MockFuchsiaDevice(this.id, this.portForwarder, this.ipv6);
|
MockFuchsiaDevice(this.id, this.portForwarder, this.ipv6);
|
||||||
|
|
||||||
@ -419,3 +558,192 @@ class MockIsolate extends Mock implements Isolate {
|
|||||||
@override
|
@override
|
||||||
final String name;
|
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