Skip to content

Commit 417a2c1

Browse files
allevatoadincebic
authored andcommitted
Add rpaths for the toolchain's compatibility back-deployment libraries to binaries built with swift_{binary, compiler_plugin, test}.
This is necessary to ensure that these binaries run correctly when using APIs like `Span` that are back-deployed on older OSes. Since the `swift_*` rules don't do bundling of their own, we simply point to the libraries in the toolchain itself (using the `xcode-select`-dependent symlinks, as we were already doing for tests). To actually distribute such a binary, it's the responsibility of the user to include the required dylibs with it (or, better, use a rule from rules_apple to perform the bundling). I've explicitly ignored the swift-5.0 and swift-5.5 directory since we have no plans to deploy anything from these rules to such old OSes. PiperOrigin-RevId: 817593332 (cherry picked from commit 2f5aa6e)
1 parent 3dfbf33 commit 417a2c1

File tree

1 file changed

+124
-4
lines changed

1 file changed

+124
-4
lines changed

swift/toolchains/xcode_swift_toolchain.bzl

Lines changed: 124 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,15 @@ load(
108108
)
109109
load("//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+
111120
def _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+
133174
def _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

Comments
 (0)