Repository navigation
[flutter_tools] Add DeviceService app launch and flutter run support for tool extensions - #194589
Conversation
…for tool extensions Introduce `DeviceService.launchApp`, `getVmServiceUri`, and `stopApp` RPC methods, `TargetDevice.buildTarget`, `ExtensionBuildResult.executablePath`, and `ExtensionBackedDevice.startApp`/`stopApp` with VM Service discovery and `ResidentRunner` integration so `flutter run` works on custom extension devices.
…ager wiring Delegates extension app builds in `ExtensionBackedDevice.startApp` to `ExtensionBuildManager`, supports `prebuiltApplication: true` via `PrebuiltApplicationPackage`, adds strongly-typed `ExtensionDebuggingOptions`, and hardens null-safe `vmService` handling in `ResidentRunner` and `HotRunner`.
Reverts unnecessary `source.dart` changes, removes arbitrary RPC and process launch timeouts, logs non-boolean RPC responses, tightens `ExtensionBackedDevice.startApp`, `FlutterDeviceManager`, and `ResidentHandlers.flutterDevices` nullability, documents early `pub.get()` in `FlutterCommand`, and parses `pubspec.yaml` via `package:yaml` in `LinuxBuildService`.
Use absolute file Uri for executableUri on ExtensionBuildResult and DeviceService.launchApp, require ExtensionDebuggingOptions on launchApp, fail ExtensionBackedDevice.startApp when VM Service discovery returns null with debugging enabled, revert core runner and early pub.get changes, consolidate LinuxDeviceService process state into _RunningApp, and support base64url padding in VM Service URI matching.
…I validation Exports RpcException from package:flutter_tools_extension and updates prototype RPC error assertions to match RpcException directly. Exposes protected logger on Device to remove duplicate _logger in ExtensionBackedDevice, adds ExtensionFileUriValidation.isValidFileUri in package:flutter_tools_core, and simplifies ExtensionBuildResult._parseExecutableUri.
…ch models and RPCs
…aunch # Conflicts: # packages/flutter_tools/lib/src/context/tool_dependencies.dart # packages/flutter_tools/lib/src/experimental/extension_device_manager.dart # packages/flutter_tools/lib/src/flutter_device_manager.dart # packages/flutter_tools/packages/flutter_tools_extension/lib/src/device.dart # packages/flutter_tools/packages/flutter_tools_extension_linux_prototype/lib/src/device.dart # packages/flutter_tools/packages/flutter_tools_extension_linux_prototype/test/linux_device_test.dart # packages/flutter_tools/test/commands.shard/hermetic/tool_extensions_device_test.dart
…unch - Require debuggingEnabled in ExtensionDebuggingOptions constructor and deserialization. - Reject prebuilt application bundles with a clean error on ExtensionBackedDevice until support is implemented. - Enforce strict BINARY_NAME extraction from linux/CMakeLists.txt in LinuxBuildService without pubspec name fallbacks. - Use allowMalformed: true when decoding process stdout/stderr in LinuxDeviceService and validate received VM service URIs. - Revert modifications to Device base class to preserve Google3 compatibility.
There was a problem hiding this comment.
Code Review
This pull request implements application launching, stopping, and VM Service URI discovery for tool extension devices. It updates the Device and DeviceService interfaces, provides a prototype implementation for Linux, and adds corresponding tests. The review feedback recommends making the VM Service URI matching regular expression more robust by supporting localhost and optional auth tokens, and correcting invalid leading-dot enum shorthand syntax (.debug to BuildMode.debug) in several test files.
|
I'll review this one tomorrow! :) |
| final String? buildTargetName = _targetDevice.buildTarget; | ||
| if (buildTargetName == null) { | ||
| logger.printError('Device $name does not specify a buildTarget for launching applications.'); | ||
| return LaunchResult.failed(); | ||
| } | ||
|
|
||
| final String targetMainPath = mainPath ?? 'lib/main.dart'; | ||
| logger.printTrace('Building application using extension target "$buildTargetName"...'); | ||
| final ExtensionBuildResult result = await _buildManager.build( | ||
| buildMode: buildMode, | ||
| mainPath: targetMainPath, | ||
| projectRoot: _fileSystem.currentDirectory.uri, | ||
| targetName: buildTargetName, | ||
| ); | ||
| if (!result.success) { | ||
| final String? errorMessage = result.errorMessage; | ||
| logger.printError( | ||
| errorMessage != null && errorMessage.isNotEmpty | ||
| ? 'Build failed: $errorMessage' | ||
| : 'Build failed.', | ||
| ); | ||
| return LaunchResult.failed(); | ||
| } |
There was a problem hiding this comment.
Should we make the extension responsible for building the app as part of launchApp? This would help reduce communication between the tool and the extension. It also gives the extension greater control over what happens before/after the app is built.
There was a problem hiding this comment.
Keeping the build step orchestrated by the host in ExtensionBackedDevice.startApp lets startApp support prebuiltApplication: true (--use-application-binary) without rebuilding, and lets the host resolve build output directories, artifact dependencies, and flutter assemble targets before passing the resulting binary URI to DeviceService.launchApp.
There was a problem hiding this comment.
I still feel uncertain but I'd be fine with this approach if we feel confident this will support all Android/iOS/web scenarios.
Today a device can have different build logic for its flutter build foo and flutter run -d foo commands. There might not be a flutter build subcommand that mirrors the build logic that runs on flutter run -d foo. But if this is a real scenario, I suspect we might be able to address it by supporting "hidden" build targets.
There was a problem hiding this comment.
If a device needs separate build logic for flutter run that shouldn't be exposed as a flutter build subcommand, we could also allow TargetDevice to reference an AssembleTarget (which is already internal to flutter assemble) rather than needing hidden BuildTargets.
| /// [ExtensionDebuggingOptions.debuggingEnabled] is `true`. Implementations | ||
| /// may await VM Service discovery from the running process before returning, | ||
| /// or return `null` if the process exits or VM Service discovery fails. | ||
| Future<Uri?> getVmServiceUri({required String deviceId}); |
There was a problem hiding this comment.
With this current pattern, if we need to add an optional argument to getVmServiceUri, changing the method signature would be a breaking change. Instead, we'd need to introduce a new method with the new optional argument.
Perhaps all ToolExtensionService methods that an extension implements should accept an FooOptions object. For example, this would be:
final class GetVmServiceUriOptions {
GetVmServiceUriOptions({
required this.deviceId,
});
final String deviceId;
}
abstract base class DeviceService extends ToolExtensionService {
Future<Uri?> getVmServiceUri(GetVmServiceUriOptions options);
}(Apologies if I've already commented on this and I'm repeating feedback!)
There was a problem hiding this comment.
Since extensions can constrain their package:flutter_tools_extension dependency in pubspec.yaml, signature changes only affect extensions when they explicitly bump their dependency constraint. The cross-version compatibility boundary between a newer flutter_tools SDK and an already-published extension is the JSON-RPC wire protocol, so keeping the wire protocol tolerant to new request fields and response object properties can give us compatibility without needing FooOptions wrappers on every method.
I think that encouraging extension authors to pin their package:flutter_tools_extension dependency or use caret version ranges would accomplish the same thing as wrapper objects if we respect semver properly. I'm not a huge fan of wrapper objects for parameters, but I do agree that they're probably the most robust way to handle adding additional parameters. I'm open to having a discussion on this :)
There was a problem hiding this comment.
Since extensions can constrain their
package:flutter_tools_extensiondependency inpubspec.yaml, signature changes only affect extensions when they explicitly bump their dependency constraint.
Ah and if I remember correctly, two extension can have two different package:flutter_tools_extension versions? If that's the case, I'm OK with skipping wrapper objects.
There was a problem hiding this comment.
Yes, that's correct! Individual extensions can have completely different dependencies, including conflicting ones.
…-launch # Conflicts: # packages/flutter_tools/packages/flutter_tools_core/lib/src/project.dart # packages/flutter_tools/packages/flutter_tools_extension_linux_prototype/lib/src/build.dart # packages/flutter_tools/packages/flutter_tools_extension_linux_prototype/test/linux_build_test.dart # packages/flutter_tools/test/commands.shard/hermetic/tool_extensions_build_test.dart
…ess.kill() in stopApp Do not call stopApp when getVmServiceUri returns null during ExtensionBackedDevice.startApp, and return false from LinuxDeviceService.stopApp if process.kill() fails.
|
|
||
| /// The [ExtensionBuildTarget.name] of the extension build target used to | ||
| /// build an application for this device during `flutter run`. | ||
| final String? buildTarget; |
There was a problem hiding this comment.
Hey, it looks like ExtensionBuildTarget.name doesn't validate the extension's value. We should also add some docs to explain what's legal or not. Feel free to punt to another PR though.
There was a problem hiding this comment.
Added ExtensionBuildTarget.isValidName validation and documented the valid target name format on ExtensionBuildTarget.name, BuildTarget.name, and TargetDevice.buildTarget.
…aunch # Conflicts: # packages/flutter_tools/lib/src/experimental/extension_device_manager.dart # packages/flutter_tools/packages/flutter_tools_extension/lib/src/device.dart # packages/flutter_tools/test/commands.shard/hermetic/tool_extensions_device_test.dart
Validate ExtensionBuildTarget.name with isValidName, constructor assertion, and deserialization FormatException. Remove empty build target name bypass in ExtensionBuildManager. Define ExtensionUriValidation on non-nullable Uri. Restore CMake BINARY_NAME check in LinuxBuildService.build. Ensure LinuxDeviceService.stopApp verifies process termination before cleaning up tracking state. Update tracking issue link for forwarding additional build and debug options. Update unit tests for invalid build target names and URI validation.
Add const to collection arguments in build_test.dart and use inferred type for executableFile. Add explicit String type annotation to _invalidNameMessage. Remove redundant timeouts in ExtensionDeviceClient isSupportedForProject and stopApp. Restore exact origin/master variable naming in ExtensionDevices.pollingGetDevices. Update Phase 8 tracking issue URL in extension_device_manager.dart. Fail early in ExtensionBackedDevice.startApp when no extension provides the requested build target. Add unit test verifying startApp fails when the build target is missing.
flutter/flutter@d4c8577...0c2473c 2026-10-28 [email protected] Roll Packages from a8549ae to 13d38e0 (9 revisions) (flutter/flutter#195488) 2026-10-28 [email protected] Use int.oneBitCount in PersistentHashMap and scope 53-bit caps to JS (flutter/flutter#195005) 2026-10-28 [email protected] Roll pub packages (flutter/flutter#195483) 2026-10-28 [email protected] [iPhone Duo] Use the correct UIScreen to report the display size and update when fold/unfold (flutter/flutter#195199) 2026-10-28 [email protected] [fuchsia] Fix portable_ui_test.h includes for newer Fuchsia SDKs (flutter/flutter#195420) 2026-10-28 [email protected] Change default pipeline options to match production draw calls (flutter/flutter#194597) 2026-10-28 [email protected] Roll Fuchsia Test Scripts from 2T18KC7-LDQtmQiLP... to xl1LWBuppzu-BnOzV... (flutter/flutter#195450) 2026-10-27 [email protected] [flutter_tools] Inject dependencies into Darwin build targets, native assets, and Xcode build settings (flutter/flutter#194052) 2026-10-27 [email protected] Revert: Reland square corners fix (flutter/flutter#195436) 2026-10-27 [email protected] [flutter_tools] Add DeviceService app launch and flutter run support for tool extensions (flutter/flutter#194589) 2026-10-27 [email protected] [engine] Honor targetWidth/targetHeight for animated images (flutter/flutter#194615) 2026-10-27 [email protected] [Android][a11y] Set visibleToUser=false for hidden semantics nodes (flutter/flutter#194411) 2026-10-27 [email protected] Fix repo deletion test flake (flutter/flutter#195349) 2026-10-27 [email protected] Roll Packages from 23a05b6 to a8549ae (10 revisions) (flutter/flutter#195434) 2026-10-27 [email protected] [web] Capture pointers to the Flutter view so drags survive platform view iframes (flutter/flutter#195187) 2026-10-27 [email protected] [web] Add opt-in raw samples, measured sample count and warm-up phase hook to the web benchmark recorder (flutter/flutter#195118) 2026-10-27 [email protected] Link migration guide in deprecation messages (flutter/flutter#195361) 2026-10-27 [email protected] Pin basic_material_app_macos__compile to macOS 26 (flutter/flutter#195429) 2026-10-27 [email protected] Add benchmark for warmed pipeline usage (flutter/flutter#195001) 2026-10-27 [email protected] [flutter_tools] Add device log streaming support to DeviceService and ExtensionBackedDevice (flutter/flutter#194964) 2026-10-27 [email protected] Roll Dart SDK from 1d16c04dc346 to 65fc979759ca (4 revisions) (flutter/flutter#195383) If this roll has caused a breakage, revert this CL and set the roller to dry run mode using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC [email protected],[email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
…for tool extensions (flutter#194589) Adds `DeviceService` RPC methods (`device.launchApp`, `device.getVmServiceUri`, and `device.stopApp`) and `ExtensionDebuggingOptions` to support building, launching, and stopping applications on tool extension devices via `flutter run`. Also exposes `@protected final Logger logger` on `Device` for subclasses. Part of flutter#190753
Adds
DeviceServiceRPC methods (device.launchApp,device.getVmServiceUri, anddevice.stopApp) andExtensionDebuggingOptionsto support building, launching, and stopping applications on tool extension devices viaflutter run. Also exposes@protected final Logger loggeronDevicefor subclasses.Part of #190753