diff --git a/.github/workflows/build-test-release.yml b/.github/workflows/build-test-release.yml index 360cd33ec..d87186b1a 100644 --- a/.github/workflows/build-test-release.yml +++ b/.github/workflows/build-test-release.yml @@ -118,14 +118,9 @@ jobs: pgm-build-setup-ga-ci - uses: ./.github/actions/enable-msvc - if: runner.os == 'Windows' - name: Build and test run: | - # Resolve dirty PATH environment - # TODO(mgovers): Remove after https://github.com/actions/runner-images/issues/10001 is resolved - $env:PATH = ($env:PATH -split ';' | Where-Object { $_ -ne 'C:\Program Files\LLVM\bin' }) -join ';' - # generate cmake cache cmake --preset ${{ env.PRESET }} -DCMAKE_PREFIX_PATH=C:\conda_envs\cpp_pkgs\Library; if(!$?) { Exit $LASTEXITCODE } # build diff --git a/.github/workflows/check-build-reproducibility.yml b/.github/workflows/check-build-reproducibility.yml new file mode 100644 index 000000000..6b6c5a4b0 --- /dev/null +++ b/.github/workflows/check-build-reproducibility.yml @@ -0,0 +1,170 @@ +# SPDX-FileCopyrightText: Contributors to the Power Grid Model project +# +# SPDX-License-Identifier: MPL-2.0 + +name: Build and Test C++ and Python + +# Controls when the workflow will run +on: + # run pipeline from another workflow + workflow_call: + # run this workflow manually from the Actions tab + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-build-reproducibility + cancel-in-progress: true + +jobs: + check-power-grid-model-c-linux: + runs-on: ubuntu-latest + strategy: + matrix: + compiler: [gcc, clang] + include: + - c_compiler: gcc-14 + cxx_compiler: g++-14 + - c_compiler: clang-18 + cxx_compiler: clang++-18 + + env: + CMAKE_PREFIX_PATH: /home/linuxbrew/.linuxbrew + PRESET: ci-${{ matrix.compiler }}-reproducible + HOMEBREW_FORCE_BREWED_CURL: 1 + + steps: + - uses: actions/checkout@v6 + - name: Install packages + run: | + sudo apt-get update + sudo apt-get install -y ninja-build gcc-14 g++-14 clang-18 + sudo ln -s /usr/bin/clang-18 /usr/local/bin/clang + sudo ln -s /usr/bin/clang++-18 /usr/local/bin/clang++ + sudo ln -s /usr/bin/gcc-14 /usr/local/bin/gcc + sudo ln -s /usr/bin/g++-14 /usr/local/bin/g++ + + - name: Install uv + uses: astral-sh/setup-uv@v7 + + - name: Install pgm-build-dependencies + run: | + uv tool install https://github.com/PowerGridModel/pgm-build-dependencies/releases/latest/download/pgm_build_dependencies-0.1.0-py3-none-any.whl + pgm-build-setup-ga-ci + + - name: Build twice + run: | + cmake --preset ${{ env.PRESET }} + cmake --build --preset ${{ env.PRESET }} --verbose -j 1 + cmake --build --preset ${{ env.PRESET }} --verbose -j 1 --target install + mv install/${{ env.PRESET }} install/${{ env.PRESET }}-1 + mv cpp_build/ cpp_build_1/ + cmake --preset ${{ env.PRESET }} + cmake --build --preset ${{ env.PRESET }} --verbose -j 1 + cmake --build --preset ${{ env.PRESET }} --verbose -j 1 --target install + mv install/${{ env.PRESET }} install/${{ env.PRESET }}-2 + mv cpp_build/ cpp_build_2/ + + - name: Compare builds + run: | + diff -qr install/${{ env.PRESET }}-1 install/${{ env.PRESET }}-2 + if [ $? -ne 0 ]; then + echo "Build mismatch detected" + exit 1 + fi + echo "Builds are identical" + + check-reproducibility-c-windows: + runs-on: windows-latest + strategy: + matrix: + compiler: [msvc, clang-cl] + + env: + PRESET: ci-${{ matrix.compiler }}-reproducible + + steps: + - uses: actions/checkout@v6 + + - name: Install uv + uses: astral-sh/setup-uv@v7 + + - name: Install pgm-build-dependencies + run: | + uv tool install https://github.com/PowerGridModel/pgm-build-dependencies/releases/latest/download/pgm_build_dependencies-0.1.0-py3-none-any.whl + pgm-build-setup-ga-ci + + - uses: ./.github/actions/enable-msvc + + - name: Build twice + run: | + cmake --preset ${{ env.PRESET }} -DCMAKE_PREFIX_PATH=C:\conda_envs\cpp_pkgs\Library; if(!$?) { Exit $LASTEXITCODE } + cmake --build --preset ${{ env.PRESET }} --verbose -j 1; if(!$?) { Exit $LASTEXITCODE } + cmake --build --preset ${{ env.PRESET }} --verbose -j 1 --target install; if(!$?) { Exit $LASTEXITCODE } + mv install/${{ env.PRESET }} install/${{ env.PRESET }}-1; if(!$?) { Exit $LASTEXITCODE } + mv cpp_build/ cpp_build_1/; if(!$?) { Exit $LASTEXITCODE } + cmake --preset ${{ env.PRESET }} -DCMAKE_PREFIX_PATH=C:\conda_envs\cpp_pkgs\Library; if(!$?) { Exit $LASTEXITCODE } + cmake --build --preset ${{ env.PRESET }} --verbose -j 1; if(!$?) { Exit $LASTEXITCODE } + cmake --build --preset ${{ env.PRESET }} --verbose -j 1 --target install; if(!$?) { Exit $LASTEXITCODE } + mv install/${{ env.PRESET }} install/${{ env.PRESET }}-2; if(!$?) { Exit $LASTEXITCODE } + mv cpp_build/ cpp_build_2/; if(!$?) { Exit $LASTEXITCODE } + + - name: Compare builds + run: | + $hash1 = Get-ChildItem -Path install/${{ env.PRESET }}-1 -Recurse -File | Get-FileHash | Sort-Object Path + $hash2 = Get-ChildItem -Path install/${{ env.PRESET }}-2 -Recurse -File | Get-FileHash | Sort-Object Path + Compare-Object $hash1 $hash2 -Property Hash -PassThru -IncludeEqual | ForEach-Object { + $currentPath = $_.Path + + if ($_.SideIndicator -eq '==') { + Write-Host "Files match: $currentPath" + } else { + Write-Error "Build mismatch detected: $currentPath"; + Exit 1 + } + } + if ($LASTEXITCODE -eq 1) { Exit 1 } + Write-Host "Builds are identical" + + check-reproducibility-c-macos: + runs-on: macos-latest + env: + CMAKE_PREFIX_PATH: /usr/local + PRESET: ci-apple-clang-reproducible + + steps: + - uses: actions/checkout@v6 + + - name: Set up XCode + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: latest-stable + + - name: Install uv + uses: astral-sh/setup-uv@v7 + + - name: Install pgm-build-dependencies + run: | + uv tool install https://github.com/PowerGridModel/pgm-build-dependencies/releases/latest/download/pgm_build_dependencies-0.1.0-py3-none-any.whl + pgm-build-setup-ga-ci + + - name: Build twice + run: | + cmake --preset ${{ env.PRESET }} + cmake --build --preset ${{ env.PRESET }} --verbose -j 1 + cmake --build --preset ${{ env.PRESET }} --verbose -j 1 --target install + mv install/${{ env.PRESET }} install/${{ env.PRESET }}-1 + mv cpp_build/ cpp_build_1 + cmake --preset ${{ env.PRESET }} + cmake --build --preset ${{ env.PRESET }} --verbose -j 1 + cmake --build --preset ${{ env.PRESET }} --verbose -j 1 --target install + mv install/${{ env.PRESET }} install/${{ env.PRESET }}-2 + mv cpp_build/ cpp_build_2 + + - name: Compare builds + run: | + diff -qr install/${{ env.PRESET }}-1 install/${{ env.PRESET }}-2 + if [ $? -ne 0 ]; then + echo "Build mismatch detected" + exit 1 + fi + echo "Builds are identical" diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 799ed4481..785aa73e4 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -22,6 +22,9 @@ jobs: with: create_release: false + check-build-reproducibility: + uses: "./.github/workflows/check-build-reproducibility.yml" + check-code-quality: uses: "./.github/workflows/check-code-quality.yml" diff --git a/CMakePresets.json b/CMakePresets.json index 9e68e113f..577b6d497 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -1,5 +1,5 @@ { - "version": 3, + "version": 6, "configurePresets": [ { "name": "base", @@ -14,11 +14,14 @@ "environment": { "PLATFORM_C_FLAGS": "", "PLATFORM_CXX_FLAGS": "$env{PLATFORM_C_FLAGS}", + "PLATFORM_LINKER_FLAGS": "", "COMPILER_C_FLAGS": "", "COMPILER_CXX_FLAGS": "$env{COMPILER_C_FLAGS}", - "CFLAGS": "$penv{CFLAGS} $env{PLATFORM_C_FLAGS} $env{COMPILER_C_FLAGS} $env{COVERAGE_FLAGS} $env{SANITIZER_FLAGS}", - "CXXFLAGS": "$penv{CXXFLAGS} $env{PLATFORM_CXX_FLAGS} $env{COMPILER_CXX_FLAGS} $env{COVERAGE_FLAGS} $env{SANITIZER_FLAGS}", - "LDFLAGS": "$env{SANITIZER_FLAGS}" + "REPRODUCIBILITY_COMMON_FLAGS": "", + "REPRODUCIBILITY_LINKER_FLAGS": "", + "CFLAGS": "$penv{CFLAGS} $env{PLATFORM_C_FLAGS} $env{COMPILER_C_FLAGS} $env{COVERAGE_FLAGS} $env{SANITIZER_FLAGS} $env{REPRODUCIBILITY_COMMON_FLAGS}", + "CXXFLAGS": "$penv{CXXFLAGS} $env{PLATFORM_CXX_FLAGS} $env{COMPILER_CXX_FLAGS} $env{COVERAGE_FLAGS} $env{SANITIZER_FLAGS} $env{REPRODUCIBILITY_COMMON_FLAGS}", + "LDFLAGS": "$env{PLATFORM_LINKER_FLAGS} $env{SANITIZER_FLAGS} $env{REPRODUCIBILITY_LINKER_FLAGS} $env{REPRODUCIBILITY_LINKER_FLAGS}" }, "architecture": { "value": "x64", @@ -296,6 +299,54 @@ { "name": "ci-clang-tidy-release", "inherits": "clang-tidy-release" + }, + { + "name": "ci-msvc-reproducible", + "inherits": "msvc-release", + "environment": { + "REPRODUCIBILITY_COMMON_FLAGS": "/d1nodatetime /experimental:deterministic" + }, + "cacheVariables": { + "PGM_ENABLE_DEV_BUILD": "OFF", + "CMAKE_SHARED_LINKER_FLAGS": "/Brepro /INCREMENTAL:NO" + } + }, + { + "name": "ci-clang-cl-reproducible", + "inherits": "clang-cl-release", + "cacheVariables": { + "PGM_ENABLE_DEV_BUILD": "OFF", + "CMAKE_SHARED_LINKER_FLAGS": "/Brepro /INCREMENTAL:NO" + } + }, + { + "name": "ci-gcc-reproducible", + "inherits": "gcc-release", + "environment": { + "REPRODUCIBILITY_COMMON_FLAGS": "$penv{REPRODUCIBILITY_COMMON_FLAGS} -frandom-seed=0 -Wdate-time" + }, + "cacheVariables": { + "PGM_ENABLE_DEV_BUILD": "OFF" + } + }, + { + "name": "ci-clang-reproducible", + "inherits": "clang-release", + "environment": { + "REPRODUCIBILITY_COMMON_FLAGS": "$penv{REPRODUCIBILITY_COMMON_FLAGS} -frandom-seed=0 -Wdate-time" + }, + "cacheVariables": { + "PGM_ENABLE_DEV_BUILD": "OFF" + } + }, + { + "name": "ci-apple-clang-reproducible", + "inherits": "ci-clang-reproducible", + "environment": { + "SOURCE_DATE_EPOCH": "1", + "DSYMUTIL": "", + "REPRODUCIBILITY_LINKER_FLAGS": "-Wl,-no_uuid -Wl,-S" + } } ], "buildPresets": [ @@ -374,6 +425,26 @@ { "name": "ci-clang-tidy-debug", "configurePreset": "ci-clang-tidy-debug" + }, + { + "name": "ci-msvc-reproducible", + "configurePreset": "ci-msvc-reproducible" + }, + { + "name": "ci-clang-cl-reproducible", + "configurePreset": "ci-clang-cl-reproducible" + }, + { + "name": "ci-gcc-reproducible", + "configurePreset": "ci-gcc-reproducible" + }, + { + "name": "ci-clang-reproducible", + "configurePreset": "ci-clang-reproducible" + }, + { + "name": "ci-apple-clang-reproducible", + "configurePreset": "ci-apple-clang-reproducible" } ], "testPresets": [ @@ -453,5 +524,143 @@ "name": "ci-clang-tidy-debug", "configurePreset": "ci-clang-tidy-debug" } + ], + "workflowPresets": [ + { + "name": "msvc-debug", + "steps": [ + { + "type": "configure", + "name": "msvc-debug" + }, + { + "type": "build", + "name": "msvc-debug" + }, + { + "type": "test", + "name": "msvc-debug" + } + ] + }, + { + "name": "msvc-release", + "steps": [ + { + "type": "configure", + "name": "msvc-release" + }, + { + "type": "build", + "name": "msvc-release" + }, + { + "type": "test", + "name": "msvc-release" + } + ] + }, + { + "name": "clang-cl-debug", + "steps": [ + { + "type": "configure", + "name": "clang-cl-debug" + }, + { + "type": "build", + "name": "clang-cl-debug" + }, + { + "type": "test", + "name": "clang-cl-debug" + } + ] + }, + { + "name": "clang-cl-release", + "steps": [ + { + "type": "configure", + "name": "clang-cl-release" + }, + { + "type": "build", + "name": "clang-cl-release" + }, + { + "type": "test", + "name": "clang-cl-release" + } + ] + }, + { + "name": "gcc-debug", + "steps": [ + { + "type": "configure", + "name": "gcc-debug" + }, + { + "type": "build", + "name": "gcc-debug" + }, + { + "type": "test", + "name": "gcc-debug" + } + ] + }, + { + "name": "gcc-release", + "steps": [ + { + "type": "configure", + "name": "gcc-release" + }, + { + "type": "build", + "name": "gcc-release" + }, + { + "type": "test", + "name": "gcc-release" + } + ] + }, + { + "name": "clang-debug", + "steps": [ + { + "type": "configure", + "name": "clang-debug" + }, + { + "type": "build", + "name": "clang-debug" + }, + { + "type": "test", + "name": "clang-debug" + } + ] + }, + { + "name": "clang-release", + "steps": [ + { + "type": "configure", + "name": "clang-release" + }, + { + "type": "build", + "name": "clang-release" + }, + { + "type": "test", + "name": "clang-release" + } + ] + } ] } \ No newline at end of file