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}" )