Create deferred implementation wrappers and add implicit dep.

A future change will use these wrapped functions instead of directly loading the implementation.

Also delete some duplicate definitions introduced in the previous change.

PiperOrigin-RevId: 874599289
Change-Id: I1934885db00044e9d40ba2b12973ca9e185636fa
diff --git a/cc/private/rules_impl/BUILD b/cc/private/rules_impl/BUILD
index d924c5e..1bbece7 100644
--- a/cc/private/rules_impl/BUILD
+++ b/cc/private/rules_impl/BUILD
@@ -18,6 +18,7 @@
         "**/BUILD",
     ]) + [
         "//cc/private/rules_impl/fdo:srcs",
+        "//cc/private/rules_impl/wrappers:srcs",
     ],
 )
 
@@ -57,6 +58,7 @@
         "@cc_compatibility_proxy//:__pkg__",
     ],
     deps = [
+        ":function_providing_rule_bzl",
         ":objc_common",
         ":objc_compilation_support_bzl",
         "//cc:action_names_bzl",
@@ -145,3 +147,9 @@
         "@cc_compatibility_proxy//:__pkg__",
     ],
 )
+
+bzl_library(
+    name = "function_providing_rule_bzl",
+    srcs = ["function_providing_rule.bzl"],
+    visibility = ["//cc:__subpackages__"],
+)
diff --git a/cc/private/rules_impl/cc_binary.bzl b/cc/private/rules_impl/cc_binary.bzl
index cb11b07..d5f0b71 100644
--- a/cc/private/rules_impl/cc_binary.bzl
+++ b/cc/private/rules_impl/cc_binary.bzl
@@ -49,7 +49,10 @@
   empty file.</li>
 </ul>
 """ + semantics.cc_binary_extra_docs,
-    attrs = cc_binary_attrs,
+    attrs = cc_binary_attrs | {"_impl_delegate": attr.label(
+        default = Label("//cc/private/rules_impl/wrappers:cc_binary_impl_wrapper"),
+        cfg = "exec",
+    )},
     outputs = {
         "dwp_file": "%{name}.dwp",
         "stripped_binary": "%{name}.stripped",
diff --git a/cc/private/rules_impl/cc_binary_impl.bzl b/cc/private/rules_impl/cc_binary_impl.bzl
index fb4ef55..2306143 100644
--- a/cc/private/rules_impl/cc_binary_impl.bzl
+++ b/cc/private/rules_impl/cc_binary_impl.bzl
@@ -23,11 +23,10 @@
 load("//cc/common:debug_package_info.bzl", "DebugPackageInfo")
 load("//cc/common:semantics.bzl", "semantics")
 load("//cc/private:graph_node_info.bzl", "GraphNodeInfo")
-load(":cc_shared_library.bzl", "add_unused_dynamic_deps", "build_exports_map_from_only_dynamic_deps", "build_link_once_static_libs_map", "merge_cc_shared_library_infos", "separate_static_and_dynamic_link_libraries", "sort_linker_inputs", "throw_linked_but_not_exported_errors")
+load(":cc_shared_library_impl.bzl", "add_unused_dynamic_deps", "build_exports_map_from_only_dynamic_deps", "build_link_once_static_libs_map", "merge_cc_shared_library_infos", "separate_static_and_dynamic_link_libraries", "sort_linker_inputs", "throw_linked_but_not_exported_errors")
+load(":function_providing_rule.bzl", "wrap_starlark_function")
 
-visibility("private")
-
-_CcLauncherInfo = cc_common.launcher_provider
+_CcLauncherInfo = getattr(cc_common, "launcher_provider", None)
 
 # TODO(blaze-team): cleanup lint target types
 _EXECUTABLE = "executable"
@@ -871,3 +870,4 @@
     return providers
 
 impl = _impl
+cc_binary_impl_wrapper = wrap_starlark_function(_impl)
diff --git a/cc/private/rules_impl/cc_library.bzl b/cc/private/rules_impl/cc_library.bzl
index 2536893..2c0530a 100755
--- a/cc/private/rules_impl/cc_library.bzl
+++ b/cc/private/rules_impl/cc_library.bzl
@@ -419,6 +419,10 @@
         "_stl": semantics.get_stl(),
         "_def_parser": semantics.get_def_parser(),
         "_use_auto_exec_groups": attr.bool(default = True),
+        "_impl_delegate": attr.label(
+            default = Label("//cc/private/rules_impl/wrappers:cc_library_impl_wrapper"),
+            cfg = "exec",
+        ),
     } | semantics.get_implementation_deps_allowed_attr() | semantics.get_nocopts_attr(),  # buildifier: disable=attr-licenses
     toolchains = use_cc_toolchain() + semantics.get_runtimes_toolchain(),
     fragments = ["cpp"] + semantics.additional_fragments(),
diff --git a/cc/private/rules_impl/cc_library_impl.bzl b/cc/private/rules_impl/cc_library_impl.bzl
index f17c75b..dabef76 100755
--- a/cc/private/rules_impl/cc_library_impl.bzl
+++ b/cc/private/rules_impl/cc_library_impl.bzl
@@ -19,8 +19,7 @@
 load("//cc/common:cc_helper.bzl", "cc_helper")
 load("//cc/common:cc_info.bzl", "CcInfo")
 load("//cc/common:semantics.bzl", "semantics")
-
-visibility("private")
+load(":function_providing_rule.bzl", "wrap_starlark_function")
 
 def _cc_library_impl(ctx):
     semantics.validate(ctx, "cc_library")
@@ -556,3 +555,4 @@
 LINKER_SCRIPT = [".ld", ".lds", ".ldscript"]
 
 cc_library_impl = _cc_library_impl
+cc_library_impl_wrapper = wrap_starlark_function(_cc_library_impl)
diff --git a/cc/private/rules_impl/cc_shared_library.bzl b/cc/private/rules_impl/cc_shared_library.bzl
index 4284d75..0d53754 100644
--- a/cc/private/rules_impl/cc_shared_library.bzl
+++ b/cc/private/rules_impl/cc_shared_library.bzl
@@ -32,291 +32,6 @@
 # used sparingly after making sure it's safe to use.
 LINKABLE_MORE_THAN_ONCE = "LINKABLE_MORE_THAN_ONCE"
 
-def _programmatic_error(message = ""):
-    fail("Your build has triggered a programmatic error in the cc_shared_library rule. " +
-         "Please file an issue in https://github.com/bazelbuild/bazel : {}".format(message))
-
-def _sort_linker_inputs(topologically_sorted_labels, label_to_linker_inputs, linker_inputs_count):
-    # len(label_to_linker_inputs) might not match the topologically_sorted_labels
-    # size. The latter is looking at nodes in the graph but a node may not
-    # necessarily contribute any linker_inputs. For example a cc_library with
-    # no sources and only deps. Every linker_input.owner must be in
-    # topologically_sorted_labels otherwise there is an error in the rule
-    # implementation of the target providing that linker_input, e.g. it's
-    # missing a CcSharedLibraryHintInfo if it had custom owner names for linker
-    # inputs.
-    sorted_linker_inputs = []
-    for label in topologically_sorted_labels:
-        if label not in label_to_linker_inputs:
-            # This is ok. It can happen if no linker_inputs
-            # were added by a node in the graph.
-            continue
-        sorted_linker_inputs.extend(label_to_linker_inputs[label])
-
-    if len(sorted_linker_inputs) != linker_inputs_count:
-        owners = []
-        for sorted_linker_input in sorted_linker_inputs:
-            owners.append(str(sorted_linker_input.owner))
-        _programmatic_error("{} vs {}".format(",".join(owners), linker_inputs_count))
-
-    return sorted_linker_inputs
-
-# For each target, find out whether it should be linked statically or
-# dynamically. The transitive_dynamic_dep_labels parameter is only needed for
-# binaries because they link all dynamic_deps (cc_binary|cc_test).
-def _separate_static_and_dynamic_link_libraries(
-        dynamic_deps,
-        deps_graph_nodes,
-        can_be_linked_dynamically):
-    (
-        transitive_dynamic_dep_labels,
-        all_dynamic_dep_linker_inputs,
-    ) = _build_map_direct_dynamic_dep_to_transitive_dynamic_deps(dynamic_deps)
-
-    node = None
-    deps_graph_nodes_to_process = reversed(deps_graph_nodes)
-    targets_to_be_linked_statically_map = {}
-    targets_to_be_linked_dynamically_set = {}
-    seen_labels = {}
-
-    # The cc_shared_library graph is parallel to the cc_library graph.
-    # Propagation of linker inputs between cc_libraries happens via the CcInfo
-    # provider. Parallel to this we have cc_shared_libraries which may decide
-    # different partitions of the cc_library graph.
-    #
-    # In a previous implementation of cc_shared_library we relied on the
-    # topological sort given by flattening
-    # cc_info.linking_context.linker_inputs.to_list(), however this was wrong
-    # because the dependencies of a shared library (i.e. a pruned node here)
-    # influenced the final order.
-    #
-    # In order to fix this, the pruning below was changed from breadth-first
-    # traversal to depth-first traversal. While doing this we also recreate a
-    # depset with topological order that takes into account the pruned nodes
-    # and which will later be used to order the libraries in the linking
-    # command line. This will be in topological order and will respect the
-    # order of the deps as listed on the BUILD file as much as possible.
-    #
-    # Here we say "first_owner" because each node (see GraphNodeInfo) may have
-    # more than one linker_input (each potentially with a different owner) but
-    # using only the first owner as a key is enough.
-    first_owner_to_depset = {}
-
-    # Horrible I know. Perhaps Starlark team gives me a way to prune a tree.
-    for _unused in range(2147483647):
-        if not len(deps_graph_nodes_to_process):
-            break
-
-        node = deps_graph_nodes_to_process[-1]
-
-        must_add_children = False
-
-        # The *_seen variables are used to track a programmatic error and fail
-        # if it happens.  Every value in node.owners presumably corresponds to
-        # a linker_input in the same exact target. Therefore if we have seen
-        # any of the owners already, then we must have also seen all the other
-        # owners in the same node. Viceversa when we haven't seen them yet. If
-        # both of these values are non-zero after the loop, the most likely
-        # reason would be a bug in the implementation. It could potentially be
-        # triggered by users if they use owner labels that do not keep most of
-        # the ctx.label.package and ctx.label.name which then clash with other
-        # target's owners (unlikely). For now though if the error is
-        # triggered, it's reasonable to require manual revision by
-        # the cc_shared_library implementation owners.
-        has_owners_seen = False
-        has_owners_not_seen = False
-        linked_dynamically = False
-        linked_statically = False
-        for owner in node.owners:
-            # TODO(bazel-team): Do not convert Labels to string to save on
-            # garbage string allocations.
-            owner_str = str(owner)
-
-            if owner_str in seen_labels:
-                has_owners_seen = True
-                continue
-
-            has_owners_not_seen = True
-            seen_labels[owner_str] = True
-
-            if owner_str in can_be_linked_dynamically:
-                targets_to_be_linked_dynamically_set[owner_str] = True
-                linked_dynamically = True
-            else:
-                targets_to_be_linked_statically_map[owner_str] = node.linkable_more_than_once
-                must_add_children = True
-                linked_statically = True
-
-        if has_owners_seen and has_owners_not_seen:
-            _programmatic_error()
-
-        if linked_dynamically and linked_statically:
-            error_owners_list = [str(owner) for owner in node.owners]
-
-            # Our granularity is target level. Unless there is a different
-            # unsupported custom implementation of this rule it should be
-            # impossible for two linker_inputs from the same target to be
-            # linked differently, one statically and the other dynamically.
-            _programmatic_error(
-                message = "Nodes with linker_inputs linked statically and dynamically:" +
-                          "\n{}".format("\n".join(error_owners_list)),
-            )
-
-        if must_add_children:
-            # The order in which we process the children matter. all_children
-            # is being used as a stack, we will process first the nodes at the
-            # top of the stack (last in the list). The children are the
-            # dependencies of the current node, in order to respect the order
-            # in which dependencies were listed in the deps attribute in the
-            # BUILD file we must reverse the list so that the first one listed
-            # in the BUILD file is processed first.
-            deps_graph_nodes_to_process.extend(reversed(node.children))
-        else:
-            if node.owners[0] not in first_owner_to_depset:
-                # We have 3 cases in this branch:
-                #   1. Node has no children
-                #   2. The children have been pruned because the node is linked dynamically
-                #   3. Node has children that have been processed
-                # For case 3 we add the children's depsets. For case 2 we add the dynamic
-                # dep labels for transitive dynamic deps.
-                transitive = []
-                if str(node.owners[0]) in targets_to_be_linked_statically_map:
-                    for child in node.children:
-                        transitive.append(first_owner_to_depset[child.owners[0]])
-                elif str(node.owners[0]) in transitive_dynamic_dep_labels:
-                    transitive.append(transitive_dynamic_dep_labels[str(node.owners[0])])
-
-                first_owner_to_depset[node.owners[0]] = depset(direct = node.owners, transitive = transitive, order = "topological")
-            deps_graph_nodes_to_process.pop()
-
-    topologically_sorted_labels = []
-    if deps_graph_nodes:
-        transitive = []
-        for child in deps_graph_nodes:
-            transitive.append(first_owner_to_depset[child.owners[0]])
-        topologically_sorted_labels = depset(transitive = transitive, order = "topological").to_list()
-
-    return (targets_to_be_linked_statically_map, targets_to_be_linked_dynamically_set, topologically_sorted_labels, all_dynamic_dep_linker_inputs)
-
-def _merge_cc_shared_library_infos(ctx):
-    dynamic_deps = []
-    transitive_dynamic_deps = []
-    for dep in ctx.attr.dynamic_deps:
-        dynamic_dep_entry = struct(
-            exports = dep[CcSharedLibraryInfo].exports,
-            linker_input = dep[CcSharedLibraryInfo].linker_input,
-            link_once_static_libs = dep[CcSharedLibraryInfo].link_once_static_libs,
-        )
-        dynamic_deps.append(dynamic_dep_entry)
-        transitive_dynamic_deps.append(dep[CcSharedLibraryInfo].dynamic_deps)
-
-    return depset(direct = dynamic_deps, transitive = transitive_dynamic_deps, order = "topological")
-
-def _build_exports_map_from_only_dynamic_deps(merged_cc_shared_library_infos_list):
-    exports_map = {}
-    for entry in merged_cc_shared_library_infos_list:
-        exports = entry.exports
-        linker_input = entry.linker_input
-        for export in exports:
-            if export in exports_map:
-                fail("Two shared libraries in dependencies export the same symbols. Both " +
-                     exports_map[export].libraries[0].dynamic_library.short_path +
-                     " and " + linker_input.libraries[0].dynamic_library.short_path +
-                     " export " + export)
-            exports_map[export] = linker_input
-    return exports_map
-
-# The map points from the target that can only be linked once to the
-# cc_shared_library target that already links it.
-def _build_link_once_static_libs_map(merged_cc_shared_library_infos_list):
-    link_once_static_libs_map = {}
-    for entry in merged_cc_shared_library_infos_list:
-        link_once_static_libs = entry.link_once_static_libs
-        linker_input = entry.linker_input
-        for static_lib in link_once_static_libs:
-            if static_lib in link_once_static_libs_map:
-                fail("Two shared libraries in dependencies link the same " +
-                     " library statically. Both " + link_once_static_libs_map[static_lib] +
-                     " and " + str(linker_input.owner) +
-                     " link statically " + static_lib)
-            link_once_static_libs_map[static_lib] = str(linker_input.owner)
-    return link_once_static_libs_map
-
-def _check_if_target_under_path(value, pattern):
-    if pattern.workspace_name != value.workspace_name:
-        return False
-    if pattern.name == "__pkg__":
-        return pattern.package == value.package
-    if pattern.name == "__subpackages__":
-        return _same_package_or_above(pattern, value)
-
-    return pattern.package == value.package and pattern.name == value.name
-
-def _throw_linked_but_not_exported_errors(error_libs_dict):
-    if not error_libs_dict:
-        return
-
-    error_builder = ["The following libraries were linked statically by different cc_shared_libraries but not exported:\n"]
-    for cc_shared_library_target, error_libs in error_libs_dict.items():
-        error_builder.append("cc_shared_library %s:\n" % str(cc_shared_library_target))
-        for error_lib in error_libs:
-            error_builder.append("  \"%s\",\n" % str(error_lib))
-
-    error_builder.append("If you are sure that the previous libraries are exported by the cc_shared_libraries because:\n")
-    error_builder.append("  1. You have visibility declarations in the source code\n")
-    error_builder.append("  2. Or you are passing a visibility script to the linker to export symbols from them\n")
-    error_builder.append("then add those libraries to roots or exports_filter for each cc_shared_library.\n")
-
-    fail("".join(error_builder))
-
-def _same_package_or_above(label_a, label_b):
-    if label_a.workspace_name != label_b.workspace_name:
-        return False
-    package_a_tokenized = label_a.package.split("/")
-    package_b_tokenized = label_b.package.split("/")
-    if len(package_b_tokenized) < len(package_a_tokenized):
-        return False
-
-    if package_a_tokenized[0] != "":
-        for i in range(len(package_a_tokenized)):
-            if package_a_tokenized[i] != package_b_tokenized[i]:
-                return False
-
-    return True
-
-def _build_map_direct_dynamic_dep_to_transitive_dynamic_deps(direct_dynamic_deps):
-    all_dynamic_dep_linker_inputs = {}
-    direct_dynamic_dep_to_transitive_dynamic_deps = {}
-    for dep in direct_dynamic_deps:
-        owner = dep[CcSharedLibraryInfo].linker_input.owner
-        all_dynamic_dep_linker_inputs[owner] = dep[CcSharedLibraryInfo].linker_input
-        transitive_dynamic_dep_labels = []
-        for dynamic_dep in dep[CcSharedLibraryInfo].dynamic_deps.to_list():
-            all_dynamic_dep_linker_inputs[dynamic_dep.linker_input.owner] = dynamic_dep.linker_input
-            transitive_dynamic_dep_labels.append(dynamic_dep.linker_input.owner)
-        transitive_dynamic_dep_labels_set = depset(transitive_dynamic_dep_labels, order = "topological")
-        for export in dep[CcSharedLibraryInfo].exports:
-            direct_dynamic_dep_to_transitive_dynamic_deps[export] = transitive_dynamic_dep_labels_set
-
-    return direct_dynamic_dep_to_transitive_dynamic_deps, all_dynamic_dep_linker_inputs
-
-def _add_unused_dynamic_deps(ctx, unused_dynamic_linker_inputs, add_linker_inputs_lambda, topologically_sorted_labels, link_indirect_deps):
-    linker_inputs_count = 0
-    direct_dynamic_dep_labels = {dep[CcSharedLibraryInfo].linker_input.owner: True for dep in ctx.attr.dynamic_deps}
-    topologically_sorted_labels_set = {label: True for label in topologically_sorted_labels}
-    for dynamic_linker_input_owner, unused_linker_input in unused_dynamic_linker_inputs.items():
-        should_link_input = (unused_linker_input and
-                             (link_indirect_deps or dynamic_linker_input_owner in direct_dynamic_dep_labels))
-        if should_link_input:
-            add_linker_inputs_lambda(
-                dynamic_linker_input_owner,
-                unused_dynamic_linker_inputs[dynamic_linker_input_owner],
-            )
-            linker_inputs_count += 1
-            if dynamic_linker_input_owner not in topologically_sorted_labels_set:
-                topologically_sorted_labels.append(dynamic_linker_input_owner)
-    return linker_inputs_count
-
 def _graph_structure_aspect_impl(target, ctx):
     children = []
 
@@ -605,6 +320,10 @@
  )
 </code></pre>"""),
         "_def_parser": semantics.get_def_parser(),
+        "_impl_delegate": attr.label(
+            default = Label("//cc/private/rules_impl/wrappers:cc_shared_library_impl_wrapper"),
+            cfg = "exec",
+        ),
     },  # buildifier: disable=unsorted-dict-items
     toolchains = use_cc_toolchain() + semantics.get_runtimes_toolchain(),
     fragments = ["cpp"] + semantics.additional_fragments(),
@@ -659,12 +378,3 @@
         aspects = [graph_structure_aspect],
     ),
 }
-
-for_testing_dont_use_check_if_target_under_path = _check_if_target_under_path
-merge_cc_shared_library_infos = _merge_cc_shared_library_infos
-build_link_once_static_libs_map = _build_link_once_static_libs_map
-build_exports_map_from_only_dynamic_deps = _build_exports_map_from_only_dynamic_deps
-throw_linked_but_not_exported_errors = _throw_linked_but_not_exported_errors
-separate_static_and_dynamic_link_libraries = _separate_static_and_dynamic_link_libraries
-sort_linker_inputs = _sort_linker_inputs
-add_unused_dynamic_deps = _add_unused_dynamic_deps
diff --git a/cc/private/rules_impl/cc_shared_library_impl.bzl b/cc/private/rules_impl/cc_shared_library_impl.bzl
index 18b6103..9b017c7 100644
--- a/cc/private/rules_impl/cc_shared_library_impl.bzl
+++ b/cc/private/rules_impl/cc_shared_library_impl.bzl
@@ -15,17 +15,14 @@
 """Implementation of cc_shared_library"""
 
 load("@bazel_skylib//lib:paths.bzl", "paths")
-load("@com_google_protobuf//bazel/common:proto_info.bzl", "ProtoInfo")
-load("//cc:find_cc_toolchain.bzl", "find_cc_toolchain", "use_cc_toolchain")
+load("//cc:find_cc_toolchain.bzl", "find_cc_toolchain")
 load("//cc/common:cc_common.bzl", "cc_common")
 load("//cc/common:cc_helper.bzl", "cc_helper")
 load("//cc/common:cc_info.bzl", "CcInfo")
-load("//cc/common:cc_shared_library_hint_info.bzl", "CcSharedLibraryHintInfo")
 load("//cc/common:cc_shared_library_info.bzl", "CcSharedLibraryInfo")
 load("//cc/common:semantics.bzl", "semantics")
 load("//cc/private:graph_node_info.bzl", "GraphNodeInfo")
-
-visibility("private")
+load(":function_providing_rule.bzl", "wrap_starlark_function")
 
 # TODO(#5200): Add export_define to library_to_link and cc_library
 
@@ -785,349 +782,6 @@
         ),
     ]
 
-def _graph_structure_aspect_impl(target, ctx):
-    children = []
-
-    attributes = dir(ctx.rule.attr)
-    owners = [ctx.label]
-    if CcSharedLibraryHintInfo in target:
-        attributes = getattr(target[CcSharedLibraryHintInfo], "attributes", dir(ctx.rule.attr))
-        owners = getattr(target[CcSharedLibraryHintInfo], "owners", [ctx.label])
-
-    # Collect graph structure info from any possible deplike attribute. The aspect
-    # itself applies across every deplike attribute (attr_aspects is *), so enumerate
-    # over all attributes and consume GraphNodeInfo if available.
-    for fieldname in attributes:
-        deps = getattr(ctx.rule.attr, fieldname, None)
-        if type(deps) == "list":
-            for dep in deps:
-                if type(dep) == "Target" and GraphNodeInfo in dep:
-                    children.append(dep[GraphNodeInfo])
-        elif type(deps) == "Target" and GraphNodeInfo in deps:
-            children.append(deps[GraphNodeInfo])
-
-    # TODO(bazel-team): Add flag to Bazel that can toggle the initialization of
-    # linkable_more_than_once.
-    linkable_more_than_once = False
-    if hasattr(ctx.rule.attr, "tags"):
-        for tag in ctx.rule.attr.tags:
-            if tag == LINKABLE_MORE_THAN_ONCE:
-                linkable_more_than_once = True
-    return [GraphNodeInfo(
-        owners = owners,
-        children = children,
-        linkable_more_than_once = linkable_more_than_once,
-    )]
-
-graph_structure_aspect = aspect(
-    attr_aspects = ["*"],
-    required_providers = [[CcInfo], [CcSharedLibraryHintInfo], [ProtoInfo]],
-    required_aspect_providers = [[CcInfo], [CcSharedLibraryHintInfo]],
-    implementation = _graph_structure_aspect_impl,
-)
-
-def _cc_shared_library_initializer(**kwargs):
-    """Converts labels in exports_filter into canonical form relative to the current repository.
-
-    This conversion can only be done in a macro as it requires access to the repository mapping of
-    the repository containing the cc_shared_library target. This mapping is automatically
-    applied to label attributes, but exports_filter is a list of strings attribute.
-    """
-    if "exports_filter" not in kwargs:
-        return kwargs
-
-    raw_exports_filter = kwargs["exports_filter"]
-    if type(raw_exports_filter) != type([]):
-        # TODO: Also canonicalize labels in selects once macros can operate on them.
-        # https://github.com/bazelbuild/bazel/issues/14157
-        return kwargs
-
-    canonical_exports_filter = [
-        str(native.package_relative_label(s))
-        for s in raw_exports_filter
-    ]
-    return kwargs | {"exports_filter": canonical_exports_filter}
-
-cc_shared_library = rule(
-    implementation = _cc_shared_library_impl,
-    initializer = _cc_shared_library_initializer,
-    doc = """
-<p>It produces a shared library.</p>
-
-<h4 id="cc_shard_library_examples">Example</h4>
-
-<pre class="code">
-cc_shared_library(
-    name = "foo_shared",
-    deps = [
-        ":foo",
-    ],
-    dynamic_deps = [
-        ":bar_shared",
-    ],
-    additional_linker_inputs = [
-        ":foo.lds",
-    ],
-    user_link_flags = [
-        "-Wl,--version-script=$(location :foo.lds)",
-    ],
-)
-cc_library(
-    name = "foo",
-    srcs = ["foo.cc"],
-    hdrs = ["foo.h"],
-    deps = [
-        ":bar",
-        ":baz",
-    ],
-)
-cc_shared_library(
-    name = "bar_shared",
-    shared_lib_name = "bar.so",
-    deps = [":bar"],
-)
-cc_library(
-    name = "bar",
-    srcs = ["bar.cc"],
-    hdrs = ["bar.h"],
-)
-cc_library(
-    name = "baz",
-    srcs = ["baz.cc"],
-    hdrs = ["baz.h"],
-)
-</pre>
-
-<p>In the example <code>foo_shared</code> statically links <code>foo</code>
-and <code>baz</code>, the latter being a transitive dependency. It doesn't
-link <code>bar</code> because it is already provided dynamically by the
-<code>dynamic_dep</code> <code>bar_shared</code>.</p>
-
-<p><code>foo_shared</code> uses a linker script *.lds file to control which
-symbols should be exported. The <code>cc_shared_library</code> rule logic does
-not control which symbols get exported, it only uses what is assumed to be
-exported to give errors during analysis phase if two shared libraries export the
-same targets.</p>
-
-<p>Every direct dependency of <code>cc_shared_library</code> is assumed to be
-exported. Therefore, Bazel assumes during analysis that <code>foo</code> is being
-exported by <code>foo_shared</code>. <code>baz</code> is not assumed to be exported
-by <code>foo_shared</code>. Every target matched by the <code>exports_filter</code>
-is also assumed to be exported.</p>
-
-<p>Every single <code>cc_library</code> in the example should appear at most in one
-<code>cc_shared_library</code>. If we wanted to link <code>baz</code> also into
-<code>bar_shared</code> we would need to add
-<code>tags = ["LINKABLE_MORE_THAN_ONCE"]</code> to <code>baz</code>.</p>
-
-<p>Due to the <code>shared_lib_name</code> attribute, the file produced by
-<code>bar_shared</code> will have the name <code>bar.so</code> as opposed
-to the name <code>libbar.so</code> that it would have by default on Linux.</p>
-
-<h4 id="cc_shard_library_examples">Errors</h4>
-<h5><code>Two shared libraries in dependencies export the same symbols.</code></h5>
-
-<p>This will happen whenever you are creating a target with two different
-<code>cc_shared_library</code> dependencies that export the same target. To fix this
-you need to stop the libraries from being exported in one of the
-<code>cc_shared_library</code> dependencies.</p>
-
-<h5><code>Two shared libraries in dependencies link the same library statically</code></h5>
-
-<p>This will happen whenever you are creating a new <code>cc_shared_library</code> with two
-different <code>cc_shared_library</code> dependencies that link the same target statically.
-Similar to the error with exports.</p>
-
-<p>One way to fix this is to stop linking the library into one of the
-<code>cc_shared_library</code> dependencies. At the same time, the one that still links it
-needs to export the library so that the one not linking it keeps visibility to
-the symbols. Another way is to pull out a third library that exports the target.
-A third way is to tag the culprit <code>cc_library</code> with <code>LINKABLE_MORE_THAN_ONCE</code>
-but this fix should be rare and you should absolutely make sure that the
-<code>cc_library</code> is indeed safe to link more than once.</p>
-
-<h5><code>'//foo:foo' is already linked statically in '//bar:bar' but not exported`</code></h5>
-
-<p>This means that a library in the transitive closure of your <code>deps</code> is reachable
-without going through one of the <code>cc_shared_library</code> dependencies but is already
-linked into a different <code>cc_shared_library</code> in <code>dynamic_deps</code> and is not
-exported.</p>
-
-<p>The solution is to export it from the <code>cc_shared_library</code> dependency or pull out
-a third <code>cc_shared_library</code> that exports it.</p>
-
-<h5><code>Do not place libraries which only contain a precompiled dynamic library in deps.
-</code></h5>
-
-<p>If you have a precompiled dynamic library, this doesn't need to and cannot be
-linked statically into the current <code>cc_shared_library</code> target that you are
-currently creating. Therefore, it doesn't belong in <code>deps</code> of the
-<code>cc_shared_library</code>. If this precompiled dynamic library is a dependency of one
-of your <code>cc_libraries</code>, then the <code>cc_library</code> needs to depend on it
-directly.</p>
-
-<h5><code>Trying to export a library already exported by a different shared library</code></h5>
-
-<p>You will see this error if on the current rule you are claiming to export a
-target that is already being exported by one of your dynamic dependencies.</p>
-
-<p>To fix this, remove the target from <code>deps</code> and just rely on it from the dynamic
-dependency or make sure that the <code>exports_filter</code> doesn't catch this target.</p>""",
-    attrs = {
-        "additional_linker_inputs": attr.label_list(allow_files = True, doc = """
-Any additional files that you may want to pass to the linker, for example, linker scripts.
-You have to separately pass any linker flags that the linker needs in order to be aware
-of this file. You can do so via the <code>user_link_flags</code> attribute."""),
-        "shared_lib_name": attr.string(doc = """
-By default cc_shared_library will use a name for the shared library output file based on
-the target's name and the platform. This includes an extension and sometimes a prefix.
-Sometimes you may not want the default name, for example, when loading C++ shared libraries
-for Python the default lib* prefix is often not desired, in which case you can use this
-attribute to choose a custom name."""),
-        "dynamic_deps": attr.label_list(providers = [CcSharedLibraryInfo], doc = """
-These are other <code>cc_shared_library</code> dependencies the current target depends on.
-
-<p>
-The <code>cc_shared_library</code> implementation will use the list of
-<code>dynamic_deps</code> (transitively, i.e. also the <code>dynamic_deps</code> of the
-current target's <code>dynamic_deps</code>) to decide which <code>cc_libraries</code> in
-the transitive <code>deps</code> should not be linked in because they are already provided
-by a different <code>cc_shared_library</code>.
-</p>"""),
-        "exports_filter": attr.string_list(doc = """
-This attribute contains a list of targets that are claimed to be exported by the current
-shared library.
-
-<p>
-Any target <code>deps</code> is already understood to be exported by the shared library.
-This attribute should be used to list any targets that are exported by the shared library
-but are transitive dependencies of <code>deps</code>.
-</p>
-
-<p>
-Note that this attribute is not actually adding a dependency edge to those targets, the
-dependency edge should instead be created by <code>deps</code>.The entries in this
-attribute are just strings. Keep in mind that when placing a target in this attribute,
-this is considered a claim that the shared library exports the symbols from that target.
-The <code>cc_shared_library</code> logic  doesn't actually handle telling the linker which
-symbols should be exported.
-</p>
-
-<p>The following syntax is allowed:</p>
-<p><code>//foo:__pkg__</code> to account for any target in foo/BUILD</p>
-<p><code>//foo:__subpackages__</code> to account for any target in foo/BUILD or any other
-package below foo/ like foo/bar/BUILD</p>"""),
-        "win_def_file": attr.label(allow_single_file = [".def"], doc = """
-The Windows DEF file to be passed to linker.
-<p>This attribute should only be used when Windows is the target platform.
-It can be used to <a href="https://msdn.microsoft.com/en-us/library/d91k01sh.aspx">
-export symbols</a> during linking a shared library.</p>"""),
-        "roots": attr.label_list(providers = [CcInfo], aspects = [graph_structure_aspect]),
-        "deps": attr.label_list(providers = [CcInfo], aspects = [graph_structure_aspect], doc = """
-Top level libraries that will unconditionally be statically linked into the shared library
-after being whole-archived.
-
-<p>
-Any transitive library dependency of these direct deps will be linked into this shared
-library as long as they have not already been linked by a <code>cc_shared_library</code>
-in <code>dynamic_deps</code>.</p>
-
-<p>
-During analysis, the rule implementation will consider any target listed in
-<code>deps</code> as being exported by the shared library in order to give errors when
-multiple <code>cc_shared_libraries</code> export the same targets. The rule implementation
-does not take care of informing the linker about which symbols should be exported by the
-shared object. The user should take care of this via linker scripts or visibility
-declarations in the source code.</p>
-
-<p>
-The implementation will also trigger errors whenever the same library is linked statically
-into more than one <code>cc_shared_library</code>. This can be avoided by adding
-<code>"LINKABLE_MORE_THAN_ONCE"</code> to the <code>cc_library.tags</code> or by listing
-the `cc_library` as an export of one of the shared libraries so that one can be made a
-<code>dynamic_dep</code> of the other.
-</p>"""),
-        "static_deps": attr.string_list(),
-        "user_link_flags": attr.string_list(doc = """
-Any additional flags that you may want to pass to the linker. For example, to make the
-linker aware of a linker script passed via additional_linker_inputs you can use the
-following:
-
-<pre><code class="lang-starlark">
- cc_shared_library(
-    name = "foo_shared",
-    additional_linker_inputs = select({
-      "//src/conditions:linux": [
-        ":foo.lds",
-        ":additional_script.txt",
-      ],
-      "//conditions:default": []}),
-    user_link_flags = select({
-      "//src/conditions:linux": [
-        "-Wl,-rpath,kittens",
-        "-Wl,--version-script=$(location :foo.lds)",
-        "-Wl,--script=$(location :additional_script.txt)",
-      ],
-      "//conditions:default": []}),
-      ...
- )
-</code></pre>"""),
-        "_def_parser": semantics.get_def_parser(),
-    },  # buildifier: disable=unsorted-dict-items
-    toolchains = use_cc_toolchain() + semantics.get_runtimes_toolchain(),
-    fragments = ["cpp"] + semantics.additional_fragments(),
-)
-
-def dynamic_deps_initializer(**kwargs):
-    """Initializes dynamic_deps_attrs
-
-    Args:
-        **kwargs: (dict)
-
-    Returns:
-        (dict)
-    """
-    if "dynamic_deps" in kwargs and cc_helper.is_non_empty_list_or_select(kwargs["dynamic_deps"], "dynamic_deps"):
-        # Propagate an aspect if dynamic_deps attribute is specified.
-        # Use += for lists rather than extend or append to allow for the case where deps
-        # is a select.
-        all_deps = []
-        if "deps" in kwargs:
-            all_deps += kwargs["deps"]
-
-        if "linkshared" not in kwargs or not kwargs["linkshared"]:
-            # The += [...] pattern below doesn't work if malloc or link_extra_lib are
-            # themselves selects, but as of March 2024, there is no way to combine mixed
-            # selects and these attributes usually point to label flags anyway.
-            if "link_extra_lib" in kwargs:
-                all_deps += [kwargs["link_extra_lib"]]  # buildifier: disable=list-append
-            if "malloc" in kwargs:
-                all_deps += [kwargs["malloc"]]  # buildifier: disable=list-append
-
-        return kwargs | {"_deps_analyzed_by_graph_structure_aspect": all_deps}
-    return kwargs
-
-dynamic_deps_attrs = {
-    "dynamic_deps": attr.label_list(
-        allow_files = False,
-        providers = [CcSharedLibraryInfo],
-        doc = """
-These are other <code>cc_shared_library</code> dependencies the current target depends on.
-
-<p>
-The <code>cc_shared_library</code> implementation will use the list of
-<code>dynamic_deps</code> (transitively, i.e. also the <code>dynamic_deps</code> of the
-current target's <code>dynamic_deps</code>) to decide which <code>cc_libraries</code> in
-the transitive <code>deps</code> should not be linked in because they are already provided
-by a different <code>cc_shared_library</code>.
-        """ + semantics.dynamic_deps_extra_docs,
-    ),
-    "_deps_analyzed_by_graph_structure_aspect": attr.label_list(
-        providers = [CcInfo],
-        aspects = [graph_structure_aspect],
-    ),
-}
-
 for_testing_dont_use_check_if_target_under_path = _check_if_target_under_path
 merge_cc_shared_library_infos = _merge_cc_shared_library_infos
 build_link_once_static_libs_map = _build_link_once_static_libs_map
@@ -1138,3 +792,4 @@
 add_unused_dynamic_deps = _add_unused_dynamic_deps
 
 cc_shared_library_impl = _cc_shared_library_impl
+cc_shared_library_impl_wrapper = wrap_starlark_function(_cc_shared_library_impl)
diff --git a/cc/private/rules_impl/cc_test.bzl b/cc/private/rules_impl/cc_test.bzl
index e02be7e..375ff0f 100644
--- a/cc/private/rules_impl/cc_test.bzl
+++ b/cc/private/rules_impl/cc_test.bzl
@@ -40,6 +40,10 @@
     env_inherit = attr.string_list(),
     stamp = attr.int(values = [-1, 0, 1], default = 0, doc = stamp_doc),
     linkstatic = attr.bool(default = False, doc = linkstatic_doc),
+    _impl_delegate = attr.label(
+        default = Label("//cc/private/rules_impl/wrappers:cc_test_impl_wrapper"),
+        cfg = "exec",
+    ),
 )
 _cc_test_attrs.update(semantics.get_test_malloc_attr())
 _cc_test_attrs.update(semantics.get_coverage_attrs())
diff --git a/cc/private/rules_impl/cc_test_impl.bzl b/cc/private/rules_impl/cc_test_impl.bzl
index 4636534..d223169 100644
--- a/cc/private/rules_impl/cc_test_impl.bzl
+++ b/cc/private/rules_impl/cc_test_impl.bzl
@@ -16,10 +16,8 @@
 
 load("//cc/common:cc_helper.bzl", "cc_helper")
 load("//cc/common:semantics.bzl", "semantics")
-load(":attrs.bzl", "cc_binary_attrs", "linkstatic_doc", "stamp_doc")
 load(":cc_binary_impl.bzl", "cc_binary_impl")
-
-visibility("private")
+load(":function_providing_rule.bzl", "wrap_starlark_function")
 
 _CC_TEST_TOOLCHAIN_TYPE = "@bazel_tools//tools/cpp:test_runner_toolchain_type"
 
@@ -74,25 +72,5 @@
     providers.extend(test_providers)
     return providers
 
-_cc_test_attrs = dict(cc_binary_attrs)
-
-# Update cc_test defaults:
-_cc_test_attrs.update(
-    _is_test = attr.bool(default = True),
-    _apple_constraints = attr.label_list(
-        default = [
-            "@platforms//os:ios",
-            "@platforms//os:macos",
-            "@platforms//os:tvos",
-            "@platforms//os:watchos",
-        ],
-    ),
-    # Starlark tests don't get `env_inherit` by default.
-    env_inherit = attr.string_list(),
-    stamp = attr.int(values = [-1, 0, 1], default = 0, doc = stamp_doc),
-    linkstatic = attr.bool(default = False, doc = linkstatic_doc),
-)
-_cc_test_attrs.update(semantics.get_test_malloc_attr())
-_cc_test_attrs.update(semantics.get_coverage_attrs())
-
 impl = _impl
+cc_test_impl_wrapper = wrap_starlark_function(_impl)
diff --git a/cc/private/rules_impl/function_providing_rule.bzl b/cc/private/rules_impl/function_providing_rule.bzl
new file mode 100644
index 0000000..950e10c
--- /dev/null
+++ b/cc/private/rules_impl/function_providing_rule.bzl
@@ -0,0 +1,30 @@
+# Copyright 2020 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Trivially simple rule to provide a Starlark function via a target"""
+
+visibility([
+    "//third_party/bazel_rules/rules_cc/private/rules_impl",
+])
+
+FunctionInfo = provider("Wraps a Starlark function", fields = ["func"])
+
+def wrap_starlark_function(func):
+    return rule(
+        implementation = lambda _unused_ctx: FunctionInfo(func = func),
+        provides = [FunctionInfo],
+    )
+
+def proxy(ctx):
+    return ctx.attr._impl_delegate[FunctionInfo].func(ctx)
diff --git a/cc/private/rules_impl/wrappers/BUILD b/cc/private/rules_impl/wrappers/BUILD
new file mode 100644
index 0000000..c16cf37
--- /dev/null
+++ b/cc/private/rules_impl/wrappers/BUILD
@@ -0,0 +1,38 @@
+load("//cc/private/rules_impl:cc_binary_impl.bzl", "cc_binary_impl_wrapper")
+load("//cc/private/rules_impl:cc_library_impl.bzl", "cc_library_impl_wrapper")
+load("//cc/private/rules_impl:cc_shared_library_impl.bzl", "cc_shared_library_impl_wrapper")
+load("//cc/private/rules_impl:cc_test_impl.bzl", "cc_test_impl_wrapper")
+
+licenses(["notice"])
+
+filegroup(
+    name = "srcs",
+    srcs = [
+        "BUILD",
+    ],
+    visibility = ["//cc/private/rules_impl:__pkg__"],
+)
+
+cc_binary_impl_wrapper(
+    name = "cc_binary_impl_wrapper",
+    applicable_licenses = [],
+    visibility = ["//visibility:public"],
+)
+
+cc_library_impl_wrapper(
+    name = "cc_library_impl_wrapper",
+    applicable_licenses = [],
+    visibility = ["//visibility:public"],
+)
+
+cc_test_impl_wrapper(
+    name = "cc_test_impl_wrapper",
+    applicable_licenses = [],
+    visibility = ["//visibility:public"],
+)
+
+cc_shared_library_impl_wrapper(
+    name = "cc_shared_library_impl_wrapper",
+    applicable_licenses = [],
+    visibility = ["//visibility:public"],
+)