From 31d467cd51768af5a079b7ede19f8065f29f3658 Mon Sep 17 00:00:00 2001 From: yasuo Date: Tue, 3 Mar 2026 15:55:30 +0900 Subject: [PATCH 1/8] Implement CI to build wheel file and bundle newsynth --- .github/workflows/build.yaml | 197 ++++++++++++++++++++++++ pyproject.toml | 2 +- quration-core/python/pyqret/__init__.py | 48 ++++++ 3 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/build.yaml diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..f3fbecd --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,197 @@ +name: python-tests + +on: + push: + branches: [main, py_wheel] + release: + types: [published] + +jobs: + wheel: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - macos-15-intel + - macos-latest + - windows-latest + - ubuntu-latest + python-version: + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Setup Haskell (GHC/Cabal) + uses: haskell-actions/setup@v2 + with: + ghc-version: "9.6.6" + cabal-version: "3.12.1.0" + + - name: Setup MSVC (Windows) + if: runner.os == 'Windows' + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x64 + + - name: Shorten build paths (Windows) + if: runner.os == 'Windows' + run: | + New-Item -ItemType Directory -Force -Path C:\b | Out-Null + New-Item -ItemType Directory -Force -Path C:\t | Out-Null + Add-Content -Path $env:GITHUB_ENV -Value "SKBUILD_BUILD_DIR=C:\b" + Add-Content -Path $env:GITHUB_ENV -Value "TMP=C:\t" + Add-Content -Path $env:GITHUB_ENV -Value "TEMP=C:\t" + shell: pwsh + + - name: Configure vcpkg toolchain + run: | + mkdir -p "${{ github.workspace }}/.vcpkg" + echo "VCPKG_ROOT=${{ github.workspace }}/.vcpkg" >> "$GITHUB_ENV" + git clone https://github.com/microsoft/vcpkg.git "${{ github.workspace }}/.vcpkg" + ls "${{ github.workspace }}/.vcpkg" + + - name: Install libyaml-cpp (Linux) + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y build-essential gcc g++ libyaml-cpp0.8 libyaml-cpp-dev + + - name: Set compiler env (Linux) + if: runner.os == 'Linux' + run: | + echo "CC=gcc" >> "$GITHUB_ENV" + echo "CXX=g++" >> "$GITHUB_ENV" + + - name: Install libyaml-cpp (macOS) + if: runner.os == 'macOS' + run: | + brew install yaml-cpp + + - name: Configure Homebrew lib path (macOS) + if: runner.os == 'macOS' + run: | + brew_prefix=$(brew --prefix yaml-cpp) + if [ -n "$brew_prefix" ]; then + echo "DYLD_LIBRARY_PATH=$brew_prefix/lib:$DYLD_LIBRARY_PATH" >> "$GITHUB_ENV" + fi + + - name: Set compiler env (macOS) + if: runner.os == 'macOS' + run: | + echo "CC=clang" >> "$GITHUB_ENV" + echo "CXX=clang++" >> "$GITHUB_ENV" + + - name: Build newsynth binaries from Hackage + run: | + cabal update + cabal install newsynth --installdir "$PWD/externals/bin" --install-method=copy --overwrite-policy=always + if [ "$RUNNER_OS" != "Windows" ]; then + chmod +x "$PWD/externals/bin/gridsynth" + fi + ls -la "$PWD/externals/bin" + shell: bash + + - name: Download newsynth LICENSE files + run: | + rm -rf "$PWD/externals/newsynth-src" + rm -rf "$PWD/externals/newsynth-license" + mkdir -p "$PWD/externals/newsynth-src" + mkdir -p "$PWD/externals/newsynth-license" + cabal get newsynth --destdir "$PWD/externals/newsynth-src" --pristine + newsynth_src_dir=$(find "$PWD/externals/newsynth-src" -maxdepth 1 -type d -name "newsynth-*" | head -n 1) + if [ -n "$newsynth_src_dir" ]; then + find "$newsynth_src_dir" -maxdepth 2 -type f \ + \( -iname "LICENSE*" -o -iname "COPYING*" -o -iname "README*" -o -iname "GPL*" \) \ + -exec cp {} "$PWD/externals/newsynth-license/" \; + fi + echo "---" + ls -la "$PWD/externals/newsynth-src" + echo "---" + ls -la "$PWD/externals/newsynth-license" + shell: bash + + - name: Build wheel + env: + SKBUILD_CMAKE_DEFINE: "CMAKE_TOOLCHAIN_FILE=${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake;QRET_BUILD_PYTHON=ON;QRET_PYTHON_WITH_ALGORITHM=ON" + run: | + python -m pip install --upgrade pip build pytest coverage wheel numpy + python -m build --wheel + shell: bash + + - name: Embed newsynth binary and LICENSE files into wheel + run: | + wheel_file=$(ls dist/*.whl) + python -m wheel unpack "$wheel_file" -d dist/unpacked + wheel_dir=$(ls -d dist/unpacked/*) + pkg_dir="$wheel_dir/pyqret/externals/newsynth" + mkdir -p "$pkg_dir" + + # Copy built binaries into pyqret/externals/newsynth. + for file in "$PWD"/externals/bin/*; do + cp "$file" "$pkg_dir/" + done + + # Copy downloaded license files. + find "$PWD/externals/newsynth-license" -mindepth 1 -maxdepth 1 -type f -exec cp {} "$pkg_dir/" \; + + rm -f "$wheel_file" + python -m wheel pack "$wheel_dir" -d dist + ls -la "$pkg_dir" + shell: bash + + - name: Install wheel + run: python -m pip install dist/*.whl + shell: bash + + - name: Run tests + run: python -m pytest quration-core/python/tests + shell: bash + + - name: Upload Python artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: pyqret-wheel-${{ matrix.os }}-py${{ matrix.python-version }} + path: | + dist/*.whl + + merge: + needs: [wheel] + runs-on: ubuntu-latest + steps: + - name: Merge Artifacts + uses: actions/upload-artifact/merge@v4 + with: + name: pyqret + pattern: pyqret-* + + release: + if: github.event_name == 'release' + needs: [merge] + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Download merged artifact + uses: actions/download-artifact@v4 + with: + name: pyqret + path: dist + + - name: Upload artifact to release + uses: softprops/action-gh-release@v2 + with: + files: dist/* diff --git a/pyproject.toml b/pyproject.toml index 4162c73..0dad292 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "pyqret" description = "Python wrapper of QRET" -readme = "quration-core/python/README.md" +readme = "README.md" required-python = ">=3.10" # authors = diff --git a/quration-core/python/pyqret/__init__.py b/quration-core/python/pyqret/__init__.py index 8e53219..8f6c1cf 100644 --- a/quration-core/python/pyqret/__init__.py +++ b/quration-core/python/pyqret/__init__.py @@ -6,6 +6,14 @@ from typing_extensions import Self +import importlib.util + +import os + +import platform + +from pathlib import Path + from . import _qret_impl as _M if TYPE_CHECKING: @@ -281,3 +289,43 @@ def __next__(self): # noqa:ANN204 ret = self._convert_to() ret._impl = next(self._iter) return ret + + +_ENV_PATH = "GRIDSYNTH_PATH" + + +def _qsvt_package_root() -> Path | None: + spec = importlib.util.find_spec("pyqsvt") + if spec is None: + return None + if spec.origin: + return Path(spec.origin).resolve().parent + return None + + +def _set_gridsynth_env_from_package() -> None: + if _ENV_PATH in os.environ: + return + + package_root = _qsvt_package_root() + if package_root is None: + return + + bin_dir = package_root / "externals" / "newsynth" + if not bin_dir.is_dir(): + return + + candidates = [ + bin_dir / "gridsynth", + bin_dir / "gridsynth.exe", + ] + if platform.system().lower() == "windows": + candidates.reverse() + + for path in candidates: + if path.is_file() and os.access(path, os.X_OK): + os.environ[_ENV_PATH] = str(path.resolve()) + break + + +_set_gridsynth_env_from_package() From 26ae4b7e204eaf0fb34f730e49f0c232e8b1e547 Mon Sep 17 00:00:00 2001 From: yasuo Date: Wed, 4 Mar 2026 16:37:25 +0900 Subject: [PATCH 2/8] add gridsynth test --- .github/workflows/build.yaml | 3 +++ quration-core/python/pyqret/__init__.py | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index f3fbecd..2e3b041 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -160,6 +160,9 @@ jobs: run: python -m pytest quration-core/python/tests shell: bash + - name: test gridsynth bundle + run: python -c "import pyqret; import os; import subprocess; subprocess.run([os.environ['GRIDSYNTH_PATH'], '--help'], check=True)" + - name: Upload Python artifacts if: always() uses: actions/upload-artifact@v4 diff --git a/quration-core/python/pyqret/__init__.py b/quration-core/python/pyqret/__init__.py index 8f6c1cf..f785f74 100644 --- a/quration-core/python/pyqret/__init__.py +++ b/quration-core/python/pyqret/__init__.py @@ -294,8 +294,8 @@ def __next__(self): # noqa:ANN204 _ENV_PATH = "GRIDSYNTH_PATH" -def _qsvt_package_root() -> Path | None: - spec = importlib.util.find_spec("pyqsvt") +def _qret_package_root() -> Path | None: + spec = importlib.util.find_spec("pyqret") if spec is None: return None if spec.origin: @@ -307,7 +307,7 @@ def _set_gridsynth_env_from_package() -> None: if _ENV_PATH in os.environ: return - package_root = _qsvt_package_root() + package_root = _qret_package_root() if package_root is None: return From 7ebf5a3fc89b2b4d836d4f3f0acd620613bca4b7 Mon Sep 17 00:00:00 2001 From: yasuo Date: Tue, 17 Mar 2026 15:19:19 +0900 Subject: [PATCH 3/8] Fix --- .github/workflows/build.yaml | 197 ++++++++++++++++++++++++++++------- MANIFEST.in | 2 + pyproject.toml | 10 +- 3 files changed, 162 insertions(+), 47 deletions(-) create mode 100644 MANIFEST.in diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 2e3b041..a53dd9f 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -7,7 +7,7 @@ on: types: [published] jobs: - wheel: + newsynth: runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -17,29 +17,89 @@ jobs: - macos-latest - windows-latest - ubuntu-latest - python-version: - - "3.10" - - "3.11" - - "3.12" - - "3.13" - - "3.14" steps: - name: Checkout uses: actions/checkout@v4 with: submodules: recursive - - name: Setup Python - uses: actions/setup-python@v5 + - name: Restore newsynth cache + id: newsynth-cache + uses: actions/cache@v4 with: - python-version: ${{ matrix.python-version }} + path: | + externals/bin + externals/newsynth-license + key: newsynth-${{ matrix.os }}-v1 - name: Setup Haskell (GHC/Cabal) + if: steps.newsynth-cache.outputs.cache-hit != 'true' uses: haskell-actions/setup@v2 with: ghc-version: "9.6.6" cabal-version: "3.12.1.0" + - name: Build newsynth binaries from Hackage + if: steps.newsynth-cache.outputs.cache-hit != 'true' + run: | + cabal update + cabal install newsynth --installdir "$PWD/externals/bin" --install-method=copy --overwrite-policy=always + if [ "$RUNNER_OS" != "Windows" ]; then + chmod +x "$PWD/externals/bin/gridsynth" + fi + ls -la "$PWD/externals/bin" + shell: bash + + - name: Download newsynth LICENSE files + if: steps.newsynth-cache.outputs.cache-hit != 'true' + run: | + rm -rf "$PWD/externals/newsynth-src" + rm -rf "$PWD/externals/newsynth-license" + mkdir -p "$PWD/externals/newsynth-src" + mkdir -p "$PWD/externals/newsynth-license" + cabal get newsynth --destdir "$PWD/externals/newsynth-src" --pristine + newsynth_src_dir=$(find "$PWD/externals/newsynth-src" -maxdepth 1 -type d -name "newsynth-*" | head -n 1) + if [ -n "$newsynth_src_dir" ]; then + find "$newsynth_src_dir" -maxdepth 2 -type f \ + \( -iname "LICENSE*" -o -iname "COPYING*" -o -iname "README*" -o -iname "GPL*" \) \ + -exec cp {} "$PWD/externals/newsynth-license/" \; + fi + echo "---" + ls -la "$PWD/externals/newsynth-src" + echo "---" + ls -la "$PWD/externals/newsynth-license" + shell: bash + + wheel: + needs: [newsynth] + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - macos-15-intel + - macos-latest + - windows-latest + - ubuntu-latest + python-version: + - "3.10" + #- "3.11" + #- "3.12" + #- "3.13" + #- "3.14" + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: "pip" + cache-dependency-path: pyproject.toml + - name: Setup MSVC (Windows) if: runner.os == 'Windows' uses: ilammy/msvc-dev-cmd@v1 @@ -94,33 +154,18 @@ jobs: echo "CC=clang" >> "$GITHUB_ENV" echo "CXX=clang++" >> "$GITHUB_ENV" - - name: Build newsynth binaries from Hackage - run: | - cabal update - cabal install newsynth --installdir "$PWD/externals/bin" --install-method=copy --overwrite-policy=always - if [ "$RUNNER_OS" != "Windows" ]; then - chmod +x "$PWD/externals/bin/gridsynth" - fi - ls -la "$PWD/externals/bin" - shell: bash + - name: Restore newsynth cache + uses: actions/cache@v4 + with: + path: | + externals/bin + externals/newsynth-license + key: newsynth-${{ matrix.os }}-v1 + fail-on-cache-miss: true - - name: Download newsynth LICENSE files - run: | - rm -rf "$PWD/externals/newsynth-src" - rm -rf "$PWD/externals/newsynth-license" - mkdir -p "$PWD/externals/newsynth-src" - mkdir -p "$PWD/externals/newsynth-license" - cabal get newsynth --destdir "$PWD/externals/newsynth-src" --pristine - newsynth_src_dir=$(find "$PWD/externals/newsynth-src" -maxdepth 1 -type d -name "newsynth-*" | head -n 1) - if [ -n "$newsynth_src_dir" ]; then - find "$newsynth_src_dir" -maxdepth 2 -type f \ - \( -iname "LICENSE*" -o -iname "COPYING*" -o -iname "README*" -o -iname "GPL*" \) \ - -exec cp {} "$PWD/externals/newsynth-license/" \; - fi - echo "---" - ls -la "$PWD/externals/newsynth-src" - echo "---" - ls -la "$PWD/externals/newsynth-license" + - name: Fix newsynth permissions (Unix) + if: runner.os != 'Windows' + run: chmod +x "$PWD/externals/bin/gridsynth" shell: bash - name: Build wheel @@ -156,13 +201,13 @@ jobs: run: python -m pip install dist/*.whl shell: bash + - name: test gridsynth bundle + run: python -c "import pyqret; import os; import subprocess; subprocess.run([os.environ['GRIDSYNTH_PATH'], '--help'], check=True)" + - name: Run tests run: python -m pytest quration-core/python/tests shell: bash - - name: test gridsynth bundle - run: python -c "import pyqret; import os; import subprocess; subprocess.run([os.environ['GRIDSYNTH_PATH'], '--help'], check=True)" - - name: Upload Python artifacts if: always() uses: actions/upload-artifact@v4 @@ -171,8 +216,80 @@ jobs: path: | dist/*.whl + sdist: + needs: [newsynth] + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.10" + cache: "pip" + cache-dependency-path: pyproject.toml + + - name: Configure vcpkg toolchain + run: | + mkdir -p "${{ github.workspace }}/.vcpkg" + echo "VCPKG_ROOT=${{ github.workspace }}/.vcpkg" >> "$GITHUB_ENV" + git clone https://github.com/microsoft/vcpkg.git "${{ github.workspace }}/.vcpkg" + ls "${{ github.workspace }}/.vcpkg" + + - name: Install libyaml-cpp (Linux) + run: | + sudo apt-get update + sudo apt-get install -y build-essential gcc g++ libyaml-cpp0.8 libyaml-cpp-dev + + - name: Set compiler env (Linux) + run: | + echo "CC=gcc" >> "$GITHUB_ENV" + echo "CXX=g++" >> "$GITHUB_ENV" + + - name: Restore newsynth cache + uses: actions/cache@v4 + with: + path: | + externals/bin + externals/newsynth-license + key: newsynth-ubuntu-latest-v1 + fail-on-cache-miss: true + + - name: Set newsynth environment + run: | + chmod +x "$PWD/externals/bin/gridsynth" + echo "GRIDSYNTH_PATH=$PWD/externals/bin/gridsynth" >> "$GITHUB_ENV" + ls -la "$PWD/externals/bin" + shell: bash + + - name: Build sdist + run: | + python -m pip install --upgrade pip build pytest coverage numpy + python -m build --sdist + shell: bash + + - name: Install sdist + env: + SKBUILD_CMAKE_DEFINE: "CMAKE_TOOLCHAIN_FILE=${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake;QRET_BUILD_PYTHON=ON;QRET_PYTHON_WITH_ALGORITHM=ON" + run: python -m pip install -vvv dist/*.tar.gz + shell: bash + + - name: Run tests + run: python -m pytest quration-core/python/tests + shell: bash + + - name: Upload sdist artifact + uses: actions/upload-artifact@v4 + with: + name: pyqret-sdist + path: | + dist/*.tar.gz + merge: - needs: [wheel] + needs: [wheel, sdist] runs-on: ubuntu-latest steps: - name: Merge Artifacts diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..65f4862 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,2 @@ +graft include +graft lib diff --git a/pyproject.toml b/pyproject.toml index 0dad292..15b9df0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,15 +60,11 @@ where = ["quration-core/python"] [tool.scikit-build] # Protect the configuration against future changes in scikit-build-core minimum-version = "0.4" - -# Setuptools-style build caching in a local directory build-dir = "build/{wheel_tag}" - -# Build stable ABI wheels for CPython 3.12+ -wheel.py-api = "cp312" - -# Include Python package from the non-root location. +wheel.py-api = "py3" wheel.packages = ["quration-core/python/pyqret"] +wheel.exclude = ["include/**", "lib/**"] +sdist.include = ["externals/**", "quration-core/src/**"] [tool.scikit-build.cmake.define] # nanobind requires libraries to be position-independent in order to link correctly From 83c5fbc9fe2e7e899091b4b24aa8f720b06d7953 Mon Sep 17 00:00:00 2001 From: yasuo Date: Thu, 26 Mar 2026 20:40:01 +0900 Subject: [PATCH 4/8] support 3.11 or older --- .github/workflows/build.yaml | 206 +++++++++++++++++++++++++++++------ 1 file changed, 175 insertions(+), 31 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index a53dd9f..a0a994b 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -2,7 +2,7 @@ name: python-tests on: push: - branches: [main, py_wheel] + branches: [main, py_wheel, "py_wheel_newabi"] release: types: [published] @@ -81,12 +81,6 @@ jobs: - macos-latest - windows-latest - ubuntu-latest - python-version: - - "3.10" - #- "3.11" - #- "3.12" - #- "3.13" - #- "3.14" steps: - name: Checkout uses: actions/checkout@v4 @@ -96,7 +90,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v5 with: - python-version: ${{ matrix.python-version }} + python-version: "3.10" cache: "pip" cache-dependency-path: pyproject.toml @@ -117,6 +111,7 @@ jobs: shell: pwsh - name: Configure vcpkg toolchain + if: runner.os != 'Linux' run: | mkdir -p "${{ github.workspace }}/.vcpkg" echo "VCPKG_ROOT=${{ github.workspace }}/.vcpkg" >> "$GITHUB_ENV" @@ -168,7 +163,26 @@ jobs: run: chmod +x "$PWD/externals/bin/gridsynth" shell: bash + - name: Build wheel with cibuildwheel (Linux) + if: runner.os == 'Linux' + uses: pypa/cibuildwheel@v2.23.0 + env: + CIBW_BUILD: cp310-manylinux_x86_64 + CIBW_SKIP: "*-musllinux_*" + CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28 + CIBW_BEFORE_ALL_LINUX: | + dnf install -y git gcc gcc-c++ make zip unzip tar curl perl + git clone https://github.com/microsoft/vcpkg.git /project/.vcpkg + /project/.vcpkg/bootstrap-vcpkg.sh -disableMetrics + CIBW_ENVIRONMENT_LINUX: >- + VCPKG_ROOT=/project/.vcpkg + SKBUILD_CMAKE_DEFINE="CMAKE_TOOLCHAIN_FILE=/project/.vcpkg/scripts/buildsystems/vcpkg.cmake;QRET_BUILD_PYTHON=ON;QRET_PYTHON_WITH_ALGORITHM=ON" + with: + package-dir: . + output-dir: dist + - name: Build wheel + if: runner.os != 'Linux' env: SKBUILD_CMAKE_DEFINE: "CMAKE_TOOLCHAIN_FILE=${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake;QRET_BUILD_PYTHON=ON;QRET_PYTHON_WITH_ALGORITHM=ON" run: | @@ -179,6 +193,7 @@ jobs: - name: Embed newsynth binary and LICENSE files into wheel run: | wheel_file=$(ls dist/*.whl) + python -m pip install --upgrade wheel python -m wheel unpack "$wheel_file" -d dist/unpacked wheel_dir=$(ls -d dist/unpacked/*) pkg_dir="$wheel_dir/pyqret/externals/newsynth" @@ -197,22 +212,11 @@ jobs: ls -la "$pkg_dir" shell: bash - - name: Install wheel - run: python -m pip install dist/*.whl - shell: bash - - - name: test gridsynth bundle - run: python -c "import pyqret; import os; import subprocess; subprocess.run([os.environ['GRIDSYNTH_PATH'], '--help'], check=True)" - - - name: Run tests - run: python -m pytest quration-core/python/tests - shell: bash - - name: Upload Python artifacts if: always() uses: actions/upload-artifact@v4 with: - name: pyqret-wheel-${{ matrix.os }}-py${{ matrix.python-version }} + name: pyqret-wheel-${{ matrix.os }} path: | dist/*.whl @@ -258,10 +262,9 @@ jobs: key: newsynth-ubuntu-latest-v1 fail-on-cache-miss: true - - name: Set newsynth environment + - name: Set newsynth permissions run: | chmod +x "$PWD/externals/bin/gridsynth" - echo "GRIDSYNTH_PATH=$PWD/externals/bin/gridsynth" >> "$GITHUB_ENV" ls -la "$PWD/externals/bin" shell: bash @@ -271,25 +274,166 @@ jobs: python -m build --sdist shell: bash - - name: Install sdist - env: - SKBUILD_CMAKE_DEFINE: "CMAKE_TOOLCHAIN_FILE=${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake;QRET_BUILD_PYTHON=ON;QRET_PYTHON_WITH_ALGORITHM=ON" - run: python -m pip install -vvv dist/*.tar.gz + - name: Upload sdist artifact + uses: actions/upload-artifact@v4 + with: + name: pyqret-sdist + path: | + dist/*.tar.gz + + test-wheel: + needs: [wheel] + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - macos-15-intel + - macos-latest + - windows-latest + - ubuntu-latest + python-version: + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: "pip" + cache-dependency-path: pyproject.toml + + - name: Download wheel artifact + uses: actions/download-artifact@v4 + with: + name: pyqret-wheel-${{ matrix.os }} + path: dist + + - name: Install wheel + run: | + python -m pip install --upgrade pip pytest coverage numpy + python -m pip install dist/*.whl shell: bash + - name: test gridsynth bundle + run: python -c "import pyqret; import os; import subprocess; subprocess.run([os.environ['GRIDSYNTH_PATH'], '--help'], check=True)" + - name: Run tests run: python -m pytest quration-core/python/tests shell: bash - - name: Upload sdist artifact - uses: actions/upload-artifact@v4 + test-sdist: + needs: [sdist] + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + #- macos-15-intel + #- macos-latest + #- windows-latest + - ubuntu-latest + python-version: + - "3.10" + #- "3.11" + #- "3.12" + #- "3.13" + #- "3.14" + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: "pip" + cache-dependency-path: pyproject.toml + + - name: Setup MSVC (Windows) + if: runner.os == 'Windows' + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x64 + + - name: Shorten build paths (Windows) + if: runner.os == 'Windows' + run: | + New-Item -ItemType Directory -Force -Path C:\b | Out-Null + New-Item -ItemType Directory -Force -Path C:\t | Out-Null + Add-Content -Path $env:GITHUB_ENV -Value "SKBUILD_BUILD_DIR=C:\b" + Add-Content -Path $env:GITHUB_ENV -Value "TMP=C:\t" + Add-Content -Path $env:GITHUB_ENV -Value "TEMP=C:\t" + shell: pwsh + + - name: Configure vcpkg toolchain + run: | + mkdir -p "${{ github.workspace }}/.vcpkg" + echo "VCPKG_ROOT=${{ github.workspace }}/.vcpkg" >> "$GITHUB_ENV" + git clone https://github.com/microsoft/vcpkg.git "${{ github.workspace }}/.vcpkg" + ls "${{ github.workspace }}/.vcpkg" + + - name: Install libyaml-cpp (Linux) + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y build-essential gcc g++ libyaml-cpp0.8 libyaml-cpp-dev + + - name: Set compiler env (Linux) + if: runner.os == 'Linux' + run: | + echo "CC=gcc" >> "$GITHUB_ENV" + echo "CXX=g++" >> "$GITHUB_ENV" + + - name: Install libyaml-cpp (macOS) + if: runner.os == 'macOS' + run: | + brew install yaml-cpp + + - name: Configure Homebrew lib path (macOS) + if: runner.os == 'macOS' + run: | + brew_prefix=$(brew --prefix yaml-cpp) + if [ -n "$brew_prefix" ]; then + echo "DYLD_LIBRARY_PATH=$brew_prefix/lib:$DYLD_LIBRARY_PATH" >> "$GITHUB_ENV" + fi + + - name: Set compiler env (macOS) + if: runner.os == 'macOS' + run: | + echo "CC=clang" >> "$GITHUB_ENV" + echo "CXX=clang++" >> "$GITHUB_ENV" + + - name: Download sdist artifact + uses: actions/download-artifact@v4 with: name: pyqret-sdist - path: | - dist/*.tar.gz + path: dist + + - name: Install sdist + env: + SKBUILD_CMAKE_DEFINE: "CMAKE_TOOLCHAIN_FILE=${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake;QRET_BUILD_PYTHON=ON;QRET_PYTHON_WITH_ALGORITHM=ON" + run: | + python -m pip install --upgrade pip pytest coverage numpy + python -m pip install -vvv dist/*.tar.gz + shell: bash + + - name: Run tests + run: python -m pytest quration-core/python/tests + shell: bash merge: - needs: [wheel, sdist] + needs: [test-wheel, test-sdist] runs-on: ubuntu-latest steps: - name: Merge Artifacts From 15ea887de97222bcb2a02fa00b0ce02e212d64ef Mon Sep 17 00:00:00 2001 From: yasuo Date: Fri, 27 Mar 2026 12:27:00 +0900 Subject: [PATCH 5/8] use stable abi only for >=12 --- .github/workflows/build.yaml | 38 +++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index a0a994b..32fc485 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -2,7 +2,7 @@ name: python-tests on: push: - branches: [main, py_wheel, "py_wheel_newabi"] + branches: [main, py_wheel] release: types: [published] @@ -81,6 +81,11 @@ jobs: - macos-latest - windows-latest - ubuntu-latest + build-target: + - py310 + - py311 + - py312-stable-api + continue-on-error: true steps: - name: Checkout uses: actions/checkout@v4 @@ -90,7 +95,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v5 with: - python-version: "3.10" + python-version: ${{ matrix.build-target == 'py310' && '3.10' || matrix.build-target == 'py311' && '3.11' || '3.12' }} cache: "pip" cache-dependency-path: pyproject.toml @@ -167,7 +172,7 @@ jobs: if: runner.os == 'Linux' uses: pypa/cibuildwheel@v2.23.0 env: - CIBW_BUILD: cp310-manylinux_x86_64 + CIBW_BUILD: ${{ matrix.build-target == 'py310' && 'cp310-manylinux_x86_64' || matrix.build-target == 'py311' && 'cp311-manylinux_x86_64' || 'cp312-manylinux_x86_64' }} CIBW_SKIP: "*-musllinux_*" CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28 CIBW_BEFORE_ALL_LINUX: | @@ -176,13 +181,14 @@ jobs: /project/.vcpkg/bootstrap-vcpkg.sh -disableMetrics CIBW_ENVIRONMENT_LINUX: >- VCPKG_ROOT=/project/.vcpkg + ${{ matrix.build-target == 'py312-stable-api' && 'SKBUILD_WHEEL_PY_API=cp312' || '' }} SKBUILD_CMAKE_DEFINE="CMAKE_TOOLCHAIN_FILE=/project/.vcpkg/scripts/buildsystems/vcpkg.cmake;QRET_BUILD_PYTHON=ON;QRET_PYTHON_WITH_ALGORITHM=ON" with: package-dir: . output-dir: dist - - name: Build wheel - if: runner.os != 'Linux' + - name: Build wheel (non-stable API, non-Linux) + if: runner.os != 'Linux' && matrix.build-target != 'py312-stable-api' env: SKBUILD_CMAKE_DEFINE: "CMAKE_TOOLCHAIN_FILE=${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake;QRET_BUILD_PYTHON=ON;QRET_PYTHON_WITH_ALGORITHM=ON" run: | @@ -190,6 +196,16 @@ jobs: python -m build --wheel shell: bash + - name: Build wheel (stable API, non-Linux) + if: runner.os != 'Linux' && matrix.build-target == 'py312-stable-api' + env: + SKBUILD_WHEEL_PY_API: "cp312" + SKBUILD_CMAKE_DEFINE: "CMAKE_TOOLCHAIN_FILE=${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake;QRET_BUILD_PYTHON=ON;QRET_PYTHON_WITH_ALGORITHM=ON" + run: | + python -m pip install --upgrade pip build pytest coverage wheel numpy + python -m build --wheel + shell: bash + - name: Embed newsynth binary and LICENSE files into wheel run: | wheel_file=$(ls dist/*.whl) @@ -216,7 +232,7 @@ jobs: if: always() uses: actions/upload-artifact@v4 with: - name: pyqret-wheel-${{ matrix.os }} + name: pyqret-wheel-${{ matrix.os }}-${{ matrix.build-target }} path: | dist/*.whl @@ -314,7 +330,7 @@ jobs: - name: Download wheel artifact uses: actions/download-artifact@v4 with: - name: pyqret-wheel-${{ matrix.os }} + name: pyqret-wheel-${{ matrix.os }}-${{ matrix.python-version == '3.10' && 'py310' || matrix.python-version == '3.11' && 'py311' || 'py312-stable-api' }} path: dist - name: Install wheel @@ -337,16 +353,16 @@ jobs: fail-fast: false matrix: os: - #- macos-15-intel - #- macos-latest - #- windows-latest + - macos-15-intel + - macos-latest + - windows-latest - ubuntu-latest python-version: - "3.10" #- "3.11" #- "3.12" #- "3.13" - #- "3.14" + - "3.14" steps: - name: Checkout uses: actions/checkout@v4 From 21a116cda336071173a8b123c5450ab801bbf148 Mon Sep 17 00:00:00 2001 From: yasuo Date: Mon, 30 Mar 2026 21:33:22 +0900 Subject: [PATCH 6/8] change abi for 3.10-3.11 --- .github/workflows/build.yaml | 115 +++++++++++++++++++++++++---------- 1 file changed, 84 insertions(+), 31 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 32fc485..4cd64cb 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -2,7 +2,7 @@ name: python-tests on: push: - branches: [main, py_wheel] + branches: [main, py_wheel*] release: types: [published] @@ -82,9 +82,16 @@ jobs: - windows-latest - ubuntu-latest build-target: - - py310 - - py311 - - py312-stable-api + - cp310-cp310 + - cp311-cp311 + - cp312-abi3 + include: + - build-target: cp310-cp310 + python-version: "3.10" + - build-target: cp311-cp311 + python-version: "3.11" + - build-target: cp312-abi3 + python-version: "3.12" continue-on-error: true steps: - name: Checkout @@ -95,7 +102,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v5 with: - python-version: ${{ matrix.build-target == 'py310' && '3.10' || matrix.build-target == 'py311' && '3.11' || '3.12' }} + python-version: ${{ matrix.python-version }} cache: "pip" cache-dependency-path: pyproject.toml @@ -123,17 +130,17 @@ jobs: git clone https://github.com/microsoft/vcpkg.git "${{ github.workspace }}/.vcpkg" ls "${{ github.workspace }}/.vcpkg" - - name: Install libyaml-cpp (Linux) - if: runner.os == 'Linux' - run: | - sudo apt-get update - sudo apt-get install -y build-essential gcc g++ libyaml-cpp0.8 libyaml-cpp-dev + # - name: Install libyaml-cpp (Linux) + # if: runner.os == 'Linux' + # run: | + # sudo apt-get update + # sudo apt-get install -y build-essential gcc g++ libyaml-cpp0.8 libyaml-cpp-dev - - name: Set compiler env (Linux) - if: runner.os == 'Linux' - run: | - echo "CC=gcc" >> "$GITHUB_ENV" - echo "CXX=g++" >> "$GITHUB_ENV" + # - name: Set compiler env (Linux) + # if: runner.os == 'Linux' + # run: | + # echo "CC=gcc" >> "$GITHUB_ENV" + # echo "CXX=g++" >> "$GITHUB_ENV" - name: Install libyaml-cpp (macOS) if: runner.os == 'macOS' @@ -172,38 +179,28 @@ jobs: if: runner.os == 'Linux' uses: pypa/cibuildwheel@v2.23.0 env: - CIBW_BUILD: ${{ matrix.build-target == 'py310' && 'cp310-manylinux_x86_64' || matrix.build-target == 'py311' && 'cp311-manylinux_x86_64' || 'cp312-manylinux_x86_64' }} + CIBW_BUILD: ${{ matrix.build-target == 'cp310-cp310' && 'cp310-manylinux_x86_64' || matrix.build-target == 'cp311-cp311' && 'cp311-manylinux_x86_64' || 'cp312-manylinux_x86_64' }} CIBW_SKIP: "*-musllinux_*" CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28 + CIBW_CONFIG_SETTINGS: ${{ matrix.build-target == 'cp312-abi3' && 'wheel.py-api=cp312' || 'wheel.py-api=' }} CIBW_BEFORE_ALL_LINUX: | dnf install -y git gcc gcc-c++ make zip unzip tar curl perl git clone https://github.com/microsoft/vcpkg.git /project/.vcpkg /project/.vcpkg/bootstrap-vcpkg.sh -disableMetrics CIBW_ENVIRONMENT_LINUX: >- VCPKG_ROOT=/project/.vcpkg - ${{ matrix.build-target == 'py312-stable-api' && 'SKBUILD_WHEEL_PY_API=cp312' || '' }} SKBUILD_CMAKE_DEFINE="CMAKE_TOOLCHAIN_FILE=/project/.vcpkg/scripts/buildsystems/vcpkg.cmake;QRET_BUILD_PYTHON=ON;QRET_PYTHON_WITH_ALGORITHM=ON" with: package-dir: . output-dir: dist - - name: Build wheel (non-stable API, non-Linux) - if: runner.os != 'Linux' && matrix.build-target != 'py312-stable-api' - env: - SKBUILD_CMAKE_DEFINE: "CMAKE_TOOLCHAIN_FILE=${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake;QRET_BUILD_PYTHON=ON;QRET_PYTHON_WITH_ALGORITHM=ON" - run: | - python -m pip install --upgrade pip build pytest coverage wheel numpy - python -m build --wheel - shell: bash - - - name: Build wheel (stable API, non-Linux) - if: runner.os != 'Linux' && matrix.build-target == 'py312-stable-api' + - name: Build wheel (non-Linux) + if: runner.os != 'Linux' env: - SKBUILD_WHEEL_PY_API: "cp312" SKBUILD_CMAKE_DEFINE: "CMAKE_TOOLCHAIN_FILE=${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake;QRET_BUILD_PYTHON=ON;QRET_PYTHON_WITH_ALGORITHM=ON" run: | python -m pip install --upgrade pip build pytest coverage wheel numpy - python -m build --wheel + python -m build --wheel -Cwheel.py-api=${{ matrix.build-target == 'cp312-abi3' && 'cp312' || '' }} shell: bash - name: Embed newsynth binary and LICENSE files into wheel @@ -223,11 +220,67 @@ jobs: # Copy downloaded license files. find "$PWD/externals/newsynth-license" -mindepth 1 -maxdepth 1 -type f -exec cp {} "$pkg_dir/" \; + # Remove unexpected top-level lib64 directory from the wheel payload. + rm -rf "$wheel_dir/lib64" + rm -f "$wheel_file" python -m wheel pack "$wheel_dir" -d dist ls -la "$pkg_dir" shell: bash + - name: Validate wheel filename and extension tags + run: | + wheel_file=$(ls dist/*.whl) + wheel_name=$(basename "$wheel_file") + echo "Built wheel: $wheel_name" + + expected_tag="${{ matrix.build-target }}-" + + if [[ "$wheel_name" != *"$expected_tag"* ]]; then + echo "Unexpected wheel ABI tag for ${{ matrix.build-target }}." + echo "Actual filename: $wheel_name" + echo "Expected filename to contain: $expected_tag" + exit 1 + fi + + check_dir="dist/wheel-check" + rm -rf "$check_dir" + mkdir -p "$check_dir" + python -m zipfile -e "$wheel_file" "$check_dir" + + target_tag="${{ matrix.build-target }}" + py_tag="${target_tag%%-*}" + abi_tag="${target_tag#*-}" + py_major_minor="${py_tag#cp}" + py_major="${py_major_minor:0:1}" + py_minor="${py_major_minor:1}" + cpython_tag="cpython-${py_major}${py_minor}" + found_ext=0 + + while IFS= read -r ext_file; do + found_ext=1 + ext_name=$(basename "$ext_file") + echo "Found extension stub: $ext_name" + if [[ "$ext_name" == *"$abi_tag"* || "$ext_name" == *"$py_tag"* || "$ext_name" == *"$cpython_tag"* ]]; then + continue + fi + + if [[ "${{ runner.os }}" == "Windows" && "$ext_name" == *.pyd ]]; then + echo "Accepting untagged Windows extension stub: $ext_name" + continue + fi + + echo "Extension stub tag mismatch for ${{ matrix.build-target }}: $ext_name" + echo "Expected extension filename to contain one of: $abi_tag, $py_tag, $cpython_tag" + exit 1 + done < <(find "$check_dir" -type f \( -name "*.so" -o -name "*.dylib" -o -name "*.lib" -o -name "*.pyd" \)) + + if [ "$found_ext" -eq 0 ]; then + echo "No extension stub (*.so/*.dylib/*.lib/*.pyd) found in wheel." + exit 1 + fi + shell: bash + - name: Upload Python artifacts if: always() uses: actions/upload-artifact@v4 @@ -330,7 +383,7 @@ jobs: - name: Download wheel artifact uses: actions/download-artifact@v4 with: - name: pyqret-wheel-${{ matrix.os }}-${{ matrix.python-version == '3.10' && 'py310' || matrix.python-version == '3.11' && 'py311' || 'py312-stable-api' }} + name: pyqret-wheel-${{ matrix.os }}-${{ matrix.python-version == '3.10' && 'cp310-cp310' || matrix.python-version == '3.11' && 'cp311-cp311' || 'cp312-abi3' }} path: dist - name: Install wheel From f174e7095ac02e238ff7375bba33868549f5a466 Mon Sep 17 00:00:00 2001 From: yasuo Date: Wed, 1 Apr 2026 20:41:13 +0900 Subject: [PATCH 7/8] Ignore lib64 dir for wheel --- MANIFEST.in | 1 + pyproject.toml | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 65f4862..72ed58e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,3 @@ graft include graft lib +graft lib64 diff --git a/pyproject.toml b/pyproject.toml index 15b9df0..5da1dfd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,10 +60,16 @@ where = ["quration-core/python"] [tool.scikit-build] # Protect the configuration against future changes in scikit-build-core minimum-version = "0.4" + +# Setuptools-style build caching in a local directory build-dir = "build/{wheel_tag}" -wheel.py-api = "py3" + +# Build stable ABI wheels for CPython 3.12+ +wheel.py-api = "cp312" + +# Include Python package from the non-root location. wheel.packages = ["quration-core/python/pyqret"] -wheel.exclude = ["include/**", "lib/**"] +wheel.exclude = ["include/**", "lib/**", "lib64/**"] sdist.include = ["externals/**", "quration-core/src/**"] [tool.scikit-build.cmake.define] From 474a48ecf7f192449821d784955d34ad4efa92a6 Mon Sep 17 00:00:00 2001 From: yasuo Date: Fri, 3 Apr 2026 18:48:51 +0900 Subject: [PATCH 8/8] disable algo --- .github/workflows/build.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 4cd64cb..9b2d941 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -189,7 +189,7 @@ jobs: /project/.vcpkg/bootstrap-vcpkg.sh -disableMetrics CIBW_ENVIRONMENT_LINUX: >- VCPKG_ROOT=/project/.vcpkg - SKBUILD_CMAKE_DEFINE="CMAKE_TOOLCHAIN_FILE=/project/.vcpkg/scripts/buildsystems/vcpkg.cmake;QRET_BUILD_PYTHON=ON;QRET_PYTHON_WITH_ALGORITHM=ON" + SKBUILD_CMAKE_DEFINE="CMAKE_TOOLCHAIN_FILE=/project/.vcpkg/scripts/buildsystems/vcpkg.cmake;QRET_BUILD_PYTHON=ON" with: package-dir: . output-dir: dist @@ -197,7 +197,7 @@ jobs: - name: Build wheel (non-Linux) if: runner.os != 'Linux' env: - SKBUILD_CMAKE_DEFINE: "CMAKE_TOOLCHAIN_FILE=${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake;QRET_BUILD_PYTHON=ON;QRET_PYTHON_WITH_ALGORITHM=ON" + SKBUILD_CMAKE_DEFINE: "CMAKE_TOOLCHAIN_FILE=${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake;QRET_BUILD_PYTHON=ON" run: | python -m pip install --upgrade pip build pytest coverage wheel numpy python -m build --wheel -Cwheel.py-api=${{ matrix.build-target == 'cp312-abi3' && 'cp312' || '' }} @@ -491,7 +491,7 @@ jobs: - name: Install sdist env: - SKBUILD_CMAKE_DEFINE: "CMAKE_TOOLCHAIN_FILE=${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake;QRET_BUILD_PYTHON=ON;QRET_PYTHON_WITH_ALGORITHM=ON" + SKBUILD_CMAKE_DEFINE: "CMAKE_TOOLCHAIN_FILE=${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake;QRET_BUILD_PYTHON=ON" run: | python -m pip install --upgrade pip pytest coverage numpy python -m pip install -vvv dist/*.tar.gz