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: ''

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 for default branch
      if: inputs.ccache_enabled == 'true'
      id: ccache-restore
      uses: actions/cache/restore@v4
      with:
        path: ~/.ccache-main
        key: ${{ runner.os }}-ccache-v${{ inputs.cache_version }}-${{ inputs.compiler-id }}-${{ inputs.configuration }}-${{ inputs.main_branch }}
        restore-keys: |
          ${{ runner.os }}-ccache-v${{ inputs.cache_version }}-${{ inputs.compiler-id }}-${{ inputs.configuration }}-
          ${{ runner.os }}-ccache-v${{ inputs.cache_version }}-${{ inputs.compiler-id }}-

    - name: Restore ccache directory for current branch
      if: inputs.ccache_enabled == 'true' && steps.safe-branch.outputs.name != inputs.main_branch
      id: ccache-restore-current-branch
      uses: actions/cache/restore@v4
      with:
        path: ~/.ccache-current
        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 }}-

    - 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
        
        
        # Configure ccache launcher args
        CCACHE_ARGS=""
        if [ "${{ inputs.ccache_enabled }}" = "true" ]; then
          CCACHE_ARGS="-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache"
        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 }}" \
          $CCACHE_ARGS \
          ${CMAKE_CXX_FLAGS:+-DCMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS"} \
          -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \
          -DCMAKE_BUILD_TYPE=${{ inputs.configuration }}

    - name: Build project
      shell: bash
      run: |
        cd ${{ inputs.build_dir }}
        cmake --build . --config ${{ inputs.configuration }} --parallel $(nproc)

    - name: Show ccache statistics
      if: inputs.ccache_enabled == 'true'
      shell: bash
      run: ccache -s

    - name: Save ccache directory for default branch
      if: always() && inputs.ccache_enabled == 'true' && steps.safe-branch.outputs.name == inputs.main_branch
      uses: actions/cache/save@v4
      with:
        path: ~/.ccache-main
        key: ${{ steps.ccache-restore.outputs.cache-primary-key }}

    - name: Save ccache directory for current branch
      if: always() && inputs.ccache_enabled == 'true' && steps.safe-branch.outputs.name != inputs.main_branch
      uses: actions/cache/save@v4
      with:
        path: ~/.ccache-current
        key: ${{ steps.ccache-restore-current-branch.outputs.cache-primary-key }}
