diff --git a/.github/scripts/install-macos-build-deps.sh b/.github/scripts/install-macos-build-deps.sh index ec3029b2..5820e4c3 100755 --- a/.github/scripts/install-macos-build-deps.sh +++ b/.github/scripts/install-macos-build-deps.sh @@ -1,14 +1,6 @@ #!/bin/bash -set -ex +set -euxo pipefail -# update brew -brew update - -# Update openssl if necessary -brew outdated openssl || brew upgrade openssl - -# Install packages needed to build lib-secp256k1 -for pkg in pkg-config; do - brew list $pkg > /dev/null || brew install $pkg - brew outdated --quiet $pkg || brew upgrade $pkg -done +# CMake uses pkg-config to detect system libraries. Avoid updating the entire +# Homebrew installation in every CI job; the hosted runner image is immutable. +brew list pkg-config > /dev/null 2>&1 || brew install pkg-config diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml new file mode 100644 index 00000000..e0343cc7 --- /dev/null +++ b/.github/workflows/benchmark.yml @@ -0,0 +1,27 @@ +name: benchmark + +on: + workflow_dispatch: + +jobs: + benchmark: + name: Run benchmarks + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-python@v6 + with: + python-version: '3.14' + + - name: Install GMP headers + run: | + sudo apt-get update + sudo apt-get install --yes libgmp-dev + + - name: Install uv + uses: astral-sh/setup-uv@v8 + + - name: Run benchmarks + run: uv run --python-preference system scripts/bench.py diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index aa62bbf5..78f194e8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,11 +11,11 @@ on: - master concurrency: - group: build-${{ github.head_ref }} - cancel-in-progress: true + group: build-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/') }} env: - PYTHON_VERSION: '3.13' + PYTHON_VERSION: '3.14' COINCURVE_IGNORE_SYSTEM_LIB: 'ON' COINCURVE_SECP256K1_STATIC: 'ON' COINCURVE_CROSS_HOST: '' @@ -33,10 +33,28 @@ env: assert a.ecdh(b.public_key.format())==b.ecdh(a.public_key.format()) " && python -m pytest {project} - CIBW_SKIP: > - pp* jobs: + quality: + name: Static analysis and types + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-python@v6 + with: + python-version: ${{ env.PYTHON_VERSION }} + + - name: Install Hatch + uses: pypa/hatch@install + + - name: Run static analysis + run: hatch fmt --check + + - name: Check types + run: hatch run types:check + test: name: Test Python ${{ matrix.python-version }} on ${{ startsWith(matrix.os, 'macos-') && 'macOS' || startsWith(matrix.os, 'windows-') && 'Windows' || 'Linux' }} runs-on: ${{ matrix.os }} @@ -44,14 +62,12 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.14t"] steps: - - name: Checkout code - uses: actions/checkout@v5 + - uses: actions/checkout@v7 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v6 + - uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} @@ -59,91 +75,166 @@ jobs: if: runner.os == 'macOS' run: ./.github/scripts/install-macos-build-deps.sh - - name: Install Hatch - uses: pypa/hatch@install + - name: Install project and test dependencies + run: python -m pip install . pytest pytest-randomly - - name: Run static analysis - run: hatch fmt --check + - name: Run tests + run: python -m pytest tests - - name: Check types - run: hatch run types:check + coverage: + name: Upload coverage + runs-on: ubuntu-latest + env: + PYTHON_VERSION: '3.14t' - - name: Run tests - run: hatch test --python ${{ matrix.python-version }} --cover-quiet --randomize + permissions: + contents: read + id-token: write - - name: Create coverage report - run: hatch run hatch-test.py${{ matrix.python-version }}:coverage xml + steps: + - uses: actions/checkout@v7 - - name: Upload coverage data - uses: actions/upload-artifact@v4 + - uses: actions/setup-python@v6 with: - name: coverage-${{ matrix.os }}-${{ matrix.python-version }} - path: coverage.xml + python-version: ${{ env.PYTHON_VERSION }} - - name: Install uv - uses: astral-sh/setup-uv@v5 + - name: Install Hatch + uses: pypa/hatch@install - - name: Benchmark - run: uv run --python-preference system scripts/bench.py + - name: Run tests with coverage + run: hatch test --python ${{ env.PYTHON_VERSION }} --cover-quiet --randomize - coverage: - name: Upload coverage + - name: Create coverage report + run: hatch run hatch-test.py${{ env.PYTHON_VERSION }}:coverage xml + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v7 + with: + files: coverage.xml + use_oidc: true + + sdist: + name: Build source distribution needs: + - quality - test runs-on: ubuntu-latest - permissions: - contents: read - id-token: write + steps: + - uses: actions/checkout@v7 + + - name: Install build dependencies + run: python -m pip install build + + - name: Build source distribution + run: python -m build --sdist + + - uses: actions/upload-artifact@v7 + with: + name: artifact-sdist + path: dist/* + if-no-files-found: error + + prerelease-source: + name: Test sdist on Python ${{ matrix.python.version }} on ${{ startsWith(matrix.os, 'macos-') && 'macOS' || startsWith(matrix.os, 'windows-') && 'Windows' || 'Linux' }} + needs: + - sdist + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + python: + - version: 3.15-dev + free-threaded: false + - version: 3.15t-dev + free-threaded: true steps: - - name: Download coverage data - uses: actions/download-artifact@v5 + - uses: actions/checkout@v7 + + - uses: actions/setup-python@v6 with: - pattern: coverage-* - path: coverage_data + python-version: ${{ matrix.python.version }} + check-latest: true - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v5 + - name: Verify prerelease interpreter + env: + EXPECTED_FREE_THREADED: ${{ matrix.python.free-threaded }} + run: python scripts/check_prerelease_python.py + + - uses: actions/download-artifact@v8 with: - directory: coverage_data - use_oidc: true + name: artifact-sdist + path: dist + + - name: Install source distribution + run: > + python -m pip install pytest pytest-randomly + dist/coincurve-22.0.0.tar.gz + + - name: Run tests + run: python -m pytest tests linux-wheels-x86_64: name: Build Linux wheels for x86-64 needs: + - quality - test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v7 - name: Build wheels - uses: pypa/cibuildwheel@v2.23 + uses: pypa/cibuildwheel@v4.1.0 + env: + CIBW_ARCHS_LINUX: x86_64 - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: artifact-linux-wheels-x86_64 path: wheelhouse/*.whl if-no-files-found: error + linux-wheels-arm64: + name: Build Linux wheels for ARM64 + needs: + - quality + - test + runs-on: ubuntu-24.04-arm + + steps: + - uses: actions/checkout@v7 + + - name: Build wheels + uses: pypa/cibuildwheel@v4.1.0 + env: + CIBW_ARCHS_LINUX: aarch64 + + - uses: actions/upload-artifact@v7 + with: + name: artifact-linux-wheels-arm64 + path: wheelhouse/*.whl + if-no-files-found: error + macos-wheels-x86_64: name: Build macOS wheels for x86-64 needs: + - quality - test runs-on: macos-15-intel steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v7 - name: Build wheels - uses: pypa/cibuildwheel@v3.2.0 + uses: pypa/cibuildwheel@v4.1.0 env: CIBW_ARCHS_MACOS: x86_64 - CIBW_SKIP: "cp314t-*" - MACOSX_DEPLOYMENT_TARGET: 10.13 + MACOSX_DEPLOYMENT_TARGET: '10.15' - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: artifact-macos-wheels-x86_64 path: wheelhouse/*.whl @@ -152,16 +243,19 @@ jobs: macos-wheels-arm64: name: Build macOS wheels for ARM64 needs: + - quality - test runs-on: macos-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v7 - name: Build wheels - uses: pypa/cibuildwheel@v2.23 + uses: pypa/cibuildwheel@v4.1.0 + env: + CIBW_ARCHS_MACOS: arm64 - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: artifact-macos-wheels-arm64 path: wheelhouse/*.whl @@ -170,23 +264,20 @@ jobs: windows-wheels-x86_64: name: Build Windows wheels for x86-64 needs: + - quality - test runs-on: windows-latest steps: - - uses: actions/checkout@v5 - - - uses: actions/setup-python@v6 - with: - python-version: ${{ env.PYTHON_VERSION }} + - uses: actions/checkout@v7 - name: Build wheels - uses: pypa/cibuildwheel@v2.23 + uses: pypa/cibuildwheel@v4.1.0 env: - CIBW_ARCHS_WINDOWS: 'AMD64' + CIBW_ARCHS_WINDOWS: AMD64 CIBW_BEFORE_ALL: choco install -y --no-progress --no-color cmake>=3.28 - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: artifact-windows-wheels-x86_64 path: wheelhouse/*.whl @@ -195,82 +286,61 @@ jobs: windows-wheels-arm64: name: Build Windows wheels for ARM64 needs: + - quality - test - runs-on: windows-latest + runs-on: windows-11-arm steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v7 - name: Build wheels - uses: pypa/cibuildwheel@v2.23 + uses: pypa/cibuildwheel@v4.1.0 env: - COINCURVE_CROSS_HOST: 'arm64' - CIBW_ARCHS_WINDOWS: 'ARM64' + CIBW_ARCHS_WINDOWS: ARM64 CIBW_BEFORE_ALL: choco install -y --no-progress --no-color cmake>=3.28 - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: artifact-windows-wheels-arm64 path: wheelhouse/*.whl if-no-files-found: error - sdist: - name: Build source distribution + validate-distribution: + name: Validate release artifacts needs: - - test + - prerelease-source + - linux-wheels-x86_64 + - linux-wheels-arm64 + - macos-wheels-x86_64 + - macos-wheels-arm64 + - windows-wheels-x86_64 + - windows-wheels-arm64 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 - - - name: Install build dependencies - run: python -m pip install build + - uses: actions/checkout@v7 - - name: Build source distribution - run: python -m build --sdist - - - uses: actions/upload-artifact@v4 + - uses: actions/download-artifact@v8 with: - name: artifact-sdist - path: dist/* - if-no-files-found: error - - linux-wheels-arm64: - name: Build Linux wheels for ARM64 - needs: - - test - runs-on: ubuntu-latest - if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.event.ref, 'refs/tags')) - - steps: - - uses: actions/checkout@v5 + pattern: artifact-* + merge-multiple: true + path: dist - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - with: - platforms: arm64 + - name: Install validation tools + run: python -m pip install "packaging>=24.2" check-wheel-contents twine - - name: Build wheels - uses: pypa/cibuildwheel@v2.23 - env: - CIBW_ARCHS_LINUX: aarch64 + - name: Validate metadata and wheel contents + run: | + python -m twine check dist/* + check-wheel-contents dist/*.whl - - uses: actions/upload-artifact@v4 - with: - name: artifact-linux-wheels-arm64 - path: wheelhouse/*.whl - if-no-files-found: error + - name: Validate artifact inventory + run: python scripts/check_dist.py dist 22.0.0 publish: name: Publish release needs: - - linux-wheels-x86_64 - - macos-wheels-x86_64 - - macos-wheels-arm64 - - windows-wheels-x86_64 - - windows-wheels-arm64 - - sdist - - linux-wheels-arm64 + - validate-distribution runs-on: ubuntu-latest if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') @@ -278,14 +348,13 @@ jobs: id-token: write steps: - - uses: actions/download-artifact@v5 + - uses: actions/download-artifact@v8 with: pattern: artifact-* merge-multiple: true path: dist - - run: ls -l dist - name: Push build artifacts to PyPI - uses: pypa/gh-action-pypi-publish@v1.13.0 + uses: pypa/gh-action-pypi-publish@v1.14.0 with: skip-existing: true diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 2827c288..7a687e90 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v7 with: # Fetch all history for applying timestamps to every page fetch-depth: 0 @@ -32,7 +32,7 @@ jobs: - name: Build documentation run: hatch run docs:build - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: documentation path: site @@ -45,7 +45,7 @@ jobs: - build steps: - - uses: actions/download-artifact@v5 + - uses: actions/download-artifact@v8 with: name: documentation path: site diff --git a/.github/workflows/verify_conda_build.yml b/.github/workflows/verify_conda_build.yml index 3f4c415e..fd69a722 100644 --- a/.github/workflows/verify_conda_build.yml +++ b/.github/workflows/verify_conda_build.yml @@ -11,8 +11,8 @@ on: - master concurrency: - group: build_conda-${{ github.head_ref }} - cancel-in-progress: true + group: conda-build-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/') }} env: PYTHON_VERSION: '3.12' @@ -20,23 +20,6 @@ env: COINCURVE_IGNORE_SYSTEM_LIB: 'OFF' COINCURVE_SECP256K1_STATIC: 'OFF' COINCURVE_CROSS_HOST: '' - CIBW_ENVIRONMENT_PASS_LINUX: > - COINCURVE_UPSTREAM_REF - COINCURVE_IGNORE_SYSTEM_LIB - COINCURVE_SECP256K1_STATIC - COINCURVE_CROSS_HOST - CIBW_BEFORE_ALL_MACOS: ./.github/scripts/install-macos-build-deps.sh - CIBW_TEST_REQUIRES: pytest - CIBW_TEST_COMMAND: > - python -c - "from coincurve import PrivateKey; - a=PrivateKey(); - b=PrivateKey(); - assert a.ecdh(b.public_key.format())==b.ecdh(a.public_key.format()) - " && - python -m pytest {project} - CIBW_SKIP: > - pp* jobs: test: @@ -47,7 +30,7 @@ jobs: shell: bash -el {0} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v7 - name: Install Miniconda uses: conda-incubator/setup-miniconda@v3 @@ -60,20 +43,8 @@ jobs: - name: Install Hatch uses: pypa/hatch@install - - name: Run static analysis - run: hatch fmt --check - - - name: Check types - run: hatch run types:check - - name: Run tests - run: LD_LIBRARY_PATH=$CONDA_PREFIX/lib hatch test - - - name: Install uv - uses: astral-sh/setup-uv@v5 - - - name: Benchmark - run: LD_LIBRARY_PATH=$CONDA_PREFIX/lib uv run --python-preference system scripts/bench.py + run: LD_LIBRARY_PATH="$CONDA_PREFIX/lib" hatch test --python "$PYTHON_VERSION" --randomize linux-wheels-x86_64: name: Build Linux wheels @@ -85,7 +56,7 @@ jobs: shell: bash -el {0} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v7 - name: Install Miniconda uses: conda-incubator/setup-miniconda@v3 diff --git a/.github/workflows/verify_shared_build.yml b/.github/workflows/verify_shared_build.yml index bce2f171..5e4db761 100644 --- a/.github/workflows/verify_shared_build.yml +++ b/.github/workflows/verify_shared_build.yml @@ -11,18 +11,25 @@ on: - master concurrency: - group: build_shared-${{ github.head_ref }} - cancel-in-progress: true + group: shared-build-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/') }} env: - PYTHON_VERSION: '3.12' - COINCURVE_IGNORE_SYSTEM_LIB: '1' - # Only 'SHARED' is recognized, any other string means 'not SHARED' - COINCURVE_SECP256K1_BUILD: 'SHARED' + PYTHON_VERSION: '3.14' + COINCURVE_IGNORE_SYSTEM_LIB: 'ON' + COINCURVE_SECP256K1_STATIC: 'OFF' + COINCURVE_CROSS_HOST: '' + CIBW_BUILD: 'cp314-*' CIBW_ENVIRONMENT_PASS_LINUX: > COINCURVE_IGNORE_SYSTEM_LIB - COINCURVE_SECP256K1_BUILD + COINCURVE_SECP256K1_STATIC + COINCURVE_CROSS_HOST CIBW_BEFORE_ALL_MACOS: ./.github/scripts/install-macos-build-deps.sh + # The shared Windows wheel already installs libsecp256k1 beside the extension. + # delvewheel only searches outside the wheel for that dependency, so preserve + # the already self-contained wheel instead of trying to vendor it a second time. + CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: >- + python -c "import shutil; shutil.copy2(r'{wheel}', r'{dest_dir}')" CIBW_TEST_REQUIRES: pytest CIBW_TEST_COMMAND: > python -c @@ -32,116 +39,79 @@ env: assert a.ecdh(b.public_key.format())==b.ecdh(a.public_key.format()) " && python -m pytest {project} - CIBW_TEST_SKIP: "*-macosx_arm64" - CIBW_SKIP: > - pp* jobs: - test: - name: Test latest Python + linux-wheels-x86_64: + name: Build shared Linux wheel for x86-64 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 - - - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@v6 - with: - python-version: ${{ env.PYTHON_VERSION }} - - - name: Install system dependencies - if: runner.os == 'macOS' - run: ./.github/scripts/install-macos-build-deps.sh - - - name: Install Hatch - uses: pypa/hatch@install - - - name: Run static analysis - run: hatch fmt --check - - - name: Check types - run: hatch run types:check - - - name: Run tests - run: hatch test - - - name: Install uv - uses: astral-sh/setup-uv@v5 + - uses: actions/checkout@v7 - - name: Benchmark - run: uv run --python-preference system scripts/bench.py + - name: Build wheel + uses: pypa/cibuildwheel@v4.1.0 + env: + CIBW_ARCHS_LINUX: x86_64 - linux-wheels-x86_64: - name: Build Linux wheels for x86-64 - needs: - - test - runs-on: ubuntu-latest + linux-wheels-arm64: + name: Build shared Linux wheel for ARM64 + runs-on: ubuntu-24.04-arm steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v7 - - name: Build wheels - uses: pypa/cibuildwheel@v2.23 + - name: Build wheel + uses: pypa/cibuildwheel@v4.1.0 + env: + CIBW_ARCHS_LINUX: aarch64 macos-wheels-x86_64: - name: Build macOS wheels for x86-64 - needs: - - test + name: Build shared macOS wheel for x86-64 runs-on: macos-15-intel steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v7 - - name: Build wheels - uses: pypa/cibuildwheel@v3.2.0 + - name: Build wheel + uses: pypa/cibuildwheel@v4.1.0 env: CIBW_ARCHS_MACOS: x86_64 - CIBW_SKIP: "cp314t-*" - MACOSX_DEPLOYMENT_TARGET: 10.13 + MACOSX_DEPLOYMENT_TARGET: '10.15' macos-wheels-arm64: - name: Build macOS wheels for ARM64 - needs: - - test + name: Build shared macOS wheel for ARM64 runs-on: macos-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v7 - - name: Build wheels - uses: pypa/cibuildwheel@v2.23 + - name: Build wheel + uses: pypa/cibuildwheel@v4.1.0 + env: + CIBW_ARCHS_MACOS: arm64 windows-wheels-x86_64: - name: Build Windows wheels for x86-64 - needs: - - test + name: Build shared Windows wheel for x86-64 runs-on: windows-latest steps: - - uses: actions/checkout@v5 - - - uses: actions/setup-python@v6 - with: - python-version: ${{ env.PYTHON_VERSION }} + - uses: actions/checkout@v7 - - name: Build wheels - uses: pypa/cibuildwheel@v2.23 + - name: Build wheel + uses: pypa/cibuildwheel@v4.1.0 env: - CIBW_ARCHS_WINDOWS: 'AMD64' + CIBW_ARCHS_WINDOWS: AMD64 CIBW_BEFORE_ALL: choco install -y --no-progress --no-color cmake>=3.28 windows-wheels-arm64: - name: Build Windows wheels for ARM64 - needs: - - test - runs-on: windows-latest + name: Build shared Windows wheel for ARM64 + runs-on: windows-11-arm steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v7 - - name: Build wheels - uses: pypa/cibuildwheel@v2.23 + - name: Build wheel + uses: pypa/cibuildwheel@v4.1.0 env: - COINCURVE_CROSS_HOST: 'arm64' - CIBW_ARCHS_WINDOWS: 'ARM64' + CIBW_ARCHS_WINDOWS: ARM64 CIBW_BEFORE_ALL: choco install -y --no-progress --no-color cmake>=3.28 diff --git a/CMakeLists.txt b/CMakeLists.txt index ea135d03..c4bd6b73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,7 @@ set(CFFI_HEADERS_DIR ${PROJECT_BINARY_DIR}/_gen_cffi_headers) set(CFFI_OUTPUT_LIBRARY "_${VENDORED_LIBRARY_PKG_CONFIG}") # Setting python for the host system (before change in CMAKE_SYSTEM_PROCESSOR) -find_package(Python 3 REQUIRED COMPONENTS Interpreter Development.Module Development.SABIModule) +find_package(Python 3 REQUIRED COMPONENTS Interpreter Development.Module) include(SetCrossCompilerGithubActions) SetCrossCompilerGithubActions() @@ -43,5 +43,23 @@ add_subdirectory(cm_library_cffi_headers) add_subdirectory(cm_library_c_binding) add_subdirectory(cm_python_module) -# Configure installation of the shared library ${CFFI_OUTPUT_LIBRARY} in the package -install(TARGETS ${CFFI_OUTPUT_LIBRARY} LIBRARY DESTINATION ${SKBUILD_PLATLIB_DIR}/${SKBUILD_PROJECT_NAME}) +# Configure installation of the extension in the package. +set(PROJECT_PACKAGE_DIR ${SKBUILD_PLATLIB_DIR}/${SKBUILD_PROJECT_NAME}) +install(TARGETS ${CFFI_OUTPUT_LIBRARY} LIBRARY DESTINATION ${PROJECT_PACKAGE_DIR}) + +# A dynamically linked vendored libsecp256k1 must survive the temporary build +# directory so wheel repair tools can find it. Keep it beside the extension and +# use a loader-relative runtime path; Windows already searches the module's +# directory for dependent DLLs. +if ((PROJECT_IGNORE_SYSTEM_LIB OR NOT VENDORED_AS_SYSTEM_LIB_FOUND) AND NOT VENDORED_LIBRARY_STATIC_BUILD) + if (APPLE) + set_target_properties(${CFFI_OUTPUT_LIBRARY} PROPERTIES INSTALL_RPATH "@loader_path") + elseif (UNIX) + set_target_properties(${CFFI_OUTPUT_LIBRARY} PROPERTIES INSTALL_RPATH "$ORIGIN") + endif() + install( + TARGETS ${CFFI_INPUT_LIBRARY} + LIBRARY DESTINATION ${PROJECT_PACKAGE_DIR} + RUNTIME DESTINATION ${PROJECT_PACKAGE_DIR} + ) +endif() diff --git a/LICENSE-cffi b/LICENSE-cffi new file mode 100644 index 00000000..a19e1eea --- /dev/null +++ b/LICENSE-cffi @@ -0,0 +1,21 @@ +Except when otherwise stated (look for LICENSE files in directories or +information at the beginning of each file) all software and +documentation is licensed as follows: + + MIT No Attribution + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the + Software is furnished to do so. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. diff --git a/cm_library_c_binding/build.py b/cm_library_c_binding/build.py index 47505a4f..f056bbdc 100644 --- a/cm_library_c_binding/build.py +++ b/cm_library_c_binding/build.py @@ -66,7 +66,7 @@ def mk_ffi( code.append(source.include) code.append("#define PY_USE_BUNDLED") - _ffi.set_source(name, "\n".join(code)) + _ffi.set_source(name, "\n".join(code), py_limited_api=False) return _ffi diff --git a/cm_python_module/CMakeLists.txt b/cm_python_module/CMakeLists.txt index c73cc637..98ab8a73 100644 --- a/cm_python_module/CMakeLists.txt +++ b/cm_python_module/CMakeLists.txt @@ -1,11 +1,10 @@ -# Create the shared library from the CFFI binding and the static library from ${CFFI_INPUT_LIBRARY} -if (CMAKE_SYSTEM_NAME STREQUAL "Windows") - Python_add_library(${CFFI_OUTPUT_LIBRARY} MODULE USE_SABI 3.8 "${CFFI_C_CODE_DIR}/${CFFI_C_CODE}") -else() - set(Python_SOABI ${SKBUILD_SOABI}) - Python_add_library(${CFFI_OUTPUT_LIBRARY} MODULE WITH_SOABI "${CFFI_C_CODE_DIR}/${CFFI_C_CODE}") - target_compile_definitions(${CFFI_OUTPUT_LIBRARY} PRIVATE Py_LIMITED_API) -endif() +# Create an interpreter-specific shared library from the CFFI binding. +# The bundled _cffi_backend is interpreter-specific as well, so the limited API +# cannot make these wheels abi3. Free-threaded CPython 3.14 also requires the +# extension to be built without Py_LIMITED_API. +set(Python_SOABI ${SKBUILD_SOABI}) +Python_add_library(${CFFI_OUTPUT_LIBRARY} MODULE WITH_SOABI "${CFFI_C_CODE_DIR}/${CFFI_C_CODE}") +target_compile_definitions(${CFFI_OUTPUT_LIBRARY} PRIVATE _CFFI_NO_LIMITED_API) set_source_files_properties("${CFFI_C_CODE_DIR}/${CFFI_C_CODE}" PROPERTIES GENERATED 1) @@ -20,7 +19,6 @@ if (PROJECT_IGNORE_SYSTEM_LIB OR NOT VENDORED_AS_SYSTEM_LIB_FOUND) target_include_directories(${CFFI_OUTPUT_LIBRARY} PUBLIC ${VENDORED_HEADERS_DIR}) # Link the vendored library to the output library - # https://docs.python.org/3/c-api/stable.html#limited-c-api target_link_libraries(${CFFI_OUTPUT_LIBRARY} PRIVATE ${CFFI_INPUT_LIBRARY}) elseif(VENDORED_AS_SYSTEM_LIB_FOUND) message(STATUS "Vendored system library found: ${VENDORED_AS_SYSTEM_LIB_LIBRARIES}") diff --git a/docs/history.md b/docs/history.md index 5d47dac9..5cdbe0e2 100644 --- a/docs/history.md +++ b/docs/history.md @@ -4,7 +4,18 @@ Important changes are emphasized. -## Unreleased +## 22.0.0 + +- **Breaking:** Drop support for Python 3.9 +- **Breaking:** Stop building 32-bit Linux wheels +- Add binary wheels for Python 3.14 +- Add binary wheels for free-threaded Python 3.14 +- Add source-build support for Python 3.15, including free-threaded builds +- Fix source builds when the installed CFFI distribution does not include its license metadata +- Fix `COINCURVE_VENDOR_CFFI` so disabling it also excludes the bundled `_cffi_backend` module +- Build Windows ARM64 and Linux AArch64 wheels on native GitHub Actions runners +- Require macOS 10.15 or later for x86-64 wheels +- Improve release artifact validation and reduce duplicated CI work ## 21.0.0 diff --git a/docs/install.md b/docs/install.md index d321bdb6..0fbdfabb 100644 --- a/docs/install.md +++ b/docs/install.md @@ -10,16 +10,22 @@ pip install coincurve ## Wheel -Binary wheels are available for most platforms and require at least version `19.3` of pip to install. +Binary wheels are available for most platforms. An up-to-date version of pip is recommended, +particularly when installing free-threaded wheels. | | | | | | | --- | --- | --- | --- | --- | | | macOS | Windows | Linux (glibc) | Linux (musl) | -| CPython 3.9 |