Migrate more shell tests from bazel to rules_cc.

I couldn't get test_include_validation_sandbox_disabled to work and didn't want to add a Java dep for test_sibling_repository_layout_include_external_repo_output; those remain in bazel for now.

PiperOrigin-RevId: 955961739
Change-Id: Id5565b0e82296488a1a738dcf025473ab857c387
diff --git a/BUILD b/BUILD
index 278e2f5..0dd8f77 100644
--- a/BUILD
+++ b/BUILD
@@ -51,6 +51,7 @@
     srcs = [
         "BUILD",
         "MODULE.bazel",
+        "MODULE.bazel.lock",
         "//cc:srcs",
     ],
     visibility = ["//:__subpackages__"],
diff --git a/cc/BUILD b/cc/BUILD
index 6e68abd..ba15d95 100644
--- a/cc/BUILD
+++ b/cc/BUILD
@@ -77,8 +77,10 @@
         "**/BUILD",
     ]) + [
         "//cc/common:srcs",
+        "//cc/compiler:srcs",
         "//cc/private:srcs",
         "//cc/proto:srcs",
+        "//cc/runfiles:srcs",
         "//cc/settings:srcs",
         "//cc/toolchains:srcs",
         "@bazel_skylib//:test_deps",
diff --git a/cc/compiler/BUILD b/cc/compiler/BUILD
index 6db7145..33e3db8 100644
--- a/cc/compiler/BUILD
+++ b/cc/compiler/BUILD
@@ -47,6 +47,11 @@
 
 licenses(["notice"])
 
+filegroup(
+    name = "srcs",
+    srcs = ["BUILD"],
+)
+
 compiler_flag(name = "compiler")
 
 config_setting(
diff --git a/cc/private/BUILD b/cc/private/BUILD
index cb150f0..475461f 100644
--- a/cc/private/BUILD
+++ b/cc/private/BUILD
@@ -34,6 +34,7 @@
         "**/BUILD",
         "*.bzl",
     ]) + [
+        "//cc/private/bazel7:BUILD",
         "//cc/private/compile:srcs",
         "//cc/private/coverage:srcs",
         "//cc/private/link:srcs",
diff --git a/cc/runfiles/BUILD b/cc/runfiles/BUILD
index 6a81a7c..a46a8e3 100644
--- a/cc/runfiles/BUILD
+++ b/cc/runfiles/BUILD
@@ -10,3 +10,14 @@
     strip_include_prefix = ".",
     visibility = ["//visibility:public"],
 )
+
+filegroup(
+    name = "srcs",
+    srcs = glob([
+        "*.cc",
+        "*.h",
+    ]) + [
+        "BUILD",
+    ],
+    visibility = ["//visibility:public"],
+)
diff --git a/tests/integration/BUILD b/tests/integration/BUILD
index a2681f0..6beca51 100644
--- a/tests/integration/BUILD
+++ b/tests/integration/BUILD
@@ -1,4 +1,4 @@
-load("@bazel_features//:features.bzl", "bazel_features")  # copybara-uncomment-this-please
+load("@bazel_features//:features.bzl", "bazel_features")
 
 #buildifier: disable=bzl-visibility
 load("@bazel_features//private:util.bzl", _features_ge = "ge")
@@ -15,6 +15,19 @@
     test_script = "cc_tests.sh",
 )
 
+# Tests migrated from bazelbuild/bazel/src/test/shell/bazel/cc_integration_test.sh
+# In a separate file from cc_tests.sh to make migration clearer.
+rules_cc_integration_test(
+    name = "cc_integration_test",
+    shard_count = 10,
+    target_compatible_with = select({
+        "@platforms//os:macos": ["@platforms//:incompatible"],
+        "@platforms//os:windows": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    }) if bazel_features.cc.cc_common_is_in_rules_cc else ["@platforms//:incompatible"],
+    test_script = "cc_integration_test.sh",
+)
+
 rules_cc_integration_test(
     name = "lto_tests",
     target_compatible_with = select({
@@ -47,15 +60,15 @@
     test_script = "fission_tests.sh",
 )
 
-VERSION_GATE = [] if bazel_features.cc.cc_common_is_in_rules_cc else ["@platforms//:incompatible"]  # copybara-uncomment-this-please
-# VERSION_GATE = ["@platforms//:incompatible"]  # copybara-comment-this-out-please
+STATIC_LIBRARY_VERSION_GATE = [] if bazel_features.cc.cc_common_is_in_rules_cc else ["@platforms//:incompatible"]  # copybara-uncomment-this-please
+# STATIC_LIBRARY_VERSION_GATE = ["@platforms//:incompatible"]  # copybara-comment-this-out-please
 
 rules_cc_integration_test(
     name = "cc_static_library_failure_tests",
     tags = ["notap"],
     target_compatible_with = select({
         "@platforms//os:macos": ["@platforms//:incompatible"],
-        "//conditions:default": VERSION_GATE,
+        "//conditions:default": STATIC_LIBRARY_VERSION_GATE,
     }),
     test_script = "cc_static_library_failure_tests.sh",
 )
diff --git a/tests/integration/cc_integration_test.sh b/tests/integration/cc_integration_test.sh
new file mode 100755
index 0000000..08aba64
--- /dev/null
+++ b/tests/integration/cc_integration_test.sh
@@ -0,0 +1,2536 @@
+#!/usr/bin/env bash
+#
+# Copyright 2016 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.
+#
+
+set -euo pipefail
+
+# Tests migrated from bazelbuild/bazel/src/test/shell/bazel/cc_integration_test.sh
+# In a separate file from cc_tests.sh to make migration clearer.
+
+source "$(rlocation rules_cc/tests/test_utils.sh)"
+source "$(rlocation rules_cc/tests/unittest.bash)"
+
+function test_extra_action_for_compile() {
+  
+  mkdir -p ea
+  cat > ea/BUILD <<EOF
+load("@rules_cc//cc:cc_library.bzl", "cc_library")
+action_listener(
+    name = "al",
+    extra_actions = [":ea"],
+    mnemonics = ["CppCompile"],
+    visibility = ["//visibility:public"],
+)
+
+extra_action(
+    name = "ea",
+    cmd = "if ! [[ -r ea/cc.cc ]]; then echo 'source file not in inputs'; exit 1; fi",
+)
+
+cc_library(
+    name = "cc",
+    srcs = ["cc.cc"],
+)
+EOF
+
+  echo 'void cc() {}' > ea/cc.cc
+
+  bazel build --experimental_action_listener=//ea:al //ea:cc || fail "expected success"
+}
+
+function test_cc_library_include_prefix_external_repository() {
+  is_bazel || return 0
+
+  r="$TEST_TMPDIR/r"
+  mkdir -p "$TEST_TMPDIR/r/foo/v1"
+  touch "$TEST_TMPDIR/r/REPO.bazel"
+  echo "#define FOO 42" > "$TEST_TMPDIR/r/foo/v1/foo.h"
+  cat > "$TEST_TMPDIR/r/foo/BUILD" <<EOF
+load("@rules_cc//cc:cc_library.bzl", "cc_library")
+cc_library(
+  name = "foo",
+  hdrs = ["v1/foo.h"],
+  include_prefix = "foolib",
+  strip_include_prefix = "v1",
+  visibility = ["//visibility:public"],
+)
+EOF
+  
+  cat >> MODULE.bazel <<EOF
+local_repository = use_repo_rule("@bazel_tools//tools/build_defs/repo:local.bzl", "local_repository")
+local_repository(
+  name = "foo",
+  path = "$TEST_TMPDIR/r",
+)
+EOF
+
+  cat > BUILD <<EOF
+load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
+cc_binary(
+  name = "ok",
+  srcs = ["ok.cc"],
+  deps = ["@foo//foo"],
+)
+
+cc_binary(
+  name = "still_ok",
+  srcs = ["still_ok.cc"],
+  deps = ["@foo//foo"],
+)
+EOF
+
+  cat > ok.cc <<EOF
+#include <stdio.h>
+#include "foolib/foo.h"
+int main() {
+  printf("FOO is %d\n", FOO);
+}
+EOF
+
+  cat > still_ok.cc <<EOF
+#include <stdio.h>
+#include "foo/v1/foo.h"
+int main() {
+  printf("FOO is %d\n", FOO);
+}
+EOF
+
+  bazel build :ok || fail "Should have found include at synthetic path"
+  bazel build :still_ok \
+    || fail "Should have found include at repository-relative path"
+}
+
+function test_tree_artifact_headers_are_invalidated() {
+
+  mkdir -p "ta_headers"
+  cat > "ta_headers/BUILD" <<EOF
+load("@rules_shell//shell:sh_binary.bzl", "sh_binary")
+load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
+load("@rules_cc//cc:cc_library.bzl", "cc_library")
+
+load(":mygen.bzl", "mygen")
+
+sh_binary(
+  name = "mygen_sh",
+  srcs = ["mygen.sh"],
+  visibility = ["//visibility:public"],
+)
+
+mygen(
+    name="mylib_generated",
+    srcs=[":mydef.txt"],
+)
+
+cc_library(
+    name = "mylib",
+    srcs = [":mylib_generated"],
+    hdrs = [":mylib_generated"],
+)
+
+cc_binary(
+    name = "myexec",
+    srcs = [],
+    deps = [":mylib"],
+)
+EOF
+  cat > "ta_headers/mygen.sh" <<'EOF'
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+src_files=$1
+hdr_files=$2
+
+fc_name=$(cat ta_headers/mydef.txt)
+
+mkdir -p ${src_files}
+mkdir -p ${hdr_files}
+
+cat > ${src_files}/main.c <<EOT
+#include "ta_headers/files.h/another.h"
+int main(void) {
+    return MYFC();
+}
+EOT
+
+cat > ${src_files}/another.c <<EOT
+#include "ta_headers/files.h/another.h"
+int ${fc_name}(void) {
+    return 0;
+}
+EOT
+
+cat > ${hdr_files}/another.h <<EOT
+#define MYFC ${fc_name}
+int ${fc_name}(void);
+EOT
+EOF
+  chmod +x ta_headers/mygen.sh
+  cat > "ta_headers/mygen.bzl" <<EOF
+def _mygen_impl(ctx):
+  args = ctx.actions.args()
+  treeC = ctx.actions.declare_directory("files.c")
+  treeH = ctx.actions.declare_directory("files.h")
+  args.add(treeC.path)
+  args.add(treeH.path)
+  ctx.actions.run(
+      inputs = ctx.files.srcs,
+      outputs = [treeC, treeH],
+      arguments = [args],
+      executable = ctx.executable._mygen,
+  )
+  return [DefaultInfo(files=depset([treeC, treeH]))]
+
+mygen = rule(
+  implementation=_mygen_impl,
+  attrs={
+    "srcs": attr.label_list(allow_files=True),
+    "_mygen": attr.label(
+      cfg="exec",
+      executable=True,
+      allow_files=True,
+      default=":mygen_sh",
+    ),
+  },
+)
+EOF
+
+  # So we have another.h defining a macro that is used by both main.c and
+  # another.c. :main depends on :another, and gets the header through the
+  # tree artifact. First build is fine.
+  echo "fc1" > "ta_headers/mydef.txt"
+  bazel build //ta_headers:myexec || fail \
+    "First build failed, something is wrong with the test."
+
+  # Now we change the content of another.h to define a different macro.
+  # This test verifies that not only another.c is recompiled, but also
+  # main.c. This is a regression test for
+  # https://github.com/bazelbuild/bazel/issues/5785.
+  echo "fc2" > "ta_headers/mydef.txt"
+  bazel build //ta_headers:myexec || fail \
+    "Second build failed, tree artifact was not invalidated."
+}
+
+function test_save_feature_state() {
+  
+  mkdir -p ea
+  cat > ea/BUILD <<EOF
+load("@rules_cc//cc:cc_library.bzl", "cc_library")
+cc_library(
+    name = "cc",
+    srcs = ["cc.cc", "cc1.cc"],
+    features = ["test_feature"],
+)
+EOF
+
+  echo 'void cc() {}' > ea/cc.cc
+  echo 'void cc1() {}' > ea/cc1.cc
+
+  bazel build --experimental_builtins_injection_override=+cc_library --experimental_save_feature_state //ea:cc || fail "expected success"
+  ls bazel-bin/ea/cc_feature_state.txt || fail "cc_feature_state.txt not created"
+  # This assumes "grep" is supported in any environment bazel is used.
+  grep "test_feature" bazel-bin/ea/cc_feature_state.txt || fail "test_feature should have been found in feature_state."
+}
+
+# TODO: test include dirs and defines
+function setup_cc_starlark_api_test() {
+  local pkg="$1"
+
+  touch "$pkg"/MODULE.bazel
+  
+
+  mkdir "$pkg"/include_dir
+  touch "$pkg"/include_dir/include.h
+  mkdir "$pkg"/system_include_dir
+  touch "$pkg"/system_include_dir/system_include.h
+  mkdir "$pkg"/quote_include_dir
+  touch "$pkg"/quote_include_dir/quote_include.h
+
+  cat > "$pkg"/cc_api_rules.bzl <<EOF
+
+"""Example C++ API usage"""
+
+load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cc_toolchain", "use_cc_toolchain")
+load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
+load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
+
+def _filter_none(input_list):
+    filtered_list = []
+    for element in input_list:
+        if element != None:
+            filtered_list.append(element)
+    return filtered_list
+
+def _cc_lib_impl(ctx):
+    cc_toolchain = find_cc_toolchain(ctx)
+    feature_configuration = cc_common.configure_features(
+        ctx = ctx,
+        cc_toolchain = cc_toolchain,
+        requested_features = ctx.features,
+        unsupported_features = ctx.disabled_features,
+    )
+    compilation_contexts = []
+    linking_contexts = []
+    for dep in ctx.attr.deps:
+        if CcInfo in dep:
+            compilation_contexts.append(dep[CcInfo].compilation_context)
+            linking_contexts.append(dep[CcInfo].linking_context)
+
+    (compilation_context, compilation_outputs) = cc_common.compile(
+        name = ctx.label.name,
+        actions = ctx.actions,
+        feature_configuration = feature_configuration,
+        cc_toolchain = cc_toolchain,
+        public_hdrs = ctx.files.public_hdrs,
+        private_hdrs = ctx.files.private_hdrs,
+        srcs = ctx.files.srcs,
+        includes = ctx.attr.includes,
+        quote_includes = ctx.attr.quote_includes,
+        system_includes = ctx.attr.system_includes,
+        defines = ctx.attr.defines,
+        user_compile_flags = ctx.attr.user_compile_flags,
+        compilation_contexts = compilation_contexts,
+    )
+    (linking_context, linking_outputs) = cc_common.create_linking_context_from_compilation_outputs(
+        name = ctx.label.name,
+        actions = ctx.actions,
+        feature_configuration = feature_configuration,
+        cc_toolchain = cc_toolchain,
+        language = "c++",
+        compilation_outputs = compilation_outputs,
+        linking_contexts = linking_contexts,
+    )
+    library = linking_outputs.library_to_link
+    files = []
+    files.extend(compilation_outputs.objects)
+    files.extend(compilation_outputs.pic_objects)
+    files.append(library.pic_static_library)
+    files.append(library.static_library)
+
+    files.append(library.dynamic_library)
+
+    return [
+        DefaultInfo(
+            files = depset(_filter_none(files)),
+        ),
+        CcInfo(
+            compilation_context = compilation_context,
+            linking_context = linking_context,
+        ),
+    ]
+
+cc_lib = rule(
+    implementation = _cc_lib_impl,
+    attrs = {
+        "public_hdrs": attr.label_list(allow_files = [".h"]),
+        "private_hdrs": attr.label_list(allow_files = [".h"]),
+        "srcs": attr.label_list(allow_files = [".cc"]),
+        "deps": attr.label_list(
+            allow_empty = True,
+            providers = [[CcInfo]],
+        ),
+        "user_compile_flags": attr.string_list(),
+        "user_link_flags": attr.string_list(),
+        "includes": attr.string_list(),
+        "quote_includes": attr.string_list(),
+        "system_includes": attr.string_list(),
+        "defines": attr.string_list(),
+        "alwayslink": attr.bool(default = False),
+    },
+    fragments = ["cpp"],
+    toolchains = use_cc_toolchain(),
+)
+
+def _cc_bin_impl(ctx):
+    cc_toolchain = find_cc_toolchain(ctx)
+    feature_configuration = cc_common.configure_features(
+        ctx = ctx,
+        cc_toolchain = cc_toolchain,
+        requested_features = ctx.features,
+        unsupported_features = ctx.disabled_features,
+    )
+    compilation_contexts = []
+    linking_contexts = []
+    for dep in ctx.attr.deps:
+        if CcInfo in dep:
+            compilation_contexts.append(dep[CcInfo].compilation_context)
+            linking_contexts.append(dep[CcInfo].linking_context)
+
+    (_compilation_context, compilation_outputs) = cc_common.compile(
+        name = ctx.label.name,
+        actions = ctx.actions,
+        feature_configuration = feature_configuration,
+        cc_toolchain = cc_toolchain,
+        srcs = ctx.files.srcs,
+        compilation_contexts = compilation_contexts,
+    )
+    output_type = "dynamic_library" if ctx.attr.linkshared else "executable"
+    user_link_flags = []
+    for user_link_flag in ctx.attr.user_link_flags:
+        user_link_flags.append(ctx.expand_location(user_link_flag, targets = ctx.attr.additional_linker_inputs))
+
+    malloc = ctx.attr._custom_malloc or ctx.attr.malloc
+    linking_contexts.append(malloc[CcInfo].linking_context)
+
+    linking_outputs = cc_common.link(
+        name = ctx.label.name,
+        actions = ctx.actions,
+        feature_configuration = feature_configuration,
+        cc_toolchain = cc_toolchain,
+        language = "c++",
+        compilation_outputs = compilation_outputs,
+        linking_contexts = linking_contexts,
+        user_link_flags = user_link_flags,
+        link_deps_statically = ctx.attr.linkstatic,
+        stamp = ctx.attr.stamp,
+        additional_inputs = ctx.files.additional_linker_inputs,
+        output_type = output_type,
+    )
+    files = []
+    if output_type == "executable":
+        files.append(linking_outputs.executable)
+    elif output_type == "dynamic_library":
+        files.append(linking_outputs.library_to_link.dynamic_library)
+        files.append(linking_outputs.library_to_link.resolved_symlink_dynamic_library)
+
+    return [
+        DefaultInfo(
+            files = depset(_filter_none(files)),
+            runfiles = ctx.runfiles(files = ctx.files.data),
+        ),
+    ]
+
+cc_bin = rule(
+    implementation = _cc_bin_impl,
+    attrs = {
+        "srcs": attr.label_list(allow_files = [".cc"]),
+        "additional_linker_inputs": attr.label_list(
+            allow_empty = True,
+            allow_files = [".lds"],
+        ),
+        "deps": attr.label_list(
+            allow_empty = True,
+            providers = [CcInfo],
+        ),
+        "data": attr.label_list(
+            default = [],
+            allow_files = True,
+        ),
+        "user_link_flags": attr.string_list(),
+        "linkstatic": attr.bool(default = True),
+        "linkshared": attr.bool(default = False),
+        "stamp": attr.int(default = -1),
+        "malloc": attr.label(
+            default = "@bazel_tools//tools/cpp:malloc",
+            providers = [CcInfo],
+        ),
+        # Exposes --custom_malloc flag, if you really need behavior to match
+        # native.cc_binary and have that override the malloc attr.
+        "_custom_malloc": attr.label(
+            default = configuration_field(
+                fragment = "cpp",
+                name = "custom_malloc",
+            ),
+            providers = [CcInfo],
+        ),
+    },
+    fragments = ["cpp"],
+    toolchains = use_cc_toolchain(),
+)
+EOF
+
+  cat > "$pkg"/BUILD << EOF
+load("//${pkg}:cc_api_rules.bzl", "cc_lib", "cc_bin")
+
+cc_lib(
+    name = "a",
+    srcs = [
+        "a1.cc",
+        "a2.cc",
+    ],
+    private_hdrs = [
+      "a2.h",
+      "include_dir/include.h",
+      "system_include_dir/system_include.h",
+      "quote_include_dir/quote_include.h"
+    ],
+    user_compile_flags = ["-DA_DEFINITION_LOCAL"],
+    public_hdrs = ["a.h"],
+    includes = ["$pkg/include_dir"],
+    system_includes = ["$pkg/system_include_dir"],
+    quote_includes = ["$pkg/quote_include_dir"],
+    defines = ["A_DEFINITION"],
+    deps = [
+        ":b",
+        ":d",
+    ],
+)
+
+cc_lib(
+    name = "b",
+    srcs = [
+        "b.cc",
+    ],
+    public_hdrs = ["b.h"],
+    deps = [":c"],
+)
+
+cc_lib(
+    name = "c",
+    srcs = [
+        "c.cc",
+    ],
+    public_hdrs = ["c.h"],
+)
+
+cc_lib(
+    name = "d",
+    srcs = ["d.cc"],
+    public_hdrs = ["d.h"],
+)
+
+cc_bin(
+    name = "e",
+    srcs = ["e.cc"],
+    data = [":f"],
+    linkstatic = 1,
+    user_link_flags = [
+        "-ldl",
+        "-lm",
+        "-Wl,-rpath,bazel-bin/${pkg}",
+    ],
+    deps = [
+        ":a",
+    ],
+)
+
+cc_bin(
+    name = "f",
+    srcs = ["f.cc"],
+    linkshared = 1,
+    deps = [
+        ":a",
+    ],
+)
+EOF
+
+  cat > $pkg/a1.cc << EOF
+#include <system_include.h>
+#include "include.h"
+
+#include "quote_include.h"
+#include "a.h"
+#include "a2.h"
+
+#ifdef A_DEFINITION_LOCAL
+#include "b.h"
+#include "d.h"
+#endif
+
+using namespace std;
+
+string alongernamethanusual() { return "a1" + a2() + b() + d(); }
+EOF
+
+  cat > $pkg/a2.cc << EOF
+#include <string>
+using namespace std;
+
+string a2() { return "a2"; }
+EOF
+
+  cat > $pkg/a.h << EOF
+#ifndef HEADER_A
+#define HEADER_A
+#include <string>
+using namespace std;
+string alongernamethanusual();
+#endif
+EOF
+
+  cat > $pkg/a2.h << EOF
+#ifndef HEADER_A2
+#define HEADER_A2
+#include <string>
+using namespace std;
+string a2();
+#endif
+EOF
+
+  cat > $pkg/b.cc << EOF
+#include "b.h"
+#include <string>
+#include "c.h"
+using namespace std;
+
+string b() { return "b" + c(); }
+EOF
+
+  cat > $pkg/b.h << EOF
+#ifndef HEADER_B
+#define HEADER_B
+#include <string>
+using namespace std;
+string b();
+#endif
+EOF
+
+  cat > $pkg/c.cc << EOF
+#include "c.h"
+#include <algorithm>
+#include <string>
+
+using namespace std;
+
+string c() { return "c"; }
+EOF
+
+  cat > $pkg/c.h << EOF
+#ifndef HEADER_C
+#define HEADER_C
+#include <string>
+using namespace std;
+string c();
+#endif
+EOF
+
+  cat > $pkg/d.cc << EOF
+#include "d.h"
+#include <string>
+using namespace std;
+
+string d() { return "d"; }
+EOF
+
+  cat > $pkg/d.h << EOF
+#ifndef HEADER_D
+#define HEADER_D
+#include <string>
+using namespace std;
+string d();
+#endif
+EOF
+
+  cat > $pkg/e.cc << EOF
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <iostream>
+
+#ifdef A_DEFINITION
+#include "a.h"
+#endif
+
+#ifdef A_DEFINITION_LOCAL
+#include "thisdoesntexist.h"
+#endif
+
+using namespace std;
+
+#ifdef __APPLE__
+#define DYNAMIC_LIB_EXT "dylib"
+#else
+#define DYNAMIC_LIB_EXT "so"
+#endif
+
+int main() {
+  void* handle = dlopen("libf." DYNAMIC_LIB_EXT, RTLD_LAZY);
+
+  typedef string (*f_t)();
+
+  f_t f = (f_t)dlsym(handle, "f");
+  cout << alongernamethanusual() + f() << endl;
+  return 0;
+}
+
+EOF
+
+  cat > $pkg/f.cc << EOF
+#include <algorithm>
+#include <string>
+#include "a.h"
+
+using namespace std;
+
+extern "C" string f() {
+  string str = alongernamethanusual();
+  reverse(str.begin(), str.end());
+  return str;
+}
+EOF
+
+  cat > $pkg/script.lds << EOF
+VERS_42.0 {
+  global:
+    f;
+};
+EOF
+
+
+}
+
+function test_cc_starlark_api_default_values() {
+  is_bazel || return 0
+
+  local pkg="${FUNCNAME[0]}"
+  mkdir -p "$pkg"
+
+  setup_cc_starlark_api_test "${FUNCNAME[0]}"
+
+  bazel build --experimental_cc_skylark_api_enabled_packages=, --verbose_failures \
+    //"$pkg":e  &>"$TEST_log" || fail "Build failed"
+
+  nm -u bazel-bin/"$pkg"/e  | grep alongernamethanusual && \
+    fail "alongernamethanusual is not defined"
+
+  bazel-bin/"$pkg"/e | grep a1a2bcddcb2a1a || fail "output is incorrect"
+}
+
+
+function test_cc_starlark_api_link_static_false() {
+  is_bazel || return 0
+
+  if is_darwin; then
+    return 0
+  fi
+
+  local pkg="${FUNCNAME[0]}"
+  mkdir -p "$pkg"
+
+  setup_cc_starlark_api_test "${FUNCNAME[0]}"
+
+  cat >> "$pkg"/BUILD << EOF
+cc_bin(
+    name = "g",
+    srcs = ["e.cc"],
+    data = [":f"],
+    linkstatic = 0,
+    user_link_flags = [
+        "-ldl",
+        "-lm",
+        "-Wl,-rpath,bazel-bin/${pkg}",
+    ],
+    deps = [
+        ":a",
+    ],
+)
+EOF
+
+  bazel build --experimental_cc_skylark_api_enabled_packages=, --verbose_failures \
+    //"$pkg":g  &>"$TEST_log" || fail "Build failed"
+
+  nm -u bazel-bin/"$pkg"/g  | grep alongernamethanusual || fail "alongernamethanusual is defined"
+
+  bazel-bin/"$pkg"/g | grep a1a2bcddcb2a1a || fail "output is incorrect"
+}
+
+function test_cc_starlark_api_additional_inputs() {
+  is_bazel || return 0
+
+  # This uses --version-script which isn't available on Mac linker.
+  if is_darwin; then
+    return 0
+  fi
+
+  local pkg="${FUNCNAME[0]}"
+  mkdir -p "$pkg"
+
+  setup_cc_starlark_api_test "${FUNCNAME[0]}"
+
+  cat >> "$pkg"/BUILD << EOF
+cc_bin(
+    name = "g",
+    srcs = ["f.cc"],
+    linkshared = 1,
+    additional_linker_inputs = ["script.lds"],
+    user_link_flags = [
+        "-ldl",
+        "-lm",
+        "-Wl,-rpath,bazel-bin/${pkg}",
+        "-Wl,--version-script=\$(location script.lds)",
+    ],
+    deps = [
+        ":a",
+    ],
+)
+EOF
+
+  bazel build --experimental_cc_skylark_api_enabled_packages=, --verbose_failures \
+    //"$pkg":g  &>"$TEST_log" || fail "Build failed"
+
+  nm -D bazel-bin/"$pkg"/libg.so  | grep VERS_42.0 || fail "VERS_42.0 not in binary"
+}
+
+function test_aspect_accessing_args_link_action_with_tree_artifact() {
+  is_bazel || return 0
+
+  # This test assumes the presence of "nodeps" dynamic libraries, which do not
+  # function on Apple platforms.
+  if is_darwin; then
+    return 0
+  fi
+
+  
+  local package="${FUNCNAME[0]}"
+  mkdir -p "${package}"
+  cat > "${package}/makes_tree_artifacts.sh" <<EOF
+#!/usr/bin/env bash
+my_dir=\$1
+
+echo "int a() { return 0; }" > \$my_dir/a.cc
+echo "int b() { return 0; }" > \$my_dir/b.cc
+echo "int c() { return 0; }" > \$my_dir/c.cc
+EOF
+  chmod 755 "${package}/makes_tree_artifacts.sh"
+
+  cat > "${package}/write.sh" <<EOF
+#!/usr/bin/env bash
+output_file=\$1
+shift;
+
+echo "\$@" > \$output_file
+EOF
+  chmod 755 "${package}/write.sh"
+
+  cat > "${package}/lib.bzl" <<EOF
+def _tree_art_impl(ctx):
+  my_dir = ctx.actions.declare_directory('dir.cc')
+  ctx.actions.run(
+    executable = ctx.executable._makes_tree,
+    outputs = [my_dir],
+    arguments = [my_dir.path])
+
+  return [DefaultInfo(files=depset([my_dir]))]
+
+tree_art_rule = rule(implementation = _tree_art_impl,
+  attrs = {
+    "_makes_tree" : attr.label(allow_single_file = True,
+      cfg = "exec",
+      executable = True,
+      default = "//${package}:makes_tree_artifacts.sh"),
+    "_write" : attr.label(allow_single_file = True,
+      cfg = "exec",
+      executable = True,
+      default = "//${package}:write.sh")})
+
+def _actions_test_impl(target, ctx):
+  for action in target.actions:
+    if action.mnemonic != "CppArchive":
+      continue
+    aspect_out = ctx.actions.declare_file('aspect_out')
+    ctx.actions.write(aspect_out, action.args[1])
+    return [OutputGroupInfo(out=[aspect_out])]
+  fail("Expected the at least one action to be CppArchive.")
+
+actions_test_aspect = aspect(implementation = _actions_test_impl)
+EOF
+
+  cat > "${package}/BUILD" <<EOF
+load("@rules_cc//cc:cc_library.bzl", "cc_library")
+load(":lib.bzl", "tree_art_rule")
+
+tree_art_rule(name = "tree")
+
+cc_library(
+  name = "x",
+  srcs = [":tree"],
+)
+EOF
+
+  bazel build "${package}:x" \
+      --aspects="//${package}:lib.bzl%actions_test_aspect" \
+      --output_groups=out
+
+  cat "bazel-bin/${package}/aspect_out" | grep "\(ar\|libtool\)" \
+      || fail "args didn't contain the tool path"
+
+  cat "bazel-bin/${package}/aspect_out" | grep "/a.*o" \
+      || fail "args didn't contain tree artifact paths"
+  cat "bazel-bin/${package}/aspect_out" | grep "/b.*o" \
+      || fail "args didn't contain tree artifact paths"
+}
+
+function test_argv_in_compile_action() {
+
+  local package="${FUNCNAME[0]}"
+  mkdir -p "${package}"
+
+  cat > "${package}/lib.bzl" <<EOF
+def _actions_test_impl(target, ctx):
+    action = [a for a in target.actions if a.mnemonic == "CppCompile"][0]
+    aspect_out = ctx.actions.declare_file('aspect_out')
+    ctx.actions.run_shell(inputs = action.inputs,
+                          outputs = [aspect_out],
+                          command = "echo \$@ > " + aspect_out.path,
+                          arguments = action.argv)
+    return [OutputGroupInfo(out=[aspect_out])]
+
+actions_test_aspect = aspect(implementation = _actions_test_impl)
+EOF
+
+  touch "${package}/x.cc"
+  cat > "${package}/BUILD" <<EOF
+load("@rules_cc//cc:cc_library.bzl", "cc_library")
+cc_library(
+  name = "x",
+  srcs = ["x.cc"],
+)
+EOF
+
+  bazel build "${package}:x" \
+      --aspects="//${package}:lib.bzl%actions_test_aspect" \
+      --output_groups=out
+
+  cat "bazel-bin/${package}/aspect_out" | \
+      grep "\(gcc\|clang\|clanc-cl.exe\|cl.exe\|cc_wrapper.sh\)" \
+      || fail "args didn't contain the tool path"
+
+  cat "bazel-bin/${package}/aspect_out" | grep "a.*o .*b.*o .*c.*o" \
+      || fail "args didn't contain tree artifact paths"
+}
+
+function test_directory_arg_compile_action() {
+  # This test assumes the presence of "nodeps" dynamic libraries, which do not
+  # function on Apple platforms.
+  if is_darwin; then
+    return 0
+  fi
+
+  
+  local package="${FUNCNAME[0]}"
+  mkdir -p "${package}"
+
+  cat > "${package}/lib.bzl" <<EOF
+def _actions_test_impl(target, ctx):
+    action = None
+    for a in target.actions:
+      if a.mnemonic == "CppCompile":
+        action = a
+        break
+    if not action:
+      fail("Expected the at least one action to be CppCompile")
+    aspect_out = ctx.actions.declare_file('aspect_out')
+    ctx.actions.run_shell(inputs = action.inputs,
+                          outputs = [aspect_out],
+                          command = "echo \$@ > " + aspect_out.path,
+                          arguments = action.args)
+    return [OutputGroupInfo(out=[aspect_out])]
+
+actions_test_aspect = aspect(implementation = _actions_test_impl)
+EOF
+
+  touch "${package}/x.cc"
+  cat > "${package}/BUILD" <<EOF
+load("@rules_cc//cc:cc_library.bzl", "cc_library")
+cc_library(
+  name = "x",
+  srcs = ["x.cc"],
+)
+EOF
+
+  bazel build "${package}:x" \
+      --aspects="//${package}:lib.bzl%actions_test_aspect" \
+      --output_groups=out
+
+  cat "bazel-bin/${package}/aspect_out" | \
+      grep "\(gcc\|clang\|clanc-cl.exe\|cl.exe\)" \
+      || fail "args didn't contain the tool path"
+
+  cat "bazel-bin/${package}/aspect_out" | grep "a.*o .*b.*o .*c.*o" \
+      || fail "args didn't contain tree artifact paths"
+}
+
+function test_reconstructing_cpp_actions() {
+  if is_darwin; then
+    # Darwin toolchain uses env variables and those are not properly exported
+    # to Starlark.
+    # TODO(#10376): Remove once env vars on C++ actions are exported.
+    return 0
+  fi
+
+  
+  local package="${FUNCNAME[0]}"
+  mkdir -p "${package}"
+
+  cat > "${package}/lib.bzl" <<EOF
+load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
+
+def _actions_test_impl(target, ctx):
+    compile_action = None
+    archive_action = None
+    link_action = None
+
+    for action in target.actions:
+      if action.mnemonic == "CppCompile":
+        compile_action = action
+      if action.mnemonic == "CppArchive":
+        archive_action = action
+      if action.mnemonic == "CppLink":
+        link_action = action
+
+    if not compile_action or not archive_action or not link_action:
+      fail("Couldn't find compile, archive, or link action.")
+
+    cc_info = target[CcInfo]
+    compile_action_outputs = compile_action.outputs.to_list()
+
+    compile_args = ctx.actions.declare_file("compile_args")
+    ctx.actions.run_shell(
+        outputs = [compile_args],
+        command = "echo \$@ > " + compile_args.path,
+        arguments = compile_action.args,
+    )
+
+    inputs = depset(
+        direct = [compile_args],
+        transitive = [
+            compile_action.inputs,
+            # Because C++ compilation actions prune their headers in the
+            # execution phase, and this code runs in analysis phase,
+            # action.inputs is not processed yet. It doesn't contain
+            # headers/module files yet. Let's add all unpruned headers
+            # explicitly.
+            cc_info.compilation_context.headers,
+        ],
+    )
+
+    compile_out = ctx.actions.declare_file("compile_out.o")
+    ctx.actions.run_shell(
+        inputs = inputs,
+        mnemonic = "RecreatedCppCompile",
+        outputs = [compile_out],
+        env = compile_action.env,
+        command = "\$(cat %s | sed 's|%s|%s|g' | sed 's|%s|%s|g')" % (
+            compile_args.path,
+            # We need to replace the original output path with something else
+            compile_action_outputs[0].path,
+            compile_out.path,
+            # We need to replace the original .d file output path with something
+            # else
+            compile_action_outputs[0].path.replace(".o", ".d"),
+            compile_out.path + ".d",
+        ),
+    )
+
+    archive_out = ctx.actions.declare_file("archive_out.a")
+    ctx.actions.run_shell(
+        inputs = archive_action.inputs,
+        mnemonic = "RecreatedCppArchive",
+        outputs = [archive_out],
+        env = archive_action.env,
+        command = "\$@ && cp %s %s" % (
+            archive_action.outputs.to_list()[0].path,
+            archive_out.path,
+        ),
+        arguments = archive_action.args,
+    )
+
+    link_out = ctx.actions.declare_file("link_out.so")
+    ctx.actions.run_shell(
+        inputs = link_action.inputs,
+        mnemonic = "RecreatedCppLink",
+        outputs = [link_out],
+        env = link_action.env,
+        command = "\$@ && cp %s %s" % (
+            link_action.outputs.to_list()[0].path,
+            link_out.path,
+        ),
+        arguments = link_action.args,
+    )
+
+    return [OutputGroupInfo(out = [
+        compile_args,
+        compile_out,
+        archive_out,
+        link_out,
+    ])]
+
+actions_test_aspect = aspect(implementation = _actions_test_impl)
+EOF
+
+  echo "inline int x() { return 42; }" > "${package}/x.h"
+  cat > "${package}/a.cc" <<EOF
+#include "${package}/x.h"
+
+int a() { return x(); }
+EOF
+  cat > "${package}/BUILD" <<EOF
+load("@rules_cc//cc:cc_library.bzl", "cc_library")
+cc_library(
+  name = "x",
+  hdrs  = ["x.h"],
+)
+
+cc_library(
+  name = "a",
+  srcs = ["a.cc"],
+  deps = [":x"],
+)
+EOF
+
+  # Test that actions are reconstructible under default configuration
+  bazel build "${package}:a" \
+      --aspects="//${package}:lib.bzl%actions_test_aspect" \
+      --output_groups=out || \
+      fail "bazel build should've passed"
+
+  # Test that compile actions are reconstructible when using param files
+  bazel build "${package}:a" \
+      --features=compiler_param_file \
+      --aspects="//${package}:lib.bzl%actions_test_aspect" \
+      --output_groups=out || \
+      fail "bazel build should've passed with --features=compiler_param_file"
+}
+
+function test_disable_cc_toolchain_detection() {
+ is_bazel || return 0
+  cat >> MODULE.bazel <<'EOF'
+cc_configure = use_extension("@rules_cc//cc:extensions.bzl", "cc_configure_extension")
+use_repo(cc_configure, "local_config_cc")
+EOF
+
+  cat > ok.cc <<EOF
+#include <stdio.h>
+int main() {
+  printf("Hello\n");
+}
+EOF
+
+  cat > BUILD <<EOF
+load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
+cc_binary(
+  name = "ok",
+  srcs = ["ok.cc"],
+)
+EOF
+  # As long as the default workspace suffix runs cc_configure the local_config_cc toolchain suite will be evaluated.
+  # Ensure the fake cc_toolchain_suite target doesn't have any errors.
+  BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1 BAZEL_NO_APPLE_CPP_TOOLCHAIN=1 bazel build '@local_config_cc//:toolchain' &>/dev/null || \
+    fail "Fake toolchain target causes analysis errors"
+
+  BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1 BAZEL_NO_APPLE_CPP_TOOLCHAIN=1 bazel build  '//:ok' --toolchain_resolution_debug=@bazel_tools//tools/cpp:toolchain_type &>"$TEST_log" && \
+    fail "Toolchains shouldn't be found"
+  expect_log "ToolchainResolution: No @@bazel_tools//tools/cpp:toolchain_type toolchain found for target platform @@platforms//host:host."
+}
+
+function setup_workspace_layout_with_external_directory() {
+  # Make the following layout to test builds in //external subpackages:
+  #
+  #├── baz
+  #│   ├── binary.cc
+  #│   └── BUILD
+  #└── external
+  #    └── foo
+  #        ├── BUILD
+  #        ├── lib.cc
+  #        └── lib.h
+  mkdir -p external/foo
+  cat > external/foo/BUILD <<EOF
+load("@rules_cc//cc:cc_library.bzl", "cc_library")
+cc_library(
+    name = "lib",
+    srcs = ["lib.cc"],
+    hdrs = ["lib.h"],
+    visibility = ["//baz:__subpackages__"],
+)
+EOF
+  cat > external/foo/lib.cc <<EOF
+#include "external/foo/lib.h"
+#include <iostream>
+
+using std::cout;
+using std::endl;
+using std::string;
+
+HelloLib::HelloLib(const string& greeting) : greeting_(new string(greeting)) {
+}
+
+void HelloLib::greet(const string& thing) {
+  cout << *greeting_ << " " << thing << endl;
+}
+EOF
+
+  cat > external/foo/lib.h <<EOF
+#include <string>
+#include <memory>
+
+class HelloLib {
+ public:
+  explicit HelloLib(const std::string &greeting);
+  void greet(const std::string &thing);
+
+ private:
+  std::unique_ptr<const std::string> greeting_;
+};
+EOF
+
+  mkdir baz
+  cat > baz/BUILD <<EOF
+load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
+cc_binary(
+    name = "binary",
+    srcs = ["binary.cc"],
+    deps = ["//external/foo:lib"],
+)
+EOF
+  cat > baz/binary.cc <<EOF
+#include "external/foo/lib.h"
+#include <string>
+
+int main(int argc, char** argv) {
+  HelloLib lib("Hello");
+  std::string thing = "world";
+  if (argc > 1) {
+    thing = argv[1];
+  }
+  lib.greet(thing);
+  return 0;
+}
+EOF
+
+}
+
+function test_execroot_subdir_layout_fails_for_external_subpackages() {
+  is_bazel || return 0
+  
+  setup_workspace_layout_with_external_directory
+
+  bazel build --experimental_sibling_repository_layout=false //baz:binary &> "$TEST_log" \
+    && fail "build should have failed with sources in the external directory" || true
+  expect_log "error:.*external/foo/lib.*"
+  expect_log "Target //baz:binary failed to build"
+}
+
+function test_execroot_sibling_layout_null_build_for_external_subpackages() {
+  
+  setup_workspace_layout_with_external_directory
+  bazel build --experimental_sibling_repository_layout //baz:binary \
+    || fail "expected build success"
+
+  # Null build.
+  bazel build --experimental_sibling_repository_layout //baz:binary &> "$TEST_log" \
+    || fail "expected build success"
+
+  is_bazel || return 0  # internal bazel logs differently
+  expect_log "INFO: 1 process: 1 internal"
+}
+
+function test_execroot_sibling_layout_header_scanning_in_external_subpackage() {
+  
+  setup_workspace_layout_with_external_directory
+  cat << 'EOF' > external/foo/BUILD
+load("@rules_cc//cc:cc_library.bzl", "cc_library")
+cc_library(
+    name = "lib",
+    srcs = ["lib.cc"],
+    # missing header declaration
+    visibility = ["//baz:__subpackages__"],
+)
+EOF
+
+  bazel build --experimental_sibling_repository_layout --spawn_strategy=standalone //external/foo:lib &> "$TEST_log" \
+    && fail "build should not have succeeded with missing header file"
+
+  expect_log "undeclared inclusion(s) in rule '//external/foo:lib'" \
+     "could not find 'undeclared inclusion' error message in bazel output"
+}
+
+
+# Test writing the exposed args of CPPCompileAction to parameters file
+# This is needed to avoid too long commands when the args of one of the target's
+# actions are used to run a new action from the aspect. Fixes b/168634763
+function test_using_compile_action_args_params_file() {
+  is_bazel || return 0
+
+  mkdir -p package
+
+  cat > "package/lib.bzl" <<EOF
+def _actions_test_impl(target, ctx):
+    compile_action = None
+
+    for action in target.actions:
+      if action.mnemonic == "CppCompile":
+        compile_action = action
+
+    args = compile_action.args[0]
+    aspect_out = ctx.actions.declare_file('aspect_out')
+
+    # Passing compile_action.outputs as input to the aspect action to ensure
+    # it gets the modified args value after executing the compile action.
+    ctx.actions.run_shell(inputs = compile_action.outputs,
+                          outputs = [aspect_out],
+                          command = "for v in \$@; do echo \$v; done > " + aspect_out.path,
+                          arguments = [args])
+    return [OutputGroupInfo(out=[aspect_out])]
+
+actions_test_aspect = aspect(implementation = _actions_test_impl)
+EOF
+
+  cat > "package/x.cc" <<EOF
+#include <stdio.h>
+int main() {
+  printf("Hello\n");
+}
+EOF
+
+  cat > "package/BUILD" <<EOF
+load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
+cc_binary(
+  name = "x",
+  srcs = ["x.cc"],
+)
+EOF
+
+  # The args should not be written to a file if the experimental flag is not set
+  bazel build "package:x" \
+      --aspects="//package:lib.bzl%actions_test_aspect" \
+      --output_groups=out
+
+  cat "bazel-bin/package/aspect_out" | grep ".params" \
+      && fail "CPPCompileAction Args should not have used a params file"
+
+  # Copy the args to be used for validating the params file contents
+  cp "bazel-bin/package/aspect_out" "package/expected_args"
+
+  # The args should be written to a file if the experimental flag is set
+  bazel build "package:x" \
+      --aspects="//package:lib.bzl%actions_test_aspect" \
+      --output_groups=out \
+      --experimental_use_cpp_compile_action_args_params_file
+
+  cat "bazel-bin/package/aspect_out" | grep ".params" \
+      || fail "CPPCompileAction Args should have used a params file"
+
+  # Validate the contents of the params file (with unquoting)
+  assert_equals "$(sed 's/\\//g' bazel-bin/package/aspect_out-0.params)" \
+      "$(cat package/expected_args)"
+}
+
+function test_include_external_genrule_header() {
+  is_bazel || return 0
+  REPO_PATH=$TEST_TMPDIR/repo
+  mkdir -p "$REPO_PATH"
+  touch "$REPO_PATH/REPO.bazel"
+  mkdir "$REPO_PATH/foo"
+  cat > "$REPO_PATH/foo/BUILD" <<'EOF'
+load("@rules_cc//cc:cc_library.bzl", "cc_library")
+cc_library(
+  name = "bar",
+  srcs = [
+    "bar.cc",
+    "inc.h",
+  ],
+)
+
+genrule(
+  name = "inc_h",
+  srcs = ["inc.txt"],
+  outs = ["inc.h"],
+  cmd = "cp $< $@",
+)
+EOF
+  cat > "$REPO_PATH/foo/bar.cc" <<'EOF'
+#include "foo/inc.h"
+
+int main() {
+  sayhello();
+}
+EOF
+  cat > "$REPO_PATH/foo/inc.txt" <<'EOF'
+#include <stdio.h>
+
+void sayhello() {
+  printf("hello\n");
+}
+EOF
+
+  cat >> MODULE.bazel <<EOF
+local_repository(name = 'repo', path='$REPO_PATH')
+EOF
+
+  bazel build @repo//foo:bar \
+    > "$TEST_log" || fail "expected build success"
+  bazel build --experimental_sibling_repository_layout @repo//foo:bar \
+    > "$TEST_log" || fail "expected build success"
+}
+
+function test_reconstructing_cpp_actions_using_shadowed_action() {
+  
+  local package="${FUNCNAME[0]}"
+  mkdir -p "${package}"
+
+  cat > "${package}/lib.bzl" <<EOF
+def _actions_test_impl(target, ctx):
+    compile_action = None
+    archive_action = None
+    link_action = None
+
+    for action in target.actions:
+      if action.mnemonic == "CppCompile":
+        compile_action = action
+      if action.mnemonic == "CppArchive":
+        archive_action = action
+
+    if not compile_action or not archive_action:
+      fail("Couldn't find compile or archive action.")
+
+    compile_action_outputs = compile_action.outputs.to_list()
+    compile_args = ctx.actions.declare_file("compile_args")
+    ctx.actions.run_shell(
+        outputs = [compile_args],
+        command = "echo \$@ > " + compile_args.path,
+        arguments = compile_action.args,
+    )
+
+    compile_out = ctx.actions.declare_file("compile_out.o")
+    ctx.actions.run_shell(
+        inputs = [compile_args],
+        shadowed_action = compile_action,
+        mnemonic = "RecreatedCppCompile",
+        outputs = [compile_out],
+        command = "\$(cat %s | sed 's|%s|%s|g' | sed 's|%s|%s|g')" % (
+            compile_args.path,
+            # We need to replace the original output path with something else
+            compile_action_outputs[0].path,
+            compile_out.path,
+            # We need to replace the original .d file output path with something
+            # else
+            compile_action_outputs[0].path.replace(".o", ".d"),
+            compile_out.path + ".d",
+        ),
+    )
+
+    archive_out = ctx.actions.declare_file("archive_out.a")
+    ctx.actions.run_shell(
+        shadowed_action = archive_action,
+        mnemonic = "RecreatedCppArchive",
+        outputs = [archive_out],
+        command = "\$@ && cp %s %s" % (
+            archive_action.outputs.to_list()[0].path,
+            archive_out.path,
+        ),
+        arguments = archive_action.args,
+    )
+
+    return [OutputGroupInfo(out = [
+        compile_args,
+        compile_out,
+        archive_out,
+    ])]
+
+actions_test_aspect = aspect(implementation = _actions_test_impl)
+EOF
+
+  echo "inline int x() { return 42; }" > "${package}/x.h"
+  cat > "${package}/a.cc" <<EOF
+#include "${package}/x.h"
+
+int a() { return x(); }
+EOF
+  cat > "${package}/BUILD" <<EOF
+load("@rules_cc//cc:cc_library.bzl", "cc_library")
+cc_library(
+  name = "x",
+  hdrs  = ["x.h"],
+)
+
+cc_library(
+  name = "a",
+  srcs = ["a.cc"],
+  deps = [":x"],
+)
+EOF
+
+  # Test that actions are reconstructible under default configuration
+  bazel build "${package}:a" \
+      --aspects="//${package}:lib.bzl%actions_test_aspect" \
+      --output_groups=out || \
+      fail "bazel build should've succeeded"
+
+  # Test that compile actions are reconstructible when using param files
+  bazel build "${package}:a" \
+      --features=compiler_param_file \
+      --aspects="//${package}:lib.bzl%actions_test_aspect" \
+      --output_groups=out || \
+      fail "bazel build should've succeeded with --features=compiler_param_file"
+}
+
+function test_include_scanning_smoketest() {
+  is_bazel || return 0
+  # Make sure there are no packages containing tools/cpp/INCLUDE_HINTS to exercise that case in
+  # IncludeHintsFunction.
+  rm -rf BUILD tools
+  
+  mkdir -p pkg
+  cat > pkg/BUILD <<EOF
+load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
+load("@rules_cc//cc:cc_library.bzl", "cc_library")
+cc_binary(
+  name = 'bin',
+  srcs = ['bin.cc'],
+  deps = [':spurious_dep'],
+)
+
+cc_library(
+  name = 'spurious_dep',
+  hdrs = ['dep.h'],
+)
+EOF
+
+  cat > pkg/bin.cc <<EOF
+#define NASTY "dep.h"
+#include NASTY
+int main() { return 0; }
+EOF
+
+  touch pkg/dep.h
+
+  bazel build --experimental_unsupported_and_brittle_include_scanning --features=cc_include_scanning //pkg:bin &>"$TEST_log" && fail 'include scanning did not (wrongly) remove dependency' || true
+  expect_log "fatal error: '\?dep.h'\?"
+}
+
+function test_env_inherit_cc_test() {
+  
+  mkdir pkg
+  cat > pkg/BUILD <<EOF
+load("@rules_cc//cc:cc_test.bzl", "cc_test")
+cc_test(
+  name = 'foo_test',
+  srcs = ['foo_test.cc'],
+  env_inherit = ['FOO'],
+)
+EOF
+
+  cat > pkg/foo_test.cc <<EOF
+#include <stdlib.h>
+
+int main() {
+  auto foo = getenv("FOO");
+  if (foo == nullptr) {
+    return 1;
+  }
+  return 0;
+}
+EOF
+
+  bazel test //pkg:foo_test &> "$TEST_log" && fail "Did not fail as expected. ENV leak?" || true
+  FOO=1 bazel test //pkg:foo_test &> "$TEST_log" || fail "Should have inherited FOO env."
+}
+
+function test_env_attr_cc_binary() {
+  
+  mkdir pkg
+  cat > pkg/BUILD <<EOF
+load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
+cc_binary(
+  name = 'foo_bin_with_env',
+  srcs = ['foo_test.cc'],
+  env = {'FOO': 'bar'},
+)
+
+cc_binary(
+  name = 'foo_bin',
+  srcs = ['foo_test.cc'],
+)
+EOF
+
+  cat > pkg/foo_test.cc <<EOF
+#include <stdlib.h>
+
+int main() {
+  auto foo = getenv("FOO");
+  if (foo == nullptr) {
+    return 1;
+  }
+  return 0;
+}
+EOF
+
+  bazel run //pkg:foo_bin &> "$TEST_log" && fail "Did not fail as expected. ENV leak?" || true
+  bazel run //pkg:foo_bin_with_env &> "$TEST_log" || fail "Should have used env attr."
+}
+
+function external_cc_test_setup() {
+ is_bazel || return 0
+
+  cat >> MODULE.bazel <<'EOF'
+local_repo = use_repo_rule("@bazel_tools//tools/build_defs/repo:local.bzl", "local_repository")
+local_repo(
+  name = "other_repo",
+  path = "other_repo",
+)
+EOF
+
+  mkdir -p other_repo
+  touch other_repo/REPO.bazel
+
+  mkdir -p other_repo/lib
+  cat > other_repo/lib/BUILD <<'EOF'
+load("@rules_cc//cc:cc_library.bzl", "cc_library")
+cc_library(
+  name = "lib",
+  srcs = ["lib.cpp"],
+  hdrs = ["lib.h"],
+  visibility = ["//visibility:public"],
+)
+EOF
+  cat > other_repo/lib/lib.h <<'EOF'
+void print_greeting();
+EOF
+  cat > other_repo/lib/lib.cpp <<'EOF'
+#include <cstdio>
+void print_greeting() {
+  printf("Hello, world!\n");
+}
+EOF
+
+  mkdir -p other_repo/test
+  cat > other_repo/test/BUILD <<'EOF'
+load("@rules_cc//cc:cc_test.bzl", "cc_test")
+cc_test(
+  name = "test",
+  srcs = ["test.cpp"],
+  deps = ["//lib"],
+)
+EOF
+  cat > other_repo/test/test.cpp <<'EOF'
+#include "lib/lib.h"
+int main() {
+  print_greeting();
+}
+EOF
+}
+
+function test_external_cc_test_sandboxed() {
+  if is_windows; then
+    return 0
+  fi
+  is_bazel || return 0
+
+  external_cc_test_setup
+
+  bazel test \
+      --test_output=errors \
+      --strategy=sandboxed \
+      @other_repo//test >& $TEST_log || fail "test failed"
+}
+
+function test_external_cc_test_sandboxed_sibling_repository_layout() {
+  if is_windows; then
+    return 0
+  fi
+  is_bazel || return 0
+
+  external_cc_test_setup
+
+  bazel test \
+      --test_output=errors \
+      --strategy=sandboxed \
+      --experimental_sibling_repository_layout \
+      @other_repo//test >& $TEST_log || fail "Test should pass"
+}
+
+function test_external_cc_test_local() {
+  is_bazel || return 0
+
+  external_cc_test_setup
+
+  bazel test \
+      --test_output=errors \
+      --strategy=local \
+      @other_repo//test >& $TEST_log || fail "Test should pass"
+}
+
+function test_external_cc_test_local_sibling_repository_layout() {
+ is_bazel || return 0
+
+  external_cc_test_setup
+
+  bazel test \
+      --test_output=errors \
+      --strategy=local \
+      --experimental_sibling_repository_layout \
+      @other_repo//test >& $TEST_log || fail "Test should pass"
+
+  # Test cc compile action can hit the action cache. See
+  # https://github.com/bazelbuild/bazel/issues/17819
+  bazel shutdown
+
+  bazel test \
+      --test_output=errors \
+      --strategy=local \
+      --experimental_sibling_repository_layout \
+      @other_repo//test >& $TEST_log || fail "Test should pass"
+  expect_log "1 process: .*1 internal"
+}
+
+function test_bazel_current_repository_define() {
+  is_bazel || return 0
+  
+  cat >> MODULE.bazel <<'EOF'
+local_repository = use_repo_rule("@bazel_tools//tools/build_defs/repo:local.bzl", "local_repository")
+local_repository(
+  name = "other_repo",
+  path = "other_repo",
+)
+EOF
+
+  mkdir -p pkg
+  cat > pkg/BUILD <<'EOF'
+load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
+load("@rules_cc//cc:cc_library.bzl", "cc_library")
+load("@rules_cc//cc:cc_test.bzl", "cc_test")
+cc_library(
+  name = "library",
+  srcs = ["library.cpp"],
+  hdrs = ["library.h"],
+  implementation_deps = ["@bazel_tools//tools/cpp/runfiles"],
+  visibility = ["//visibility:public"],
+)
+
+cc_binary(
+  name = "binary",
+  srcs = ["binary.cpp"],
+  deps = [
+    ":library",
+    "@bazel_tools//tools/cpp/runfiles",
+  ],
+)
+
+cc_test(
+  name = "test",
+  srcs = ["test.cpp"],
+  deps = [
+    ":library",
+    "@bazel_tools//tools/cpp/runfiles",
+  ],
+)
+EOF
+
+  cat > pkg/library.cpp <<'EOF'
+#include "library.h"
+#include <iostream>
+void print_repo_name() {
+  std::cout << "in " << __FILE__ << ": '" << BAZEL_CURRENT_REPOSITORY << "'" << std::endl;
+}
+EOF
+
+  cat > pkg/library.h <<'EOF'
+void print_repo_name();
+EOF
+
+  cat > pkg/binary.cpp <<'EOF'
+#include <iostream>
+#include "library.h"
+int main() {
+  std::cout << "in " << __FILE__ << ": '" << BAZEL_CURRENT_REPOSITORY << "'" << std::endl;
+  print_repo_name();
+}
+EOF
+
+  cat > pkg/test.cpp <<'EOF'
+#include <iostream>
+#include "library.h"
+int main() {
+  std::cout << "in " << __FILE__ << ": '" << BAZEL_CURRENT_REPOSITORY << "'" << std::endl;
+  print_repo_name();
+}
+EOF
+
+  mkdir -p other_repo
+  touch other_repo/REPO.bazel
+
+  mkdir -p other_repo/pkg
+  cat > other_repo/pkg/BUILD <<'EOF'
+load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
+load("@rules_cc//cc:cc_test.bzl", "cc_test")
+cc_binary(
+  name = "binary",
+  srcs = ["binary.cpp"],
+  deps = [
+    "@//pkg:library",
+    "@bazel_tools//tools/cpp/runfiles",
+  ],
+)
+
+cc_test(
+  name = "test",
+  srcs = ["test.cpp"],
+  deps = [
+    "@//pkg:library",
+    "@bazel_tools//tools/cpp/runfiles",
+  ],
+)
+EOF
+
+  cat > other_repo/pkg/binary.cpp <<'EOF'
+#include <iostream>
+#include "pkg/library.h"
+int main() {
+  std::cout << "in " << __FILE__ << ": '" << BAZEL_CURRENT_REPOSITORY << "'" << std::endl;
+  print_repo_name();
+}
+EOF
+
+  cat > other_repo/pkg/test.cpp <<'EOF'
+#include <iostream>
+#include "pkg/library.h"
+int main() {
+  std::cout << "in " << __FILE__ << ": '" << BAZEL_CURRENT_REPOSITORY << "'" << std::endl;
+  print_repo_name();
+}
+EOF
+
+  bazel run //pkg:binary &>"$TEST_log" || fail "Run should succeed"
+  expect_log "in pkg/binary.cpp: ''"
+  expect_log "in pkg/library.cpp: ''"
+
+  bazel test --test_output=streamed //pkg:test &>"$TEST_log" || fail "Test should succeed"
+  expect_log "in pkg/test.cpp: ''"
+  expect_log "in pkg/library.cpp: ''"
+
+  bazel run @other_repo//pkg:binary &>"$TEST_log" || fail "Run should succeed"
+  expect_log "in external/+local_repository+other_repo/pkg/binary.cpp: '+local_repository+other_repo'"
+  expect_log "in pkg/library.cpp: ''"
+
+  bazel test --test_output=streamed \
+    @other_repo//pkg:test &>"$TEST_log" || fail "Test should succeed"
+  expect_log "in external/+local_repository+other_repo/pkg/test.cpp: '+local_repository+other_repo'"
+  expect_log "in pkg/library.cpp: ''"
+}
+
+function test_compiler_flag_gcc() {
+  # The default macOS toolchain always uses XCode's clang.
+  if is_darwin; then
+    return 0
+  fi
+
+  type -P gcc || return 0
+
+  
+  cat > BUILD <<'EOF'
+load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
+config_setting(
+    name = "gcc_compiler",
+    flag_values = {"@rules_cc//cc/private/toolchain:compiler": "gcc"},
+)
+
+cc_binary(
+  name = "main",
+  srcs = select({":gcc_compiler": ["main.cc"]}),
+)
+EOF
+  cat > main.cc <<'EOF'
+int main() {}
+EOF
+
+  export CC=gcc
+  bazel build //:main --repo_env=CC=gcc || fail "Expected compiler flag to have value 'gcc'"
+}
+
+function test_compiler_flag_clang() {
+  type -P clang || return 0
+
+  
+  cat > BUILD <<'EOF'
+load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
+config_setting(
+    name = "clang_compiler",
+    flag_values = {"@rules_cc//cc/private/toolchain:compiler": "clang"},
+)
+
+cc_binary(
+  name = "main",
+  srcs = select({":clang_compiler": ["main.cc"]}),
+)
+EOF
+  cat > main.cc <<'EOF'
+int main() {}
+EOF
+
+  export CC=clang
+  bazel build //:main --repo_env=CC=clang || fail "Expected compiler flag to have value 'clang'"
+}
+
+function test_bazel_cxxopts() {
+ is_bazel || return 0
+ is_darwin && return 0
+
+  cat > BUILD <<'EOF'
+load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
+cc_binary(
+  name = "main_c",
+  srcs = ["main.c"],
+)
+cc_binary(
+  name = "main_cpp",
+  srcs = ["main.cpp"],
+)
+EOF
+  cat > main.c <<'EOF'
+#include <stdlib.h>
+int main() {
+  exit(EXIT_CODE);
+}
+EOF
+  cat > main.cpp <<'EOF'
+#include <stdlib.h>
+int main() {
+  exit(EXIT_CODE);
+}
+EOF
+
+  export BAZEL_USE_CPP_ONLY_TOOLCHAIN=1
+  export BAZEL_CXXOPTS=-DEXIT_CODE=0
+  bazel build //:main_c \
+    --repo_env=BAZEL_USE_CPP_ONLY_TOOLCHAIN=1 \
+    --repo_env=BAZEL_CXXOPTS=-DEXIT_CODE=0 && fail "Expected C compilation to fail"
+  bazel run //:main_cpp \
+    --repo_env=BAZEL_USE_CPP_ONLY_TOOLCHAIN=1 \
+    --repo_env=BAZEL_CXXOPTS=-DEXIT_CODE=0 || fail "Expected C++ compilation to pass"
+}
+
+function test_bazel_conlyopts() {
+ is_bazel || return 0
+ is_darwin && return 0
+  
+  cat > BUILD <<'EOF'
+load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
+cc_binary(
+  name = "main_c",
+  srcs = ["main.c"],
+)
+cc_binary(
+  name = "main_cpp",
+  srcs = ["main.cpp"],
+)
+EOF
+  cat > main.c <<'EOF'
+#include <stdlib.h>
+int main() {
+  exit(EXIT_CODE);
+}
+EOF
+  cat > main.cpp <<'EOF'
+#include <stdlib.h>
+int main() {
+  exit(EXIT_CODE);
+}
+EOF
+
+  export BAZEL_USE_CPP_ONLY_TOOLCHAIN=1
+  export BAZEL_CONLYOPTS=-DEXIT_CODE=0
+  bazel build //:main_cpp \
+    --repo_env=BAZEL_USE_CPP_ONLY_TOOLCHAIN=1 \
+    --repo_env=BAZEL_CONLYOPTS=-DEXIT_CODE=0 && fail "Expected C++ compilation to fail"
+  bazel run //:main_c \
+    --repo_env=BAZEL_USE_CPP_ONLY_TOOLCHAIN=1 \
+    --repo_env=BAZEL_CONLYOPTS=-DEXIT_CODE=0 || fail "Expected C compilation to pass"
+}
+
+function test_cc_test_no_target_coverage_dep() {
+  # Regression test for https://github.com/bazelbuild/bazel/issues/16961
+  is_bazel || return 0
+
+  cat >> MODULE.bazel <<'EOF'
+remote_coverage_tools_extension = use_extension("@bazel_tools//tools/test:extensions.bzl", "remote_coverage_tools_extension")
+use_repo(remote_coverage_tools_extension, "remote_coverage_tools")
+EOF
+
+  local package="${FUNCNAME[0]}"
+  mkdir -p "${package}"
+
+  cat > "${package}"/BUILD <<'EOF'
+load("@rules_cc//cc:cc_test.bzl", "cc_test")
+cc_test(
+  name = "test",
+  srcs = ["test.cc"],
+)
+EOF
+  touch "${package}"/test.cc
+
+  out=$(bazel cquery --collect_code_coverage \
+   "deps(//${package}:test) intersect config(@remote_coverage_tools//:all, target)")
+  if [[ -n "$out" ]]; then
+    fail "Expected no dependency on lcov_merger in the target configuration, but got: $out"
+  fi
+}
+
+function test_cc_test_no_coverage_tools_dep_without_coverage() {
+  is_bazel || return 0
+
+  cat >> MODULE.bazel <<'EOF'
+remote_coverage_tools_extension = use_extension("@bazel_tools//tools/test:extensions.bzl", "remote_coverage_tools_extension")
+use_repo(remote_coverage_tools_extension, "remote_coverage_tools")
+EOF
+
+  # Regression test for https://github.com/bazelbuild/bazel/issues/16961 and
+  # https://github.com/bazelbuild/bazel/issues/15088.
+  local package="${FUNCNAME[0]}"
+  mkdir -p "${package}"
+
+  cat > "${package}"/BUILD <<'EOF'
+load("@rules_cc//cc:cc_test.bzl", "cc_test")
+cc_test(
+  name = "test",
+  srcs = ["test.cc"],
+)
+EOF
+  touch "${package}"/test.cc
+
+  out=$(bazel cquery "somepath(//${package}:test,@remote_coverage_tools//:all)")
+  if [[ -n "$out" ]]; then
+    fail "Expected no dependency on remote coverage tools, but got: $out"
+  fi
+}
+
+# sanitizer features are opt-in so we check if the sanitizer library is
+# installed and skip the test if it isn't (e.g. centos-7-openjdk-11-gcc-10)
+function __is_installed() {
+  local lib="$1"
+
+  if is_linux; then
+    return $(ldconfig -p | grep -q "$lib")
+  fi
+
+  # assume installed for darwin
+}
+
+function test_cc_toolchain_asan_feature() {
+  is_bazel || return 0
+
+  local feature=asan
+  __is_installed "lib$feature" || return 0
+
+  
+  mkdir pkg
+  cat > pkg/BUILD <<EOF
+load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
+cc_binary(
+  name = 'example',
+  srcs = ['example.cc'],
+  features = ['$feature'],
+)
+EOF
+
+  # some versions of clang will optimize away the pointer assignment and
+  # dereference without volatile
+  # https://godbolt.org/z/of8cr3P8q
+  cat > pkg/example.cc <<EOF
+int main() {
+  volatile int* p;
+
+  {
+    volatile int x = 0;
+    p = &x;
+  }
+
+  return *p;
+}
+EOF
+
+  bazel run //pkg:example &> "$TEST_log" && fail "Should have failed due to $feature" || true
+  expect_log "AddressSanitizer"
+}
+
+function test_cc_toolchain_tsan_feature() {
+  is_bazel || return 0
+
+  local feature=tsan
+  __is_installed "lib$feature" || return 0
+
+  
+  mkdir pkg
+  cat > pkg/BUILD <<EOF
+load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
+cc_binary(
+  name = 'example',
+  srcs = ['example.cc'],
+  features = ['$feature'],
+)
+EOF
+
+  cat > pkg/example.cc <<EOF
+#include <thread>
+
+int value = 0;
+
+void increment() {
+  ++value;
+}
+
+int main() {
+  std::thread t1(increment);
+  std::thread t2(increment);
+  t1.join();
+  t2.join();
+
+  return value;
+}
+EOF
+
+  bazel run //pkg:example &> "$TEST_log" && fail "Should have failed due to $feature" || true
+  # TODO: we used to expect "WARNING: ThreadSanitizer: data race" here, but that
+  # has suddenly started failing on Ubuntu on Bazel CI (see
+  # https://buildkite.com/bazel/google-bazel-presubmit/builds/92979). We should
+  # figure out what's going on and fix this check eventually.
+
+  expect_log "ThreadSanitizer"
+}
+
+function test_cc_toolchain_ubsan_feature() {
+  is_bazel || return 0
+
+  local feature=ubsan
+  __is_installed "lib$feature" || return 0
+
+  
+  mkdir -p pkg
+  cat > pkg/BUILD <<EOF
+load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
+cc_binary(
+  name = 'example',
+  srcs = ['example.cc'],
+  copts = ['-Wno-array-bounds'],
+  features = ['$feature'],
+)
+EOF
+
+  cat > pkg/example.cc <<EOF
+int main() {
+  int array[10];
+  return array[10];
+}
+EOF
+
+  bazel run //pkg:example &> "$TEST_log" && fail "Should have failed due to $feature" || true
+  expect_log "runtime error: index 10 out of bounds"
+}
+
+function setup_find_optional_cpp_toolchain() {
+
+  mkdir -p pkg
+
+  cat > pkg/BUILD <<'EOF'
+load(":rules.bzl", "my_rule")
+
+my_rule(
+  name = "my_rule",
+)
+
+constraint_setting(name = "cpu")
+
+constraint_value(
+    name = "exotic_cpu",
+    constraint_setting = ":cpu",
+)
+
+platform(
+  name = "exotic_platform",
+  constraint_values = [
+    ":exotic_cpu",
+  ],
+)
+EOF
+
+  cat > pkg/rules.bzl <<'EOF'
+load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cc_toolchain", "use_cc_toolchain")
+
+def _my_rule_impl(ctx):
+    out = ctx.actions.declare_file(ctx.attr.name)
+    toolchain = find_cc_toolchain(ctx, mandatory = False)
+    if toolchain:
+        ctx.actions.write(out, "Toolchain found")
+    else:
+        ctx.actions.write(out, "Toolchain not found")
+    return [DefaultInfo(files = depset([out]))]
+
+my_rule = rule(
+    implementation = _my_rule_impl,
+    attrs = {
+        "_cc_toolchain": attr.label(
+            default = "@bazel_tools//tools/cpp:optional_current_cc_toolchain",
+        ),
+    },
+    toolchains = use_cc_toolchain(mandatory = False),
+)
+EOF
+}
+
+function test_find_optional_cpp_toolchain_present() {
+  is_bazel || return 0
+  setup_find_optional_cpp_toolchain
+
+  bazel build //pkg:my_rule &> "$TEST_log" || fail "Build failed"
+  assert_contains "Toolchain found" bazel-bin/pkg/my_rule
+}
+
+function test_find_optional_cpp_toolchain_not_present() {
+  is_bazel || return 0
+  setup_find_optional_cpp_toolchain
+
+  bazel build //pkg:my_rule --platforms=//pkg:exotic_platform \
+    &> "$TEST_log" || fail "Build failed"
+   assert_contains "Toolchain not found" bazel-bin/pkg/my_rule
+}
+
+function test_no_cpp_stdlib_linked_to_c_library() {
+  
+  mkdir pkg
+  cat > pkg/BUILD <<'EOF'
+load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
+cc_binary(
+  name = 'example',
+  srcs = ['example.c'],
+)
+EOF
+  cat > pkg/example.c <<'EOF'
+int main() {}
+EOF
+
+  bazel build //pkg:example &> "$TEST_log" || fail "Build failed"
+  if is_darwin; then
+    otool -L bazel-bin/pkg/example &> "$TEST_log" || fail "otool failed"
+    expect_log 'libc'
+    expect_not_log 'libc\+\+'
+  else
+    ldd bazel-bin/pkg/example &> "$TEST_log" || fail "ldd failed"
+    expect_log 'libc'
+    expect_not_log 'libstdc\+\+'
+  fi
+}
+
+function test_parse_headers_unclean() {
+  
+  mkdir -p pkg
+  cat > pkg/BUILD <<'EOF'
+load("@rules_cc//cc:cc_library.bzl", "cc_library")
+cc_library(name = "lib", hdrs = ["lib.h"])
+EOF
+  cat > pkg/lib.h <<'EOF'
+// Missing include of cstdint, which defines uint8_t.
+uint8_t foo();
+EOF
+
+  bazel build -s --process_headers_in_dependencies --features parse_headers \
+    //pkg:lib &> "$TEST_log" && fail "Build should have failed due to unclean headers"
+  expect_log "Compiling pkg/lib.h"
+  expect_log "error:.*uint8_t"
+
+  bazel build -s --process_headers_in_dependencies \
+    //pkg:lib &> "$TEST_log" || fail "Build should have passed"
+}
+
+function test_parse_headers_clean() {
+  
+  mkdir pkg
+  cat > pkg/BUILD <<'EOF'
+load("@rules_cc//cc:cc_library.bzl", "cc_library")
+package(features = ["parse_headers"])
+cc_library(name = "lib", hdrs = ["lib.h"])
+EOF
+  cat > pkg/lib.h <<'EOF'
+#include <cstdint>
+uint8_t foo();
+EOF
+
+  bazel build -s --process_headers_in_dependencies \
+    //pkg:lib &> "$TEST_log" || fail "Build should have passed"
+  expect_log "Compiling pkg/lib.h"
+}
+
+function test_tree_artifact_sources_in_no_deps_library() {
+  is_bazel || return 0
+
+  mkdir -p pkg
+  cat > pkg/BUILD <<'EOF'
+load("@rules_shell//shell:sh_binary.bzl", "sh_binary")
+load("@rules_cc//cc:cc_library.bzl", "cc_library")
+load("@rules_cc//cc:cc_test.bzl", "cc_test")
+
+load("generate.bzl", "generate_source")
+sh_binary(
+    name = "generate_tool",
+    srcs = ["generate.sh"],
+)
+
+generate_source(
+    name = "generated_source",
+    tool = ":generate_tool",
+    output_dir = "generated",
+)
+
+cc_library(
+    name = "hello_world",
+    srcs = [":generated_source"],
+    hdrs = [":generated_source"],
+)
+
+cc_test(
+    name = "testCodegen",
+    srcs = ["testCodegen.cpp"],
+    deps = [":hello_world"],
+)
+EOF
+  cat > pkg/generate.bzl <<'EOF'
+def _generate_source_impl(ctx):
+    output_dir = ctx.attr.output_dir
+    files = ctx.actions.declare_directory(output_dir)
+
+    ctx.actions.run(
+        inputs = [],
+        outputs = [files],
+        arguments = [files.path],
+        executable = ctx.executable.tool
+    )
+
+    return [
+        DefaultInfo(files = depset([files]))
+    ]
+
+
+generate_source = rule(
+    implementation = _generate_source_impl,
+    attrs = {
+        "output_dir": attr.string(),
+        "tool": attr.label(executable = True, cfg = "exec")
+    }
+)
+EOF
+  cat > pkg/generate.sh <<'EOF2'
+#!/usr/bin/env bash
+
+OUTPUT_DIR=$1
+
+cat << EOF > $OUTPUT_DIR/test.hpp
+#pragma once
+void hello_world();
+EOF
+
+
+cat << EOF > $OUTPUT_DIR/test.cpp
+#include "test.hpp"
+#include <cstdio>
+void hello_world()
+{
+    puts("Hello World!");
+}
+EOF
+EOF2
+  chmod +x pkg/generate.sh
+  cat > pkg/testCodegen.cpp <<'EOF'
+#include "pkg/generated/test.hpp"
+
+int main() {
+    hello_world();
+    return 0;
+}
+EOF
+
+  bazel build //pkg:testCodegen &> "$TEST_log" || fail "Build failed"
+}
+
+function test_extend_cc_binary_with_dynamic_deps() {
+  is_bazel || return 0
+
+  mkdir -p pkg
+  cat >pkg/BUILD <<'EOF'
+load("my_cc_binary.bzl", "my_cc_binary")
+
+constraint_setting(name = "foo")
+constraint_value(name = "never_selected", constraint_setting = ":foo")
+
+my_cc_binary(
+    name = "hello",
+    srcs = ["main.cpp"],
+    # Ensure that the select has no effect but can't be simplified.
+    dynamic_deps = select({":never_selected": ["unused"], "//conditions:default": []}),
+)
+EOF
+
+  cat >pkg/my_cc_binary.bzl << 'EOF'
+load("@rules_cc//cc/private/rules_impl:cc_binary.bzl", "cc_binary")
+def _my_cc_binary_impl(ctx):
+  print("Hello from my_cc_binary")
+  return ctx.super()
+
+my_cc_binary = rule(
+  implementation = _my_cc_binary_impl,
+  parent = cc_binary,
+)
+EOF
+
+  cat >pkg/main.cpp <<'EOF'
+#include <iostream>
+
+int main() {
+  std::cout << "Hello from main.cpp" << std::endl;
+  return 0;
+}
+EOF
+
+  bazel run //pkg:hello &> $TEST_log || fail "Expected success"
+  expect_log "Hello from my_cc_binary"
+  expect_log "Hello from main.cpp"
+}
+
+function test_cpp20_modules_with_clang() {
+  type -P clang || return 0
+  # Check if clang version is less than 17
+  clang_version=$(clang --version | head -n1 | grep -oE '[0-9]+\.[0-9]+' | head -n1)
+  if [[ -n "$clang_version" ]]; then
+    major_version=$(echo "$clang_version" | cut -d. -f1)
+    if [[ "$major_version" -lt 17 ]]; then
+      return 0
+    fi
+  fi
+  if [[ "$(uname -s)" == "Darwin" ]]; then
+    return 0
+  fi
+
+  
+
+  cat > BUILD <<'EOF'
+load("@rules_cc//cc:defs.bzl", "cc_library", "cc_binary")
+
+package(features = ["cpp_modules"])
+
+cc_library(
+  name = "base",
+  module_interfaces = ["base.cppm"],
+)
+cc_library(
+  name = "foo",
+  module_interfaces = ["foo.cppm"],
+  deps = [":base"]
+)
+cc_library(
+  name = "bar",
+  module_interfaces = ["bar.cppm"],
+  deps = [":base"]
+)
+cc_binary(
+  name = "main",
+  srcs = ["main.cc"],
+  deps = [":foo", ":bar"]
+)
+EOF
+  cat > main.cc <<'EOF'
+import foo;
+import bar;
+
+void f() {
+  f_foo();
+  f_bar();
+}
+
+int main() {
+  f();
+  return 0;
+}
+EOF
+  cat > foo.cppm <<'EOF'
+export module foo;
+import base;
+
+export void f_foo() {
+  f_base();
+}
+EOF
+  cat > bar.cppm <<'EOF'
+export module bar;
+import base;
+
+export void f_bar() {
+  f_base();
+}
+EOF
+  cat > base.cppm <<'EOF'
+export module base;
+
+export void f_base() {
+}
+EOF
+
+  export CC=clang
+  bazel build //:main --experimental_cpp_modules --repo_env=CC=clang --copt=-std=c++20 --disk_cache=disk &> $TEST_log || fail "Expected build C++20 Modules success with compiler 'clang'"
+
+  # Verify that the build can hit the cache without action cycles.
+  bazel clean || fail "Expected clean success"
+  bazel build //:main --experimental_cpp_modules --repo_env=CC=clang --copt=-std=c++20 --disk_cache=disk &> $TEST_log || fail "Expected build C++20 Modules success with compiler 'clang'"
+  expect_log "17 disk cache hit"
+}
+
+function test_external_repo_lto() {
+  is_bazel || return 0
+  
+  REPO_PATH=$TEST_TMPDIR/repo
+  mkdir -p "$REPO_PATH"
+  touch "$REPO_PATH/REPO.bazel"
+  mkdir "$REPO_PATH/foo"
+  cat > "$REPO_PATH/foo/BUILD" <<'EOF'
+load("@rules_cc//cc:cc_library.bzl", "cc_library")
+cc_library(
+  name = "foo",
+  srcs = [
+    "foo.cc",
+  ],
+  hdrs = [
+    "foo.h",
+  ],
+)
+EOF
+  cat > "$REPO_PATH/foo/foo.cc" <<'EOF'
+#include "foo.h"
+
+int main() {
+  sayhello();
+}
+EOF
+  cat > "$REPO_PATH/foo/foo.h" <<'EOF'
+#include <stdio.h>
+
+void sayhello() {
+  printf("hello\n");
+}
+EOF
+
+  cat >> MODULE.bazel <<EOF
+local_repository = use_repo_rule("@bazel_tools//tools/build_defs/repo:local.bzl", "local_repository")
+local_repository(name = 'repo', path='$REPO_PATH')
+EOF
+  cat > BUILD <<'EOF'
+load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
+cc_binary(
+  name = "main",
+  srcs = ["main.cc"],
+  deps = ["@repo//foo:foo"],
+)
+EOF
+  cat > main.cc <<'EOF'
+#include "foo/foo.h"
+int main() {
+  sayhello();
+}
+EOF
+  export CC=clang
+  bazel build --repo_env=CC=clang --features=thin_lto @repo//foo \
+    > "$TEST_log" || fail "expected build success"
+  bazel build --repo_env=CC=clang --features=thin_lto --experimental_sibling_repository_layout @repo//foo \
+    > "$TEST_log" || fail "expected build success"
+}
+
+run_suite "cc_integration_test"
diff --git a/tests/integration/test_launcher.sh b/tests/integration/test_launcher.sh
index 563cc6f..94072d7 100755
--- a/tests/integration/test_launcher.sh
+++ b/tests/integration/test_launcher.sh
@@ -55,6 +55,28 @@
 mkdir -p "$scratch_workspace"
 cd "$scratch_workspace"
 
+
+
+# Extract the module version used in the default lock file.
+function get_version_from_default_lock_file() {
+  lockfile=$(rlocation rules_cc/MODULE.bazel.lock)
+  module=$1
+  local version=$(sed -n "s|.*modules/$module/\([^/]*\)/source\.json.*|\1|p" "$lockfile")
+  if [[ -z $version ]]; then
+      echo "Version not found for module $module in $lockfile"
+      exit 1
+  else
+      echo "$version"
+  fi
+}
+
+function add_bazel_dep() {
+  version=$(get_version_from_default_lock_file "$1")
+  cat >> "$2" <<EOF
+bazel_dep(name = "$1", version = "$version")
+EOF
+}
+
 # Make rules_cc available
 cat > MODULE.bazel << EOF
 module(name = "test_module")
@@ -65,6 +87,9 @@
     path = "$rules_cc_dir"
 )
 EOF
+add_bazel_dep "rules_shell" MODULE.bazel
+
+
 
 test_runner_path="$(rlocation "$TEST_RUNNER")" || (echo >&2 "FAILED TO LOAD TEST RUNNER" && exit 1)
 test_runner_cmd=( "${test_runner_path}" )