From db6d103827baed53d97217cc0b2bd8a52e6f06ef Mon Sep 17 00:00:00 2001 From: John Travers Date: Wed, 4 Nov 2026 20:56:25 +0000 Subject: [PATCH 1/7] Compile the CUDA and HIP targets in CI One job per vendor on a hosted runner, on a change to native/, with no device. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01XGENPtq9Gp7VCh5QmcDdzQ --- .github/workflows/device-compile.yml | 112 ++++++++++++++++++++++ changelog.d/151-device-compile.changed.md | 1 + native/AGENTS.md | 3 +- 3 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/device-compile.yml create mode 100644 changelog.d/151-device-compile.changed.md diff --git a/.github/workflows/device-compile.yml b/.github/workflows/device-compile.yml new file mode 100644 index 00000000..c84ee8fb --- /dev/null +++ b/.github/workflows/device-compile.yml @@ -0,0 +1,112 @@ +# SPDX-FileCopyrightText: 2026 John C. Travers and contributors +# +# SPDX-License-Identifier: GPL-3.0-or-later + +name: Device compile + +# One compile of each device target on a change to native/, on a hosted runner with no +# device, tests included: what breaks a user's first GPU build is found here and not when +# someone next rents a card. Nothing here runs on a device, and that stays by hand (#354). + +on: + pull_request: + paths: + - "native/**" + - ".github/workflows/device-compile.yml" + push: + branches: [main] + paths: + - "native/**" + - ".github/workflows/device-compile.yml" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: device-compile-${{ github.ref }} + cancel-in-progress: true + +jobs: + cuda: + name: CUDA 12.8 + runs-on: ubuntu-24.04 + timeout-minutes: 150 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + # The full history: the native version comes from the tags. + fetch-depth: 0 + - uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2 + with: + enable-cache: true + - name: Make room for the toolkit + run: | + sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc /usr/local/.ghcup + df -h / + - name: Install CUDA 12.8, the floor + run: | + curl -fsSL https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2404/x86_64/cuda-keyring_1.1-1_all.deb -o keyring.deb + sudo dpkg -i keyring.deb + sudo apt-get update + sudo apt-get install --yes --no-install-recommends cmake g++-14 libfftw3-dev libopenblas-dev ninja-build pkg-config cuda-compiler-12-8 cuda-libraries-dev-12-8 + df -h / + - run: uv python install 3.14 + - name: Configure + working-directory: native + env: + NVCC_WRAPPER_DEFAULT_COMPILER: g++-14 + run: | + export PATH="/usr/local/cuda-12.8/bin:$PATH" + cmake --preset cuda \ + -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_CUDA_HOST_COMPILER=g++-14 \ + -DCUDA_ROOT=/usr/local/cuda-12.8 \ + -DSOLPROP_NATIVE_BUILD_BENCHMARKS=OFF \ + -DPython_EXECUTABLE="$(uv python find 3.14)" + - name: Compile, tests included + working-directory: native + env: + NVCC_WRAPPER_DEFAULT_COMPILER: g++-14 + run: | + export PATH="/usr/local/cuda-12.8/bin:$PATH" + cmake --build --preset cuda --parallel 4 + df -h / + + hip: + name: HIP, ROCm 7.2 + runs-on: ubuntu-24.04 + timeout-minutes: 150 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + - uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2 + with: + enable-cache: true + - name: Make room for the toolkit + run: | + sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc /usr/local/.ghcup + df -h / + - name: Install ROCm + run: | + curl -fsSL https://repo.radeon.com/rocm/rocm.gpg.key | gpg --dearmor | sudo tee /etc/apt/keyrings/rocm.gpg > /dev/null + echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/rocm.gpg] https://repo.radeon.com/rocm/apt/7.2 noble main" | sudo tee /etc/apt/sources.list.d/rocm.list + sudo apt-get update + sudo apt-get install --yes --no-install-recommends cmake libfftw3-dev libopenblas-dev ninja-build pkg-config g++-14 hip-dev hipcc rocm-llvm rocm-device-libs rocm-cmake rocfft-dev rocblas-dev + df -h / + - run: uv python install 3.14 + - name: Configure + working-directory: native + run: | + export ROCM_PATH=/opt/rocm PATH="/opt/rocm/bin:$PATH" + cmake --preset hip \ + -DCMAKE_CXX_COMPILER="$ROCM_PATH/bin/hipcc" -DCMAKE_PREFIX_PATH="$ROCM_PATH" \ + -DCMAKE_CXX_SCAN_FOR_MODULES=OFF -DKokkos_ENABLE_ROCTHRUST=OFF \ + -DSOLPROP_NATIVE_BUILD_BENCHMARKS=OFF \ + -DPython_EXECUTABLE="$(uv python find 3.14)" + - name: Compile, tests included + working-directory: native + run: | + export ROCM_PATH=/opt/rocm PATH="/opt/rocm/bin:$PATH" + cmake --build --preset hip --parallel 4 + df -h / diff --git a/changelog.d/151-device-compile.changed.md b/changelog.d/151-device-compile.changed.md new file mode 100644 index 00000000..9f2a69f2 --- /dev/null +++ b/changelog.d/151-device-compile.changed.md @@ -0,0 +1 @@ +Continuous integration now compiles the CUDA and HIP targets on a change to `native/`, with no device, so a change that breaks a GPU build is found before it merges. (#151) diff --git a/native/AGENTS.md b/native/AGENTS.md index 63a2743b..15971835 100644 --- a/native/AGENTS.md +++ b/native/AGENTS.md @@ -88,7 +88,8 @@ the `native-quality` jobs run `native/scripts/check.sh` on Linux, one group of i each (`SOLPROP_NATIVE_CHECK_STAGES`). The `native-wheel` jobs build the CPU distribution wheel on Linux, macOS and Windows, cached under a hash of `native/`, and solprop's default lane runs on it on macOS and Windows with the native backend selected, and in -the coverage lane on Linux. GPU builds are validated by hand +the coverage lane on Linux. `.github/workflows/device-compile.yml` compiles the CUDA and HIP targets, tests included, +on a change to `native/`, with no device. Running on one stays by hand (`.github/workflows/cuda.yml` on a self-hosted runner, or `pytest -m gpu` on a machine with a device). From c2e0030f3864711b0d9d4e4e331fd82650dc46d8 Mon Sep 17 00:00:00 2001 From: John Travers Date: Wed, 4 Nov 2026 21:18:28 +0000 Subject: [PATCH 2/7] Find ROCm where it installs, and nanobind in the project's environment Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01XGENPtq9Gp7VCh5QmcDdzQ --- .github/workflows/device-compile.yml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/device-compile.yml b/.github/workflows/device-compile.yml index c84ee8fb..1127173f 100644 --- a/.github/workflows/device-compile.yml +++ b/.github/workflows/device-compile.yml @@ -52,6 +52,7 @@ jobs: sudo apt-get install --yes --no-install-recommends cmake g++-14 libfftw3-dev libopenblas-dev ninja-build pkg-config cuda-compiler-12-8 cuda-libraries-dev-12-8 df -h / - run: uv python install 3.14 + - run: uv sync --locked --no-install-package solprop-native - name: Configure working-directory: native env: @@ -62,7 +63,7 @@ jobs: -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_CUDA_HOST_COMPILER=g++-14 \ -DCUDA_ROOT=/usr/local/cuda-12.8 \ -DSOLPROP_NATIVE_BUILD_BENCHMARKS=OFF \ - -DPython_EXECUTABLE="$(uv python find 3.14)" + -DPython_EXECUTABLE="$GITHUB_WORKSPACE/.venv/bin/python" - name: Compile, tests included working-directory: native env: @@ -93,20 +94,24 @@ jobs: echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/rocm.gpg] https://repo.radeon.com/rocm/apt/7.2 noble main" | sudo tee /etc/apt/sources.list.d/rocm.list sudo apt-get update sudo apt-get install --yes --no-install-recommends cmake libfftw3-dev libopenblas-dev ninja-build pkg-config g++-14 hip-dev hipcc rocm-llvm rocm-device-libs rocm-cmake rocfft-dev rocblas-dev + # Under its version, with no fixed link at this set of packages. + hipcc_path="$(dpkg -L hipcc | grep -m 1 '/bin/hipcc$')" + echo "ROCM_PATH=$(dirname "$(dirname "$hipcc_path")")" >> "$GITHUB_ENV" df -h / - run: uv python install 3.14 + - run: uv sync --locked --no-install-package solprop-native - name: Configure working-directory: native run: | - export ROCM_PATH=/opt/rocm PATH="/opt/rocm/bin:$PATH" + export PATH="$ROCM_PATH/bin:$PATH" cmake --preset hip \ -DCMAKE_CXX_COMPILER="$ROCM_PATH/bin/hipcc" -DCMAKE_PREFIX_PATH="$ROCM_PATH" \ -DCMAKE_CXX_SCAN_FOR_MODULES=OFF -DKokkos_ENABLE_ROCTHRUST=OFF \ -DSOLPROP_NATIVE_BUILD_BENCHMARKS=OFF \ - -DPython_EXECUTABLE="$(uv python find 3.14)" + -DPython_EXECUTABLE="$GITHUB_WORKSPACE/.venv/bin/python" - name: Compile, tests included working-directory: native run: | - export ROCM_PATH=/opt/rocm PATH="/opt/rocm/bin:$PATH" + export PATH="$ROCM_PATH/bin:$PATH" cmake --build --preset hip --parallel 4 df -h / From 651944ced1d32f414a7e313eba249a881867b900 Mon Sep 17 00:00:00 2001 From: John Travers Date: Wed, 4 Nov 2026 21:19:23 +0000 Subject: [PATCH 3/7] Take hipcc from AMD's repository, not Ubuntu's Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01XGENPtq9Gp7VCh5QmcDdzQ --- .github/workflows/device-compile.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/device-compile.yml b/.github/workflows/device-compile.yml index 1127173f..bcc18352 100644 --- a/.github/workflows/device-compile.yml +++ b/.github/workflows/device-compile.yml @@ -92,11 +92,12 @@ jobs: run: | curl -fsSL https://repo.radeon.com/rocm/rocm.gpg.key | gpg --dearmor | sudo tee /etc/apt/keyrings/rocm.gpg > /dev/null echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/rocm.gpg] https://repo.radeon.com/rocm/apt/7.2 noble main" | sudo tee /etc/apt/sources.list.d/rocm.list + # Ubuntu has an older hipcc whose version number sorts higher than AMD's. + printf 'Package: *\nPin: origin repo.radeon.com\nPin-Priority: 1001\n' | sudo tee /etc/apt/preferences.d/rocm sudo apt-get update sudo apt-get install --yes --no-install-recommends cmake libfftw3-dev libopenblas-dev ninja-build pkg-config g++-14 hip-dev hipcc rocm-llvm rocm-device-libs rocm-cmake rocfft-dev rocblas-dev - # Under its version, with no fixed link at this set of packages. - hipcc_path="$(dpkg -L hipcc | grep -m 1 '/bin/hipcc$')" - echo "ROCM_PATH=$(dirname "$(dirname "$hipcc_path")")" >> "$GITHUB_ENV" + echo "ROCM_PATH=/opt/rocm" >> "$GITHUB_ENV" + /opt/rocm/bin/hipcc --version df -h / - run: uv python install 3.14 - run: uv sync --locked --no-install-package solprop-native From ab7e945955c8e6e72e01bf9ec0e3c90c2aa9627b Mon Sep 17 00:00:00 2001 From: John Travers Date: Wed, 4 Nov 2026 21:43:07 +0000 Subject: [PATCH 4/7] Name a test's pair list in full, for nvcc A CUDA build with its tests stopped here: nvcc can't deduce a list whose later members are bare braces. Found by the new compile job. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01XGENPtq9Gp7VCh5QmcDdzQ --- native/tests/native/test_aperture.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/native/tests/native/test_aperture.cpp b/native/tests/native/test_aperture.cpp index b65c7be3..a518b184 100644 --- a/native/tests/native/test_aperture.cpp +++ b/native/tests/native/test_aperture.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -521,11 +522,13 @@ TEST(FarFieldPass, TheShapesAreTheirFormulas) { // The offset is X - x, so x >= X reads across <= 0, whichever side is asked for. EXPECT_EQ(native::far_field_offset(2.0, -3.0, 1.0), -5.0); const auto plane = outline(0.0, 0.0, 1.0, 1.0, 0.0); - for (const auto& [across, kept] : {std::pair{-1.0, 1.0}, - {-0.0, 1.0}, - {0.0, 1.0}, - {std::nextafter(0.0, 1.0), 0.0}, - {1.0, 0.0}}) { + // Named in full: nvcc can't deduce a list whose later members are bare braces. + for (const auto& [across, kept] : + std::initializer_list>{{-1.0, 1.0}, + {-0.0, 1.0}, + {0.0, 1.0}, + {std::nextafter(0.0, 1.0), 0.0}, + {1.0, 0.0}}) { SCOPED_TRACE(across); EXPECT_EQ(native::far_field_shaped_weight(across, 9.0, plane, native::far_field_half_plane_x_ge, 0), From de8105b73fd80151366cb22b9d4126632feb18d5 Mon Sep 17 00:00:00 2001 From: John Travers Date: Wed, 4 Nov 2026 22:18:11 +0000 Subject: [PATCH 5/7] Break each device build on purpose, to show its job can fail Reverted in the next commit (#151's test). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01XGENPtq9Gp7VCh5QmcDdzQ --- native/src/general.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/native/src/general.cpp b/native/src/general.cpp index 1a9d7364..ba14a5f5 100644 --- a/native/src/general.cpp +++ b/native/src/general.cpp @@ -67,6 +67,23 @@ template <> struct Kokkos::reduction_identity { namespace solprop_native { namespace { +// Deliberately broken, to show that the compile jobs can fail (#151). Reverted next. +#if defined(KOKKOS_ENABLE_CUDA) +template +void deliberately_broken_capture(const Kokkos::View& view) { + const Value amount{1}; + if constexpr (std::is_same_v) { + Kokkos::parallel_for( + "deliberately_broken", view.extent(0), + KOKKOS_LAMBDA(const std::size_t index) { view(index) = amount; }); + } +} +[[maybe_unused]] void deliberately_broken_use(const Kokkos::View& view) { + deliberately_broken_capture(view); +} +#elif defined(KOKKOS_ENABLE_HIP) +void deliberately_broken_unused() { const int unused = 1; } +#endif // Sets a coupled flow aside for a stage evaluation and puts it back however the scope // is left: the Adams sample is the material source alone (solprop SEP 16 phase 5). From a35c4d1d2a6a6ed370cd4af64b0e7fced4f84b57 Mon Sep 17 00:00:00 2001 From: John Travers Date: Wed, 4 Nov 2026 22:39:18 +0000 Subject: [PATCH 6/7] Make the device compiles lanes of CI, and relax warnings for users Under ci-passed, so that a broken device build blocks a merge, and only on a change to native/. A build with its tests stays strict, and so do CI's wheels; a build from source doesn't stop on a compiler's warning. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01XGENPtq9Gp7VCh5QmcDdzQ --- .github/workflows/ci.yml | 106 +++++++++++++++++++ .github/workflows/device-compile.yml | 118 ---------------------- changelog.d/151-device-compile.changed.md | 2 +- docs/dev/tooling.md | 4 + docs/guide/performance/building-native.md | 2 +- native/AGENTS.md | 4 +- native/CMakeLists.txt | 4 +- native/pyproject.toml | 2 +- native/scripts/check.sh | 2 +- 9 files changed, 119 insertions(+), 125 deletions(-) delete mode 100644 .github/workflows/device-compile.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 155f7872..d4033698 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,6 +42,7 @@ jobs: pull-requests: read outputs: lanes: ${{ steps.diff.outputs.lanes }} + device: ${{ steps.diff.outputs.device }} steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -77,6 +78,12 @@ jobs: done fi echo "lanes=$lanes" | tee -a "$GITHUB_OUTPUT" + # Whether a device build could have changed; without a diff to read, it could. + device=true + if [ -f changed.txt ] && ! grep -vE '\.md$' changed.txt | grep -qE '^native/|^\.github/workflows/ci\.yml$'; then + device=false + fi + echo "device=$device" | tee -a "$GITHUB_OUTPUT" docs-tests: needs: [changes, native-wheel] @@ -567,6 +574,103 @@ jobs: - run: uv sync --locked --no-install-package solprop-native - run: uv run poe check-native + # One compile of each device target on a change to native/, on a hosted runner with no + # device, tests included: what breaks a user's first GPU build is found here and not + # when someone next rents a card. Nothing here runs on a device, which stays by hand + # (#354), and a fault only a card shows still needs one. + device-compile-cuda: + needs: changes + if: needs.changes.outputs.lanes == 'full' && needs.changes.outputs.device == 'true' + name: device-compile (CUDA 12.8) + runs-on: ubuntu-24.04 + timeout-minutes: 150 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + # The full history: the native version comes from the tags. + fetch-depth: 0 + - uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2 + with: + enable-cache: true + - name: Make room for the toolkit + run: | + sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc /usr/local/.ghcup + df -h / + - name: Install CUDA 12.8, the floor + run: | + curl -fsSL https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2404/x86_64/cuda-keyring_1.1-1_all.deb -o keyring.deb + sudo dpkg -i keyring.deb + sudo apt-get update + sudo apt-get install --yes --no-install-recommends cmake g++-14 libfftw3-dev libopenblas-dev ninja-build pkg-config cuda-compiler-12-8 cuda-libraries-dev-12-8 + df -h / + - run: uv python install 3.14 + - run: uv sync --locked --no-install-package solprop-native + - name: Configure + working-directory: native + env: + NVCC_WRAPPER_DEFAULT_COMPILER: g++-14 + run: | + export PATH="/usr/local/cuda-12.8/bin:$PATH" + cmake --preset cuda \ + -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_CUDA_HOST_COMPILER=g++-14 \ + -DCUDA_ROOT=/usr/local/cuda-12.8 \ + -DSOLPROP_NATIVE_BUILD_BENCHMARKS=OFF \ + -DPython_EXECUTABLE="$GITHUB_WORKSPACE/.venv/bin/python" + - name: Compile, tests included + working-directory: native + env: + NVCC_WRAPPER_DEFAULT_COMPILER: g++-14 + run: | + export PATH="/usr/local/cuda-12.8/bin:$PATH" + cmake --build --preset cuda --parallel 4 + df -h / + + device-compile-hip: + needs: changes + if: needs.changes.outputs.lanes == 'full' && needs.changes.outputs.device == 'true' + name: device-compile (HIP, ROCm 7.2) + runs-on: ubuntu-24.04 + timeout-minutes: 150 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + - uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2 + with: + enable-cache: true + - name: Make room for the toolkit + run: | + sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc /usr/local/.ghcup + df -h / + - name: Install ROCm + run: | + curl -fsSL https://repo.radeon.com/rocm/rocm.gpg.key | gpg --dearmor | sudo tee /etc/apt/keyrings/rocm.gpg > /dev/null + echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/rocm.gpg] https://repo.radeon.com/rocm/apt/7.2 noble main" | sudo tee /etc/apt/sources.list.d/rocm.list + # Ubuntu has an older hipcc whose version number sorts higher than AMD's. + printf 'Package: *\nPin: origin repo.radeon.com\nPin-Priority: 1001\n' | sudo tee /etc/apt/preferences.d/rocm + sudo apt-get update + sudo apt-get install --yes --no-install-recommends cmake libfftw3-dev libopenblas-dev ninja-build pkg-config g++-14 hip-dev hipcc rocm-llvm rocm-device-libs rocm-cmake rocfft-dev rocblas-dev + echo "ROCM_PATH=/opt/rocm" >> "$GITHUB_ENV" + /opt/rocm/bin/hipcc --version + df -h / + - run: uv python install 3.14 + - run: uv sync --locked --no-install-package solprop-native + - name: Configure + working-directory: native + run: | + export PATH="$ROCM_PATH/bin:$PATH" + cmake --preset hip \ + -DCMAKE_CXX_COMPILER="$ROCM_PATH/bin/hipcc" -DCMAKE_PREFIX_PATH="$ROCM_PATH" \ + -DCMAKE_CXX_SCAN_FOR_MODULES=OFF -DKokkos_ENABLE_ROCTHRUST=OFF \ + -DSOLPROP_NATIVE_BUILD_BENCHMARKS=OFF \ + -DPython_EXECUTABLE="$GITHUB_WORKSPACE/.venv/bin/python" + - name: Compile, tests included + working-directory: native + run: | + export PATH="$ROCM_PATH/bin:$PATH" + cmake --build --preset hip --parallel 4 + df -h / + # The front end's gate (`poe check-gui`: its types, docstrings and tests, the layers # without Qt held to 100 % coverage) on each desktop platform, with the widgets drawn # offscreen. Its lint, format, spelling and licence checks are the static job's. @@ -617,6 +721,8 @@ jobs: - oldest - no-native - native-quality + - device-compile-cuda + - device-compile-hip - gui runs-on: ubuntu-latest timeout-minutes: 5 diff --git a/.github/workflows/device-compile.yml b/.github/workflows/device-compile.yml deleted file mode 100644 index bcc18352..00000000 --- a/.github/workflows/device-compile.yml +++ /dev/null @@ -1,118 +0,0 @@ -# SPDX-FileCopyrightText: 2026 John C. Travers and contributors -# -# SPDX-License-Identifier: GPL-3.0-or-later - -name: Device compile - -# One compile of each device target on a change to native/, on a hosted runner with no -# device, tests included: what breaks a user's first GPU build is found here and not when -# someone next rents a card. Nothing here runs on a device, and that stays by hand (#354). - -on: - pull_request: - paths: - - "native/**" - - ".github/workflows/device-compile.yml" - push: - branches: [main] - paths: - - "native/**" - - ".github/workflows/device-compile.yml" - workflow_dispatch: - -permissions: - contents: read - -concurrency: - group: device-compile-${{ github.ref }} - cancel-in-progress: true - -jobs: - cuda: - name: CUDA 12.8 - runs-on: ubuntu-24.04 - timeout-minutes: 150 - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - # The full history: the native version comes from the tags. - fetch-depth: 0 - - uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2 - with: - enable-cache: true - - name: Make room for the toolkit - run: | - sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc /usr/local/.ghcup - df -h / - - name: Install CUDA 12.8, the floor - run: | - curl -fsSL https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2404/x86_64/cuda-keyring_1.1-1_all.deb -o keyring.deb - sudo dpkg -i keyring.deb - sudo apt-get update - sudo apt-get install --yes --no-install-recommends cmake g++-14 libfftw3-dev libopenblas-dev ninja-build pkg-config cuda-compiler-12-8 cuda-libraries-dev-12-8 - df -h / - - run: uv python install 3.14 - - run: uv sync --locked --no-install-package solprop-native - - name: Configure - working-directory: native - env: - NVCC_WRAPPER_DEFAULT_COMPILER: g++-14 - run: | - export PATH="/usr/local/cuda-12.8/bin:$PATH" - cmake --preset cuda \ - -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_CUDA_HOST_COMPILER=g++-14 \ - -DCUDA_ROOT=/usr/local/cuda-12.8 \ - -DSOLPROP_NATIVE_BUILD_BENCHMARKS=OFF \ - -DPython_EXECUTABLE="$GITHUB_WORKSPACE/.venv/bin/python" - - name: Compile, tests included - working-directory: native - env: - NVCC_WRAPPER_DEFAULT_COMPILER: g++-14 - run: | - export PATH="/usr/local/cuda-12.8/bin:$PATH" - cmake --build --preset cuda --parallel 4 - df -h / - - hip: - name: HIP, ROCm 7.2 - runs-on: ubuntu-24.04 - timeout-minutes: 150 - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - fetch-depth: 0 - - uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2 - with: - enable-cache: true - - name: Make room for the toolkit - run: | - sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc /usr/local/.ghcup - df -h / - - name: Install ROCm - run: | - curl -fsSL https://repo.radeon.com/rocm/rocm.gpg.key | gpg --dearmor | sudo tee /etc/apt/keyrings/rocm.gpg > /dev/null - echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/rocm.gpg] https://repo.radeon.com/rocm/apt/7.2 noble main" | sudo tee /etc/apt/sources.list.d/rocm.list - # Ubuntu has an older hipcc whose version number sorts higher than AMD's. - printf 'Package: *\nPin: origin repo.radeon.com\nPin-Priority: 1001\n' | sudo tee /etc/apt/preferences.d/rocm - sudo apt-get update - sudo apt-get install --yes --no-install-recommends cmake libfftw3-dev libopenblas-dev ninja-build pkg-config g++-14 hip-dev hipcc rocm-llvm rocm-device-libs rocm-cmake rocfft-dev rocblas-dev - echo "ROCM_PATH=/opt/rocm" >> "$GITHUB_ENV" - /opt/rocm/bin/hipcc --version - df -h / - - run: uv python install 3.14 - - run: uv sync --locked --no-install-package solprop-native - - name: Configure - working-directory: native - run: | - export PATH="$ROCM_PATH/bin:$PATH" - cmake --preset hip \ - -DCMAKE_CXX_COMPILER="$ROCM_PATH/bin/hipcc" -DCMAKE_PREFIX_PATH="$ROCM_PATH" \ - -DCMAKE_CXX_SCAN_FOR_MODULES=OFF -DKokkos_ENABLE_ROCTHRUST=OFF \ - -DSOLPROP_NATIVE_BUILD_BENCHMARKS=OFF \ - -DPython_EXECUTABLE="$GITHUB_WORKSPACE/.venv/bin/python" - - name: Compile, tests included - working-directory: native - run: | - export PATH="$ROCM_PATH/bin:$PATH" - cmake --build --preset hip --parallel 4 - df -h / diff --git a/changelog.d/151-device-compile.changed.md b/changelog.d/151-device-compile.changed.md index 9f2a69f2..835b6e50 100644 --- a/changelog.d/151-device-compile.changed.md +++ b/changelog.d/151-device-compile.changed.md @@ -1 +1 @@ -Continuous integration now compiles the CUDA and HIP targets on a change to `native/`, with no device, so a change that breaks a GPU build is found before it merges. (#151) +Continuous integration now compiles the CUDA and HIP targets on a change to `native/`, with no device, so a change that breaks a GPU build is found before it merges. A build from source no longer stops on a compiler's warning: `SOLPROP_NATIVE_WARNINGS_AS_ERRORS` now follows `BUILD_TESTING`, so it stays on for the presets and is off for `pip` and `uv` builds, while continuous integration keeps it on for its wheels. (#151) diff --git a/docs/dev/tooling.md b/docs/dev/tooling.md index 75ad0b27..683829aa 100644 --- a/docs/dev/tooling.md +++ b/docs/dev/tooling.md @@ -244,6 +244,10 @@ jaxtyping hook) run in the coverage lane only; the other test lanes set its lint and strict types, clang-format and its pytest suite (`lint pytest`); clang-tidy and the native tests (`tidy`); and ASan and UBSan (`sanitize`). The distribution wheel's stage is native-wheel's Linux job. +- **device-compile** (Linux, two jobs): on a change to `native/`, compiles the `cuda` + preset under CUDA 12.8 and the `hip` preset under ROCm 7.2, tests included, with warnings + as errors and no device. About 25 and 11 minutes, with no cache. A fault only a card + shows, or a test that fails on one, still needs a run on a device. - **gui** (Linux x86-64, macOS arm64, Windows x64): `uv run poe check-gui`, the front end's gate (its types against `gui/pyproject.toml`, its docstrings and its tests, with the layers without Qt held to 100 % coverage), after `uv sync --extra gui --group diff --git a/docs/guide/performance/building-native.md b/docs/guide/performance/building-native.md index 5da0482e..b2bff969 100644 --- a/docs/guide/performance/building-native.md +++ b/docs/guide/performance/building-native.md @@ -388,7 +388,7 @@ The options are read by `native/CMakeLists.txt` and `native/cmake/HostLibraries. | option | default | values and effect | |---|---|---| | `BUILD_TESTING` | `OFF` in pip and uv builds, `ON` in the presets | builds the GoogleTest native tests and registers them with CTest | -| `SOLPROP_NATIVE_WARNINGS_AS_ERRORS` | `ON` | `-Werror` on the package's own sources | +| `SOLPROP_NATIVE_WARNINGS_AS_ERRORS` | as `BUILD_TESTING`: `OFF` in pip and uv builds, `ON` in the presets | `-Werror` on the package's own sources. Continuous integration turns it on for its wheels too | | `SOLPROP_NATIVE_ENABLE_CLANG_TIDY` | `OFF` | runs clang-tidy while compiling the package's sources | | `SOLPROP_NATIVE_ENABLE_SANITIZERS` | `OFF` | AddressSanitizer and UBSan on the package's targets; CPU builds only | | `SOLPROP_NATIVE_BUILD_BENCHMARKS` | `OFF` | builds the `solprop_native_gemm_benchmark` microbenchmark (never installed) | diff --git a/native/AGENTS.md b/native/AGENTS.md index 15971835..4480e6a5 100644 --- a/native/AGENTS.md +++ b/native/AGENTS.md @@ -88,8 +88,8 @@ the `native-quality` jobs run `native/scripts/check.sh` on Linux, one group of i each (`SOLPROP_NATIVE_CHECK_STAGES`). The `native-wheel` jobs build the CPU distribution wheel on Linux, macOS and Windows, cached under a hash of `native/`, and solprop's default lane runs on it on macOS and Windows with the native backend selected, and in -the coverage lane on Linux. `.github/workflows/device-compile.yml` compiles the CUDA and HIP targets, tests included, -on a change to `native/`, with no device. Running on one stays by hand +the coverage lane on Linux. The `device-compile` lanes compile the CUDA and HIP targets, tests included, on a change to +`native/`, with no device. Running on one stays by hand (`.github/workflows/cuda.yml` on a self-hosted runner, or `pytest -m gpu` on a machine with a device). diff --git a/native/CMakeLists.txt b/native/CMakeLists.txt index 6e5aeca0..22a1d9ca 100644 --- a/native/CMakeLists.txt +++ b/native/CMakeLists.txt @@ -281,8 +281,10 @@ option(SOLPROP_NATIVE_ENABLE_AGGRESSIVE_VECTORIZATION "Assert independent Kokkos loop iterations to the vectorizer" ON) option(SOLPROP_NATIVE_ENABLE_IPO "Enable checked interprocedural/link-time optimization for CPU builds" ON) +# A build with its tests is development, and CI, and stops on a warning. A user's build +# from source isn't: one warning from a newer compiler shouldn't stop it. option(SOLPROP_NATIVE_WARNINGS_AS_ERRORS - "Treat diagnostics in project-owned C++ sources as errors" ON) + "Treat diagnostics in project-owned C++ sources as errors" ${BUILD_TESTING}) option(SOLPROP_NATIVE_ENABLE_CLANG_TIDY "Run clang-tidy while compiling project-owned C++ sources" OFF) option(SOLPROP_NATIVE_ENABLE_SANITIZERS diff --git a/native/pyproject.toml b/native/pyproject.toml index 18006419..971d903f 100644 --- a/native/pyproject.toml +++ b/native/pyproject.toml @@ -84,7 +84,7 @@ build = "cp314-*" skip = ["cp314t-*", "*-musllinux_*"] archs = ["auto64"] build-verbosity = 1 -config-settings = { "cmake.define.SOLPROP_NATIVE_PROFILE" = "DISTRIBUTION", "build-dir" = "build/cibw-{wheel_tag}" } +config-settings = { "cmake.define.SOLPROP_NATIVE_PROFILE" = "DISTRIBUTION", "cmake.define.SOLPROP_NATIVE_WARNINGS_AS_ERRORS" = "ON", "build-dir" = "build/cibw-{wheel_tag}" } # A first smoke test in cibuildwheel's test environment, beside solprop from the same # checkout (check_build reads solprop's kernel contract); the suite and the # coexistence check run in the workflow's test jobs. diff --git a/native/scripts/check.sh b/native/scripts/check.sh index 0d206031..ed30cd27 100755 --- a/native/scripts/check.sh +++ b/native/scripts/check.sh @@ -168,7 +168,7 @@ if ! stage wheel; then fi quality_temp=$(mktemp -d "${TMPDIR:-/tmp}/solprop-native-check.XXXXXX") trap 'rm -rf "$quality_temp"' EXIT -distribution_wheel=(-Ccmake.define.SOLPROP_NATIVE_PROFILE=DISTRIBUTION "-Cbuild-dir=build/distribution-{wheel_tag}") +distribution_wheel=(-Ccmake.define.SOLPROP_NATIVE_PROFILE=DISTRIBUTION -Ccmake.define.SOLPROP_NATIVE_WARNINGS_AS_ERRORS=ON "-Cbuild-dir=build/distribution-{wheel_tag}") if [[ -n "$qualification_python" ]]; then uv build --wheel --no-build-isolation "${distribution_wheel[@]}" --out-dir "$quality_temp/wheel" else From 4421d978556a44a773eb0d417755d35e34bd1e12 Mon Sep 17 00:00:00 2001 From: John Travers Date: Wed, 4 Nov 2026 22:39:18 +0000 Subject: [PATCH 7/7] Take out the deliberate breakage Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01XGENPtq9Gp7VCh5QmcDdzQ --- native/src/general.cpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/native/src/general.cpp b/native/src/general.cpp index ba14a5f5..1a9d7364 100644 --- a/native/src/general.cpp +++ b/native/src/general.cpp @@ -67,23 +67,6 @@ template <> struct Kokkos::reduction_identity { namespace solprop_native { namespace { -// Deliberately broken, to show that the compile jobs can fail (#151). Reverted next. -#if defined(KOKKOS_ENABLE_CUDA) -template -void deliberately_broken_capture(const Kokkos::View& view) { - const Value amount{1}; - if constexpr (std::is_same_v) { - Kokkos::parallel_for( - "deliberately_broken", view.extent(0), - KOKKOS_LAMBDA(const std::size_t index) { view(index) = amount; }); - } -} -[[maybe_unused]] void deliberately_broken_use(const Kokkos::View& view) { - deliberately_broken_capture(view); -} -#elif defined(KOKKOS_ENABLE_HIP) -void deliberately_broken_unused() { const int unused = 1; } -#endif // Sets a coupled flow aside for a stage evaluation and puts it back however the scope // is left: the Adams sample is the material source alone (solprop SEP 16 phase 5).