Skip to content

Commit

Permalink
Refactor (and properly support) link stamping for Swift binaries.
Browse files Browse the repository at this point in the history
RELNOTES: Link stamping has been refactored. Now instead of using a fixed dependency (via its label) containing the stamp data, the toolchain provider contains a Skylib partial that is called when linking a Swift binary or test target. This partial can return a C++ linking context that will be included among the other dependencies.
PiperOrigin-RevId: 261938307
  • Loading branch information
allevato authored and swiple-rules-gardener committed Aug 6, 2019
1 parent 900bef3 commit a2c1bfc
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 50 deletions.
23 changes: 12 additions & 11 deletions swift/internal/linking.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ def register_libraries_to_link(
def register_link_executable_action(
actions,
action_environment,
additional_linking_contexts,
cc_feature_configuration,
clang_executable,
deps,
Expand All @@ -158,6 +159,8 @@ def register_link_executable_action(
actions: The object used to register actions.
action_environment: A `dict` of environment variables that should be set for the compile
action.
additional_linking_contexts: Additional linking contexts that provide libraries or flags
that should be linked into the executable.
cc_feature_configuration: The C++ feature configuration to use when constructing the
action.
clang_executable: The path to the `clang` executable that will be invoked to link, which is
Expand Down Expand Up @@ -188,17 +191,6 @@ def register_link_executable_action(

deps_libraries = []

if swift_toolchain.stamp:
stamp_libs_to_link = []
for library in swift_toolchain.stamp[CcInfo].linking_context.libraries_to_link:
if library.pic_static_library:
stamp_libs_to_link.append(library.pic_static_library)
elif library.static_library:
stamp_libs_to_link.append(library.static_library)

if stamp_libs_to_link:
deps_libraries.extend(stamp_libs_to_link)

additional_input_depsets = []
all_linkopts = list(expanded_linkopts)
deps_dynamic_framework_names = []
Expand Down Expand Up @@ -239,6 +231,15 @@ def register_link_executable_action(
deps_sdk_dylibs.append(objc.sdk_dylib)
deps_sdk_frameworks.append(objc.sdk_framework)

for linking_context in additional_linking_contexts:
additional_input_depsets.append(linking_context.additional_inputs)
all_linkopts.extend(linking_context.user_link_flags)
for library in linking_context.libraries_to_link:
if library.pic_static_library:
deps_libraries.append(library.pic_static_library)
elif library.static_library:
deps_libraries.append(library.static_library)

libraries = depset(deps_libraries, order = "topological")
link_input_depsets = [
libraries,
Expand Down
23 changes: 20 additions & 3 deletions swift/internal/providers.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,26 @@ they are also passed to the C++ APIs used when linking (so features defined in C
here).
""",
"root_dir": "`String`. The workspace-relative root directory of the toolchain.",
"stamp": """
`Target`. A `CcInfo`-providing target that should be linked into any binaries that are built with
stamping enabled.
"stamp_producer": """
Skylib `partial`. A partial function that compiles build data that should be stamped into binaries.
This value may be `None` if the toolchain does not support link stamping.
The `swift_binary` and `swift_test` rules call this function _whether or not_ link stamping is
enabled for that target. This provides toolchains the option of still linking fixed placeholder
data into the binary if desired, instead of linking nothing at all. Whether stamping is enabled can
be checked by inspecting `ctx.attr.stamp` inside the partial's implementation.
The rule implementation will call this partial and pass it the following four arguments:
* `ctx`: The rule context of the target being built.
* `cc_feature_configuration`: The C++ feature configuration to use when compiling the stamp
code.
* `cc_toolchain`: The C++ toolchain (`CcToolchainInfo` provider) to use when compiling the
stamp code.
* `binary`: The `File` object representing the binary being linked.
The partial should return a `CcLinkingContext` containing the data (such as object files) to be
linked into the binary, or `None` if nothing should be linked into the binary.
""",
"supports_objc_interop": """
`Boolean`. Indicates whether or not the toolchain supports Objective-C interop.
Expand Down
75 changes: 55 additions & 20 deletions swift/internal/swift_binary_test.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -25,33 +25,55 @@ load(":non_swift_target_aspect.bzl", "non_swift_target_aspect")
load(":providers.bzl", "SwiftToolchainInfo")
load(":utils.bzl", "expand_locations")

# Attributes common to both `swift_binary` and `swift_test`.
_BINARY_RULE_ATTRS = dicts.add(
swift_common.compilation_attrs(additional_deps_aspects = [non_swift_target_aspect]),
{
"linkopts": attr.string_list(
doc = """
def _binary_rule_attrs(stamp_default):
"""Returns the dictionary of attributes common to both `swift_binary` and `swift_test`.
Args:
stamp_default: The default value of the `stamp` attribute.
Returns:
A `dict` of attributes for a binary or test rule.
"""
return dicts.add(
swift_common.compilation_attrs(additional_deps_aspects = [non_swift_target_aspect]),
{
"linkopts": attr.string_list(
doc = """
Additional linker options that should be passed to `clang`. These strings are subject to
`$(location ...)` expansion.
""",
mandatory = False,
),
"malloc": attr.label(
default = Label("@bazel_tools//tools/cpp:malloc"),
doc = """
mandatory = False,
),
"malloc": attr.label(
default = Label("@bazel_tools//tools/cpp:malloc"),
doc = """
Override the default dependency on `malloc`.
By default, Swift binaries are linked against `@bazel_tools//tools/cpp:malloc"`, which is an empty
library and the resulting binary will use libc's `malloc`. This label must refer to a `cc_library`
rule.
""",
mandatory = False,
providers = [[CcInfo]],
),
# Do not add references; temporary attribute for C++ toolchain Skylark migration.
"_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")),
},
)
mandatory = False,
providers = [[CcInfo]],
),
"stamp": attr.bool(
default = stamp_default,
doc = """
Enable or disable link stamping.
If this value is true (and if the toolchain supports link stamping), then the toolchain's stamping
logic will be invoked to link additional identifying information into the binary. This information
typically comes from the stable and volatile build information written by Bazel in the output
directory, but could be anything that the toolchain wishes to link into binaries.
If false, no stamp information will be linked into the binary, which improves build caching.
""",
mandatory = False,
),
# Do not add references; temporary attribute for C++ toolchain Skylark migration.
"_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")),
},
)

def _configure_features_for_binary(ctx, requested_features = [], unsupported_features = []):
"""Creates and returns the feature configuration for binary linking.
Expand Down Expand Up @@ -184,9 +206,22 @@ def _swift_linking_rule_impl(
if ctx.attr.malloc:
deps_to_link.append(ctx.attr.malloc)

additional_linking_contexts = []
if swift_toolchain.stamp_producer:
stamp_context = partial.call(
swift_toolchain.stamp_producer,
ctx,
cc_feature_configuration,
swift_toolchain.cc_toolchain_info,
out_bin,
)
if stamp_context:
additional_linking_contexts.append(stamp_context)

register_link_executable_action(
actions = ctx.actions,
action_environment = swift_toolchain.action_environment,
additional_linking_contexts = additional_linking_contexts,
cc_feature_configuration = cc_feature_configuration,
clang_executable = swift_toolchain.clang_executable,
deps = deps_to_link,
Expand Down Expand Up @@ -329,7 +364,7 @@ def _swift_test_impl(ctx):
]

swift_binary = rule(
attrs = _BINARY_RULE_ATTRS,
attrs = _binary_rule_attrs(stamp_default = True),
doc = """
Compiles and links Swift code into an executable binary.
Expand Down Expand Up @@ -357,7 +392,7 @@ instead of `swift_binary`.

swift_test = rule(
attrs = dicts.add(
_BINARY_RULE_ATTRS,
_binary_rule_attrs(stamp_default = False),
{
"_apple_coverage_support": attr.label(
cfg = "host",
Expand Down
9 changes: 1 addition & 8 deletions swift/internal/swift_toolchain.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def _swift_toolchain_impl(ctx):
object_format = "elf",
requested_features = requested_features,
root_dir = toolchain_root,
stamp = ctx.attr.stamp,
stamp_producer = None,
supports_objc_interop = False,
swiftc_copts = [],
swift_worker = ctx.executable._worker,
Expand Down Expand Up @@ -162,13 +162,6 @@ content, such as "linux" in "lib/swift/linux".
"root": attr.string(
mandatory = True,
),
"stamp": attr.label(
doc = """
A `CcInfo`-providing target that should be linked into any binaries that are built with stamping
enabled.
""",
providers = [[CcInfo]],
),
"_cc_toolchain": attr.label(
default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
doc = """
Expand Down
9 changes: 1 addition & 8 deletions swift/internal/xcode_swift_toolchain.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ def _xcode_swift_toolchain_impl(ctx):
object_format = "macho",
requested_features = requested_features,
root_dir = None,
stamp = ctx.attr.stamp if _is_macos(platform) else None,
stamp_producer = None,
supports_objc_interop = True,
swiftc_copts = swiftc_copts,
swift_worker = ctx.executable._worker,
Expand All @@ -342,13 +342,6 @@ def _xcode_swift_toolchain_impl(ctx):

xcode_swift_toolchain = rule(
attrs = dicts.add({
"stamp": attr.label(
doc = """
A `CcInfo`-providing target that should be linked into any binaries that are built with stamping
enabled.
""",
providers = [[CcInfo]],
),
"_cc_toolchain": attr.label(
default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
doc = """
Expand Down

0 comments on commit a2c1bfc

Please sign in to comment.