Skip to content

Commit bc41423

Browse files
authored
healpy: Add version 1.20.0 (#2430)
* healpy: Add version 1.20.0 Build riscv64 wheels for healpy, the Cython wrapper around the bundled/vendored cfitsio and HEALPix C++ (incl. libsharp) libraries. Upstream publishes no riscv64 wheel anywhere. Mirrors upstream's cibuildwheel.yml, narrowed to Linux/riscv64. * healpy: work around riscv64 libgomp fault in libsharp's OpenMP loops The cp314 leg of PR #2430 (run 36393265628, job 108833715006) segfaulted inside libgomp during test_sphtfunc.py::test_map2alm_pol ("Fatal Python error: Segmentation fault", exit 139), with the C stack trace going healpy._sphtools -> libgomp -> libsharp -> libgomp -> libc. cp313 on the same commit built and tested clean. That signature matches gotcha 166 (riscv64 runners' libgomp faulting on dynamic/guided OpenMP schedules) exactly. cextern/healpix vendors libsharp, whose SHT execution path (sharp.c's map2phase/phase2map/ sharp_execute_job) and Gauss-Legendre root finder (sharp_legendre_roots.c) all use schedule(dynamic,*); sharp_mpi.c's own instance is dead code here (behind #ifdef USE_MPI, never defined by this build). Patch 0002 moves those four loops to schedule(static), mirroring the existing lightgbm/4.7.0/0002 patch for the same defect, tagged Upstream-Status: Inappropriate with the tracking issue (#617). * healpy: use schedule(static) for dist2holes's dynamic OpenMP loops CI's cp313 leg segfaulted in test_dist2holes_no_hole_filter (PR #2430, run 36412601673) on hp.dist2holes(mask), a different crash than the one 0002 already fixes. The C++ dist2holes() (mask_tools.cc) and the swap_scheme() it calls on RING masks (healpix_map.h) each contain OpenMP loops using dynamic scheduling, matching gotcha 166's libgomp signature again, this time in HEALPix C++ rather than libsharp. * healpy: pin setuptools_scm version to avoid dirty-tree dev suffix The OpenMP fix patches are applied with a plain `git apply`, which leaves the checkout dirty at the 1.20.0 tag. setuptools_scm then reports the dirty tree as a post-release dev version (1.20.1.dev0+g<sha>.d<date>) instead of 1.20.0, which the publish step correctly rejects since it doesn't match docs/packages/healpy.yaml. Set SETUPTOOLS_SCM_PRETEND_VERSION_FOR_HEALPY in CIBW_ENVIRONMENT, matching the established pattern used by every other package in this repo that patches a setuptools_scm checkout (gotcha 31).
1 parent 978b9e3 commit bc41423

5 files changed

Lines changed: 1143 additions & 0 deletions

‎.github/workflows/build-healpy.yml‎

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
# SPDX-FileCopyrightText: 2026 The RISE Project
2+
# SPDX-License-Identifier: MIT
3+
---
4+
# This workflow is based on: https://github.com/healpy/healpy/blob/1.20.0/.github/workflows/cibuildwheel.yml
5+
name: Build healpy wheels (riscv64)
6+
7+
on:
8+
workflow_dispatch:
9+
inputs:
10+
version:
11+
description: 'Version glob to (re)build; empty builds every version of docs/packages/healpy.yaml not released yet'
12+
required: false
13+
default: ''
14+
pull_request:
15+
branches: [main]
16+
paths:
17+
- '.github/workflows/build-healpy.yml'
18+
- 'docs/packages/healpy.yaml'
19+
- 'patches/healpy/**'
20+
push:
21+
branches: [main]
22+
paths:
23+
- '.github/workflows/build-healpy.yml'
24+
- 'docs/packages/healpy.yaml'
25+
- 'patches/healpy/**'
26+
27+
concurrency:
28+
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
29+
cancel-in-progress: true
30+
31+
permissions:
32+
contents: read # to fetch code (actions/checkout)
33+
34+
env:
35+
MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64
36+
37+
jobs:
38+
setup:
39+
uses: $/.github/workflows/_setup.yml
40+
with:
41+
package: healpy
42+
version: ${{ inputs.version }}
43+
44+
build_wheels:
45+
needs: [setup]
46+
if: needs.setup.outputs.versions != '[]'
47+
name: Build healpy ${{ matrix.version }} ${{ matrix.python }}-manylinux_riscv64
48+
runs-on: ubuntu-24.04-riscv
49+
timeout-minutes: 180
50+
strategy:
51+
fail-fast: false
52+
matrix:
53+
version: ${{ fromJSON(needs.setup.outputs.versions) }}
54+
# Per-interpreter (not abi3): the Cython/C++ extensions link the
55+
# version-specific ABI. No cp314t: upstream builds none either (no
56+
# free-threaded entry in its own CIBW_BUILD), and astropy -- a hard
57+
# runtime dependency -- has no riscv64 wheel for it anywhere.
58+
python:
59+
- "cp312"
60+
- "cp313"
61+
- "cp314"
62+
63+
env:
64+
HEALPY_VERSION: ${{ matrix.version }}
65+
66+
steps:
67+
- name: Checkout healpy ${{ env.HEALPY_VERSION }}
68+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
69+
with:
70+
repository: healpy/healpy
71+
ref: ${{ env.HEALPY_VERSION }}
72+
submodules: true
73+
persist-credentials: false
74+
75+
- name: Checkout python-wheels
76+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
77+
with:
78+
path: python-wheels
79+
persist-credentials: false
80+
81+
- name: Patch healpy source
82+
run: git apply python-wheels/patches/healpy/${{ env.HEALPY_VERSION }}/*.patch
83+
84+
- name: Build wheels
85+
uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0
86+
with:
87+
output-dir: wheelhouse/
88+
only: ${{ matrix.python }}-manylinux_riscv64
89+
env:
90+
CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }}
91+
# numpy/astropy are hard runtime deps and scipy/matplotlib are test
92+
# extras; only our registry has riscv64 wheels for any of them, and
93+
# only-binary keeps a newer PyPI release from winning the resolution
94+
# and building from sdist instead.
95+
# SETUPTOOLS_SCM_PRETEND_VERSION_FOR_HEALPY: the `git apply` above
96+
# dirties the tree, which setuptools_scm otherwise reports as a
97+
# `.devN+g<sha>` version (gotcha 31).
98+
CIBW_ENVIRONMENT: >-
99+
PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/
100+
PIP_ONLY_BINARY=numpy,astropy,scipy,matplotlib
101+
MPLLOCALFREETYPE=1
102+
MPLCONFIGDIR=/tmp
103+
SETUPTOOLS_SCM_PRETEND_VERSION_FOR_HEALPY=${{ env.HEALPY_VERSION }}
104+
CIBW_TEST_ENVIRONMENT: >-
105+
PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/
106+
PIP_ONLY_BINARY=numpy,astropy,scipy,matplotlib
107+
MPLLOCALFREETYPE=1
108+
MPLCONFIGDIR=/tmp
109+
110+
- name: Check wheel contents
111+
run: |
112+
python3 - wheelhouse/*.whl <<'EOF'
113+
import sys, zipfile
114+
path = sys.argv[1]
115+
assert "-${{ matrix.python }}-" in path, path
116+
names = zipfile.ZipFile(path).namelist()
117+
exts = {n.rsplit("/", 1)[1].split(".", 1)[0] for n in names if n.endswith(".so") and ".libs/" not in n}
118+
assert exts == {
119+
"_healpy_pixel_lib", "_healpy_sph_transform_lib", "_hotspots",
120+
"_line_integral_convolution", "_masktools", "_pixelfunc",
121+
"_query_disc", "_sphtools",
122+
}, exts
123+
licences = {n.split(".dist-info/licenses/", 1)[1] for n in names
124+
if ".dist-info/licenses/" in n} - {""}
125+
assert licences == {
126+
"COPYING", "LICENSE.cfitsio", "LICENSE.healpix_cxx",
127+
"LICENSE.libsharp", "LICENSE.pocketfft",
128+
}, licences
129+
EOF
130+
131+
- name: Store wheels
132+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
133+
with:
134+
name: healpy-${{ env.HEALPY_VERSION }}-${{ matrix.python }}-manylinux_riscv64
135+
path: ./wheelhouse/*.whl
136+
if-no-files-found: error
137+
138+
gpl_sources:
139+
needs: [setup]
140+
if: needs.setup.outputs.versions != '[]'
141+
strategy:
142+
fail-fast: false
143+
matrix:
144+
version: ${{ fromJSON(needs.setup.outputs.versions) }}
145+
name: Collect GPL sources
146+
runs-on: ubuntu-24.04-riscv
147+
148+
env:
149+
HEALPY_VERSION: ${{ matrix.version }}
150+
151+
steps:
152+
- name: Checkout python-wheels
153+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
154+
with:
155+
persist-credentials: false
156+
157+
# libsharp and HEALPix C++ (both cextern/ submodules) link OpenMP when
158+
# available, so auditwheel vendors the image's libgomp into healpy.libs/.
159+
- uses: ./actions/collect-gpl-sources
160+
with:
161+
image: ${{ env.MANYLINUX_RISCV64_IMAGE }}
162+
packages: gcc
163+
output: gpl-sources.tar
164+
165+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
166+
with:
167+
name: healpy-${{ env.HEALPY_VERSION }}-gpl-sources
168+
path: gpl-sources.tar
169+
if-no-files-found: error
170+
171+
publish:
172+
name: Publish healpy ${{ matrix.version }}
173+
needs: [setup, build_wheels, gpl_sources]
174+
if: needs.setup.outputs.versions != '[]'
175+
strategy:
176+
fail-fast: false
177+
matrix:
178+
version: ${{ fromJSON(needs.setup.outputs.versions) }}
179+
permissions:
180+
contents: write
181+
pull-requests: write
182+
uses: $/.github/workflows/_publish-wheel.yml
183+
secrets:
184+
app-private-key: ${{ secrets.RISEPROJECT_APP_PRIVATE_KEY }}
185+
with:
186+
artifact-pattern: healpy-${{ matrix.version }}-*-manylinux_riscv64
187+
gpl-sources-artifact: healpy-${{ matrix.version }}-gpl-sources
188+
gpl-sources-description: gcc

‎docs/packages/healpy.yaml‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package-name: healpy
2+
source-code: https://github.com/healpy/healpy
3+
license: GPL-2.0-only
4+
versions:
5+
- version: 1.20.0
6+
patched: true

0 commit comments

Comments
 (0)