Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -617,6 +721,8 @@ jobs:
- oldest
- no-native
- native-quality
- device-compile-cuda
- device-compile-hip
- gui
runs-on: ubuntu-latest
timeout-minutes: 5
Expand Down
1 change: 1 addition & 0 deletions changelog.d/151-device-compile.changed.md
Original file line number Diff line number Diff line change
@@ -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. 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)
4 changes: 4 additions & 0 deletions docs/dev/tooling.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/performance/building-native.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) |
Expand Down
3 changes: 2 additions & 1 deletion native/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. 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).

Expand Down
4 changes: 3 additions & 1 deletion native/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion native/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion native/scripts/check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 8 additions & 5 deletions native/tests/native/test_aperture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <complex>
#include <cstddef>
#include <cstdint>
#include <initializer_list>
#include <limits>
#include <numbers>
#include <random>
Expand Down Expand Up @@ -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<std::pair<double, double>>{{-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),
Expand Down
Loading