Skip to content

Commit 73096fd

Browse files
a-wallena-wallenjmagman
authored
[macos] add flavor options to commands in the flutter_tool (flutter#118421)
* Add new macos target configured for flavors * Rename Free App copy-Info.plist to Free App Info.plist * Remove bogus entitlements * Remove Generated.xcconfig * Audit project.pbxproj * Remove unused configs * share one info.plist * Modify scheme so that paid app works * Codesign automatic * Pipe flavor as scheme into xcodebuild * Ignore incoming flavor string * pipe flavor for flutter run to work * Add devicelab tests * Error if host and target device are same for flutter install desktop * Avoid bang (!) by promoting a local. Co-authored-by: Jenn Magder <[email protected]> * Add supportsInstall property * Override in test classes * Add install test on macOS * Refactor application_package and add tests for package directory Co-authored-by: a-wallen <[email protected]> Co-authored-by: Jenn Magder <[email protected]>
1 parent 09bd0f6 commit 73096fd

21 files changed

+173
-43
lines changed

.ci.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2668,6 +2668,20 @@ targets:
26682668
- bin/**
26692669
- .ci.yaml
26702670

2671+
- name: Mac flavors_test_macos
2672+
bringup: true
2673+
recipe: devicelab/devicelab_drone
2674+
timeout: 60
2675+
properties:
2676+
dependencies: >-
2677+
[
2678+
{"dependency": "xcode", "version": "14a5294e"},
2679+
{"dependency": "gems", "version": "v3.3.14"}
2680+
]
2681+
tags: >
2682+
["devicelab", "hostonly", "mac"]
2683+
task_name: flavors_test_macos
2684+
26712685
- name: Mac flutter_gallery_macos__compile
26722686
presubmit: false
26732687
recipe: devicelab/devicelab_drone

TESTOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@
220220
/dev/devicelab/bin/tasks/complex_layout_win_desktop__start_up.dart @yaakovschectman @flutter/desktop
221221
/dev/devicelab/bin/tasks/dart_plugin_registry_test.dart @stuartmorgan @flutter/plugin
222222
/dev/devicelab/bin/tasks/entrypoint_dart_registrant.dart @aaclarke @flutter/plugin
223+
/dev/devicelab/bin/tasks/flavors_test_macos.dart @a-wallen @flutter/desktop
223224
/dev/devicelab/bin/tasks/flutter_gallery_macos__compile.dart @a-wallen @flutter/desktop
224225
/dev/devicelab/bin/tasks/flutter_gallery_macos__start_up.dart @a-wallen @flutter/desktop
225226
/dev/devicelab/bin/tasks/flutter_gallery_win_desktop__compile.dart @yaakovschectman @flutter/desktop
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter_devicelab/framework/devices.dart';
6+
import 'package:flutter_devicelab/framework/framework.dart';
7+
import 'package:flutter_devicelab/framework/task_result.dart';
8+
import 'package:flutter_devicelab/framework/utils.dart';
9+
import 'package:flutter_devicelab/tasks/integration_tests.dart';
10+
11+
Future<void> main() async {
12+
deviceOperatingSystem = DeviceOperatingSystem.macos;
13+
await task(() async {
14+
await createFlavorsTest().call();
15+
await createIntegrationTestFlavorsTest().call();
16+
17+
await inDirectory('${flutterDirectory.path}/dev/integration_tests/flavors', () async {
18+
final StringBuffer stderr = StringBuffer();
19+
20+
await evalFlutter(
21+
'install',
22+
canFail: true,
23+
stderr: stderr,
24+
options: <String>[
25+
'--d', 'macos',
26+
'--flavor', 'free'
27+
],
28+
);
29+
30+
final String stderrString = stderr.toString();
31+
if (!stderrString.contains('Host and target are the same. Nothing to install.')) {
32+
print(stderrString);
33+
return TaskResult.failure('Installing a macOS app on macOS should no-op');
34+
}
35+
});
36+
37+
return TaskResult.success(null);
38+
});
39+
}

dev/integration_tests/flavors/macos/Runner.xcodeproj/project.pbxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,7 @@
651651
"@executable_path/../Frameworks",
652652
);
653653
PRODUCT_BUNDLE_IDENTIFIER = com.yourcompany.flavors.free;
654+
PRODUCT_FLAVOR = free;
654655
PRODUCT_NAME = "$(TARGET_NAME)";
655656
PROVISIONING_PROFILE_SPECIFIER = "";
656657
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -673,6 +674,7 @@
673674
"@executable_path/../Frameworks",
674675
);
675676
PRODUCT_BUNDLE_IDENTIFIER = com.yourcompany.flavors.free;
677+
PRODUCT_FLAVOR = free;
676678
PRODUCT_NAME = "$(TARGET_NAME)";
677679
PROVISIONING_PROFILE_SPECIFIER = "";
678680
SWIFT_VERSION = 5.0;

packages/flutter_tools/bin/macos_assemble.sh

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,32 @@ EchoError() {
1717
echo "$@" 1>&2
1818
}
1919

20+
ParseFlutterBuildMode() {
21+
# Use FLUTTER_BUILD_MODE if it's set, otherwise use the Xcode build configuration name
22+
# This means that if someone wants to use an Xcode build config other than Debug/Profile/Release,
23+
# they _must_ set FLUTTER_BUILD_MODE so we know what type of artifact to build.
24+
local build_mode="$(echo "${FLUTTER_BUILD_MODE:-${CONFIGURATION}}" | tr "[:upper:]" "[:lower:]")"
25+
26+
case "$build_mode" in
27+
*release*) build_mode="release";;
28+
*profile*) build_mode="profile";;
29+
*debug*) build_mode="debug";;
30+
*)
31+
EchoError "========================================================================"
32+
EchoError "ERROR: Unknown FLUTTER_BUILD_MODE: ${build_mode}."
33+
EchoError "Valid values are 'Debug', 'Profile', or 'Release' (case insensitive)."
34+
EchoError "This is controlled by the FLUTTER_BUILD_MODE environment variable."
35+
EchoError "If that is not set, the CONFIGURATION environment variable is used."
36+
EchoError ""
37+
EchoError "You can fix this by either adding an appropriately named build"
38+
EchoError "configuration, or adding an appropriate value for FLUTTER_BUILD_MODE to the"
39+
EchoError ".xcconfig file for the current build configuration (${CONFIGURATION})."
40+
EchoError "========================================================================"
41+
exit -1;;
42+
esac
43+
echo "${build_mode}"
44+
}
45+
2046
BuildApp() {
2147
# Set the working directory to the project root
2248
local project_path="${SOURCE_ROOT}/.."
@@ -28,8 +54,10 @@ BuildApp() {
2854
target_path="${FLUTTER_TARGET}"
2955
fi
3056

31-
# Set the build mode
32-
local build_mode="$(echo "${FLUTTER_BUILD_MODE:-${CONFIGURATION}}" | tr "[:upper:]" "[:lower:]")"
57+
# Use FLUTTER_BUILD_MODE if it's set, otherwise use the Xcode build configuration name
58+
# This means that if someone wants to use an Xcode build config other than Debug/Profile/Release,
59+
# they _must_ set FLUTTER_BUILD_MODE so we know what type of artifact to build.
60+
local build_mode="$(ParseFlutterBuildMode)"
3361

3462
if [[ -n "$LOCAL_ENGINE" ]]; then
3563
if [[ $(echo "$LOCAL_ENGINE" | tr "[:upper:]" "[:lower:]") != *"$build_mode"* ]]; then

packages/flutter_tools/lib/src/commands/install.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ class InstallCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts
5050
if (device == null) {
5151
throwToolExit('No target device found');
5252
}
53+
54+
if (!device!.supportsInstall) {
55+
throwToolExit('Host and target are the same. Nothing to install.');
56+
}
57+
5358
if (userIdentifier != null && device is! AndroidDevice) {
5459
throwToolExit('--${FlutterOptions.kDeviceUser} is only supported for Android');
5560
}

packages/flutter_tools/lib/src/desktop_device.dart

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ abstract class DesktopDevice extends Device {
104104
@override
105105
void clearLogs() {}
106106

107+
@override
108+
bool get supportsInstall => false;
109+
107110
@override
108111
Future<LaunchResult> startApp(
109112
ApplicationPackage package, {
@@ -123,9 +126,9 @@ abstract class DesktopDevice extends Device {
123126
}
124127

125128
// Ensure that the executable is locatable.
126-
final BuildMode buildMode = debuggingOptions.buildInfo.mode;
129+
final BuildInfo buildInfo = debuggingOptions.buildInfo;
127130
final bool traceStartup = platformArgs['trace-startup'] as bool? ?? false;
128-
final String? executable = executablePathForDevice(package, buildMode);
131+
final String? executable = executablePathForDevice(package, buildInfo);
129132
if (executable == null) {
130133
_logger.printError('Unable to find executable to run');
131134
return LaunchResult.failed();
@@ -161,7 +164,7 @@ abstract class DesktopDevice extends Device {
161164
try {
162165
final Uri? observatoryUri = await observatoryDiscovery.uri;
163166
if (observatoryUri != null) {
164-
onAttached(package, buildMode, process);
167+
onAttached(package, buildInfo, process);
165168
return LaunchResult.succeeded(observatoryUri: observatoryUri);
166169
}
167170
_logger.printError(
@@ -203,11 +206,11 @@ abstract class DesktopDevice extends Device {
203206

204207
/// Returns the path to the executable to run for [package] on this device for
205208
/// the given [buildMode].
206-
String? executablePathForDevice(ApplicationPackage package, BuildMode buildMode);
209+
String? executablePathForDevice(ApplicationPackage package, BuildInfo buildInfo);
207210

208211
/// Called after a process is attached, allowing any device-specific extra
209212
/// steps to be run.
210-
void onAttached(ApplicationPackage package, BuildMode buildMode, Process process) {}
213+
void onAttached(ApplicationPackage package, BuildInfo buildInfo, Process process) {}
211214

212215
/// Computes a set of environment variables used to pass debugging information
213216
/// to the engine without interfering with application level command line

packages/flutter_tools/lib/src/device.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,10 @@ abstract class Device {
579579
/// Whether the device supports the '--fast-start' development mode.
580580
bool get supportsFastStart => false;
581581

582+
/// Whether this device supports the installation of a flutter app via
583+
/// `flutter install`.
584+
bool get supportsInstall => true;
585+
582586
/// Stop an app package on the current device.
583587
///
584588
/// Specify [userIdentifier] to stop app installed to a profile (Android only).

packages/flutter_tools/lib/src/linux/linux_device.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ class LinuxDevice extends DesktopDevice {
7070
}
7171

7272
@override
73-
String executablePathForDevice(covariant LinuxApp package, BuildMode buildMode) {
74-
return package.executable(buildMode);
73+
String executablePathForDevice(covariant LinuxApp package, BuildInfo buildInfo) {
74+
return package.executable(buildInfo.mode);
7575
}
7676
}
7777

packages/flutter_tools/lib/src/macos/application_package.dart

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,9 @@ abstract class MacOSApp extends ApplicationPackage {
106106
@override
107107
String get displayName => id;
108108

109-
String? applicationBundle(BuildMode buildMode);
109+
String? applicationBundle(BuildInfo buildInfo);
110110

111-
String? executable(BuildMode buildMode);
111+
String? executable(BuildInfo buildInfo);
112112
}
113113

114114
class PrebuiltMacOSApp extends MacOSApp implements PrebuiltApplicationPackage {
@@ -135,10 +135,10 @@ class PrebuiltMacOSApp extends MacOSApp implements PrebuiltApplicationPackage {
135135
String get name => bundleName;
136136

137137
@override
138-
String? applicationBundle(BuildMode buildMode) => uncompressedBundle.path;
138+
String? applicationBundle(BuildInfo buildInfo) => uncompressedBundle.path;
139139

140140
@override
141-
String? executable(BuildMode buildMode) => _executable;
141+
String? executable(BuildInfo buildInfo) => _executable;
142142

143143
/// A [File] or [Directory] pointing to the application bundle.
144144
///
@@ -156,23 +156,30 @@ class BuildableMacOSApp extends MacOSApp {
156156
String get name => 'macOS';
157157

158158
@override
159-
String? applicationBundle(BuildMode buildMode) {
159+
String? applicationBundle(BuildInfo buildInfo) {
160160
final File appBundleNameFile = project.nameFile;
161161
if (!appBundleNameFile.existsSync()) {
162162
globals.printError('Unable to find app name. ${appBundleNameFile.path} does not exist');
163163
return null;
164164
}
165+
165166
return globals.fs.path.join(
166167
getMacOSBuildDirectory(),
167168
'Build',
168169
'Products',
169-
sentenceCase(getNameForBuildMode(buildMode)),
170+
bundleDirectory(buildInfo),
170171
appBundleNameFile.readAsStringSync().trim());
171172
}
172173

174+
String bundleDirectory(BuildInfo buildInfo) {
175+
return sentenceCase(buildInfo.mode.name) + (buildInfo.flavor != null
176+
? ' ${sentenceCase(buildInfo.flavor!)}'
177+
: '');
178+
}
179+
173180
@override
174-
String? executable(BuildMode buildMode) {
175-
final String? directory = applicationBundle(buildMode);
181+
String? executable(BuildInfo buildInfo) {
182+
final String? directory = applicationBundle(buildInfo);
176183
if (directory == null) {
177184
return null;
178185
}

packages/flutter_tools/lib/src/macos/build_macos.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ Future<void> buildMacOS({
114114
'xcodebuild',
115115
'-workspace', xcodeWorkspace.path,
116116
'-configuration', configuration,
117-
'-scheme', 'Runner',
117+
'-scheme', scheme,
118118
'-derivedDataPath', flutterBuildDir.absolute.path,
119119
'-destination', 'platform=macOS',
120120
'OBJROOT=${globals.fs.path.join(flutterBuildDir.absolute.path, 'Build', 'Intermediates.noindex')}',

packages/flutter_tools/lib/src/macos/macos_device.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,17 +78,17 @@ class MacOSDevice extends DesktopDevice {
7878
}
7979

8080
@override
81-
String? executablePathForDevice(covariant MacOSApp package, BuildMode buildMode) {
82-
return package.executable(buildMode);
81+
String? executablePathForDevice(covariant MacOSApp package, BuildInfo buildInfo) {
82+
return package.executable(buildInfo);
8383
}
8484

8585
@override
86-
void onAttached(covariant MacOSApp package, BuildMode buildMode, Process process) {
86+
void onAttached(covariant MacOSApp package, BuildInfo buildInfo, Process process) {
8787
// Bring app to foreground. Ideally this would be done post-launch rather
8888
// than post-attach, since this won't run for release builds, but there's
8989
// no general-purpose way of knowing when a process is far enough along in
9090
// the launch process for 'open' to foreground it.
91-
final String? applicationBundle = package.applicationBundle(buildMode);
91+
final String? applicationBundle = package.applicationBundle(buildInfo);
9292
if (applicationBundle == null) {
9393
_logger.printError('Failed to foreground app; application bundle not found');
9494
return;

packages/flutter_tools/lib/src/macos/macos_ipad_device.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class MacOSDesignedForIPadDevice extends DesktopDevice {
5454
}
5555

5656
@override
57-
String? executablePathForDevice(ApplicationPackage package, BuildMode buildMode) => null;
57+
String? executablePathForDevice(ApplicationPackage package, BuildInfo buildInfo) => null;
5858

5959
@override
6060
Future<LaunchResult> startApp(

packages/flutter_tools/lib/src/windows/windows_device.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ class WindowsDevice extends DesktopDevice {
6161
}
6262

6363
@override
64-
String executablePathForDevice(covariant WindowsApp package, BuildMode buildMode) {
65-
return package.executable(buildMode);
64+
String executablePathForDevice(covariant WindowsApp package, BuildInfo buildInfo) {
65+
return package.executable(buildInfo.mode);
6666
}
6767
}
6868

packages/flutter_tools/test/commands.shard/hermetic/install_test.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,12 @@ class FakeIOSDevice extends Fake implements IOSDevice {
157157
IOSApp app, {
158158
String? userIdentifier,
159159
}) async => true;
160+
161+
@override
162+
String get name => 'iOS';
163+
164+
@override
165+
bool get supportsInstall => true;
160166
}
161167

162168
// Unfortunately Device, despite not being immutable, has an `operator ==`.
@@ -177,4 +183,10 @@ class FakeAndroidDevice extends Fake implements AndroidDevice {
177183
AndroidApk app, {
178184
String? userIdentifier,
179185
}) async => true;
186+
187+
@override
188+
String get name => 'Android';
189+
190+
@override
191+
bool get supportsInstall => true;
180192
}

packages/flutter_tools/test/general.shard/desktop_device_test.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ void main() {
8686
),
8787
]);
8888
final FakeDesktopDevice device = setUpDesktopDevice(processManager: processManager, fileSystem: fileSystem);
89-
final String? executableName = device.executablePathForDevice(FakeApplicationPackage(), BuildMode.debug);
89+
final String? executableName = device.executablePathForDevice(FakeApplicationPackage(), BuildInfo.debug);
9090
fileSystem.file(executableName).writeAsStringSync('\n');
9191
final FakeApplicationPackage package = FakeApplicationPackage();
9292
final LaunchResult result = await device.startApp(
@@ -367,11 +367,11 @@ class FakeDesktopDevice extends DesktopDevice {
367367

368368
// Dummy implementation that just returns the build mode name.
369369
@override
370-
String? executablePathForDevice(ApplicationPackage package, BuildMode buildMode) {
370+
String? executablePathForDevice(ApplicationPackage package, BuildInfo buildInfo) {
371371
if (nullExecutablePathForDevice) {
372372
return null;
373373
}
374-
return buildMode == null ? 'null' : getNameForBuildMode(buildMode);
374+
return buildInfo == null ? 'null' : getNameForBuildMode(buildInfo.mode);
375375
}
376376
}
377377

packages/flutter_tools/test/general.shard/linux/linux_device_test.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,9 @@ void main() {
154154
operatingSystemUtils: FakeOperatingSystemUtils(),
155155
);
156156

157-
expect(device.executablePathForDevice(mockApp, BuildMode.debug), 'debug/executable');
158-
expect(device.executablePathForDevice(mockApp, BuildMode.profile), 'profile/executable');
159-
expect(device.executablePathForDevice(mockApp, BuildMode.release), 'release/executable');
157+
expect(device.executablePathForDevice(mockApp, BuildInfo.debug), 'debug/executable');
158+
expect(device.executablePathForDevice(mockApp, BuildInfo.profile), 'profile/executable');
159+
expect(device.executablePathForDevice(mockApp, BuildInfo.release), 'release/executable');
160160
});
161161
}
162162

0 commit comments

Comments
 (0)