Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Propagate Swift-specific linker flags as an implicit dependency of the toolchain. #1186

Merged
merged 2 commits into from Jun 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 0 additions & 18 deletions apple/apple_binary.bzl
Expand Up @@ -22,14 +22,6 @@ load(
"@build_bazel_rules_apple//apple/internal:rule_factory.bzl",
"rule_factory",
)
load(
"@build_bazel_rules_apple//apple/internal:swift_support.bzl",
"swift_support",
)
load(
"@build_bazel_rules_swift//swift:swift.bzl",
"swift_common",
)

def _linker_flag_for_sdk_dylib(dylib):
"""Returns a linker flag suitable for linking the given `sdk_dylib` value.
Expand Down Expand Up @@ -60,16 +52,6 @@ def _apple_binary_impl(ctx):
for framework in ctx.attr.weak_sdk_frameworks
]

deps = getattr(ctx.attr, "deps", [])
swift_usage_info = swift_support.swift_usage_info(deps)
if swift_usage_info:
swift_linkopts = swift_common.swift_runtime_linkopts(
is_static = False,
is_test = False,
toolchain = swift_usage_info.toolchain,
)
extra_linkopts.extend(swift_linkopts)

link_result = linking_support.register_linking_action(
ctx,
extra_linkopts = extra_linkopts,
Expand Down
30 changes: 24 additions & 6 deletions apple/internal/apple_framework_import.bzl
Expand Up @@ -174,12 +174,15 @@ def _grouped_framework_files(framework_imports):

return framework_groups

def _objc_provider_with_dependencies(ctx, objc_provider_fields):
def _objc_provider_with_dependencies(ctx, objc_provider_fields, additional_objc_infos = []):
"""Returns a new Objc provider which includes transitive Objc dependencies."""
objc_provider_fields["providers"] = [dep[apple_common.Objc] for dep in ctx.attr.deps]
objc_provider_fields["providers"] = [
dep[apple_common.Objc]
for dep in ctx.attr.deps
] + additional_objc_infos
return apple_common.new_objc_provider(**objc_provider_fields)

def _cc_info_with_dependencies(ctx, header_imports):
def _cc_info_with_dependencies(ctx, header_imports, additional_cc_infos = []):
"""Returns a new CcInfo which includes transitive Cc dependencies."""
cc_info = CcInfo(
compilation_context = cc_common.create_compilation_context(
Expand All @@ -189,7 +192,7 @@ def _cc_info_with_dependencies(ctx, header_imports):
)
dep_cc_infos = [dep[CcInfo] for dep in ctx.attr.deps]
return cc_common.merge_cc_infos(
cc_infos = [cc_info] + dep_cc_infos,
cc_infos = [cc_info] + dep_cc_infos + additional_cc_infos,
)

def _transitive_framework_imports(deps):
Expand Down Expand Up @@ -415,18 +418,33 @@ def _apple_static_framework_import_impl(ctx):
if _is_swiftmodule(header.basename)
]

additional_objc_infos = []
additional_cc_infos = []

if swiftmodule_imports:
toolchain = ctx.attr._toolchain[SwiftToolchainInfo]
providers.append(SwiftUsageInfo(toolchain = toolchain))

# The Swift toolchain propagates Swift-specific linker flags (e.g.,
# library/framework search paths) as an implicit dependency. In the
# rare case that a binary has a Swift framework import dependency but
# no other Swift dependencies, make sure we pick those up so that it
# links to the standard libraries correctly.
additional_objc_infos.extend(toolchain.implicit_deps_providers.objc_infos)
additional_cc_infos.extend(toolchain.implicit_deps_providers.cc_infos)

if _is_debugging(ctx):
cpu = ctx.fragments.apple.single_arch_cpu
swiftmodule = _swiftmodule_for_cpu(swiftmodule_imports, cpu)
if swiftmodule:
objc_provider_fields.update(_ensure_swiftmodule_is_embedded(swiftmodule))

providers.append(_objc_provider_with_dependencies(ctx, objc_provider_fields))
providers.append(_cc_info_with_dependencies(ctx, header_imports))
providers.append(
_objc_provider_with_dependencies(ctx, objc_provider_fields, additional_objc_infos),
)
providers.append(
_cc_info_with_dependencies(ctx, header_imports, additional_cc_infos),
)

# For now, Swift interop is restricted only to a Clang module map inside
# the framework.
Expand Down
7 changes: 2 additions & 5 deletions apple/internal/aspects/swift_dynamic_framework_aspect.bzl
Expand Up @@ -82,11 +82,8 @@ framework module {module_name} {{
def _swift_dynamic_framework_aspect_impl(target, ctx):
"""Aspect implementation for Swift dynamic framework support."""

if not hasattr(ctx.rule.attr, "deps"):
return []

swiftdeps = [x for x in ctx.rule.attr.deps if SwiftInfo in x]
ccinfos = [x for x in ctx.rule.attr.deps if CcInfo in x]
swiftdeps = [x for x in [target] if SwiftInfo in x]
ccinfos = [x for x in [target] if CcInfo in x]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great! It cleans up the implementation a lot and makes more sense without the swift_runtime_linkopts dependency


# If there are no Swift dependencies, return nothing.
if not swiftdeps:
Expand Down
68 changes: 4 additions & 64 deletions apple/internal/binary_support.bzl
Expand Up @@ -18,61 +18,20 @@ load(
"@build_bazel_rules_apple//apple/internal:entitlement_rules.bzl",
"entitlements",
)
load(
"@build_bazel_rules_apple//apple/internal:swift_support.bzl",
"swift_runtime_linkopts",
)

def _create_swift_runtime_linkopts_target(
name,
deps,
is_static,
is_test,
tags,
testonly):
"""Creates a build target to propagate Swift runtime linker flags.

Args:
name: The name of the base target.
deps: The list of dependencies of the base target.
is_static: True to use the static Swift runtime, or False to use the
dynamic Swift runtime.
is_test: True to make sure test specific linkopts are propagated.
tags: Tags to add to the created targets.
testonly: Whether the target should be testonly.

Returns:
A build label that can be added to the deps of the binary target.
"""
swift_runtime_linkopts_name = name + ".swift_runtime_linkopts"
swift_runtime_linkopts(
name = swift_runtime_linkopts_name,
is_static = is_static,
is_test = is_test,
testonly = testonly,
tags = tags,
deps = deps,
)
return ":" + swift_runtime_linkopts_name

def _add_entitlements_and_swift_linkopts(
def _add_entitlements(
name,
platform_type,
product_type,
include_entitlements = True,
is_stub = False,
link_swift_statically = False,
is_test = False,
**kwargs):
"""Adds entitlements and Swift linkopts targets for a bundle target.
"""Adds entitlements targets for a bundle target.

This function creates an entitlements target to ensure that a binary
created using the `link_multi_arch_binary` API or by copying a stub
executable gets signed appropriately.

Similarly, for bundles with user-provided binaries, this function also
adds any Swift linkopts that are necessary for it to link correctly.

Args:
name: The name of the bundle target, from which the targets' names
will be derived.
Expand All @@ -82,9 +41,6 @@ def _add_entitlements_and_swift_linkopts(
Defaults to True.
is_stub: True/False, indicates whether the function is being called for a bundle that uses a
stub executable.
link_swift_statically: True/False, indicates whether the static versions of the Swift standard
libraries should be used during linking. Only used if include_swift_linkopts is True.
is_test: True/False, indicates if test specific linker flags should be propagated.
**kwargs: The arguments that were passed into the top-level macro.

Returns:
Expand Down Expand Up @@ -119,29 +75,13 @@ def _add_entitlements_and_swift_linkopts(
# propagate linkopts.
additional_deps.append(":{}".format(entitlements_name))

deps = bundling_args.get("deps", [])

if not is_stub:
# Propagate the linker flags that dynamically link the Swift runtime, if Swift was used. If
# it wasn't, this target propagates no linkopts.
additional_deps.append(
_create_swift_runtime_linkopts_target(
name,
deps,
link_swift_statically,
bool(is_test or testonly),
tags = tags,
testonly = testonly,
),
)

all_deps = deps + additional_deps
all_deps = bundling_args.get("deps", []) + additional_deps
if all_deps:
bundling_args["deps"] = all_deps

return bundling_args

# Define the loadable module that lists the exported symbols in this file.
binary_support = struct(
add_entitlements_and_swift_linkopts = _add_entitlements_and_swift_linkopts,
add_entitlements = _add_entitlements,
)
7 changes: 3 additions & 4 deletions apple/internal/ios_rules.bzl
Expand Up @@ -1013,17 +1013,16 @@ def _ios_extension_impl(ctx):
def _ios_dynamic_framework_impl(ctx):
"""Experimental implementation of ios_dynamic_framework."""

# This rule should only have one swift_library dependency. This means len(ctx.attr.deps) should be 2
# because of the swift_runtime_linkopts dep that comes with the swift_libray
# This rule should only have one swift_library dependency. This means len(ctx.attr.deps) should be 1
swiftdeps = [x for x in ctx.attr.deps if SwiftInfo in x]
if len(swiftdeps) != 1 or len(ctx.attr.deps) > 2:
if len(swiftdeps) != 1 or len(ctx.attr.deps) > 1:
fail(
"""\
error: Swift dynamic frameworks expect a single swift_library dependency.
""",
)

binary_target = [deps for deps in ctx.attr.deps if deps.label.name.endswith("swift_runtime_linkopts")][0]
binary_target = ctx.attr.deps[0]
extra_linkopts = []
if ctx.attr.extension_safe:
extra_linkopts.append("-fapplication-extension")
Expand Down
44 changes: 0 additions & 44 deletions apple/internal/swift_support.bzl
Expand Up @@ -17,8 +17,6 @@
load(
"@build_bazel_rules_swift//swift:swift.bzl",
"SwiftUsageInfo",
"swift_common",
"swift_usage_aspect",
)

def _swift_usage_info(targets):
Expand Down Expand Up @@ -56,48 +54,6 @@ def _uses_swift(targets):
"""
return (_swift_usage_info(targets) != None)

def _swift_runtime_linkopts_impl(ctx):
"""Implementation of the internal `swift_runtime_linkopts` rule.

This rule is an internal implementation detail and should not be used directly
by clients. It examines the dependencies of the target to determine if Swift
was used and, if so, propagates additional linker options to have the runtime
either dynamically or statically linked.

Args:
ctx: The rule context.

Returns:
A `struct` containing the `objc` provider that should be propagated to a
binary to dynamically or statically link the Swift runtime.
"""
linkopts = []
swift_usage_info = _swift_usage_info(ctx.attr.deps)
if swift_usage_info:
linkopts.extend(swift_common.swift_runtime_linkopts(
is_static = ctx.attr.is_static,
is_test = ctx.attr.is_test,
toolchain = swift_usage_info.toolchain,
))

if linkopts:
return [apple_common.new_objc_provider(linkopt = depset(linkopts, order = "topological"))]
else:
return [apple_common.new_objc_provider()]

swift_runtime_linkopts = rule(
_swift_runtime_linkopts_impl,
attrs = {
"is_static": attr.bool(mandatory = True),
"is_test": attr.bool(mandatory = True),
"deps": attr.label_list(
aspects = [swift_usage_aspect],
mandatory = True,
),
},
fragments = ["apple", "objc"],
)

# Define the loadable module that lists the exported symbols in this file.
swift_support = struct(
swift_usage_info = _swift_usage_info,
Expand Down
3 changes: 1 addition & 2 deletions apple/internal/testing/apple_test_assembler.bzl
Expand Up @@ -98,12 +98,11 @@ def _assemble(name, bundle_rule, test_rule, runner = None, runners = None, **kwa
if "bundle_name" in kwargs:
fail("bundle_name is not supported in test rules.")

bundling_args = binary_support.add_entitlements_and_swift_linkopts(
bundling_args = binary_support.add_entitlements(
test_bundle_name,
bundle_name = name,
platform_type = str(apple_common.platform_type.ios),
product_type = apple_product_type.unit_test_bundle,
is_test = True,
include_entitlements = False,
testonly = True,
**bundle_attrs
Expand Down
7 changes: 3 additions & 4 deletions apple/internal/tvos_rules.bzl
Expand Up @@ -302,17 +302,16 @@ def _tvos_application_impl(ctx):
def _tvos_dynamic_framework_impl(ctx):
"""Experimental implementation of tvos_dynamic_framework."""

# This rule should only have one swift_library dependency. This means len(ctx.attr.deps) should be 2
# because of the swift_runtime_linkopts dep that comes with the swift_libray
# This rule should only have one swift_library dependency. This means len(ctx.attr.deps) should be 1
swiftdeps = [x for x in ctx.attr.deps if SwiftInfo in x]
if len(swiftdeps) != 1 or len(ctx.attr.deps) > 2:
if len(swiftdeps) != 1 or len(ctx.attr.deps) > 1:
fail(
"""\
error: Swift dynamic frameworks expect a single swift_library dependency.
""",
)

binary_target = [deps for deps in ctx.attr.deps if deps.label.name.endswith("swift_runtime_linkopts")][0]
binary_target = ctx.attr.deps[0]
link_result = linking_support.register_linking_action(
ctx,
stamp = ctx.attr.stamp,
Expand Down
7 changes: 3 additions & 4 deletions apple/internal/watchos_rules.bzl
Expand Up @@ -94,17 +94,16 @@ load(
def _watchos_dynamic_framework_impl(ctx):
"""Experimental implementation of watchos_dynamic_framework."""

# This rule should only have one swift_library dependency. This means len(ctx.attr.deps) should be 2
# because of the swift_runtime_linkopts dep that comes with the swift_libray
# This rule should only have one swift_library dependency. This means len(ctx.attr.deps) should be 1
swiftdeps = [x for x in ctx.attr.deps if SwiftInfo in x]
if len(swiftdeps) != 1 or len(ctx.attr.deps) > 2:
if len(swiftdeps) != 1 or len(ctx.attr.deps) > 1:
fail(
"""\
error: Swift dynamic frameworks expect a single swift_library dependency.
""",
)

binary_target = [deps for deps in ctx.attr.deps if deps.label.name.endswith("swift_runtime_linkopts")][0]
binary_target = ctx.attr.deps[0]
extra_linkopts = []
if ctx.attr.extension_safe:
extra_linkopts.append("-fapplication-extension")
Expand Down