Skip to content

Commit 0321ef5

Browse files
authored
Add buildMode, icuDataPath and engineRevision interpolations for custom devices (flutter#164455)
<!-- Thanks for filing a pull request! Reviewers are typically assigned within a week of filing a request. To learn more about code review, see our documentation on Tree Hygiene: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md --> Make 3 additional string interpolated values available to the `postBuild` and `runDebug` custom device commands: - `${icuDataPath}` - `${engineRevision}` - `${buildMode}` flutter#164454 provides some additional context and the main motivation for this change. Fixes flutter#164454. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
1 parent f1dc730 commit 0321ef5

File tree

3 files changed

+118
-5
lines changed

3 files changed

+118
-5
lines changed

packages/flutter_tools/lib/src/custom_devices/custom_device.dart

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:meta/meta.dart';
88
import 'package:process/process.dart';
99

1010
import '../application_package.dart';
11+
import '../artifacts.dart';
1112
import '../base/common.dart';
1213
import '../base/file_system.dart';
1314
import '../base/io.dart';
@@ -21,6 +22,7 @@ import '../convert.dart';
2122
import '../device.dart';
2223
import '../device_port_forwarder.dart';
2324
import '../features.dart';
25+
import '../globals.dart' as globals;
2426
import '../project.dart';
2527
import '../protocol_discovery.dart';
2628
import '../vmservice.dart';
@@ -341,6 +343,7 @@ class CustomDeviceAppSession {
341343
Map<String, Object?> platformArgs = const <String, Object>{},
342344
bool prebuiltApplication = false,
343345
String? userIdentifier,
346+
Map<String, String> additionalReplacementValues = const <String, String>{},
344347
}) async {
345348
final bool traceStartup = platformArgs['trace-startup'] as bool? ?? false;
346349
final String? packageName = _appPackage.name;
@@ -352,7 +355,7 @@ class CustomDeviceAppSession {
352355
'remotePath': '/tmp/',
353356
'appName': packageName,
354357
'engineOptions': _getEngineOptionsForCmdline(debuggingOptions, traceStartup, route),
355-
});
358+
}, additionalReplacementValues: additionalReplacementValues);
356359

357360
final Process process = await _processUtils.start(interpolated);
358361
assert(_process == null);
@@ -700,14 +703,24 @@ class CustomDevice extends Device {
700703
String? userIdentifier,
701704
BundleBuilder? bundleBuilder,
702705
}) async {
706+
final TargetPlatform platform = await targetPlatform;
707+
final Artifacts artifacts = globals.artifacts!;
708+
709+
final Map<String, String> additionalReplacementValues = <String, String>{
710+
'buildMode': debuggingOptions.buildInfo.modeName,
711+
'icuDataPath': artifacts.getArtifactPath(Artifact.icuData, platform: platform),
712+
'engineRevision':
713+
artifacts.usesLocalArtifacts ? 'local' : globals.flutterVersion.engineRevision,
714+
};
715+
703716
if (!prebuiltApplication) {
704717
final String assetBundleDir = getAssetBuildDirectory();
705718

706719
bundleBuilder ??= BundleBuilder();
707720

708721
// this just builds the asset bundle, it's the same as `flutter build bundle`
709722
await bundleBuilder.build(
710-
platform: await targetPlatform,
723+
platform: platform,
711724
buildInfo: debuggingOptions.buildInfo,
712725
mainPath: mainPath,
713726
depfilePath: defaultDepfilePath,
@@ -720,7 +733,11 @@ class CustomDevice extends Device {
720733
if (packageName == null) {
721734
throwToolExit('Could not start app, name for $package is unknown.');
722735
}
723-
await _tryPostBuild(appName: packageName, localPath: assetBundleDir);
736+
await _tryPostBuild(
737+
appName: packageName,
738+
localPath: assetBundleDir,
739+
additionalReplacementValues: additionalReplacementValues,
740+
);
724741
}
725742
}
726743

@@ -736,6 +753,7 @@ class CustomDevice extends Device {
736753
platformArgs: platformArgs,
737754
prebuiltApplication: prebuiltApplication,
738755
userIdentifier: userIdentifier,
756+
additionalReplacementValues: additionalReplacementValues,
739757
);
740758
}
741759

packages/flutter_tools/static/custom-devices.schema.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
"required": false
6060
},
6161
"postBuild": {
62-
"description": "The command to be invoked after the build process is done, to do any additional packaging for example.",
62+
"description": "The command to be invoked after the build process is done, to do any additional packaging for example. The following variables are available via string interpolation:\n- ${appName}\n- ${localPath}\n- ${buildMode}\n- ${icuDataPath}\n- ${engineRevision}",
6363
"type": ["array", "null"],
6464
"items": {
6565
"type": "string"
@@ -91,7 +91,7 @@
9191
]
9292
},
9393
"runDebug": {
94-
"description": "The command to be invoked to run the app in debug mode. The name of the app to be started is available via the ${appName} string interpolation. Make sure the flutter cmdline output is available via this commands stdout/stderr since the SDK needs the \"VM Service is now listening on ...\" message to function. If the forwardPort command is not specified, the VM Service URL will be connected to as-is, without any port forwarding. In that case you need to make sure it is reachable from your host device, possibly via the \"--vm-service-host=<ip>\" engine flag.",
94+
"description": "The command to be invoked to run the app in debug mode. Make sure the flutter cmdline output is available via this commands stdout/stderr since the SDK needs the \"VM Service is now listening on ...\" message to function. If the forwardPort command is not specified, the VM Service URL will be connected to as-is, without any port forwarding. In that case you need to make sure it is reachable from your host device, possibly via the \"--vm-service-host=<ip>\" engine flag. The following variables are available via string interpolation:\n- ${appName}\n- ${engineOptions}\n- ${buildMode}\n- ${icuDataPath}\n- ${engineRevision}",
9595
"type": "array",
9696
"items": {
9797
"type": "string"

packages/flutter_tools/test/general.shard/custom_devices/custom_device_test.dart

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'dart:async';
77
import 'package:file/file.dart';
88
import 'package:file/memory.dart';
99
import 'package:file_testing/file_testing.dart';
10+
import 'package:flutter_tools/src/artifacts.dart';
1011
import 'package:flutter_tools/src/base/logger.dart';
1112
import 'package:flutter_tools/src/build_info.dart';
1213
import 'package:flutter_tools/src/build_system/build_system.dart';
@@ -530,6 +531,100 @@ void main() {
530531
},
531532
);
532533

534+
testUsingContext(
535+
'custom device command string interpolation end-to-end test',
536+
() async {
537+
final Completer<void> runDebugCompleter = Completer<void>();
538+
539+
final CustomDeviceConfig config = testConfig.copyWith(
540+
platform: TargetPlatform.linux_arm64,
541+
postBuildCommand: const <String>[
542+
'testpostbuild',
543+
r'--buildMode=${buildMode}',
544+
r'--icuDataPath=${icuDataPath}',
545+
r'--engineRevision=${engineRevision}',
546+
],
547+
runDebugCommand: const <String>[
548+
'testrundebug',
549+
r'--buildMode=${buildMode}',
550+
r'--icuDataPath=${icuDataPath}',
551+
r'--engineRevision=${engineRevision}',
552+
],
553+
);
554+
555+
final List<Pattern> commandArgumentsPattern = <Pattern>[
556+
RegExp(r'--buildMode=.*'),
557+
RegExp(r'--icuDataPath=.*'),
558+
RegExp(r'--engineRevision=.*'),
559+
];
560+
561+
final String expectedIcuDataPath = globals.artifacts!.getArtifactPath(
562+
Artifact.icuData,
563+
platform: config.platform,
564+
);
565+
final String expectedEngineRevision = globals.flutterVersion.engineRevision;
566+
567+
final List<String> expectedCommandArguments = <String>[
568+
'--buildMode=debug',
569+
'--icuDataPath=$expectedIcuDataPath',
570+
'--engineRevision=$expectedEngineRevision',
571+
];
572+
573+
final List<String> expectedRunDebugCommand = <String>[
574+
'testrundebug',
575+
...expectedCommandArguments,
576+
];
577+
final List<String> expectedPostBuildCommand = <String>[
578+
'testpostbuild',
579+
...expectedCommandArguments,
580+
];
581+
582+
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
583+
FakeCommand(
584+
command: <Pattern>['testpostbuild', ...commandArgumentsPattern],
585+
onRun: (List<String> command) => expect(command, expectedPostBuildCommand),
586+
),
587+
FakeCommand(command: config.uninstallCommand),
588+
FakeCommand(command: config.installCommand),
589+
FakeCommand(
590+
command: <Pattern>['testrundebug', ...commandArgumentsPattern],
591+
completer: runDebugCompleter,
592+
onRun: (List<String> command) => expect(command, expectedRunDebugCommand),
593+
stdout: 'The Dart VM service is listening on http://127.0.0.1:12345/abcd/\n',
594+
),
595+
FakeCommand(
596+
command: config.forwardPortCommand!,
597+
stdout: testConfigForwardPortSuccessOutput,
598+
),
599+
]);
600+
601+
// CustomDevice.startApp doesn't care whether we pass a prebuilt app or
602+
// buildable app as long as we pass prebuiltApplication as false
603+
final PrebuiltLinuxApp app = PrebuiltLinuxApp(executable: 'testexecutable');
604+
605+
// finally start actually testing things
606+
final CustomDevice device = CustomDevice(
607+
config: config,
608+
logger: BufferLogger.test(),
609+
processManager: processManager,
610+
);
611+
612+
await device.startApp(
613+
app,
614+
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
615+
bundleBuilder: FakeBundleBuilder(),
616+
);
617+
expect(runDebugCompleter.isCompleted, false);
618+
619+
expect(await device.stopApp(app), true);
620+
expect(runDebugCompleter.isCompleted, true);
621+
},
622+
overrides: <Type, Generator>{
623+
FileSystem: () => MemoryFileSystem.test(),
624+
ProcessManager: () => FakeProcessManager.any(),
625+
},
626+
);
627+
533628
testWithoutContext('CustomDevice screenshotting', () async {
534629
bool screenshotCommandWasExecuted = false;
535630

0 commit comments

Comments
 (0)