Skip to content
Merged
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
265 changes: 173 additions & 92 deletions .github/workflows/pythonpackage.yml
Original file line number Diff line number Diff line change
@@ -1,105 +1,186 @@
name: pyroomacoustics

on: [push, pull_request]
on:
push:
branches: [master]
tags: ["v*", "test-*"]
pull_request:
types: [opened, synchronize, reopened, labeled]
workflow_dispatch:

jobs:
build:
permissions:
contents: read

runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
max-parallel: 12
matrix:
# switch temporarily to windows-2019 due to a breaking
# change making mutex constexpr
# a crash occurs if python and the extension are built
# with different versions
# over time, this should hopefully resolves itself
# ref: https://github.com/microsoft/STL/issues/4875
os: [ubuntu-latest, macos-15-intel, macos-latest, windows-latest]
python-version: [3.9, "3.10", "3.11", "3.12", "3.13", "3.14"]
exclude:
- os: macos-latest
python-version: 3.8
- os: macos-latest
python-version: 3.9
jobs:
setup-sofa:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# ➡️ CRITICAL FIX: Set a short temporary directory
- name: Set short temp path for Windows build
if: matrix.os == 'windows-latest'
shell: powershell
run: |
$short_temp = "C:\T"
mkdir $short_temp -Force
echo "TEMP=$short_temp" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
echo "TMP=$short_temp" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
- name: Checkout submodules
shell: bash
run: |
auth_header="$(git config --local --get http.https://github.com/.extraheader)"
git submodule sync --recursive
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
- name: Cache SOFA data
uses: actions/cache@v4
with:
path: pyroomacoustics/data/sofa
key: sofa-data-${{ hashFiles('pyroomacoustics/data/sofa_files.json') }}

build_sdist:
needs: setup-sofa
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Upgrade pip
python-version: "3.9"
- name: Build sdist
run: |
python -m pip install --upgrade pip
- name: Workaround for windows and python 3.8
if: matrix.os == 'windows-latest' && matrix.python-version == 3.8
run: |
pip install netCDF4<=1.5.8
# There is no binary package of netCF4>=1.6.0 for windows and python 3.7
# the largest supported version is 1.5.8
- name: Install dependencies
run: |
pip install -r requirements.txt
- name: Build package
run: |
python -m pip install -e .
- name: Test with pytest
run: |
pip install -U pytest setuptools build wheel twine
pytest
- name: Test the universal wheels
if: matrix.os == 'ubuntu-latest'
run: |
pip install -U setuptools setuptools_scm build twine
python -m build --sdist
twine check dist/*
- name: Test the binary wheels
if: matrix.os != 'ubuntu-latest'
run: |
python -m build --wheel
twine check dist/*
- name: Publish sdist to pypi
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v') && matrix.os == 'ubuntu-latest'
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
twine upload --skip-existing dist/*
- name: Publish bdist to pypi
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v') && matrix.os != 'ubuntu-latest'
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
twine upload --skip-existing dist/*
- name: Publish sdist to pypi-test
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/test-') && matrix.os == 'ubuntu-latest'
- uses: actions/upload-artifact@v6
with:
name: sdist
path: dist/*.tar.gz

build_wheels_pr:
if: github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'full-build')
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
cibw_build: "cp314-manylinux_x86_64"
cibw_archs: "x86_64"
- os: macos-latest
cibw_build: "cp314-macosx_universal2"
cibw_archs: "universal2"
- os: windows-latest
cibw_build: "cp314-win_amd64"
cibw_archs: "AMD64"
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
fetch-depth: 0
- name: Restore SOFA data
uses: actions/cache/restore@v4
with:
path: pyroomacoustics/data/sofa
key: sofa-data-${{ hashFiles('pyroomacoustics/data/sofa_files.json') }}
- uses: pypa/cibuildwheel@v3.4.0
env:
TWINE_USERNAME: ${{ secrets.PYPITEST_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPITEST_PASSWORD }}
TWINE_REPOSITORY_URL: https://test.pypi.org/legacy/
run: |
twine upload --skip-existing dist/*
- name: Publish bdist to pypi-test
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/test-') && matrix.os != 'ubuntu-latest'
CIBW_BUILD: ${{ matrix.cibw_build }}
CIBW_ARCHS: ${{ matrix.cibw_archs }}
CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28
CIBW_TEST_REQUIRES: "pytest numpy scipy matplotlib python-sofa soxr"
CIBW_TEST_COMMAND: "pytest {project}/tests"
CIBW_ENVIRONMENT: PYROOMACOUSTICS_DATA_PATH={project}/pyroomacoustics/data/sofa

build_wheels:
needs: setup-sofa
if: >-
github.event_name == 'push' ||
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'full-build'))
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
# Linux x86_64
- { os: ubuntu-latest, python: "cp39-*", arch: "x86_64" }
- { os: ubuntu-latest, python: "cp310-*", arch: "x86_64" }
- { os: ubuntu-latest, python: "cp311-*", arch: "x86_64" }
- { os: ubuntu-latest, python: "cp312-*", arch: "x86_64" }
- { os: ubuntu-latest, python: "cp313-*", arch: "x86_64" }
- { os: ubuntu-latest, python: "cp314-*", arch: "x86_64" }
# Linux aarch64
- { os: ubuntu-latest, python: "cp311-*", arch: "aarch64" }
- { os: ubuntu-latest, python: "cp312-*", arch: "aarch64" }
- { os: ubuntu-latest, python: "cp313-*", arch: "aarch64" }
- { os: ubuntu-latest, python: "cp314-*", arch: "aarch64" }
# MacOS (universal2 builds both x86_64 and arm64)
- { os: macos-latest, python: "cp39-*", arch: "universal2" }
- { os: macos-latest, python: "cp310-*", arch: "universal2" }
- { os: macos-latest, python: "cp311-*", arch: "universal2" }
- { os: macos-latest, python: "cp312-*", arch: "universal2" }
- { os: macos-latest, python: "cp313-*", arch: "universal2" }
- { os: macos-latest, python: "cp314-*", arch: "universal2" }
# Windows
- { os: windows-latest, python: "cp39-*", arch: "AMD64" }
- { os: windows-latest, python: "cp310-*", arch: "AMD64" }
- { os: windows-latest, python: "cp311-*", arch: "AMD64" }
- { os: windows-latest, python: "cp312-*", arch: "AMD64" }
- { os: windows-latest, python: "cp313-*", arch: "AMD64" }
- { os: windows-latest, python: "cp314-*", arch: "AMD64" }
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
fetch-depth: 0
- name: Set up QEMU
if: runner.os == 'Linux' && matrix.arch == 'aarch64'
uses: docker/setup-qemu-action@v3
with:
platforms: all
- name: Restore SOFA data
uses: actions/cache/restore@v4
with:
path: pyroomacoustics/data/sofa
key: sofa-data-${{ hashFiles('pyroomacoustics/data/sofa_files.json') }}
- uses: pypa/cibuildwheel@v3.4.0
env:
TWINE_USERNAME: ${{ secrets.PYPITEST_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPITEST_PASSWORD }}
TWINE_REPOSITORY_URL: https://test.pypi.org/legacy/
run: |
twine upload --skip-existing dist/*
CIBW_BUILD: ${{ matrix.python }}
CIBW_ARCHS: ${{ matrix.arch }}
CIBW_SKIP: "*-musllinux_*"
CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28
CIBW_MANYLINUX_AARCH64_IMAGE: manylinux_2_28
CIBW_TEST_REQUIRES: "pytest numpy scipy matplotlib python-sofa soxr"
CIBW_TEST_COMMAND: "pytest {project}/tests"
CIBW_TEST_SKIP: "cp314-*"
CIBW_ENVIRONMENT: PYROOMACOUSTICS_DATA_PATH={project}/pyroomacoustics/data/sofa
- uses: actions/upload-artifact@v6
with:
name: cibw-wheels-${{ matrix.os }}-${{ matrix.arch }}-${{ strategy.job-index }}
path: wheelhouse/*.whl

publish:
needs: [build_sdist, build_wheels]
runs-on: ubuntu-latest
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v')
environment: pypi
permissions:
id-token: write # MANDATORY for OIDC publishing
steps:
- uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
verbose: true
attestations: false

publish-test:
needs: [build_sdist, build_wheels]
runs-on: ubuntu-latest
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/test-')
environment: testpypi
permissions:
id-token: write # MANDATORY for OIDC publishing
steps:
- uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true
- name: Publish to PyPI Test
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
verbose: true
attestations: false
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ pyroomacoustics.egg-info/
pyroomacoustics/build_rir.c
pyroomacoustics/**/*.so
pyroomacoustics/directivities/tests/data/*.pdf
# version file generated by setuptools_scm
pyroomacoustics/version.py
6 changes: 0 additions & 6 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,6 +0,0 @@
[submodule "pyroomacoustics/libroom_src/ext/eigen"]
path = pyroomacoustics/libroom_src/ext/eigen
url = https://github.com/eigenteam/eigen-git-mirror.git
[submodule "pyroomacoustics/libroom_src/ext/nanoflann"]
path = pyroomacoustics/libroom_src/ext/nanoflann
url = https://github.com/jlblancoc/nanoflann.git
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ repos:
rev: 7.0.0
hooks:
- id: isort
files: ^(pyroomacoustics/|examples/|setup\.py)
files: ^(pyroomacoustics/|examples/|tests/|setup\.py)

- repo: https://github.com/psf/black
rev: 26.1.0
hooks:
- id: black
files: ^(pyroomacoustics/|examples/|setup\.py)
files: ^(pyroomacoustics/|examples/|tests/|setup\.py)

# - repo: https://github.com/pycqa/flake8
# rev: 7.3.0
Expand Down
13 changes: 12 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,18 @@ adheres to `Semantic Versioning <http://semver.org/spec/v2.0.0.html>`_.
`Unreleased`_
-------------

Nothing yet.
Changed
~~~~~~~

- Modernized the build system to use ``pyproject.toml`` and ``CMake``.
- External dependencies (``Eigen``, ``nanoflann``, ``pybind11``) are now
automatically managed via CMake's ``FetchContent``.
- Switched to dynamic versioning using ``setuptools_scm``.
- Relocated the test suite from the source package to a top-level ``tests/`` directory.
- Update the continuous integration to use cibuildwheel to handle all platforms
including manylinux.
- The location of the SOFA files can be specified by the
``PYROOMACOUSTICS_DATA_PATH`` environment variable.


`0.10.0`_ - 2026-04-01
Expand Down
49 changes: 49 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
cmake_minimum_required(VERSION 3.15)
set(CMAKE_POLICY_VERSION_MINIMUM 3.5)

project(pyroomacoustics)

# Find pybind11 (handled by external/CMakeLists.txt via FetchContent)
set(PYBIND11_FINDPYTHON ON)

# adds the external dependencies
add_subdirectory(external)

# Source directory for libroom
set(LIBROOM_SRC_DIR pyroomacoustics/libroom_src)

# Collect source files
set(LIBROOM_SOURCES
${LIBROOM_SRC_DIR}/libroom.cpp
)

# Define the extension module
pybind11_add_module(libroom ${LIBROOM_SOURCES})

# Include directories
target_include_directories(libroom PRIVATE
.
${LIBROOM_SRC_DIR}
)

# Link dependencies from FetchContent
target_link_libraries(libroom PRIVATE
Eigen3::Eigen
nanoflann::nanoflann
)

# Compile options
if(MSVC)
target_compile_options(libroom PRIVATE /EHsc)
else()
target_compile_options(libroom PRIVATE -O3 -Wall -fvisibility=hidden)
endif()

# Eigen definitions
target_compile_definitions(libroom PRIVATE EIGEN_MPL2_ONLY EIGEN_NO_DEBUG)

# Setting the output name and location
set_target_properties(libroom PROPERTIES
PREFIX ""
OUTPUT_NAME "libroom"
)
Loading
Loading