Skip to content

Commit

Permalink
Update rules for ObjcProvider deprecations (#850)
Browse files Browse the repository at this point in the history
In Bazel 7+ `ObjcProvider` no longer supports/provides the required
linking attributes.

The migrations is detailed here:
bazelbuild/bazel#16939. In summary, as part of
the migration, the `ObjcProvider` fields which previously provided
linking related information are now now longer providing that info. In
addition to this, a new flag:
`--incompatible_objc_linking_info_migration` was added to further delete
these link attrs from the `ObjcProvider` making it an error if the attr
is used or set.

The goal of this PR is to address support for `ObjcProvider` migration
and to instead use the correct linking information from `CcInfo`. This
will support both Bazel 6/7+. It does not try to support
`--incompatible_objc_linking_info_migration` as that requires more
changes and should be a separate PR

Depends on: 

- #848 
- #847
  • Loading branch information
luispadron authored Apr 18, 2024
1 parent 76c35f8 commit 795191e
Show file tree
Hide file tree
Showing 9 changed files with 203 additions and 49 deletions.
124 changes: 106 additions & 18 deletions rules/force_load_direct_deps.bzl
Original file line number Diff line number Diff line change
@@ -1,36 +1,116 @@
load("@bazel_skylib//lib:paths.bzl", "paths")
load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain", "use_cpp_toolchain")
load("//rules:providers.bzl", "AvoidDepsInfo")
load("//rules:transition_support.bzl", "transition_support")

def _impl(ctx):
if not ctx.attr.should_force_load:
return apple_common.new_objc_provider()
def _objc_provider_static_libraries(dep):
"""Returns the ObjcProvider static libraries (.a) that should be force loaded.
"""
if not apple_common.Objc in dep:
return []

return dep[apple_common.Objc].library.to_list()

def _cc_info_static_libraries(dep):
"""Returns the CcInfo static libraries (.a) that should be force loaded.
NOTE: CcInfo, unlike ObjcProvider, does not encode where the static library came from.
In the existing `_objc_provider_static_libraries` we only collect `.library` from ObjcProvider.
ObjcProvider `.library` list static library dependencies of the current target,
it does not include imported static libraries (such as those from `.framework` files).
CcInfo only provides `.static_library` and does not make this distinction.
To match this behavior, we only collect `.static_library` from CcInfo that are not from `.framework`s.
"""
if not CcInfo in dep:
return []

static_cc_libraries = []
for linker_input in dep[CcInfo].linking_context.linker_inputs.to_list():
for library_to_link in linker_input.libraries:
if not library_to_link.static_library:
continue
containing_path = paths.dirname(library_to_link.static_library.path)
if containing_path.endswith(".framework"):
continue
static_cc_libraries.append(library_to_link.static_library)

force_load = []
return static_cc_libraries

# TODO: We should deprecate this rule for Bazel 7+ as `--incompatible_objc_alwayslink_by_default` effectively
# does the same thing.
def _force_load_direct_deps_impl(ctx):
"""This rule will traverse the direct deps of the target and force load the static libraries of the objc deps.
"""

if not ctx.attr.should_force_load:
return [
apple_common.new_objc_provider(),
CcInfo(),
]

force_load_libraries = []
force_load_cc_libraries = []
avoid_deps = []
avoid_libraries = {}
avoid_cc_libraries = {}
cc_toolchain = find_cpp_toolchain(ctx)
cc_features = cc_common.configure_features(
ctx = ctx,
cc_toolchain = cc_toolchain,
language = "objc",
)

# Set the deps that should be avoided and not linked.
for dep in ctx.attr.deps:
if AvoidDepsInfo in dep:
avoid_deps.extend(dep[AvoidDepsInfo].libraries)

avoid_libraries = {}
# Collect the libraries that should be avoided.
for dep in avoid_deps:
if apple_common.Objc in dep:
for lib in dep[apple_common.Objc].library.to_list():
avoid_libraries[lib] = True
for lib in _objc_provider_static_libraries(dep):
avoid_libraries[lib] = True
for lib in _cc_info_static_libraries(dep):
avoid_cc_libraries[lib] = True

force_load = []
# Collect the libraries that should be force loaded.
for dep in ctx.attr.deps:
if apple_common.Objc in dep:
for lib in dep[apple_common.Objc].library.to_list():
if not lib in avoid_libraries:
force_load.append(lib)
return apple_common.new_objc_provider(
force_load_library = depset(force_load),
link_inputs = depset(force_load),
)
for lib in _objc_provider_static_libraries(dep):
if not lib in avoid_libraries:
force_load_libraries.append(lib)
for lib in _cc_info_static_libraries(dep):
if not lib in avoid_cc_libraries:
force_load_cc_libraries.append(lib)

return [
apple_common.new_objc_provider(
force_load_library = depset(force_load_libraries),
link_inputs = depset(force_load_libraries),
),
CcInfo(
linking_context = cc_common.create_linking_context(
linker_inputs = depset([
cc_common.create_linker_input(
owner = ctx.label,
libraries = depset([
cc_common.create_library_to_link(
actions = ctx.actions,
cc_toolchain = cc_toolchain,
feature_configuration = cc_features,
static_library = library,
alwayslink = True,
)
for library in force_load_cc_libraries
]),
),
]),
),
),
]

force_load_direct_deps = rule(
implementation = _impl,
implementation = _force_load_direct_deps_impl,
toolchains = use_cpp_toolchain(),
fragments = ["apple", "cpp", "objc"],
attrs = {
"deps": attr.label_list(
cfg = transition_support.apple_platform_split_transition,
Expand Down Expand Up @@ -58,6 +138,14 @@ force_load_direct_deps = rule(
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
doc = "Needed to allow this rule to have an incoming edge configuration transition.",
),
"_cc_toolchain": attr.label(
providers = [cc_common.CcToolchainInfo],
default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
doc = """\
The C++ toolchain from which linking flags and other tools needed by the Swift
toolchain (such as `clang`) will be retrieved.
""",
),
},
doc = """
A rule to link with `-force_load` for direct`deps`
Expand Down
32 changes: 27 additions & 5 deletions rules/framework.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ load("//rules:library.bzl", "PrivateHeadersInfo", "apple_library")
load("//rules:plists.bzl", "process_infoplists")
load("//rules:providers.bzl", "AvoidDepsInfo", "FrameworkInfo")
load("//rules:transition_support.bzl", "transition_support")
load("//rules:utils.bzl", "is_bazel_7")
load("//rules/internal:objc_provider_utils.bzl", "objc_provider_utils")
load("@bazel_skylib//lib:partial.bzl", "partial")
load("@bazel_skylib//lib:paths.bzl", "paths")
Expand Down Expand Up @@ -534,6 +535,18 @@ def _get_symlinked_framework_clean_action(ctx, framework_files, compilation_cont
else:
ctx.actions.write(framework_manifest, "# Empty framework\n")

def _get_cc_info_linker_inputs(*, deps):
linker_inputs = []

for dep in deps:
if not CcInfo in dep:
continue

for linker_input in dep[CcInfo].linking_context.linker_inputs.to_list():
linker_inputs.append(linker_input)

return depset(linker_inputs)

def _create_swiftmodule(attrs):
kwargs = {}

Expand Down Expand Up @@ -986,7 +999,9 @@ def _apple_framework_packaging_impl(ctx):

split_slice_key = "{}_{}{}".format(platform, split_slice_varint, arch)
deps = _attrs_for_split_slice(ctx.split_attr.deps, split_slice_key)
dep_cc_infos = [dep[CcInfo] for dep in deps if CcInfo in dep]
transitive_deps = _attrs_for_split_slice(ctx.split_attr.transitive_deps, split_slice_key)
transitive_dep_cc_infos = [dep[CcInfo] for dep in transitive_deps if CcInfo in dep]
vfs = _attrs_for_split_slice(ctx.split_attr.vfs, split_slice_key)

current_apple_platform = transition_support.current_apple_platform(apple_fragment = ctx.fragments.apple, xcode_config = ctx.attr._xcode_config)
Expand All @@ -1004,15 +1019,15 @@ def _apple_framework_packaging_impl(ctx):
))
objc_provider_utils.add_to_dict_if_present(compilation_context_fields, "defines", depset(
direct = [],
transitive = [getattr(dep[CcInfo].compilation_context, "defines") for dep in deps if CcInfo in dep],
transitive = [getattr(cc_info.compilation_context, "defines") for cc_info in dep_cc_infos],
))
objc_provider_utils.add_to_dict_if_present(compilation_context_fields, "includes", depset(
direct = [],
transitive = [getattr(dep[CcInfo].compilation_context, "includes") for dep in deps if CcInfo in dep],
transitive = [getattr(cc_info.compilation_context, "includes") for cc_info in dep_cc_infos],
))
objc_provider_utils.add_to_dict_if_present(compilation_context_fields, "framework_includes", depset(
direct = [],
transitive = [getattr(dep[CcInfo].compilation_context, "framework_includes") for dep in deps if CcInfo in dep],
transitive = [getattr(cc_info.compilation_context, "framework_includes") for cc_info in dep_cc_infos],
))

# Compute cc_info and swift_info
Expand All @@ -1031,17 +1046,23 @@ def _apple_framework_packaging_impl(ctx):
# If not virtualizing the framework - then it runs a "clean"
_get_symlinked_framework_clean_action(ctx, framework_files, compilation_context_fields)

# Construct the `CcInfo` provider, the linking context here used instead of ObjcProvider in Bazel 7+.
cc_info_provider = CcInfo(
compilation_context = cc_common.create_compilation_context(
**compilation_context_fields
),
linking_context = cc_common.create_linking_context(
linker_inputs = _get_cc_info_linker_inputs(deps = deps) if is_bazel_7 else depset([]),
),
)

if virtualize_frameworks:
cc_info = cc_common.merge_cc_infos(direct_cc_infos = [cc_info_provider])
else:
dep_cc_infos = [dep[CcInfo] for dep in transitive_deps if CcInfo in dep]
cc_info = cc_common.merge_cc_infos(direct_cc_infos = [cc_info_provider], cc_infos = dep_cc_infos)
cc_info = cc_common.merge_cc_infos(
direct_cc_infos = [cc_info_provider],
cc_infos = transitive_dep_cc_infos,
)

# Propagate the avoid deps information upwards
avoid_deps = []
Expand Down Expand Up @@ -1072,6 +1093,7 @@ def _apple_framework_packaging_impl(ctx):
providers = [dep[apple_common.Objc] for dep in deps if apple_common.Objc in dep],
transitive = [dep[apple_common.Objc] for dep in transitive_deps if apple_common.Objc in dep],
)

return [
avoid_deps_info,
framework_info,
Expand Down
18 changes: 6 additions & 12 deletions rules/import_middleman.bzl
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
load("@build_bazel_rules_apple//apple/internal:providers.bzl", "AppleFrameworkImportInfo", "new_appleframeworkimportinfo")
load("//rules:features.bzl", "feature_names")
load("//rules/internal:objc_provider_utils.bzl", "objc_provider_utils")
load("@build_bazel_rules_apple//apple/internal:bundling_support.bzl", "bundling_support")

Expand Down Expand Up @@ -174,7 +173,6 @@ def _file_collector_rule_impl(ctx):
# This should be correctly configured upstream: see setup in rules_ios
fail("using import_middleman ({}) on wrong transition ({},{},is_device={})".format(ctx.attr.name, platform, arch, ctx.fragments.apple.single_arch_platform.is_device))

virtualize_frameworks = feature_names.virtualize_frameworks in ctx.features
merge_keys = [
"sdk_dylib",
"sdk_framework",
Expand All @@ -184,17 +182,13 @@ def _file_collector_rule_impl(ctx):
"link_inputs",
"linkopt",
"library",
] + ([] if is_sim_arm64 else [
# Merge in the objc provider fields
"imported_library",
"dynamic_framework_file",
"static_framework_file",
])
]

objc_provider_fields = objc_provider_utils.merge_objc_providers_dict(
providers = [dep[apple_common.Objc] for dep in ctx.attr.deps],
merge_keys = merge_keys,
)

exisiting_imported_libraries = objc_provider_fields.get("imported_library", depset([]))
replaced_imported_libraries = _replace_inputs(ctx, exisiting_imported_libraries, input_imported_libraries, _update_lib).inputs
objc_provider_fields["imported_library"] = depset(_deduplicate_test_deps(test_linker_deps[1], replaced_imported_libraries))
Expand Down Expand Up @@ -260,15 +254,15 @@ def _file_collector_rule_impl(ctx):
**objc_provider_fields
)

additional_providers = []
# Create the CcInfo provider, linking information from this is used in Bazel 7+.
dep_cc_infos = [dep[CcInfo] for dep in ctx.attr.deps if CcInfo in dep]
cc_info = cc_common.merge_cc_infos(direct_cc_infos = [], cc_infos = dep_cc_infos)
additional_providers.append(cc_info)
cc_info = cc_common.merge_cc_infos(cc_infos = dep_cc_infos)

return [
DefaultInfo(files = depset(dynamic_framework_dirs + replaced_frameworks)),
objc,
] + _make_imports(dynamic_framework_dirs) + additional_providers
cc_info,
] + _make_imports(dynamic_framework_dirs)

import_middleman = rule(
implementation = _file_collector_rule_impl,
Expand Down
Loading

0 comments on commit 795191e

Please sign in to comment.