Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions packages/flutter_tools/lib/src/debug_adapters/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,20 @@ Arguments common to both `launchRequest` and `attachRequest` are:
- `bool? evaluateToStringInDebugViews` - whether to invoke `toString()` in expression evaluation requests (inc. hovers/watch windows) (if not supplied, defaults to `false`)
- `bool? sendLogsToClient` - used to proxy all VM Service traffic back to the client in custom `dart.log` events (has performance implications, intended for troubleshooting) (if not supplied, defaults to `false`)
- `List<String>? additionalProjectPaths` - paths of any projects (outside of `cwd`) that are open in the users workspace
- `String? cwd` - the working directory for the Dart process to be spawned in
- `String? cwd` - the working directory for the Flutter process to be spawned in
- `List<String>? toolArgs` - arguments for the `flutter run`, `flutter attach` or `flutter test` commands
- `String? customTool` - an optional tool to run instead of `flutter` - the custom tool must be completely compatible with the tool/command it is replacing
- `int? customToolReplacesArgs` - the number of arguments to delete from the beginning of the argument list when invoking `customTool` - e.g. setting `customTool` to `flutter_test_wrapper` and `customToolReplacesArgs` to `1` for a test run would invoke `flutter_test_wrapper foo_test.dart` instead of `flutter test foo_test.dart` (if larger than the number of computed arguments all arguments will be removed, if not supplied will default to `0`)

Arguments specific to `launchRequest` are:

- `bool? noDebug` - whether to run in debug or noDebug mode (if not supplied, defaults to debug)
- `String program` - the path of the Flutter application to run
- `List<String>? args` - arguments to be passed to the Flutter program
- `List<String>? toolArgs` - arguments for the `flutter run` or `flutter test` commands
- `String? customTool` - an optional tool to run instead of `flutter` - the custom tool must be completely compatible with the tool/command it is replacing
- `int? customToolReplacesArgs` - the number of arguments to delete from the beginning of the argument list when invoking `customTool` - e.g. setting `customTool` to `flutter_test_wrapper` and `customToolReplacesArgs` to `1` for a test run would invoke `flutter_test_wrapper foo_test.dart` instead of `flutter test foo_test.dart` (if larger than the number of computed arguments all arguments will be removed, if not supplied will default to `0`)

`attachRequest` is not currently supported, but will be documented here when it is.
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)

## Custom Requests

Expand Down
111 changes: 79 additions & 32 deletions packages/flutter_tools/lib/src/debug_adapters/flutter_adapter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,51 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments
@override
bool get terminateOnVmServiceClose => false;

/// Whether or not the user requested debugging be enabled.
///
/// debug/noDebug here refers to the DAP "debug" mode and not the Flutter
/// debug mode (vs Profile/Release). It is provided by the client editor based
/// on whether a user chooses to "Run" or "Debug" their app.
///
/// This is always enabled for attach requests, but can be disabled for launch
/// requests via DAP's `noDebug` flag. If `noDebug` is not provided, will
/// default to debugging.
///
/// When not debugging, we will not connect to the VM Service so some
/// functionality (breakpoints, evaluation, etc.) will not be available.
/// Functionality provided via the daemon (hot reload/restart) will still be
/// available.
bool get debug {
final DartCommonLaunchAttachRequestArguments args = this.args;
if (args is FlutterLaunchRequestArguments) {
// Invert DAP's noDebug flag, treating it as false (so _do_ debug) if not
// provided.
return !(args.noDebug ?? false);
}

// Otherwise (attach), always debug.
return true;
}

/// Called by [attachRequest] to request that we actually connect to the app to be debugged.
@override
Future<void> attachImpl() async {
sendOutput('console', '\nAttach is not currently supported');
handleSessionTerminate();
final FlutterAttachRequestArguments args = this.args as FlutterAttachRequestArguments;

final String? vmServiceUri = args.vmServiceUri;
final List<String> toolArgs = <String>[
'attach',
'--machine',
if (vmServiceUri != null)
...<String>['--debug-uri', vmServiceUri],
];

await _startProcess(
toolArgs: toolArgs,
customTool: args.customTool,
customToolReplacesArgs: args.customToolReplacesArgs,
userToolArgs: args.toolArgs,
);
}

/// [customRequest] handles any messages that do not match standard messages in the spec.
Expand Down Expand Up @@ -171,34 +211,46 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments
Future<void> launchImpl() async {
final FlutterLaunchRequestArguments args = this.args as FlutterLaunchRequestArguments;

// "debug"/"noDebug" refers to the DAP "debug" mode and not the Flutter
// debug mode (vs Profile/Release). It is possible for the user to "Run"
// from VS Code (eg. not want to hit breakpoints/etc.) but still be running
// a debug build.
final bool debug = !(args.noDebug ?? false);
final String? program = args.program;

final List<String> toolArgs = <String>[
'run',
'--machine',
if (debug) '--start-paused',
];

await _startProcess(
toolArgs: toolArgs,
customTool: args.customTool,
customToolReplacesArgs: args.customToolReplacesArgs,
targetProgram: args.program,
userToolArgs: args.toolArgs,
userArgs: args.args,
);
}

/// Starts the `flutter` process to run/attach to the required app.
Future<void> _startProcess({
required String? customTool,
required int? customToolReplacesArgs,
required List<String> toolArgs,
required List<String>? userToolArgs,
String? targetProgram,
List<String>? userArgs,
}) async {
// Handle customTool and deletion of any arguments for it.
final String executable = args.customTool ?? fileSystem.path.join(Cache.flutterRoot!, 'bin', platform.isWindows ? 'flutter.bat' : 'flutter');
final int? removeArgs = args.customToolReplacesArgs;
if (args.customTool != null && removeArgs != null) {
final String executable = customTool ?? fileSystem.path.join(Cache.flutterRoot!, 'bin', platform.isWindows ? 'flutter.bat' : 'flutter');
final int? removeArgs = customToolReplacesArgs;
if (customTool != null && removeArgs != null) {
toolArgs.removeRange(0, math.min(removeArgs, toolArgs.length));
}

final List<String> processArgs = <String>[
...toolArgs,
...?args.toolArgs,
if (program != null) ...<String>[
...?userToolArgs,
if (targetProgram != null) ...<String>[
'--target',
program,
targetProgram,
],
...?args.args,
...?userArgs,
];

// Find the package_config file for this script. This is used by the
Expand All @@ -207,12 +259,12 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments
// be correctly classes as "my code", "sdk" or "external packages".
// TODO(dantup): Remove this once https://github.com/dart-lang/sdk/issues/45530
// is done as it will not be necessary.
final String? possibleRoot = program == null
final String? possibleRoot = targetProgram == null
? args.cwd
: fileSystem.path.isAbsolute(program)
? fileSystem.path.dirname(program)
: fileSystem.path.isAbsolute(targetProgram)
? fileSystem.path.dirname(targetProgram)
: fileSystem.path.dirname(
fileSystem.path.normalize(fileSystem.path.join(args.cwd ?? '', args.program)));
fileSystem.path.normalize(fileSystem.path.join(args.cwd ?? '', targetProgram)));
if (possibleRoot != null) {
final File? packageConfig = findPackageConfigFile(possibleRoot);
if (packageConfig != null) {
Expand Down Expand Up @@ -330,8 +382,6 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments
void _handleDebugPort(Map<String, Object?> params) {
// When running in noDebug mode, Flutter may still provide us a VM Service
// URI, but we will not connect it because we don't want to do any debugging.
final FlutterLaunchRequestArguments args = this.args as FlutterLaunchRequestArguments;
final bool debug = !(args.noDebug ?? false);
if (!debug) {
return;
}
Expand Down Expand Up @@ -411,13 +461,13 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments
// general output printed by the user.
final String outputCategory = _receivedAppStarted ? 'stdout' : 'console';

// Output in stdout can include both user output (eg. print) and Flutter
// daemon output. Since it's not uncommon for users to print JSON while
// debugging, we must try to detect which messages are likely Flutter
// messages as reliably as possible, as trying to process users output
// as a Flutter message may result in an unhandled error that will
// terminate the debug adater in a way that does not provide feedback
// because the standard crash violates the DAP protocol.
// Output in stdout can include both user output (eg. print) and Flutter
// daemon output. Since it's not uncommon for users to print JSON while
// debugging, we must try to detect which messages are likely Flutter
// messages as reliably as possible, as trying to process users output
// as a Flutter message may result in an unhandled error that will
// terminate the debug adater in a way that does not provide feedback
// because the standard crash violates the DAP protocol.
Object? jsonData;
try {
jsonData = jsonDecode(data);
Expand Down Expand Up @@ -459,9 +509,6 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments
bool fullRestart, [
String? reason,
]) async {
final DartCommonLaunchAttachRequestArguments args = this.args;
final bool debug =
args is! FlutterLaunchRequestArguments || args.noDebug != true;
try {
await sendFlutterRequest('app.restart', <String, Object?>{
'appId': _appId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@ import 'package:dds/dap.dart';
/// An implementation of [AttachRequestArguments] that includes all fields used by the Flutter debug adapter.
///
/// This class represents the data passed from the client editor to the debug
/// adapter in attachRequest, which is a request to start debugging an
/// adapter in attachRequest, which is a request to attach to/debug a running
/// application.
class FlutterAttachRequestArguments
extends DartCommonLaunchAttachRequestArguments
implements AttachRequestArguments {
FlutterAttachRequestArguments({
this.toolArgs,
this.customTool,
this.customToolReplacesArgs,
this.vmServiceUri,
Object? restart,
String? name,
String? cwd,
Expand All @@ -34,11 +38,49 @@ class FlutterAttachRequestArguments
sendLogsToClient: sendLogsToClient,
);

FlutterAttachRequestArguments.fromMap(Map<String, Object?> obj):
FlutterAttachRequestArguments.fromMap(Map<String, Object?> obj)
: toolArgs = (obj['toolArgs'] as List<Object?>?)?.cast<String>(),
customTool = obj['customTool'] as String?,
customToolReplacesArgs = obj['customToolReplacesArgs'] as int?,
vmServiceUri = obj['vmServiceUri'] as String?,
super.fromMap(obj);

static FlutterAttachRequestArguments fromJson(Map<String, Object?> obj) =>
FlutterAttachRequestArguments.fromMap(obj);

/// Arguments to be passed to the tool that will run [program] (for example, the VM or Flutter tool).
final List<String>? toolArgs;

/// An optional tool to run instead of "flutter".
///
/// In combination with [customToolReplacesArgs] allows invoking a custom
/// tool instead of "flutter" to launch scripts/tests. The custom tool must be
/// completely compatible with the tool/command it is replacing.
///
/// This field should be a full absolute path if the tool may not be available
/// in `PATH`.
final String? customTool;

/// The number of arguments to delete from the beginning of the argument list
/// when invoking [customTool].
///
/// For example, setting [customTool] to `flutter_test_wrapper` and
/// `customToolReplacesArgs` to `1` for a test run would invoke
/// `flutter_test_wrapper foo_test.dart` instead of `flutter test foo_test.dart`.
final int? customToolReplacesArgs;

/// The VM Service URI of the running Flutter app to connect to.
final String? vmServiceUri;

@override
Map<String, Object?> toJson() => <String, Object?>{
...super.toJson(),
if (toolArgs != null) 'toolArgs': toolArgs,
if (customTool != null) 'customTool': customTool,
if (customToolReplacesArgs != null)
'customToolReplacesArgs': customToolReplacesArgs,
if (vmServiceUri != null) 'vmServiceUri': vmServiceUri,
};
}

/// An implementation of [LaunchRequestArguments] that includes all fields used by the Flutter debug adapter.
Expand Down
Loading