Attach command: add Bazel filesystem support (#21082)
diff --git a/packages/flutter_tools/lib/src/commands/attach.dart b/packages/flutter_tools/lib/src/commands/attach.dart
index 2812b4c..66ea962 100644
--- a/packages/flutter_tools/lib/src/commands/attach.dart
+++ b/packages/flutter_tools/lib/src/commands/attach.dart
@@ -37,12 +37,17 @@
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 @@
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 @@
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 @@
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 @@
stayResident: stayResident,
ipv6: ipv6,
);
-}
\ No newline at end of file
+}
diff --git a/packages/flutter_tools/lib/src/commands/build_bundle.dart b/packages/flutter_tools/lib/src/commands/build_bundle.dart
index 24c162e..5f34c98 100644
--- a/packages/flutter_tools/lib/src/commands/build_bundle.dart
+++ b/packages/flutter_tools/lib/src/commands/build_bundle.dart
@@ -13,6 +13,7 @@
class BuildBundleCommand extends BuildSubCommand {
BuildBundleCommand({bool verboseHelp = false}) {
usesTargetOption();
+ usesFilesystemOptions(hide: !verboseHelp);
addBuildModeFlags();
argParser
..addFlag('precompiled', negatable: false)
@@ -58,18 +59,7 @@
..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();
}
diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart
index 359fd54..2fbeeab 100644
--- a/packages/flutter_tools/lib/src/commands/run.dart
+++ b/packages/flutter_tools/lib/src/commands/run.dart
@@ -80,6 +80,7 @@
RunCommand({ bool verboseHelp = false }) : super(verboseHelp: verboseHelp) {
requiresPubspecYaml();
+ usesFilesystemOptions(hide: !verboseHelp);
argParser
..addFlag('start-paused',
@@ -171,23 +172,8 @@
'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;
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart
index 255756d..e9d901e 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart
@@ -122,6 +122,31 @@
_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'
diff --git a/packages/flutter_tools/test/commands/attach_test.dart b/packages/flutter_tools/test/commands/attach_test.dart
index 475b2d0..8326f2b 100644
--- a/packages/flutter_tools/test/commands/attach_test.dart
+++ b/packages/flutter_tools/test/commands/attach_test.dart
@@ -11,6 +11,7 @@
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 @@
.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();
- mockLogReader.dispose();
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- },
- );
+ 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,
+ });
+ });
+
testUsingContext('selects specified target', () async {
const int devicePort = 499;
@@ -102,6 +187,9 @@
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(