Skip to content

Commit ccfe53c

Browse files
committed
WHL: build limited-api compliant wheels
1 parent b693757 commit ccfe53c

3 files changed

Lines changed: 79 additions & 41 deletions

File tree

.github/workflows/cibuildwheel.yml

Lines changed: 12 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,8 @@ jobs:
5959
arch: aarch64
6060
- os: macos-14
6161
arch: arm64
62-
CIBW_ENVIRONMENT: MACOSX_DEPLOYMENT_TARGET=14.0
6362
- os: macos-13
6463
arch: x86_64
65-
CIBW_ENVIRONMENT: MACOSX_DEPLOYMENT_TARGET=13.0
6664

6765
steps:
6866
- uses: actions/checkout@v5
@@ -80,33 +78,20 @@ jobs:
8078
shell: bash
8179
# On PRs we run only oldest and newest Python versions to reduce CI load.
8280
# Skips pypy and musllinux everywhere.
83-
# We are buiding 38 and 312 for now.
81+
# We are building 39, 311 and 314 for now.
82+
# (3.11 is the oldest version for which we support abi3 wheels)
8483
# These needs to rotate every new Python release.
8584
run: |
86-
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
87-
CIBW_SKIP="pp* cp36-* cp37-* cp38-* cp314t-* *-musllinux* cp39-* cp310-* cp311-* cp312-* cp313-*"
88-
else
89-
CIBW_SKIP="pp* cp36-* cp37-* cp38-* cp314t-* *-musllinux*"
90-
fi
91-
echo "CIBW_SKIP=$CIBW_SKIP" >> $GITHUB_ENV
92-
echo "Setting CIBW_SKIP=$CIBW_SKIP"
85+
set -x
86+
echo "CIBW_BUILD=cp39-* cp311-* cp314-*" >> $GITHUB_ENV
87+
set +x
88+
89+
if: ${{ github.event_name }} == "pull_request"
9390

9491
- name: "Building ${{ matrix.os }} (${{ matrix.arch }}) wheels"
9592
uses: pypa/cibuildwheel@v3.1.4
9693
env:
97-
CIBW_SKIP: ${{ env.CIBW_SKIP }}
9894
CIBW_ARCHS: ${{ matrix.arch }}
99-
CIBW_BUILD_FRONTEND: build
100-
CIBW_MANYLINUX_X86_64_IMAGE: ghcr.io/ocefpaf/manylinux_2_28_x86_64-netcdf
101-
CIBW_MANYLINUX_AARCH64_IMAGE: ghcr.io/ocefpaf/manylinux_2_28_aarch64-netcdf
102-
# Emulation testing is slow, testing only latest Python.
103-
CIBW_TEST_SKIP: "cp39-*_aarch64 cp310-*_aarch64 cp311-*_aarch64 cp312-*_aarch64"
104-
CIBW_ENVIRONMENT: ${{ matrix.CIBW_ENVIRONMENT }}
105-
CIBW_BEFORE_BUILD_MACOS: brew install hdf5 netcdf
106-
CIBW_TEST_REQUIRES: pytest cython packaging typing-extensions
107-
CIBW_TEST_COMMAND: >
108-
python -c "import netCDF4; print(f'netCDF4 v{netCDF4.__version__}')"
109-
&& pytest -s -rxs -v {project}/test
11095

11196
- uses: actions/upload-artifact@v4
11297
with:
@@ -120,7 +105,7 @@ jobs:
120105
strategy:
121106
matrix:
122107
os: [windows-latest]
123-
arch: [win_amd64]
108+
arch: [AMD64]
124109

125110
steps:
126111
- uses: actions/checkout@v5
@@ -140,25 +125,12 @@ jobs:
140125
create-args: >-
141126
python=${{ matrix.python-version }} libnetcdf=4.9.2 --channel conda-forge
142127
143-
- name: Install cibuildwheel
144-
run: |
145-
python -m pip install --upgrade cibuildwheel delvewheel
146-
147128
- name: Build wheels for Windows (${{ matrix.arch }})
148-
run: cibuildwheel --output-dir wheelhouse
129+
uses: pypa/cibuildwheel@v3.1.4
149130
env:
150-
CIBW_BUILD: "cp39-${{ matrix.arch }} cp310-${{ matrix.arch }} cp311-${{ matrix.arch }} cp312-${{ matrix.arch }} cp313-${{ matrix.arch }}"
151-
CIBW_ENVIRONMENT_WINDOWS: >
152-
HDF5_DIR="C:\\Users\\runneradmin\\micromamba\\envs\\build\\Library"
153-
netCDF4_DIR="C:\\Users\\runneradmin\\micromamba\\envs\\build\\Library"
154-
PATH="C:\\Users\\runneradmin\\micromamba\\envs\\build\\Library\\bin;${PATH}"
155-
CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: >
156-
delvewheel show {wheel}
157-
&& delvewheel repair -w {dest_dir} {wheel}
158-
CIBW_TEST_REQUIRES: pytest cython packaging typing-extensions
159-
CIBW_TEST_COMMAND: >
160-
python -c "import netCDF4; print(f'netCDF4 v{netCDF4.__version__}')"
161-
&& pytest -s -rxs -v {project}\\test
131+
CIBW_ARCHS: ${{ matrix.arch }}
132+
# cannot build cftime for this target (missing a wheel at the time of writing)
133+
CIBW_SKIP: "cp314*"
162134

163135
- uses: actions/upload-artifact@v4
164136
with:

pyproject.toml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ tests = [
4545
"Cython",
4646
"packaging",
4747
"pytest",
48+
"typing-extensions",
4849
]
4950
parallel = [
5051
"mpi4py",
@@ -102,3 +103,48 @@ module = [
102103
"filter_availability",
103104
"matplotlib.*"
104105
]
106+
107+
[tool.cibuildwheel]
108+
build-verbosity = 1
109+
build-frontend = "build"
110+
skip = [
111+
"*-musllinux*",
112+
"cp314t-*",
113+
]
114+
test-extras = "tests"
115+
test-sources = [
116+
"test",
117+
"pyproject.toml"
118+
]
119+
test-command = [
120+
'''python -c "import netCDF4; print(f'netCDF4 v{netCDF4.__version__}')"''',
121+
"pytest -s -rxs -v test",
122+
]
123+
manylinux-x86_64-image = "ghcr.io/ocefpaf/manylinux_2_28_x86_64-netcdf"
124+
manylinux-aarch64-image = "ghcr.io/ocefpaf/manylinux_2_28_aarch64-netcdf"
125+
environment = {NETCDF4_LIMITED_API="1"}
126+
127+
[tool.cibuildwheel.macos]
128+
before-build = "brew install hdf5 netcdf"
129+
130+
[[tool.cibuildwheel.overrides]]
131+
select = "*-macosx*x86_64"
132+
inherit.environment = "append"
133+
environment = {MACOSX_DEPLOYMENT_TARGET="13.0"}
134+
135+
[[tool.cibuildwheel.overrides]]
136+
select = "*-macosx*arm64"
137+
inherit.environment = "append"
138+
environment = {MACOSX_DEPLOYMENT_TARGET="14.0"}
139+
140+
[tool.cibuildwheel.windows]
141+
before-build = "python -m pip install delvewheel"
142+
repair-wheel-command = [
143+
"delvewheel show {wheel}",
144+
"delvewheel repair -w {dest_dir} {wheel}",
145+
]
146+
147+
[[tool.cibuildwheel.overrides]]
148+
select = "*-win_*"
149+
inherit.environment = "append"
150+
environment = {HDF5_DIR='C:\\\\Users\\runneradmin\\micromamba\\envs\\build\\Library', netCDF4_DIR='C:\\\\Users\\runneradmin\\micromamba\\envs\\build\\Library', PATH='C:\\\\Users\\runneradmin\\micromamba\\envs\\build\\Library\\bin;${PATH}' }

setup.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,27 @@
33
import pathlib
44
import shutil
55
import configparser
6+
import sysconfig
67
from setuptools import setup, Extension
78
from setuptools.dist import Distribution
89
from typing import List
910

1011

12+
USE_PY_LIMITED_API = (
13+
# require opt-in (builds are specilized by default)
14+
os.getenv('NETCDF4_LIMITED_API', '0') == '1'
15+
# Cython + numpy + limited API de facto requires Python >=3.11
16+
and sys.version_info >= (3, 11)
17+
# as of Python 3.14t, free-threaded builds don't support the limited API
18+
and not sysconfig.get_config_var("Py_GIL_DISABLED")
19+
)
20+
ABI3_TARGET_VERSION = "".join(str(_) for _ in sys.version_info[:2])
21+
ABI3_TARGET_HEX = hex(sys.hexversion & 0xFFFF00F0)
22+
23+
if USE_PY_LIMITED_API:
24+
SETUP_OPTIONS = {"bdist_wheel": {"py_limited_api": f"cp{ABI3_TARGET_VERSION}"}}
25+
else:
26+
SETUP_OPTIONS = {}
1127

1228
open_kwargs = {'encoding': 'utf-8'}
1329

@@ -436,14 +452,17 @@ def _populate_hdf5_info(dirstosearch, inc_dirs, libs, lib_dirs):
436452
str(nc_complex_dir / "include/generated_fallbacks"),
437453
]
438454
DEFINE_MACROS += [("NC_COMPLEX_NO_EXPORT", "1")]
455+
if USE_PY_LIMITED_API:
456+
DEFINE_MACROS.append(("Py_LIMITED_API", ABI3_TARGET_HEX))
439457

440458
ext_modules = [Extension("netCDF4._netCDF4",
441459
source_files,
442460
define_macros=DEFINE_MACROS,
443461
libraries=libs,
444462
library_dirs=lib_dirs,
445463
include_dirs=include_dirs,
446-
runtime_library_dirs=runtime_lib_dirs)]
464+
runtime_library_dirs=runtime_lib_dirs,
465+
py_limited_api=USE_PY_LIMITED_API)]
447466
# set language_level directive to 3
448467
for e in ext_modules:
449468
e.cython_directives = {'language_level': "3"} #
@@ -477,6 +496,7 @@ def _populate_hdf5_info(dirstosearch, inc_dirs, libs, lib_dirs):
477496
name="netCDF4", # need by GitHub dependency graph
478497
version=extract_version(netcdf4_src_pyx),
479498
ext_modules=ext_modules,
499+
options=SETUP_OPTIONS,
480500
)
481501

482502
# remove plugin files copied from outside source tree

0 commit comments

Comments
 (0)