@@ -108,6 +108,15 @@ load(
108108)
109109load ("//swift/toolchains/config:tool_config.bzl" , "ToolConfigInfo" )
110110
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+
111120def _platform_developer_framework_dir (
112121 apple_toolchain ,
113122 target_triple ):
@@ -130,6 +139,38 @@ def _platform_developer_framework_dir(
130139 "Developer/Library/Frameworks" ,
131140 )
132141
142+ def _swift_compatibility_lib_paths (* , target_triple , xcode_config ):
143+ """Returns the paths to the Swift compatibility libraries in the toolchain.
144+
145+ The returned paths are relative to the `Developer` directory; they do not
146+ contain the Bazel placeholder that would be substituted with the actual
147+ `Developer` directory at execution time.
148+
149+ Args:
150+ target_triple: The target triple `struct`.
151+ xcode_config: The `apple_common.XcodeVersionConfig` provider.
152+
153+ Returns:
154+ A list of paths to the Swift compatibility libraries in the toolchain.
155+ """
156+
157+ # We choose to ignore swift-5.0 and swift-5.5 because they correspond to
158+ # such old OS versions that nobody is targeting with rules like
159+ # `swift_binary` and `swift_test`. (And if they were, they would already be
160+ # broken before this addition.)
161+ versions = []
162+ if _is_xcode_at_least_version (xcode_config , "26.0" ):
163+ versions .append ("6.2" )
164+
165+ platform_name = target_triples .platform_name_for_swift (target_triple )
166+ return [
167+ "Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-{}/{}" .format (
168+ version ,
169+ platform_name ,
170+ )
171+ for version in versions
172+ ]
173+
133174def _sdk_developer_framework_dir (apple_toolchain , target_triple ):
134175 """Returns the Developer framework directory for the SDK.
135176
@@ -154,7 +195,8 @@ def _swift_linkopts_cc_info(
154195 apple_toolchain ,
155196 target_triple ,
156197 toolchain_label ,
157- toolchain_root ):
198+ toolchain_root ,
199+ xcode_config ):
158200 """Returns a `CcInfo` containing flags that should be passed to the linker.
159201
160202 The providers returned by this function will be used as implicit
@@ -168,11 +210,21 @@ def _swift_linkopts_cc_info(
168210 owner of the linker input propagating the flags.
169211 toolchain_root: The path to a custom Swift toolchain that could contain
170212 libraries required to link the binary
213+ xcode_config: The `apple_common.XcodeVersionConfig` provider.
171214
172215 Returns:
173216 A `CcInfo` provider that will provide linker flags to binaries that
174217 depend on Swift targets.
175218 """
219+ platform_developer_framework_dir = _platform_developer_framework_dir (
220+ apple_toolchain ,
221+ target_triple ,
222+ )
223+ sdk_developer_framework_dir = _sdk_developer_framework_dir (
224+ apple_toolchain ,
225+ target_triple ,
226+ )
227+
176228 linkopts = []
177229 if toolchain_root :
178230 # This -L has to come before Xcode's to make sure libraries are
@@ -189,6 +241,12 @@ def _swift_linkopts_cc_info(
189241 )
190242
191243 linkopts .extend ([
244+ "-F{}" .format (path )
245+ for path in compact ([
246+ platform_developer_framework_dir ,
247+ sdk_developer_framework_dir ,
248+ ])
249+ ] + [
192250 "-L{}" .format (swift_lib_dir ),
193251 "-L/usr/lib/swift" ,
194252 # TODO(b/112000244): This should get added by the C++ Starlark API,
@@ -197,9 +255,46 @@ def _swift_linkopts_cc_info(
197255 # variables not provided by cc_common. Figure out how to handle this
198256 # correctly.
199257 "-Wl,-objc_abi_version,2" ,
200- "-Wl,-rpath,/usr/lib/swift" ,
201258 ])
202259
260+ # Compute the necessary rpaths for back-deployed compatibility libraries.
261+ # Note that this implies that a `swift_{binary,compiler_plugin,test}` isn't
262+ # portable to a different machine unless that machine also has the correct
263+ # version of Xcode selected. Users who need to build a binary that can be
264+ # moved to machines with different or no Xcode should use an appropriate
265+ # rule from rules_apple, which ensures that the dylibs are bundled as part
266+ # of the application.
267+ #
268+ # The system `/usr/lib/swift` must always be first in the list, to ensure
269+ # that system libraries are preferred over those in the toolchain.
270+ swift_compatibility_lib_dirs = _swift_compatibility_lib_paths (
271+ target_triple = target_triple ,
272+ xcode_config = xcode_config ,
273+ )
274+ rpaths = ["/usr/lib/swift" ] + [
275+ paths .join (developer_dir_symlink , compatibility_dir )
276+ for developer_dir_symlink in _DEVELOPER_DIR_SYMLINKS
277+ for compatibility_dir in swift_compatibility_lib_dirs
278+ ]
279+ linkopts += [
280+ "-Wl,-rpath,{}" .format (rpath )
281+ for rpath in rpaths
282+ ]
283+
284+ # Add the linker path to the directory containing the dylib with Swift
285+ # extensions for the XCTest module.
286+ if platform_developer_framework_dir :
287+ linkopts .extend ([
288+ "-L{}" .format (
289+ swift_developer_lib_dir ([
290+ struct (
291+ developer_path_label = "platform" ,
292+ path = platform_developer_framework_dir ,
293+ ),
294+ ]),
295+ ),
296+ ])
297+
203298 return CcInfo (
204299 linking_context = cc_common .create_linking_context (
205300 linker_inputs = depset ([
@@ -246,16 +341,40 @@ def _test_linking_context(
246341 linkopts .append ("-Wl,-weak_framework,Testing" )
247342
248343 if platform_developer_framework_dir :
344+ linkopts .extend ([
345+ "-F{}" .format (platform_developer_framework_dir ),
346+ "-L{}" .format (
347+ swift_developer_lib_dir ([
348+ struct (
349+ developer_path_label = "platform" ,
350+ path = platform_developer_framework_dir ,
351+ ),
352+ ]),
353+ ),
354+ ])
355+
356+ # We use these as the rpaths for linking tests so that the required
357+ # libraries are found if Xcode is installed in a different location on the
358+ # machine that runs the tests than the machine used to link them.
359+ for developer_dir in _DEVELOPER_DIR_SYMLINKS :
360+ platform_developer_framework_dir_symlink = paths .join (
361+ developer_dir ,
362+ "Platforms" ,
363+ "{}.platform" .format (
364+ target_triples .bazel_apple_platform (target_triple ).name_in_plist ,
365+ ),
366+ "Developer/Library/Frameworks" ,
367+ )
249368 linkopts .extend ([
250369 "-Wl,-rpath,{}" .format (path )
251370 for path in compact ([
252371 swift_developer_lib_dir ([
253372 struct (
254373 developer_path_label = "platform" ,
255- path = platform_developer_framework_dir ,
374+ path = platform_developer_framework_dir_symlink ,
256375 ),
257376 ]),
258- platform_developer_framework_dir ,
377+ platform_developer_framework_dir_symlink ,
259378 ])
260379 ])
261380
@@ -680,6 +799,7 @@ def _xcode_swift_toolchain_impl(ctx):
680799 target_triple = target_triple ,
681800 toolchain_label = ctx .label ,
682801 toolchain_root = toolchain_root or custom_xcode_toolchain_root ,
802+ xcode_config = xcode_config ,
683803 )
684804
685805 # Compute the default requested features and conditional ones based on Xcode
0 commit comments