@@ -108,21 +108,45 @@ load(
108108)
109109load ("//swift/toolchains/config:tool_config.bzl" , "ToolConfigInfo" )
110110
111- def _platform_developer_framework_dir (
112- apple_toolchain ,
113- target_triple ):
111+ visibility ("public" )
112+
113+ # These are symlink locations known to be used by xcode-select in various Xcode
114+ # and macOS versions to point to the selected `Developer` directory.
115+ _DEVELOPER_DIR_SYMLINKS = [
116+ "/private/var/select/developer_dir" ,
117+ "/var/db/xcode_select_link" ,
118+ ]
119+
120+ def _swift_developer_lib_dir (platform_framework_dir ):
121+ """Returns the directory containing extra Swift developer libraries.
122+
123+ Args:
124+ platform_framework_dir: The developer platform framework directory for
125+ the current platform.
126+
127+ Returns:
128+ The directory containing extra Swift-specific development libraries and
129+ swiftmodules.
130+ """
131+ return paths .join (
132+ paths .dirname (paths .dirname (platform_framework_dir )),
133+ "usr" ,
134+ "lib" ,
135+ )
136+
137+ def _platform_developer_framework_dir (developer_dir , target_triple ):
114138 """Returns the Developer framework directory for the platform.
115139
116140 Args:
117- apple_toolchain : The `apple_common.apple_toolchain()` object .
141+ developer_dir : The developer directory .
118142 target_triple: The triple of the platform being targeted.
119143
120144 Returns:
121145 The path to the Developer framework directory for the platform if one
122146 exists, otherwise `None`.
123147 """
124148 return paths .join (
125- apple_toolchain . developer_dir () ,
149+ developer_dir ,
126150 "Platforms" ,
127151 "{}.platform" .format (
128152 target_triples .bazel_apple_platform (target_triple ).name_in_plist ,
@@ -150,11 +174,46 @@ def _sdk_developer_framework_dir(apple_toolchain, target_triple):
150174
151175 return paths .join (apple_toolchain .sdk_dir (), "Developer/Library/Frameworks" )
152176
177+ def _swift_compatibility_lib_paths (* , target_triple , xcode_config ):
178+ """Returns the paths to the Swift compatibility libraries in the toolchain.
179+
180+ The returned paths are relative to the `Developer` directory; they do not
181+ contain the Bazel placeholder that would be substituted with the actual
182+ `Developer` directory at execution time.
183+
184+ Args:
185+ target_triple: The target triple `struct`.
186+ xcode_config: The `apple_common.XcodeVersionConfig` provider.
187+
188+ Returns:
189+ A list of paths to the Swift compatibility libraries in the toolchain.
190+ """
191+
192+ # We choose to ignore swift-5.0 and swift-5.5 because they correspond to
193+ # such old OS versions that nobody is targeting with rules like
194+ # `swift_binary` and `swift_test`. (And if they were, they would already be
195+ # broken before this addition.)
196+ versions = []
197+ if _is_xcode_at_least_version (xcode_config , "26.0" ):
198+ versions .append ("6.2" )
199+
200+ platform_name = target_triples .platform_name_for_swift (target_triple )
201+ return [
202+ "Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-{}/{}" .format (
203+ version ,
204+ platform_name ,
205+ )
206+ for version in versions
207+ ]
208+
153209def _swift_linkopts_cc_info (
154210 apple_toolchain ,
211+ platform_developer_framework_dir ,
212+ sdk_developer_framework_dir ,
155213 target_triple ,
156214 toolchain_label ,
157- toolchain_root ):
215+ toolchain_root ,
216+ xcode_config ):
158217 """Returns a `CcInfo` containing flags that should be passed to the linker.
159218
160219 The providers returned by this function will be used as implicit
@@ -163,11 +222,14 @@ def _swift_linkopts_cc_info(
163222
164223 Args:
165224 apple_toolchain: The `apple_common.apple_toolchain()` object.
225+ platform_developer_framework_dir: The platform developer framework dir.
226+ sdk_developer_framework_dir: The SDK developer framework dir.
166227 target_triple: The target triple `struct`.
167228 toolchain_label: The label of the Swift toolchain that will act as the
168229 owner of the linker input propagating the flags.
169230 toolchain_root: The path to a custom Swift toolchain that could contain
170231 libraries required to link the binary
232+ xcode_config: The `apple_common.XcodeVersionConfig` provider.
171233
172234 Returns:
173235 A `CcInfo` provider that will provide linker flags to binaries that
@@ -189,6 +251,12 @@ def _swift_linkopts_cc_info(
189251 )
190252
191253 linkopts .extend ([
254+ "-F{}" .format (path )
255+ for path in compact ([
256+ platform_developer_framework_dir ,
257+ sdk_developer_framework_dir ,
258+ ])
259+ ] + [
192260 "-L{}" .format (swift_lib_dir ),
193261 "-L/usr/lib/swift" ,
194262 # TODO(b/112000244): This should get added by the C++ Starlark API,
@@ -197,9 +265,41 @@ def _swift_linkopts_cc_info(
197265 # variables not provided by cc_common. Figure out how to handle this
198266 # correctly.
199267 "-Wl,-objc_abi_version,2" ,
200- "-Wl,-rpath,/usr/lib/swift" ,
201268 ])
202269
270+ # Compute the necessary rpaths for back-deployed compatibility libraries.
271+ # Note that this implies that a `swift_{binary,compiler_plugin,test}` isn't
272+ # portable to a different machine unless that machine also has the correct
273+ # version of Xcode selected. Users who need to build a binary that can be
274+ # moved to machines with different or no Xcode should use an appropriate
275+ # rule from rules_apple, which ensures that the dylibs are bundled as part
276+ # of the application.
277+ #
278+ # The system `/usr/lib/swift` must always be first in the list, to ensure
279+ # that system libraries are preferred over those in the toolchain.
280+ swift_compatibility_lib_dirs = _swift_compatibility_lib_paths (
281+ target_triple = target_triple ,
282+ xcode_config = xcode_config ,
283+ )
284+ rpaths = ["/usr/lib/swift" ] + [
285+ paths .join (developer_dir_symlink , compatibility_dir )
286+ for developer_dir_symlink in _DEVELOPER_DIR_SYMLINKS
287+ for compatibility_dir in swift_compatibility_lib_dirs
288+ ]
289+ linkopts += [
290+ "-Wl,-rpath,{}" .format (rpath )
291+ for rpath in rpaths
292+ ]
293+
294+ # Add the linker path to the directory containing the dylib with Swift
295+ # extensions for the XCTest module.
296+ if platform_developer_framework_dir :
297+ linkopts .extend ([
298+ "-L{}" .format (
299+ _swift_developer_lib_dir (platform_developer_framework_dir ),
300+ ),
301+ ])
302+
203303 return CcInfo (
204304 linking_context = cc_common .create_linking_context (
205305 linker_inputs = depset ([
@@ -211,41 +311,28 @@ def _swift_linkopts_cc_info(
211311 ),
212312 )
213313
214- def _test_linking_context (
215- apple_toolchain ,
216- target_triple ,
217- toolchain_label ,
218- xcode_config ):
314+ def _test_linking_context (target_triple , toolchain_label ):
219315 """Returns a `CcLinkingContext` containing linker flags for test binaries.
220316
221317 Args:
222- apple_toolchain: The `apple_common.apple_toolchain()` object.
223318 target_triple: The target triple `struct`.
224319 toolchain_label: The label of the Swift toolchain that will act as the
225320 owner of the linker input propagating the flags.
226- xcode_config: The Xcode configuration.
227321
228322 Returns:
229323 A `CcLinkingContext` that will provide linker flags to `swift_test`
230324 binaries.
231325 """
232- platform_developer_framework_dir = _platform_developer_framework_dir (
233- apple_toolchain ,
234- target_triple ,
235- )
236-
237- # Weakly link to XCTest. It's possible that machine that links the test
238- # binary will have Xcode installed at a different path than the machine that
239- # runs the binary. To handle this, the binary `dlopen`s XCTest at startup
240- # using the path Bazel passes in the test action's environment.
241- linkopts = [
242- "-Wl,-weak_framework,XCTest" ,
243- "-Wl,-weak-lXCTestSwiftSupport" ,
244- ]
245- if _is_xcode_at_least_version (xcode_config , "16.0" ):
246- linkopts .append ("-Wl,-weak_framework,Testing" )
247326
248- if platform_developer_framework_dir :
327+ # We use these as the rpaths for linking tests so that the required
328+ # libraries are found if Xcode is installed in a different location on the
329+ # machine that runs the tests than the machine used to link them.
330+ linkopts = []
331+ for developer_dir in _DEVELOPER_DIR_SYMLINKS :
332+ platform_developer_framework_dir = _platform_developer_framework_dir (
333+ developer_dir ,
334+ target_triple ,
335+ )
249336 linkopts .extend ([
250337 "-Wl,-rpath,{}" .format (path )
251338 for path in compact ([
@@ -646,10 +733,8 @@ def _xcode_swift_toolchain_impl(ctx):
646733 swiftcopts .extend (ctx .attr ._copts [BuildSettingInfo ].value )
647734
648735 test_linking_context = _test_linking_context (
649- apple_toolchain = apple_toolchain ,
650736 target_triple = target_triple ,
651737 toolchain_label = ctx .label ,
652- xcode_config = xcode_config ,
653738 )
654739
655740 # `--define=SWIFT_USE_TOOLCHAIN_ROOT=<path>` is a rapid development feature
@@ -675,11 +760,23 @@ def _xcode_swift_toolchain_impl(ctx):
675760 elif custom_toolchain :
676761 custom_xcode_toolchain_root = "__BAZEL_CUSTOM_XCODE_TOOLCHAIN_PATH__"
677762
763+ platform_developer_framework_dir = _platform_developer_framework_dir (
764+ apple_toolchain .developer_dir (),
765+ target_triple ,
766+ )
767+ sdk_developer_framework_dir = _sdk_developer_framework_dir (
768+ apple_toolchain ,
769+ target_triple ,
770+ )
771+
678772 swift_linkopts_cc_info = _swift_linkopts_cc_info (
679773 apple_toolchain = apple_toolchain ,
774+ platform_developer_framework_dir = platform_developer_framework_dir ,
775+ sdk_developer_framework_dir = sdk_developer_framework_dir ,
680776 target_triple = target_triple ,
681777 toolchain_label = ctx .label ,
682778 toolchain_root = toolchain_root or custom_xcode_toolchain_root ,
779+ xcode_config = xcode_config ,
683780 )
684781
685782 # Compute the default requested features and conditional ones based on Xcode
@@ -759,21 +856,13 @@ def _xcode_swift_toolchain_impl(ctx):
759856 xcode_config = xcode_config ,
760857 )
761858 swift_toolchain_developer_paths = []
762- platform_developer_framework_dir = _platform_developer_framework_dir (
763- apple_toolchain ,
764- target_triple ,
765- )
766859 if platform_developer_framework_dir :
767860 swift_toolchain_developer_paths .append (
768861 struct (
769862 developer_path_label = "platform" ,
770863 path = platform_developer_framework_dir ,
771864 ),
772865 )
773- sdk_developer_framework_dir = _sdk_developer_framework_dir (
774- apple_toolchain ,
775- target_triple ,
776- )
777866 if sdk_developer_framework_dir :
778867 swift_toolchain_developer_paths .append (
779868 struct (
0 commit comments