@@ -110,6 +110,15 @@ load("//swift/toolchains/config:tool_config.bzl", "ToolConfigInfo")
110110
111111_CPP_TOOLCHAIN_TYPE = Label ("@bazel_tools//tools/cpp:toolchain_type" )
112112
113+ visibility ("public" )
114+
115+ # These are symlink locations known to be used by xcode-select in various Xcode
116+ # and macOS versions to point to the selected `Developer` directory.
117+ _DEVELOPER_DIR_SYMLINKS = [
118+ "/private/var/select/developer_dir" ,
119+ "/var/db/xcode_select_link" ,
120+ ]
121+
113122def _platform_developer_framework_dir (
114123 apple_toolchain ,
115124 target_triple ):
@@ -132,6 +141,38 @@ def _platform_developer_framework_dir(
132141 "Developer/Library/Frameworks" ,
133142 )
134143
144+ def _swift_compatibility_lib_paths (* , target_triple , xcode_config ):
145+ """Returns the paths to the Swift compatibility libraries in the toolchain.
146+
147+ The returned paths are relative to the `Developer` directory; they do not
148+ contain the Bazel placeholder that would be substituted with the actual
149+ `Developer` directory at execution time.
150+
151+ Args:
152+ target_triple: The target triple `struct`.
153+ xcode_config: The `apple_common.XcodeVersionConfig` provider.
154+
155+ Returns:
156+ A list of paths to the Swift compatibility libraries in the toolchain.
157+ """
158+
159+ # We choose to ignore swift-5.0 and swift-5.5 because they correspond to
160+ # such old OS versions that nobody is targeting with rules like
161+ # `swift_binary` and `swift_test`. (And if they were, they would already be
162+ # broken before this addition.)
163+ versions = []
164+ if _is_xcode_at_least_version (xcode_config , "26.0" ):
165+ versions .append ("6.2" )
166+
167+ platform_name = target_triples .platform_name_for_swift (target_triple )
168+ return [
169+ "Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-{}/{}" .format (
170+ version ,
171+ platform_name ,
172+ )
173+ for version in versions
174+ ]
175+
135176def _sdk_developer_framework_dir (apple_toolchain , target_triple ):
136177 """Returns the Developer framework directory for the SDK.
137178
@@ -156,7 +197,8 @@ def _swift_linkopts_cc_info(
156197 apple_toolchain ,
157198 target_triple ,
158199 toolchain_label ,
159- toolchain_root ):
200+ toolchain_root ,
201+ xcode_config ):
160202 """Returns a `CcInfo` containing flags that should be passed to the linker.
161203
162204 The providers returned by this function will be used as implicit
@@ -170,11 +212,21 @@ def _swift_linkopts_cc_info(
170212 owner of the linker input propagating the flags.
171213 toolchain_root: The path to a custom Swift toolchain that could contain
172214 libraries required to link the binary
215+ xcode_config: The `apple_common.XcodeVersionConfig` provider.
173216
174217 Returns:
175218 A `CcInfo` provider that will provide linker flags to binaries that
176219 depend on Swift targets.
177220 """
221+ platform_developer_framework_dir = _platform_developer_framework_dir (
222+ apple_toolchain ,
223+ target_triple ,
224+ )
225+ sdk_developer_framework_dir = _sdk_developer_framework_dir (
226+ apple_toolchain ,
227+ target_triple ,
228+ )
229+
178230 linkopts = []
179231 if toolchain_root :
180232 # This -L has to come before Xcode's to make sure libraries are
@@ -191,6 +243,12 @@ def _swift_linkopts_cc_info(
191243 )
192244
193245 linkopts .extend ([
246+ "-F{}" .format (path )
247+ for path in compact ([
248+ platform_developer_framework_dir ,
249+ sdk_developer_framework_dir ,
250+ ])
251+ ] + [
194252 "-L{}" .format (swift_lib_dir ),
195253 "-L/usr/lib/swift" ,
196254 # TODO(b/112000244): This should get added by the C++ Starlark API,
@@ -199,9 +257,46 @@ def _swift_linkopts_cc_info(
199257 # variables not provided by cc_common. Figure out how to handle this
200258 # correctly.
201259 "-Wl,-objc_abi_version,2" ,
202- "-Wl,-rpath,/usr/lib/swift" ,
203260 ])
204261
262+ # Compute the necessary rpaths for back-deployed compatibility libraries.
263+ # Note that this implies that a `swift_{binary,compiler_plugin,test}` isn't
264+ # portable to a different machine unless that machine also has the correct
265+ # version of Xcode selected. Users who need to build a binary that can be
266+ # moved to machines with different or no Xcode should use an appropriate
267+ # rule from rules_apple, which ensures that the dylibs are bundled as part
268+ # of the application.
269+ #
270+ # The system `/usr/lib/swift` must always be first in the list, to ensure
271+ # that system libraries are preferred over those in the toolchain.
272+ swift_compatibility_lib_dirs = _swift_compatibility_lib_paths (
273+ target_triple = target_triple ,
274+ xcode_config = xcode_config ,
275+ )
276+ rpaths = ["/usr/lib/swift" ] + [
277+ paths .join (developer_dir_symlink , compatibility_dir )
278+ for developer_dir_symlink in _DEVELOPER_DIR_SYMLINKS
279+ for compatibility_dir in swift_compatibility_lib_dirs
280+ ]
281+ linkopts += [
282+ "-Wl,-rpath,{}" .format (rpath )
283+ for rpath in rpaths
284+ ]
285+
286+ # Add the linker path to the directory containing the dylib with Swift
287+ # extensions for the XCTest module.
288+ if platform_developer_framework_dir :
289+ linkopts .extend ([
290+ "-L{}" .format (
291+ swift_developer_lib_dir ([
292+ struct (
293+ developer_path_label = "platform" ,
294+ path = platform_developer_framework_dir ,
295+ ),
296+ ]),
297+ ),
298+ ])
299+
205300 return CcInfo (
206301 linking_context = cc_common .create_linking_context (
207302 linker_inputs = depset ([
@@ -214,14 +309,12 @@ def _swift_linkopts_cc_info(
214309 )
215310
216311def _test_linking_context (
217- apple_toolchain ,
218312 target_triple ,
219313 toolchain_label ,
220314 xcode_config ):
221315 """Returns a `CcLinkingContext` containing linker flags for test binaries.
222316
223317 Args:
224- apple_toolchain: The `apple_common.apple_toolchain()` object.
225318 target_triple: The target triple `struct`.
226319 toolchain_label: The label of the Swift toolchain that will act as the
227320 owner of the linker input propagating the flags.
@@ -231,10 +324,6 @@ def _test_linking_context(
231324 A `CcLinkingContext` that will provide linker flags to `swift_test`
232325 binaries.
233326 """
234- platform_developer_framework_dir = _platform_developer_framework_dir (
235- apple_toolchain ,
236- target_triple ,
237- )
238327
239328 # Weakly link to XCTest. It's possible that machine that links the test
240329 # binary will have Xcode installed at a different path than the machine that
@@ -247,17 +336,28 @@ def _test_linking_context(
247336 if _is_xcode_at_least_version (xcode_config , "16.0" ):
248337 linkopts .append ("-Wl,-weak_framework,Testing" )
249338
250- if platform_developer_framework_dir :
339+ # We use these as the rpaths for linking tests so that the required
340+ # libraries are found if Xcode is installed in a different location on the
341+ # machine that runs the tests than the machine used to link them.
342+ for developer_dir in _DEVELOPER_DIR_SYMLINKS :
343+ platform_developer_framework_dir_symlink = paths .join (
344+ developer_dir ,
345+ "Platforms" ,
346+ "{}.platform" .format (
347+ target_triples .bazel_apple_platform (target_triple ).name_in_plist ,
348+ ),
349+ "Developer/Library/Frameworks" ,
350+ )
251351 linkopts .extend ([
252352 "-Wl,-rpath,{}" .format (path )
253353 for path in compact ([
254354 swift_developer_lib_dir ([
255355 struct (
256356 developer_path_label = "platform" ,
257- path = platform_developer_framework_dir ,
357+ path = platform_developer_framework_dir_symlink ,
258358 ),
259359 ]),
260- platform_developer_framework_dir ,
360+ platform_developer_framework_dir_symlink ,
261361 ])
262362 ])
263363
@@ -653,7 +753,6 @@ def _xcode_swift_toolchain_impl(ctx):
653753 swiftcopts .extend (ctx .attr ._copts [BuildSettingInfo ].value )
654754
655755 test_linking_context = _test_linking_context (
656- apple_toolchain = apple_toolchain ,
657756 target_triple = target_triple ,
658757 toolchain_label = ctx .label ,
659758 xcode_config = xcode_config ,
@@ -687,6 +786,7 @@ def _xcode_swift_toolchain_impl(ctx):
687786 target_triple = target_triple ,
688787 toolchain_label = ctx .label ,
689788 toolchain_root = toolchain_root or custom_xcode_toolchain_root ,
789+ xcode_config = xcode_config ,
690790 )
691791
692792 # Compute the default requested features and conditional ones based on Xcode
0 commit comments