diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b20dfdd..ce9eac9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,9 +19,16 @@ jobs: with: extra_args: --all-files + toolchain-detection-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Run toolchain detection tests + run: bash core/scripts/test_detect_toolchain.sh + tests: runs-on: ubuntu-latest - steps: + steps: - name: Checkout code uses: actions/checkout@v3 with: diff --git a/core/BUILD b/core/BUILD index 3d366b3..0e1a575 100644 --- a/core/BUILD +++ b/core/BUILD @@ -68,6 +68,29 @@ cc_library( ) +# Generate a header with C++ toolchain information +genrule( + name = "toolchain_info_gen", + srcs = ["scripts/detect_toolchain.sh"], + outs = ["toolchain_info.h"], + cmd = """ +case "$(COMPILATION_MODE)" in + opt) BUILD_TYPE="Release" ;; + dbg) BUILD_TYPE="Debug" ;; + fastbuild) BUILD_TYPE="FastBuild" ;; + *) BUILD_TYPE="$(COMPILATION_MODE)" ;; +esac +bash $(location scripts/detect_toolchain.sh) "$(CC)" "$$BUILD_TYPE" > $@ +""", + toolchains = ["@bazel_tools//tools/cpp:current_cc_toolchain"], +) + +cc_library( + name = "toolchain_info", + hdrs = [":toolchain_info_gen"], + includes = ["."], +) + # Define the codspeed library cc_library( name = "codspeed", @@ -87,7 +110,7 @@ cc_library( ":walltime_mode": ["CODSPEED_ENABLED", "CODSPEED_WALLTIME", "CODSPEED_MODE_DISPLAY=\\\"walltime\\\""], "//conditions:default": [], }), - deps = [":instrument_hooks"], + deps = [":instrument_hooks", ":toolchain_info"], visibility = ["//visibility:public"], ) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 8a7eca0..1ef46df 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -15,7 +15,7 @@ include(FetchContent) FetchContent_Declare( instrument_hooks_repo GIT_REPOSITORY https://github.com/CodSpeedHQ/instrument-hooks - GIT_TAG 89fb72a076ec71c9eca6eee9bca98bada4b4dfb4 + GIT_TAG e86719c70c9c0b1646db182a7c748230e243dace ) FetchContent_MakeAvailable(instrument_hooks_repo) FetchContent_GetProperties(instrument_hooks_repo) @@ -96,6 +96,22 @@ target_link_libraries(codspeed PRIVATE instrument_hooks) # Version add_compile_definitions(CODSPEED_VERSION="${CODSPEED_VERSION}") +# Collect compiler toolchain information for environment reporting +# Use the shared detect_toolchain.sh script (same as Bazel) for consistent output +execute_process( + COMMAND bash "${CMAKE_CURRENT_SOURCE_DIR}/scripts/detect_toolchain.sh" + "${CMAKE_CXX_COMPILER}" "${CMAKE_BUILD_TYPE}" + OUTPUT_VARIABLE CODSPEED_TOOLCHAIN_HEADER + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE DETECT_TOOLCHAIN_RESULT +) +if(DETECT_TOOLCHAIN_RESULT EQUAL 0) + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/toolchain_info.h" "${CODSPEED_TOOLCHAIN_HEADER}\n") +else() + message(WARNING "detect_toolchain.sh failed, toolchain info will not be available") +endif() +target_include_directories(codspeed PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") + # Specify the include directories for users of the library target_include_directories( codspeed diff --git a/core/include/measurement.hpp b/core/include/measurement.hpp index 1644415..20f211a 100644 --- a/core/include/measurement.hpp +++ b/core/include/measurement.hpp @@ -35,6 +35,25 @@ inline bool measurement_is_instrumented() { inline void measurement_set_metadata() { std::string version = get_version(); instrument_hooks_set_integration(g_hooks, "codspeed-cpp", version.c_str()); + + // Report C++ toolchain information +#ifdef CODSPEED_CXX_COMPILER_ID + instrument_hooks_set_environment(g_hooks, "C++ Compiler", "compiler_id", + CODSPEED_CXX_COMPILER_ID); +#endif +#ifdef CODSPEED_CXX_COMPILER_VERSION + instrument_hooks_set_environment(g_hooks, "C++ Compiler", "version", + CODSPEED_CXX_COMPILER_VERSION); +#endif +#ifdef CODSPEED_CXX_COMPILER_FULL_VERSION + instrument_hooks_set_environment(g_hooks, "C++ Compiler", "build", + CODSPEED_CXX_COMPILER_FULL_VERSION); +#endif +#ifdef CODSPEED_BUILD_TYPE + instrument_hooks_set_environment(g_hooks, "C++ Compiler", "build_type", + CODSPEED_BUILD_TYPE); +#endif + instrument_hooks_write_environment(g_hooks, static_cast(getpid())); } ALWAYS_INLINE void measurement_start() { @@ -54,9 +73,9 @@ ALWAYS_INLINE uint64_t measurement_current_timestamp() { return instrument_hooks_current_timestamp(); } -ALWAYS_INLINE int8_t measurement_add_marker(uint8_t marker_type, - uint64_t timestamp) { - auto pid = getpid(); +ALWAYS_INLINE uint8_t measurement_add_marker(uint8_t marker_type, + uint64_t timestamp) { + auto pid = static_cast(getpid()); return instrument_hooks_add_marker(g_hooks, pid, marker_type, timestamp); } diff --git a/core/instrument-hooks b/core/instrument-hooks index 89fb72a..e86719c 160000 --- a/core/instrument-hooks +++ b/core/instrument-hooks @@ -1 +1 @@ -Subproject commit 89fb72a076ec71c9eca6eee9bca98bada4b4dfb4 +Subproject commit e86719c70c9c0b1646db182a7c748230e243dace diff --git a/core/scripts/detect_toolchain.sh b/core/scripts/detect_toolchain.sh new file mode 100755 index 0000000..2d9014b --- /dev/null +++ b/core/scripts/detect_toolchain.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# Detect C++ compiler toolchain information from ` --version` output. +# Used by Bazel genrule to generate toolchain_info.h. +# +# Usage: detect_toolchain.sh +# Output: Writes a C header to stdout with CODSPEED_CXX_* defines. + +set -e + +COMPILER="${1:?Usage: detect_toolchain.sh }" +BUILD_TYPE="${2:?Usage: detect_toolchain.sh }" + +FULL_VERSION=$("$COMPILER" --version 2>/dev/null | head -n1) || FULL_VERSION="unknown" + +COMPILER_ID="unknown" +COMPILER_VERSION="unknown" + +if echo "$FULL_VERSION" | grep -qi "clang"; then + COMPILER_ID="Clang" + COMPILER_VERSION=$(echo "$FULL_VERSION" | grep -oP '\d+\.\d+\.\d+' | head -n1) || COMPILER_VERSION="unknown" +elif echo "$FULL_VERSION" | grep -qi "gcc\|g++\|GNU"; then + COMPILER_ID="GNU" + COMPILER_VERSION=$(echo "$FULL_VERSION" | grep -oP '\d+\.\d+\.\d+' | head -n1) || COMPILER_VERSION="unknown" +elif echo "$FULL_VERSION" | grep -qi "MSVC\|Microsoft"; then + COMPILER_ID="MSVC" + COMPILER_VERSION=$(echo "$FULL_VERSION" | grep -oP '\d+\.\d+\.\d+' | head -n1) || COMPILER_VERSION="unknown" +fi + +cat << HEADER_EOF +// Auto-generated - do not edit +#ifndef CODSPEED_TOOLCHAIN_INFO_H +#define CODSPEED_TOOLCHAIN_INFO_H + +#define CODSPEED_CXX_COMPILER_ID "$COMPILER_ID" +#define CODSPEED_CXX_COMPILER_VERSION "$COMPILER_VERSION" +#define CODSPEED_CXX_COMPILER_FULL_VERSION "$FULL_VERSION" +#define CODSPEED_BUILD_TYPE "$BUILD_TYPE" + +#endif // CODSPEED_TOOLCHAIN_INFO_H +HEADER_EOF diff --git a/core/scripts/test_detect_toolchain.sh b/core/scripts/test_detect_toolchain.sh new file mode 100755 index 0000000..44b6c76 --- /dev/null +++ b/core/scripts/test_detect_toolchain.sh @@ -0,0 +1,101 @@ +#!/usr/bin/env bash +# Manual tests for detect_toolchain.sh +# Run: bash core/scripts/test_detect_toolchain.sh +# +# Uses mock compiler scripts to simulate different --version outputs. + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +DETECT_SCRIPT="$SCRIPT_DIR/detect_toolchain.sh" +TMPDIR=$(mktemp -d) +trap 'rm -rf "$TMPDIR"' EXIT + +PASS=0 +FAIL=0 + +assert_contains() { + local label="$1" + local expected="$2" + local actual="$3" + if echo "$actual" | grep -qF "$expected"; then + PASS=$((PASS + 1)) + else + FAIL=$((FAIL + 1)) + echo "FAIL: $label" + echo " expected to contain: $expected" + echo " got: $actual" + fi +} + +# Create a mock compiler that outputs a given --version string +make_mock_compiler() { + local name="$1" + local version_output="$2" + local path="$TMPDIR/$name" + cat > "$path" << EOF +#!/usr/bin/env bash +echo "$version_output" +EOF + chmod +x "$path" + echo "$path" +} + +# --- Test cases --- + +echo "Running detect_toolchain.sh tests..." +echo + +# Test: GCC +mock=$(make_mock_compiler "gcc-mock" "gcc (GCC) 14.3.0") +output=$(bash "$DETECT_SCRIPT" "$mock" "Release") +assert_contains "GCC compiler_id" 'CODSPEED_CXX_COMPILER_ID "GNU"' "$output" +assert_contains "GCC version" 'CODSPEED_CXX_COMPILER_VERSION "14.3.0"' "$output" +assert_contains "GCC full_version" 'CODSPEED_CXX_COMPILER_FULL_VERSION "gcc (GCC) 14.3.0"' "$output" +assert_contains "GCC build_type" 'CODSPEED_BUILD_TYPE "Release"' "$output" + +# Test: g++ +mock=$(make_mock_compiler "gpp-mock" "g++ (GCC) 13.2.1 20230801") +output=$(bash "$DETECT_SCRIPT" "$mock" "Debug") +assert_contains "g++ compiler_id" 'CODSPEED_CXX_COMPILER_ID "GNU"' "$output" +assert_contains "g++ version" 'CODSPEED_CXX_COMPILER_VERSION "13.2.1"' "$output" +assert_contains "g++ build_type" 'CODSPEED_BUILD_TYPE "Debug"' "$output" + +# Test: Clang +mock=$(make_mock_compiler "clang-mock" "clang version 19.1.7 (https://github.com/llvm/llvm-project abc123)") +output=$(bash "$DETECT_SCRIPT" "$mock" "Release") +assert_contains "Clang compiler_id" 'CODSPEED_CXX_COMPILER_ID "Clang"' "$output" +assert_contains "Clang version" 'CODSPEED_CXX_COMPILER_VERSION "19.1.7"' "$output" + +# Test: Apple Clang +mock=$(make_mock_compiler "apple-clang-mock" "Apple clang version 15.0.0 (clang-1500.0.40.1)") +output=$(bash "$DETECT_SCRIPT" "$mock" "Release") +assert_contains "Apple Clang compiler_id" 'CODSPEED_CXX_COMPILER_ID "Clang"' "$output" +assert_contains "Apple Clang version" 'CODSPEED_CXX_COMPILER_VERSION "15.0.0"' "$output" + +# Test: Ubuntu GCC +mock=$(make_mock_compiler "ubuntu-gcc-mock" "gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0") +output=$(bash "$DETECT_SCRIPT" "$mock" "Release") +assert_contains "Ubuntu GCC compiler_id" 'CODSPEED_CXX_COMPILER_ID "GNU"' "$output" +assert_contains "Ubuntu GCC version" 'CODSPEED_CXX_COMPILER_VERSION "13.3.0"' "$output" + +# Test: Unknown compiler +mock=$(make_mock_compiler "unknown-mock" "some-compiler v2.0") +output=$(bash "$DETECT_SCRIPT" "$mock" "Release") +assert_contains "Unknown compiler_id" 'CODSPEED_CXX_COMPILER_ID "unknown"' "$output" +assert_contains "Unknown version" 'CODSPEED_CXX_COMPILER_VERSION "unknown"' "$output" + +# Test: Header guard present +assert_contains "Header guard" '#ifndef CODSPEED_TOOLCHAIN_INFO_H' "$output" +assert_contains "Header guard endif" '#endif' "$output" + +# --- Summary --- + +echo +TOTAL=$((PASS + FAIL)) +if [ "$FAIL" -eq 0 ]; then + echo "All $TOTAL tests passed." +else + echo "$FAIL/$TOTAL tests FAILED." + exit 1 +fi diff --git a/core/src/codspeed.cpp b/core/src/codspeed.cpp index d82d074..0791b6f 100644 --- a/core/src/codspeed.cpp +++ b/core/src/codspeed.cpp @@ -4,6 +4,10 @@ #include #include +#if __has_include("toolchain_info.h") +#include "toolchain_info.h" +#endif + #include "measurement.hpp" namespace codspeed {