Fix flutter attach local engine (#131825)
Fixes: https://github.com/flutter/flutter/issues/124970 Part of https://github.com/flutter/flutter/issues/47161 Before this change, there were two places we overrode the `Artifacts` in a Zone: 1. if/when we parse local-engine CLI options:1cf3907407/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart (L281)
2. an additional override for fuchsia platform dill (no longer used, deleted in this PR):1cf3907407/packages/flutter_tools/lib/src/commands/attach.dart (L274)
Note 1 above creates a new instance of `Artifacts.getLocalEngine()`. In this flow, there exist two instances of `Artifacts`: 1. The default fallback instance of `CachedArtifacts` (which gets all artifacts from flutter/bin/cache), instantiated in context_runner.dart:1cf3907407/packages/flutter_tools/lib/src/context_runner.dart (L137)
2. An instance of `CachedLocalEngineArtifacts` created in the command runner once the CLI options have been parsed:1cf3907407/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart (L281)
The regression happened when we direct injected the Artifacts 1 from above BEFORE we parsed the local-engine flag, and then used this in the second zone override, and then when creating the `FlutterDevice` there are multiple calls to `globals.artifacts` returned it when it should have returned Artifacts 2:1cf3907407/packages/flutter_tools/lib/src/resident_runner.dart (L80)
Device.artifactOverrides was originally introduced in https://github.com/flutter/flutter/pull/32071, but is no longer used, so I deleted it. I also removed direct injection of `Artifacts` to the attach sub-command, because that class now no longer references artifacts. I believe the ideal true fix for this would be to: 1. Migrate all leaf calls to `globals.artifacts` to use direct injection (in this case, the offending invocations were in [`FlutterDevice.create()`](1cf3907407/packages/flutter_tools/lib/src/resident_runner.dart (L80-L218)
), but I'm not sure that something else would not have broken later) 2. Ensure we are always direct injecting the desired instance of `Artifacts`--that is, if the user desires local engine artifacts, that we are passing an instance of `CachedLocalEngineArtifacts`. a. Alternatively, and probably simpler, teach `CachedArtifacts` to know about the local engine. This would mean parsing the global CLI options BEFORE we ever construct any instance of `Artifacts`. As an overall recommendation for implementing https://github.com/flutter/flutter/issues/47161, in the overall tree of tool function calls, we should probably migrate the leaves first (that is, migrate the sub-commands last). We should also audit and reconsider any usage of `runZoned()` or `context.run()` for the purpose overriding zoneValues.
This commit is contained in:
parent
64a0683b41
commit
a6118612ac
@ -156,7 +156,6 @@ List<FlutterCommand> generateCommands({
|
||||
AssembleCommand(verboseHelp: verboseHelp, buildSystem: globals.buildSystem),
|
||||
AttachCommand(
|
||||
verboseHelp: verboseHelp,
|
||||
artifacts: globals.artifacts,
|
||||
stdio: globals.stdio,
|
||||
logger: globals.logger,
|
||||
terminal: globals.terminal,
|
||||
|
@ -7,9 +7,7 @@ import 'dart:async';
|
||||
import 'package:vm_service/vm_service.dart';
|
||||
|
||||
import '../android/android_device.dart';
|
||||
import '../artifacts.dart';
|
||||
import '../base/common.dart';
|
||||
import '../base/context.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../base/io.dart';
|
||||
import '../base/logger.dart';
|
||||
@ -65,7 +63,6 @@ class AttachCommand extends FlutterCommand {
|
||||
AttachCommand({
|
||||
bool verboseHelp = false,
|
||||
HotRunnerFactory? hotRunnerFactory,
|
||||
required Artifacts? artifacts,
|
||||
required Stdio stdio,
|
||||
required Logger logger,
|
||||
required Terminal terminal,
|
||||
@ -73,15 +70,14 @@ class AttachCommand extends FlutterCommand {
|
||||
required Platform platform,
|
||||
required ProcessInfo processInfo,
|
||||
required FileSystem fileSystem,
|
||||
}): _artifacts = artifacts,
|
||||
_hotRunnerFactory = hotRunnerFactory ?? HotRunnerFactory(),
|
||||
_stdio = stdio,
|
||||
_logger = logger,
|
||||
_terminal = terminal,
|
||||
_signals = signals,
|
||||
_platform = platform,
|
||||
_processInfo = processInfo,
|
||||
_fileSystem = fileSystem {
|
||||
}) : _hotRunnerFactory = hotRunnerFactory ?? HotRunnerFactory(),
|
||||
_stdio = stdio,
|
||||
_logger = logger,
|
||||
_terminal = terminal,
|
||||
_signals = signals,
|
||||
_platform = platform,
|
||||
_processInfo = processInfo,
|
||||
_fileSystem = fileSystem {
|
||||
addBuildModeFlags(verboseHelp: verboseHelp, defaultToRelease: false, excludeRelease: true);
|
||||
usesTargetOption();
|
||||
usesPortOptions(verboseHelp: verboseHelp);
|
||||
@ -145,7 +141,6 @@ class AttachCommand extends FlutterCommand {
|
||||
}
|
||||
|
||||
final HotRunnerFactory _hotRunnerFactory;
|
||||
final Artifacts? _artifacts;
|
||||
final Stdio _stdio;
|
||||
final Logger _logger;
|
||||
final Terminal _terminal;
|
||||
@ -267,13 +262,7 @@ known, it can be explicitly provided to attach via the command-line, e.g.
|
||||
throwToolExit('Did not find any valid target devices.');
|
||||
}
|
||||
|
||||
final Artifacts? overrideArtifacts = device.artifactOverrides ?? _artifacts;
|
||||
await context.run<void>(
|
||||
body: () => _attachToDevice(device),
|
||||
overrides: <Type, Generator>{
|
||||
Artifacts: () => overrideArtifacts,
|
||||
},
|
||||
);
|
||||
await _attachToDevice(device);
|
||||
|
||||
return FlutterCommandResult.success();
|
||||
}
|
||||
|
@ -585,7 +585,7 @@ class DefaultResidentCompiler implements ResidentCompiler {
|
||||
required this.buildMode,
|
||||
required Logger logger,
|
||||
required ProcessManager processManager,
|
||||
required Artifacts artifacts,
|
||||
required this.artifacts,
|
||||
required Platform platform,
|
||||
required FileSystem fileSystem,
|
||||
this.testCompilation = false,
|
||||
@ -604,7 +604,6 @@ class DefaultResidentCompiler implements ResidentCompiler {
|
||||
@visibleForTesting StdoutHandler? stdoutHandler,
|
||||
}) : _logger = logger,
|
||||
_processManager = processManager,
|
||||
_artifacts = artifacts,
|
||||
_stdoutHandler = stdoutHandler ?? StdoutHandler(logger: logger, fileSystem: fileSystem),
|
||||
_platform = platform,
|
||||
dartDefines = dartDefines ?? const <String>[],
|
||||
@ -615,7 +614,7 @@ class DefaultResidentCompiler implements ResidentCompiler {
|
||||
|
||||
final Logger _logger;
|
||||
final ProcessManager _processManager;
|
||||
final Artifacts _artifacts;
|
||||
final Artifacts artifacts;
|
||||
final Platform _platform;
|
||||
|
||||
final bool testCompilation;
|
||||
@ -751,12 +750,12 @@ class DefaultResidentCompiler implements ResidentCompiler {
|
||||
{String? additionalSourceUri}
|
||||
) async {
|
||||
final TargetPlatform? platform = (targetModel == TargetModel.dartdevc) ? TargetPlatform.web_javascript : null;
|
||||
final String frontendServer = _artifacts.getArtifactPath(
|
||||
final String frontendServer = artifacts.getArtifactPath(
|
||||
Artifact.frontendServerSnapshotForEngineDartSdk,
|
||||
platform: platform,
|
||||
);
|
||||
final List<String> command = <String>[
|
||||
_artifacts.getArtifactPath(Artifact.engineDartBinary, platform: platform),
|
||||
artifacts.getArtifactPath(Artifact.engineDartBinary, platform: platform),
|
||||
'--disable-dart-dev',
|
||||
frontendServer,
|
||||
'--sdk-root',
|
||||
|
@ -8,7 +8,6 @@ import 'dart:math' as math;
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import 'application_package.dart';
|
||||
import 'artifacts.dart';
|
||||
import 'base/context.dart';
|
||||
import 'base/dds.dart';
|
||||
import 'base/file_system.dart';
|
||||
@ -741,9 +740,6 @@ abstract class Device {
|
||||
/// Clear the device's logs.
|
||||
void clearLogs();
|
||||
|
||||
/// Optional device-specific artifact overrides.
|
||||
OverrideArtifacts? get artifactOverrides => null;
|
||||
|
||||
/// Start an app package on the current device.
|
||||
///
|
||||
/// [platformArgs] allows callers to pass platform-specific arguments to the
|
||||
|
@ -19,6 +19,7 @@ import 'package:flutter_tools/src/base/terminal.dart';
|
||||
import 'package:flutter_tools/src/build_info.dart';
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/commands/attach.dart';
|
||||
import 'package:flutter_tools/src/compile.dart';
|
||||
import 'package:flutter_tools/src/device.dart';
|
||||
import 'package:flutter_tools/src/device_port_forwarder.dart';
|
||||
import 'package:flutter_tools/src/ios/application_package.dart';
|
||||
@ -72,12 +73,12 @@ void main() {
|
||||
testFileSystem = MemoryFileSystem.test();
|
||||
testFileSystem.directory('lib').createSync();
|
||||
testFileSystem.file(testFileSystem.path.join('lib', 'main.dart')).createSync();
|
||||
artifacts = Artifacts.test();
|
||||
artifacts = Artifacts.test(fileSystem: testFileSystem);
|
||||
stdio = FakeStdio();
|
||||
terminal = FakeTerminal();
|
||||
signals = Signals.test();
|
||||
processInfo = FakeProcessInfo();
|
||||
testDeviceManager = TestDeviceManager(logger: BufferLogger.test());
|
||||
testDeviceManager = TestDeviceManager(logger: logger);
|
||||
});
|
||||
|
||||
group('with one device and no specified target file', () {
|
||||
@ -135,7 +136,6 @@ void main() {
|
||||
|
||||
await createTestCommandRunner(AttachCommand(
|
||||
hotRunnerFactory: hotRunnerFactory,
|
||||
artifacts: artifacts,
|
||||
stdio: stdio,
|
||||
logger: logger,
|
||||
terminal: terminal,
|
||||
@ -199,7 +199,6 @@ void main() {
|
||||
|
||||
await createTestCommandRunner(AttachCommand(
|
||||
hotRunnerFactory: hotRunnerFactory,
|
||||
artifacts: artifacts,
|
||||
stdio: stdio,
|
||||
logger: logger,
|
||||
terminal: terminal,
|
||||
@ -229,6 +228,75 @@ void main() {
|
||||
Signals: () => FakeSignals(),
|
||||
});
|
||||
|
||||
testUsingContext('local engine artifacts are passed to runner', () async {
|
||||
const String localEngineSrc = '/path/to/local/engine/src';
|
||||
const String localEngineDir = 'host_debug_unopt';
|
||||
testFileSystem.directory('$localEngineSrc/out/$localEngineDir').createSync(recursive: true);
|
||||
final FakeIOSDevice device = FakeIOSDevice(
|
||||
portForwarder: portForwarder,
|
||||
majorSdkVersion: 12,
|
||||
onGetLogReader: () {
|
||||
fakeLogReader.addLine('Foo');
|
||||
fakeLogReader.addLine('The Dart VM service is listening on http://127.0.0.1:$devicePort');
|
||||
return fakeLogReader;
|
||||
},
|
||||
);
|
||||
testDeviceManager.devices = <Device>[device];
|
||||
final Completer<void> completer = Completer<void>();
|
||||
final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) {
|
||||
if (message == '[verbose] VM Service URL on device: http://127.0.0.1:$devicePort') {
|
||||
// The "VM Service URL on device" message is output by the ProtocolDiscovery when it found the VM Service.
|
||||
completer.complete();
|
||||
}
|
||||
});
|
||||
final FakeHotRunner hotRunner = FakeHotRunner();
|
||||
hotRunner.onAttach = (
|
||||
Completer<DebugConnectionInfo>? connectionInfoCompleter,
|
||||
Completer<void>? appStartedCompleter,
|
||||
bool allowExistingDdsInstance,
|
||||
bool enableDevTools,
|
||||
) async => 0;
|
||||
hotRunner.exited = false;
|
||||
hotRunner.isWaitingForVmService = false;
|
||||
bool passedArtifactTest = false;
|
||||
final FakeHotRunnerFactory hotRunnerFactory = FakeHotRunnerFactory()
|
||||
..hotRunner = hotRunner
|
||||
.._artifactTester = (Artifacts artifacts) {
|
||||
expect(artifacts, isA<CachedLocalEngineArtifacts>());
|
||||
// expecting this to be true ensures this test ran
|
||||
passedArtifactTest = true;
|
||||
};
|
||||
|
||||
await createTestCommandRunner(AttachCommand(
|
||||
hotRunnerFactory: hotRunnerFactory,
|
||||
stdio: stdio,
|
||||
logger: logger,
|
||||
terminal: terminal,
|
||||
signals: signals,
|
||||
platform: platform,
|
||||
processInfo: processInfo,
|
||||
fileSystem: testFileSystem,
|
||||
)).run(<String>['attach', '--local-engine-src-path=$localEngineSrc', '--local-engine=$localEngineDir']);
|
||||
await Future.wait<void>(<Future<void>>[
|
||||
completer.future,
|
||||
fakeLogReader.dispose(),
|
||||
loggerSubscription.cancel(),
|
||||
]);
|
||||
expect(passedArtifactTest, isTrue);
|
||||
}, overrides: <Type, Generator>{
|
||||
Artifacts: () => artifacts,
|
||||
DeviceManager: () => testDeviceManager,
|
||||
FileSystem: () => testFileSystem,
|
||||
Logger: () => logger,
|
||||
MDnsVmServiceDiscovery: () => MDnsVmServiceDiscovery(
|
||||
mdnsClient: FakeMDnsClient(<PtrResourceRecord>[], <String, List<SrvResourceRecord>>{}),
|
||||
preliminaryMDnsClient: FakeMDnsClient(<PtrResourceRecord>[], <String, List<SrvResourceRecord>>{}),
|
||||
logger: logger,
|
||||
flutterUsage: TestUsage(),
|
||||
),
|
||||
ProcessManager: () => FakeProcessManager.empty(),
|
||||
});
|
||||
|
||||
testUsingContext('succeeds with iOS device with mDNS', () async {
|
||||
final FakeIOSDevice device = FakeIOSDevice(
|
||||
portForwarder: portForwarder,
|
||||
@ -254,7 +322,6 @@ void main() {
|
||||
|
||||
await createTestCommandRunner(AttachCommand(
|
||||
hotRunnerFactory: hotRunnerFactory,
|
||||
artifacts: artifacts,
|
||||
stdio: stdio,
|
||||
logger: logger,
|
||||
terminal: terminal,
|
||||
@ -319,7 +386,6 @@ void main() {
|
||||
|
||||
await createTestCommandRunner(AttachCommand(
|
||||
hotRunnerFactory: hotRunnerFactory,
|
||||
artifacts: artifacts,
|
||||
stdio: stdio,
|
||||
logger: logger,
|
||||
terminal: terminal,
|
||||
@ -390,7 +456,6 @@ void main() {
|
||||
|
||||
await createTestCommandRunner(AttachCommand(
|
||||
hotRunnerFactory: hotRunnerFactory,
|
||||
artifacts: artifacts,
|
||||
stdio: stdio,
|
||||
logger: logger,
|
||||
terminal: terminal,
|
||||
@ -465,7 +530,6 @@ void main() {
|
||||
|
||||
await createTestCommandRunner(AttachCommand(
|
||||
hotRunnerFactory: hotRunnerFactory,
|
||||
artifacts: artifacts,
|
||||
stdio: stdio,
|
||||
logger: logger,
|
||||
terminal: terminal,
|
||||
@ -534,7 +598,6 @@ void main() {
|
||||
}
|
||||
});
|
||||
final Future<void> task = createTestCommandRunner(AttachCommand(
|
||||
artifacts: artifacts,
|
||||
stdio: stdio,
|
||||
logger: logger,
|
||||
terminal: terminal,
|
||||
@ -567,7 +630,6 @@ void main() {
|
||||
};
|
||||
testDeviceManager.devices = <Device>[device];
|
||||
expect(() => createTestCommandRunner(AttachCommand(
|
||||
artifacts: artifacts,
|
||||
stdio: stdio,
|
||||
logger: logger,
|
||||
terminal: terminal,
|
||||
@ -611,7 +673,6 @@ void main() {
|
||||
|
||||
final AttachCommand command = AttachCommand(
|
||||
hotRunnerFactory: hotRunnerFactory,
|
||||
artifacts: artifacts,
|
||||
stdio: stdio,
|
||||
logger: logger,
|
||||
terminal: terminal,
|
||||
@ -655,7 +716,6 @@ void main() {
|
||||
testDeviceManager.devices = <Device>[device];
|
||||
|
||||
final AttachCommand command = AttachCommand(
|
||||
artifacts: artifacts,
|
||||
stdio: stdio,
|
||||
logger: logger,
|
||||
terminal: terminal,
|
||||
@ -709,7 +769,6 @@ void main() {
|
||||
|
||||
await createTestCommandRunner(AttachCommand(
|
||||
hotRunnerFactory: hotRunnerFactory,
|
||||
artifacts: artifacts,
|
||||
stdio: stdio,
|
||||
logger: logger,
|
||||
terminal: terminal,
|
||||
@ -741,7 +800,6 @@ void main() {
|
||||
testDeviceManager.devices = <Device>[device];
|
||||
|
||||
final AttachCommand command = AttachCommand(
|
||||
artifacts: artifacts,
|
||||
stdio: stdio,
|
||||
logger: logger,
|
||||
terminal: terminal,
|
||||
@ -790,7 +848,6 @@ void main() {
|
||||
}
|
||||
});
|
||||
final Future<void> task = createTestCommandRunner(AttachCommand(
|
||||
artifacts: artifacts,
|
||||
stdio: stdio,
|
||||
logger: logger,
|
||||
terminal: terminal,
|
||||
@ -825,7 +882,6 @@ void main() {
|
||||
}
|
||||
});
|
||||
final Future<void> task = createTestCommandRunner(AttachCommand(
|
||||
artifacts: artifacts,
|
||||
stdio: stdio,
|
||||
logger: logger,
|
||||
terminal: terminal,
|
||||
@ -861,7 +917,6 @@ void main() {
|
||||
}
|
||||
});
|
||||
final Future<void> task = createTestCommandRunner(AttachCommand(
|
||||
artifacts: artifacts,
|
||||
stdio: stdio,
|
||||
logger: logger,
|
||||
terminal: terminal,
|
||||
@ -906,7 +961,6 @@ void main() {
|
||||
}
|
||||
});
|
||||
final Future<void> task = createTestCommandRunner(AttachCommand(
|
||||
artifacts: artifacts,
|
||||
stdio: stdio,
|
||||
logger: logger,
|
||||
terminal: terminal,
|
||||
@ -943,7 +997,6 @@ void main() {
|
||||
|
||||
testUsingContext('exits when no device connected', () async {
|
||||
final AttachCommand command = AttachCommand(
|
||||
artifacts: artifacts,
|
||||
stdio: stdio,
|
||||
logger: logger,
|
||||
terminal: terminal,
|
||||
@ -967,7 +1020,6 @@ void main() {
|
||||
final FakeIOSDevice device = FakeIOSDevice();
|
||||
testDeviceManager.devices = <Device>[device];
|
||||
expect(createTestCommandRunner(AttachCommand(
|
||||
artifacts: artifacts,
|
||||
stdio: stdio,
|
||||
logger: logger,
|
||||
terminal: terminal,
|
||||
@ -988,7 +1040,6 @@ void main() {
|
||||
|
||||
testUsingContext('exits when multiple devices connected', () async {
|
||||
final AttachCommand command = AttachCommand(
|
||||
artifacts: artifacts,
|
||||
stdio: stdio,
|
||||
logger: logger,
|
||||
terminal: terminal,
|
||||
@ -1038,7 +1089,6 @@ void main() {
|
||||
|
||||
final AttachCommand command = AttachCommand(
|
||||
hotRunnerFactory: hotRunnerFactory,
|
||||
artifacts: artifacts,
|
||||
stdio: stdio,
|
||||
logger: logger,
|
||||
terminal: terminal,
|
||||
@ -1079,7 +1129,6 @@ void main() {
|
||||
|
||||
final AttachCommand command = AttachCommand(
|
||||
hotRunnerFactory: hotRunnerFactory,
|
||||
artifacts: artifacts,
|
||||
stdio: stdio,
|
||||
logger: logger,
|
||||
terminal: terminal,
|
||||
@ -1134,6 +1183,7 @@ class FakeHotRunnerFactory extends Fake implements HotRunnerFactory {
|
||||
String? dillOutputPath;
|
||||
String? projectRootPath;
|
||||
late List<FlutterDevice> devices;
|
||||
void Function(Artifacts artifacts)? _artifactTester;
|
||||
|
||||
@override
|
||||
HotRunner build(
|
||||
@ -1150,6 +1200,11 @@ class FakeHotRunnerFactory extends Fake implements HotRunnerFactory {
|
||||
bool ipv6 = false,
|
||||
FlutterProject? flutterProject,
|
||||
}) {
|
||||
if (_artifactTester != null) {
|
||||
for (final FlutterDevice device in devices) {
|
||||
_artifactTester!((device.generator! as DefaultResidentCompiler).artifacts);
|
||||
}
|
||||
}
|
||||
this.devices = devices;
|
||||
this.dillOutputPath = dillOutputPath;
|
||||
this.projectRootPath = projectRootPath;
|
||||
@ -1399,9 +1454,6 @@ class FakeAndroidDevice extends Fake implements AndroidDevice {
|
||||
return onGetLogReader!();
|
||||
}
|
||||
|
||||
@override
|
||||
OverrideArtifacts? get artifactOverrides => null;
|
||||
|
||||
@override
|
||||
final PlatformType platformType = PlatformType.android;
|
||||
|
||||
@ -1456,9 +1508,6 @@ class FakeIOSDevice extends Fake implements IOSDevice {
|
||||
return onGetLogReader!();
|
||||
}
|
||||
|
||||
@override
|
||||
OverrideArtifacts? get artifactOverrides => null;
|
||||
|
||||
@override
|
||||
final String name = 'name';
|
||||
|
||||
|
@ -5,7 +5,6 @@
|
||||
import 'package:args/args.dart';
|
||||
import 'package:args/command_runner.dart';
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:flutter_tools/src/artifacts.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/io.dart';
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
@ -75,7 +74,6 @@ void main() {
|
||||
),
|
||||
),
|
||||
AttachCommand(
|
||||
artifacts: Artifacts.test(),
|
||||
stdio: FakeStdio(),
|
||||
logger: logger,
|
||||
terminal: FakeTerminal(),
|
||||
|
Loading…
x
Reference in New Issue
Block a user