Pass environment variables through to xcodebuild (#43553)
diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart
index e29c1df..d872f19 100644
--- a/packages/flutter_tools/lib/src/ios/mac.dart
+++ b/packages/flutter_tools/lib/src/ios/mac.dart
@@ -464,6 +464,7 @@
// e.g. `flutter build bundle`.
buildCommands.add('FLUTTER_SUPPRESS_ANALYTICS=true');
buildCommands.add('COMPILER_INDEX_STORE_ENABLE=NO');
+ buildCommands.addAll(environmentVariablesAsXcodeBuildSettings());
final Stopwatch sw = Stopwatch()..start();
initialBuildStatus = logger.startProgress('Running Xcode build...', timeout: timeoutConfiguration.fastOperation);
diff --git a/packages/flutter_tools/lib/src/ios/xcodeproj.dart b/packages/flutter_tools/lib/src/ios/xcodeproj.dart
index 9244719..40bdd7d 100644
--- a/packages/flutter_tools/lib/src/ios/xcodeproj.dart
+++ b/packages/flutter_tools/lib/src/ios/xcodeproj.dart
@@ -293,9 +293,10 @@
'-target',
target,
'-showBuildSettings',
+ ...environmentVariablesAsXcodeBuildSettings()
];
try {
- // showBuildSettings is reported to ocassionally timeout. Here, we give it
+ // showBuildSettings is reported to occasionally timeout. Here, we give it
// a lot of wiggle room (locally on Flutter Gallery, this takes ~1s).
// When there is a timeout, we retry once.
final RunResult result = await processUtils.run(
@@ -329,6 +330,7 @@
scheme,
'-quiet',
'clean',
+ ...environmentVariablesAsXcodeBuildSettings()
], workingDirectory: fs.currentDirectory.path);
}
@@ -354,6 +356,21 @@
}
}
+/// Environment variables prefixed by FLUTTER_XCODE_ will be passed as build configurations to xcodebuild.
+/// This allows developers to pass arbitrary build settings in without the tool needing to make a flag
+/// for or be aware of each one. This could be used to set code signing build settings in a CI
+/// environment without requiring settings changes in the Xcode project.
+List<String> environmentVariablesAsXcodeBuildSettings() {
+ const String xcodeBuildSettingPrefix = 'FLUTTER_XCODE_';
+ return platform.environment.entries.where((MapEntry<String, String> mapEntry) {
+ return mapEntry.key.startsWith(xcodeBuildSettingPrefix);
+ }).expand<String>((MapEntry<String, String> mapEntry) {
+ // Remove FLUTTER_XCODE_ prefix from the environment variable to get the build setting.
+ final String trimmedBuildSettingKey = mapEntry.key.substring(xcodeBuildSettingPrefix.length);
+ return <String>['$trimmedBuildSettingKey=${mapEntry.value}'];
+ }).toList();
+}
+
Map<String, String> parseXcodeBuildSettings(String showBuildSettingsOutput) {
final Map<String, String> settings = <String, String>{};
for (Match match in showBuildSettingsOutput.split('\n').map<Match>(_settingExpr.firstMatch)) {
diff --git a/packages/flutter_tools/lib/src/macos/build_macos.dart b/packages/flutter_tools/lib/src/macos/build_macos.dart
index 69571bc..d9b16f3 100644
--- a/packages/flutter_tools/lib/src/macos/build_macos.dart
+++ b/packages/flutter_tools/lib/src/macos/build_macos.dart
@@ -79,6 +79,7 @@
'OBJROOT=${fs.path.join(flutterBuildDir.absolute.path, 'Build', 'Intermediates.noindex')}',
'SYMROOT=${fs.path.join(flutterBuildDir.absolute.path, 'Build', 'Products')}',
'COMPILER_INDEX_STORE_ENABLE=NO',
+ ...environmentVariablesAsXcodeBuildSettings()
], trace: true);
} finally {
status.cancel();
diff --git a/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart b/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart
index b362ca0..bbaf067 100644
--- a/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart
+++ b/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart
@@ -23,7 +23,7 @@
const String xcodebuild = '/usr/bin/xcodebuild';
void main() {
- group('xcodebuild versioning', () {
+ group('xcodebuild commands', () {
mocks.MockProcessManager mockProcessManager;
XcodeProjectInterpreter xcodeProjectInterpreter;
FakePlatform macOS;
@@ -172,6 +172,54 @@
FileSystem: () => fs,
ProcessManager: () => mockProcessManager,
});
+
+ testUsingOsxContext('build settings contains Flutter Xcode environment variables', () async {
+ macOS.environment = Map<String, String>.unmodifiable(<String, String>{
+ 'FLUTTER_XCODE_CODE_SIGN_STYLE': 'Manual',
+ 'FLUTTER_XCODE_ARCHS': 'arm64'
+ });
+ when(mockProcessManager.runSync(<String>[
+ xcodebuild,
+ '-project',
+ macOS.pathSeparator,
+ '-target',
+ '',
+ '-showBuildSettings',
+ 'CODE_SIGN_STYLE=Manual',
+ 'ARCHS=arm64'
+ ],
+ workingDirectory: anyNamed('workingDirectory'),
+ environment: anyNamed('environment')))
+ .thenReturn(ProcessResult(1, 0, '', ''));
+ expect(await xcodeProjectInterpreter.getBuildSettings('', ''), const <String, String>{});
+ });
+
+ testUsingOsxContext('clean contains Flutter Xcode environment variables', () async {
+ macOS.environment = Map<String, String>.unmodifiable(<String, String>{
+ 'FLUTTER_XCODE_CODE_SIGN_STYLE': 'Manual',
+ 'FLUTTER_XCODE_ARCHS': 'arm64'
+ });
+ when(mockProcessManager.runSync(
+ any,
+ workingDirectory: anyNamed('workingDirectory')))
+ .thenReturn(ProcessResult(1, 0, '', ''));
+ xcodeProjectInterpreter.cleanWorkspace('workspace_path', 'Runner');
+ final List<dynamic> captured = verify(mockProcessManager.runSync(
+ captureAny,
+ workingDirectory: anyNamed('workingDirectory'),
+ environment: anyNamed('environment'))).captured;
+ expect(captured.first, <String>[
+ xcodebuild,
+ '-workspace',
+ 'workspace_path',
+ '-scheme',
+ 'Runner',
+ '-quiet',
+ 'clean',
+ 'CODE_SIGN_STYLE=Manual',
+ 'ARCHS=arm64'
+ ]);
+ });
});
group('xcodebuild -list', () {
@@ -338,6 +386,27 @@
});
});
+ group('environmentVariablesAsXcodeBuildSettings', () {
+ FakePlatform platform;
+
+ setUp(() {
+ platform = fakePlatform('ignored');
+ });
+
+ testUsingContext('environment variables as Xcode build settings', () {
+ platform.environment = Map<String, String>.unmodifiable(<String, String>{
+ 'Ignored': 'Bogus',
+ 'FLUTTER_NOT_XCODE': 'Bogus',
+ 'FLUTTER_XCODE_CODE_SIGN_STYLE': 'Manual',
+ 'FLUTTER_XCODE_ARCHS': 'arm64'
+ });
+ final List<String> environmentVariablesAsBuildSettings = environmentVariablesAsXcodeBuildSettings();
+ expect(environmentVariablesAsBuildSettings, <String>['CODE_SIGN_STYLE=Manual', 'ARCHS=arm64']);
+ }, overrides: <Type, Generator>{
+ Platform: () => platform
+ });
+ });
+
group('updateGeneratedXcodeProperties', () {
MockLocalEngineArtifacts mockArtifacts;
MockProcessManager mockProcessManager;