Support the new(ish) `@//tools/cpp:cc_runtimes_toolchain_type` toolchain for adding runtimes-on-demand.
* Add `get_cc_runtimes` and `get_cc_runtimes_copts` implementations to `cc_helper[_internal]`.
* TODO update usage in `rules_python` and others to call `cc_helper` and deprecate `semantics.get_cc_runtimes*`

Basic Usage:

Define a provider like:
```
CcRuntimesInfo = provider(
    doc = "Information about runtime libraries to link into c++ targets.",
    fields = ["runtimes", "copts"],
)
```

Define a toolchain that returns that provider:
```
def _cc_runtimes_toolchain_impl(ctx):
    return [platform_common.ToolchainInfo(
        cc_runtimes_info = CcRuntimesInfo(
            runtimes = ctx.attr.runtimes,
            copts = ctx.attr.copts,
        ),
    )]
```
* `runtimes` is a collection of CcInfo providing targets
* `copts` provides compilation flags that must be used alongside this toolchain, e.g. to influence include ordering.

This may be used, e.g. in rules_android_ndk: https://github.com/bazelbuild/rules_android_ndk/issues/93

PiperOrigin-RevId: 941244580
Change-Id: I31bade595714dbe44aa0d70afaa97ddd788e5635
diff --git a/cc/common/cc_helper.bzl b/cc/common/cc_helper.bzl
index 92483ac..115052a 100644
--- a/cc/common/cc_helper.bzl
+++ b/cc/common/cc_helper.bzl
@@ -26,6 +26,8 @@
     "should_create_per_object_debug_info",
     _artifact_category = "artifact_category_names",
     _extensions = "extensions",
+    _get_cc_runtimes = "get_cc_runtimes",
+    _get_cc_runtimes_copts = "get_cc_runtimes_copts",
     _is_stamping_enabled = "is_stamping_enabled",
     _package_source_root = "package_source_root",
     _repository_exec_path = "repository_exec_path",
@@ -1182,4 +1184,6 @@
     has_target_constraints = _has_target_constraints,
     package_exec_path = _package_exec_path,
     should_create_test_dwp_for_statically_linked_test = _should_create_test_dwp_for_statically_linked_test,
+    get_cc_runtimes = _get_cc_runtimes,
+    get_cc_runtimes_copts = _get_cc_runtimes_copts,
 )
diff --git a/cc/common/cc_helper_internal.bzl b/cc/common/cc_helper_internal.bzl
index 735e050..df6b198 100644
--- a/cc/common/cc_helper_internal.bzl
+++ b/cc/common/cc_helper_internal.bzl
@@ -43,6 +43,55 @@
 CPP_SOURCE_TYPE_HEADER = "HEADER"
 CPP_SOURCE_TYPE_SOURCE = "SOURCE"
 CPP_SOURCE_TYPE_CLIF_INPUT_PROTO = "CLIF_INPUT_PROTO"
+CC_RUNTIMES_TOOLCHAIN_TYPE = Label("@bazel_tools//tools/cpp:cc_runtimes_toolchain_type")
+
+def get_cc_runtimes(ctx, is_library):
+    """Returns the list of C++ runtime dependency targets for the rule.
+
+    Args:
+      ctx: The rule context.
+      is_library: True if the target being evaluated is a library rule (which
+        excludes malloc and link_extra_lib), False otherwise.
+
+    Returns:
+      A list of Target objects representing the required runtime libraries.
+    """
+    runtimes = []
+
+    # Executable builds also include the "malloc" and "link_extra_lib" libraries.
+    if not is_library:
+        runtimes.append(ctx.attr.link_extra_lib)
+
+        if ctx.fragments.cpp.custom_malloc != None:
+            # ctx.attr._default_malloc is set via ctx.fragments.cpp.custom_malloc.
+            runtimes.append(ctx.attr._default_malloc)
+        else:
+            runtimes.append(ctx.attr.malloc)
+
+    cc_runtimes_toolchain = ctx.toolchains[CC_RUNTIMES_TOOLCHAIN_TYPE]
+
+    # All builds include the runtimes from the cc_runtimes_toolchain.
+    if cc_runtimes_toolchain:
+        runtimes += cc_runtimes_toolchain.cc_runtimes_info.runtimes
+    elif hasattr(ctx.attr, "tags") and "__CC_STL__" in ctx.attr.tags:
+        # TODO(b/141613846): Remove this workaround.
+        pass
+    elif getattr(ctx.attr, "_stl", None) != None:
+        runtimes.append(ctx.attr._stl)
+
+    return runtimes
+
+def get_cc_runtimes_copts(ctx):
+    """Returns the C++ compiler flags required by the C++ runtimes toolchain.
+
+    Args:
+      ctx: The rule context.
+
+    Returns:
+      A list of command-line compiler option strings from the runtimes toolchain.
+    """
+    cc_runtimes_toolchain = ctx.toolchains[CC_RUNTIMES_TOOLCHAIN_TYPE]
+    return cc_runtimes_toolchain.cc_runtimes_info.copts if cc_runtimes_toolchain else []
 
 def get_fdo_build_stamp(cpp_configuration, fdo_context, feature_configuration):
     """Returns the FDO build stamp.
diff --git a/cc/common/semantics.bzl b/cc/common/semantics.bzl
index 0b208c7..267259a 100644
--- a/cc/common/semantics.bzl
+++ b/cc/common/semantics.bzl
@@ -14,6 +14,13 @@
 
 """Semantics for Bazel cc rules"""
 
+load(
+    ":cc_helper_internal.bzl",
+    "CC_RUNTIMES_TOOLCHAIN_TYPE",
+    _get_cc_runtimes = "get_cc_runtimes",
+    _get_cc_runtimes_copts = "get_cc_runtimes_copts",
+)
+
 # Point virtual includes symlinks to the source root for better IDE integration.
 # See https://github.com/bazelbuild/bazel/pull/20540.
 # TODO: b/320980684 - Add a test that fails if this is flipped to True.
@@ -66,7 +73,12 @@
     return attr.label()
 
 def _get_runtimes_toolchain():
-    return []
+    return [
+        config_common.toolchain_type(
+            CC_RUNTIMES_TOOLCHAIN_TYPE,
+            mandatory = False,
+        ),
+    ]
 
 def _get_test_malloc_attr():
     return {}
@@ -88,22 +100,6 @@
 def _get_coverage_env(ctx):
     return ctx.runfiles(), {}
 
-def _get_cc_runtimes(ctx, is_library):
-    if is_library:
-        return []
-
-    runtimes = [ctx.attr.link_extra_lib]
-
-    if ctx.fragments.cpp.custom_malloc != None:
-        runtimes.append(ctx.attr._default_malloc)
-    else:
-        runtimes.append(ctx.attr.malloc)
-
-    return runtimes
-
-def _get_cc_runtimes_copts(_ctx):
-    return []
-
 def _get_implementation_deps_allowed_attr():
     return {}
 
diff --git a/cc/private/rules_impl/cc_binary_impl.bzl b/cc/private/rules_impl/cc_binary_impl.bzl
index 6659384..3325e93 100644
--- a/cc/private/rules_impl/cc_binary_impl.bzl
+++ b/cc/private/rules_impl/cc_binary_impl.bzl
@@ -169,10 +169,10 @@
 
     builder = builder.merge_all([
         _default_runfiles_function(ctx, runtime)
-        for runtime in semantics.get_cc_runtimes(ctx, _is_link_shared(ctx))
+        for runtime in cc_helper.get_cc_runtimes(ctx, _is_link_shared(ctx))
     ] + [
         ctx.runfiles(transitive_files = _runfiles_function(runtime, linking_mode != linker_mode.LINKING_DYNAMIC))
-        for runtime in semantics.get_cc_runtimes(ctx, _is_link_shared(ctx))
+        for runtime in cc_helper.get_cc_runtimes(ctx, _is_link_shared(ctx))
     ])
 
     return (builder.merge(ctx.runfiles(files = builder_artifacts, transitive_files = depset(builder_transitive_artifacts))), runtime_objects_for_coverage)
@@ -208,7 +208,7 @@
     return dynamic_libraries_for_runtime
 
 def _get_providers(ctx):
-    all_deps = ctx.attr.deps + semantics.get_cc_runtimes(ctx, _is_link_shared(ctx))
+    all_deps = ctx.attr.deps + cc_helper.get_cc_runtimes(ctx, _is_link_shared(ctx))
     return [dep[CcInfo] for dep in all_deps if CcInfo in dep]
 
 def _filter_libraries_that_are_linked_dynamically(ctx, feature_configuration, cc_linking_context):
@@ -283,7 +283,7 @@
     # Deps that came from cc_runtimes may have been filtered out above, so we need to re-add them.
     # For example, libc++/libstdc++ or malloc-like deps should still be linked in normally
     # even though they will also be a transitive dep of things in dynamic_deps.
-    cc_runtimes = semantics.get_cc_runtimes(ctx, _is_link_shared(ctx))
+    cc_runtimes = cc_helper.get_cc_runtimes(ctx, _is_link_shared(ctx))
     cc_runtimes_infos = [dep[CcInfo] for dep in cc_runtimes if CcInfo in dep]
 
     return cc_common.merge_cc_infos(direct_cc_infos = [
@@ -512,10 +512,10 @@
 
     cc_helper.check_cpp_modules(ctx, feature_configuration)
 
-    all_deps = ctx.attr.deps + semantics.get_cc_runtimes(ctx, _is_link_shared(ctx))
+    all_deps = ctx.attr.deps + cc_helper.get_cc_runtimes(ctx, _is_link_shared(ctx))
     compilation_context_deps = [dep[CcInfo].compilation_context for dep in all_deps if CcInfo in dep]
 
-    runtimes_copts = semantics.get_cc_runtimes_copts(ctx)
+    runtimes_copts = cc_helper.get_cc_runtimes_copts(ctx)
 
     additional_make_variable_substitutions = cc_helper.get_toolchain_global_make_variables(cc_toolchain, feature_configuration)
     additional_make_variable_substitutions.update(cc_helper.get_cc_flags_make_variable(ctx, feature_configuration, cc_toolchain))
diff --git a/cc/private/rules_impl/cc_import.bzl b/cc/private/rules_impl/cc_import.bzl
index 6574b69..b1fd6fb 100644
--- a/cc/private/rules_impl/cc_import.bzl
+++ b/cc/private/rules_impl/cc_import.bzl
@@ -164,8 +164,8 @@
     additional_make_variable_substitutions = cc_helper.get_toolchain_global_make_variables(cc_toolchain, feature_configuration)
     additional_make_variable_substitutions.update(cc_helper.get_cc_flags_make_variable(ctx, feature_configuration, cc_toolchain))
 
-    runtimes_deps = semantics.get_cc_runtimes(ctx, True)
-    runtimes_copts = semantics.get_cc_runtimes_copts(ctx)
+    runtimes_deps = cc_helper.get_cc_runtimes(ctx, True)
+    runtimes_copts = cc_helper.get_cc_runtimes_copts(ctx)
     compilation_contexts = cc_helper.get_compilation_contexts_from_deps(runtimes_deps)
     (compilation_context, _) = cc_common.compile(
         actions = ctx.actions,
diff --git a/cc/private/rules_impl/cc_library_impl.bzl b/cc/private/rules_impl/cc_library_impl.bzl
index 83b21c4..2005486 100755
--- a/cc/private/rules_impl/cc_library_impl.bzl
+++ b/cc/private/rules_impl/cc_library_impl.bzl
@@ -45,8 +45,8 @@
     _check_no_repeated_srcs(ctx)
 
     semantics.check_can_use_implementation_deps(ctx)
-    interface_deps = ctx.attr.deps + semantics.get_cc_runtimes(ctx, True)
-    runtimes_copts = semantics.get_cc_runtimes_copts(ctx)
+    interface_deps = ctx.attr.deps + cc_helper.get_cc_runtimes(ctx, True)
+    runtimes_copts = cc_helper.get_cc_runtimes_copts(ctx)
     compilation_contexts = cc_helper.get_compilation_contexts_from_deps(interface_deps)
     implementation_compilation_contexts = cc_helper.get_compilation_contexts_from_deps(ctx.attr.implementation_deps)
 
@@ -110,7 +110,7 @@
     linking_contexts = cc_helper.get_linking_contexts_from_deps(ctx.attr.deps)
     linking_contexts.extend(
         cc_helper.get_linking_contexts_from_deps(
-            ctx.attr.implementation_deps + semantics.get_cc_runtimes(ctx, True),
+            ctx.attr.implementation_deps + cc_helper.get_cc_runtimes(ctx, True),
         ),
     )
     if ctx.file.linkstamp != None:
diff --git a/cc/private/rules_impl/cc_shared_library_impl.bzl b/cc/private/rules_impl/cc_shared_library_impl.bzl
index 188b42e..905c52a 100644
--- a/cc/private/rules_impl/cc_shared_library_impl.bzl
+++ b/cc/private/rules_impl/cc_shared_library_impl.bzl
@@ -655,7 +655,7 @@
 
     linking_context = _create_linker_context(linker_inputs)
 
-    cc_runtimes_deps = semantics.get_cc_runtimes(ctx, True)
+    cc_runtimes_deps = cc_helper.get_cc_runtimes(ctx, True)
     runtimes_linking_contexts = cc_helper.get_linking_contexts_from_deps(cc_runtimes_deps)
 
     user_link_flags = []
diff --git a/cc/private/rules_impl/objc_compilation_support.bzl b/cc/private/rules_impl/objc_compilation_support.bzl
index 4e9bdfb..b497193 100644
--- a/cc/private/rules_impl/objc_compilation_support.bzl
+++ b/cc/private/rules_impl/objc_compilation_support.bzl
@@ -17,7 +17,6 @@
 load("//cc:build_settings.bzl", "cc")
 load("//cc/common:cc_common.bzl", "cc_common")
 load("//cc/common:cc_helper.bzl", "cc_helper")
-load("//cc/common:semantics.bzl", cc_semantics = "semantics")
 load(":objc_common.bzl", "objc_common")
 load(":objc_compilation_artifacts_info.bzl", "CompilationArtifactsInfo")
 load(":objc_intermediate_artifacts.bzl", "create_intermediate_artifacts")
@@ -172,10 +171,10 @@
     compilation_contexts = (
         objc_compilation_context.cc_compilation_contexts +
         cc_helper.get_compilation_contexts_from_deps(
-            cc_semantics.get_cc_runtimes(common_variables.ctx, True),
+            cc_helper.get_cc_runtimes(common_variables.ctx, True),
         )
     )
-    runtimes_copts = cc_semantics.get_cc_runtimes_copts(common_variables.ctx)
+    runtimes_copts = cc_helper.get_cc_runtimes_copts(common_variables.ctx)
 
     return cc_common.compile(
         actions = common_variables.ctx.actions,
diff --git a/tests/cc/common/cc_binary_configured_target_tests.bzl b/tests/cc/common/cc_binary_configured_target_tests.bzl
index b4fd7c2..6d46286 100644
--- a/tests/cc/common/cc_binary_configured_target_tests.bzl
+++ b/tests/cc/common/cc_binary_configured_target_tests.bzl
@@ -1925,7 +1925,7 @@
             _test_linking_mode_features_false_fully,
             _test_linking_mode_features_true_off,
             _test_linking_mode_features_false_off,
-            # _test_cc_runtimes_added_to_libraries,  # copybara-comment-this-out-please
+            _test_cc_runtimes_added_to_libraries,
         ])
 
     test_suite(