| #!/bin/bash -eu |
| # |
| # 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. |
| # |
| # Tests the behavior of C++ rules. |
| |
| # Load the test setup defined in the parent directory |
| CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| source "${CURRENT_DIR}/../integration_test_setup.sh" \ |
| || { echo "integration_test_setup.sh not found!" >&2; exit 1; } |
| |
| function test_extra_action_for_compile() { |
| mkdir -p ea |
| cat > ea/BUILD <<EOF |
| 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() { |
| r="$TEST_TMPDIR/r" |
| mkdir -p "$TEST_TMPDIR/r/foo/v1" |
| create_workspace_with_default_repos "$TEST_TMPDIR/r/WORKSPACE" |
| echo "#define FOO 42" > "$TEST_TMPDIR/r/foo/v1/foo.h" |
| cat > "$TEST_TMPDIR/r/foo/BUILD" <<EOF |
| cc_library( |
| name = "foo", |
| hdrs = ["v1/foo.h"], |
| include_prefix = "foolib", |
| strip_include_prefix = "v1", |
| visibility = ["//visibility:public"], |
| ) |
| EOF |
| cat >> $(create_workspace_with_default_repos WORKSPACE) <<EOF |
| local_repository( |
| name = "foo", |
| path = "$TEST_TMPDIR/r", |
| ) |
| EOF |
| |
| cat > BUILD <<EOF |
| 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_include_validation_sandbox_disabled() { |
| local workspace="${FUNCNAME[0]}" |
| mkdir -p "${workspace}"/lib |
| |
| create_workspace_with_default_repos "${workspace}/WORKSPACE" |
| cat >> "${workspace}/BUILD" << EOF |
| cc_library( |
| name = "foo", |
| srcs = ["lib/foo.cc"], |
| hdrs = ["lib/foo.h"], |
| strip_include_prefix = "lib", |
| ) |
| EOF |
| cat >> "${workspace}/lib/foo.cc" << EOF |
| #include "foo.h" |
| EOF |
| |
| touch "${workspace}/lib/foo.h" |
| |
| cd "${workspace}" |
| bazel build --spawn_strategy=standalone //:foo &>"$TEST_log" \ |
| || fail "Build failed but should have succeeded" |
| } |
| |
| function test_tree_artifact_headers_are_invalidated() { |
| mkdir -p "ta_headers" |
| cat > "ta_headers/BUILD" <<EOF |
| 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' |
| #!/bin/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="host", |
| 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." |
| } |
| |
| # This test tests that Bazel can produce dynamic libraries that have undefined |
| # symbols on Mac and Linux. Not sure it is a sane default to allow undefined |
| # symbols, but it's the default we had historically. This test creates |
| # an executable (main) that defines bar(), and a shared library (plugin) that |
| # calls bar(). When linking the libplugin.so, symbol 'bar' is undefined. |
| # +-----------------------------+ +----------------------------------+ |
| # | main | | libplugin.so | |
| # | | | | |
| # | main() { return foo(); } +---------> foo() { return bar() - 42; } | |
| # | | | + | |
| # | | | | | |
| # | bar() { return 42; } <------------------+ | |
| # | | | | |
| # +-----------------------------+ +----------------------------------+ |
| function test_undefined_dynamic_lookup() { |
| if is_windows; then |
| # Windows doesn't allow undefined symbols in shared libraries. |
| return 0 |
| fi |
| mkdir -p "dynamic_lookup" |
| cat > "dynamic_lookup/BUILD" <<EOF |
| cc_binary( |
| name = "libplugin.so", |
| srcs = ["plugin.cc"], |
| linkshared = 1, |
| ) |
| |
| cc_binary( |
| name = "main", |
| srcs = ["main.cc", "libplugin.so"], |
| ) |
| EOF |
| |
| cat > "dynamic_lookup/plugin.cc" <<EOF |
| int bar(); |
| int foo() { return bar() - 42; } |
| EOF |
| |
| cat > "dynamic_lookup/main.cc" <<EOF |
| int foo(); |
| int bar() { return 42; } |
| int main() { return foo(); } |
| EOF |
| |
| bazel build //dynamic_lookup:main || fail "Bazel couldn't build the binary." |
| bazel run //dynamic_lookup:main || fail "Run of the binary failed." |
| } |
| |
| function test_save_feature_state() { |
| mkdir -p ea |
| cat > ea/BUILD <<EOF |
| 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" |
| |
| create_workspace_with_default_repos "$pkg"/WORKSPACE |
| |
| 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"/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 { |
| local: |
| *; |
| }; |
| EOF |
| |
| cp "$CURRENT_DIR"/cc_api_rules.bzl "$pkg"/cc_api_rules.bzl |
| |
| } |
| |
| function test_cc_starlark_api_default_values() { |
| 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() { |
| [ "$PLATFORM" != "darwin" ] || return 0 |
| |
| 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() { |
| # This uses --version-script which isn't available on Mac linker. |
| [ "$PLATFORM" != "darwin" ] || return 0 |
| |
| 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 = 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 bazel-bin/"$pkg"/g | grep VERS_42.0 || fail "VERS_42.0 not in binary" |
| |
| bazel-bin/"$pkg"/g | grep a1a2bcddcb2a1a || fail "output is incorrect" |
| } |
| |
| function test_incompatible_validate_top_level_header_inclusions() { |
| local workspace="${FUNCNAME[0]}" |
| mkdir -p "${workspace}" |
| |
| create_workspace_with_default_repos "${workspace}/WORKSPACE" |
| cat >> "${workspace}/BUILD" << EOF |
| cc_library( |
| name = "foo", |
| srcs = ["foo.cc"], |
| ) |
| EOF |
| cat >> "${workspace}/foo.cc" << EOF |
| #include "top_level.h" |
| |
| int foo() { |
| return bar(); |
| } |
| EOF |
| cat >> "${workspace}/top_level.h" << EOF |
| inline int bar() { return 42; } |
| EOF |
| |
| cd "${workspace}" |
| bazel build --noincompatible_validate_top_level_header_inclusions \ |
| --spawn_strategy=standalone \ |
| //:foo &>"$TEST_log" || fail "Build failed but should have succeeded" |
| |
| bazel build --incompatible_validate_top_level_header_inclusions \ |
| --spawn_strategy=standalone \ |
| //:foo &>"$TEST_log" && fail "Build succeeded but should have failed" |
| expect_log "this rule is missing dependency declarations for the "\ |
| "following files included by 'foo.cc'" |
| } |
| |
| function test_aspect_accessing_args_link_action_with_tree_artifact() { |
| # This test assumes the presence of "nodeps" dynamic libraries, which do not |
| # function on Apple platforms. |
| [ "$PLATFORM" != "darwin" ] || return 0 |
| |
| local package="${FUNCNAME[0]}" |
| mkdir -p "${package}" |
| cat > "${package}/makes_tree_artifacts.sh" <<EOF |
| #!/bin/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 |
| #!/bin/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): |
| action = target.actions[1] |
| if action.mnemonic != "CppArchive": |
| fail("Expected the second action to be CppArchive.") |
| 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 |
| |
| cat > "${package}/BUILD" <<EOF |
| 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 .*b.*o .*c.*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 |
| 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_directory_arg_compile_action() { |
| # This test assumes the presence of "nodeps" dynamic libraries, which do not |
| # function on Apple platforms. |
| [ "$PLATFORM" != "darwin" ] || return 0 |
| |
| local package="${FUNCNAME[0]}" |
| mkdir -p "${package}" |
| |
| cat > "${package}/lib.bzl" <<EOF |
| def _actions_test_impl(target, ctx): |
| action = target.actions[0] |
| if action.mnemonic != "CppCompile": |
| fail("Expected the first 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 |
| 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 |
| 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_args = ctx.actions.declare_file("archive_args") |
| ctx.actions.run_shell( |
| outputs = [archive_args], |
| command = "echo \$@ > " + archive_args.path, |
| arguments = archive_action.args, |
| ) |
| |
| archive_out = ctx.actions.declare_file("archive_out.a") |
| archive_param_file = None |
| for i in archive_action.inputs.to_list(): |
| if i.path.endswith("params"): |
| archive_param_file = i |
| ctx.actions.run_shell( |
| inputs = depset(direct = [archive_args], transitive = [archive_action.inputs]), |
| mnemonic = "RecreatedCppArchive", |
| outputs = [archive_out], |
| env = archive_action.env, |
| command = "\$(cat %s) && cp %s %s" % ( |
| archive_args.path, |
| archive_action.outputs.to_list()[0].path, |
| archive_out.path, |
| ), |
| ) |
| |
| link_args = ctx.actions.declare_file("link_args") |
| ctx.actions.run_shell( |
| outputs = [link_args], |
| command = "echo \$@ > " + link_args.path, |
| arguments = link_action.args, |
| ) |
| |
| link_out = ctx.actions.declare_file("link_out.so") |
| ctx.actions.run_shell( |
| inputs = depset(direct = [link_args], transitive = [link_action.inputs]), |
| mnemonic = "RecreatedCppLink", |
| outputs = [link_out], |
| env = link_action.env, |
| command = "\$(cat %s) && cp %s %s" % ( |
| link_args.path, |
| link_action.outputs.to_list()[0].path, |
| link_out.path, |
| ), |
| ) |
| |
| return [OutputGroupInfo(out = [ |
| compile_args, |
| compile_out, |
| archive_args, |
| archive_out, |
| link_args, |
| 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 |
| 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() { |
| cat > ok.cc <<EOF |
| #include <stdio.h> |
| int main() { |
| printf("Hello\n"); |
| } |
| EOF |
| |
| cat > BUILD <<EOF |
| 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 build '@local_config_cc//:toolchain' &>/dev/null || \ |
| fail "Fake toolchain target causes analysis errors" |
| |
| # This only shows reliably for query due to ordering issues in how Bazel shows |
| # errors. |
| BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1 bazel query 'deps(//:ok)' &>"$TEST_log" || \ |
| fail "Should pass with fake toolchain" |
| expect_not_log "An error occurred during the fetch of repository 'local_config_cc'" |
| expect_log "@local_config_cc//:empty" |
| } |
| |
| 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 |
| 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 |
| 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() { |
| 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" |
| 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 |
| 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" |
| } |
| |
| function test_sibling_repository_layout_include_external_repo_output() { |
| mkdir test |
| cat > test/BUILD <<'EOF' |
| cc_library( |
| name = "foo", |
| srcs = ["foo.cc"], |
| deps = ["@bazel_tools//tools/jdk:jni"], |
| ) |
| EOF |
| cat > test/foo.cc <<'EOF' |
| #include <jni.h> |
| #include <stdio.h> |
| |
| extern "C" JNIEXPORT void JNICALL Java_foo_App_f(JNIEnv *env, jclass clazz, jint x) { |
| printf("hello %d\n", x); |
| } |
| EOF |
| bazel build --experimental_sibling_repository_layout //test:foo > "$TEST_log" \ |
| || fail "expected build success" |
| } |
| |
| # 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() { |
| 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 |
| 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() { |
| REPO_PATH=$TEST_TMPDIR/repo |
| mkdir -p "$REPO_PATH" |
| create_workspace_with_default_repos "$REPO_PATH/WORKSPACE" |
| mkdir "$REPO_PATH/foo" |
| cat > "$REPO_PATH/foo/BUILD" <<'EOF' |
| 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 >> $(create_workspace_with_default_repos WORKSPACE) <<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_args = ctx.actions.declare_file("archive_args") |
| ctx.actions.run_shell( |
| outputs = [archive_args], |
| command = "echo \$@ > " + archive_args.path, |
| arguments = archive_action.args, |
| ) |
| |
| archive_out = ctx.actions.declare_file("archive_out.a") |
| ctx.actions.run_shell( |
| inputs = [archive_args], |
| shadowed_action = archive_action, |
| mnemonic = "RecreatedCppArchive", |
| outputs = [archive_out], |
| command = "\$(cat %s | sed 's|%s|%s|g')" % ( |
| archive_args.path, |
| archive_action.outputs.to_list()[0].path, |
| archive_out.path, |
| ), |
| ) |
| |
| return [OutputGroupInfo(out = [ |
| compile_args, |
| compile_out, |
| archive_args, |
| 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 |
| 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() { |
| # Make sure there are no packages containing tools/cpp/INCLUDE_HINTS to exercise that case in |
| # IncludeHintsFunction. |
| rm -rf BUILD tools |
| mkdir pkg |
| cat > pkg/BUILD <<EOF |
| 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 "Include scanning enabled. This feature is unsupported." |
| expect_log "fatal error: '\?dep.h'\?" |
| } |
| |
| function test_env_inherit_cc_test() { |
| mkdir pkg |
| cat > pkg/BUILD <<EOF |
| 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 |
| 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 test_getting_compile_action_env_with_cctoolchain_config_features() { |
| [ "$PLATFORM" != "darwin" ] || 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 in ["CppCompile", "ObjcCompile"]: |
| compile_action = action |
| |
| print(compile_action.env) |
| return [] |
| |
| 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 |
| cc_binary( |
| name = "x", |
| srcs = ["x.cc"], |
| ) |
| EOF |
| |
| # Without the flag, the env should not return extra fixed variables |
| bazel build "package:x" \ |
| --aspects="//package:lib.bzl%actions_test_aspect" &>"$TEST_log" \ |
| || fail "Build failed but should have succeeded" |
| |
| expect_not_log "\"PWD\": \"/proc/self/cwd\"" |
| |
| # With the flag, the env should return extra fixed variables |
| bazel build "package:x" \ |
| --aspects="//package:lib.bzl%actions_test_aspect" \ |
| --experimental_get_fixed_configured_action_env &>"$TEST_log" \ |
| || fail "Build failed but should have succeeded" |
| |
| expect_log "\"PWD\": \"/proc/self/cwd\"" |
| } |
| |
| function external_cc_test_setup() { |
| cat >> WORKSPACE <<'EOF' |
| local_repository( |
| name = "other_repo", |
| path = "other_repo", |
| ) |
| EOF |
| |
| mkdir -p other_repo |
| touch other_repo/WORKSPACE |
| |
| mkdir -p other_repo/lib |
| cat > other_repo/lib/BUILD <<'EOF' |
| 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' |
| 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() { |
| [ "$PLATFORM" != "windows" ] || return 0 |
| |
| external_cc_test_setup |
| |
| bazel test \ |
| --test_output=errors \ |
| --strategy=sandboxed \ |
| @other_repo//test >& $TEST_log || fail "Test should pass" |
| } |
| |
| function test_external_cc_test_sandboxed_sibling_repository_layout() { |
| [ "$PLATFORM" != "windows" ] || 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() { |
| 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() { |
| external_cc_test_setup |
| |
| bazel test \ |
| --test_output=errors \ |
| --strategy=local \ |
| --experimental_sibling_repository_layout \ |
| @other_repo//test >& $TEST_log || fail "Test should pass" |
| } |
| |
| function test_bazel_current_repository_define() { |
| cat >> WORKSPACE <<'EOF' |
| local_repository( |
| name = "other_repo", |
| path = "other_repo", |
| ) |
| EOF |
| |
| mkdir -p pkg |
| cat > pkg/BUILD.bazel <<'EOF' |
| cc_library( |
| name = "library", |
| srcs = ["library.cpp"], |
| hdrs = ["library.h"], |
| visibility = ["//visibility:public"], |
| ) |
| |
| cc_binary( |
| name = "binary", |
| srcs = ["binary.cpp"], |
| deps = [":library"], |
| ) |
| |
| cc_test( |
| name = "test", |
| srcs = ["test.cpp"], |
| deps = [":library"], |
| ) |
| 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/WORKSPACE |
| |
| mkdir -p other_repo/pkg |
| cat > other_repo/pkg/BUILD.bazel <<'EOF' |
| cc_binary( |
| name = "binary", |
| srcs = ["binary.cpp"], |
| deps = ["@//pkg:library"], |
| ) |
| |
| cc_test( |
| name = "test", |
| srcs = ["test.cpp"], |
| deps = ["@//pkg:library"], |
| ) |
| 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/other_repo/pkg/binary.cpp: '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/other_repo/pkg/test.cpp: 'other_repo'" |
| expect_log "in pkg/library.cpp: ''" |
| } |
| |
| function test_compiler_flag_gcc() { |
| # The default macOS toolchain always uses XCode's clang. |
| [ "$PLATFORM" != "darwin" ] || return 0 |
| type -P gcc || return 0 |
| |
| cat > BUILD.bazel <<'EOF' |
| config_setting( |
| name = "gcc_compiler", |
| flag_values = {"@bazel_tools//tools/cpp:compiler": "gcc"}, |
| ) |
| |
| cc_binary( |
| name = "main", |
| srcs = select({":gcc_compiler": ["main.cc"]}), |
| ) |
| EOF |
| cat > main.cc <<'EOF' |
| int main() {} |
| EOF |
| |
| 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.bazel <<'EOF' |
| config_setting( |
| name = "clang_compiler", |
| flag_values = {"@bazel_tools//tools/cpp:compiler": "clang"}, |
| ) |
| |
| cc_binary( |
| name = "main", |
| srcs = select({":clang_compiler": ["main.cc"]}), |
| ) |
| EOF |
| cat > main.cc <<'EOF' |
| int main() {} |
| EOF |
| |
| bazel build //:main --repo_env=CC=clang || fail "Expected compiler flag to have value 'clang'" |
| } |
| |
| function test_cc_test_no_target_coverage_dep() { |
| # Regression test for https://github.com/bazelbuild/bazel/issues/16961 |
| local package="${FUNCNAME[0]}" |
| mkdir -p "${package}" |
| |
| cat > "${package}"/BUILD.bazel <<'EOF' |
| 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() { |
| # 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.bazel <<'EOF' |
| 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 [[ "$(uname -s | tr 'A-Z' 'a-z')" == "linux" ]]; then |
| return $(ldconfig -p | grep -q "$lib") |
| fi |
| |
| # assume installed for darwin |
| } |
| |
| function test_cc_toolchain_asan_feature() { |
| local feature=asan |
| __is_installed "lib$feature" || return 0 |
| |
| mkdir pkg |
| cat > pkg/BUILD <<EOF |
| 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 "ERROR: AddressSanitizer: stack-use-after-scope" |
| } |
| |
| function test_cc_toolchain_tsan_feature() { |
| local feature=tsan |
| __is_installed "lib$feature" || return 0 |
| |
| mkdir pkg |
| cat > pkg/BUILD <<EOF |
| 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 |
| expect_log "WARNING: ThreadSanitizer: data race" |
| } |
| |
| function test_cc_toolchain_ubsan_feature() { |
| local feature=ubsan |
| __is_installed "lib$feature" || return 0 |
| |
| mkdir pkg |
| cat > pkg/BUILD <<EOF |
| cc_binary( |
| name = 'example', |
| srcs = ['example.cc'], |
| 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" |
| } |
| |
| run_suite "cc_integration_test" |