Add CI toolchain verification tests. This adds C++ preprocessor/runtime and ELF binary verification tests in `//ci` to validate the compiler (`clang`, `gcc`, `msvc`, `clang-cl`), C++ standard library (`libc++`, `libstdc++`, MSVC STL), exception handling mode, target architecture, and sanitizer instrumentation across our CI toolchains. PiperOrigin-RevId: 999050499
diff --git a/.bazelrc b/.bazelrc index fd7c3ef..8040621 100644 --- a/.bazelrc +++ b/.bazelrc
@@ -30,7 +30,7 @@ # Configure our Linux Docker toolchain setup. common --extra_toolchains=@docker_toolchain//:all build:linux --cxxopt=-std=c++17 --host_cxxopt=-std=c++17 -build:linux --copt=-fno-exceptions +build:linux --copt=-fno-exceptions --//ci:exceptions=disabled build:linux --copt=-Werror build:linux --copt=-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1 build:linux --features=external_include_paths @@ -43,9 +43,10 @@ build:windows-msvc --copt=/WX build:windows-msvc --per_file_copt=external/.*@/w build:windows-msvc --host_per_file_copt=external/.*@/w +build:windows-msvc --//ci:compiler=msvc --//ci:stdlib=msvc --//ci:exceptions=enabled build:windows-clangcl --config=windows-msvc -build:windows-clangcl --compiler=clang-cl +build:windows-clangcl --compiler=clang-cl --//ci:compiler=clang-cl build:windows-clangcl --copt=-Wno-microsoft-cast build:windows-clangcl --extra_execution_platforms=//:x64_windows-clang-cl build:windows-clangcl --extra_toolchains=@local_config_cc//:cc-toolchain-x64_windows-clang-cl @@ -57,27 +58,28 @@ build:macos --features=external_include_paths build:macos --per_file_copt=external/.*@-w build:macos --host_per_file_copt=external/.*@-w +build:macos --//ci:compiler=clang --//ci:stdlib=libc++ --//ci:exceptions=enabled -build:gcc --config=linux --platforms=//ci:linux_gcc -build:gcc-exceptions --config=gcc --copt=-fexceptions -build:clang --config=linux --platforms=//ci:linux_clang --conlyopt=-Wno-unused-command-line-argument -build:clang-exceptions --config=clang --copt=-fexceptions -build:clang-arm --config=linux --platforms=//ci:linux_arm_clang --conlyopt=-Wno-unused-command-line-argument -build:libstdc++ --config=clang --features=libstdc++ --copt=-march=haswell +build:gcc --config=linux --platforms=//ci:linux_gcc --//ci:compiler=gcc --//ci:stdlib=libstdc++ +build:gcc-exceptions --config=gcc --copt=-fexceptions --//ci:exceptions=enabled +build:clang --config=linux --platforms=//ci:linux_clang --conlyopt=-Wno-unused-command-line-argument --//ci:compiler=clang --//ci:stdlib=libc++ +build:clang-exceptions --config=clang --copt=-fexceptions --//ci:exceptions=enabled +build:clang-arm --config=linux --platforms=//ci:linux_arm_clang --conlyopt=-Wno-unused-command-line-argument --//ci:compiler=clang --//ci:stdlib=libc++ +build:libstdc++ --config=clang --features=libstdc++ --copt=-march=haswell --//ci:stdlib=libstdc++ # Sanitizer configurations. -build:ubsan --features=ubsan +build:ubsan --features=ubsan --//ci:sanitizer=ubsan build:ubsan --copt=-fsanitize=float-divide-by-zero,nullability,undefined build:ubsan --copt=-fno-sanitize-recover=float-divide-by-zero,nullability,undefined build:ubsan --copt=-fno-sanitize-blacklist test:ubsan --test_env=UBSAN_OPTIONS=print_stacktrace=1 -build:asan --config=clang --config=ubsan --features=asan +build:asan --config=clang --config=ubsan --features=asan --//ci:sanitizer=asan build:asan --copt=-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG build:asan --linkopt=-fsanitize-link-c++-runtime test:asan --test_tag_filters=-benchmark,-noasan -build:tsan --config=clang --features=tsan +build:tsan --config=clang --features=tsan --//ci:sanitizer=tsan build:tsan --copt=-fno-sanitize-blacklist build:tsan --build_tag_filters=-notsan test:tsan --test_tag_filters=-benchmark,-notsan
diff --git a/MODULE.bazel b/MODULE.bazel index d4096d2..0c70441 100644 --- a/MODULE.bazel +++ b/MODULE.bazel
@@ -35,6 +35,11 @@ version = "1.9.5", dev_dependency = True, ) +bazel_dep( + name = "rules_shell", + version = "0.6.1", + dev_dependency = True, +) # Note: Googletest is NOT a dev_dependency. Some Abseil test utilities # intended to be used by Abseil users depend on GoogleTest.
diff --git a/ci/BUILD.bazel b/ci/BUILD.bazel index c83d079..c1ece12 100644 --- a/ci/BUILD.bazel +++ b/ci/BUILD.bazel
@@ -12,8 +12,97 @@ # See the License for the specific language governing permissions and # limitations under the License. +load("@bazel_skylib//rules:common_settings.bzl", "string_flag") +load("@rules_cc//cc:cc_test.bzl", "cc_test") +load("@rules_shell//shell:sh_test.bzl", "sh_test") + package(default_visibility = ["//visibility:private"]) +string_flag( + name = "compiler", + build_setting_default = "none", +) + +config_setting( + name = "clang_compiler", + flag_values = {":compiler": "clang"}, +) + +config_setting( + name = "gcc_compiler", + flag_values = {":compiler": "gcc"}, +) + +config_setting( + name = "msvc_compiler", + flag_values = {":compiler": "msvc"}, +) + +config_setting( + name = "clang_cl_compiler", + flag_values = {":compiler": "clang-cl"}, +) + +string_flag( + name = "stdlib", + build_setting_default = "none", +) + +config_setting( + name = "libcxx_enabled", + flag_values = {":stdlib": "libc++"}, +) + +config_setting( + name = "libstdcxx_enabled", + flag_values = {":stdlib": "libstdc++"}, +) + +config_setting( + name = "msvc_stdlib_enabled", + flag_values = {":stdlib": "msvc"}, +) + +string_flag( + name = "exceptions", + build_setting_default = "none", +) + +config_setting( + name = "exceptions_enabled", + flag_values = {":exceptions": "enabled"}, +) + +config_setting( + name = "exceptions_disabled", + flag_values = {":exceptions": "disabled"}, +) + +string_flag( + name = "sanitizer", + build_setting_default = "none", +) + +config_setting( + name = "asan_enabled", + flag_values = {":sanitizer": "asan"}, +) + +config_setting( + name = "tsan_enabled", + flag_values = {":sanitizer": "tsan"}, +) + +config_setting( + name = "msan_enabled", + flag_values = {":sanitizer": "msan"}, +) + +config_setting( + name = "ubsan_enabled", + flag_values = {":sanitizer": "ubsan"}, +) + platform( name = "rbe", constraint_values = [ @@ -82,3 +171,241 @@ "@bazel_tools//tools/cpp:clang", ], ) + +# Verification of our CI toolchains. + +cc_test( + name = "toolchain_test", + srcs = ["toolchain_test.cc"], + local_defines = select({ + ":clang_compiler": ["ABSL_CI_EXPECT_COMPILER_CLANG=1"], + ":gcc_compiler": ["ABSL_CI_EXPECT_COMPILER_GCC=1"], + ":msvc_compiler": ["ABSL_CI_EXPECT_COMPILER_MSVC=1"], + ":clang_cl_compiler": ["ABSL_CI_EXPECT_COMPILER_CLANG_CL=1"], + "//conditions:default": [], + }) + select({ + ":libcxx_enabled": ["ABSL_CI_EXPECT_STDLIB_LIBCXX=1"], + ":libstdcxx_enabled": ["ABSL_CI_EXPECT_STDLIB_LIBSTDCXX=1"], + ":msvc_stdlib_enabled": ["ABSL_CI_EXPECT_STDLIB_MSVC=1"], + "//conditions:default": [], + }) + select({ + ":exceptions_enabled": ["ABSL_CI_EXPECT_EXCEPTIONS_ENABLED=1"], + ":exceptions_disabled": ["ABSL_CI_EXPECT_EXCEPTIONS_DISABLED=1"], + "//conditions:default": [], + }) + select({ + ":asan_enabled": ["ABSL_CI_EXPECT_SANITIZER_ASAN=1"], + ":tsan_enabled": ["ABSL_CI_EXPECT_SANITIZER_TSAN=1"], + ":msan_enabled": ["ABSL_CI_EXPECT_SANITIZER_MSAN=1"], + "//conditions:default": [], + }), +) + +# Architecture verification tests +sh_test( + name = "x86_64_architecture_test", + srcs = ["verify_binary.sh"], + args = [ + "$(rootpath :toolchain_test)", + "arch", + "X86-64", + ], + data = [":toolchain_test"], + target_compatible_with = [ + "@platforms//os:linux", + "@platforms//cpu:x86_64", + ], +) + +sh_test( + name = "aarch64_architecture_test", + srcs = ["verify_binary.sh"], + args = [ + "$(rootpath :toolchain_test)", + "arch", + "AArch64", + ], + data = [":toolchain_test"], + target_compatible_with = [ + "@platforms//os:linux", + "@platforms//cpu:aarch64", + ], +) + +# Compiler ELF .comment verification tests +sh_test( + name = "clang_verification_test", + srcs = ["verify_binary.sh"], + args = [ + "$(rootpath :toolchain_test)", + "comment", + "clang version", + ], + data = [":toolchain_test"], + target_compatible_with = [ + "@platforms//os:linux", + ] + select({ + ":clang_compiler": [], + "//conditions:default": ["@platforms//:incompatible"], + }), +) + +sh_test( + name = "gcc_verification_test", + srcs = ["verify_binary.sh"], + args = [ + "$(rootpath :toolchain_test)", + "comment", + "GCC:", + ], + data = [":toolchain_test"], + target_compatible_with = [ + "@platforms//os:linux", + ] + select({ + ":gcc_compiler": [], + "//conditions:default": ["@platforms//:incompatible"], + }), +) + +# Standard library dynamic linkage verification tests +sh_test( + name = "libstdcxx_verification_test", + srcs = ["verify_binary.sh"], + args = [ + "$(rootpath :toolchain_test)", + "needed", + "libstdc++.so", + ], + data = [":toolchain_test"], + target_compatible_with = [ + "@platforms//os:linux", + ] + select({ + ":libstdcxx_enabled": [], + "//conditions:default": ["@platforms//:incompatible"], + }), +) + +sh_test( + name = "libstdcxx_not_needed_test", + srcs = ["verify_binary.sh"], + args = [ + "$(rootpath :toolchain_test)", + "not_needed", + "libstdc++.so", + ], + data = [":toolchain_test"], + target_compatible_with = [ + "@platforms//os:linux", + ] + select({ + ":libcxx_enabled": [], + "//conditions:default": ["@platforms//:incompatible"], + }), +) + +sh_test( + name = "libcxx_verification_test", + srcs = ["verify_binary.sh"], + args = [ + "$(rootpath :toolchain_test)", + "needed", + "libc++.so", + ], + data = [":toolchain_test"], + target_compatible_with = [ + "@platforms//os:linux", + ] + select({ + ":libcxx_enabled": [], + "//conditions:default": ["@platforms//:incompatible"], + }) + select({ + # These sanitizers link libc++ statically. + ":msan_enabled": ["@platforms//:incompatible"], + ":tsan_enabled": ["@platforms//:incompatible"], + "//conditions:default": [], + }), +) + +sh_test( + name = "libcxx_not_needed_test", + srcs = ["verify_binary.sh"], + args = [ + "$(rootpath :toolchain_test)", + "not_needed", + "libc++.so", + ], + data = [":toolchain_test"], + target_compatible_with = [ + "@platforms//os:linux", + ] + select({ + ":libstdcxx_enabled": [], + "//conditions:default": ["@platforms//:incompatible"], + }), +) + +# Sanitizer runtime instrumentation verification tests +sh_test( + name = "asan_verification_test", + srcs = ["verify_binary.sh"], + args = [ + "$(rootpath :toolchain_test)", + "symbol", + "__asan", + ], + data = [":toolchain_test"], + target_compatible_with = [ + "@platforms//os:linux", + ] + select({ + ":asan_enabled": [], + "//conditions:default": ["@platforms//:incompatible"], + }), +) + +sh_test( + name = "ubsan_verification_test", + srcs = ["verify_binary.sh"], + args = [ + "$(rootpath :toolchain_test)", + "symbol", + "__ubsan", + ], + data = [":toolchain_test"], + target_compatible_with = [ + "@platforms//os:linux", + ] + select({ + ":asan_enabled": [], + ":ubsan_enabled": [], + "//conditions:default": ["@platforms//:incompatible"], + }), +) + +sh_test( + name = "msan_verification_test", + srcs = ["verify_binary.sh"], + args = [ + "$(rootpath :toolchain_test)", + "symbol", + "__msan", + ], + data = [":toolchain_test"], + target_compatible_with = [ + "@platforms//os:linux", + ] + select({ + ":msan_enabled": [], + "//conditions:default": ["@platforms//:incompatible"], + }), +) + +sh_test( + name = "tsan_verification_test", + srcs = ["verify_binary.sh"], + args = [ + "$(rootpath :toolchain_test)", + "symbol", + "__tsan", + ], + data = [":toolchain_test"], + target_compatible_with = [ + "@platforms//os:linux", + ] + select({ + ":tsan_enabled": [], + "//conditions:default": ["@platforms//:incompatible"], + }), +)
diff --git a/ci/toolchain_test.cc b/ci/toolchain_test.cc new file mode 100644 index 0000000..86cbcba --- /dev/null +++ b/ci/toolchain_test.cc
@@ -0,0 +1,147 @@ +// Copyright 2026 The Abseil Authors. +// +// 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 +// +// https://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. + +#include <cstddef> +#include <iostream> +#include <memory> +#include <string> +#include <vector> + +#if defined(ABSL_CI_EXPECT_COMPILER_CLANG) +#if !defined(__clang__) || defined(_MSC_VER) +#error "Expected Clang, but __clang__ is not defined or _MSC_VER is defined" +#endif +#endif + +#if defined(ABSL_CI_EXPECT_COMPILER_GCC) +#if !defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER) +#error "Expected GCC, but __clang__ or _MSC_VER is defined or __GNUC__ is not" +#endif +#endif + +#if defined(ABSL_CI_EXPECT_COMPILER_MSVC) +#if !defined(_MSC_VER) || defined(__clang__) +#error "Expected MSVC, but _MSC_VER is not defined or __clang__ is defined" +#endif +#endif + +#if defined(ABSL_CI_EXPECT_COMPILER_CLANG_CL) +#if !defined(_MSC_VER) || !defined(__clang__) +#error "Expected clang-cl compiler, but _MSC_VER or __clang__ is not defined" +#endif +#endif + +#if defined(ABSL_CI_EXPECT_STDLIB_LIBCXX) +#if !defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) || \ + defined(_MSVC_STL_UPDATE) +#error "Expected libc++, but _LIBCPP_VERSION is not defined" +#endif +#endif + +#if defined(ABSL_CI_EXPECT_STDLIB_LIBSTDCXX) +#if !defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) || \ + defined(_MSVC_STL_UPDATE) +#error "Expected libstdc++, but __GLIBCXX__ is not defined" +#endif +#endif + +#if defined(ABSL_CI_EXPECT_STDLIB_MSVC) +#if !defined(_MSVC_STL_UPDATE) || defined(_LIBCPP_VERSION) || \ + defined(__GLIBCXX__) +#error "Expected MSVC STL, but _MSVC_STL_UPDATE is not defined" +#endif +#endif + +#if defined(ABSL_CI_EXPECT_EXCEPTIONS_ENABLED) +#if !defined(__EXCEPTIONS) && !defined(_CPPUNWIND) +#error "Expected C++ exceptions, but __EXCEPTIONS and _CPPUNWIND are undefined" +#endif +#endif + +#if defined(ABSL_CI_EXPECT_EXCEPTIONS_DISABLED) +#if defined(__EXCEPTIONS) || defined(_CPPUNWIND) +#error "Expected no C++ exceptions, but __EXCEPTIONS or _CPPUNWIND is defined" +#endif +#endif + +#if defined(ABSL_CI_EXPECT_SANITIZER_ASAN) +#if !defined(__SANITIZE_ADDRESS__) +#if !defined(__has_feature) || !__has_feature(address_sanitizer) +#error "Expected AddressSanitizer to be enabled" +#endif +#endif +#endif + +#if defined(ABSL_CI_EXPECT_SANITIZER_TSAN) +#if !defined(__SANITIZE_THREAD__) +#if !defined(__has_feature) || !__has_feature(thread_sanitizer) +#error "Expected ThreadSanitizer to be enabled" +#endif +#endif +#endif + +#if defined(ABSL_CI_EXPECT_SANITIZER_MSAN) +#if !defined(__SANITIZE_MEMORY__) +#if !defined(__has_feature) || !__has_feature(memory_sanitizer) +#error "Expected MemorySanitizer to be enabled" +#endif +#endif +#endif + +namespace { + +// Prevent the compiler from optimizing away memory access or sanitizer +// instrumentation. +#if defined(_MSC_VER) && !defined(__clang__) +__declspec(noinline) +#else +__attribute__((noinline)) +#endif +void TouchMemory(int* ptr) { + volatile int* vptr = ptr; + *vptr = 42; +} + +} // namespace + +int main(int argc, char** /*argv*/) { + // 1. Long string to bypass Short String Optimization (SSO) and exercise + // stdlib heap. + std::string s = + "abseil toolchain test with long string to bypass SSO allocation limit"; + std::cout << s << '\n'; + + // 2. Dynamic heap allocation and pointer access to ensure ASAN/MSAN/TSAN + // hooks. + auto vec = std::make_unique<std::vector<int>>(100, 7); + TouchMemory(vec->data()); + + // 3. Volatile indexing and arithmetic to ensure UBSAN checks are retained. + volatile std::size_t idx = argc > 0 ? 0 : 1; + (*vec)[idx] += 1; + +#if defined(ABSL_CI_EXPECT_EXCEPTIONS_ENABLED) + bool caught = false; + try { + throw 42; + } catch (int v) { + caught = (v == 42); + } + if (!caught) { + return 1; + } +#endif + + return 0; +}
diff --git a/ci/verify_binary.sh b/ci/verify_binary.sh new file mode 100755 index 0000000..60700e4 --- /dev/null +++ b/ci/verify_binary.sh
@@ -0,0 +1,75 @@ +#!/bin/bash +# Copyright 2026 The Abseil Authors. +# +# 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 +# +# https://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. + +# Checks ELF header architecture, dynamic needed libraries, or binary symbol tables. +set -euo pipefail + +BINARY="$1" +MODE="$2" +PATTERN="$3" + +echo "Inspecting BINARY: $BINARY" >&2 +if [[ ! -f "$BINARY" ]]; then + echo "ERROR: binary $BINARY not found" >&2 + exit 1 +fi + +case "$MODE" in + arch) + echo "Verifying ELF architecture header matches '$PATTERN'..." + readelf -h "$BINARY" | grep "$PATTERN" >/dev/null || { + echo "ERROR: ELF header for $BINARY does not match expected architecture '$PATTERN'" >&2 + readelf -h "$BINARY" >&2 + exit 1 + } + ;; + comment) + echo "Verifying ELF .comment section matches '$PATTERN'..." + readelf -p .comment "$BINARY" | grep "$PATTERN" >/dev/null || { + echo "ERROR: ELF .comment section for $BINARY does not match '$PATTERN'" >&2 + readelf -p .comment "$BINARY" >&2 + exit 1 + } + ;; + symbol) + echo "Verifying binary symbol table contains '$PATTERN'..." + nm "$BINARY" | grep "$PATTERN" >/dev/null || { + echo "ERROR: Symbol '$PATTERN' not found in $BINARY" >&2 + exit 1 + } + ;; + needed) + echo "Verifying ELF NEEDED dynamic dependencies contain '$PATTERN'..." + readelf -d "$BINARY" | grep "$PATTERN" >/dev/null || { + echo "ERROR: Dynamic dependency '$PATTERN' not found in $BINARY" >&2 + readelf -d "$BINARY" >&2 + exit 1 + } + ;; + not_needed) + echo "Verifying ELF NEEDED dynamic dependencies do NOT contain '$PATTERN'..." + if readelf -d "$BINARY" | grep "$PATTERN" >/dev/null; then + echo "ERROR: Unexpected dynamic dependency '$PATTERN' found in $BINARY" >&2 + readelf -d "$BINARY" >&2 + exit 1 + fi + ;; + *) + echo "ERROR: Unknown verification mode '$MODE'" >&2 + exit 1 + ;; +esac + +echo "SUCCESS: Verified $MODE '$PATTERN' in $BINARY."