Skip to content

Commit 615505e

Browse files
adincebicallevato
andauthored
Add rpaths for the toolchain's compatibility back-deployment librarie (#1589)
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. Cherry pick: [2f5aa6e](2f5aa6e) --------- Co-authored-by: Tony Allevato <allevato@google.com>
1 parent 858b1fc commit 615505e

File tree

1 file changed

+112
-12
lines changed

1 file changed

+112
-12
lines changed

swift/toolchains/xcode_swift_toolchain.bzl

Lines changed: 112 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
113122
def _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+
135176
def _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

216311
def _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

Comments
 (0)