@@ -90,67 +90,105 @@ jobs:
9090 manylinux : ${{ matrix.libc == 'manylinux' && 'auto' || 'musllinux_1_2' }}
9191 before-script-linux : |
9292 # hdf5-metno-src builds HDF5 from source via cmake; it requires
93- # CMake >= 3.26. Both manylinux_2_17 (AlmaLinux yum cmake = 3.20)
94- # and musllinux_1_2 (Alpine apk cmake = 3.22) ship versions that
95- # are too old. Install a recent cmake from PyPI and pin its
96- # path via CMAKE=... (set on the step's env above) so the
97- # cmake-rs crate uses it deterministically.
93+ # CMake >= 3.26. Both manylinux_2_17 (AlmaLinux yum cmake 3.20)
94+ # and musllinux_1_2 (Alpine apk cmake 3.22) ship too-old
95+ # versions, and cross-compile images from messense (used for
96+ # aarch64) don't have /opt/python/ at all. Install a recent
97+ # cmake via pip from whatever Python we can find, and pin its
98+ # path via the CMAKE env var.
9899 #
99100 # `set -ex` prints every command so failures are diagnosable
100101 # from the workflow log without re-running.
101102 set -ex
102103
103104 uname -a
104105 cat /etc/os-release || true
106+ ls -d /opt/python 2>&1 || echo "(no /opt/python — likely a cross-compile image)"
105107
106- # Detect package manager and install minimal build tools for
107- # the HDF5 source compile.
108+ # Install minimal build tools + a system Python if needed.
109+ # The cross-compile images have package managers but often no
110+ # Python preinstalled, so add it here.
108111 if command -v apk >/dev/null 2>&1; then
109- # Alpine / musllinux. apk update so package metadata is fresh.
110112 apk update
111- apk add --no-cache make gcc g++ musl-dev linux-headers perl bash
113+ apk add --no-cache make gcc g++ musl-dev linux-headers perl bash python3 py3-pip
112114 elif command -v dnf >/dev/null 2>&1; then
113- # AlmaLinux 9 / manylinux_2_28
114- dnf install -y make gcc gcc-c++ perl
115+ dnf install -y make gcc gcc-c++ perl python3 python3-pip
115116 elif command -v yum >/dev/null 2>&1; then
116- # AlmaLinux 8 / manylinux_2_17
117- yum install -y make gcc gcc-c++ perl
117+ yum install -y make gcc gcc-c++ perl python3 python3-pip
118+ elif command -v apt-get >/dev/null 2>&1; then
119+ apt-get update
120+ apt-get install -y make gcc g++ perl python3 python3-pip
118121 fi
119122
120- # Find a CPython 3.x in the standard PyPA layout. Prefer 3.10
121- # since that's the abi3 minimum we promise; any cp3* works.
123+ # Resolve a pip we can use. Prefer the PyPA-style /opt/python
124+ # bundled CPython (manylinux_2_17 / musllinux_1_2 native images);
125+ # fall back to system python3 (typical of cross-compile images).
126+ PIP_CMD=""
122127 PYBIN=""
123- for d in /opt/python/cp310-cp310/bin /opt/python/cp311-cp311/bin \
124- /opt/python/cp312-cp312/bin /opt/python/cp39-cp39/bin \
125- /opt/python/cp313-cp313/bin; do
126- if [ -d "$d" ]; then PYBIN="$d"; break; fi
128+ for cand in /opt/python/cp310-cp310/bin /opt/python/cp311-cp311/bin \
129+ /opt/python/cp312-cp312/bin /opt/python/cp39-cp39/bin \
130+ /opt/python/cp313-cp313/bin; do
131+ if [ -d "$cand" ]; then
132+ PYBIN="$cand"
133+ PIP_CMD="$cand/pip"
134+ break
135+ fi
127136 done
128- if [ -z "$PYBIN" ]; then
129- echo "::error::No /opt/python/cp3*/bin found"
130- ls -la /opt/python/ 2>&1 || true
131- exit 1
137+ if [ -z "$PIP_CMD" ]; then
138+ if command -v pip3 >/dev/null 2>&1; then
139+ PIP_CMD="pip3"
140+ elif command -v python3 >/dev/null 2>&1; then
141+ python3 -m ensurepip --default-pip 2>/dev/null || true
142+ PIP_CMD="python3 -m pip"
143+ else
144+ echo "::error::No Python/pip found in build container"
145+ exit 1
146+ fi
132147 fi
133- echo "Selected PYBIN=$PYBIN"
134- "$PYBIN/python" --version
148+ echo "Using PIP_CMD='$PIP_CMD' (PYBIN='${PYBIN:-system}')"
135149
136- # Install a recent cmake from PyPI. Use --no-cache-dir so we
137- # don't blow up the build cache.
138- "$PYBIN/pip" install --no-cache-dir --upgrade "cmake>=3.26"
150+ # Install cmake>=3.26 from PyPI. `--break-system-packages` is
151+ # for PEP 668-marked Pythons (some distros); harmless on others.
152+ $PIP_CMD install --no-cache-dir --upgrade --break-system-packages "cmake>=3.26" 2>/dev/null \
153+ || $PIP_CMD install --no-cache-dir --upgrade "cmake>=3.26"
139154
140- # Pin the cmake path to match the CMAKE env var set above.
155+ # Find where the pip-installed cmake landed. With /opt/python
156+ # it's in $PYBIN/cmake; with system pip it's wherever pip puts
157+ # console scripts — discoverable via `command -v cmake` after
158+ # install (PATH is updated by the install).
159+ INSTALLED_CMAKE=""
160+ if [ -n "$PYBIN" ] && [ -x "$PYBIN/cmake" ]; then
161+ INSTALLED_CMAKE="$PYBIN/cmake"
162+ elif command -v cmake >/dev/null 2>&1; then
163+ INSTALLED_CMAKE=$(command -v cmake)
164+ else
165+ # Last-ditch: ask pip where it put the script.
166+ INSTALLED_CMAKE=$($PIP_CMD show -f cmake 2>/dev/null | awk '/Location:/ {loc=$2} /bin\/cmake/ {print loc"/"$1}' | head -1)
167+ # That gives a path relative to site-packages; resolve to abs.
168+ if [ -n "$INSTALLED_CMAKE" ]; then
169+ INSTALLED_CMAKE=$(realpath "$INSTALLED_CMAKE" 2>/dev/null || echo "$INSTALLED_CMAKE")
170+ fi
171+ fi
172+ if [ -z "$INSTALLED_CMAKE" ] || [ ! -x "$INSTALLED_CMAKE" ]; then
173+ echo "::error::Could not locate pip-installed cmake binary"
174+ $PIP_CMD show -f cmake || true
175+ exit 1
176+ fi
177+ echo "Pip-installed cmake at: $INSTALLED_CMAKE"
178+
179+ # Pin the path to match the CMAKE env var set on the step.
141180 mkdir -p /opt/_pipcmake/bin
142- ln -sf "$PYBIN/cmake " /opt/_pipcmake/bin/cmake
181+ ln -sf "$INSTALLED_CMAKE " /opt/_pipcmake/bin/cmake
143182
144- # Shadow common system locations as a fallback for any tool
145- # that ignores CMAKE and just resolves `cmake` via PATH.
183+ # Shadow common system locations as a PATH fallback.
146184 for target in /usr/local/bin/cmake /usr/bin/cmake; do
147185 parent=$(dirname "$target")
148- [ -d "$parent" ] && ln -sf "$PYBIN/cmake " "$target" || true
186+ [ -d "$parent" ] && ln -sf "$INSTALLED_CMAKE " "$target" || true
149187 done
150188
151- # Diagnostic: show what's where so future failures are obvious .
152- command -v cmake || true
153- "$PYBIN/cmake " --version
189+ # Diagnostic: confirm the right cmake is found via every route .
190+ echo "--- cmake versions:"
191+ "$INSTALLED_CMAKE " --version
154192 /opt/_pipcmake/bin/cmake --version
155193 cmake --version
156194 - uses : actions/upload-artifact@v4
@@ -159,9 +197,22 @@ jobs:
159197 path : python/dist
160198
161199 # ─── macOS wheels (x86_64 + Apple Silicon) ─────────────────────────────────
200+ #
201+ # Runner mapping:
202+ # - aarch64 → macos-14 (Apple Silicon, free, ample capacity)
203+ # - x86_64 → macos-13-large (Intel, paid 6-core, much better queue
204+ # priority than the free macos-13).
205+ #
206+ # macos-13 is the newest Intel macOS image GitHub offers (Apple stopped
207+ # making Intel Macs), and the free macos-13 runner pool is usually
208+ # under-provisioned. macos-13-large costs billing minutes but reliably
209+ # picks up jobs in seconds rather than tens of minutes. If you'd rather
210+ # not consume the budget, the cleanest alternative is to drop the
211+ # x86_64 wheel altogether — the sdist + arm64 wheel cover ~all current
212+ # macOS Python users.
162213 macos :
163214 name : macOS ${{ matrix.target }}
164- runs-on : ${{ matrix.target == 'aarch64' && 'macos-14' || 'macos-13' }}
215+ runs-on : ${{ matrix.target == 'aarch64' && 'macos-14' || 'macos-13-large ' }}
165216 needs : [verify-version]
166217 strategy :
167218 fail-fast : false
0 commit comments