From 4bc580d12cdf8af63476153e24c10f4ee1fb1595 Mon Sep 17 00:00:00 2001 From: "Victor A. P. Magri" Date: Wed, 9 Sep 2026 20:28:42 -0400 Subject: [PATCH 01/19] Add macOS CI build --- .github/workflows/docker_build_tpls.yml | 79 ++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker_build_tpls.yml b/.github/workflows/docker_build_tpls.yml index c5abfea8..a9940e43 100644 --- a/.github/workflows/docker_build_tpls.yml +++ b/.github/workflows/docker_build_tpls.yml @@ -292,10 +292,85 @@ jobs: echo "No local image to remove: ${image}" fi - # Convenience job - passes when all docker images are built. + # macOS TPLs must be built natively; Docker containers build Linux binaries. + build_macos: + name: macOS 15 - Apple Clang (arm64) + runs-on: macos-15 + defaults: + run: + shell: bash + env: + HOMEBREW_NO_ANALYTICS: 1 + + steps: + - name: Checkout + uses: actions/checkout@v7 + with: + submodules: true + lfs: true + + - name: Set up Python for Uberenv + uses: actions/setup-python@v6 + with: + python-version: '3.12' + + - name: Set up build environment + run: | + python3 -m pip install clingo + echo "SPACK_PYTHON=$(command -v python3)" >> "$GITHUB_ENV" + echo "GEOSX_TPL_DIR=$RUNNER_TEMP/geos-tpls" >> "$GITHUB_ENV" + echo "SPACK_CONFIG=$RUNNER_TEMP/geosx-macos-spack.yaml" >> "$GITHUB_ENV" + + - name: Test macOS dependency setup + run: bash scripts/tests/macos_homebrew/test_setupMacOS_TPL_deps.bash + + - name: Prepare Homebrew dependencies + run: | + # Refresh the disposable runner's preinstalled formulas before the + # macOS setup script checks them against the dependency manifest. + unset HOMEBREW_NO_INSTALL_UPGRADE HOMEBREW_NO_INSTALL_CLEANUP + brew update + python3 - <<'PY' + import json + import subprocess + + with open('scripts/spack_configs/macOS/homebrew-manifest.json') as stream: + formulae = [entry['name'] for entry in json.load(stream)['formulae']] + installed = subprocess.check_output(['brew', 'list', '--formula'], text=True).split() + if 'mpich' in installed: + formulae.remove('open-mpi') + subprocess.run(['brew', 'install', *formulae], check=True) + subprocess.run(['brew', 'cleanup', *formulae], check=True) + PY + bash scripts/setupMacOS-TPL-deps.bash --spack-config-out "$SPACK_CONFIG" \ + 2>&1 | tee "$RUNNER_TEMP/macos-setup.log" + + - name: Build macOS TPLs + run: | + python3 scripts/uberenv/uberenv.py \ + --project-json="$GITHUB_WORKSPACE/.uberenv_config.json" \ + --spack-env-file="$SPACK_CONFIG" \ + --prefix="$GEOSX_TPL_DIR" \ + --spec="~pygeosx ~docs ~openmp %c,cxx=apple-clang %fortran=gcc@16.2.0" \ + -j 2 2>&1 | tee "$RUNNER_TEMP/macos-build.log" + + - name: Upload macOS build logs and host configs + if: always() + uses: actions/upload-artifact@v4 + with: + name: macos-arm64-build + path: | + ${{ runner.temp }}/macos-*.log + ${{ runner.temp }}/geosx-macos-spack.yaml + ${{ runner.temp }}/geos-tpls/spack_env/spack.yaml + ${{ runner.temp }}/geos-tpls/spack_env/spack.lock + ${{ runner.temp }}/geos-tpls/build_stage/**/spack-build-out.txt + ${{ github.workspace }}/*.cmake + + # Convenience job - passes when all Docker images and macOS TPLs are built. check_that_all_images_built: runs-on: ubuntu-22.04 - needs: [build_images] + needs: [build_images, build_macos] steps: - name: Success run: "true" From 747b808f858c8dd766380b4e0f98ad48af763fc9 Mon Sep 17 00:00:00 2001 From: "Victor A. P. Magri" Date: Wed, 9 Sep 2026 21:26:54 -0400 Subject: [PATCH 02/19] Fix macOS MPI setup --- .github/workflows/docker_build_tpls.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker_build_tpls.yml b/.github/workflows/docker_build_tpls.yml index a9940e43..4d3964cc 100644 --- a/.github/workflows/docker_build_tpls.yml +++ b/.github/workflows/docker_build_tpls.yml @@ -337,7 +337,7 @@ jobs: with open('scripts/spack_configs/macOS/homebrew-manifest.json') as stream: formulae = [entry['name'] for entry in json.load(stream)['formulae']] installed = subprocess.check_output(['brew', 'list', '--formula'], text=True).split() - if 'mpich' in installed: + if 'mpich' in installed and 'open-mpi' in formulae: formulae.remove('open-mpi') subprocess.run(['brew', 'install', *formulae], check=True) subprocess.run(['brew', 'cleanup', *formulae], check=True) From 03a2a0abe52c0311035ea0b34832f1617da573b2 Mon Sep 17 00:00:00 2001 From: "Victor A. P. Magri" Date: Wed, 9 Sep 2026 21:32:37 -0400 Subject: [PATCH 03/19] Fix macOS formula install --- .github/workflows/docker_build_tpls.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/docker_build_tpls.yml b/.github/workflows/docker_build_tpls.yml index 4d3964cc..3e06061e 100644 --- a/.github/workflows/docker_build_tpls.yml +++ b/.github/workflows/docker_build_tpls.yml @@ -339,8 +339,7 @@ jobs: installed = subprocess.check_output(['brew', 'list', '--formula'], text=True).split() if 'mpich' in installed and 'open-mpi' in formulae: formulae.remove('open-mpi') - subprocess.run(['brew', 'install', *formulae], check=True) - subprocess.run(['brew', 'cleanup', *formulae], check=True) + subprocess.run(['brew', 'install', '--formula', *formulae], check=True) PY bash scripts/setupMacOS-TPL-deps.bash --spack-config-out "$SPACK_CONFIG" \ 2>&1 | tee "$RUNNER_TEMP/macos-setup.log" From e17d4d1ed63419b3821c0c701a380e00fd59ab7e Mon Sep 17 00:00:00 2001 From: "Victor A. P. Magri" Date: Wed, 9 Sep 2026 21:40:16 -0400 Subject: [PATCH 04/19] Update macOS pkgconf --- scripts/spack_configs/macOS/homebrew-manifest.json | 6 +++--- scripts/spack_configs/macOS/spack.yaml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/spack_configs/macOS/homebrew-manifest.json b/scripts/spack_configs/macOS/homebrew-manifest.json index c1dfddcc..dccad2a4 100644 --- a/scripts/spack_configs/macOS/homebrew-manifest.json +++ b/scripts/spack_configs/macOS/homebrew-manifest.json @@ -96,11 +96,11 @@ }, { "name": "pkgconf", - "brew_version": "3.0.6", - "formula_sha256": "53cae4d107ccc45a50359ea12428bddec0ecd0ef1fe8283a3ace27cd497ef4e0", + "brew_version": "3.0.7", + "formula_sha256": "7859e6f357dbdab1077d9bba38af2e409b9f3fc7c4c06aafdc5c3d81772b9ffb", "prefix": "/opt/homebrew/opt/pkgconf", "spack_package": "pkgconf", - "spack_version": "3.0.6", + "spack_version": "3.0.7", "required_paths": [ "bin/pkg-config", "bin/pkgconf" diff --git a/scripts/spack_configs/macOS/spack.yaml b/scripts/spack_configs/macOS/spack.yaml index 1c577927..2147d91b 100644 --- a/scripts/spack_configs/macOS/spack.yaml +++ b/scripts/spack_configs/macOS/spack.yaml @@ -80,7 +80,7 @@ spack: require: "openblas@=0.3.34" pkgconfig: buildable: false - require: "pkgconf@=3.0.6" + require: "pkgconf@=3.0.7" zlib-api: require: "zlib@=1.3.2" @@ -150,7 +150,7 @@ spack: pkgconf: buildable: false externals: - - spec: "pkgconf@=3.0.6 platform=darwin target=aarch64" + - spec: "pkgconf@=3.0.7 platform=darwin target=aarch64" prefix: /opt/homebrew/opt/pkgconf autoconf: From ea2f8ee2444b0f5ffb4065b769150e47df150194 Mon Sep 17 00:00:00 2001 From: "Victor A. P. Magri" Date: Wed, 23 Sep 2026 10:54:25 -0400 Subject: [PATCH 05/19] Refresh macOS Homebrew pins after formula drift. The macOS CI setup rejects exact formula checksums that no longer match Homebrew core, including readline 8.3.6 and automake 1.19. --- scripts/spack_configs/macOS/README.md | 2 +- .../macOS/homebrew-manifest.json | 32 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/scripts/spack_configs/macOS/README.md b/scripts/spack_configs/macOS/README.md index 14af9b0b..48ce8c71 100644 --- a/scripts/spack_configs/macOS/README.md +++ b/scripts/spack_configs/macOS/README.md @@ -114,7 +114,7 @@ dependency set with a clean TPL build and update the manifest and macOS Spack configuration together. The checked-in exact formula pins and source checksums come from the official -Homebrew formula API snapshot dated 2026-09-04 and are selected for +Homebrew formula API snapshot dated 2026-09-23 and are selected for qualification. They are not yet described as qualified until a clean TPL build and its smoke tests pass. The manifest records the exact host used to select them for traceability, but macOS patch/build revisions, Apple Clang build diff --git a/scripts/spack_configs/macOS/homebrew-manifest.json b/scripts/spack_configs/macOS/homebrew-manifest.json index dccad2a4..331747ae 100644 --- a/scripts/spack_configs/macOS/homebrew-manifest.json +++ b/scripts/spack_configs/macOS/homebrew-manifest.json @@ -2,7 +2,7 @@ "schema_version": 1, "source": { "description": "Official Homebrew formula API metadata and version policies selected for GEOS macOS TPL qualification", - "as_of": "2026-09-04" + "as_of": "2026-09-23" }, "supported_platform": { "os": "Darwin", @@ -23,7 +23,7 @@ { "name": "gcc", "brew_version": "16.2.0", - "formula_sha256": "0683955ef01d30162abfcfff3fe9b0e73eebba9a5f9920fb53cb94bc8cdc3f43", + "formula_sha256": "f09c8035d0874fb1d8a2d7ca3464f8108273c4c532cec582d7b9f340aa544aa3", "prefix": "/opt/homebrew/opt/gcc", "spack_package": "gcc", "spack_version": "16.2.0", @@ -34,7 +34,7 @@ { "name": "openblas", "brew_version": "0.3.34", - "formula_sha256": "b6c9d393f4c2a6ebe1b66354decd9d7da8ce66952f9e760454fd601689e2d89a", + "formula_sha256": "a9df5fa47015b4814c0ea8616127e248e48cc5a1136c1f33b13f5cdbe2da454e", "prefix": "/opt/homebrew/opt/openblas", "spack_package": "openblas", "spack_version": "0.3.34", @@ -63,7 +63,7 @@ "version_policy": "minimum", "minimum_version": "3.24", "brew_version": "4.4.3", - "formula_sha256": "55fab0cd335d245c13704744585837a4e593aad16335097fc554df712e2dba1b", + "formula_sha256": "b6640b40b96c513cfe79cb15f43c4b6de9b08386104bb9096c965ad02fd1f87f", "prefix": "/opt/homebrew/opt/cmake", "spack_package": "cmake", "spack_version": "4.4.3", @@ -73,8 +73,8 @@ }, { "name": "readline", - "brew_version": "8.3.3", - "formula_sha256": "660d4099f7dcd652c78f530672ffaf01103a00b3ee72aca4059f10c1038b1228", + "brew_version": "8.3.6", + "formula_sha256": "ead60941ba0f81334499b208e26382eefda15eb1b9614c417d70f1d827dc2e64", "prefix": "/opt/homebrew/opt/readline", "spack_package": "readline", "spack_version": "8.3", @@ -86,7 +86,7 @@ { "name": "m4", "brew_version": "1.4.21", - "formula_sha256": "79b4221c141d51a12b59824d0dd69e59c90ee582ca9451150ea973278e19fabf", + "formula_sha256": "4f77735ee9b10e73b569387e86f8c456c3d50d253d70325543dba1d6aba24675", "prefix": "/opt/homebrew/opt/m4", "spack_package": "m4", "spack_version": "1.4.21", @@ -97,7 +97,7 @@ { "name": "pkgconf", "brew_version": "3.0.7", - "formula_sha256": "7859e6f357dbdab1077d9bba38af2e409b9f3fc7c4c06aafdc5c3d81772b9ffb", + "formula_sha256": "aa4787b4c8af3925054fb55ae38c9ce16cea5325ce5788e2d1c6cec514f30f5c", "prefix": "/opt/homebrew/opt/pkgconf", "spack_package": "pkgconf", "spack_version": "3.0.7", @@ -109,7 +109,7 @@ { "name": "autoconf", "brew_version": "2.73", - "formula_sha256": "036d4f18fa1b9072705af36ebc9c8d02700853c0149e04feeb0fc2232aecab96", + "formula_sha256": "cab75402095f9a65be6a5d3fc11a2f7a6a7fdf04189abee37bfc1c6e5adff499", "prefix": "/opt/homebrew/opt/autoconf", "spack_package": "autoconf", "spack_version": "2.73", @@ -119,8 +119,8 @@ }, { "name": "automake", - "brew_version": "1.18.1_1", - "formula_sha256": "187e1f49c7831094765bc2630eef07270d5080670f67b3b4a610391cb73bd233", + "brew_version": "1.19", + "formula_sha256": "706ee509ee3d3a4bc05999b0695f0998cddedeb7d534699bf721ebb0ee4ec487", "prefix": "/opt/homebrew/opt/automake", "spack_package": "automake", "spack_version": "1.18.1", @@ -131,7 +131,7 @@ { "name": "libtool", "brew_version": "2.6.2", - "formula_sha256": "1b21318c46a9bba74d7013aef9d76ee0144f1b747aceecaa7ba92438462c3a79", + "formula_sha256": "5513e09178e3dccafd9cbb3e24c1af5c4aee5d40769256a3963ef8b234c35a39", "prefix": "/opt/homebrew/opt/libtool", "spack_package": "libtool", "spack_version": "2.6.2", @@ -143,7 +143,7 @@ { "name": "gettext", "brew_version": "1.0", - "formula_sha256": "bd0322f7114c68dd627c974ec16ceaa6295bf68eac55995ee2dfb59ce6fefd51", + "formula_sha256": "9c90f7cf5a45c828e2a66d9339b25b781302cf49316e939d029e6079e1b9d916", "prefix": "/opt/homebrew/opt/gettext", "spack_package": "gettext", "spack_version": "1.0", @@ -155,7 +155,7 @@ { "name": "binutils", "brew_version": "2.47", - "formula_sha256": "f1fd62bf787bec32ca4f0bf872372bddd6432f0db24b5c5d6c68fb0b340d047e", + "formula_sha256": "8f4e28410aec61265d8c13578741a617423f9289fdb0ef1dbff8368eceb1bff7", "prefix": "/opt/homebrew/opt/binutils", "spack_package": "binutils", "spack_version": "2.47", @@ -166,7 +166,7 @@ { "name": "python@3.14", "brew_version": "3.14.7", - "formula_sha256": "e7e3a023de8b88f5a7e2e09c861e9e15fb0d0a2d78b6bb619a1b27e7da155b9a", + "formula_sha256": "0165e2f65d6b237155d0fb089a6b27259fd85e0fa7582c6ee293df587346cfee", "prefix": "/opt/homebrew/opt/python@3.14", "spack_package": "python", "spack_version": "3.14.7", @@ -178,7 +178,7 @@ "name": "perl", "version_policy": "any", "brew_version": "5.44.0", - "formula_sha256": "19d8fe283a3699d99d4a81d521b2cf5317f5e8ad728319138518b2e803e70dcb", + "formula_sha256": "8549ff64704f61aa09cf20f19861111db8f28798c1ef2129fb31ec481447effa", "prefix": "/opt/homebrew/opt/perl", "spack_package": "perl", "spack_version": "5.44.0", From f680faaa153b0e3887d9a16f4d7c71d38c359a13 Mon Sep 17 00:00:00 2001 From: "Victor A. P. Magri" Date: Wed, 23 Sep 2026 13:41:08 -0400 Subject: [PATCH 06/19] Run macOS artifact upload on Node.js 24. actions/upload-artifact@v4 still targets Node.js 20, which GitHub Actions now warns about. --- .github/workflows/docker_build_tpls.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker_build_tpls.yml b/.github/workflows/docker_build_tpls.yml index 3e06061e..66051c0a 100644 --- a/.github/workflows/docker_build_tpls.yml +++ b/.github/workflows/docker_build_tpls.yml @@ -355,7 +355,7 @@ jobs: - name: Upload macOS build logs and host configs if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: macos-arm64-build path: | From c4123ba0ba2a7cceb6d21588dc825cd2ba046d88 Mon Sep 17 00:00:00 2001 From: "Victor A. P. Magri" Date: Wed, 23 Sep 2026 15:15:42 -0400 Subject: [PATCH 07/19] Stop failing macOS setup when Homebrew rewrites a formula file. A formula-file checksum changes on bottle rebuilds even when the installed version does not. Version drift and missing required files still fail the job. --- scripts/setupMacOS-TPL-deps.bash | 26 ++++++++------ scripts/spack_configs/macOS/README.md | 36 +++++++++++-------- .../test_setupMacOS_TPL_deps.bash | 8 +++-- 3 files changed, 42 insertions(+), 28 deletions(-) diff --git a/scripts/setupMacOS-TPL-deps.bash b/scripts/setupMacOS-TPL-deps.bash index cf2e3fd6..27d9732f 100755 --- a/scripts/setupMacOS-TPL-deps.bash +++ b/scripts/setupMacOS-TPL-deps.bash @@ -54,11 +54,12 @@ usage() Usage: scripts/setupMacOS-TPL-deps.bash [options] Validate the Homebrew dependency set recorded in the checked-in manifest. Exact -pins and minimum-version policies are applied as declared. The manifest's host -and platform versions describe the qualification host; they are not exact -host-version gates. By default, missing formulas are installed only after the -complete preflight succeeds. Exact-pinned formula drift is never upgraded or -downgraded. +version pins and minimum-version policies are applied as declared. The +manifest's host and platform versions describe the qualification host; they are +not exact host-version gates. A rewritten Homebrew formula file with the same +version is reported and accepted. By default, missing formulas are installed +only after the complete preflight succeeds. Exact-pinned version drift is never +upgraded or downgraded. Options: --check-only Validate without installing anything. @@ -546,10 +547,16 @@ formula_metadata_matches() record_error "Formula '${name}' metadata drift: expected version '${expected_version}', found '${actual_version}'" return 1 fi - if [[ "${actual_sha}" != "${expected_sha}" ]]; then - record_error "Formula '${name}' source drift: expected checksum '${expected_sha}', found '${actual_sha}'" + # ruby_source_checksum hashes the Homebrew formula file. Core rewrites + # that file for bottle rebuilds without changing the installed version. + # A missing checksum is incomplete metadata, not that rewrite. + if [[ -z "${actual_sha}" ]]; then + record_error "Formula '${name}' is missing ruby_source_checksum" return 1 fi + if [[ "${actual_sha}" != "${expected_sha}" ]]; then + echo "INFO: Formula '${name}' formula file checksum differs from the qualification snapshot '${expected_sha}'; version '${actual_version}' still matches" + fi ;; minimum) if ! version_at_least "${actual_version}" "${minimum_version}"; then @@ -727,9 +734,8 @@ preflight_formulae() i=$((i + 1)) continue fi - # Check source identity for exact-pinned formulas even when already - # installed. This makes stale API caches and silently rewritten formulas - # visible drift. + # Check the live formula version even when the formula is already + # installed. A rewritten formula file with the same version is informational. formula_metadata_matches "${i}" || true validate_formula_installation "${i}" true || true i=$((i + 1)) diff --git a/scripts/spack_configs/macOS/README.md b/scripts/spack_configs/macOS/README.md index 48ce8c71..73aec7fe 100644 --- a/scripts/spack_configs/macOS/README.md +++ b/scripts/spack_configs/macOS/README.md @@ -8,9 +8,11 @@ built by Spack. The setup is fail-closed for the dependency set. It does not install Homebrew, update Homebrew, upgrade or downgrade an installed formula, or silently accept -a changed exact-pinned formula definition. The macOS, SDK, Apple Clang, and +a changed exact-pinned formula version. The macOS, SDK, Apple Clang, and Homebrew versions recorded in the manifest identify the qualification host; -they are reported for traceability but are not exact host-version gates. +they are reported for traceability but are not exact host-version gates. A +rewritten Homebrew formula file with the same version is also reported and +accepted. ## One-time prerequisite @@ -23,7 +25,7 @@ eval "$(brew shellenv)" The dependency script discovers Homebrew through `PATH` (and also checks the standard Apple Silicon and Intel install paths). It validates exact versions -and source checksums for the exact-pinned formulas in the manifest. No GEOS tap +for the exact-pinned formulas in the manifest. No GEOS tap or other Homebrew tap is required. CMake only has to satisfy the project minimum of `3.24`; an installed `mpich` or `open-mpi` satisfies the MPI requirement. @@ -74,8 +76,8 @@ Installation occurs only when all of the following preflight checks pass: - the host is Darwin/arm64 and the Apple Command Line Tools are available; - CMake is at least `3.24`, and either `mpich` or `open-mpi` is available; -- Homebrew reports the exact stable formula version, formula revision, and Ruby - source checksum for the exact-pinned formulas in the manifest; +- Homebrew reports the exact stable formula version and formula revision for + the exact-pinned formulas in the manifest; - every formula already installed has the exact receipt version and prefix; - every required executable, header, and library from an installed formula is present. @@ -87,8 +89,9 @@ metadata, receipts, prefixes, and required paths. A partial or changed installation is an error. The script exports `HOMEBREW_NO_AUTO_UPDATE=1`, so the formula metadata visible -to the invoked Homebrew is authoritative for that run. A stale local API cache -is reported as drift instead of being mistaken for the tested formula set. +to the invoked Homebrew is authoritative for that run. A version that differs +from an exact pin is rejected. A formula-file checksum that differs while the +version still matches is printed and does not fail the run. The generated Spack environment selects the installed MPI provider and its concrete version. It also selects `/usr/bin/ar` and @@ -113,25 +116,28 @@ upgrade`, an unreviewed downgrade, or `--force` linking. Qualify the newer dependency set with a clean TPL build and update the manifest and macOS Spack configuration together. -The checked-in exact formula pins and source checksums come from the official +The checked-in exact formula versions and source checksums come from the official Homebrew formula API snapshot dated 2026-09-23 and are selected for qualification. They are not yet described as qualified until a clean TPL build and its smoke tests pass. The manifest records the exact host used to select them for traceability, but macOS patch/build revisions, Apple Clang build revisions, Apple Clang versions, CMake versions at or above `3.24`, MPI provider -choice, and Homebrew executable patch releases are informational rather than -support gates. Homebrew-managed transitive dependencies are not separate Spack -externals; the post-install executable and link-library checks are the local -compatibility guard for this boundary. +choice, Homebrew executable patch releases, and later rewrites of a formula +file that keep the same version are informational rather than support gates. +Homebrew-managed transitive dependencies are not separate Spack externals; the +post-install executable and link-library checks are the local compatibility +guard for this boundary. ## Updating the manifest Treat a manifest change as a toolchain change: 1. obtain exact-pinned formula versions (`stable`, plus `_` when the - formula revision is nonzero) and `ruby_source_checksum.sha256` from - `brew info --json=v2` or the official formula API; keep CMake at or above - `3.24` and keep either `mpich` or `open-mpi` available; + formula revision is nonzero) from `brew info --json=v2` or the official + formula API when that version changes; record `ruby_source_checksum.sha256` + for the qualification snapshot, but do not treat a later formula-file + rewrite as a failure; keep CMake at or above `3.24` and keep either `mpich` + or `open-mpi` available; 2. update the matching external version and prefix in the macOS Spack environment; 3. run `scripts/tests/macos_homebrew/test_setupMacOS_TPL_deps.bash`; diff --git a/scripts/tests/macos_homebrew/test_setupMacOS_TPL_deps.bash b/scripts/tests/macos_homebrew/test_setupMacOS_TPL_deps.bash index b3a40479..09e33a5f 100755 --- a/scripts/tests/macos_homebrew/test_setupMacOS_TPL_deps.bash +++ b/scripts/tests/macos_homebrew/test_setupMacOS_TPL_deps.bash @@ -518,8 +518,8 @@ create_fake_tools write_manifest test_committed_manifest -# Host releases newer than the recorded qualification host are accepted. The -# Homebrew formula versions and source checksums remain exact. +# Host releases newer than the recorded qualification host are accepted. Exact +# formula versions remain gates. Formula-file checksum rewrites do not. reset_state expect_success "qualification-host macOS/SDK versions accept patch drift" run_setup --check-only @@ -695,7 +695,9 @@ expect_failure "formula version metadata drift is rejected" run_setup reset_state set_candidate beta 2.0 1 cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc -expect_failure "formula source checksum drift is rejected" run_setup +expect_success "formula file checksum drift does not fail the build" run_setup +assert_file_contains "checksum drift is reported" \ + "${LAST_OUTPUT}" "formula file checksum differs from the qualification snapshot" reset_state rm -f "${FAKE_PREFIX}/opt/beta/bin/beta-tool" From 71031a969ed909aa24db9229d842226dba3f69ad Mon Sep 17 00:00:00 2001 From: "Victor A. P. Magri" Date: Wed, 23 Sep 2026 19:08:38 -0400 Subject: [PATCH 08/19] Build macOS TPLs on the macOS 26 arm64 runner. The qualification host is already macOS 26, and the GitHub macos-26 label is the standard arm64 image. --- .github/workflows/docker_build_tpls.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker_build_tpls.yml b/.github/workflows/docker_build_tpls.yml index 66051c0a..b0461b4e 100644 --- a/.github/workflows/docker_build_tpls.yml +++ b/.github/workflows/docker_build_tpls.yml @@ -294,8 +294,8 @@ jobs: # macOS TPLs must be built natively; Docker containers build Linux binaries. build_macos: - name: macOS 15 - Apple Clang (arm64) - runs-on: macos-15 + name: macOS 26 - Apple Clang (arm64) + runs-on: macos-26 defaults: run: shell: bash From ca02a2ba2cec4bee283e9d98d3b434ad44020f2c Mon Sep 17 00:00:00 2001 From: "Victor A. P. Magri" Date: Wed, 23 Sep 2026 20:53:30 -0400 Subject: [PATCH 09/19] Publish the macOS TPL install as a tagged image. --- .github/workflows/docker_build_tpls.yml | 45 ++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/.github/workflows/docker_build_tpls.yml b/.github/workflows/docker_build_tpls.yml index b0461b4e..549fcfca 100644 --- a/.github/workflows/docker_build_tpls.yml +++ b/.github/workflows/docker_build_tpls.yml @@ -317,9 +317,14 @@ jobs: - name: Set up build environment run: | python3 -m pip install clingo + short_sha=${GITHUB_SHA:0:7} + docker_tag="${{ github.event.number }}-${{ github.run_number }}" echo "SPACK_PYTHON=$(command -v python3)" >> "$GITHUB_ENV" - echo "GEOSX_TPL_DIR=$RUNNER_TEMP/geos-tpls" >> "$GITHUB_ENV" + echo "DOCKER_TAG=${docker_tag}" >> "$GITHUB_ENV" + echo "GEOSX_TPL_DIR=/opt/GEOS/GEOS_TPL-${docker_tag}-${short_sha}" >> "$GITHUB_ENV" echo "SPACK_CONFIG=$RUNNER_TEMP/geosx-macos-spack.yaml" >> "$GITHUB_ENV" + sudo mkdir -p /opt/GEOS + sudo chown "$(id -un)" /opt/GEOS - name: Test macOS dependency setup run: bash scripts/tests/macos_homebrew/test_setupMacOS_TPL_deps.bash @@ -353,6 +358,39 @@ jobs: --spec="~pygeosx ~docs ~openmp %c,cxx=apple-clang %fortran=gcc@16.2.0" \ -j 2 2>&1 | tee "$RUNNER_TEMP/macos-build.log" + - name: Publish macOS TPL image + run: | + set -euo pipefail + host_config=$(find "$GITHUB_WORKSPACE" -maxdepth 1 -name '*.cmake' -print -quit) + test -n "$host_config" + cp "$host_config" /opt/GEOS/spack-generated.cmake + # Drop the Spack checkout and build trees. Package prefixes recorded in + # the host-config stay under GEOSX_TPL_DIR, matching the Linux images. + rm -rf \ + "$GEOSX_TPL_DIR/bin" \ + "$GEOSX_TPL_DIR/build_stage" \ + "$GEOSX_TPL_DIR/builtin_spack_packages_repo" \ + "$GEOSX_TPL_DIR/misc_cache" \ + "$GEOSX_TPL_DIR/spack" \ + "$GEOSX_TPL_DIR/spack_env" \ + "$GEOSX_TPL_DIR/.spack-db" + tpl_name=$(basename "$GEOSX_TPL_DIR") + cat > "$RUNNER_TEMP/Dockerfile" < Date: Wed, 23 Sep 2026 20:59:42 -0400 Subject: [PATCH 10/19] Export GEOS_TPL_DIR from the TPL images. --- .github/workflows/docker_build_tpls.yml | 22 +++++++++++----------- docker/Stanford/Dockerfile | 10 +++++----- docker/TotalEnergies/Dockerfile | 10 +++++----- docker/configure-tpl.sh | 6 +++--- docker/tpl-rockylinux.Dockerfile | 12 ++++++------ docker/tpl-ubuntu-hip.Dockerfile | 16 ++++++++-------- docker/tpl-ubuntu.Dockerfile | 12 ++++++------ 7 files changed, 44 insertions(+), 44 deletions(-) diff --git a/.github/workflows/docker_build_tpls.yml b/.github/workflows/docker_build_tpls.yml index 549fcfca..5375b4d6 100644 --- a/.github/workflows/docker_build_tpls.yml +++ b/.github/workflows/docker_build_tpls.yml @@ -321,7 +321,7 @@ jobs: docker_tag="${{ github.event.number }}-${{ github.run_number }}" echo "SPACK_PYTHON=$(command -v python3)" >> "$GITHUB_ENV" echo "DOCKER_TAG=${docker_tag}" >> "$GITHUB_ENV" - echo "GEOSX_TPL_DIR=/opt/GEOS/GEOS_TPL-${docker_tag}-${short_sha}" >> "$GITHUB_ENV" + echo "GEOS_TPL_DIR=/opt/GEOS/GEOS_TPL-${docker_tag}-${short_sha}" >> "$GITHUB_ENV" echo "SPACK_CONFIG=$RUNNER_TEMP/geosx-macos-spack.yaml" >> "$GITHUB_ENV" sudo mkdir -p /opt/GEOS sudo chown "$(id -un)" /opt/GEOS @@ -354,7 +354,7 @@ jobs: python3 scripts/uberenv/uberenv.py \ --project-json="$GITHUB_WORKSPACE/.uberenv_config.json" \ --spack-env-file="$SPACK_CONFIG" \ - --prefix="$GEOSX_TPL_DIR" \ + --prefix="$GEOS_TPL_DIR" \ --spec="~pygeosx ~docs ~openmp %c,cxx=apple-clang %fortran=gcc@16.2.0" \ -j 2 2>&1 | tee "$RUNNER_TEMP/macos-build.log" @@ -365,16 +365,16 @@ jobs: test -n "$host_config" cp "$host_config" /opt/GEOS/spack-generated.cmake # Drop the Spack checkout and build trees. Package prefixes recorded in - # the host-config stay under GEOSX_TPL_DIR, matching the Linux images. + # the host-config stay under GEOS_TPL_DIR, matching the Linux images. rm -rf \ - "$GEOSX_TPL_DIR/bin" \ - "$GEOSX_TPL_DIR/build_stage" \ - "$GEOSX_TPL_DIR/builtin_spack_packages_repo" \ - "$GEOSX_TPL_DIR/misc_cache" \ - "$GEOSX_TPL_DIR/spack" \ - "$GEOSX_TPL_DIR/spack_env" \ - "$GEOSX_TPL_DIR/.spack-db" - tpl_name=$(basename "$GEOSX_TPL_DIR") + "$GEOS_TPL_DIR/bin" \ + "$GEOS_TPL_DIR/build_stage" \ + "$GEOS_TPL_DIR/builtin_spack_packages_repo" \ + "$GEOS_TPL_DIR/misc_cache" \ + "$GEOS_TPL_DIR/spack" \ + "$GEOS_TPL_DIR/spack_env" \ + "$GEOS_TPL_DIR/.spack-db" + tpl_name=$(basename "$GEOS_TPL_DIR") cat > "$RUNNER_TEMP/Dockerfile" <&2 ; \ @@ -149,7 +149,7 @@ RUN --mount=src=.,dst=$SRC_DIR,readwrite cd ${SRC_DIR} && \ --spec '${GEOSX_SPEC}' \ --spack-env-file=${GEOSX_SPACK_ENV_FILE} \ --project-json=${SRC_DIR}/.uberenv_config.json \ - --prefix ${GEOSX_TPL_DIR} \ + --prefix ${GEOS_TPL_DIR} \ -j ${SPACK_BUILD_JOBS} \ -k " ; \ else \ @@ -157,19 +157,19 @@ RUN --mount=src=.,dst=$SRC_DIR,readwrite cd ${SRC_DIR} && \ --spec "${GEOSX_SPEC}" \ --spack-env-file=${GEOSX_SPACK_ENV_FILE} \ --project-json=${SRC_DIR}/.uberenv_config.json \ - --prefix ${GEOSX_TPL_DIR} \ + --prefix ${GEOS_TPL_DIR} \ -j ${SPACK_BUILD_JOBS} \ -k ; \ fi && \ rm -f lvarray* && \ cp *.cmake /spack-generated.cmake && \ - cd ${GEOSX_TPL_DIR} && \ + cd ${GEOS_TPL_DIR} && \ rm -rf bin/ build_stage/ builtin_spack_packages_repo/ misc_cache/ spack/ spack_env/ .spack-db/ # ----- Final GEOS-build image ----- FROM tpl_toolchain_intersect_geosx_toolchain AS geosx_toolchain ARG SRC_DIR -COPY --from=tpl_toolchain $GEOSX_TPL_DIR $GEOSX_TPL_DIR +COPY --from=tpl_toolchain $GEOS_TPL_DIR $GEOS_TPL_DIR COPY --from=tpl_toolchain /spack-generated.cmake / RUN dnf -y install \ diff --git a/docker/tpl-ubuntu-hip.Dockerfile b/docker/tpl-ubuntu-hip.Dockerfile index 10879e0d..b7783b4e 100644 --- a/docker/tpl-ubuntu-hip.Dockerfile +++ b/docker/tpl-ubuntu-hip.Dockerfile @@ -28,9 +28,9 @@ ENV OPENSSL_FORCE_FIPS_MODE=0 \ OPENSSL_CONF=/etc/ssl/openssl-non-fips.cnf # Install directory provided as a docker build argument; forwarded via ENV -# (GEOSX_TPL_DIR is part of the image contract consumed by GEOS). +# (GEOS_TPL_DIR is part of the image contract consumed by GEOS). ARG INSTALL_DIR -ENV GEOSX_TPL_DIR=$INSTALL_DIR +ENV GEOS_TPL_DIR=$INSTALL_DIR # ROCm parameters ARG AMDGPU_TARGET=gfx942 @@ -166,7 +166,7 @@ RUN apt-get update && \ # for hours and spack is otherwise silent, which makes a stalled build # indistinguishable from a slow one in CI logs. RUN --mount=src=.,dst=$SRC_DIR,readwrite cd ${SRC_DIR} && \ - mkdir -p ${GEOSX_TPL_DIR} && \ + mkdir -p ${GEOS_TPL_DIR} && \ GEOSX_SPEC="${SPEC}" && \ if [ -z "${GEOSX_SPEC}" ] || [ "${GEOSX_SPEC}" = "undefined" ]; then \ echo "ERROR: SPEC build-arg must be supplied" >&2 ; \ @@ -176,12 +176,12 @@ RUN --mount=src=.,dst=$SRC_DIR,readwrite cd ${SRC_DIR} && \ ( while true; do \ sleep 60; \ echo "[heartbeat] $(date -Iseconds) uberenv/spack still running"; \ - find ${GEOSX_TPL_DIR}/build_stage -maxdepth 2 -mindepth 2 -type d -printf '%T@ %p\n' 2>/dev/null | \ + find ${GEOS_TPL_DIR}/build_stage -maxdepth 2 -mindepth 2 -type d -printf '%T@ %p\n' 2>/dev/null | \ sort -nr | head -n 3 | \ while read -r _ path; do \ echo "[heartbeat] recent stage dir: ${path}"; \ done; \ - find ${GEOSX_TPL_DIR}/build_stage -maxdepth 4 \( -name spack-build-out.txt -o -name spack-build-env.txt -o -name spack-configure-args.txt \) -printf '%T@ %p\n' 2>/dev/null | \ + find ${GEOS_TPL_DIR}/build_stage -maxdepth 4 \( -name spack-build-out.txt -o -name spack-build-env.txt -o -name spack-configure-args.txt \) -printf '%T@ %p\n' 2>/dev/null | \ sort -nr | head -n 3 | \ while read -r _ path; do \ echo "[heartbeat] recent stage file: ${path}"; \ @@ -199,7 +199,7 @@ RUN --mount=src=.,dst=$SRC_DIR,readwrite cd ${SRC_DIR} && \ --spec "${GEOSX_SPEC}" \ --spack-env-file=${SRC_DIR}/docker/spack-rocm.yaml \ --project-json=${SRC_DIR}/.uberenv_config.json \ - --prefix ${GEOSX_TPL_DIR} \ + --prefix ${GEOS_TPL_DIR} \ -j ${SPACK_BUILD_JOBS} \ -k; \ rc=$?; \ @@ -209,7 +209,7 @@ RUN --mount=src=.,dst=$SRC_DIR,readwrite cd ${SRC_DIR} && \ } && \ rm -f lvarray* && \ cp *.cmake /spack-generated.cmake && \ - cd ${GEOSX_TPL_DIR} && \ + cd ${GEOS_TPL_DIR} && \ rm -rf bin/ build_stage/ builtin_spack_packages_repo/ misc_cache/ spack/ spack_env/ .spack-db/ # ----- Final GEOS-build image ----- @@ -228,7 +228,7 @@ ARG ROCM_VERSION # it is actually needed. ENV OPENSSL_CONF="" -COPY --from=tpl_toolchain $GEOSX_TPL_DIR $GEOSX_TPL_DIR +COPY --from=tpl_toolchain $GEOS_TPL_DIR $GEOS_TPL_DIR # Extract the generated host-config COPY --from=tpl_toolchain /spack-generated.cmake / diff --git a/docker/tpl-ubuntu.Dockerfile b/docker/tpl-ubuntu.Dockerfile index 542fc29b..e40aaa68 100644 --- a/docker/tpl-ubuntu.Dockerfile +++ b/docker/tpl-ubuntu.Dockerfile @@ -31,9 +31,9 @@ ENV OPENSSL_FORCE_FIPS_MODE=0 \ OPENSSL_CONF=/etc/ssl/openssl-non-fips.cnf # Install directory provided as a docker build argument; forwarded via ENV -# (GEOSX_TPL_DIR is part of the image contract consumed by GEOS). +# (GEOS_TPL_DIR is part of the image contract consumed by GEOS). ARG INSTALL_DIR -ENV GEOSX_TPL_DIR=$INSTALL_DIR +ENV GEOS_TPL_DIR=$INSTALL_DIR # Packages needed both for the TPL build and for the downstream GEOS build. # We avoid reinstalling anything already present in the base image (compiler, @@ -162,7 +162,7 @@ RUN apt-get update && \ # Run uberenv. The SPEC is supplied by the matrix because the spack toolchain # tag depends on the compiler+version baked into the base image. RUN --mount=src=.,dst=$SRC_DIR,readwrite cd ${SRC_DIR} && \ - mkdir -p ${GEOSX_TPL_DIR} && \ + mkdir -p ${GEOS_TPL_DIR} && \ GEOSX_SPEC="${SPEC}" && \ if [ -z "${GEOSX_SPEC}" ] || [ "${GEOSX_SPEC}" = "undefined" ]; then \ echo "ERROR: SPEC build-arg must be supplied" >&2 ; \ @@ -179,12 +179,12 @@ RUN --mount=src=.,dst=$SRC_DIR,readwrite cd ${SRC_DIR} && \ --spec "${GEOSX_SPEC}" \ --spack-env-file=${GEOSX_SPACK_ENV_FILE} \ --project-json=${SRC_DIR}/.uberenv_config.json \ - --prefix ${GEOSX_TPL_DIR} \ + --prefix ${GEOS_TPL_DIR} \ -j ${SPACK_BUILD_JOBS} \ -k && \ rm -f lvarray* && \ cp *.cmake /spack-generated.cmake && \ - cd ${GEOSX_TPL_DIR} && \ + cd ${GEOS_TPL_DIR} && \ rm -rf bin/ build_stage/ builtin_spack_packages_repo/ misc_cache/ spack/ spack_env/ .spack-db/ # ----- Final GEOS-build image ----- @@ -199,7 +199,7 @@ ARG SRC_DIR # it is actually needed. ENV OPENSSL_CONF="" -COPY --from=tpl_toolchain $GEOSX_TPL_DIR $GEOSX_TPL_DIR +COPY --from=tpl_toolchain $GEOS_TPL_DIR $GEOS_TPL_DIR COPY --from=tpl_toolchain /spack-generated.cmake / RUN apt-get update && \ From 3e54716076ea738d4fa720db45a658c0c7a53d16 Mon Sep 17 00:00:00 2001 From: "Victor A. P. Magri" Date: Wed, 23 Sep 2026 21:09:02 -0400 Subject: [PATCH 11/19] Fix the workflow parse error that skipped the image check. --- .github/workflows/docker_build_tpls.yml | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docker_build_tpls.yml b/.github/workflows/docker_build_tpls.yml index 5375b4d6..c832fd43 100644 --- a/.github/workflows/docker_build_tpls.yml +++ b/.github/workflows/docker_build_tpls.yml @@ -375,11 +375,11 @@ jobs: "$GEOS_TPL_DIR/spack_env" \ "$GEOS_TPL_DIR/.spack-db" tpl_name=$(basename "$GEOS_TPL_DIR") - cat > "$RUNNER_TEMP/Dockerfile" < "$RUNNER_TEMP/Dockerfile" docker build -f "$RUNNER_TEMP/Dockerfile" -t "geosx/macos-26-apple-clang:${DOCKER_TAG}" /opt/GEOS - name: Login to DockerHub @@ -405,6 +405,11 @@ EOF check_that_all_images_built: runs-on: ubuntu-22.04 needs: [build_images, build_macos] + if: always() steps: - name: Success - run: "true" + run: | + echo "build_images: ${{ needs.build_images.result }}" + echo "build_macos: ${{ needs.build_macos.result }}" + test "${{ needs.build_images.result }}" = success + test "${{ needs.build_macos.result }}" = success From 7e52d5ba1fe471a1ba311c78d00b4fac781aab3f Mon Sep 17 00:00:00 2001 From: "Victor A. P. Magri" Date: Wed, 23 Sep 2026 21:10:49 -0400 Subject: [PATCH 12/19] Build the macOS TPL image in the build_images matrix. --- .github/workflows/docker_build_tpls.yml | 116 +++++++++++------------- 1 file changed, 55 insertions(+), 61 deletions(-) diff --git a/.github/workflows/docker_build_tpls.yml b/.github/workflows/docker_build_tpls.yml index c832fd43..83651218 100644 --- a/.github/workflows/docker_build_tpls.yml +++ b/.github/workflows/docker_build_tpls.yml @@ -173,6 +173,14 @@ jobs: SPACK_BUILD_JOBS: 8 SPEC: "+rocm ~uncrustify ~openmp ~pygeosx ~docs ~trilinos ~petsc amdgpu_target=gfx942 %%amdclang-19 ^caliper~papi~gotcha~sampler~libunwind~libdw" + # Built natively. The published image carries the macOS prefix; it is + # not a Linux container produced by docker-build.sh. + - name: macOS 26 - Apple Clang (arm64) + RUNS_ON: macos-26 + DOCKER_REPOSITORY: geosx/macos-26-apple-clang + SPEC: "~pygeosx ~docs ~openmp %c,cxx=apple-clang %fortran=gcc@16.2.0" + SPACK_BUILD_JOBS: 2 + steps: - name: Checkout uses: actions/checkout@v7 @@ -182,7 +190,7 @@ jobs: - name: Set up Docker Buildx id: buildx_hosted - if: matrix.RUNS_ON != 'streak2' + if: matrix.RUNS_ON != 'streak2' && matrix.RUNS_ON != 'macos-26' uses: docker/setup-buildx-action@v4 with: cleanup: true @@ -200,6 +208,7 @@ jobs: - name: Compose DOCKER_BASE_IMAGE id: base + if: matrix.RUNS_ON != 'macos-26' run: | # Rows that do not build on a docker_base_images image (e.g. ROCm) # pin their base image directly. @@ -264,57 +273,18 @@ jobs: DOCKER_BUILDER: ${{ steps.buildx_streak2.outputs.name || steps.buildx_hosted.outputs.name }} DOCKER_LOAD: 1 DOCKER_NETWORK: ${{ matrix.RUNS_ON == 'streak2' && 'host' || '' }} + if: matrix.RUNS_ON != 'macos-26' run: bash -x ./scripts/docker-build.sh - - - name: Login to DockerHub - uses: docker/login-action@v4 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - logout: ${{ matrix.RUNS_ON != 'streak2' }} - - - name: Push docker image - env: - DOCKER_REPOSITORY: ${{ matrix.DOCKER_REPOSITORY }} - DOCKER_TAG: ${{ github.event.number }}-${{ github.run_number }} - run: docker push ${DOCKER_REPOSITORY}:${DOCKER_TAG} - - - name: Remove local Docker image - if: always() && matrix.RUNS_ON == 'streak2' - env: - DOCKER_REPOSITORY: ${{ matrix.DOCKER_REPOSITORY }} - DOCKER_TAG: ${{ github.event.number }}-${{ github.run_number }} - run: | - image="${DOCKER_REPOSITORY}:${DOCKER_TAG}" - if docker image inspect "${image}" >/dev/null 2>&1; then - docker image rm "${image}" || echo "::warning::Failed to remove local image ${image}" - else - echo "No local image to remove: ${image}" - fi - - # macOS TPLs must be built natively; Docker containers build Linux binaries. - build_macos: - name: macOS 26 - Apple Clang (arm64) - runs-on: macos-26 - defaults: - run: - shell: bash - env: - HOMEBREW_NO_ANALYTICS: 1 - - steps: - - name: Checkout - uses: actions/checkout@v7 - with: - submodules: true - lfs: true - - name: Set up Python for Uberenv + if: matrix.RUNS_ON == 'macos-26' uses: actions/setup-python@v6 with: python-version: '3.12' - - name: Set up build environment + - name: Set up macOS build environment + if: matrix.RUNS_ON == 'macos-26' + env: + HOMEBREW_NO_ANALYTICS: 1 run: | python3 -m pip install clingo short_sha=${GITHUB_SHA:0:7} @@ -327,12 +297,14 @@ jobs: sudo chown "$(id -un)" /opt/GEOS - name: Test macOS dependency setup + if: matrix.RUNS_ON == 'macos-26' run: bash scripts/tests/macos_homebrew/test_setupMacOS_TPL_deps.bash - name: Prepare Homebrew dependencies + if: matrix.RUNS_ON == 'macos-26' + env: + HOMEBREW_NO_ANALYTICS: 1 run: | - # Refresh the disposable runner's preinstalled formulas before the - # macOS setup script checks them against the dependency manifest. unset HOMEBREW_NO_INSTALL_UPGRADE HOMEBREW_NO_INSTALL_CLEANUP brew update python3 - <<'PY' @@ -350,22 +322,29 @@ jobs: 2>&1 | tee "$RUNNER_TEMP/macos-setup.log" - name: Build macOS TPLs + if: matrix.RUNS_ON == 'macos-26' + env: + HOMEBREW_NO_ANALYTICS: 1 + SPEC: ${{ matrix.SPEC }} + SPACK_BUILD_JOBS: ${{ matrix.SPACK_BUILD_JOBS }} run: | python3 scripts/uberenv/uberenv.py \ --project-json="$GITHUB_WORKSPACE/.uberenv_config.json" \ --spack-env-file="$SPACK_CONFIG" \ --prefix="$GEOS_TPL_DIR" \ - --spec="~pygeosx ~docs ~openmp %c,cxx=apple-clang %fortran=gcc@16.2.0" \ - -j 2 2>&1 | tee "$RUNNER_TEMP/macos-build.log" + --spec="$SPEC" \ + -j "$SPACK_BUILD_JOBS" 2>&1 | tee "$RUNNER_TEMP/macos-build.log" - - name: Publish macOS TPL image + - name: Package macOS TPL image + if: matrix.RUNS_ON == 'macos-26' + env: + DOCKER_REPOSITORY: ${{ matrix.DOCKER_REPOSITORY }} + DOCKER_TAG: ${{ github.event.number }}-${{ github.run_number }} run: | set -euo pipefail host_config=$(find "$GITHUB_WORKSPACE" -maxdepth 1 -name '*.cmake' -print -quit) test -n "$host_config" cp "$host_config" /opt/GEOS/spack-generated.cmake - # Drop the Spack checkout and build trees. Package prefixes recorded in - # the host-config stay under GEOS_TPL_DIR, matching the Linux images. rm -rf \ "$GEOS_TPL_DIR/bin" \ "$GEOS_TPL_DIR/build_stage" \ @@ -380,19 +359,36 @@ jobs: "COPY ${tpl_name} /opt/GEOS/${tpl_name}" \ 'COPY spack-generated.cmake /spack-generated.cmake' \ > "$RUNNER_TEMP/Dockerfile" - docker build -f "$RUNNER_TEMP/Dockerfile" -t "geosx/macos-26-apple-clang:${DOCKER_TAG}" /opt/GEOS + docker build -f "$RUNNER_TEMP/Dockerfile" -t "${DOCKER_REPOSITORY}:${DOCKER_TAG}" /opt/GEOS - name: Login to DockerHub uses: docker/login-action@v4 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} + logout: ${{ matrix.RUNS_ON != 'streak2' }} - - name: Push macOS TPL image - run: docker push "geosx/macos-26-apple-clang:${DOCKER_TAG}" + - name: Push docker image + env: + DOCKER_REPOSITORY: ${{ matrix.DOCKER_REPOSITORY }} + DOCKER_TAG: ${{ github.event.number }}-${{ github.run_number }} + run: docker push ${DOCKER_REPOSITORY}:${DOCKER_TAG} + + - name: Remove local Docker image + if: always() && matrix.RUNS_ON == 'streak2' + env: + DOCKER_REPOSITORY: ${{ matrix.DOCKER_REPOSITORY }} + DOCKER_TAG: ${{ github.event.number }}-${{ github.run_number }} + run: | + image="${DOCKER_REPOSITORY}:${DOCKER_TAG}" + if docker image inspect "${image}" >/dev/null 2>&1; then + docker image rm "${image}" || echo "::warning::Failed to remove local image ${image}" + else + echo "No local image to remove: ${image}" + fi - name: Upload macOS build logs and host configs - if: always() + if: always() && matrix.RUNS_ON == 'macos-26' uses: actions/upload-artifact@v7 with: name: macos-arm64-build @@ -401,15 +397,13 @@ jobs: ${{ runner.temp }}/geosx-macos-spack.yaml /opt/GEOS/spack-generated.cmake - # Convenience job - passes when all Docker images and macOS TPLs are built. + # Convenience job - passes when every build_images row, including macOS, succeeds. check_that_all_images_built: runs-on: ubuntu-22.04 - needs: [build_images, build_macos] + needs: [build_images] if: always() steps: - name: Success run: | echo "build_images: ${{ needs.build_images.result }}" - echo "build_macos: ${{ needs.build_macos.result }}" test "${{ needs.build_images.result }}" = success - test "${{ needs.build_macos.result }}" = success From cfa0f55f4c5cb481c21753e8fa65c86753f7fb05 Mon Sep 17 00:00:00 2001 From: "Victor A. P. Magri" Date: Wed, 23 Sep 2026 22:23:39 -0400 Subject: [PATCH 13/19] Start Docker on the macOS runner before packaging the TPL image. --- .github/workflows/docker_build_tpls.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/docker_build_tpls.yml b/.github/workflows/docker_build_tpls.yml index 83651218..1868c314 100644 --- a/.github/workflows/docker_build_tpls.yml +++ b/.github/workflows/docker_build_tpls.yml @@ -335,6 +335,13 @@ jobs: --spec="$SPEC" \ -j "$SPACK_BUILD_JOBS" 2>&1 | tee "$RUNNER_TEMP/macos-build.log" + - name: Install Docker for the macOS image + if: matrix.RUNS_ON == 'macos-26' + run: | + brew install docker colima + colima start --cpu 2 --memory 4 --disk 60 + docker info + - name: Package macOS TPL image if: matrix.RUNS_ON == 'macos-26' env: From 5a6741f5a5c669d81919bffb25946efd91f74df5 Mon Sep 17 00:00:00 2001 From: "Victor A. P. Magri" Date: Wed, 23 Sep 2026 22:39:30 -0400 Subject: [PATCH 14/19] Use generic names for the shared image build steps. --- .github/workflows/docker_build_tpls.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/docker_build_tpls.yml b/.github/workflows/docker_build_tpls.yml index 1868c314..d1fe2922 100644 --- a/.github/workflows/docker_build_tpls.yml +++ b/.github/workflows/docker_build_tpls.yml @@ -281,7 +281,7 @@ jobs: with: python-version: '3.12' - - name: Set up macOS build environment + - name: Set up build environment if: matrix.RUNS_ON == 'macos-26' env: HOMEBREW_NO_ANALYTICS: 1 @@ -296,11 +296,11 @@ jobs: sudo mkdir -p /opt/GEOS sudo chown "$(id -un)" /opt/GEOS - - name: Test macOS dependency setup + - name: Test dependency setup if: matrix.RUNS_ON == 'macos-26' run: bash scripts/tests/macos_homebrew/test_setupMacOS_TPL_deps.bash - - name: Prepare Homebrew dependencies + - name: Prepare dependencies if: matrix.RUNS_ON == 'macos-26' env: HOMEBREW_NO_ANALYTICS: 1 @@ -321,7 +321,7 @@ jobs: bash scripts/setupMacOS-TPL-deps.bash --spack-config-out "$SPACK_CONFIG" \ 2>&1 | tee "$RUNNER_TEMP/macos-setup.log" - - name: Build macOS TPLs + - name: Build TPLs if: matrix.RUNS_ON == 'macos-26' env: HOMEBREW_NO_ANALYTICS: 1 @@ -335,14 +335,14 @@ jobs: --spec="$SPEC" \ -j "$SPACK_BUILD_JOBS" 2>&1 | tee "$RUNNER_TEMP/macos-build.log" - - name: Install Docker for the macOS image + - name: Install Docker if: matrix.RUNS_ON == 'macos-26' run: | brew install docker colima colima start --cpu 2 --memory 4 --disk 60 docker info - - name: Package macOS TPL image + - name: Package TPL image if: matrix.RUNS_ON == 'macos-26' env: DOCKER_REPOSITORY: ${{ matrix.DOCKER_REPOSITORY }} @@ -394,7 +394,7 @@ jobs: echo "No local image to remove: ${image}" fi - - name: Upload macOS build logs and host configs + - name: Upload build logs and host configs if: always() && matrix.RUNS_ON == 'macos-26' uses: actions/upload-artifact@v7 with: From 570b21d3d3ecf0a6b1ba0d7e0a4cf6f1b922cb5a Mon Sep 17 00:00:00 2001 From: "Victor A. P. Magri" Date: Thu, 24 Sep 2026 02:00:09 -0400 Subject: [PATCH 15/19] Publish the macOS TPL image without a local Docker daemon. --- .github/workflows/docker_build_tpls.yml | 36 ++++++++++++++++--------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/.github/workflows/docker_build_tpls.yml b/.github/workflows/docker_build_tpls.yml index d1fe2922..e2809d9e 100644 --- a/.github/workflows/docker_build_tpls.yml +++ b/.github/workflows/docker_build_tpls.yml @@ -335,23 +335,26 @@ jobs: --spec="$SPEC" \ -j "$SPACK_BUILD_JOBS" 2>&1 | tee "$RUNNER_TEMP/macos-build.log" - - name: Install Docker + - name: Prepare image publish if: matrix.RUNS_ON == 'macos-26' run: | - brew install docker colima - colima start --cpu 2 --memory 4 --disk 60 - docker info + set -euo pipefail + curl -fsSL -o "$RUNNER_TEMP/crane.tgz" \ + https://github.com/google/go-containerregistry/releases/download/v0.20.6/go-containerregistry_Darwin_arm64.tar.gz + tar -xzf "$RUNNER_TEMP/crane.tgz" -C "$RUNNER_TEMP" crane + "$RUNNER_TEMP/crane" version - name: Package TPL image if: matrix.RUNS_ON == 'macos-26' env: DOCKER_REPOSITORY: ${{ matrix.DOCKER_REPOSITORY }} DOCKER_TAG: ${{ github.event.number }}-${{ github.run_number }} + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} run: | set -euo pipefail - host_config=$(find "$GITHUB_WORKSPACE" -maxdepth 1 -name '*.cmake' -print -quit) + host_config=$(find "$GITHUB_WORKSPACE" -maxdepth 1 -name '*.cmake' | head -n 1) test -n "$host_config" - cp "$host_config" /opt/GEOS/spack-generated.cmake rm -rf \ "$GEOS_TPL_DIR/bin" \ "$GEOS_TPL_DIR/build_stage" \ @@ -361,14 +364,20 @@ jobs: "$GEOS_TPL_DIR/spack_env" \ "$GEOS_TPL_DIR/.spack-db" tpl_name=$(basename "$GEOS_TPL_DIR") - printf '%s\n' \ - 'FROM scratch' \ - "COPY ${tpl_name} /opt/GEOS/${tpl_name}" \ - 'COPY spack-generated.cmake /spack-generated.cmake' \ - > "$RUNNER_TEMP/Dockerfile" - docker build -f "$RUNNER_TEMP/Dockerfile" -t "${DOCKER_REPOSITORY}:${DOCKER_TAG}" /opt/GEOS + stage="$RUNNER_TEMP/image-root" + mkdir -p "$stage/opt/GEOS" + mv "$GEOS_TPL_DIR" "$stage/opt/GEOS/$tpl_name" + cp "$host_config" "$stage/spack-generated.cmake" + "$RUNNER_TEMP/crane" auth login index.docker.io \ + -u "$DOCKER_USERNAME" -p "$DOCKER_PASSWORD" + image="${DOCKER_REPOSITORY}:${DOCKER_TAG}" + ref=$("$RUNNER_TEMP/crane" append --platform linux/arm64 \ + -f <(tar -C "$stage" -cf - .) -t "$image") + "$RUNNER_TEMP/crane" mutate "$ref" \ + -e "GEOS_TPL_DIR=/opt/GEOS/${tpl_name}" -t "$image" - name: Login to DockerHub + if: matrix.RUNS_ON != 'macos-26' uses: docker/login-action@v4 with: username: ${{ secrets.DOCKER_USERNAME }} @@ -376,6 +385,7 @@ jobs: logout: ${{ matrix.RUNS_ON != 'streak2' }} - name: Push docker image + if: matrix.RUNS_ON != 'macos-26' env: DOCKER_REPOSITORY: ${{ matrix.DOCKER_REPOSITORY }} DOCKER_TAG: ${{ github.event.number }}-${{ github.run_number }} @@ -402,7 +412,7 @@ jobs: path: | ${{ runner.temp }}/macos-*.log ${{ runner.temp }}/geosx-macos-spack.yaml - /opt/GEOS/spack-generated.cmake + ${{ runner.temp }}/image-root/spack-generated.cmake # Convenience job - passes when every build_images row, including macOS, succeeds. check_that_all_images_built: From 7da60cb85eee9d5342490e58d3598ddc1af76d4d Mon Sep 17 00:00:00 2001 From: "Victor A. P. Magri" Date: Thu, 24 Sep 2026 17:28:30 -0400 Subject: [PATCH 16/19] Package the GEOS host-config instead of the LvArray one. --- .github/workflows/docker_build_tpls.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker_build_tpls.yml b/.github/workflows/docker_build_tpls.yml index e2809d9e..1bb5e9c4 100644 --- a/.github/workflows/docker_build_tpls.yml +++ b/.github/workflows/docker_build_tpls.yml @@ -353,7 +353,7 @@ jobs: DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} run: | set -euo pipefail - host_config=$(find "$GITHUB_WORKSPACE" -maxdepth 1 -name '*.cmake' | head -n 1) + host_config=$(find "$GITHUB_WORKSPACE" -maxdepth 1 -name '*.cmake' ! -name 'lvarray-*' | head -n 1) test -n "$host_config" rm -rf \ "$GEOS_TPL_DIR/bin" \ From ac6480e1ebbd978ace9e8a3727f557179f56f0e1 Mon Sep 17 00:00:00 2001 From: "Victor A. P. Magri" Date: Thu, 24 Sep 2026 22:06:47 -0400 Subject: [PATCH 17/19] Do not add -march=native or -mtune=native for Apple Clang. --- scripts/spack_packages/packages/geosx/package.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/spack_packages/packages/geosx/package.py b/scripts/spack_packages/packages/geosx/package.py index e895b710..749c8ba0 100644 --- a/scripts/spack_packages/packages/geosx/package.py +++ b/scripts/spack_packages/packages/geosx/package.py @@ -354,7 +354,8 @@ def geos_hostconfig(self, spec, prefix, py_site_pkgs_dir=None): cfg.write(cmake_cache_string("CMAKE_CXX_FLAGS", cxxflags)) release_flags = "-O3 -DNDEBUG" - if "clang" in self.compiler.cxx: + # Apple Clang accepts neither -mtune=native nor a useful -march=native. + if "clang" in self.compiler.cxx and "apple-clang" not in self.compiler.cxx: release_flags += " -march=native -mtune=native" cfg.write(cmake_cache_string("CMAKE_CXX_FLAGS_RELEASE", release_flags)) reldebinf_flags = "-O2 -g -DNDEBUG" From f6cd1bfaee7cef54a60ed987f9ebeaa5de45a333 Mon Sep 17 00:00:00 2001 From: "Victor A. P. Magri" Date: Fri, 25 Sep 2026 21:38:46 -0400 Subject: [PATCH 18/19] Build hypredrive against the pinned hypre in every TPL image. --- .github/workflows/docker_build_tpls.yml | 34 +++++++++---------- .../spack_packages/packages/geosx/package.py | 4 +++ .../packages/hypredrive/package.py | 24 ++++++------- 3 files changed, 33 insertions(+), 29 deletions(-) diff --git a/.github/workflows/docker_build_tpls.yml b/.github/workflows/docker_build_tpls.yml index 1bb5e9c4..8e351f45 100644 --- a/.github/workflows/docker_build_tpls.yml +++ b/.github/workflows/docker_build_tpls.yml @@ -31,7 +31,7 @@ jobs: DOCKER_BASE_IMAGE_REPO: geosx/ubuntu DOCKER_REPOSITORY: geosx/ubuntu24.04-gcc12 GCC_VERSION: 12 - SPEC: "~pygeosx ~docs %gcc-12" + SPEC: "+hypredrive ~pygeosx ~docs %gcc-12" - name: Ubuntu 24.04 - gcc 13 (docs) RUNS_ON: ubuntu-latest @@ -40,7 +40,7 @@ jobs: DOCKER_BASE_IMAGE_REPO: geosx/ubuntu DOCKER_REPOSITORY: geosx/ubuntu24.04-gcc13 GCC_VERSION: 13 - SPEC: "~pygeosx +docs %gcc-13" + SPEC: "+hypredrive ~pygeosx +docs %gcc-13" - name: Ubuntu 24.04 - clang 19 RUNS_ON: ubuntu-latest @@ -49,7 +49,7 @@ jobs: DOCKER_BASE_IMAGE_REPO: geosx/ubuntu DOCKER_REPOSITORY: geosx/ubuntu24.04-clang19 CLANG_VERSION: 19 - SPEC: "~pygeosx ~docs %clang-19" + SPEC: "+hypredrive ~pygeosx ~docs %clang-19" - name: Ubuntu 24.04 - clang 20 RUNS_ON: ubuntu-latest @@ -58,7 +58,7 @@ jobs: DOCKER_BASE_IMAGE_REPO: geosx/ubuntu DOCKER_REPOSITORY: geosx/ubuntu24.04-clang20 CLANG_VERSION: 20 - SPEC: "~pygeosx ~docs %clang-20" + SPEC: "+hypredrive ~pygeosx ~docs %clang-20" # First Rocky non-CUDA row re-enabled for incremental validation. - name: Rocky Linux 8 - gcc 12 @@ -67,7 +67,7 @@ jobs: DOCKER_BASE_IMAGE_TAG: 8-gcc12 DOCKER_BASE_IMAGE_REPO: geosx/rockylinux DOCKER_REPOSITORY: geosx/rockylinux8-gcc12 - SPEC: "~pygeosx ~docs %gcc-12" + SPEC: "+hypredrive ~pygeosx ~docs %gcc-12" - name: Rocky Linux 8 - gcc 13 RUNS_ON: ubuntu-latest @@ -75,7 +75,7 @@ jobs: DOCKER_BASE_IMAGE_TAG: 8-gcc13 DOCKER_BASE_IMAGE_REPO: geosx/rockylinux DOCKER_REPOSITORY: geosx/rockylinux8-gcc13 - SPEC: "~pygeosx ~docs %gcc-13" + SPEC: "+hypredrive ~pygeosx ~docs %gcc-13" - name: Rocky Linux 8 - clang 19 RUNS_ON: ubuntu-latest @@ -83,7 +83,7 @@ jobs: DOCKER_BASE_IMAGE_TAG: 8-clang19 DOCKER_BASE_IMAGE_REPO: geosx/rockylinux DOCKER_REPOSITORY: geosx/rockylinux8-clang19 - SPEC: "~pygeosx ~docs %clang-19" + SPEC: "+hypredrive ~pygeosx ~docs %clang-19" - name: Rocky Linux 9 - clang 22 RUNS_ON: ubuntu-latest @@ -91,7 +91,7 @@ jobs: DOCKER_BASE_IMAGE_TAG: 9-clang22 DOCKER_BASE_IMAGE_REPO: geosx/rockylinux DOCKER_REPOSITORY: geosx/rockylinux9-clang22 - SPEC: "~pygeosx ~docs %clang-22" + SPEC: "+hypredrive ~pygeosx ~docs %clang-22" # CUDA restart slice. - name: Ubuntu 24.04 - gcc 13 + CUDA 12.9.1 @@ -101,7 +101,7 @@ jobs: DOCKER_BASE_IMAGE_REPO: geosx/ubuntu DOCKER_REPOSITORY: geosx/ubuntu24.04-gcc13-cuda12.9.1 GCC_VERSION: 13 - SPEC: "+cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %gcc-13 ^cuda@12.9.1+allow-unsupported-compilers" + SPEC: "+hypredrive +cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %gcc-13 ^cuda@12.9.1+allow-unsupported-compilers" # CUDA 13 is blocked by the pinned RAJA package: @@ -113,7 +113,7 @@ jobs: # DOCKER_BASE_IMAGE_REPO: geosx/ubuntu # DOCKER_REPOSITORY: geosx/ubuntu24.04-gcc14-cuda13.2.1 # GCC_VERSION: 14 - # SPEC: "+cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %gcc-14 ^cuda@13.2.1+allow-unsupported-compilers" + # SPEC: "+hypredrive +cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %gcc-14 ^cuda@13.2.1+allow-unsupported-compilers" - name: Ubuntu 24.04 - clang 19 + CUDA 12.9.1 RUNS_ON: streak2 @@ -122,7 +122,7 @@ jobs: DOCKER_BASE_IMAGE_REPO: geosx/ubuntu DOCKER_REPOSITORY: geosx/ubuntu24.04-clang19-cuda12.9.1 CLANG_VERSION: 19 - SPEC: "+cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %clang-19 ^cuda@12.9.1+allow-unsupported-compilers" + SPEC: "+hypredrive +cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %clang-19 ^cuda@12.9.1+allow-unsupported-compilers" # - name: Ubuntu 24.04 - clang 20 + CUDA 13.2.1 # RUNS_ON: streak2 @@ -131,7 +131,7 @@ jobs: # DOCKER_BASE_IMAGE_REPO: geosx/ubuntu # DOCKER_REPOSITORY: geosx/ubuntu24.04-clang20-cuda13.2.1 # CLANG_VERSION: 20 - # SPEC: "+cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %clang-20 ^cuda@13.2.1+allow-unsupported-compilers" + # SPEC: "+hypredrive +cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %clang-20 ^cuda@13.2.1+allow-unsupported-compilers" - name: Rocky Linux 8 - gcc 13 + CUDA 12.9.1 RUNS_ON: streak2 @@ -139,7 +139,7 @@ jobs: DOCKER_BASE_IMAGE_TAG: 8-gcc13-cuda12.9.1 DOCKER_BASE_IMAGE_REPO: geosx/rockylinux DOCKER_REPOSITORY: geosx/rockylinux8-gcc13-cuda12.9.1 - SPEC: "+cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %gcc-13 ^cuda@12.9.1+allow-unsupported-compilers" + SPEC: "+hypredrive +cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %gcc-13 ^cuda@12.9.1+allow-unsupported-compilers" # Rocky8 clang19 CUDA 12.9.1 is blocked by SuperLU_DIST using # C++11 with CUDA 12.9 + clang19 + GCC 14 libstdc++. @@ -149,7 +149,7 @@ jobs: # DOCKER_BASE_IMAGE_TAG: 8-clang19-cuda12.9.1 # DOCKER_BASE_IMAGE_REPO: geosx/rockylinux # DOCKER_REPOSITORY: geosx/rockylinux8-clang19-cuda12.9.1 - # SPEC: "+cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %clang-19 ^cuda@12.9.1+allow-unsupported-compilers" + # SPEC: "+hypredrive +cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %clang-19 ^cuda@12.9.1+allow-unsupported-compilers" # - name: Rocky Linux 9 - gcc 15 + CUDA 13.2.1 # RUNS_ON: streak2 @@ -157,7 +157,7 @@ jobs: # DOCKER_BASE_IMAGE_TAG: 9-gcc15-cuda13.2.1 # DOCKER_BASE_IMAGE_REPO: geosx/rockylinux # DOCKER_REPOSITORY: geosx/rockylinux9-gcc15-cuda13.2.1 - # SPEC: "+cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %gcc-15 ^cuda@13.2.1+allow-unsupported-compilers" + # SPEC: "+hypredrive +cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %gcc-15 ^cuda@13.2.1+allow-unsupported-compilers" # ROCm build. Unlike the rows above it does not layer on a # geosx/:- base image: AMD's own ROCm image already @@ -171,14 +171,14 @@ jobs: ROCM_VERSION: 6.4.3 AMDGPU_TARGET: gfx942 SPACK_BUILD_JOBS: 8 - SPEC: "+rocm ~uncrustify ~openmp ~pygeosx ~docs ~trilinos ~petsc amdgpu_target=gfx942 %%amdclang-19 ^caliper~papi~gotcha~sampler~libunwind~libdw" + SPEC: "+hypredrive +rocm ~uncrustify ~openmp ~pygeosx ~docs ~trilinos ~petsc amdgpu_target=gfx942 %%amdclang-19 ^caliper~papi~gotcha~sampler~libunwind~libdw" # Built natively. The published image carries the macOS prefix; it is # not a Linux container produced by docker-build.sh. - name: macOS 26 - Apple Clang (arm64) RUNS_ON: macos-26 DOCKER_REPOSITORY: geosx/macos-26-apple-clang - SPEC: "~pygeosx ~docs ~openmp %c,cxx=apple-clang %fortran=gcc@16.2.0" + SPEC: "+hypredrive ~pygeosx ~docs ~openmp %c,cxx=apple-clang %fortran=gcc@16.2.0" SPACK_BUILD_JOBS: 2 steps: diff --git a/scripts/spack_packages/packages/geosx/package.py b/scripts/spack_packages/packages/geosx/package.py index 749c8ba0..50d9b964 100644 --- a/scripts/spack_packages/packages/geosx/package.py +++ b/scripts/spack_packages/packages/geosx/package.py @@ -134,6 +134,7 @@ class Geosx(CMakePackage, CudaPackage, ROCmPackage): depends_on('chai+cuda~separable_compilation cuda_arch={0}'.format(sm_), when='cuda_arch={0}'.format(sm_)) depends_on('camp+cuda cuda_arch={0}'.format(sm_), when='cuda_arch={0}'.format(sm_)) depends_on('hypre+cuda cuda_arch={0}'.format(sm_), when='cuda_arch={0}'.format(sm_)) + depends_on('hypredrive+cuda cuda_arch={0}'.format(sm_), when='+hypredrive cuda_arch={0}'.format(sm_)) with when('+rocm'): for gfx_ in ROCmPackage.amdgpu_targets: @@ -142,6 +143,7 @@ class Geosx(CMakePackage, CudaPackage, ROCmPackage): depends_on(f"chai+rocm~separable_compilation amdgpu_target={gfx_}", when=f"amdgpu_target={gfx_}") depends_on(f"camp+rocm amdgpu_target={gfx_}", when=f"amdgpu_target={gfx_}") depends_on(f"hypre+rocm amdgpu_target={gfx_}", when=f"amdgpu_target={gfx_}") + depends_on(f"hypredrive+rocm amdgpu_target={gfx_}", when=f"+hypredrive amdgpu_target={gfx_}") # # IO @@ -199,6 +201,8 @@ class Geosx(CMakePackage, CudaPackage, ROCmPackage): depends_on("hypredrive +pic", when="~shared") depends_on("hypredrive +shared", when="+shared") depends_on("hypredrive +caliper", when="+caliper") + depends_on("hypredrive +cuda", when="+cuda") + depends_on("hypredrive +rocm", when="+rocm") depends_on('petsc@3.19.4~hdf5~hypre+int64', when='+petsc') depends_on('petsc+ptscotch', when='+petsc+scotch') diff --git a/scripts/spack_packages/packages/hypredrive/package.py b/scripts/spack_packages/packages/hypredrive/package.py index d4dd280d..38e39558 100644 --- a/scripts/spack_packages/packages/hypredrive/package.py +++ b/scripts/spack_packages/packages/hypredrive/package.py @@ -51,19 +51,19 @@ class Hypredrive(CMakePackage, CudaPackage, ROCmPackage): depends_on("cmake@3.23:", type="build") depends_on("mpi") - depends_on("hypre@develop +mpi") - depends_on("hypre@develop +shared", when="+shared") - depends_on("hypre@develop +pic~shared", when="+pic~shared") - depends_on("hypre@develop +caliper", when="+caliper") - depends_on("hypre@develop +cuda", when="+cuda") - depends_on("hypre@develop +rocm", when="+rocm") - depends_on("hypre@develop +sycl", when="+sycl") - depends_on("hypre@develop +superlu-dist", when="+superlu-dist") + depends_on("hypre +mpi") + depends_on("hypre +shared", when="+shared") + depends_on("hypre +pic~shared", when="+pic~shared") + depends_on("hypre +caliper", when="+caliper") + depends_on("hypre +cuda", when="+cuda") + depends_on("hypre +rocm", when="+rocm") + depends_on("hypre +sycl", when="+sycl") + depends_on("hypre +superlu-dist", when="+superlu-dist") for feature in ("fortran", "matlab", "julia", "superlu-dist"): - depends_on("hypre@develop precision=double", when="+{0}".format(feature)) + depends_on("hypre precision=double", when="+{0}".format(feature)) for feature in ("matlab", "julia"): - depends_on("hypre@develop~complex", when="+{0}".format(feature)) + depends_on("hypre~complex", when="+{0}".format(feature)) requires( "%c,cxx=oneapi", @@ -73,7 +73,7 @@ class Hypredrive(CMakePackage, CudaPackage, ROCmPackage): for arch in CudaPackage.cuda_arch_values: depends_on( - "hypre@develop+cuda cuda_arch={0}".format(arch), when="+cuda cuda_arch={0}".format(arch) + "hypre+cuda cuda_arch={0}".format(arch), when="+cuda cuda_arch={0}".format(arch) ) depends_on( "superlu-dist@9.2.1:+cuda cuda_arch={0}".format(arch), @@ -82,7 +82,7 @@ class Hypredrive(CMakePackage, CudaPackage, ROCmPackage): for target in ROCmPackage.amdgpu_targets: depends_on( - "hypre@develop+rocm amdgpu_target={0}".format(target), + "hypre+rocm amdgpu_target={0}".format(target), when="+rocm amdgpu_target={0}".format(target), ) depends_on( From 61b4b4818cdcaa24bbce6652a1c2fa2578c9f74c Mon Sep 17 00:00:00 2001 From: "Victor A. P. Magri" Date: Fri, 25 Sep 2026 22:00:23 -0400 Subject: [PATCH 19/19] Rely on the hypredrive default instead of listing it in every spec. --- .github/workflows/docker_build_tpls.yml | 34 ++++++++++++------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/docker_build_tpls.yml b/.github/workflows/docker_build_tpls.yml index 8e351f45..1bb5e9c4 100644 --- a/.github/workflows/docker_build_tpls.yml +++ b/.github/workflows/docker_build_tpls.yml @@ -31,7 +31,7 @@ jobs: DOCKER_BASE_IMAGE_REPO: geosx/ubuntu DOCKER_REPOSITORY: geosx/ubuntu24.04-gcc12 GCC_VERSION: 12 - SPEC: "+hypredrive ~pygeosx ~docs %gcc-12" + SPEC: "~pygeosx ~docs %gcc-12" - name: Ubuntu 24.04 - gcc 13 (docs) RUNS_ON: ubuntu-latest @@ -40,7 +40,7 @@ jobs: DOCKER_BASE_IMAGE_REPO: geosx/ubuntu DOCKER_REPOSITORY: geosx/ubuntu24.04-gcc13 GCC_VERSION: 13 - SPEC: "+hypredrive ~pygeosx +docs %gcc-13" + SPEC: "~pygeosx +docs %gcc-13" - name: Ubuntu 24.04 - clang 19 RUNS_ON: ubuntu-latest @@ -49,7 +49,7 @@ jobs: DOCKER_BASE_IMAGE_REPO: geosx/ubuntu DOCKER_REPOSITORY: geosx/ubuntu24.04-clang19 CLANG_VERSION: 19 - SPEC: "+hypredrive ~pygeosx ~docs %clang-19" + SPEC: "~pygeosx ~docs %clang-19" - name: Ubuntu 24.04 - clang 20 RUNS_ON: ubuntu-latest @@ -58,7 +58,7 @@ jobs: DOCKER_BASE_IMAGE_REPO: geosx/ubuntu DOCKER_REPOSITORY: geosx/ubuntu24.04-clang20 CLANG_VERSION: 20 - SPEC: "+hypredrive ~pygeosx ~docs %clang-20" + SPEC: "~pygeosx ~docs %clang-20" # First Rocky non-CUDA row re-enabled for incremental validation. - name: Rocky Linux 8 - gcc 12 @@ -67,7 +67,7 @@ jobs: DOCKER_BASE_IMAGE_TAG: 8-gcc12 DOCKER_BASE_IMAGE_REPO: geosx/rockylinux DOCKER_REPOSITORY: geosx/rockylinux8-gcc12 - SPEC: "+hypredrive ~pygeosx ~docs %gcc-12" + SPEC: "~pygeosx ~docs %gcc-12" - name: Rocky Linux 8 - gcc 13 RUNS_ON: ubuntu-latest @@ -75,7 +75,7 @@ jobs: DOCKER_BASE_IMAGE_TAG: 8-gcc13 DOCKER_BASE_IMAGE_REPO: geosx/rockylinux DOCKER_REPOSITORY: geosx/rockylinux8-gcc13 - SPEC: "+hypredrive ~pygeosx ~docs %gcc-13" + SPEC: "~pygeosx ~docs %gcc-13" - name: Rocky Linux 8 - clang 19 RUNS_ON: ubuntu-latest @@ -83,7 +83,7 @@ jobs: DOCKER_BASE_IMAGE_TAG: 8-clang19 DOCKER_BASE_IMAGE_REPO: geosx/rockylinux DOCKER_REPOSITORY: geosx/rockylinux8-clang19 - SPEC: "+hypredrive ~pygeosx ~docs %clang-19" + SPEC: "~pygeosx ~docs %clang-19" - name: Rocky Linux 9 - clang 22 RUNS_ON: ubuntu-latest @@ -91,7 +91,7 @@ jobs: DOCKER_BASE_IMAGE_TAG: 9-clang22 DOCKER_BASE_IMAGE_REPO: geosx/rockylinux DOCKER_REPOSITORY: geosx/rockylinux9-clang22 - SPEC: "+hypredrive ~pygeosx ~docs %clang-22" + SPEC: "~pygeosx ~docs %clang-22" # CUDA restart slice. - name: Ubuntu 24.04 - gcc 13 + CUDA 12.9.1 @@ -101,7 +101,7 @@ jobs: DOCKER_BASE_IMAGE_REPO: geosx/ubuntu DOCKER_REPOSITORY: geosx/ubuntu24.04-gcc13-cuda12.9.1 GCC_VERSION: 13 - SPEC: "+hypredrive +cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %gcc-13 ^cuda@12.9.1+allow-unsupported-compilers" + SPEC: "+cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %gcc-13 ^cuda@12.9.1+allow-unsupported-compilers" # CUDA 13 is blocked by the pinned RAJA package: @@ -113,7 +113,7 @@ jobs: # DOCKER_BASE_IMAGE_REPO: geosx/ubuntu # DOCKER_REPOSITORY: geosx/ubuntu24.04-gcc14-cuda13.2.1 # GCC_VERSION: 14 - # SPEC: "+hypredrive +cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %gcc-14 ^cuda@13.2.1+allow-unsupported-compilers" + # SPEC: "+cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %gcc-14 ^cuda@13.2.1+allow-unsupported-compilers" - name: Ubuntu 24.04 - clang 19 + CUDA 12.9.1 RUNS_ON: streak2 @@ -122,7 +122,7 @@ jobs: DOCKER_BASE_IMAGE_REPO: geosx/ubuntu DOCKER_REPOSITORY: geosx/ubuntu24.04-clang19-cuda12.9.1 CLANG_VERSION: 19 - SPEC: "+hypredrive +cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %clang-19 ^cuda@12.9.1+allow-unsupported-compilers" + SPEC: "+cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %clang-19 ^cuda@12.9.1+allow-unsupported-compilers" # - name: Ubuntu 24.04 - clang 20 + CUDA 13.2.1 # RUNS_ON: streak2 @@ -131,7 +131,7 @@ jobs: # DOCKER_BASE_IMAGE_REPO: geosx/ubuntu # DOCKER_REPOSITORY: geosx/ubuntu24.04-clang20-cuda13.2.1 # CLANG_VERSION: 20 - # SPEC: "+hypredrive +cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %clang-20 ^cuda@13.2.1+allow-unsupported-compilers" + # SPEC: "+cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %clang-20 ^cuda@13.2.1+allow-unsupported-compilers" - name: Rocky Linux 8 - gcc 13 + CUDA 12.9.1 RUNS_ON: streak2 @@ -139,7 +139,7 @@ jobs: DOCKER_BASE_IMAGE_TAG: 8-gcc13-cuda12.9.1 DOCKER_BASE_IMAGE_REPO: geosx/rockylinux DOCKER_REPOSITORY: geosx/rockylinux8-gcc13-cuda12.9.1 - SPEC: "+hypredrive +cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %gcc-13 ^cuda@12.9.1+allow-unsupported-compilers" + SPEC: "+cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %gcc-13 ^cuda@12.9.1+allow-unsupported-compilers" # Rocky8 clang19 CUDA 12.9.1 is blocked by SuperLU_DIST using # C++11 with CUDA 12.9 + clang19 + GCC 14 libstdc++. @@ -149,7 +149,7 @@ jobs: # DOCKER_BASE_IMAGE_TAG: 8-clang19-cuda12.9.1 # DOCKER_BASE_IMAGE_REPO: geosx/rockylinux # DOCKER_REPOSITORY: geosx/rockylinux8-clang19-cuda12.9.1 - # SPEC: "+hypredrive +cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %clang-19 ^cuda@12.9.1+allow-unsupported-compilers" + # SPEC: "+cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %clang-19 ^cuda@12.9.1+allow-unsupported-compilers" # - name: Rocky Linux 9 - gcc 15 + CUDA 13.2.1 # RUNS_ON: streak2 @@ -157,7 +157,7 @@ jobs: # DOCKER_BASE_IMAGE_TAG: 9-gcc15-cuda13.2.1 # DOCKER_BASE_IMAGE_REPO: geosx/rockylinux # DOCKER_REPOSITORY: geosx/rockylinux9-gcc15-cuda13.2.1 - # SPEC: "+hypredrive +cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %gcc-15 ^cuda@13.2.1+allow-unsupported-compilers" + # SPEC: "+cuda cuda_arch=86,120 ~openmp ~pygeosx ~docs %gcc-15 ^cuda@13.2.1+allow-unsupported-compilers" # ROCm build. Unlike the rows above it does not layer on a # geosx/:- base image: AMD's own ROCm image already @@ -171,14 +171,14 @@ jobs: ROCM_VERSION: 6.4.3 AMDGPU_TARGET: gfx942 SPACK_BUILD_JOBS: 8 - SPEC: "+hypredrive +rocm ~uncrustify ~openmp ~pygeosx ~docs ~trilinos ~petsc amdgpu_target=gfx942 %%amdclang-19 ^caliper~papi~gotcha~sampler~libunwind~libdw" + SPEC: "+rocm ~uncrustify ~openmp ~pygeosx ~docs ~trilinos ~petsc amdgpu_target=gfx942 %%amdclang-19 ^caliper~papi~gotcha~sampler~libunwind~libdw" # Built natively. The published image carries the macOS prefix; it is # not a Linux container produced by docker-build.sh. - name: macOS 26 - Apple Clang (arm64) RUNS_ON: macos-26 DOCKER_REPOSITORY: geosx/macos-26-apple-clang - SPEC: "+hypredrive ~pygeosx ~docs ~openmp %c,cxx=apple-clang %fortran=gcc@16.2.0" + SPEC: "~pygeosx ~docs ~openmp %c,cxx=apple-clang %fortran=gcc@16.2.0" SPACK_BUILD_JOBS: 2 steps: