Unverified Commit 77095356 authored by David Shuckerow's avatar David Shuckerow Committed by GitHub

Attach command: add Bazel filesystem support (#21082)

parent 5e39476a
......@@ -37,12 +37,17 @@ final String ipv4Loopback = InternetAddress.loopbackIPv4.address;
class AttachCommand extends FlutterCommand {
AttachCommand({bool verboseHelp = false, this.hotRunnerFactory}) {
addBuildModeFlags(defaultToRelease: false);
usesTargetOption();
usesFilesystemOptions(hide: !verboseHelp);
argParser
..addOption(
'debug-port',
help: 'Local port where the observatory is listening.',
)
..addFlag(
)..addOption(
'project-root',
hide: !verboseHelp,
help: 'Normally used only in run target',
)..addFlag(
'preview-dart-2',
defaultsTo: true,
hide: !verboseHelp,
......@@ -53,7 +58,6 @@ class AttachCommand extends FlutterCommand {
help: 'Handle machine structured JSON command input and provide output\n'
'and progress in machine friendly format.',
);
usesTargetOption();
hotRunnerFactory ??= new HotRunnerFactory();
}
......@@ -117,8 +121,14 @@ class AttachCommand extends FlutterCommand {
observatoryUri = Uri.parse('http://$ipv4Loopback:$localPort/');
}
try {
final FlutterDevice flutterDevice = new FlutterDevice(device,
trackWidgetCreation: false, previewDart2: argResults['preview-dart-2']);
final FlutterDevice flutterDevice = new FlutterDevice(
device,
trackWidgetCreation: false,
previewDart2: argResults['preview-dart-2'],
dillOutputPath: argResults['output-dill'],
fileSystemRoots: argResults['filesystem-root'],
fileSystemScheme: argResults['filesystem-scheme'],
);
flutterDevice.observatoryUris = <Uri>[ observatoryUri ];
final HotRunner hotRunner = hotRunnerFactory.build(
<FlutterDevice>[flutterDevice],
......@@ -126,6 +136,8 @@ class AttachCommand extends FlutterCommand {
debuggingOptions: new DebuggingOptions.enabled(getBuildInfo()),
packagesFilePath: globalResults['packages'],
usesTerminalUI: daemon == null,
projectRootPath: argResults['project-root'],
dillOutputPath: argResults['output-dill'],
);
if (daemon != null) {
......@@ -178,4 +190,4 @@ class HotRunnerFactory {
stayResident: stayResident,
ipv6: ipv6,
);
}
\ No newline at end of file
}
......@@ -13,6 +13,7 @@ import 'build.dart';
class BuildBundleCommand extends BuildSubCommand {
BuildBundleCommand({bool verboseHelp = false}) {
usesTargetOption();
usesFilesystemOptions(hide: !verboseHelp);
addBuildModeFlags();
argParser
..addFlag('precompiled', negatable: false)
......@@ -58,18 +59,7 @@ class BuildBundleCommand extends BuildSubCommand {
..addFlag('report-licensed-packages',
help: 'Whether to report the names of all the packages that are included '
'in the application\'s LICENSE file.',
defaultsTo: false)
..addMultiOption('filesystem-root',
hide: !verboseHelp,
help: 'Specify the path, that is used as root in a virtual file system\n'
'for compilation. Input file name should be specified as Uri in\n'
'filesystem-scheme scheme. Use only in Dart 2 mode.\n'
'Requires --output-dill option to be explicitly specified.\n')
..addOption('filesystem-scheme',
defaultsTo: 'org-dartlang-root',
hide: !verboseHelp,
help: 'Specify the scheme that is used for virtual file system used in\n'
'compilation. See more details on filesystem-root option.\n');
defaultsTo: false);
usesPubOption();
}
......
......@@ -80,6 +80,7 @@ class RunCommand extends RunCommandBase {
RunCommand({ bool verboseHelp = false }) : super(verboseHelp: verboseHelp) {
requiresPubspecYaml();
usesFilesystemOptions(hide: !verboseHelp);
argParser
..addFlag('start-paused',
......@@ -171,23 +172,8 @@ class RunCommand extends RunCommandBase {
'results out to "refresh_benchmark.json", and exit. This flag is\n'
'intended for use in generating automated flutter benchmarks.',
)
..addOption('output-dill',
hide: !verboseHelp,
help: 'Specify the path to frontend server output kernel file.',
)
..addOption(FlutterOptions.kExtraFrontEndOptions, hide: true)
..addOption(FlutterOptions.kExtraGenSnapshotOptions, hide: true)
..addMultiOption('filesystem-root',
hide: !verboseHelp,
help: 'Specify the path, that is used as root in a virtual file system\n'
'for compilation. Input file name should be specified as Uri in\n'
'filesystem-scheme scheme. Use only in Dart 2 mode.\n'
'Requires --output-dill option to be explicitly specified.\n')
..addOption('filesystem-scheme',
defaultsTo: 'org-dartlang-root',
hide: !verboseHelp,
help: 'Specify the scheme that is used for virtual file system used in\n'
'compilation. See more details on filesystem-root option.\n');
..addOption(FlutterOptions.kExtraGenSnapshotOptions, hide: true);
}
List<Device> devices;
......
......@@ -122,6 +122,31 @@ abstract class FlutterCommand extends Command<Null> {
_usesPubOption = true;
}
/// Adds flags for using a specific filesystem root and scheme.
///
/// [hide] indicates whether or not to hide these options when the user asks
/// for help.
void usesFilesystemOptions({@required bool hide}) {
argParser
..addOption('output-dill',
hide: hide,
help: 'Specify the path to frontend server output kernel file.',
)
..addMultiOption(FlutterOptions.kFileSystemRoot,
hide: hide,
help: 'Specify the path, that is used as root in a virtual file system\n'
'for compilation. Input file name should be specified as Uri in\n'
'filesystem-scheme scheme. Use only in Dart 2 mode.\n'
'Requires --output-dill option to be explicitly specified.\n',
)
..addOption(FlutterOptions.kFileSystemScheme,
defaultsTo: 'org-dartlang-root',
hide: hide,
help: 'Specify the scheme that is used for virtual file system used in\n'
'compilation. See more details on filesystem-root option.\n',
);
}
void usesBuildNumberOption() {
argParser.addOption('build-number',
help: 'An integer used as an internal version number.\n'
......
......@@ -11,6 +11,7 @@ import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/attach.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/resident_runner.dart';
import 'package:flutter_tools/src/run_hot.dart';
import 'package:mockito/mockito.dart';
......@@ -25,48 +26,132 @@ void main() {
.posix,
);
setUpAll(() {
setUp(() {
Cache.disableLocking();
testFileSystem.directory('lib').createSync();
testFileSystem.file('lib/main.dart').createSync();
});
testUsingContext('finds observatory port and forwards', () async {
group('with one device and no specified target file', () {
const int devicePort = 499;
const int hostPort = 42;
final MockDeviceLogReader mockLogReader = new MockDeviceLogReader();
final MockPortForwarder portForwarder = new MockPortForwarder();
final MockAndroidDevice device = new MockAndroidDevice();
when(device.getLogReader()).thenAnswer((_) {
// Now that the reader is used, start writing messages to it.
Timer.run(() {
mockLogReader.addLine('Foo');
mockLogReader.addLine(
'Observatory listening on http://127.0.0.1:$devicePort');
MockDeviceLogReader mockLogReader;
MockPortForwarder portForwarder;
MockAndroidDevice device;
setUp(() {
mockLogReader = new MockDeviceLogReader();
portForwarder = new MockPortForwarder();
device = new MockAndroidDevice();
when(device.getLogReader()).thenAnswer((_) {
// Now that the reader is used, start writing messages to it.
Timer.run(() {
mockLogReader.addLine('Foo');
mockLogReader.addLine(
'Observatory listening on http://127.0.0.1:$devicePort');
});
return mockLogReader;
});
when(device.portForwarder).thenReturn(portForwarder);
when(portForwarder.forward(devicePort, hostPort: anyNamed('hostPort')))
.thenAnswer((_) async => hostPort);
when(portForwarder.forwardedPorts).thenReturn(
<ForwardedPort>[new ForwardedPort(hostPort, devicePort)]);
when(portForwarder.unforward(any)).thenAnswer((_) async => null);
return mockLogReader;
// We cannot add the device to a device manager because that is
// only enabled by the context of each testUsingContext call.
//
// Instead each test will add the device to the device manager
// on its own.
});
when(device.portForwarder).thenReturn(portForwarder);
when(portForwarder.forward(devicePort, hostPort: anyNamed('hostPort')))
.thenAnswer((_) async => hostPort);
when(portForwarder.forwardedPorts).thenReturn(
<ForwardedPort>[new ForwardedPort(hostPort, devicePort)]);
when(portForwarder.unforward(any)).thenAnswer((_) async => null);
testDeviceManager.addDevice(device);
final AttachCommand command = new AttachCommand();
tearDown(() {
mockLogReader.dispose();
});
await createTestCommandRunner(command).run(<String>['attach']);
testUsingContext('finds observatory port and forwards', () async {
testDeviceManager.addDevice(device);
verify(portForwarder.forward(devicePort, hostPort: anyNamed('hostPort')))
.called(1);
final AttachCommand command = new AttachCommand();
await createTestCommandRunner(command).run(<String>['attach']);
verify(
portForwarder.forward(devicePort, hostPort: anyNamed('hostPort')),
).called(1);
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
});
testUsingContext('accepts filesystem parameters', () async {
testDeviceManager.addDevice(device);
const String filesystemScheme = 'foo';
const String filesystemRoot = '/build-output/';
const String projectRoot = '/build-output/project-root';
const String outputDill = '/tmp/output.dill';
final MockHotRunnerFactory mockHotRunnerFactory = new MockHotRunnerFactory();
when(
mockHotRunnerFactory.build(
any,
target: anyNamed('target'),
projectRootPath: anyNamed('projectRootPath'),
dillOutputPath: anyNamed('dillOutputPath'),
debuggingOptions: anyNamed('debuggingOptions'),
packagesFilePath: anyNamed('packagesFilePath'),
usesTerminalUI: anyNamed('usesTerminalUI'),
),
)..thenReturn(new MockHotRunner());
final AttachCommand command = new AttachCommand(
hotRunnerFactory: mockHotRunnerFactory,
);
await createTestCommandRunner(command).run(<String>[
'attach',
'--filesystem-scheme',
filesystemScheme,
'--filesystem-root',
filesystemRoot,
'--project-root',
projectRoot,
'--output-dill',
outputDill,
'-v',
]);
// Validate the attach call built a mock runner with the right
// project root and output dill.
final VerificationResult verificationResult = verify(
mockHotRunnerFactory.build(
captureAny,
target: anyNamed('target'),
projectRootPath: projectRoot,
dillOutputPath: outputDill,
debuggingOptions: anyNamed('debuggingOptions'),
packagesFilePath: anyNamed('packagesFilePath'),
usesTerminalUI: anyNamed('usesTerminalUI'),
),
)..called(1);
final List<FlutterDevice> flutterDevices = verificationResult.captured.first;
expect(flutterDevices, hasLength(1));
// Validate that the attach call built a flutter device with the right
// output dill, filesystem scheme, and filesystem root.
final FlutterDevice flutterDevice = flutterDevices.first;
expect(flutterDevice.dillOutputPath, outputDill);
expect(flutterDevice.fileSystemScheme, filesystemScheme);
expect(flutterDevice.fileSystemRoots, const <String>[filesystemRoot]);
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
});
});
mockLogReader.dispose();
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
},
);
testUsingContext('selects specified target', () async {
const int devicePort = 499;
......@@ -102,6 +187,9 @@ void main() {
final File foo = fs.file('lib/foo.dart')
..createSync();
// Delete the main.dart file to be sure that attach works without it.
fs.file('lib/main.dart').deleteSync();
final AttachCommand command = new AttachCommand(
hotRunnerFactory: mockHotRunnerFactory);
await createTestCommandRunner(command).run(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment