Unverified Commit dc4541fa authored by Danny Tuppeny's avatar Danny Tuppeny Committed by GitHub

[flutter_tools] Add support for vmServiceFileInfo when attaching (#128503)

When building the new SDK DAPs, this functionality was missed from the Flutter adapter (but added to the Dart CLI adapter).

As well as passing a VM Service URI directly, we support passing a file that can be polled for it.

This uses the same mechanism we use to obtain the VM Service URI from a Dart debug session (we run `dart --write-service-info=foo.json my_file.dart` and then poll that file which the VM will write) and is useful for users that have their own mechanism for launching an app (for example using custom Flutter embedders - see https://github.com/Dart-Code/Dart-Code/issues/3353) to provide a VM Service URI once the app is up and running.

Fixes https://github.com/Dart-Code/Dart-Code/issues/4577.
parent 7214cb28
...@@ -40,7 +40,10 @@ Arguments specific to `launchRequest` are: ...@@ -40,7 +40,10 @@ Arguments specific to `launchRequest` are:
Arguments specific to `attachRequest` are: Arguments specific to `attachRequest` are:
- `String? vmServiceUri` - the VM Service URI to attach to (if not supplied, Flutter will try to discover it from the device) - `String? vmServiceInfoFile` - the file to read the VM Service info from \*
- `String? vmServiceUri` - the VM Service URI to attach to \*
\* Only one of `vmServiceInfoFile` or `vmServiceUri` may be supplied. If neither are supplied, Flutter will try to discover it from the device.
## Custom Requests ## Custom Requests
......
...@@ -11,11 +11,12 @@ import 'package:vm_service/vm_service.dart' as vm; ...@@ -11,11 +11,12 @@ import 'package:vm_service/vm_service.dart' as vm;
import '../base/io.dart'; import '../base/io.dart';
import '../cache.dart'; import '../cache.dart';
import '../convert.dart'; import '../convert.dart';
import '../globals.dart' as globals show fs;
import 'flutter_adapter_args.dart'; import 'flutter_adapter_args.dart';
import 'flutter_base_adapter.dart'; import 'flutter_base_adapter.dart';
/// A DAP Debug Adapter for running and debugging Flutter applications. /// A DAP Debug Adapter for running and debugging Flutter applications.
class FlutterDebugAdapter extends FlutterBaseDebugAdapter { class FlutterDebugAdapter extends FlutterBaseDebugAdapter with VmServiceInfoFileUtils {
FlutterDebugAdapter( FlutterDebugAdapter(
super.channel, { super.channel, {
required super.fileSystem, required super.fileSystem,
...@@ -120,6 +121,16 @@ class FlutterDebugAdapter extends FlutterBaseDebugAdapter { ...@@ -120,6 +121,16 @@ class FlutterDebugAdapter extends FlutterBaseDebugAdapter {
@override @override
Future<void> attachImpl() async { Future<void> attachImpl() async {
final FlutterAttachRequestArguments args = this.args as FlutterAttachRequestArguments; final FlutterAttachRequestArguments args = this.args as FlutterAttachRequestArguments;
String? vmServiceUri = args.vmServiceUri;
final String? vmServiceInfoFile = args.vmServiceInfoFile;
if (vmServiceUri != null && vmServiceInfoFile != null) {
sendConsoleOutput(
'To attach, provide only one (or neither) of vmServiceUri/vmServiceInfoFile',
);
handleSessionTerminate();
return;
}
launchProgress = startProgressNotification( launchProgress = startProgressNotification(
'launch', 'launch',
...@@ -127,7 +138,11 @@ class FlutterDebugAdapter extends FlutterBaseDebugAdapter { ...@@ -127,7 +138,11 @@ class FlutterDebugAdapter extends FlutterBaseDebugAdapter {
message: 'Attaching…', message: 'Attaching…',
); );
final String? vmServiceUri = args.vmServiceUri; if (vmServiceUri == null && vmServiceInfoFile != null) {
final Uri uriFromFile = await waitForVmServiceInfoFile(logger, globals.fs.file(vmServiceInfoFile));
vmServiceUri = uriFromFile.toString();
}
final List<String> toolArgs = <String>[ final List<String> toolArgs = <String>[
'attach', 'attach',
'--machine', '--machine',
......
...@@ -17,6 +17,7 @@ class FlutterAttachRequestArguments ...@@ -17,6 +17,7 @@ class FlutterAttachRequestArguments
this.customTool, this.customTool,
this.customToolReplacesArgs, this.customToolReplacesArgs,
this.vmServiceUri, this.vmServiceUri,
this.vmServiceInfoFile,
this.program, this.program,
super.restart, super.restart,
super.name, super.name,
...@@ -36,6 +37,7 @@ class FlutterAttachRequestArguments ...@@ -36,6 +37,7 @@ class FlutterAttachRequestArguments
customTool = obj['customTool'] as String?, customTool = obj['customTool'] as String?,
customToolReplacesArgs = obj['customToolReplacesArgs'] as int?, customToolReplacesArgs = obj['customToolReplacesArgs'] as int?,
vmServiceUri = obj['vmServiceUri'] as String?, vmServiceUri = obj['vmServiceUri'] as String?,
vmServiceInfoFile = obj['vmServiceInfoFile'] as String?,
program = obj['program'] as String?, program = obj['program'] as String?,
super.fromMap(); super.fromMap();
...@@ -64,8 +66,15 @@ class FlutterAttachRequestArguments ...@@ -64,8 +66,15 @@ class FlutterAttachRequestArguments
final int? customToolReplacesArgs; final int? customToolReplacesArgs;
/// The VM Service URI of the running Flutter app to connect to. /// The VM Service URI of the running Flutter app to connect to.
///
/// Only one of this or [vmServiceInfoFile] (or neither) can be supplied.
final String? vmServiceUri; final String? vmServiceUri;
/// The VM Service info file to extract the VM Service URI from to attach to.
///
/// Only one of this or [vmServiceUri] (or neither) can be supplied.
final String? vmServiceInfoFile;
/// The program/Flutter app to be run. /// The program/Flutter app to be run.
final String? program; final String? program;
......
...@@ -11,7 +11,7 @@ import 'package:flutter_tools/src/base/platform.dart'; ...@@ -11,7 +11,7 @@ import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/debug_adapters/flutter_adapter.dart'; import 'package:flutter_tools/src/debug_adapters/flutter_adapter.dart';
import 'package:flutter_tools/src/debug_adapters/flutter_adapter_args.dart'; import 'package:flutter_tools/src/debug_adapters/flutter_adapter_args.dart';
import 'package:flutter_tools/src/globals.dart' as globals show platform; import 'package:flutter_tools/src/globals.dart' as globals show fs, platform;
import 'package:test/fake.dart'; import 'package:test/fake.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'package:vm_service/vm_service.dart'; import 'package:vm_service/vm_service.dart';
...@@ -299,6 +299,107 @@ void main() { ...@@ -299,6 +299,107 @@ void main() {
])); ]));
}); });
test('runs "flutter attach" with --debug-uri if vmServiceUri is passed', () async {
final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter(
fileSystem: MemoryFileSystem.test(style: fsStyle),
platform: platform,
);
final Completer<void> responseCompleter = Completer<void>();
final FlutterAttachRequestArguments args =
FlutterAttachRequestArguments(
cwd: '/project',
program: 'program/main.dart',
vmServiceUri: 'ws://1.2.3.4/ws'
);
await adapter.configurationDoneRequest(MockRequest(), null, () {});
await adapter.attachRequest(
MockRequest(), args, responseCompleter.complete);
await responseCompleter.future;
expect(
adapter.processArgs,
containsAllInOrder(<String>[
'attach',
'--machine',
'--debug-uri',
'ws://1.2.3.4/ws',
'--target',
'program/main.dart',
]));
});
test('runs "flutter attach" with --debug-uri if vmServiceInfoFile exists', () async {
final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter(
fileSystem: MemoryFileSystem.test(style: fsStyle),
platform: platform,
);
final Completer<void> responseCompleter = Completer<void>();
final File serviceInfoFile = globals.fs.systemTempDirectory.createTempSync('dap_flutter_attach_vmServiceInfoFile').childFile('vmServiceInfo.json');
final FlutterAttachRequestArguments args =
FlutterAttachRequestArguments(
cwd: '/project',
program: 'program/main.dart',
vmServiceInfoFile: serviceInfoFile.path,
);
// Write the service info file before trying to attach:
serviceInfoFile.writeAsStringSync('{ "uri": "ws://1.2.3.4/ws" }');
await adapter.configurationDoneRequest(MockRequest(), null, () {});
await adapter.attachRequest(MockRequest(), args, responseCompleter.complete);
await responseCompleter.future;
expect(
adapter.processArgs,
containsAllInOrder(<String>[
'attach',
'--machine',
'--debug-uri',
'ws://1.2.3.4/ws',
'--target',
'program/main.dart',
]));
});
test('runs "flutter attach" with --debug-uri if vmServiceInfoFile is created later', () async {
final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter(
fileSystem: MemoryFileSystem.test(style: fsStyle),
platform: platform,
);
final Completer<void> responseCompleter = Completer<void>();
final File serviceInfoFile = globals.fs.systemTempDirectory.createTempSync('dap_flutter_attach_vmServiceInfoFile').childFile('vmServiceInfo.json');
final FlutterAttachRequestArguments args =
FlutterAttachRequestArguments(
cwd: '/project',
program: 'program/main.dart',
vmServiceInfoFile: serviceInfoFile.path,
);
await adapter.configurationDoneRequest(MockRequest(), null, () {});
final Future<void> attachResponseFuture = adapter.attachRequest(MockRequest(), args, responseCompleter.complete);
// Write the service info file a little later to ensure we detect it:
await pumpEventQueue(times:5000);
serviceInfoFile.writeAsStringSync('{ "uri": "ws://1.2.3.4/ws" }');
await attachResponseFuture;
await responseCompleter.future;
expect(
adapter.processArgs,
containsAllInOrder(<String>[
'attach',
'--machine',
'--debug-uri',
'ws://1.2.3.4/ws',
'--target',
'program/main.dart',
]));
});
test('does not record the VMs PID for terminating', () async { test('does not record the VMs PID for terminating', () async {
final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter( final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter(
fileSystem: MemoryFileSystem.test(style: fsStyle), fileSystem: MemoryFileSystem.test(style: fsStyle),
......
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