name: build description: 'Builds the project with ccache integration' inputs: generator: description: 'CMake generator to use' required: true configuration: description: 'Build configuration (Debug, Release, etc.)' required: true build_dir: description: 'Directory to build in' required: false default: '.build' cc: description: 'C compiler to use' required: false default: '' cxx: description: 'C++ compiler to use' required: false default: '' compiler-id: description: 'Unique identifier: compiler-version-stdlib[-gccversion] (e.g. clang-14-libstdcxx-gcc11, gcc-13-libstdcxx)' required: false default: '' cache_version: description: 'Cache version for invalidation' required: false default: '1' ccache_enabled: description: 'Whether to use ccache' required: false default: 'true' main_branch: description: 'Main branch name for restore keys' required: false default: 'dev' stdlib: description: 'C++ standard library to use' required: true type: choice options: - libstdcxx - libcxx clang_gcc_toolchain: description: 'GCC version to use for Clang toolchain (e.g. 11, 13)' required: false default: '' ccache_max_size: description: 'Maximum ccache size' required: false default: '2G' ccache_hash_dir: description: 'Whether to include directory paths in hash' required: false default: 'true' ccache_compiler_check: description: 'How to check compiler for changes' required: false default: 'content' aws-access-key-id: description: 'AWS Access Key ID for S3 cache storage' required: true aws-secret-access-key: description: 'AWS Secret Access Key for S3 cache storage' required: true runs: using: 'composite' steps: - name: Generate safe branch name if: inputs.ccache_enabled == 'true' id: safe-branch shell: bash run: | SAFE_BRANCH=$(echo "${{ github.ref_name }}" | tr -c 'a-zA-Z0-9_.-' '-') echo "name=${SAFE_BRANCH}" >> $GITHUB_OUTPUT - name: Restore ccache directory if: inputs.ccache_enabled == 'true' id: ccache-restore uses: ./.github/actions/xahau-actions-cache-restore with: path: ~/.ccache key: ${{ runner.os }}-ccache-v${{ inputs.cache_version }}-${{ inputs.compiler-id }}-${{ inputs.configuration }}-${{ steps.safe-branch.outputs.name }} restore-keys: | ${{ runner.os }}-ccache-v${{ inputs.cache_version }}-${{ inputs.compiler-id }}-${{ inputs.configuration }}-${{ inputs.main_branch }} ${{ runner.os }}-ccache-v${{ inputs.cache_version }}-${{ inputs.compiler-id }}-${{ inputs.configuration }}- ${{ runner.os }}-ccache-v${{ inputs.cache_version }}-${{ inputs.compiler-id }}- aws-access-key-id: ${{ inputs.aws-access-key-id }} aws-secret-access-key: ${{ inputs.aws-secret-access-key }} - name: Configure ccache if: inputs.ccache_enabled == 'true' shell: bash run: | # Use ccache's default cache_dir (~/.ccache) - don't override it # This avoids tilde expansion issues when setting it explicitly # Create cache directory using ccache's default mkdir -p ~/.ccache # Configure ccache settings (but NOT cache_dir - use default) # This overwrites any cached config to ensure fresh configuration ccache --set-config=max_size=${{ inputs.ccache_max_size }} ccache --set-config=hash_dir=${{ inputs.ccache_hash_dir }} ccache --set-config=compiler_check=${{ inputs.ccache_compiler_check }} # Note: Not setting CCACHE_DIR - let ccache use its default (~/.ccache) # Print config for verification echo "=== ccache configuration ===" ccache -p # Zero statistics before the build ccache -z - name: Configure project shell: bash run: | mkdir -p ${{ inputs.build_dir }} cd ${{ inputs.build_dir }} # Set compiler environment variables if provided if [ -n "${{ inputs.cc }}" ]; then export CC="${{ inputs.cc }}" fi if [ -n "${{ inputs.cxx }}" ]; then export CXX="${{ inputs.cxx }}" fi # Create wrapper toolchain that overlays ccache on top of Conan's toolchain # This enables ccache for the main app build without affecting Conan dependency builds if [ "${{ inputs.ccache_enabled }}" = "true" ]; then cat > wrapper_toolchain.cmake <<'EOF' # Include Conan's generated toolchain first (sets compiler, flags, etc.) # Note: CMAKE_CURRENT_LIST_DIR is the directory containing this wrapper (.build/) include(${CMAKE_CURRENT_LIST_DIR}/build/generators/conan_toolchain.cmake) # Overlay ccache configuration for main application build # This does NOT affect Conan dependency builds (already completed) set(CMAKE_C_COMPILER_LAUNCHER ccache CACHE STRING "C compiler launcher" FORCE) set(CMAKE_CXX_COMPILER_LAUNCHER ccache CACHE STRING "C++ compiler launcher" FORCE) EOF TOOLCHAIN_FILE="wrapper_toolchain.cmake" echo "✅ Created wrapper toolchain with ccache enabled" else TOOLCHAIN_FILE="build/generators/conan_toolchain.cmake" echo "ℹ️ Using Conan toolchain directly (ccache disabled)" fi # Configure C++ standard library if specified # libstdcxx used for clang-14/16 to work around missing lexicographical_compare_three_way in libc++ # libcxx can be used with clang-17+ which has full C++20 support # Note: -stdlib flag is Clang-specific, GCC always uses libstdc++ CMAKE_CXX_FLAGS="" if [[ "${{ inputs.cxx }}" == clang* ]]; then # Only Clang needs the -stdlib flag if [ "${{ inputs.stdlib }}" = "libstdcxx" ]; then CMAKE_CXX_FLAGS="-stdlib=libstdc++" elif [ "${{ inputs.stdlib }}" = "libcxx" ]; then CMAKE_CXX_FLAGS="-stdlib=libc++" fi fi # GCC always uses libstdc++ and doesn't need/support the -stdlib flag # Configure GCC toolchain for Clang if specified if [ -n "${{ inputs.clang_gcc_toolchain }}" ] && [[ "${{ inputs.cxx }}" == clang* ]]; then # Extract Clang version from compiler executable name (e.g., clang++-14 -> 14) clang_version=$(echo "${{ inputs.cxx }}" | grep -oE '[0-9]+$') # Clang 16+ supports --gcc-install-dir (precise path specification) # Clang <16 only has --gcc-toolchain (uses discovery heuristics) if [ -n "$clang_version" ] && [ "$clang_version" -ge "16" ]; then # Clang 16+ uses --gcc-install-dir (canonical, precise) CMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS --gcc-install-dir=/usr/lib/gcc/x86_64-linux-gnu/${{ inputs.clang_gcc_toolchain }}" else # Clang 14-15 uses --gcc-toolchain (deprecated but necessary) # Note: This still uses discovery, so we hide newer GCC versions in the workflow CMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS --gcc-toolchain=/usr" fi fi # Run CMake configure # Note: conanfile.py hardcodes 'build/generators' as the output path. # If we're in a 'build' folder, Conan detects this and uses just 'generators/' # If we're in '.build' (non-standard), Conan adds the full 'build/generators/' # So we get: .build/build/generators/ with our non-standard folder name cmake .. \ -G "${{ inputs.generator }}" \ ${CMAKE_CXX_FLAGS:+-DCMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS"} \ -DCMAKE_TOOLCHAIN_FILE:FILEPATH=${TOOLCHAIN_FILE} \ -DCMAKE_BUILD_TYPE=${{ inputs.configuration }} - name: Show ccache config before build if: inputs.ccache_enabled == 'true' shell: bash run: | echo "==========================================" echo "ccache configuration before build" echo "==========================================" ccache -p echo "" - name: Build project shell: bash run: | cd ${{ inputs.build_dir }} cmake --build . --config ${{ inputs.configuration }} --parallel $(nproc) -- -v - name: Show ccache statistics if: inputs.ccache_enabled == 'true' shell: bash run: ccache -s - name: Save ccache directory if: always() && inputs.ccache_enabled == 'true' uses: ./.github/actions/xahau-actions-cache-save with: path: ~/.ccache key: ${{ runner.os }}-ccache-v${{ inputs.cache_version }}-${{ inputs.compiler-id }}-${{ inputs.configuration }}-${{ steps.safe-branch.outputs.name }} aws-access-key-id: ${{ inputs.aws-access-key-id }} aws-secret-access-key: ${{ inputs.aws-secret-access-key }}