Skip to content

Commit e7bcb9d

Browse files
allevatobrentleyjones
authored andcommitted
Allow testing of swift_binary targets
PiperOrigin-RevId: 573783919 (cherry picked from commit 6cae838) Signed-off-by: Brentley Jones <[email protected]>
1 parent ea09cd9 commit e7bcb9d

File tree

5 files changed

+132
-36
lines changed

5 files changed

+132
-36
lines changed

swift/internal/attrs.bzl

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
load("@bazel_skylib//lib:dicts.bzl", "dicts")
1818
load("//swift:providers.bzl", "SwiftInfo")
19-
load(":providers.bzl", "SwiftCompilerPluginInfo")
19+
load(":providers.bzl", "SwiftBinaryInfo", "SwiftCompilerPluginInfo")
2020

2121
def swift_common_rule_attrs(
2222
additional_deps_aspects = [],
@@ -147,7 +147,7 @@ Swift 5.9+.
147147
A list of `swift_compiler_plugin` targets that should be loaded by the compiler
148148
when compiling this module and any modules that directly depend on it.
149149
""",
150-
providers = [[SwiftCompilerPluginInfo]],
150+
providers = [[SwiftBinaryInfo, SwiftCompilerPluginInfo]],
151151
),
152152
"srcs": attr.label_list(
153153
allow_empty = not requires_srcs,

swift/internal/providers.bzl

+31-11
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,45 @@
1414

1515
"""Internal providers."""
1616

17-
SwiftCompilerPluginInfo = provider(
18-
doc = "Information about compiler plugins, like macros.",
17+
SwiftBinaryInfo = provider(
18+
doc = """
19+
Information about a binary target's module.
20+
`swift_binary` and `swift_compiler_plugin` propagate this provider that wraps
21+
`CcInfo` and `SwiftInfo` providers, instead of propagating them directly, so
22+
that `swift_test` targets can depend on those binaries and test their modules
23+
(similar to what Swift Package Manager allows) without allowing any
24+
`swift_library` to depend on an arbitrary binary.
25+
""",
1926
fields = {
2027
"cc_info": """\
21-
A `CcInfo` provider containing the `swift_compiler_plugin`'s code compiled as a
22-
static library, which is suitable for linking into a `swift_test` so that unit
23-
tests can be written against it.
28+
A `CcInfo` provider containing the binary's code compiled as a static library,
29+
which is suitable for linking into a `swift_test` so that unit tests can be
30+
written against it.
31+
Notably, this `CcInfo`'s linking context does *not* contain the linker flags
32+
used to alias the `main` entry point function, because the purpose of this
33+
provider is to allow it to be linked into another binary that would provide its
34+
own entry point instead.
35+
""",
36+
"swift_info": """\
37+
A `SwiftInfo` provider representing the Swift module created by compiling the
38+
target. This is used specifically by `swift_test` to allow test code to depend
39+
on the binary's module without making it possible for arbitrary libraries or
40+
binaries to depend on other binaries.
41+
""",
42+
},
43+
)
44+
45+
SwiftCompilerPluginInfo = provider(
46+
doc = """
47+
Information about compiler plugins (like macros) that is needed by the compiler
48+
when loading modules that declare those macros.
2449
""",
50+
fields = {
2551
"executable": "A `File` representing the plugin's binary executable.",
2652
"module_names": """\
2753
A `depset` of strings denoting the names of the Swift modules that provide
2854
plugin types looked up by the compiler. This currently contains a single
2955
element, the name of the module created by the `swift_compiler_plugin` target.
30-
""",
31-
"swift_info": """\
32-
A `SwiftInfo` provider representing the Swift module created by the
33-
`swift_compiler_plugin` target. This is used specifically by `swift_test` to
34-
allow test code to depend on the plugin's module without making it possible for
35-
arbitrary libraries/binaries to depend on a plugin.
3656
""",
3757
},
3858
)

swift/swift_binary.bzl

+72-8
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,19 @@ load(
2626
"//swift/internal:linking.bzl",
2727
"binary_rule_attrs",
2828
"configure_features_for_binary",
29+
"create_linking_context_from_compilation_outputs",
2930
"malloc_linking_context",
3031
"register_link_binary_action",
3132
)
3233
load(
3334
"//swift/internal:output_groups.bzl",
3435
"supplemental_compilation_output_groups",
3536
)
36-
load("//swift/internal:providers.bzl", "SwiftCompilerPluginInfo")
37+
load(
38+
"//swift/internal:providers.bzl",
39+
"SwiftBinaryInfo",
40+
"SwiftCompilerPluginInfo",
41+
)
3742
load(
3843
"//swift/internal:toolchain_utils.bzl",
3944
"get_swift_toolchain",
@@ -88,6 +93,7 @@ def _swift_binary_impl(ctx):
8893
module_name = ctx.attr.module_name
8994
if not module_name:
9095
module_name = derive_swift_module_name(ctx.label)
96+
entry_point_function_name = "{}_main".format(module_name)
9197

9298
include_dev_srch_paths = include_developer_search_paths(ctx.attr)
9399

@@ -99,7 +105,15 @@ def _swift_binary_impl(ctx):
99105
ctx,
100106
ctx.attr.copts,
101107
ctx.attr.swiftc_inputs,
102-
) + _maybe_parse_as_library_copts(srcs),
108+
) + _maybe_parse_as_library_copts(srcs) + [
109+
# Use a custom entry point name so that the binary's code can
110+
# also be linked into another process (like a test executable)
111+
# without having its main function collide.
112+
"-Xfrontend",
113+
"-entry-point-function-name",
114+
"-Xfrontend",
115+
entry_point_function_name,
116+
],
103117
defines = ctx.attr.defines,
104118
feature_configuration = feature_configuration,
105119
include_dev_srch_paths = include_dev_srch_paths,
@@ -120,6 +134,8 @@ def _swift_binary_impl(ctx):
120134
supplemental_outputs,
121135
)
122136
else:
137+
compile_result = None
138+
entry_point_function_name = None
123139
compilation_outputs = cc_common.create_compilation_outputs()
124140

125141
additional_linking_contexts.append(malloc_linking_context(ctx))
@@ -135,6 +151,20 @@ def _swift_binary_impl(ctx):
135151
additional_debug_outputs = []
136152
variables_extension = {}
137153

154+
binary_link_flags = expand_locations(
155+
ctx,
156+
ctx.attr.linkopts,
157+
ctx.attr.swiftc_inputs,
158+
) + ctx.fragments.cpp.linkopts
159+
160+
# When linking the binary, make sure we use the correct entry point name.
161+
if entry_point_function_name:
162+
entry_point_linkopts = swift_toolchain.entry_point_linkopts_provider(
163+
entry_point_name = entry_point_function_name,
164+
).linkopts
165+
else:
166+
entry_point_linkopts = []
167+
138168
if is_feature_enabled(
139169
feature_configuration = feature_configuration,
140170
feature_name = SWIFT_FEATURE_ADD_TARGET_NAME_TO_OUTPUT,
@@ -157,15 +187,11 @@ def _swift_binary_impl(ctx):
157187
owner = ctx.label,
158188
stamp = ctx.attr.stamp,
159189
swift_toolchain = swift_toolchain,
160-
user_link_flags = expand_locations(
161-
ctx,
162-
ctx.attr.linkopts,
163-
ctx.attr.swiftc_inputs,
164-
) + ctx.fragments.cpp.linkopts,
190+
user_link_flags = binary_link_flags + entry_point_linkopts,
165191
variables_extension = variables_extension,
166192
)
167193

168-
return [
194+
providers = [
169195
DefaultInfo(
170196
executable = linking_outputs.executable,
171197
files = depset(
@@ -191,6 +217,44 @@ def _swift_binary_impl(ctx):
191217
),
192218
]
193219

220+
# Only create a linking context and propagate `SwiftBinaryInfo` if this rule
221+
# compiled something (i.e., it had sources). If it didn't, then there's
222+
# nothing to allow testing against.
223+
if compile_result:
224+
linking_context, _ = (
225+
create_linking_context_from_compilation_outputs(
226+
actions = ctx.actions,
227+
additional_inputs = ctx.files.swiftc_inputs,
228+
alwayslink = True,
229+
compilation_outputs = compilation_outputs,
230+
feature_configuration = feature_configuration,
231+
label = ctx.label,
232+
linking_contexts = [
233+
dep[CcInfo].linking_context
234+
for dep in ctx.attr.deps
235+
if CcInfo in dep
236+
],
237+
module_context = compile_result.module_context,
238+
swift_toolchain = swift_toolchain,
239+
# Exclude the entry point linkopts from this linking context,
240+
# because it is meant to be used by other binary rules that
241+
# provide their own entry point while linking this "binary" in
242+
# as a library.
243+
user_link_flags = binary_link_flags,
244+
)
245+
)
246+
providers.append(SwiftBinaryInfo(
247+
cc_info = CcInfo(
248+
compilation_context = (
249+
compile_result.module_context.clang.compilation_context
250+
),
251+
linking_context = linking_context,
252+
),
253+
swift_info = compile_result.swift_info,
254+
))
255+
256+
return providers
257+
194258
swift_binary = rule(
195259
attrs = dicts.add(
196260
binary_rule_attrs(

swift/swift_compiler_plugin.bzl

+18-10
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@ load(
3939
"//swift/internal:output_groups.bzl",
4040
"supplemental_compilation_output_groups",
4141
)
42-
load("//swift/internal:providers.bzl", "SwiftCompilerPluginInfo")
42+
load(
43+
"//swift/internal:providers.bzl",
44+
"SwiftBinaryInfo",
45+
"SwiftCompilerPluginInfo",
46+
)
4347
load(
4448
"//swift/internal:toolchain_utils.bzl",
4549
"get_swift_toolchain",
@@ -196,14 +200,16 @@ def _swift_compiler_plugin_impl(ctx):
196200
OutputGroupInfo(
197201
**supplemental_compilation_output_groups(supplemental_outputs)
198202
),
199-
SwiftCompilerPluginInfo(
203+
SwiftBinaryInfo(
200204
cc_info = CcInfo(
201205
compilation_context = module_context.clang.compilation_context,
202206
linking_context = linking_context,
203207
),
208+
swift_info = compile_result.swift_info,
209+
),
210+
SwiftCompilerPluginInfo(
204211
executable = binary_linking_outputs.executable,
205212
module_names = depset([module_name]),
206-
swift_info = compile_result.swift_info,
207213
),
208214
]
209215

@@ -305,10 +311,10 @@ def _universal_swift_compiler_plugin_impl(ctx):
305311
module_name = None
306312
swift_infos = []
307313
for plugin in ctx.split_attr.plugin.values():
308-
cc_infos.append(plugin[SwiftCompilerPluginInfo].cc_info)
309-
direct_swift_modules.extend(plugin[SwiftCompilerPluginInfo].swift_info.direct_modules)
314+
cc_infos.append(plugin[SwiftBinaryInfo].cc_info)
315+
direct_swift_modules.extend(plugin[SwiftBinaryInfo].swift_info.direct_modules)
310316
module_name = plugin[SwiftCompilerPluginInfo].module_names.to_list()[0]
311-
swift_infos.append(plugin[SwiftCompilerPluginInfo].swift_info)
317+
swift_infos.append(plugin[SwiftBinaryInfo].swift_info)
312318

313319
first_output_group_info = ctx.split_attr.plugin.values()[0][OutputGroupInfo]
314320
combined_output_group_info = {}
@@ -330,15 +336,17 @@ def _universal_swift_compiler_plugin_impl(ctx):
330336
runfiles = ctx.runfiles().merge_all(transitive_runfiles),
331337
),
332338
OutputGroupInfo(**combined_output_group_info),
333-
SwiftCompilerPluginInfo(
339+
SwiftBinaryInfo(
334340
cc_info = cc_common.merge_cc_infos(cc_infos = cc_infos),
335-
executable = output,
336-
module_names = depset([module_name]),
337341
swift_info = SwiftInfo(
338342
modules = direct_swift_modules,
339343
swift_infos = swift_infos,
340344
),
341345
),
346+
SwiftCompilerPluginInfo(
347+
executable = output,
348+
module_names = depset([module_name]),
349+
),
342350
]
343351

344352
universal_swift_compiler_plugin = rule(
@@ -349,7 +357,7 @@ universal_swift_compiler_plugin = rule(
349357
cfg = macos_universal_transition,
350358
doc = "Target to generate a 'fat' binary from.",
351359
mandatory = True,
352-
providers = [SwiftCompilerPluginInfo],
360+
providers = [[SwiftBinaryInfo, SwiftCompilerPluginInfo]],
353361
),
354362
"_allowlist_function_transition": attr.label(
355363
default = Label(

swift/swift_test.bzl

+9-5
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@ load(
3434
"//swift/internal:output_groups.bzl",
3535
"supplemental_compilation_output_groups",
3636
)
37-
load("//swift/internal:providers.bzl", "SwiftCompilerPluginInfo")
37+
load(
38+
"//swift/internal:providers.bzl",
39+
"SwiftBinaryInfo",
40+
"SwiftCompilerPluginInfo",
41+
)
3842
load(
3943
"//swift/internal:swift_symbol_graph_aspect.bzl",
4044
"make_swift_symbol_graph_aspect",
@@ -369,8 +373,8 @@ def _swift_test_impl(ctx):
369373
else:
370374
additional_link_deps = []
371375

372-
# We also need to collect nested providers from `SwiftCompilerPluginInfo`
373-
# since we support testing those.
376+
# We also need to collect nested providers from `SwiftBinaryInfo` since we
377+
# support testing those.
374378
deps_cc_infos = []
375379
deps_compilation_contexts = []
376380
deps_objc_infos = []
@@ -384,8 +388,8 @@ def _swift_test_impl(ctx):
384388
deps_objc_infos.append(dep[apple_common.Objc])
385389
if SwiftInfo in dep:
386390
deps_swift_infos.append(dep[SwiftInfo])
387-
if SwiftCompilerPluginInfo in dep:
388-
plugin_info = dep[SwiftCompilerPluginInfo]
391+
if SwiftBinaryInfo in dep:
392+
plugin_info = dep[SwiftBinaryInfo]
389393
deps_swift_infos.append(plugin_info.swift_info)
390394
additional_linking_contexts.append(
391395
plugin_info.cc_info.linking_context,

0 commit comments

Comments
 (0)