Skip to content

Commit b52297d

Browse files
authored
Fix Xcode 15 build failure due to DT_TOOLCHAIN_DIR (#132803)
Starting in Xcode 15, when building macOS, DT_TOOLCHAIN_DIR cannot be used to evaluate LD_RUNPATH_SEARCH_PATHS or LIBRARY_SEARCH_PATHS. `xcodebuild` error message recommend using TOOLCHAIN_DIR instead. Since Xcode 15 isn't in CI, I tested it in a one-off `led` test: * [Pre-fix failure](https://luci-milo.appspot.com/raw/build/logs.chromium.org/flutter/led/vashworth_google.com/04e485a0b152a0720f5e561266f7a6e4fb64fc76227fcacc95b67486ae2771e7/+/build.proto) * [Post-fix success](https://luci-milo.appspot.com/raw/build/logs.chromium.org/flutter/led/vashworth_google.com/d454a3e181e1a97692bdc1fcc197738fe04e4acf1cb20026fd040fd78513f3b0/+/build.proto) Fixes #132755.
1 parent 312ef54 commit b52297d

4 files changed

Lines changed: 222 additions & 3 deletions

File tree

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import '../build_info.dart';
1919
import '../cache.dart';
2020
import '../ios/xcodeproj.dart';
2121
import '../migrations/cocoapods_script_symlink.dart';
22+
import '../migrations/cocoapods_toolchain_directory_migration.dart';
2223
import '../reporting/reporting.dart';
2324
import '../xcode_project.dart';
2425

@@ -172,6 +173,11 @@ class CocoaPods {
172173
// This migrator works around a CocoaPods bug, and should be run after `pod install` is run.
173174
final ProjectMigration postPodMigration = ProjectMigration(<ProjectMigrator>[
174175
CocoaPodsScriptReadlink(xcodeProject, _xcodeProjectInterpreter, _logger),
176+
CocoaPodsToolchainDirectoryMigration(
177+
xcodeProject,
178+
_xcodeProjectInterpreter,
179+
_logger,
180+
),
175181
]);
176182
postPodMigration.run();
177183

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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 '../base/file_system.dart';
6+
import '../base/project_migrator.dart';
7+
import '../base/version.dart';
8+
import '../ios/xcodeproj.dart';
9+
import '../xcode_project.dart';
10+
11+
/// Starting in Xcode 15, when building macOS, DT_TOOLCHAIN_DIR cannot be used
12+
/// to evaluate LD_RUNPATH_SEARCH_PATHS or LIBRARY_SEARCH_PATHS. `xcodebuild`
13+
/// error message recommend using TOOLCHAIN_DIR instead.
14+
///
15+
/// This has been fixed upstream in CocoaPods, but migrate a copy of their
16+
/// workaround so users don't need to update.
17+
class CocoaPodsToolchainDirectoryMigration extends ProjectMigrator {
18+
CocoaPodsToolchainDirectoryMigration(
19+
XcodeBasedProject project,
20+
XcodeProjectInterpreter xcodeProjectInterpreter,
21+
super.logger,
22+
) : _podRunnerTargetSupportFiles = project.podRunnerTargetSupportFiles,
23+
_xcodeProjectInterpreter = xcodeProjectInterpreter;
24+
25+
final Directory _podRunnerTargetSupportFiles;
26+
final XcodeProjectInterpreter _xcodeProjectInterpreter;
27+
28+
@override
29+
void migrate() {
30+
if (!_podRunnerTargetSupportFiles.existsSync()) {
31+
logger.printTrace('CocoaPods Pods-Runner Target Support Files not found, skipping TOOLCHAIN_DIR workaround.');
32+
return;
33+
}
34+
35+
final Version? version = _xcodeProjectInterpreter.version;
36+
37+
// If Xcode not installed or less than 15, skip this migration.
38+
if (version == null || version < Version(15, 0, 0)) {
39+
logger.printTrace('Detected Xcode version is $version, below 15.0, skipping TOOLCHAIN_DIR workaround.');
40+
return;
41+
}
42+
43+
final List<FileSystemEntity> files = _podRunnerTargetSupportFiles.listSync();
44+
for (final FileSystemEntity file in files) {
45+
if (file.basename.endsWith('xcconfig') && file is File) {
46+
processFileLines(file);
47+
}
48+
}
49+
}
50+
51+
@override
52+
String? migrateLine(String line) {
53+
final String trimmedString = line.trim();
54+
if (trimmedString.startsWith('LD_RUNPATH_SEARCH_PATHS') || trimmedString.startsWith('LIBRARY_SEARCH_PATHS')) {
55+
const String originalReadLinkLine = r'{DT_TOOLCHAIN_DIR}';
56+
const String replacementReadLinkLine = r'{TOOLCHAIN_DIR}';
57+
58+
return line.replaceAll(originalReadLinkLine, replacementReadLinkLine);
59+
}
60+
return line;
61+
}
62+
}

packages/flutter_tools/lib/src/xcode_project.dart

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,14 @@ abstract class XcodeBasedProject extends FlutterProjectPlatform {
104104
File get podManifestLock => hostAppRoot.childDirectory('Pods').childFile('Manifest.lock');
105105

106106
/// The CocoaPods generated 'Pods-Runner-frameworks.sh'.
107-
File get podRunnerFrameworksScript => hostAppRoot
107+
File get podRunnerFrameworksScript => podRunnerTargetSupportFiles
108+
.childFile('Pods-Runner-frameworks.sh');
109+
110+
/// The CocoaPods generated directory 'Pods-Runner'.
111+
Directory get podRunnerTargetSupportFiles => hostAppRoot
108112
.childDirectory('Pods')
109113
.childDirectory('Target Support Files')
110-
.childDirectory('Pods-Runner')
111-
.childFile('Pods-Runner-frameworks.sh');
114+
.childDirectory('Pods-Runner');
112115
}
113116

114117
/// Represents the iOS sub-project of a Flutter project.

packages/flutter_tools/test/general.shard/ios/ios_project_migration_test.dart

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import 'package:flutter_tools/src/ios/migrations/remove_framework_link_and_embed
1616
import 'package:flutter_tools/src/ios/migrations/xcode_build_system_migration.dart';
1717
import 'package:flutter_tools/src/ios/xcodeproj.dart';
1818
import 'package:flutter_tools/src/migrations/cocoapods_script_symlink.dart';
19+
import 'package:flutter_tools/src/migrations/cocoapods_toolchain_directory_migration.dart';
1920
import 'package:flutter_tools/src/migrations/xcode_project_object_version_migration.dart';
2021
import 'package:flutter_tools/src/migrations/xcode_script_build_phase_migration.dart';
2122
import 'package:flutter_tools/src/migrations/xcode_thin_binary_build_phase_input_paths_migration.dart';
@@ -1003,6 +1004,150 @@ platform :ios, '11.0'
10031004
expect(testLogger.statusText, contains('Upgrading Pods-Runner-frameworks.sh'));
10041005
});
10051006
});
1007+
1008+
group('Cocoapods migrate toolchain directory', () {
1009+
late MemoryFileSystem memoryFileSystem;
1010+
late BufferLogger testLogger;
1011+
late FakeIosProject project;
1012+
late Directory podRunnerTargetSupportFiles;
1013+
late ProcessManager processManager;
1014+
late XcodeProjectInterpreter xcode15ProjectInterpreter;
1015+
1016+
setUp(() {
1017+
memoryFileSystem = MemoryFileSystem();
1018+
podRunnerTargetSupportFiles = memoryFileSystem.directory('Pods-Runner');
1019+
testLogger = BufferLogger.test();
1020+
project = FakeIosProject();
1021+
processManager = FakeProcessManager.any();
1022+
xcode15ProjectInterpreter = XcodeProjectInterpreter.test(processManager: processManager, version: Version(15, 0, 0));
1023+
project.podRunnerTargetSupportFiles = podRunnerTargetSupportFiles;
1024+
});
1025+
1026+
testWithoutContext('skip if directory is missing', () {
1027+
final CocoaPodsToolchainDirectoryMigration iosProjectMigration = CocoaPodsToolchainDirectoryMigration(
1028+
project,
1029+
xcode15ProjectInterpreter,
1030+
testLogger,
1031+
);
1032+
iosProjectMigration.migrate();
1033+
expect(podRunnerTargetSupportFiles.existsSync(), isFalse);
1034+
1035+
expect(testLogger.traceText, contains('CocoaPods Pods-Runner Target Support Files not found'));
1036+
expect(testLogger.statusText, isEmpty);
1037+
});
1038+
1039+
testWithoutContext('skip if xcconfig files are missing', () {
1040+
podRunnerTargetSupportFiles.createSync();
1041+
final CocoaPodsToolchainDirectoryMigration iosProjectMigration = CocoaPodsToolchainDirectoryMigration(
1042+
project,
1043+
xcode15ProjectInterpreter,
1044+
testLogger,
1045+
);
1046+
iosProjectMigration.migrate();
1047+
expect(podRunnerTargetSupportFiles.existsSync(), isTrue);
1048+
expect(testLogger.traceText, isEmpty);
1049+
expect(testLogger.statusText, isEmpty);
1050+
});
1051+
1052+
testWithoutContext('skip if nothing to upgrade', () {
1053+
podRunnerTargetSupportFiles.createSync();
1054+
final File debugConfig = podRunnerTargetSupportFiles.childFile('Pods-Runner.debug.xcconfig');
1055+
const String contents = r'''
1056+
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/../Frameworks' '@loader_path/Frameworks' "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
1057+
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
1058+
''';
1059+
debugConfig.writeAsStringSync(contents);
1060+
1061+
final File profileConfig = podRunnerTargetSupportFiles.childFile('Pods-Runner.profile.xcconfig');
1062+
profileConfig.writeAsStringSync(contents);
1063+
1064+
final File releaseConfig = podRunnerTargetSupportFiles.childFile('Pods-Runner.release.xcconfig');
1065+
releaseConfig.writeAsStringSync(contents);
1066+
1067+
final CocoaPodsToolchainDirectoryMigration iosProjectMigration = CocoaPodsToolchainDirectoryMigration(
1068+
project,
1069+
xcode15ProjectInterpreter,
1070+
testLogger,
1071+
);
1072+
iosProjectMigration.migrate();
1073+
expect(debugConfig.existsSync(), isTrue);
1074+
expect(testLogger.traceText, isEmpty);
1075+
expect(testLogger.statusText, isEmpty);
1076+
});
1077+
1078+
testWithoutContext('skipped if Xcode version below 15', () {
1079+
podRunnerTargetSupportFiles.createSync();
1080+
final File debugConfig = podRunnerTargetSupportFiles.childFile('Pods-Runner.debug.xcconfig');
1081+
const String contents = r'''
1082+
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/../Frameworks' '@loader_path/Frameworks' "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
1083+
LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
1084+
''';
1085+
debugConfig.writeAsStringSync(contents);
1086+
1087+
final File profileConfig = podRunnerTargetSupportFiles.childFile('Pods-Runner.profile.xcconfig');
1088+
profileConfig.writeAsStringSync(contents);
1089+
1090+
final File releaseConfig = podRunnerTargetSupportFiles.childFile('Pods-Runner.release.xcconfig');
1091+
releaseConfig.writeAsStringSync(contents);
1092+
1093+
final XcodeProjectInterpreter xcode14ProjectInterpreter = XcodeProjectInterpreter.test(
1094+
processManager: processManager,
1095+
version: Version(14, 0, 0),
1096+
);
1097+
1098+
final CocoaPodsToolchainDirectoryMigration iosProjectMigration = CocoaPodsToolchainDirectoryMigration(
1099+
project,
1100+
xcode14ProjectInterpreter,
1101+
testLogger,
1102+
);
1103+
iosProjectMigration.migrate();
1104+
expect(debugConfig.existsSync(), isTrue);
1105+
expect(testLogger.traceText, contains('Detected Xcode version is 14.0.0, below 15.0'));
1106+
expect(testLogger.statusText, isEmpty);
1107+
});
1108+
1109+
testWithoutContext('Xcode project is migrated and ignores leading whitespace', () {
1110+
podRunnerTargetSupportFiles.createSync();
1111+
final File debugConfig = podRunnerTargetSupportFiles.childFile('Pods-Runner.debug.xcconfig');
1112+
const String contents = r'''
1113+
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/../Frameworks' '@loader_path/Frameworks' "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
1114+
LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
1115+
''';
1116+
debugConfig.writeAsStringSync(contents);
1117+
1118+
final File profileConfig = podRunnerTargetSupportFiles.childFile('Pods-Runner.profile.xcconfig');
1119+
profileConfig.writeAsStringSync(contents);
1120+
1121+
final File releaseConfig = podRunnerTargetSupportFiles.childFile('Pods-Runner.release.xcconfig');
1122+
releaseConfig.writeAsStringSync(contents);
1123+
1124+
final CocoaPodsToolchainDirectoryMigration iosProjectMigration = CocoaPodsToolchainDirectoryMigration(
1125+
project,
1126+
xcode15ProjectInterpreter,
1127+
testLogger,
1128+
);
1129+
iosProjectMigration.migrate();
1130+
1131+
expect(debugConfig.existsSync(), isTrue);
1132+
expect(debugConfig.readAsStringSync(), r'''
1133+
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/../Frameworks' '@loader_path/Frameworks' "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
1134+
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
1135+
''');
1136+
expect(profileConfig.existsSync(), isTrue);
1137+
expect(profileConfig.readAsStringSync(), r'''
1138+
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/../Frameworks' '@loader_path/Frameworks' "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
1139+
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
1140+
''');
1141+
expect(releaseConfig.existsSync(), isTrue);
1142+
expect(releaseConfig.readAsStringSync(), r'''
1143+
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/../Frameworks' '@loader_path/Frameworks' "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
1144+
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
1145+
''');
1146+
expect(testLogger.statusText, contains('Upgrading Pods-Runner.debug.xcconfig'));
1147+
expect(testLogger.statusText, contains('Upgrading Pods-Runner.profile.xcconfig'));
1148+
expect(testLogger.statusText, contains('Upgrading Pods-Runner.release.xcconfig'));
1149+
});
1150+
});
10061151
});
10071152

10081153
group('update Xcode script build phase', () {
@@ -1239,6 +1384,9 @@ class FakeIosProject extends Fake implements IosProject {
12391384

12401385
@override
12411386
File podRunnerFrameworksScript = MemoryFileSystem.test().file('podRunnerFrameworksScript');
1387+
1388+
@override
1389+
Directory podRunnerTargetSupportFiles = MemoryFileSystem.test().directory('Pods-Runner');
12421390
}
12431391

12441392
class FakeIOSMigrator extends ProjectMigrator {

0 commit comments

Comments
 (0)