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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/scripts/cibw_before_all_macos.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/env bash
# One-time (per runner) setup for building macOS wheels with cibuildwheel.
# Installs the non-Python-specific casacore dependencies via Homebrew and
# downloads the sources that cibw_before_build_macos.sh compiles once per
# Python version, mirroring docker/py_wheel.docker in the casacore repo.
set -euxo pipefail

source "$(dirname "${BASH_SOURCE[0]}")/cibw_env.sh"

# bison: casacore needs >= 3 and macOS ships 2.3. flex ships with macOS.
# No BLAS package: casacore links the Accelerate framework instead.
brew install bison ccache cfitsio fftw gcc gsl hdf5 libdeflate wcslib
ccache -M 1G

mkdir -p "$CIBW_SRC"
cd "$CIBW_SRC"

# Boost source: Boost.Python must be compiled per Python version.
boost_underscored=${CIBW_BOOST_VERSION//./_}
curl -fsSL --retry 3 \
"https://archives.boost.io/release/${CIBW_BOOST_VERSION}/source/boost_${boost_underscored}.tar.bz2" \
-o boost.tar.bz2
tar xjf boost.tar.bz2

# casacore source: libcasa_python3 must be compiled per Python version.
curl -fsSL --retry 3 \
"https://github.com/casacore/casacore/archive/refs/tags/v${CIBW_CASACORE_VERSION}.tar.gz" \
-o casacore.tar.gz
tar xzf casacore.tar.gz

# Measures data, bundled into the wheels; see CIBW_DATA in cibw_env.sh.
mkdir -p "$CIBW_DATA"
curl -fsSL --retry 3 https://www.astron.nl/iers/WSRT_Measures.ztar -o measures.tgz
tar xzf measures.tgz -C "$CIBW_DATA"
95 changes: 95 additions & 0 deletions .github/scripts/cibw_before_build_macos.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#!/usr/bin/env bash
# Per-Python-version build of Boost.Python and casacore for macOS wheels.
# cibuildwheel runs this before building each wheel, with the target Python
# first on PATH as `python`. It is the macOS analogue of docker/py_wheel.docker
# in the casacore repo: libcasa_python3 embeds the CPython ABI, so both
# libraries are rebuilt per Python version into CIBW_PREFIX, where the
# python-casacore build finds them via CMAKE_PREFIX_PATH.
#
# As in the Linux wheel images, libcasa_python3 must not link a real
# libpython, or the wheels would only work with one specific interpreter:
# Python3_LIBRARY is pointed at Boost.Python and Python symbols are left
# undefined, to be resolved from the host interpreter at import time.
set -euxo pipefail

source "$(dirname "${BASH_SOURCE[0]}")/cibw_env.sh"

BREW_PREFIX=$(brew --prefix)
export PATH="$BREW_PREFIX/opt/bison/bin:$PATH"
NCPU=$(sysctl -n hw.ncpu)

PYTHON=$(command -v python)
PY_VER=$("$PYTHON" -c 'import sys; print("%d.%d" % sys.version_info[:2])')
PY_XY=${PY_VER//./}
PY_INCLUDE=$("$PYTHON" -c 'import sysconfig; print(sysconfig.get_path("include"))')

rm -rf "$CIBW_PREFIX"

# ---- Boost.Python against the target Python ------------------------------
# numpy is installed only afterwards, so that b2 skips boost_numpy, matching
# the build order in docker/py_wheel.docker.
cd "$CIBW_SRC/boost_${CIBW_BOOST_VERSION//./_}"
b2_args=()
if command -v ccache >/dev/null; then
# Route b2 through ccache too, via the darwin toolset so Boost.Python
# keeps its macOS-specific linking (no libpython; see the header).
echo "using darwin : : ccache clang++ ;" > user-config.jam
b2_args=(--user-config=user-config.jam)
fi
./bootstrap.sh --prefix="$CIBW_PREFIX" \
--with-libraries=python \
--with-python="$PYTHON" \
--with-python-version="$PY_VER"
# b2 cannot derive the include dir from a virtualenv Python; the same
# workaround as in docker/py_wheel.docker.
./b2 -j"$NCPU" "${b2_args[@]}" cxxflags="-fPIC -I$PY_INCLUDE" install

# ---- casacore against the target Python ----------------------------------
"$PYTHON" -m pip install numpy

# Flags taken from casacore's own recipes: docker/py_wheel.docker and its
# Homebrew formulae.
casacore_args=(
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_INSTALL_PREFIX="$CIBW_PREFIX"
-DBUILD_TESTING=OFF
-DBUILD_PYTHON=OFF
-DBUILD_PYTHON3=ON
-DPython3_EXECUTABLE="$PYTHON"
-DPython3_INCLUDE_DIR="$PY_INCLUDE"
# Boost.Python stands in for libpython; see the header comment.
-DPython3_LIBRARY="$CIBW_PREFIX/lib/libboost_python${PY_XY}.dylib"
-DPORTABLE=TRUE
-DUSE_PCH=FALSE
-DUSE_OPENMP=OFF
-DUSE_FFTW3=ON
-DUSE_HDF5=ON
-DDATA_DIR="$CIBW_DATA"
)

# macOS-specific additions, not present in the casacore recipes.
macos_args=(
# Find the Homebrew-installed dependencies.
-DCMAKE_PREFIX_PATH="$BREW_PREFIX"
# Find the per-Python Boost built above.
-DBoost_ROOT="$CIBW_PREFIX"
# Allow undefined symbols, so that Python symbols resolve from the host
# interpreter at import time. ELF linkers allow this by default, which is
# why the Linux recipe needs no such flag.
-DCMAKE_SHARED_LINKER_FLAGS="-Wl,-undefined,dynamic_lookup"
)

# ccache makes the repeated casacore builds cheap after the first.
CCACHE_ARGS=()
if command -v ccache >/dev/null; then
CCACHE_ARGS=(
-DCMAKE_C_COMPILER_LAUNCHER=ccache
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache
)
fi

rm -rf "$CIBW_SRC/casacore-build"
cmake -S "$CIBW_SRC/casacore-$CIBW_CASACORE_VERSION" -B "$CIBW_SRC/casacore-build" \
"${casacore_args[@]}" "${macos_args[@]}" "${CCACHE_ARGS[@]}"
cmake --build "$CIBW_SRC/casacore-build" --parallel "$NCPU"
cmake --install "$CIBW_SRC/casacore-build"
20 changes: 20 additions & 0 deletions .github/scripts/cibw_env.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
# Shared configuration for the macOS cibuildwheel scripts. On Linux the
# equivalent values are frozen into the per-Python wheel images built from
# docker/py_wheel.docker in the casacore repo; macOS has no such images, so
# the before-* scripts build the dependencies at CI time.

# Dependency versions.
export CIBW_BOOST_VERSION="1.91.0"
export CIBW_CASACORE_VERSION="3.8.1"

# Everything under one root, so a single CMAKE_PREFIX_PATH finds both installs.
# CIBW_PREFIX, CIBW_DATA and CCACHE_DIR are duplicated as literals in
# pyproject.toml; CCACHE_DIR also appears in build_release.yml.
export CIBW_ROOT="$HOME/cibw"
export CIBW_SRC="$CIBW_ROOT/src" # extracted boost + casacore sources
export CIBW_PREFIX="$CIBW_ROOT/local" # install prefix for boost + casacore
export CCACHE_DIR="$CIBW_ROOT/ccache" # persisted across CI runs
# The leaf directory must be named "data": python-casacore installs it into
# the wheel by basename, and casacore/__init__.py expects casacore/data.
export CIBW_DATA="$CIBW_ROOT/measures/data"
17 changes: 16 additions & 1 deletion .github/workflows/build_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,29 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest] #, macos-latest]
os: [ubuntu-latest, macos-15] # macos-15 is Sequoia, arm64

steps:
- uses: actions/checkout@v7
with:
# Needed for `setuptools-scm`
fetch-depth: 0

- name: Cache compiler output from the casacore builds
if: runner.os == 'macOS'
uses: actions/cache@v6
with:
# CCACHE_DIR in .github/scripts/cibw_env.sh
path: ~/cibw/ccache
key: ccache-${{ matrix.os }}-${{ hashFiles('.github/scripts/cibw_env.sh') }}
restore-keys: ccache-${{ matrix.os }}-

# We're using Homebrew for deps, so we can only build
# wheels targeting the runner's macOS version
- name: Match wheel deployment target to the runner
if: runner.os == 'macOS'
run: echo "MACOSX_DEPLOYMENT_TARGET=$(sw_vers -productVersion | cut -d. -f1).0" >> "$GITHUB_ENV"

- name: Build wheels
uses: pypa/cibuildwheel@v4.1

Expand Down
30 changes: 24 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,23 +51,41 @@ Homepage = "https://github.com/casacore/python-casacore"
#########################

[tool.cibuildwheel]
build = "cp3{10,11,12,13,14}-*_x86_64"
# Each runner builds only its native architecture: x86_64 on Linux,
# arm64 on the macOS runner.
build = "cp3{10,11,12,13,14}-*"
build-verbosity = 1
environment = """ \
CXXFLAGS="-I/usr/include/cfitsio" \
LD_LIBRARY_PATH=/opt/boost/lib \
"""
test-command = "cd {package}/tests && pytest"
test-requires = "pytest"

[tool.cibuildwheel.macos]
# before-build compiles Boost.Python and casacore against each Python version,
# the macOS analogue of the per-Python Linux images below; see the script for
# details.
before-all = "bash {project}/.github/scripts/cibw_before_all_macos.sh"
before-build = "bash {project}/.github/scripts/cibw_before_build_macos.sh"
# cibuildwheel cannot source cibw_env.sh, so these are literals. Keep the
# paths in sync with CIBW_PREFIX, CIBW_DATA and CCACHE_DIR there. CMAKE_ARGS
# routes the extension build through the same ccache as the dependencies.
environment = """ \
CMAKE_PREFIX_PATH=$HOME/cibw/local \
CASACORE_DATA=$HOME/cibw/measures/data \
CCACHE_DIR=$HOME/cibw/ccache \
CMAKE_ARGS="-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache" \
"""
repair-wheel-command = """\
DYLD_LIBRARY_PATH=${BOOST_INSTALL_DIR}/lib delocate-wheel \
source {project}/.github/scripts/cibw_env.sh && \
DYLD_LIBRARY_PATH=$CIBW_PREFIX/lib delocate-wheel \
--require-archs {delocate_archs} -w {dest_dir} -v {wheel}\
"""

[tool.cibuildwheel.linux]
skip = ["*-musllinux_*"]
# These paths exist inside the prebuilt wheel images selected below.
environment = """ \
CXXFLAGS="-I/usr/include/cfitsio" \
LD_LIBRARY_PATH=/opt/boost/lib \
"""

[[tool.cibuildwheel.overrides]]
select="cp310-*"
Expand Down