Fork `cc_test.bzl`

PiperOrigin-RevId: 791587137
Change-Id: I36c0cb6bb3200ceb661efb769166ce6fe42225a0
diff --git a/cc/common/cc_helper.bzl b/cc/common/cc_helper.bzl
index 7e129c3..afce4d0 100644
--- a/cc/common/cc_helper.bzl
+++ b/cc/common/cc_helper.bzl
@@ -1034,6 +1034,14 @@
         stamp = ctx.attr.stamp
     return stamp
 
+def _has_target_constraints(ctx, constraints):
+    # Constraints is a label_list.
+    for constraint in constraints:
+        constraint_value = constraint[platform_common.ConstraintValueInfo]
+        if ctx.target_platform_has_constraint(constraint_value):
+            return True
+    return False
+
 cc_helper = struct(
     create_strip_action = _create_strip_action,
     get_expanded_env = _get_expanded_env,
@@ -1081,5 +1089,6 @@
     is_test_target = _is_test_target,
     get_linked_artifact = _get_linked_artifact,
     should_create_per_object_debug_info = should_create_per_object_debug_info,
+    has_target_constraints = _has_target_constraints,
 )
 # LINT.ThenChange(https://github.com/bazelbuild/bazel/blob/master/src/main/starlark/builtins_bzl/common/cc/cc_helper.bzl:forked_exports)
diff --git a/cc/private/rules_impl/cc_test.bzl b/cc/private/rules_impl/cc_test.bzl
index 210e0cb..f3e2030 100644
--- a/cc/private/rules_impl/cc_test.bzl
+++ b/cc/private/rules_impl/cc_test.bzl
@@ -1,4 +1,4 @@
-# Copyright 2025 The Bazel Authors. All rights reserved.
+# Copyright 2021 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.
@@ -11,7 +11,155 @@
 # 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.
-"""cc_test rule without a macro"""
 
-# buildifier: disable=native-cc-test
-cc_test = native.cc_test
+"""cc_test Starlark implementation."""
+
+load("//cc:find_cc_toolchain.bzl", "use_cc_toolchain")
+load("//cc/common:cc_helper.bzl", "cc_helper")
+load("//cc/common:cc_info.bzl", "CcInfo")
+load("//cc/common:semantics.bzl", "semantics")
+load(":attrs.bzl", "cc_binary_attrs", "linkstatic_doc", "stamp_doc")
+load(":cc_binary.bzl", "cc_binary_impl")
+load(":cc_shared_library.bzl", "dynamic_deps_initializer")
+
+_CC_TEST_TOOLCHAIN_TYPE = "@bazel_tools//tools/cpp:test_runner_toolchain_type"
+
+def _legacy_cc_test_impl(ctx):
+    binary_info, providers = cc_binary_impl(ctx, [])
+    test_env = {}
+    test_env.update(cc_helper.get_expanded_env(ctx, {}))
+
+    coverage_runfiles, coverage_env = semantics.get_coverage_env(ctx)
+
+    runfiles_list = [binary_info.runfiles]
+    if coverage_runfiles:
+        runfiles_list.append(coverage_runfiles)
+
+    runfiles = ctx.runfiles()
+    runfiles = runfiles.merge_all(runfiles_list)
+
+    test_env.update(coverage_env)
+    providers.append(testing.TestEnvironment(
+        environment = test_env,
+        inherited_environment = ctx.attr.env_inherit,
+    ))
+    providers.append(DefaultInfo(
+        files = binary_info.files,
+        runfiles = runfiles,
+        executable = binary_info.executable,
+    ))
+
+    if cc_helper.has_target_constraints(ctx, ctx.attr._apple_constraints):
+        # When built for Apple platforms, require the execution to be on a Mac.
+        providers.append(testing.ExecutionInfo({"requires-darwin": ""}))
+    return providers
+
+def _impl(ctx):
+    semantics.validate(ctx, "cc_test")
+    cc_test_toolchain = ctx.exec_groups["test"].toolchains[_CC_TEST_TOOLCHAIN_TYPE]
+    if cc_test_toolchain:
+        cc_test_info = cc_test_toolchain.cc_test_info
+    else:
+        # This is the "legacy" cc_test flow
+        return _legacy_cc_test_impl(ctx)
+
+    binary_info, providers = cc_binary_impl(ctx, cc_test_info.linkopts, cc_test_info.linkstatic)
+    processed_environment = cc_helper.get_expanded_env(ctx, {})
+
+    test_providers = cc_test_info.get_runner.func(
+        ctx,
+        binary_info,
+        processed_environment = processed_environment,
+        **cc_test_info.get_runner.args
+    )
+    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())
+
+def cc_test_initializer(**kwargs):
+    """Entry point for cc_test rules.
+
+    It  serves to detect if the `linkstatic` attribute was explicitly set or not.
+    This is to workaround a deficiency in Starlark attributes.
+    (See: https://github.com/bazelbuild/bazel/issues/14434)
+
+    Args:
+        **kwargs: Arguments suitable for cc_test.
+    """
+
+    if "linkstatic" not in kwargs:
+        kwargs["linkstatic"] = semantics.get_linkstatic_default_for_test()
+
+    return dynamic_deps_initializer(**kwargs)
+
+cc_test = rule(
+    initializer = cc_test_initializer,
+    implementation = _impl,
+    doc = """
+<p>
+A <code>cc_test()</code> rule compiles a test.  Here, a test
+is a binary wrapper around some testing code.
+</p>
+
+<p><i>By default, C++ tests are dynamically linked.</i><br/>
+    To statically link a unit test, specify
+    <a href="${link cc_binary.linkstatic}"><code>linkstatic=True</code></a>.
+    It would probably be good to comment why your test needs
+    <code>linkstatic</code>; this is probably not obvious.</p>
+
+<h4>Implicit output targets</h4>
+<ul>
+<li><code><var>name</var>.stripped</code> (only built if explicitly requested): A stripped
+  version of the binary. <code>strip -g</code> is run on the binary to remove debug
+  symbols.  Additional strip options can be provided on the command line using
+  <code>--stripopt=-foo</code>.</li>
+<li><code><var>name</var>.dwp</code> (only built if explicitly requested): If
+  <a href="https://gcc.gnu.org/wiki/DebugFission">Fission</a> is enabled: a debug
+  information package file suitable for debugging remotely deployed binaries. Else: an
+  empty file.</li>
+</ul>
+
+<p>
+See the <a href="${link cc_binary_args}">cc_binary()</a> arguments, except that
+the <code>stamp</code> argument is set to 0 by default for tests and
+that <code>cc_test</code> has extra <a href="${link common-definitions#common-attributes-tests}">
+attributes common to all test rules (*_test)</a>.</p>
+""" + semantics.cc_test_extra_docs,
+    attrs = _cc_test_attrs,
+    outputs = {
+        "dwp_file": "%{name}.dwp",
+        # TODO(b/198254254): Handle case for windows.
+        "stripped_binary": "%{name}.stripped",
+    },
+    fragments = ["cpp", "coverage"] + semantics.additional_fragments(),
+    exec_groups = {
+        "cpp_link": exec_group(toolchains = use_cc_toolchain()),
+        # testing.ExecutionInfo defaults to an exec_group of "test".
+        "test": exec_group(toolchains = [config_common.toolchain_type(_CC_TEST_TOOLCHAIN_TYPE, mandatory = False)]),
+    } | semantics.extra_exec_groups,
+    toolchains = [] +
+                 use_cc_toolchain() +
+                 semantics.get_runtimes_toolchain(),
+    test = True,
+    provides = [CcInfo],
+)