pax_global_header00006660000000000000000000000064147727517720014535gustar00rootroot0000000000000052 comment=b30827d2fe66427e2394a39d653d547ab4fd9e57 scikit-build-core-0.11.1/000077500000000000000000000000001477275177200151265ustar00rootroot00000000000000scikit-build-core-0.11.1/.distro/000077500000000000000000000000001477275177200165105ustar00rootroot00000000000000scikit-build-core-0.11.1/.distro/.fmf/000077500000000000000000000000001477275177200173365ustar00rootroot00000000000000scikit-build-core-0.11.1/.distro/.fmf/version000066400000000000000000000000021477275177200207360ustar00rootroot000000000000001 scikit-build-core-0.11.1/.distro/plans/000077500000000000000000000000001477275177200176255ustar00rootroot00000000000000scikit-build-core-0.11.1/.distro/plans/examples.fmf000066400000000000000000000001311477275177200221300ustar00rootroot00000000000000summary: Documentation examples discover+: filter: "tag: examples" execute: how: tmt scikit-build-core-0.11.1/.distro/plans/main.fmf000066400000000000000000000003441477275177200212440ustar00rootroot00000000000000discover: how: fmf path: . adjust+: # Cannot use initiator: fedora-ci reliably yet when: initiator is not defined or initiator != packit discover+: dist-git-source: true dist-git-extract: scikit_build_core-*/ scikit-build-core-0.11.1/.distro/plans/smoke.fmf000066400000000000000000000001621477275177200214340ustar00rootroot00000000000000/: inherit: false summary: Basic smoke tests discover: how: fmf filter: "tag: smoke" execute: how: tmt scikit-build-core-0.11.1/.distro/python-scikit-build-core.spec000066400000000000000000000033151477275177200242160ustar00rootroot00000000000000%global debug_package %{nil} Name: python-scikit-build-core Version: 0.0.0 Release: %autorelease Summary: Build backend for CMake based projects # The main project is licensed under Apache-2.0, but it has a vendored project # src/scikit_build_core/_vendor/pyproject_metadata: MIT # https://github.com/scikit-build/scikit-build-core/issues/933 License: Apache-2.0 AND MIT URL: https://github.com/scikit-build/scikit-build-core Source: %{pypi_source scikit_build_core} BuildRequires: python3-devel # Testing dependences BuildRequires: cmake BuildRequires: ninja-build BuildRequires: gcc BuildRequires: gcc-c++ BuildRequires: git %global _description %{expand: A next generation Python CMake adapter and Python API for plugins } %description %_description %package -n python3-scikit-build-core Summary: %{summary} Requires: cmake Requires: ninja-build BuildArch: noarch Provides: bundled(python3dist(pyproject-metadata)) = 0.9.1 Obsoletes: python3-scikit-build-core+pyproject < 0.10.7-3 %description -n python3-scikit-build-core %_description %prep %autosetup -n scikit_build_core-%{version} # Rename the bundled license so that it can be installed together cp -p src/scikit_build_core/_vendor/pyproject_metadata/LICENSE LICENSE-pyproject-metadata %generate_buildrequires %pyproject_buildrequires -x test,test-meta,test-numpy %build %pyproject_wheel %install %pyproject_install %pyproject_save_files scikit_build_core %check %pyproject_check_import %pytest \ -m "not network" %files -n python3-scikit-build-core -f %{pyproject_files} %license LICENSE LICENSE-pyproject-metadata %doc README.md %changelog %autochangelog scikit-build-core-0.11.1/.distro/tests/000077500000000000000000000000001477275177200176525ustar00rootroot00000000000000scikit-build-core-0.11.1/.distro/tests/smoke.fmf000066400000000000000000000002751477275177200214660ustar00rootroot00000000000000# Common test variables tag: [ smoke ] tier: 0 path: / # Define tests /version: summary: Read version test: python3 -c "import scikit_build_core; print(scikit_build_core.__version__)" scikit-build-core-0.11.1/.fmf/000077500000000000000000000000001477275177200157545ustar00rootroot00000000000000scikit-build-core-0.11.1/.fmf/version000066400000000000000000000000021477275177200173540ustar00rootroot000000000000001 scikit-build-core-0.11.1/.git_archival.txt000066400000000000000000000001531477275177200204000ustar00rootroot00000000000000node: 97a1387c24dd311fe1df7bfc531dce9baaff405b node-date: 2025-03-21T16:30:26-04:00 describe-name: v0.11.1 scikit-build-core-0.11.1/.gitattributes000066400000000000000000000000401477275177200200130ustar00rootroot00000000000000.git_archival.txt export-subst scikit-build-core-0.11.1/.github/000077500000000000000000000000001477275177200164665ustar00rootroot00000000000000scikit-build-core-0.11.1/.github/CONTRIBUTING.md000066400000000000000000000240221477275177200207170ustar00rootroot00000000000000# Testing a project with a branch / main If you are testing a downstream project, you can use a branch of scikit-build-core like this: ```toml [build-system] requires = ["scikit-build-core @ git+https://github.com/scikit-build/scikit-build-core@main"] build-backend = "scikit_build_core.build" ``` Or you can build your project from the scikit-build-core source with nox: ```bash nox -s downstream -- https://github.com/... ``` # Setting up for development See the [Scientific-Python Development Guide][skdev] for a detailed description of best practices for developing packages. [skdev]: https://learn.scientific-python.org/development ## Quick development The fastest way to start with development is to use nox. If you don't have nox, you can use `pipx run nox` to run it without installing, or `pipx install nox`. If you don't have pipx (pip for applications), then you can install with with `pip install pipx` (the only case were installing an application with regular pip is reasonable). If you use macOS, then pipx and nox are both in brew, use `brew install pipx nox`. To use, run `nox`. This will lint and test using every installed version of Python on your system, skipping ones that are not installed. You can also run specific jobs: ```console $ nox -s lint # Lint only $ nox -s tests-3.9 # Python 3.9 tests only $ nox -s docs -- serve # Build and serve the docs $ nox -s build # Make an SDist and wheel ``` Nox handles everything for you, including setting up an temporary virtual environment for each run. ## Setting up a development environment manually You can set up a development environment by running: ```bash python3 -m venv .venv source ./.venv/bin/activate pip install -v -e .[dev] ``` If you have the [Python Launcher for Unix](https://github.com/brettcannon/python-launcher), you can instead do: ```bash py -m venv .venv py -m install -v -e .[dev] ``` ## Post setup You should prepare pre-commit, which will help you by checking that commits pass required checks: ```bash pip install pre-commit # or brew install pre-commit on macOS pre-commit install # Will install a pre-commit hook into the git repo ``` You can also/alternatively run `pre-commit run` (changes only) or `pre-commit run --all-files` to check even without installing the hook. ## Testing Use pytest to run the unit checks: ```bash pytest ``` ## Quick local running You can use this to build and use this with an isolated environment: ```bash pipx run build --wheel PIP_FIND_LINKS="dist" pipx run build --wheel tests/packages/simple_pyproject_ext -o dist ``` ## Building docs You can build the docs using: ```bash nox -s docs ``` You can see a preview with: ```bash nox -s docs -- serve ``` ## Pre-commit This project uses pre-commit for all style checking. While you can run it with nox, this is such an important tool that it deserves to be installed on its own. Install pre-commit and run: ```bash pre-commit run -a ``` to check all files. # Design info This section covers the design of scikit-build-core. Included modules: - `.cmake`: `CMake`/`CMaker` general interface for building code - `.fileapi`: Interface for reading the CMake File API - `.builder`: Generalized backend builder and related helpers - `.settings`: The configuration system, reading from pyproject.toml, PEP 517 The build backend and plugins: - `.build`: PEP 517 builder - `.setuptools`: The setuptools Extension interface (and PEP 517 hooks) (will likely be split out) - `.setuptools.build_api`: Wrapper injecting build requirements - `.hatch`: The hatchling extension (will likely be split out) config, and envvars ## Basic CMake usage ```python from packaging.specifiers import SpecifierSet from scikit_build_core.cmake import CMake, CMaker cmake = CMake.default_search(version=SpecifierSet(">=3.15")) config = CMaker( cmake, source_dir=source_dir, build_dir=build_dir, build_type="Release", ) config.configure() config.build() config.install(prefix) ``` ## File API If you want to access the File API, use: ```python from scikit_build_core.fileapi.query import stateless_query from scikit_build_core.fileapi.reply import load_reply_dir reply_dir = stateless_query(config.build_dir) config.configure() index = load_reply_dir(reply_dir) ``` This mostly wraps the FileAPI in classes. It autoloads some jsonFiles. This throws an `ExceptionGroup` if parsing files. It is currently experimental. ## Configuration Configuration support uses plain dataclasses: ```python @dataclasses.dataclass class NestedSettings: one: str @dataclasses.dataclass class SettingChecker: nested: NestedSettings ``` You can use different sources, currently environment variables: ```yaml PREFIX_NESTED_ONE: "envvar" ``` PEP 517 config dictionaries: ```python {"nested.one": "PEP 517 config"} ``` And TOML: ```toml [tool.scikit-build] nested.one = "TOML config" ``` The CMake config is pre-configured and available in `.settings.cmake_model`, usable as: ```python from scikit_build_core.settings.skbuild_settings import SettingsReader settings_reader = SettingsReader.from_file("pyproject.toml", config_settings) setting = settings_reader.settings assert str(settings.cmake.version) == ">=3.15" assert str(settings.ninja.version) == ">=1.5" ``` ## Builders The tools in `builder` are designed to make writing a builder easy. The `Builder` class is used in the various builder implementations. ### PEP 517 builder This is highly experimental, and currently only uses `.gitignore` to filter the SDist, and the wheel only contains the install directory - control using CMake. ```toml [build-system] requires = [ "scikit_build_core", "pybind11", ] build-backend = "scikit_build_core.build" [project] name = "cmake_example" version = "0.0.1" requires-python = ">=3.8" [project.optional-dependencies] test = ["pytest>=6.0"] ``` ```cmake cmake_minimum_required(VERSION 3.15...3.26) project("${SKBUILD_PROJECT_NAME}" LANGUAGES CXX VERSION "${SKBUILD_PROJECT_VERSION}") find_package(pybind11 CONFIG REQUIRED) pybind11_add_module(cmake_example src/main.cpp) target_compile_definitions(cmake_example PRIVATE VERSION_INFO=${PROJECT_VERSION}) install(TARGETS cmake_example DESTINATION .) ``` ### Setuptools builder Experimental. Supports only a single module, may not support extra Python files. `setup.py`: ```python from setuptools import setup setup( name="cmake_example", version="0.0.1", cmake_source_dir=".", zip_safe=False, extras_require={"test": ["pytest>=6.0"]}, python_requires=">=3.8", ) ``` `pyproject.toml`: ```toml [build-system] requires = [ "scikit_build_core", "pybind11", ] build-backend = "scikit_build_core.setuptools.build_meta" ``` ```cmake cmake_minimum_required(VERSION 3.15...3.26) project("${SKBUILD_PROJECT_NAME}" LANGUAGES CXX VERSION "${SKBUILD_PROJECT_VERSION}") find_package(pybind11 CONFIG REQUIRED) pybind11_add_module(cmake_example src/main.cpp) target_compile_definitions(cmake_example PRIVATE VERSION_INFO=${PROJECT_VERSION}) install(TARGETS cmake_example DESTINATION .) ``` ## Patterns ### Backports All backported standard library code is in `scikit_build_core._compat`, in a module with the stdlib name. Ruff will ensure you use the backport instead of the original module. ### Detecting the platform Here are some common platforms and the reported values: | OS | Compiler | `sys.platform` | `sysconfig.get_platform()` | | ------- | ---------- | -------------- | ---------------------------- | | Windows | MSVC | `win32` | `win-amd64` | | Windows | MinGW | `win32` | `mingw_x86_64` | | Windows | MinGW URCT | `win32` | `mingw_x86_64_ucrt` | | Windows | Cygwin | `cygwin` | `cygwin-3.4.6-x86_64` | | macOS | Clang | `darwin` | `macosx-10.15-x86_64` | | Linux | GCC | `linux` | `linux-x86_64` | | Pyodide | Clang | `emscripten` | `emscripten-3.1.32-wasm32` | | FreeBSD | GCC | `freebsd13` | `freebsd-13.2-RELEASE-amd64` | # Downstream packaging ## Fedora packaging We are using [`packit`](https://packit.dev/) to keep maintain the [Fedora package](https://src.fedoraproject.org/rpms/python-scikit-build-core/). There are two `packit` jobs one needs to keep in mind here: - `copr_build`: Submits copr builds to: - `@scikit-build/nightly` if there is a commit to `main`. This is intended for users to test the current development build - `@scikit-build/scikit-build-core` if there is a PR request. This is for CI purposes to confirm that the build is successful - `@scikit-build/release` whenever a new release is published on GitHub. Users can use this to get the latest release before they land on Fedora. This is also used for other copr projects to check the future release - `propose_downstream`: Submits a PR to `src.fedoraproject.org` once a release is published To interact with `packit`, you can use [`/packit command`](https://packit.dev/docs/guide/#how-to-re-trigger-packit-actions-in-your-pull-request) in PRs and commit messages or [`packit` CLI](https://packit.dev/docs/cli/). These interactions are primarily intended for controlling the CI managed on `scikit-build`. To debug and build locally or on your own copr project you may use `packit build` commands, e.g. to build locally using `mock` for fedora rawhide: ```console $ packit build in-mock -r /etc/mock/fedora-rawhide-x86_64.cfg ``` or for copr project `copr_user/scikit-build-core`: ```console $ copr-cli edit-permissions --builder=packit copr_user/scikit-build-core $ packit build in-copr --owner copr_user --project scikit-build-core ``` (Here we are making sure `packit` has the appropriate permission for `copr_user/scikit-build-core` via the `copr-cli` command. You may need to configure [`~/.config/copr`](https://packit.dev/docs/cli/build/copr/)) first Both of these methods automatically edit the `Version` in the [spec file](../.dist/scikit-build-core.spec), therefore it is intentionally marked as `0.0.0` there to avoid manually updating. Make sure to not push these changes in a PR. scikit-build-core-0.11.1/.github/codecov.yml000066400000000000000000000003121477275177200206270ustar00rootroot00000000000000comment: false codecov: notify: after_n_builds: 13 coverage: status: project: default: target: auto threshold: 5% patch: default: informational: true scikit-build-core-0.11.1/.github/dependabot.yml000066400000000000000000000003401477275177200213130ustar00rootroot00000000000000version: 2 updates: # Maintain dependencies for GitHub Actions - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" groups: actions: patterns: - "*" scikit-build-core-0.11.1/.github/release.yml000066400000000000000000000001141477275177200206250ustar00rootroot00000000000000changelog: exclude: authors: - dependabot - pre-commit-ci scikit-build-core-0.11.1/.github/workflows/000077500000000000000000000000001477275177200205235ustar00rootroot00000000000000scikit-build-core-0.11.1/.github/workflows/cd.yml000066400000000000000000000017341477275177200216410ustar00rootroot00000000000000name: CD on: workflow_dispatch: release: types: - published env: FORCE_COLOR: 3 permissions: {} jobs: dist: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 persist-credentials: false - uses: hynek/build-and-inspect-python-package@v2 deploy: if: github.event_name == 'release' && github.event.action == 'published' needs: [dist] runs-on: ubuntu-latest environment: name: pypi url: https://pypi.org/p/scikit-build-core permissions: id-token: write attestations: write steps: - uses: actions/download-artifact@v4 with: name: Packages path: dist - name: Generate artifact attestation for sdist and wheel uses: actions/attest-build-provenance@v2 with: subject-path: "dist/*" - uses: pypa/gh-action-pypi-publish@release/v1 with: attestations: true scikit-build-core-0.11.1/.github/workflows/ci.yml000066400000000000000000000234071477275177200216470ustar00rootroot00000000000000name: CI on: workflow_dispatch: pull_request: push: branches: - main - v[0123456789]* concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true env: FORCE_COLOR: 3 permissions: {} jobs: lint: name: Format runs-on: ubuntu-latest timeout-minutes: 8 steps: - uses: actions/checkout@v4 with: fetch-depth: 0 persist-credentials: false - uses: actions/setup-python@v5 with: python-version: "3.12" - uses: astral-sh/setup-uv@v5 - name: Install nox run: uv tool install nox - uses: pre-commit/action@v3.0.1 with: extra_args: --all-files - name: Run PyLint run: nox -s pylint -- --output-format=github - name: Run nox generator run: | nox -t gen git diff --exit-code checks: name: 🐍 ${{ matrix.python-version }} • CMake ${{ matrix.cmake-version }} on ${{ matrix.runs-on }} runs-on: ${{ matrix.runs-on }} timeout-minutes: 40 strategy: fail-fast: false matrix: python-version: ["3.8", "pypy-3.10", "3.13"] runs-on: [ubuntu-latest, macos-13] cmake-version: ["3.15.x"] include: - python-version: "pypy-3.8" runs-on: windows-2022 cmake-version: "3.21.x" - python-version: "3.11" runs-on: windows-2022 cmake-version: "3.26.x" - python-version: "pypy-3.9" runs-on: ubuntu-latest cmake-version: "3.15.x" - python-version: "3.8" runs-on: ubuntu-latest cmake-version: "3.21.x" - python-version: "3.9" runs-on: ubuntu-latest cmake-version: "3.20.x" - python-version: "3.9" runs-on: macos-13 cmake-version: "3.18.x" - python-version: "3.12" runs-on: macos-14 cmake-version: "3.29.x" - python-version: "3.10" runs-on: ubuntu-latest cmake-version: "3.22.x" - python-version: "3.11" runs-on: ubuntu-latest cmake-version: "3.26.x" - python-version: "3.8" runs-on: windows-2019 cmake-version: "3.24.x" - python-version: "3.12" runs-on: windows-latest cmake-version: "3.26.x" - python-version: "3.13" runs-on: windows-latest cmake-version: "3.26.x" - python-version: "3.8" runs-on: ubuntu-22.04 cmake-version: "3.15.x" - python-version: "3.13" runs-on: ubuntu-24.04-arm cmake-version: "3.31.x" - python-version: "3.14" runs-on: ubuntu-latest cmake-version: "3.30.x" steps: - uses: actions/checkout@v4 with: persist-credentials: false - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} allow-prereleases: true - uses: astral-sh/setup-uv@v5 if: matrix.python-version != 'pypy-3.8' - name: Install package (uv) if: matrix.python-version != 'pypy-3.8' run: uv pip install -e.[test,test-meta,test-numpy,test-schema,test-hatchling,wheels,cov,wheel-free-setuptools] --system - name: Install package (pip) if: matrix.python-version == 'pypy-3.8' run: pip install -e.[test,test-meta,test-numpy,test-schema,wheels,cov,wheel-free-setuptools] - name: Test package if: "!contains(matrix.python_version, 'pypy')" run: >- pytest -ra --showlocals --cov --cov-report=xml --cov-report=term -n auto --durations=20 - name: Test package (two attempts) uses: nick-fields/retry@v3 if: "contains(matrix.python_version, 'pypy')" with: max_attempts: 2 retry_on: error timeout_seconds: 5 command: >- pytest -ra --showlocals --cov --cov-report=xml --cov-report=term --durations=20 -n auto - name: Upload coverage report uses: codecov/codecov-action@v5 with: name: ${{ runner.os }}-${{ matrix.python-version }}-${{ matrix.cmake-version }} verbose: true token: 6d9cc0e0-158a-41ee-b8f4-0318d3595ac2 min: name: Min 🐍 ${{ matrix.python-version }} on ${{ matrix.runs-on }} runs-on: ${{ matrix.runs-on }} timeout-minutes: 40 strategy: fail-fast: false matrix: python-version: ["3.8", "3.11"] runs-on: [ubuntu-latest, macos-13, windows-latest] steps: - uses: actions/checkout@v4 with: persist-credentials: false - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - uses: astral-sh/setup-uv@v5 # The min requirements are not compatible with some of the extra test # deps, so limit to just built-in deps. - name: Install extra helpers run: uv pip install ninja --system - name: Install min requirements run: | uv pip install -e .[test,pyproject] --resolution=lowest-direct --system - name: Setup CMake 3.15 uses: jwlawson/actions-setup-cmake@v2.0 if: runner.os != 'Windows' with: cmake-version: "3.15.x" # First version to support VS 17.0 - name: Setup CMake 3.21 uses: jwlawson/actions-setup-cmake@v2.0 if: runner.os == 'Windows' with: cmake-version: "3.21.x" - name: Show installed packages run: pip list - name: Test min package run: pytest -n auto -ra --showlocals -Wdefault manylinux: name: Manylinux on 🐍 3.13 • Free-threaded runs-on: ubuntu-latest timeout-minutes: 40 container: quay.io/pypa/musllinux_1_2_x86_64:latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 persist-credentials: false - name: Prepare venv run: python3.13t -m venv /venv - name: Install deps run: /venv/bin/pip install -e .[test] ninja - name: Test package run: /venv/bin/pytest cygwin: name: Tests on 🐍 3.9 • cygwin runs-on: windows-latest timeout-minutes: 40 steps: - uses: actions/checkout@v4 with: fetch-depth: 0 persist-credentials: false - uses: cygwin/cygwin-install-action@v5 with: platform: x86_64 packages: cmake ninja git make gcc-g++ python39 python39-devel python39-pip - name: Install run: python3.9 -m pip install .[test] - name: Test package run: python3.9 -m pytest -n auto -ra --showlocals -m "not virtualenv" --durations=20 msys: name: Tests on 🐍 3 • msys UCRT runs-on: windows-latest timeout-minutes: 30 defaults: run: shell: msys2 {0} steps: - uses: msys2/setup-msys2@v2 with: msystem: UCRT64 path-type: minimal update: true install: >- base-devel git pacboy: >- python:p python-pip:p gcc:p cmake:p - uses: actions/checkout@v4 with: fetch-depth: 0 persist-credentials: false - name: Install run: python -m pip install .[test] - name: Test package run: >- python -m pytest -n auto -ra --showlocals -m "not broken_on_urct" --durations=20 mingw64: name: Tests on 🐍 3 • mingw64 runs-on: windows-latest timeout-minutes: 30 defaults: run: shell: msys2 {0} steps: - uses: msys2/setup-msys2@v2 with: msystem: mingw64 path-type: minimal update: true install: >- base-devel git pacboy: >- python:p python-pip:p gcc:p cmake:p - uses: actions/checkout@v4 with: fetch-depth: 0 persist-credentials: false - name: Install run: python -m pip install .[test] - name: Test package run: >- python -m pytest -n auto -ra --showlocals -m "not setuptools" --durations=20 env: SETUPTOOLS_USE_DISTUTILS: "local" dist: name: Distribution build runs-on: ubuntu-latest timeout-minutes: 5 steps: - uses: actions/checkout@v4 with: fetch-depth: 0 persist-credentials: false - uses: hynek/build-and-inspect-python-package@v2 docs: name: Docs on ${{ matrix.runs-on }} runs-on: ${{ matrix.runs-on }} timeout-minutes: 15 strategy: matrix: runs-on: [ubuntu-latest, macos-latest, windows-latest] steps: - uses: actions/checkout@v4 with: fetch-depth: 0 persist-credentials: false - uses: astral-sh/setup-uv@v5 - uses: wntrblm/nox@2025.02.09 with: python-versions: "3.12" - name: Linkcheck if: runner.os == 'Linux' run: nox -s docs -- -b linkcheck - name: Manpage run: nox -s docs -- -b man -W - name: Build docs with warnings as errors if: runner.os == 'Linux' run: nox -s docs -- -W - name: Check examples run: nox -s test_doc_examples - name: Verify no changes required to API docs run: | nox -s build_api_docs git diff --exit-code pass: if: always() needs: [lint, checks, min, cygwin, dist, docs] runs-on: ubuntu-latest timeout-minutes: 2 steps: - name: Decide whether the needed jobs succeeded or failed uses: re-actors/alls-green@release/v1 with: jobs: ${{ toJSON(needs) }} scikit-build-core-0.11.1/.gitignore000066400000000000000000000046111477275177200171200ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python /build/ /tests/*/build develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ cover/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 db.sqlite3-journal # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder .pybuilder/ target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: # .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # PEP 582; used by e.g. github.com/David-OConnor/pyflow __pypackages__/ # Celery stuff celerybeat-schedule celerybeat.pid # SageMath parsed files *.sage.py # Environments .env* .venv* env*/ venv*/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ # pytype static type analyzer .pytype/ # Cython debug symbols cython_debug/ # setuptools_scm src/*/_version.py # SKBuild cache dir _skbuild/ # Any build dirs in the tests tests/**/build/ /src/scikit_build_core/_version.py # Common editor files *~ *.swp # RPM spec file !/.distro/*.spec /.distro/*.tar.gz *.rpm # ruff .ruff_cache/ # OS specific stuff .DS_Store .DS_Store? ._* .Spotlight-V100 .Trashes ehthumbs.db Thumbs.db .idea/ /docs/**/build .vscode/ scikit-build-core-0.11.1/.packit.yaml000066400000000000000000000031351477275177200173450ustar00rootroot00000000000000files_to_sync: - src: .distro/ dest: ./ delete: true filters: - "protect .git*" - "protect sources" - "protect changelog" - "protect gating.yaml" - .packit.yaml upstream_package_name: scikit-build-core specfile_path: .distro/python-scikit-build-core.spec downstream_package_name: python-scikit-build-core upstream_tag_template: v{version} targets: &targets - fedora-all-x86_64 - fedora-all-aarch64 - epel-10-x86_64 - epel-10-aarch64 _: # Job templates - &build-in-packit job: copr_build - &build-in-scikit-build <<: *build-in-packit owner: "@scikit-build" - &tests-downstream job: tests fmf_path: .distro identifier: downstream - &tests-upstream job: tests skip_build: true identifier: upstream targets: - fedora-development jobs: # Upstream jobs - <<: *build-in-scikit-build trigger: release project: release - <<: *tests-downstream trigger: release - <<: *build-in-scikit-build trigger: commit branch: main project: nightly - <<: *tests-downstream trigger: commit branch: main - <<: *tests-upstream trigger: commit branch: main - <<: *build-in-packit trigger: pull_request - <<: *tests-downstream trigger: pull_request - <<: *tests-upstream trigger: pull_request # Downstream jobs - job: propose_downstream trigger: release dist_git_branches: - fedora-rawhide - job: koji_build trigger: commit dist_git_branches: - fedora-all - job: bodhi_update trigger: commit dist_git_branches: - fedora-branched scikit-build-core-0.11.1/.pre-commit-config.yaml000066400000000000000000000102521477275177200214070ustar00rootroot00000000000000ci: autoupdate_commit_msg: "chore(deps): update pre-commit hooks" autofix_commit_msg: "style: pre-commit fixes" autoupdate_schedule: monthly exclude: ^src/scikit_build_core/_vendor repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 hooks: - id: check-added-large-files - id: check-case-conflict - id: check-merge-conflict - id: check-symlinks - id: check-yaml - id: debug-statements - id: end-of-file-fixer - id: mixed-line-ending - id: name-tests-test args: ["--pytest-test-first"] exclude: "^tests/packages/|^tests/utils" - id: requirements-txt-fixer - id: trailing-whitespace exclude: "^tests" - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.11.0 hooks: - id: ruff args: ["--fix", "--show-fixes"] - id: ruff-format - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.10.0 hooks: - id: rst-backticks - id: rst-directive-colons - id: rst-inline-touching-normal - repo: https://github.com/adamchainz/blacken-docs rev: 1.19.1 hooks: - id: blacken-docs additional_dependencies: [black==24.*] - repo: https://github.com/cheshirekow/cmake-format-precommit rev: v0.6.13 hooks: - id: cmake-format exclude: ^src/scikit_build_core/resources/find_python - repo: https://github.com/rbubley/mirrors-prettier rev: "v3.5.3" hooks: - id: prettier types_or: [yaml, markdown, html, css, scss, javascript, json] args: [--prose-wrap=always] exclude: "^tests|src/scikit_build_core/resources/scikit-build.schema.json|^docs/projects.md" - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.15.0 hooks: - id: mypy exclude: | (?x)^( tests/packages/simplest_c/src/simplest/__init__.py| tests/packages/dynamic_metadata/src/dynamic/__init__.py| tests/packages/.*/setup.py ) files: ^(src|tests|noxfile.py) args: [] additional_dependencies: - build - cattrs - cmake - exceptiongroup - hatch-fancy-pypi-readme>=24 - importlib-resources - markdown-it-py - ninja - nox - orjson - packaging>=24.2 - pytest - pytest-subprocess - rich - setuptools-scm - tomli - types-setuptools>=70.1 - repo: https://github.com/henryiii/check-sdist rev: "v1.2.0" hooks: - id: check-sdist args: [--inject-junk] additional_dependencies: - hatchling - hatch-vcs - repo: https://github.com/codespell-project/codespell rev: v2.4.1 hooks: - id: codespell exclude: ^(LICENSE$|src/scikit_build_core/resources/find_python|tests/test_skbuild_settings.py$) - repo: https://github.com/shellcheck-py/shellcheck-py rev: v0.10.0.1 hooks: - id: shellcheck - repo: local hooks: - id: disallow-caps name: Disallow improper capitalization language: pygrep entry: PyBind|Numpy|Cmake|CCache|Github|PyTest exclude: .pre-commit-config.yaml - id: disallow-expressions name: Disallow expressions language: pygrep entry: tool\.cmake exclude: .pre-commit-config.yaml - id: cog name: Cog the documentation files: docs/projects.md entry: cog -r -c language: python additional_dependencies: [cogapp] - repo: https://github.com/henryiii/validate-pyproject-schema-store rev: 2025.03.10 hooks: - id: validate-pyproject - repo: https://github.com/python-jsonschema/check-jsonschema rev: 0.31.3 hooks: - id: check-dependabot - id: check-github-workflows - id: check-readthedocs - id: check-metaschema files: \.schema\.json - repo: https://github.com/citation-file-format/cffconvert rev: b6045d78aac9e02b039703b030588d54d53262ac hooks: - id: validate-cff - repo: https://github.com/scientific-python/cookie rev: 2025.01.22 hooks: - id: sp-repo-review scikit-build-core-0.11.1/.readthedocs.yml000066400000000000000000000011471477275177200202170ustar00rootroot00000000000000# .readthedocs.yml # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Set the version of Python and other tools you might need build: os: ubuntu-22.04 tools: python: "3.11" # Unshallow the git clone to allow vcs versioning to pick # up tags properly jobs: post_checkout: - git fetch --unshallow # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/conf.py python: install: - method: pip path: . extra_requirements: - docs - pyproject scikit-build-core-0.11.1/CITATION.cff000066400000000000000000000034001477275177200170150ustar00rootroot00000000000000cff-version: 1.2.0 message: "Please cite this software using the metadata from 'preferred-citation'." title: "Scikit-build-core" version: "0.10.7" date-released: "2024-10-20" abstract: "Build compiled Python packages using CMake" keywords: - python - cmake authors: - family-names: Schreiner given-names: Henry name-suffix: III orcid: https://orcid.org/0000-0002-7833-783X affiliation: Princeton University website: https://iscinumpy.dev alias: henryiii - family-names: Fillion-Robin given-names: Jean-Christophe orcid: https://orcid.org/0000-0002-9688-8950 affiliation: Kitware alias: jcfr - family-names: McCormick given-names: Matt orcid: https://orcid.org/0000-0001-9475-3756 affiliation: Kitware alias: thewtex - family-names: Le given-names: Christian orcid: https://orcid.org/0000-0001-5122-8521 alias: LecrisUT url: https://scikit-build-core.readthedocs.io repository-code: https://github.com/scikit-build/scikit-build-core license: Apache-2.0 preferred-citation: type: conference-paper title: Scikit-build-core conference: name: SciPy 2024 journal: Proceedings of the 23rd Python in Science Conference issn: 2575-9752 date-published: 2024-07-10 doi: 10.25080/FMKR8387 license: CC-BY-4.0 authors: - family-names: Schreiner given-names: Henry name-suffix: III orcid: https://orcid.org/0000-0002-7833-783X affiliation: Princeton University website: https://iscinumpy.dev - family-names: Fillion-Robin given-names: Jean-Christophe orcid: https://orcid.org/0000-0002-9688-8950 affiliation: Kitware - family-names: McCormick given-names: Matt orcid: https://orcid.org/0000-0001-9475-3756 affiliation: Kitware scikit-build-core-0.11.1/LICENSE000066400000000000000000000261221477275177200161360ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2022 Henry Schreiner Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. scikit-build-core-0.11.1/README.md000066400000000000000000000407761477275177200164230ustar00rootroot00000000000000# scikit-build-core [![Documentation Status][rtd-badge]][rtd-link] [![GitHub Discussion][github-discussions-badge]][github-discussions-link] [![Discord][discord-badge]][discord-link] [![Actions Status][actions-badge]][actions-link] [![codecov][codecov-badge]][codecov-link] [![PyPI version][pypi-version]][pypi-link] [![Conda-Forge][conda-badge]][conda-link] [![PyPI platforms][pypi-platforms]][pypi-link] [![Downloads][download-badge]][download-link] > [!NOTE] > > We have a public Scikit-build community meeting every month! > [Join us on Google Meet](https://meet.google.com/hzu-znrd-uef) on the third > Friday of every month at 12:00 PM EST. We also have a developer's meeting on > the first Friday of every month at the same time. Our past meeting minutes are > [available here](https://github.com/orgs/scikit-build/discussions/categories/community-meeting-notes). Scikit-build-core is a build backend for Python that uses CMake to build extension modules. It has a simple yet powerful static configuration system in pyproject.toml, and supports almost unlimited flexibility via CMake. It was initially developed to support the demanding needs of scientific users, but can build any sort of package that uses CMake. Scikit-build-core is a ground-up rewrite of the classic Scikit-build. The key features of scikit-build classic (which is setuptools based) are also present here: - Great support for or by most OSs, compilers, IDEs, and libraries - Support for C++ features and other languages like Fortran - Support for multithreaded builds - Simple CMakeFiles.txt instead of up to thousands of lines of fragile setuptools/distutils code - Cross-compile support for Apple Silicon and Windows ARM Scikit-build-core was built using Python packaging standards developed after scikit-build (classic) was written. Using it directly provides the following features over classic Scikit-build: - Better warnings, errors, and logging - No warning about unused variables - Automatically adds Ninja and/or CMake only as required - No dependency on setuptools, distutils, or wheel - Powerful config system, including config options support - Automatic inclusion of site-packages in `CMAKE_PREFIX_PATH` - FindPython is backported if running on CMake < 3.26.1 (configurable), supports PyPY SOABI & Limited API / Stable ABI - Limited API / Stable ABI and pythonless tags supported via config option - No slow generator search, ninja/make or MSVC used by default, respects `CMAKE_GENERATOR` - SDists are reproducible by default (UNIX, Python 3.9+, uncompressed comparison recommended) - Support for caching between builds (opt-in by setting `build-dir`) - Support for writing out to extra wheel folders (scripts, headers, data) - Support for selecting install components and build targets - Dedicated entrypoints for module and prefix directories - Several integrated dynamic metadata plugins (proposing standardized support soon) - Experimental editable mode support, with optional experimental auto rebuilds on import and optional in-place mode - Supports WebAssembly (Emscripten/[Pyodide](https://pyodide.org)). - Supports [free-threaded Python 3.13](https://py-free-threading.github.io). The following limitations are present compared to classic scikit-build: - The minimum supported CMake is 3.15 - The minimum supported Python is 3.8 (3.7+ for 0.10.x and older) Some known missing features that will be developed soon: - Wheels are not fully reproducible yet (nor are they in most others systems, including setuptools) - Several editable mode caveats (mentioned in the docs). Other backends are also planned: - Setuptools integration highly experimental - Hatchling plugin highly experimental The recommended interface is the native pyproject builder. There is also a WIP setuptools-based interface that is being developed to provide a transition path for classic scikit-build, and a WIP Hatchling plugin. Both might be moved to standalone packages in the future. > [!WARNING] > > Only the pyproject-based builder should be used; the setuptools backend is > experimental and likely to move to a separate package before being declared > stable, and internal API is still being solidified. A future version of this > package will support creating new build extensions. ## Example To use scikit-build-core, add it to your `build-system.requires`, and specify the `scikit_build_core.build` builder as your `build-system.build-backend`. You do _not_ need to specify `cmake` or `ninja`; scikit-build-core will require them automatically if the system versions are not sufficient. ```toml [build-system] requires = ["scikit-build-core"] build-backend = "scikit_build_core.build" [project] name = "scikit_build_simplest" version = "0.0.1" ``` You can (and should) specify the rest of the entries in `project`, but these are the minimum to get started. An example `CMakeLists.txt`: ```cmake cmake_minimum_required(VERSION 3.15...3.30) project(${SKBUILD_PROJECT_NAME} LANGUAGES C) find_package(Python COMPONENTS Interpreter Development.Module REQUIRED) Python_add_library(_module MODULE src/module.c WITH_SOABI) install(TARGETS _module DESTINATION ${SKBUILD_PROJECT_NAME}) ``` Scikit-build-core will backport FindPython from CMake 3.26.1 to older versions of Python, and will handle PyPy for you if you are building from PyPy. You will need to install everything you want into the full final path inside site-modules (so you will usually prefix everything by the package name). More examples are in the [tests/packages](https://github.com/scikit-build/scikit-build-core/tree/main/tests/packages). ## Configuration All configuration options can be placed in `pyproject.toml`, passed via `-C`/`--config-setting` in build or `-C`/`--config-settings` in `pip` , or set as environment variables. `tool.scikit-build` is used in toml, `skbuild.` for `-C` options, or `SKBUILD_*` for environment variables. The defaults are listed below: ```toml [tool.scikit-build] # The versions of CMake to allow. If CMake is not present on the system or does # not pass this specifier, it will be downloaded via PyPI if possible. An empty # string will disable this check. The default on 0.10+ is "CMakeLists.txt", # which will read it from the project's CMakeLists.txt file, or ">=3.15" if # unreadable or <0.10. cmake.version = "" # A list of args to pass to CMake when configuring the project. Setting this in # config or envvar will override toml. See also ``cmake.define``. cmake.args = [] # A table of defines to pass to CMake when configuring the project. Additive. cmake.define = {} # DEPRECATED in 0.10, use build.verbose instead. cmake.verbose = "" # The build type to use when building the project. Valid options are: "Debug", # "Release", "RelWithDebInfo", "MinSizeRel", "", etc. cmake.build-type = "Release" # The source directory to use when building the project. Currently only affects # the native builder (not the setuptools plugin). cmake.source-dir = "." # DEPRECATED in 0.10; use build.targets instead. cmake.targets = "" # The versions of Ninja to allow. If Ninja is not present on the system or does # not pass this specifier, it will be downloaded via PyPI if possible. An empty # string will disable this check. ninja.version = ">=1.5" # If Ninja is not present on the system or is older than required, it will be # downloaded via PyPI if this is false. ninja.make-fallback = true # The logging level to display, "DEBUG", "INFO", "WARNING", and "ERROR" are # possible options. logging.level = "WARNING" # Files to include in the SDist even if they are skipped by default. Supports # gitignore syntax. sdist.include = [] # Files to exclude from the SDist even if they are included by default. Supports # gitignore syntax. sdist.exclude = [] # If set to True, try to build a reproducible distribution (Unix and Python 3.9+ # recommended). ``SOURCE_DATE_EPOCH`` will be used for timestamps, or a fixed # value if not set. sdist.reproducible = true # If set to True, CMake will be run before building the SDist. sdist.cmake = false # A list of packages to auto-copy into the wheel. If this is not set, it will # default to the first of ``src/``, ``python/``, or # ```` if they exist. The prefix(s) will be stripped from the package # name inside the wheel. If a dict, provides a mapping of package name to source # directory. wheel.packages = ["src/", "python/", ""] # The Python tags. The default (empty string) will use the default Python # version. You can also set this to "cp38" to enable the CPython 3.8+ Stable ABI # / Limited API (only on CPython and if the version is sufficient, otherwise # this has no effect). Or you can set it to "py3" or "py2.py3" to ignore Python # ABI compatibility. The ABI tag is inferred from this tag. wheel.py-api = "" # Fill out extra tags that are not required. This adds "x86_64" and "arm64" to # the list of platforms when "universal2" is used, which helps older Pip's # (before 21.0.1) find the correct wheel. wheel.expand-macos-universal-tags = false # The install directory for the wheel. This is relative to the platlib root. You # might set this to the package name. The original dir is still at # SKBUILD_PLATLIB_DIR (also SKBUILD_DATA_DIR, etc. are available). EXPERIMENTAL: # An absolute path will be one level higher than the platlib root, giving access # to "/platlib", "/data", "/headers", and "/scripts". wheel.install-dir = "" # A list of license files to include in the wheel. Supports glob patterns. The # default is ``["LICEN[CS]E*", "COPYING*", "NOTICE*", "AUTHORS*"]``. Must not be # set if ``project.license-files`` is set. wheel.license-files = "" # If set to True (the default), CMake will be run before building the wheel. wheel.cmake = true # Target the platlib or the purelib. If not set, the default is to target the # platlib if wheel.cmake is true, and the purelib otherwise. wheel.platlib = "" # A set of patterns to exclude from the wheel. This is additive to the SDist # exclude patterns. This applies to the final paths in the wheel, and can # exclude files from CMake output as well. Editable installs may not respect # this exclusion. wheel.exclude = [] # The build tag to use for the wheel. If empty, no build tag is used. wheel.build-tag = "" # If CMake is less than this value, backport a copy of FindPython. Set to 0 # disable this, or the empty string. backport.find-python = "3.26.1" # Select the editable mode to use. Can be "redirect" (default) or "inplace". editable.mode = "redirect" # Turn on verbose output for the editable mode rebuilds. editable.verbose = true # Rebuild the project when the package is imported. The build-directory must be # set. editable.rebuild = false # Extra args to pass directly to the builder in the build step. build.tool-args = [] # The build targets to use when building the project. Empty builds the default # target. build.targets = [] # Verbose printout when building. build.verbose = false # Additional ``build-system.requires``. Intended to be used in combination with # ``overrides``. build.requires = [] # The components to install. If empty, all default components are installed. install.components = [] # Whether to strip the binaries. True for release builds on scikit-build-core # 0.5+ (0.5-0.10.5 also incorrectly set this for debug builds). install.strip = true # The path (relative to platlib) for the file to generate. generate[].path = "" # The template to use for the file. This includes string.Template style # placeholders for all the metadata. If empty, a template-path must be set. generate[].template = "" # The path to the template file. If empty, a template must be set. generate[].template-path = "" # The place to put the generated file. The "build" directory is useful for CMake # files, and the "install" directory is useful for Python files, usually. You # can also write directly to the "source" directory, will overwrite existing # files & remember to gitignore the file. generate[].location = "install" # A message to print after a build failure. messages.after-failure = "" # A message to print after a successful build. messages.after-success = "" # Add the python build environment site_packages folder to the CMake prefix # paths. search.site-packages = true # List dynamic metadata fields and hook locations in this table. metadata = {} # Strictly check all config options. If False, warnings will be printed for # unknown options. If True, an error will be raised. strict-config = true # Enable early previews of features not finalized yet. experimental = false # If set, this will provide a method for backward compatibility. minimum-version = "0.11" # current version # The build directory. Defaults to a temporary directory, but can be set. build-dir = "" # Immediately fail the build. This is only useful in overrides. fail = false ``` Most CMake environment variables should be supported, and `CMAKE_ARGS` can be used to set extra CMake args. `ARCHFLAGS` is used to specify macOS universal2 or cross-compiles, just like setuptools. You can also specify `[[tool.scikit-build.overrides]]` to customize values for different systems. See the docs for details. ## Other projects for building Scikit-build-core is a binary build backend. There are also other binary build backends: - [py-build-cmake][]: A different attempt at a standards compliant builder for CMake. Strong focus on cross-compilation. Uses Flit internals. - [cmeel][]: A different attempt at a standards compliant builder for CMake. Focus on building an ecosystem around a special unimportable folder in site-packages (similar to scikit-build's usage of `cmake.*` entrypoints, but folder-based). - [meson-python][]: A meson-based build backend; has some maintainer overlap with scikit-build-core. - [maturin][]: A build backend for Rust projects, using Cargo. - [enscons][]: A SCons based backend, not very actively developed (but it predates all the others in modern standard support!) If you don't need a binary build, you don't need to use a binary build backend! There are some very good Python build backends; we recommend [hatchling][] as a good balance between good defaults for beginners and good support for advanced use cases. This is the tool scikit-build-core itself uses. ## Acknowledgements Support for this work was provided by NSF grant [OAC-2209877][]. Any opinions, findings, and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation. [OAC-2209877]: https://www.nsf.gov/awardsearch/showAward?AWD_ID=2209877&HistoricalAwards=false [actions-badge]: https://github.com/scikit-build/scikit-build-core/workflows/CI/badge.svg [actions-link]: https://github.com/scikit-build/scikit-build-core/actions [cmeel]: https://github.com/cmake-wheel/cmeel [codecov-badge]: https://codecov.io/gh/scikit-build/scikit-build-core/branch/main/graph/badge.svg?token=ZLbQzIvyG8 [codecov-link]: https://codecov.io/gh/scikit-build/scikit-build-core [conda-badge]: https://img.shields.io/conda/vn/conda-forge/scikit-build-core [conda-link]: https://github.com/conda-forge/scikit-build-core-feedstock [discord-badge]: https://img.shields.io/discord/803025117553754132?label=Discord%20chat%20%23scikit-build [discord-link]: https://discord.gg/pypa [download-badge]: https://static.pepy.tech/badge/scikit-build-core/month [download-link]: https://pepy.tech/project/scikit-build-core [enscons]: https://pypi.org/project/enscons [github-discussions-badge]: https://img.shields.io/static/v1?label=Discussions&message=Ask&color=blue&logo=github [github-discussions-link]: https://github.com/orgs/scikit-build/discussions [hatchling]: https://hatch.pypa.io/latest [maturin]: https://www.maturin.rs [meson-python]: https://meson-python.readthedocs.io [py-build-cmake]: https://tttapa.github.io/py-build-cmake [pypi-link]: https://pypi.org/project/scikit-build-core/ [pypi-platforms]: https://img.shields.io/pypi/pyversions/scikit-build-core [pypi-version]: https://badge.fury.io/py/scikit-build-core.svg [rtd-badge]: https://readthedocs.org/projects/scikit-build-core/badge/?version=latest [rtd-link]: https://scikit-build-core.readthedocs.io/en/latest/?badge=latest scikit-build-core-0.11.1/docs/000077500000000000000000000000001477275177200160565ustar00rootroot00000000000000scikit-build-core-0.11.1/docs/about/000077500000000000000000000000001477275177200171705ustar00rootroot00000000000000scikit-build-core-0.11.1/docs/about/changelog.md000066400000000000000000001245371477275177200214550ustar00rootroot00000000000000# Changelog ## Version 0.11.1 This release contains two small fixes for edge case issues. Fixes: - Bump pyproject-metadata to handle form feeds on Python < 3.12.8 by @henryiii in #1014 - Increase timeout on CI by @henryiii in #1019 CI and testing: - Ignore deprecation message from cattrs on Python 3.14 alphas by @LecrisUT in #1020 - Add more integration tests in Fedora by @LecrisUT in #800 - Restore example builds for Windows/macOS by @henryiii in #1025 Internal: - Remove leftover Python 3.7 checks by @LecrisUT in #1011 - Bump pytest version and include pytest-xdist by @henryiii in #1008 - Bump to ruff 0.10, work around bug by @henryiii in #1015 Docs: - Clarify verbosity options by @LecrisUT in #1018 - Fix incorrect configuration example by @buddly27 in #1023 - Fix incorrect name (`PYBIND11_NEWPYTHON` -> `PYBIND11_FINDPYTHON`) by @henryiii in #1021 ## Version 0.11.0 This version adds support for PEP 639 (license expressions) and updates the default METADATA version 2.2. Support for Python 3.7 has been removed. You can use `build.requires` to inject build requirements using overrides. Features: - Update vendored pyproject-metadata (PEP 639 support) by @henryiii in #917 - 0.11+ default to metadata 2.2 by @henryiii in #986 - Rework CMake search path settings and add `cmake.root` by @LecrisUT in #880 - `SKBUILD_SABI_VERSION` by @henryiii in #962 - Support TOML lists in `cmake.define` by @alexreinking in #921 - Add new field `build.requires` by @LecrisUT in #992 - Drop python<=3.7 support by @kloczek in #769 - Setuptools plugin: pyproject-toml only config support by @henryiii in #975 - Setuptools plugin: error for `cmake_install_target` by @henryiii in #976 Fixes: - Lock during experimental `editable.rebuild` by @hauntsaninja in #968 - Add known wheels for armv7l by @mayeut in #960 - Ignore `build-dir` automatically by @henryiii in #916 - Longer timeout on Windows by @henryiii in #974 - `path_to_module` should handle hidden files (e.g. `.clang-tidy`) correctly by @tae-jun in #987 - Timeout checking cmake/ninja by @henryiii in #973 Internal: - Add a `format` module that is used in the expansion of `pyproject.toml` by @LecrisUT in #998 - Bump Ruff to 0.8.0 by @AlexWaygood in #949 - Add citation by @henryiii in #945 - Vendor updates by @henryiii in #918 CI and testing: - Correct the function prototype defined in the stub file in `simplest_c` example by @LinZhihao-723 in #937 - Synchronize with Fedora downstream by @LecrisUT in #884 - Fix RTD builds when last git tag is more than 50 commits behind by @agriyakhetarpal in #1001 - Tighten up permissions a bit by @henryiii in #983 - `tmt` tests improvements by @LecrisUT in #899 - Update deployment a bit by @henryiii in #922 - Use astral-sh/setup-uv instead by @henryiii in #923 - Simpler noxfile by @henryiii in #924 - Test on Linux ARM & Python 3.14 alphas by @henryiii in #1003 - Support for parallel testing by @henryiii in #1004 Docs: - `sphinx-apidoc` changed its mind by @LecrisUT in #996 - Fix Google meet reference to use updated link by @jcfr in #931 - Clarify docs for activating stable ABI by @eirrgang in #940 - Consistently use backticks to reference Python module components by @jcfr in #956 - Fix instructions to check if `Development.SABIModule` was requested by @jcfr in #957 - Add some notes and warnings about cross-compilation by @henryiii in #985 - Add sphinx-tippy by @LecrisUT in #1000 - Correct the order of entry-points and CMake variables by @njzjz in #948 - Quickstart updates by @henryiii in #939 - Some restructuring by @LecrisUT in #997 ## Version 0.10.7 This version has three fixes related to advanced usages of overrides. Fixes: - Avoid modifying the input dict by @henryiii in #903 - Avoid providing prepare-metadata methods if `failed` in overrides by @henryiii in #904 - Support negative envvar values correctly by @henryiii in #907 ## Version 0.10.6 This version has several fixes; the new `remove` key in 0.10 had a mistake making it unusable, this has been fixed along with the matching issue in the tests. Editable installs failed if there was a folder matching an extension name. Multiplexed paths were not supported for plugins, now they are. The downstream nox job is also now a bit better. Fixes: - Use correct settings key to remove parts of the version in regex metadata plugin by @philippjh in #887 - Support multiplexed path by @henryiii in #896 - Editable subpackage by @henryiii in #895 - Only strip Release and MinSizeRel builds by default by @henryiii in #898 - Typo in debug message by @LecrisUT in #892 Docs: - Add build info by @henryiii in #878 - Fix typo in `if.platform-system` example by @henryiii in #879 CI and testing: - Packit configuration update by @LecrisUT in #799 ## Version 0.10.5 This version fixes a issue rebuilding if you have a `build-dir` set and are using isolated build environments. The cache is now correctly cleared if this occurs. An issue with a certain style of gitignore was also fixed. Fixes: - Automatically refresh the cache when required by @henryiii in #870 - `sdist.exclude` takes precedence over `.gitignore` by @alexreinking in #872 ## Version 0.10.4 This version fixes the logic for handling the sysconfig variables on Windows for the values that used to be only provided on UNIX. This mostly affects targeting the Stable ABI on Python 3.13. Editable install rebuilds now work if you have a `wheel.install-dir` set, too. Fixes: - Windows 3.13 stable ABI by @LecrisUT in #865 - Editable installs now respect the value of `wheel.install-dir` by @psalvaggio in #867 Docs: - Add conda help to faqs by @henryiii in #868 ## Version 0.10.3 This release fixes an issue on Windows systems with non-utf-8 default encodings for Python \<3.14 when reading gitignores with special characters. Fixes: - Make sure encoding is specified in more places by @henryiii in #861 ## Version 0.10.2 This release fixes a regression with 0.10 where a manually included file in an sdist would get included twice, affecting some tools (like uv). Fixes: - Files included twice when they match `include_spec` by @abetlen in #857 ## Version 0.10.1 This is a quick fix release with two patches for specific situations. Fixes: - Fix crash when building inside a submodule by @ausbin in #854 - `cmake.minimum-version` logic issue by @henryiii in #853 ## Version 0.10.0 This version adds auto CMake version discovery, opt-in auto minimum-version, rebuild on failure support, quite a few new override options greatly expanding the static config options for builds, more powerful regexs, and more. Also see [the announcement post](https://iscinumpy.dev/post/scikit-build-core-0-10). New features: - Auto CMake version by @henryiii in #804 - Auto minimum-version by @henryiii in #798 - Add `fail` setting to instant-fail build by @henryiii in #824 - Add messages config for failure/success by @henryiii in #825 - New color system for message config by @henryiii in #842 - Add `if.abi-flags` (for free-threaded) by @henryiii in #819 - Adding `if.system-cmake` and `if.cmake-wheel` by @henryiii in #826 - Add `if.from-sdist` for overrides by @henryiii in #812 - Add `if.failed` (retry) by @henryiii in #820 - Add `if.scikit-build-version` by @henryiii in #851 - Packages can also be specified via a table by @henryiii in #841 - Move `cmake.targets` and `cmake.verbose` to `build.targets` and `build.verbose` by @henryiii in #793 - Support multipart regex using `result=` by @henryiii in #818 - Add `remove=` postprocess to regex by @henryiii in #828 Fixes: - Warn if cmake/ninja in build system requirements by @henryiii in #803 - Detect manual generator setting for `get_requires_*` by @henryiii in #840 - Support nested and local gitignores by @henryiii in #827 Tests: - Fix `test_cmake_config` if `CMAKE_GENERATOR` is already set by @lorepirri in #847 Internal: - Pull out overrides to separate file by @henryiii in #821 - Nicer error/warning printer by @henryiii in #813 - Speed up CI for format job by @henryiii in #814 - Avoid old wheel warning in CI test job by @henryiii in #849 Documentation: - Fix typo in projects list by @jcfr in #797 - Add disclaimer by @henryiii in #805 - Add pointer to itk by @thewtex in #817 - Add projects list by @henryiii in #788 - Examples nox job should use local copy of scikit-build-core by @henryiii in #810 - Expand defs and move schema by @henryiii in #795 - Hide deprecated options in README by @henryiii in #792 - Move overrides to page, expand by @henryiii in #815 - Use modern cmake/ninja version setting in Fortran example by @henryiii in #809 ## Version 0.9.10 This release backports a couple of fixes made during work towards 0.10. Fixes: - Regression for `'-'` in version name by @henryiii in #834 - Improve version regex to capture typed version attributes by @maxbachmann in #816 ## Version 0.9.9 This release backports a fix made during work towards 0.10. Fixes: - Strip epoch from `SKBUILD_PROJECT_VERSION` by @henryiii in #829 **Full Changelog**: https://github.com/scikit-build/scikit-build-core/compare/v0.9.8...v0.9.9 ## Version 0.9.8 This version ships a few more small fixes. Multi-target builds were missing build arguments due to an exhausted generator. And some packages seem to hard-code `${CMAKE_INSTALL_PREFIX}` in the `install()` call, which is an anti-pattern, but we can set that variable too. More tests now run on Python 3.7. Fixes: - Empty build arguments for some targets in multi-target build by @junrushao in #784 - Support packages that hardcode `CMAKE_INSTALL_PREFIX` in their `install` commands by @henryiii in #786 - Logger shouldn't warn for missing lib on UNIX by @henryiii in #787 Tests: - Use `ZipFile` instead of `zipfile.Path` to enable more tests on Python 3.7 by @henryiii in #785 ## Version 0.9.7 This release makes a few small fixes, enabling better Fortran support and correct tags on MUSL. And releases now have Attestations on GitHub at . Fixes: - Set `CC` and `CXX` from sysconfig if not set by @henryiii in #782 - `musllinux` tag incorrect by @henryiii in #773 - Logger unconditionally set to `DEBUG` in non-PEP 517 usage by @henryiii in #779 Documentation: - Fix Fortran example by @henryiii in #771 - Fix typo in skbuild soabi docs by @henryiii in #775 - Fortran and Cython updates by @henryiii in #781 ## Version 0.9.6 This release provides a fix for the experimental setuptools plugin leaking and affecting other setuptools plugins. Fixes: - Improve error message when `metadata.version.provider` not set by @thewtex in #762 - Only inject logic if `cmake_*` keywords present by @kiwifb in #768 - Only replace color tags if the color is defined by @bnavigator in #764 ## Version 0.9.5 This release fixes building for the Windows variant of free-threaded Python 3.13. Fixes: - Support Windows free-threading by @henryiii in #759 - Strip whitespace around generator so that it's passed through correctly by @vyasr in #748 Documentation: - Note that `-C` options can be prefixed with `skbuild.` by @KyleFromNVIDIA in #754 CI and testing: - Retry pypy if fails by @henryiii in #746 - Disable rpminspect on rawhide by @LecrisUT in #753 - Use uv for minimum version check by @henryiii in #679 ## Version 0.9.4 This version supports the newly available free-threading variant of Python 3.13b1 (mostly related to skipping the stable ABI). We test this via the manylinux/musllinux images. There's also a new feature requested by third-party packagers; the ability to pass args directly to the build tool Features: - Add `build.tool-args` by @henryiii in #733 Fixes: - Support free-threaded builds of Python 3.13+ on Linux by @henryiii in #741 - Slightly better stable ABI behavior using PyPy by @henryiii in #741 Documentation: - Fix example of configuration overrides in configuration.md by @wu-vincent in #739 - Update stable ABI instructions by @henryiii in #740 CI and testing: - Use pylint 3.2, gha reporter by @henryiii in #745 - Some minor improvements to running tests on some systems by @henryiii in #741 ## Version 0.9.3 This version ensures the Hatchling plugin correctly indicates editable mode is not yet supported, supports `CMAKE_ARGS` that have spaces, and has a bit of other minor cleanup. Fixes: - Properly indicate lack of editable support in Hatch plugin by @ofek in #728 - Don't generate `entrypoints.txt` if none set by @henryiii in #729 - Don't warn if lib not found on UNIX, just debug by @henryiii in #730 - Support `CMAKE_ARGS` that may have spaces inside quotes by @vyasr in #727 Docs: - Add FAQ entry on repairing wheels by @henryiii in #731 CI and testing: - Use `macos-13` and `macos-14` explicitly by @henryiii in #724 - `macos-latest` is changing to `macos-14` ARM runners by @henryiii in #725 ## Version 0.9.2 Quick fix for quick fix in 0.9.1; if `cmake` is a broken script (which can happen if you pip install outside an environment then enter an environment, there was an unbound local error instead of ignoring the broken cmake script. Fixes: - Unbound local error by @henryiii in #719 ## Version 0.9.1 Quick fix for Pyodide (WebAssembly) builds. Fixes: - Try `--version` if `-E capabilities` fails by @henryiii in #717 ## Version 0.9.0 This version adds the ability to `inherit` in override tables, matching a similar feature added to cibuildwheel 2.17's overrides. You can now write out extra metadata to `${SKBUILD_METADATA_DIR}`. A new Hatchling plugin is provided as an experimental feature (will likely be made a separate package in the future like the setuptools plugin). The meaning of `wheel.exclude` has been changed to match on the wheel path, rather than the source path. We could not find any projects that would be affected by this change, so it was not added to the minimum-version protection policy. This now allows you to ignore CMake outputs as well. Features: - Preserve additivity of `cmake.define` across `overrides` tables by @stubbiali in #564 - Add metadata dir access by @henryiii in #702 - Experimental hatchling builder by @henryiii and @aryamanjeendgar in #637 - Vendor pyproject-metadata by @henryiii in #703 - Always require pathspec by @henryiii in #711 Fixes: - Exclude installed files if listed in exclude by @henryiii in #652 - Make `.git_archival.txt` reproducible by @LecrisUT in #706 - Use `cmake -E capabilities` instead of `cmake --version` by @KyleFromNVIDIA in #675 - Ensure many/musl tags not selected by @henryiii in #698 - purelib should set py3 tag if unset by @henryiii in #661 - Validate description for 0.9+ by @henryiii in #709 - Support bools in config settings by @henryiii in #712 API changes: - `extra_settings` for SettingsReader by @henryiii in #697 - `GetRequires` args changed by @henryiii in #699 - Make `from_file` a little more powerful by @henryiii in #700 - Metadata is part of the build backend by @henryiii in #708 Documentation: - `cmakelists.md` Windows `SOABI` suffix variable by @thewtex in #684 - Fix hatch init command by @thewtex in #677 - Fix install strip default by @henryiii in #681 - Improve `ninja.make-fallback` description in the README by @thewtex in #676 - Mention printouts by @henryiii in #660 CI and testing: - Lower `pybind11` test dependency by @LecrisUT in #691 - Some cleanup from uv branch by @henryiii in #686 - Fedora CI maintenance by @LecrisUT in #689 - Small additions by @henryiii in #694 - Some changes from uv job by @henryiii in #693 - Fix setuptools on Python 3.12 by @henryiii in #701 - Fedora: Port downstream PR-49 by @LecrisUT in #678 ## Version 0.8.2 This version fixes a few small issues related to configuration. The wheel tag is fixed when `wheel.platlib` is False, inplace editable installs ignore `build-dir`, and `install-dir` no longer affects the generate path. Fixes: - Ensure `wheel.platlib` being false sets `-` as `"none-any"` by @jcfr in #655 - Inplace editable install should ignore `build-dir` by @henryiii in #644 - `install-dir` shouldn't affect generate path by @henryiii in #654 - Expand info printing by @henryiii in #651 Documentation: - Clarify requirement for using "inplace" editable mode by @jcfr in #643 - Update README to use modern GitHub admonition by @jcfr in #641 - be explicit about what it means to "use SKBUILD_SOABI" by @minrk in #646 ## Version 0.8.1 This version fixes two small issues with specific situations: if ninja/cmake is present but not executable, and ninja on Windows not respecting the build type. Fixes: - Do not exit with exec permission error in ninja check by @thewtex in #626 - Include `CMAKE_BUILD_TYPE` on Windows Ninja by @henryiii in #640 - Use `-` vs `_` in error message by @nbelakovski in #638 - Make run command logged at info by @LecrisUT in #639 Documentation: - Warning about using `cmake.args` for defines by @LecrisUT in #620 - Correct API usage example by @henryiii in #627 ## Version 0.8.0 This version replaces the `cmake`/`ninja` minimum-version config option with a more powerful free-form version field. Scikit-build-core will now respect `CMAKE_EXECUTABLE` for finding CMake. You can override based on the build state, allowing overrides for editable installs, for example. You can specify a build tag (AKA build number). And you can define CMake variables from environment variables. Features: - Add `build-tag` by @henryiii in #612 - Add `if.state` to overrides by @henryiii in #600 - Add `cmake.version` and `ninja.version` by @henryiii in #602 - Support `CMAKE_EXECUTABLE` by @henryiii in #603 - Config to set CMake variables with environment variables by @stubbiali in #565 Fixes: - Include license file entry in search by @henryiii in #601 - Make sure purelib is indicated by @henryiii in #613 - Project version should always be CMake parsable by @henryiii in #608 Tests and internal: - Compare uncompressed files by @henryiii in #610 - A couple of minor cleanups by @henryiii in #599 - Some preview Ruff touchups by @henryiii in #617 ## Version 0.7.1 This is a quick fix for a downstream change in hatch-fancy-pypi-readme that broke our plugin wrapper. Fixes: - Patch for change in hatch-fancy-pypi-readme by @henryiii in #593 CI and tests: - Remove deprecated mypy setting (now defaults on) by @henryiii in #589 - Group dependabot updates by @henryiii in #584 - Use downstream tmt plans by @LecrisUT in #518 Docs: - Fix a link by @henryiii in #568 **Full Changelog**: https://github.com/scikit-build/scikit-build-core/compare/v0.7.0...v0.7.1 ## Version 0.7.0 This release features several large improvements to overrides: a new `if.any` group to allow selections based on any item being true, and a new `if.env` option for selecting an override based on environment variables. You can now build pure Python packages with `wheel.cmake = false`, perfect for providing a slower pure Python version of a package on unsupported systems via overrides. There's also a new `inplace` mode for editable installs, which has drawbacks but feels like the classic `"--inplace"` setting in setuptools and can enable some tooling that would otherwise not support modern editable installs to work. If you are using Cython to access `.pxd` files, modern ("redirect") editable installs now support that. And to help avoid collisions with a future user feature, config settings can now be passed with an optional namespace, `skbuild.`, as well. Features: - Add inplace mode for editable installs by @henryiii in #553 - Add `wheel.exclude` by @henryiii in #560 - Support cmake-less runs (for overrides) by @henryiii in #550 - Support `if.any` by @henryiii in #548 - Support `if.env` by @henryiii in #549 - Support namespaced config settings (`skbuild.`) by @henryiii in #556 Fixes: - Correct issue with editable template & add more tests by @henryiii in #552 - Support editable installs of Cython `.pxd` files by @vyasr in #516 CI: - Fix spelling for Fedora by @henryiii in #559 Docs: - Add meeting information by @henryiii in #555 - Update intro and mention cmeel by @henryiii in #551 ## Version 0.6.1 Fixes: - Editable package imports fix (regression in 0.6.0) by @henryiii in #546 - Filter `CMAKE_ARGS` and debug printout by @henryiii in #543 Docs: - Fix bad config option name in `configuration.md` by @0xTowel in #530 Tests and internal: - Add a bit to Ruff config by @henryiii in #544 - Support editable installs in downstream nox check by @henryiii in #533 - Use ruff-format by @henryiii in #536 - Small updates from repo-review by @henryiii in #537 - Refactor and unit test editable install by @henryiii in #545 ## Version 0.6.0 This release features overrides, a system similar to mypy and cibuildwheel's overrides, allowing static configuration of special cases, like different settings per operating system or Python version ranges. It also features preliminary support for `importlib.resources` in editable mode. Features: - Adding overrides by @henryiii in #514 - Overrides platform node by @0xTowel in #526 - Support `importlib.resources` in editable installs by @LecrisUT in #399 Fixes: - Better handling for -G by @henryiii in #483 - Nicer error message when SCM version missing by @henryiii in #528 - (schema) Fix a typo and better metadata support by @henryiii in #522 - (setuptools) Remove `DEBUG` envvar by @henryiii in #527 Tests and internal: - Use virtualenv instead by @henryiii in #371 - Pre-commit update & a couple of touchups by @henryiii in #519 Docs: - Clarify verbosity when using `pip` by @LecrisUT in #513 - Build process and faq by @henryiii in #529 ## Version 0.5.1 Features: - Add `{build_type}` and `{state}` to `build-dir` by @henryiii in #504 - Include 'python' dir as an auto search path by @henryiii in #499 Fixes: - Parse CMake version strings containing '-' by @jllllll in #508 - Set NumPy include directory if known by @henryiii in #482 - Adapt for `setuptools_scm` 8 writing change by @henryiii in #509 - (setuptools) Support `build_type` set in toml by @henryiii in #498 Tests and internal: - Nicer nox for docs by @henryiii in #479 - Some extra Ruff checks by @henryiii in #478 - Packit sync by @LecrisUT in #476 Docs: - Fix template-file with template-path by @Freed-Wu in #485 - `wheel.py.api` -> `wheel.py-api` by @njzjz in #488 - A single-letter change in Configuration by @wojdyr in #490 - Fix typo in `SKBUILD_CMAKE_DEFINE` env var by @aloisklink in #501 - Typo in Configuration by @elazarcoh in #493 - Update and add discord link by @henryiii in #477 - Add page on cross-compiling by @henryiii in #510 ## Version 0.5.0 This release sees the addition of a generated schema, which has also been contributed to SchemaStore, enabling `tool.scikit-build` to be understood by most editors. There's also now a way to enable CMake during the SDist step, a new regex plugin, and a mechanism to write metadata to a file without having to depend on the plugin. Features: - Add option to run CMake during SDist creation by @henryiii in #454 - Add a schema for validate-pyproject by @henryiii in #447, #450, #455 - Added regex plugin and dynamic-metadata rewrite by @henryiii in #457 - Add configuration option to write metadata to file by @henryiii in #459 Fixes: - Normalize sdist names by @henryiii in #434 - Report cmake/ninja required if already present by @henryiii in #462 Tests and internal: - Support Literals in settings by @henryiii in #460 - Clean up some extraneous types by @henryiii in #461 - Use 2x faster black mirror by @henryiii in #456 - Fix Fedora CI by @LecrisUT and @henryiii in #449, #464, #469 - Increase timeout (PyPy slow) by @henryiii in #465 - Cleaner Fedora testing by @LecrisUT in #470 Docs: - Fix error in config names by @jpn-- in #453 - More generation of docs by @henryiii in #452 - Require Sphinx >= 7 while waiting on Furo by @henryiii in #473 - Require Sphinx < 7.2 while waiting on Setuptools by @henryii in #473 - Fix an import check issue by @LecrisUT in #471 ## Version 0.4.8 This release focus on two fixes that correct some interference issues with other setuptools plugins. A few new features were added (opt-in only): the ability to select build targets, install components, and opt-in `--strip` (will be opt-out in 0.5 if the minimum-version is set to 0.5+ or unset). Features: - Add build target support by @henryiii in #432 - Add component support and strip support by @henryiii in #430 Fixes: - (setuptools) Avoid instantiating build too soon by @henryiii in #443 - (setuptools) Avoid interfering with other setuptools plugins by @henryiii in #414 - Only link to valid module paths (not things like gitignore) in editable installs by @henryiii in #444 Docs: - Fix typo and invalid Markdown in `getting_started.md` by @0xTowel in #439 - Conf tabs as extension by @henryiii in #433 - Fix `nanobind`/`pybind11` `src` & sp-dev by @henryiii in #429 - Link to source by @henryiii in #431 - Small suggestions for docs by @rebecca-burwei in #428 Tests and other: - Fix fedora downstream tests by @LecrisUT in #416 - Ruff moved to astral-sh by @henryiii in #418 - `target-version` no longer needed by Black or Ruff by @henryiii in #419 - Use `get_origin`/`get_args` by @henryiii in #423 ## Version 0.4.7 This version fixes a unused variable message in 0.4.6, along with a debug logging improvement, and a few test fixes, including a regression in the recent noxfile reworking. - fix: remove `SKBUILD_STATE` unused message when building by @henryiii in #401 - fix: logging environment should use reprs by @henryiii in #409 Tests and CI: - Support running tests with `NO_COLOR` by @henryiii in #407 - `noxfile.py` added to mypy, two fixes by @henryiii in #408 - Get packages of interest from `pyproject.toml` by @henryiii in #402 - Enable more tests in the spec file by @LecrisUT in #400 ## Version 0.4.6 This release has one small new feature (access to `${SKBUILD_STATE}` from CMake), and fixes an issue when adding read-only files on Windows with Python 3.7. Some testing and docs infrastructure updates should make it easier for downstream packagers to ship scikit-build-core. Fixes: - Provide access to current state in CMake by @henryiii in #394 - Support building older versions of `setuptools_scm` by @henryiii in #390 - Workaround for Windows Python 3.7 `TemporaryDirectory` bug by @henryiii in #391 Tests: - Rework testing extras by @henryiii in #395 and #393 - Add `network` marker by @henryiii in #379 CI: - Add example tests to Fedora packaging by @LecrisUT in #378 - Fedora: Correct rsync rule by @LecrisUT in #389 - Use `not network` for spec by @henryiii in #383 Docs: - Add migration guide by @vyasr in #356 - Support building the documentation as a man page by @henryiii in #372 - Add nanobind example by @henryiii in #375 - Use `UseSWIG` for swig by @henryiii in #377 - Fix or hide nitpicks by @henryiii in #370 ## Version 0.4.5 This version fixes issues with output being incorrectly interleaved with logging messages. Symlinks are now followed when making SDists. And finally, `SKBUILD_SOABI` is now correctly set when cross-compiling on Windows (Warning! FindPython still does not report the correct SOABI when cross-compiling to ARM). Fixes: - Proper printout ordering and more displayed details by @henryiii in #365 - Sort `RUNENV` debugging log output by @jameslamb in #357 - Follow symlinks when making SDists by @henryiii in #362 - Report correct ABI when cross-compiling by @henryiii in #366 Tests: - Fedora downstream CI by @LecrisUT in #358 - Add downstream examples by @henryiii in #363 - Add testing for scripts processing by @henryiii in #364 ## Version 0.4.4 This version fixes some issues cross-compiling to Windows ARM when making Limited API / Stable ABI extensions, and supports multiple config generators in editable mode. - Conditional ABI3 logic fixed by @henryiii in #352 - Set `Python_SABI_LIBRARY` by @henryiii in #352 - Editable installs now support multiconfig generators by @henryiii in #353 ## Version 0.4.3 This adds support for CPython 3.12.0b1, and improves Stable ABI / Limited API support (supported by an upcoming nanobind for Python 3.12). An editable install fix allows running from any directory. Fixes: - Allow CMake to detect if limited API is targeted by @henryiii in #333 and #342 - Make abi3 support conditional on Python version by @henryiii in #344 - Windows path correction for 3.12.0b1 by @henryiii in #346 - Editable path needs to be absolute by @henryiii in #345 Other: - Add 3.12.0b1 by @henryiii in #341 - Refactor settings by @henryiii in #338 - Document that `CMAKE_ARGS` supports space separators by @henryiii in #339 ## Version 0.4.2 This is a quick followup to LICENSE file handing to closer match the current draft of [PEP 639](https://peps.python.org/pep-0639). It also removes the automatic optional Rich logging, which doesn't work well with Pip's subprocess piping, being cropped to a very narrow width regardless of terminal size. Fixes: - Add `License-file` metadata entry & update default by @henryiii in #329 - Drop optional Rich logging/error by @henryiii in #330 Other: - Update PyPI links by @henryiii in #331 ## Version 0.4.1 A fix for LICENCE files being placed in the wrong place in the wheel. Now we follow hatchling's structure of placing them in `*.dist-info/licenses`. Fixes: - LICENCE files were placed in the wrong place by @henryiii in #325 Other: - Fix rpm inspect test failures by @LecrisUT in #324 ## Version 0.4.0 An important fix/feature: LICENSE files were not being included in the wheel's metadata folder. You can configure the license file selection via a new configuration option, and a reasonable default was added. You can now select a source directory for your CMakeLists.txt. A lot of work was done on the still experimental setuptools backend; it still should be seen as completely experimental while it is being finished. Features: - `cmake.source-dir` for CMakeLists in subdirectories by @henryiii in #323 - Add `LICENSE` file option by @henryiii in #321 Fixes: - Ninja wasn't being used if present by @henryiii in #310 - Wheels were not including the `LICENSE` file by @henryiii in #321 Setuptools plugin: - Refactor plugin as custom setuptools command by @henryiii in #312 - Adding `cmake_args` by @henryiii in #314 - Add wrapper for `skbuild.setup` compat by @henryiii in #315 Other: - Add rpmlint and smoke tests by @LecrisUT in #313 ## Version 0.3.3 This version improves WebAssembly support (Pyodide) and fixes a reported bug in the new editable mode. Fixes: - Support prefix dir if toolchain has `CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY` by @henryiii in #303 - Find wheel files before local files in editable installs by @henryiii in #305 Other: - Use PyPI's new trusted publisher deployment by @henryiii in #306 ## Version 0.3.2 Some small fixes for edge cases. Several docs improvements, too. Fixes: - Suppress Unicode errors in scripts dir and move on by @henryiii in #294 - Specify platform properly for non-MSVC Windows by @henryiii in #295 Docs: - Doc updates by @zerothi in #287 - Add a bit to plugin instructions by @henryiii in #289 - Typos fixed by @afh in #291 and #292 ## Version 0.3.1 This is a small release fixing a regression in some cases caused by adding `Python_LIBRARY`. This has been reverted on non-Windows platforms, since it is only needed on Windows. Fixes: - Support older setuptools-scm by @henryiii in #284 - Only set the lib for FindPython on Windows by @henryiii in #285 Docs: - Fix incorrect tool name by @henryiii in #276 - Typo on tab Fortran (was Cython) by @zerothi in #279 - Fix wheel.packages by @henryiii in #282 Other: - Change Fedora PR targets by @LecrisUT in #273 ## Version 0.3.0 This version brings two new dynamic metadata plugins (wrappers for `setuptools_scm` & `hatch-pypi-fancy-readme`). Third-party packages can now add entry-points declaring `CMAKE_PREFIX_DIR` and `CMAKE_MODULE_DIR` entries. Support has been added for requesting metadata without building. And experimental support was added for editable installs, including an option for automatic rebuilds. Several fixes have been added as well, like SABI support, ARM cross-compiling support for FindPython, scripts entries now process shebang lines, and setting a `build-dir` with `{wheel_tag}` was not working before. The docs have been started, with a quickstart for common situations, a page on configuration, and some info on authoring a CMakeLists. Features: - Support dynamic metadata by @bennyrowland in #197 and rework by @henryiii in #251 - Support modules/prefix dirs by @henryiii in #255 - Add `get_requires_for_dynamic_metadata` by @henryiii in #235 - Make setuptools wrapper more generic by @henryiii in #225 - Experimental support for editable installs by @henryiii in #212, #268, and #271 Fixes: - CMake 3.26.0 (exactly) needs the backport too by @henryiii in #238 - Add python library artifact for better Windows cross compiling by @henryiii in #263 - Include 3.26.1 SABI fix by @henryiii in #227 - Restructure `get_requires` & fix some ninja paths by @henryiii in #250 - Support script rewriting by @henryiii in #254 - Version not a string (typing updates) by @henryiii in #231 - `{wheel_tag}` was not working by @henryiii in #262 - `CMAKE_PREFIX_DIR` and `CMAKE_MODULE_DIR` are passed in the init cache file to remove a unused variable warning by @henryiii in #272 - Support color printouts without Rich (pip requires `FORCE_COLOR`) by @henryiii in #266 Other things: - Add Fortran testing and CI by @henryiii in #86 - Avoid internet usage in non-isolated testing by @henryiii in #247 - Add an SDist checker & fix contents by @henryiii in #253 - Add more setuptools types by @henryiii in #233 - Add FedoraProject rpm spec file by @LecrisUT in #201 and #241 - Better coverage handling by @henryiii in #270 ## Version 0.2.2 This release makes a small improvement to the wheel file permissions (in line with wheel 0.40). It also ensures the test suite will still pass in an environment with `SOURCE_DATE_EPOCH` already set. A few internal changes are paving the way to 0.3.0. Fixes: - zipinfo should report regular files by @henryiii in #220 Tests: - Support running in environments with `SOURCE_DATE_EPOCH` set by @LecrisUT in #221 - Report self version too by @henryiii in #222 Other things: - refactor: use `from_pyproject` by @henryiii and @bennyrowland in #224 - chore: fix a mypy complaint on Windows by @henryiii in #217 - docs: add quickstart by @henryiii in #226 ## Version 0.2.1 This release fixes the tag for Windows ARM wheels, and has some internal refactoring to prepare for the next new features. A new `{wheel_tag}` value is available for `build-dir`. Some basic setup was done on the docs, as well. Debug logging and test output has been improved a little, as well. Changes: - Add `{wheel_tag}` for `build-dir` by @henryiii in #207 - Support for conda's `CMAKE_SYSTEM_PROCESSOR` by @henryiii in #207 Fixes: - Windows ARM tag by @henryiii in #215 - Include Windows ARM in known wheels by @henryiii in #203 - Print out paths by @henryiii in #205 Other things: - docs: update readme for 3.26 backport by @henryiii in #206 - tests: support running tests with system `cmake3` visible by @LecrisUT in #211 - tests: nicer exit, minor refactors by @henryiii in #213 - refactor: minor changes & nicer environment logging printout by @henryiii in #214 ## Version 0.2.0 This version adds local build directory support - you can now set `build-dir` and reuse build targets. This does not yet default to on, so please test it out. This can dramatically speed up rebuilds. If you want to mimic setuptools, you can set this to `build/{cache_tag}`. Or you can chose some other directory, like scikit-build classic's `_skbuild`. Along with this, we now have a native wheel writer implementation and support `prepare_metadata_for_build_wheel`. Scikit-build-core now also contains a backport of FindPython from CMake 3.26, which fixes SOABI on PyPy and supports the Stable ABI / Limited API. Features: - Local build directory setting & build reuse by @henryiii in #181 - Add `prepare_metadata_for_build_wheel` by @henryiii in #191 - Native wheel writer implementation by @henryiii in #188 - Use 3.26 dev version port of FindPython by @henryiii in #102 Tests: - Allow pytest 7.0+ instead of 7.2+ by @henryiii in #200 - Include cmake and ninja if missing in nox by @henryiii in #190 - Simpler pytest-subprocess by @henryiii in #159 Other things: - chore: Python 3.11 Self usage by @henryiii in #199 - chore: fix Ruff configuration by @henryiii in #186 - chore: minor adjustments to wheel returns by @henryiii in #195 - chore: remove duplicate Ruff code by @burgholzer in #184 ## Version 0.1.6 Fixes: - Handle local cmake dir for search by @henryiii in #179 - Avoid resolving cmake/ninja paths by @henryiii in #183 Other things: - Use Ruff by @henryiii in #175 - Ruff related additions by @henryiii in #180 - Add `isolated` marker to `test_pep518_sdist` by @bnavigator in #182 ## Version 0.1.5 Fixes: - Ninja path not being set correctly by @henryiii in #166 - Minor touchup to ninja / make by @henryiii in #167 ## Version 0.1.4 Fixes: - `entrypoints.txt` should be `entry_points.txt` by @njzjz in #161 - `EXT_SUFFIX` is wrong before 3.8.7 by @henryiii in #160 - Make tests pass on native Windows ARM by @henryiii in #157 - Windows ARM experimental cross-compile support by @henryiii in #162 Other things: - Fix spelling mistake by @maxbachmann in #156 - Add Python 3.12 alpha 3 to the CI by @henryiii in #120 - Fix issues mocking in tests with packaging 22 by @henryiii in #155 ## Version 0.1.3 Fixes: - Issue with experimental extra directory targeting by @henryiii in #144 - Sort SDist filepaths for reproducibility by @agoose77 in #153 ## Version 0.1.2 Features: - Provide null directory (not installed) by @henryiii in #143 Fixes: - Fix issue with 32-bit Windows in 0.1.1 by @henryiii in #142 ## Version 0.1.1 Fixes: - Windows non-default generators by @henryiii in #137 - Compute the correct default generator for CMake by @henryiii in #139 Testing: - Support make missing by @henryiii in #140 - Clear `CMAKE_GENERATOR` by @henryiii in #141 ## Version 0.1.0 First non-prerelease! Scikit-build-core is ready to be used. The remaining limitations (like support for editable mode and build caching) will be addressed in future releases. If you set `tool.scikit-build.minimum-version = "0.1"`, scikit-build-core will try to respect old defaults when new versions are released. ## Version 0.1.0rc2 Still preparing for release. One small addition to the error printout. Features: - Did you mean? for config-settings and pyproject.toml by @henryiii in #135 Testing: - Split up isolated and virtualenv fixtures by @henryiii in #134 ## Version 0.1.0rc1 Preparing for a non-beta release. Fixes: - Paths on Windows by @henryiii in #126 - Support pre-set generators by @henryiii in #118 - Warn on scripts with invalid shebangs by @henryiii in #132 - Minimum constraints now set by @henryiii in #129 Refactoring: - Rename pyproject -> build dir by @henryiii in #121 Testing: - Add msys2 to the CI by @henryiii in #119 - Add test report header by @henryiii in #124 - Test min constraints without Windows by @henryiii in #129 - Remove pytest-virtualenv by @henryiii in #125 and #131 - Mark unimportant test xfail non-strict for conda-forge by @henryiii in #108 ## Version 0.1.0b2 A quick fix for macOS universal2 tags not expanding when enabled. Fixes: - Expand macos tags missing by @henryiii in #105 Other things: - Add tests marker for PEP 518 by @henryiii in #104 - Require C++11 only in tests by @henryiii in #106 - Xfail a non-important test by @henryiii in #107 **Full Changelog**: https://github.com/scikit-build/scikit-build-core/compare/v0.1.0b1...v0.1.0b2 ## Version 0.1.0b1 This release is focused on preparing for conda-forge and some macOS fixes. Features: - Configuration setting for FindPython backport by @henryiii in #103 Fixes: - Conda prefix lib support by @henryiii in #95 - Guess single config for more generators by @henryiii in #101 - Universal2 tags computation was incorrect by @henryiii in #97 - Universal2 tags computation was incorrect again by @henryiii in #100 Refactoring: - Rename extra color -> rich by @henryiii in #98 Other things: - Run more tests without the cmake module by @henryiii in #96 - Support running without pytest-virtualenv by @henryiii in #94 ## Version 0.1.0b0 This release adds a lot of configuration options, including `minimum-version`, which can be set to 0.0 to ensure defaults remain consistent (similar to `cmake_minimum_required`). Features: - Dict options by @henryiii in #78 - Min version setting by @henryiii in #84 - Strict configuration checking by @henryiii in #75 - Support for args/define by @henryiii in #83 - Support for other wheel dirs by @henryiii in #82 - Support specifying a build type by @henryiii in #90 Fixes: - Better logging by @henryiii in #88 - Better macOS deployment target handling by @henryiii in #74 - Don't touch mtime in non-reproducible mode by @henryiii in #76 - Fallback to ninja sooner if on known platform by @henryiii in #80 Refactoring: - Rename CMakeConfig -> CMaker by @henryiii in #91 - Drop config prefix by @henryiii in #77 - Rename to `wheel.py-api` and expand, ignore on non-cpython / old cpython by @henryiii in #81 Other things: - Add cygwin by @henryiii in #89 ## Version 0.1.0a1 This release brings a lot of further development. This is starting to be used by downstream projects; it is a good idea to be a little careful with versions still, configuration may change. Features: - Allow python packages to be specified by @henryiii in #58 - Autocopy packages if names match by @henryiii in #53 - Include/exclude by @henryiii in #59 - Color status messages for wheel by @henryiii in #60 - Support reproducible sdist builds by @agoose77 in #64 - Prettier logging with config setting by @henryiii in #40 - Add `extra-tags` by @henryiii in #49 - Support for setting python & abi tag (including limited API) by @henryiii in #47 - (setuptools) Use setup keyword support by @henryiii in #42 - (setuptools) `cmake_source_dir` from scikit-build classic by @henryiii in #45 Fixes: - Avoid copy, avoid failure if pre-existing by @henryiii in #41 - Better support for FindPython by @henryiii in #38 - Fallback to make if available (setting) by @henryiii in #57 - Handle `PermissionError` in reading `libdir.is_dir()` by @agoose77 in #43 - Include `--config` when installing by @henryiii in #61 - Incorrect min version of macOS by @henryiii in #50 - Lists and bool settings by @henryiii in #56 - Mkdir for sdist if missing, test polish by @henryiii in #44 - Simple example PyPy support workaround by @henryiii in #37 Refactoring: - Tags configuration group by @henryiii in #55 - (setuptools) Use native bdist_wheel setting for abi3 by @henryiii in #52 - Rename `cmake_settings` to `skbuild_settings` by @henryiii in #46 - Refactor wheel code a bit to read better by @henryiii in #65 Other things: - Better logging on macOS for deployment target by @henryiii in #48 - Format cmake files by @henryiii in #54 ## Version 0.1.0a0 First experimental snapshot. scikit-build-core-0.11.1/docs/about/projects.md000066400000000000000000000106331477275177200213460ustar00rootroot00000000000000# Projects There are over 200 projects using scikit-build-core on PyPI. This is a selection of some of the projects. Feel free to add your own project to `docs/data/projects.toml`. The following selection was primarily constructed by looking at the top 8,000 most downloaded projects on PyPI. - [cmake](https://pypi.org/project/cmake) ([source](https://github.com/scikit-build/cmake-python-distributions/blob/HEAD/pyproject.toml)) - [ninja](https://pypi.org/project/ninja) ([source](https://github.com/scikit-build/ninja-python-distributions/blob/HEAD/pyproject.toml)) - [pyzmq](https://pypi.org/project/pyzmq) ([source](https://github.com/zeromq/pyzmq/blob/HEAD/pyproject.toml)) - [lightgbm](https://pypi.org/project/lightgbm) ([source](https://github.com/microsoft/LightGBM/blob/HEAD/python-package/pyproject.toml)) - [phik](https://pypi.org/project/phik) ([source](https://github.com/kaveio/phik/blob/HEAD/pyproject.toml)) - [clang-format](https://pypi.org/project/clang-format) ([source](https://github.com/ssciwr/clang-format-wheel/blob/HEAD/pyproject.toml)) - [llama-cpp-python](https://pypi.org/project/llama-cpp-python) ([source](https://github.com/abetlen/llama-cpp-python/blob/HEAD/pyproject.toml)) - [coreforecast](https://pypi.org/project/coreforecast) ([source](https://github.com/Nixtla/coreforecast/blob/HEAD/pyproject.toml)) - [sparse-dot-topn](https://pypi.org/project/sparse-dot-topn) ([source](https://github.com/ing-bank/sparse_dot_topn/blob/HEAD/pyproject.toml)) - [spglib](https://pypi.org/project/spglib) ([source](https://github.com/spglib/spglib/blob/HEAD/pyproject.toml)) - [awkward-cpp](https://pypi.org/project/awkward-cpp) ([source](https://github.com/scikit-hep/awkward/blob/HEAD/awkward-cpp/pyproject.toml)) - [OpenEXR](https://pypi.org/project/OpenEXR) ([source](https://github.com/AcademySoftwareFoundation/OpenEXR/blob/HEAD/pyproject.toml)) - [iminuit](https://pypi.org/project/iminuit) ([source](https://github.com/scikit-hep/iminuit/blob/HEAD/pyproject.toml)) - [boost-histogram](https://pypi.org/project/boost-histogram) ([source](https://github.com/scikit-hep/iminuit/blob/HEAD/pyproject.toml)) - [astyle](https://pypi.org/project/astyle) ([source](https://github.com/Freed-Wu/astyle-wheel/blob/HEAD/pyproject.toml)) - [lammps](https://pypi.org/project/lammps) ([source](https://github.com/njzjz/lammps-wheel/blob/HEAD/pyproject.toml)) - [llamacpp](https://pypi.org/project/llamacpp) ([source](https://github.com/thomasantony/llamacpp-python/blob/HEAD/pyproject.toml)) - [nodejs-wheel](https://pypi.org/project/nodejs-wheel) ([source](https://github.com/njzjz/nodejs-wheel/blob/HEAD/pyproject.toml)) - [pygram11](https://pypi.org/project/pygram11) ([source](https://github.com/douglasdavis/pygram11/blob/HEAD/pyproject.toml)) In addition, most of the [RAPIDSAI](https://github.com/rapidsai) projects use scikit-build-core, but they are not published on PyPI. A few of them are: - CuDF ([source](https://github.com/rapidsai/cudf/blob/HEAD/python/cudf/pyproject.toml)) - CuGraph ([source](https://github.com/rapidsai/cugraph/blob/HEAD/python/cugraph/pyproject.toml)) - CuML ([source](https://github.com/rapidsai/cuml/blob/HEAD/python/cuml/pyproject.toml)) - CuSpatial ([source](https://github.com/rapidsai/cuspatial/blob/HEAD/python/cuspatial/pyproject.toml)) - RMM ([source](https://github.com/rapidsai/rmm/blob/HEAD/python/rmm/pyproject.toml)) - Raft ([source](https://github.com/rapidsai/raft/blob/HEAD/python/pylibraft/pyproject.toml)) The [Insight Toolkit (ITK)](https://docs.itk.org), the initial target project for scikit-build classic, has [transitioned to sckit-build-core](https://github.com/InsightSoftwareConsortium/ITKPythonPackage/blob/master/scripts/pyproject.toml.in). ITK currently provides one example of a production SWIG-based deployment. In addition, dozens of [ITK-based extension packages are configured with scikit-build-core](https://github.com/topics/itk-module). scikit-build-core-0.11.1/docs/api/000077500000000000000000000000001477275177200166275ustar00rootroot00000000000000scikit-build-core-0.11.1/docs/api/scikit_build_core.ast.rst000066400000000000000000000010261477275177200236230ustar00rootroot00000000000000scikit\_build\_core.ast package =============================== .. automodule:: scikit_build_core.ast :members: :show-inheritance: :undoc-members: Submodules ---------- scikit\_build\_core.ast.ast module ---------------------------------- .. automodule:: scikit_build_core.ast.ast :members: :show-inheritance: :undoc-members: scikit\_build\_core.ast.tokenizer module ---------------------------------------- .. automodule:: scikit_build_core.ast.tokenizer :members: :show-inheritance: :undoc-members: scikit-build-core-0.11.1/docs/api/scikit_build_core.build.rst000066400000000000000000000016341477275177200241400ustar00rootroot00000000000000scikit\_build\_core.build package ================================= .. automodule:: scikit_build_core.build :members: :show-inheritance: :undoc-members: Submodules ---------- scikit\_build\_core.build.generate module ----------------------------------------- .. automodule:: scikit_build_core.build.generate :members: :show-inheritance: :undoc-members: scikit\_build\_core.build.metadata module ----------------------------------------- .. automodule:: scikit_build_core.build.metadata :members: :show-inheritance: :undoc-members: scikit\_build\_core.build.sdist module -------------------------------------- .. automodule:: scikit_build_core.build.sdist :members: :show-inheritance: :undoc-members: scikit\_build\_core.build.wheel module -------------------------------------- .. automodule:: scikit_build_core.build.wheel :members: :show-inheritance: :undoc-members: scikit-build-core-0.11.1/docs/api/scikit_build_core.builder.rst000066400000000000000000000025371477275177200244720ustar00rootroot00000000000000scikit\_build\_core.builder package =================================== .. automodule:: scikit_build_core.builder :members: :show-inheritance: :undoc-members: Submodules ---------- scikit\_build\_core.builder.builder module ------------------------------------------ .. automodule:: scikit_build_core.builder.builder :members: :show-inheritance: :undoc-members: scikit\_build\_core.builder.generator module -------------------------------------------- .. automodule:: scikit_build_core.builder.generator :members: :show-inheritance: :undoc-members: scikit\_build\_core.builder.get\_requires module ------------------------------------------------ .. automodule:: scikit_build_core.builder.get_requires :members: :show-inheritance: :undoc-members: scikit\_build\_core.builder.macos module ---------------------------------------- .. automodule:: scikit_build_core.builder.macos :members: :show-inheritance: :undoc-members: scikit\_build\_core.builder.sysconfig module -------------------------------------------- .. automodule:: scikit_build_core.builder.sysconfig :members: :show-inheritance: :undoc-members: scikit\_build\_core.builder.wheel\_tag module --------------------------------------------- .. automodule:: scikit_build_core.builder.wheel_tag :members: :show-inheritance: :undoc-members: scikit-build-core-0.11.1/docs/api/scikit_build_core.file_api.model.rst000066400000000000000000000033071477275177200257070ustar00rootroot00000000000000scikit\_build\_core.file\_api.model package =========================================== .. automodule:: scikit_build_core.file_api.model :members: :show-inheritance: :undoc-members: Submodules ---------- scikit\_build\_core.file\_api.model.cache module ------------------------------------------------ .. automodule:: scikit_build_core.file_api.model.cache :members: :show-inheritance: :undoc-members: scikit\_build\_core.file\_api.model.cmakefiles module ----------------------------------------------------- .. automodule:: scikit_build_core.file_api.model.cmakefiles :members: :show-inheritance: :undoc-members: scikit\_build\_core.file\_api.model.codemodel module ---------------------------------------------------- .. automodule:: scikit_build_core.file_api.model.codemodel :members: :show-inheritance: :undoc-members: scikit\_build\_core.file\_api.model.common module ------------------------------------------------- .. automodule:: scikit_build_core.file_api.model.common :members: :show-inheritance: :undoc-members: scikit\_build\_core.file\_api.model.directory module ---------------------------------------------------- .. automodule:: scikit_build_core.file_api.model.directory :members: :show-inheritance: :undoc-members: scikit\_build\_core.file\_api.model.index module ------------------------------------------------ .. automodule:: scikit_build_core.file_api.model.index :members: :show-inheritance: :undoc-members: scikit\_build\_core.file\_api.model.toolchains module ----------------------------------------------------- .. automodule:: scikit_build_core.file_api.model.toolchains :members: :show-inheritance: :undoc-members: scikit-build-core-0.11.1/docs/api/scikit_build_core.file_api.rst000066400000000000000000000012371477275177200246100ustar00rootroot00000000000000scikit\_build\_core.file\_api package ===================================== .. automodule:: scikit_build_core.file_api :members: :show-inheritance: :undoc-members: Subpackages ----------- .. toctree:: :maxdepth: 4 scikit_build_core.file_api.model Submodules ---------- scikit\_build\_core.file\_api.query module ------------------------------------------ .. automodule:: scikit_build_core.file_api.query :members: :show-inheritance: :undoc-members: scikit\_build\_core.file\_api.reply module ------------------------------------------ .. automodule:: scikit_build_core.file_api.reply :members: :show-inheritance: :undoc-members: scikit-build-core-0.11.1/docs/api/scikit_build_core.hatch.rst000066400000000000000000000010451477275177200241240ustar00rootroot00000000000000scikit\_build\_core.hatch package ================================= .. automodule:: scikit_build_core.hatch :members: :show-inheritance: :undoc-members: Submodules ---------- scikit\_build\_core.hatch.hooks module -------------------------------------- .. automodule:: scikit_build_core.hatch.hooks :members: :show-inheritance: :undoc-members: scikit\_build\_core.hatch.plugin module --------------------------------------- .. automodule:: scikit_build_core.hatch.plugin :members: :show-inheritance: :undoc-members: scikit-build-core-0.11.1/docs/api/scikit_build_core.metadata.rst000066400000000000000000000014771477275177200246260ustar00rootroot00000000000000scikit\_build\_core.metadata package ==================================== .. automodule:: scikit_build_core.metadata :members: :show-inheritance: :undoc-members: Submodules ---------- scikit\_build\_core.metadata.fancy\_pypi\_readme module ------------------------------------------------------- .. automodule:: scikit_build_core.metadata.fancy_pypi_readme :members: :show-inheritance: :undoc-members: scikit\_build\_core.metadata.regex module ----------------------------------------- .. automodule:: scikit_build_core.metadata.regex :members: :show-inheritance: :undoc-members: scikit\_build\_core.metadata.setuptools\_scm module --------------------------------------------------- .. automodule:: scikit_build_core.metadata.setuptools_scm :members: :show-inheritance: :undoc-members: scikit-build-core-0.11.1/docs/api/scikit_build_core.resources.find_python.rst000066400000000000000000000003251477275177200273670ustar00rootroot00000000000000scikit\_build\_core.resources.find\_python package ================================================== .. automodule:: scikit_build_core.resources.find_python :members: :show-inheritance: :undoc-members: scikit-build-core-0.11.1/docs/api/scikit_build_core.resources.rst000066400000000000000000000004221477275177200250450ustar00rootroot00000000000000scikit\_build\_core.resources package ===================================== .. automodule:: scikit_build_core.resources :members: :show-inheritance: :undoc-members: Subpackages ----------- .. toctree:: :maxdepth: 4 scikit_build_core.resources.find_python scikit-build-core-0.11.1/docs/api/scikit_build_core.rst000066400000000000000000000022201477275177200230320ustar00rootroot00000000000000scikit\_build\_core package =========================== .. automodule:: scikit_build_core :members: :show-inheritance: :undoc-members: Subpackages ----------- .. toctree:: :maxdepth: 4 scikit_build_core.ast scikit_build_core.build scikit_build_core.builder scikit_build_core.file_api scikit_build_core.hatch scikit_build_core.metadata scikit_build_core.resources scikit_build_core.settings scikit_build_core.setuptools Submodules ---------- scikit\_build\_core.cmake module -------------------------------- .. automodule:: scikit_build_core.cmake :members: :show-inheritance: :undoc-members: scikit\_build\_core.errors module --------------------------------- .. automodule:: scikit_build_core.errors :members: :show-inheritance: :undoc-members: scikit\_build\_core.format module --------------------------------- .. automodule:: scikit_build_core.format :members: :show-inheritance: :undoc-members: scikit\_build\_core.program\_search module ------------------------------------------ .. automodule:: scikit_build_core.program_search :members: :show-inheritance: :undoc-members: scikit-build-core-0.11.1/docs/api/scikit_build_core.settings.rst000066400000000000000000000045041477275177200247000ustar00rootroot00000000000000scikit\_build\_core.settings package ==================================== .. automodule:: scikit_build_core.settings :members: :show-inheritance: :undoc-members: Submodules ---------- scikit\_build\_core.settings.auto\_cmake\_version module -------------------------------------------------------- .. automodule:: scikit_build_core.settings.auto_cmake_version :members: :show-inheritance: :undoc-members: scikit\_build\_core.settings.auto\_requires module -------------------------------------------------- .. automodule:: scikit_build_core.settings.auto_requires :members: :show-inheritance: :undoc-members: scikit\_build\_core.settings.documentation module ------------------------------------------------- .. automodule:: scikit_build_core.settings.documentation :members: :show-inheritance: :undoc-members: scikit\_build\_core.settings.json\_schema module ------------------------------------------------ .. automodule:: scikit_build_core.settings.json_schema :members: :show-inheritance: :undoc-members: scikit\_build\_core.settings.skbuild\_docs module ------------------------------------------------- .. automodule:: scikit_build_core.settings.skbuild_docs :members: :show-inheritance: :undoc-members: scikit\_build\_core.settings.skbuild\_model module -------------------------------------------------- .. automodule:: scikit_build_core.settings.skbuild_model :members: :show-inheritance: :undoc-members: scikit\_build\_core.settings.skbuild\_overrides module ------------------------------------------------------ .. automodule:: scikit_build_core.settings.skbuild_overrides :members: :show-inheritance: :undoc-members: scikit\_build\_core.settings.skbuild\_read\_settings module ----------------------------------------------------------- .. automodule:: scikit_build_core.settings.skbuild_read_settings :members: :show-inheritance: :undoc-members: scikit\_build\_core.settings.skbuild\_schema module --------------------------------------------------- .. automodule:: scikit_build_core.settings.skbuild_schema :members: :show-inheritance: :undoc-members: scikit\_build\_core.settings.sources module ------------------------------------------- .. automodule:: scikit_build_core.settings.sources :members: :show-inheritance: :undoc-members: scikit-build-core-0.11.1/docs/api/scikit_build_core.setuptools.rst000066400000000000000000000014751477275177200252650ustar00rootroot00000000000000scikit\_build\_core.setuptools package ====================================== .. automodule:: scikit_build_core.setuptools :members: :show-inheritance: :undoc-members: Submodules ---------- scikit\_build\_core.setuptools.build\_cmake module -------------------------------------------------- .. automodule:: scikit_build_core.setuptools.build_cmake :members: :show-inheritance: :undoc-members: scikit\_build\_core.setuptools.build\_meta module ------------------------------------------------- .. automodule:: scikit_build_core.setuptools.build_meta :members: :show-inheritance: :undoc-members: scikit\_build\_core.setuptools.wrapper module --------------------------------------------- .. automodule:: scikit_build_core.setuptools.wrapper :members: :show-inheritance: :undoc-members: scikit-build-core-0.11.1/docs/conf.py000066400000000000000000000117071477275177200173630ustar00rootroot00000000000000# Configuration file for the Sphinx documentation builder. # # This file only contains a selection of the most common options. For a full # list see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html from __future__ import annotations import importlib import importlib.metadata import inspect import os import sys import warnings from pathlib import Path try: import scikit_build_core except ModuleNotFoundError: scikit_build_core = None ROOT = Path(__file__).parent.parent.resolve() # Custom extension sys.path.append(str(ROOT / "docs/ext")) try: from scikit_build_core import __version__ as version except ModuleNotFoundError: try: version = importlib.metadata.version("scikit_build_core") except ModuleNotFoundError: msg = ( "Package should be installed to produce documentation! " "Assuming a modern git archive was used for version discovery." ) warnings.warn(msg, stacklevel=1) from setuptools_scm import get_version version = get_version(root=ROOT, fallback_root=ROOT) # Filter git details from version release = version.split("+")[0] # -- Project information ----------------------------------------------------- project = "scikit-build-core" copyright = "2022, The Scikit-Build admins" author = "Henry Schreiner" # -- General configuration --------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ "myst_parser", "sphinx.ext.autodoc", "sphinx.ext.intersphinx", "sphinx.ext.linkcode", "sphinx.ext.mathjax", "sphinx.ext.napoleon", "sphinx_copybutton", "sphinx_inline_tabs", "sphinx_autodoc_typehints", "conftabs", "sphinx-jsonschema", "sphinx_tippy", ] # Add any paths that contain templates here, relative to this directory. templates_path = [] source_suffix = [".rst", ".md"] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. exclude_patterns = [ "_build", "**.ipynb_checkpoints", "Thumbs.db", ".DS_Store", ".env", "**.venv", "examples/downstream", ] intersphinx_mapping = { "python": ("https://docs.python.org/3", None), "packaging": ("https://packaging.pypa.io/en/stable", None), "setuptools": ("https://setuptools.pypa.io/en/latest", None), "hatchling": ("https://hatch.pypa.io/latest", None), } tippy_rtd_urls = [ val[0] for key, val in intersphinx_mapping.items() # Only works with RTD hosted intersphinx if key not in ("hatchling", "python") ] nitpick_ignore = [ ("py:class", "setuptools.dist.Distribution"), ("py:class", "T"), ("py:class", "scikit_build_core.settings.sources.T"), ("py:class", "scikit_build_core._vendor.pyproject_metadata.StandardMetadata"), ] linkcheck_anchors_ignore = [ # This seems to be broken on GitHub readmes "default-versioning-scheme", "git-archives", ] # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = "furo" # -- Extension configuration ------------------------------------------------- myst_enable_extensions = [ "colon_fence", "substitution", "deflist", ] myst_substitutions = { "version": version, } # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section) man_pages = [ ( "man", project, "A Python module build backend for CMake", [author], 7, ) ] commit = os.environ.get("READTHEDOCS_GIT_COMMIT_HASH", "main") code_url = "https://github.com/scikit-build/scikit-build-core/blob" def linkcode_resolve(domain: str, info: dict[str, str]) -> str | None: if scikit_build_core is None: return None if domain != "py": return None mod = importlib.import_module(info["module"]) if "." in info["fullname"]: objname, attrname = info["fullname"].split(".") obj = getattr(mod, objname) try: # object is a method of a class obj = getattr(obj, attrname) except AttributeError: # object is an attribute of a class return None else: obj = getattr(mod, info["fullname"]) try: file = Path(inspect.getsourcefile(obj)) lines = inspect.getsourcelines(obj) except TypeError: # e.g. object is a typing.Union return None try: mod = Path(inspect.getsourcefile(scikit_build_core)).parent file = file.relative_to(mod) except ValueError: return None start, end = lines[1], lines[1] + len(lines[0]) - 1 return f"{code_url}/{commit}/src/scikit_build_core/{file}#L{start}-L{end}" scikit-build-core-0.11.1/docs/configuration/000077500000000000000000000000001477275177200207255ustar00rootroot00000000000000scikit-build-core-0.11.1/docs/configuration/dynamic.md000066400000000000000000000131371477275177200227000ustar00rootroot00000000000000# Dynamic metadata Scikit-build-core supports dynamic metadata with three built-in plugins. :::{warning} Your package and third-party packages can also extend these with new plugins, but this is currently not ready for development outside of scikit-build-core; `tool.scikit-build.experimental=true` is required to use plugins that are not shipped with scikit-build-core, since the interface is provisional and may change between _minor_ versions. ::: ## `version`: Setuptools-scm You can use [setuptools-scm](https://github.com/pypa/setuptools-scm) to pull the version from VCS: ```toml [project] name = "mypackage" dynamic = ["version"] [tool.scikit-build] metadata.version.provider = "scikit_build_core.metadata.setuptools_scm" sdist.include = ["src/package/_version.py"] [tool.setuptools_scm] # Section required write_to = "src/package/_version.py" ``` This sets the python project version according to [git tags](https://github.com/pypa/setuptools-scm/blob/fb261332d9b46aa5a258042d85baa5aa7b9f4fa2/README.rst#default-versioning-scheme) or a [`.git_archival.txt`](https://github.com/pypa/setuptools-scm/blob/fb261332d9b46aa5a258042d85baa5aa7b9f4fa2/README.rst#git-archives) file, or equivalents for other VCS systems. If you need to set the CMake project version without scikit-build-core (which provides `${SKBUILD_PROJECT_VERSION}`), you can use something like [`DynamicVersion` module](https://github.com/LecrisUT/CMakeExtraUtils/blob/180604da50a3c3588f9d04e4ebc6abb4e5a0d234/cmake/DynamicVersion.md) from [github.com/LecrisUT/CMakeExtraUtils](https://github.com/LecrisUT/CMakeExtraUtils): ```cmake # Import `CMakeExtraUtils` or bundle `DynamicVersion.cmake` from there include(DynamicVersion) # Set ${PROJECT_VERSION} according to git tag or `.git_archival.txt` dynamic_version() project(MyPackage VERSION ${PROJECT_VERSION}) ``` ## `version`: Regex If you want to pull a string-valued expression (usually version) from an existing file, you can the integrated `regex` plugin to pull the information. ```toml name = "mypackage" dynamic = ["version"] [tool.scikit-build.metadata.version] provider = "scikit_build_core.metadata.regex" input = "src/mypackage/__init__.py" ``` You can set a custom regex with `regex=`. By default when targeting version, you get a reasonable regex for python files, `'(?i)^(__version__|VERSION)(?: ?\: ?str)? *= *([\'"])v?(?P.+?)\2'`. You can set `result` to a format string to process the matches; the default is `"{value}"`. You can also specify a regex for `remove=` which will strip any matches from the final result. A more complex example: ```toml [tool.scikit-build.metadata.version] provider = "scikit_build_core.metadata.regex" input = "src/mypackage/version.hpp" regex = '''(?sx) \#define \s+ VERSION_MAJOR \s+ (?P\d+) .*? \#define \s+ VERSION_MINOR \s+ (?P\d+) .*? \#define \s+ VERSION_PATCH \s+ (?P\d+) .*? \#define \s+ VERSION_DEV \s+ (?P\d+) .*? ''' result = "{major}.{minor}.{patch}dev{dev}" remove = "dev0" ``` This will remove the "dev" tag when it is equal to 0. ```{versionchanged} 0.10 Support for `result` and `remove` added. ``` ## `readme`: Fancy-pypi-readme You can use [hatch-fancy-pypi-readme](https://github.com/hynek/hatch-fancy-pypi-readme) to render your README: ```toml [project] name = "mypackage" dynamic = ["readme"] [tool.scikit-build] metadata.readme.provider = "scikit_build_core.metadata.fancy_pypi_readme" # tool.hatch.metadata.hooks.fancy-pypi-readme options here ``` ## `build-system.requires`: Scikit-build-core's `build.requires` If you need to inject and manipulate additional `build-system.requires`, you can use the `build.requires`. This is intended to be used in combination with [](./overrides.md). This is not technically a dynamic metadata and thus does not have to have the `dynamic` field defined, and it is not defined under the `metadata` table, but similar to the other dynamic metadata it injects the additional `build-system.requires`. ```toml [package] name = "mypackage" [tool.scikit-build] build.requires = ["foo"] [[tool.scikit-build.overrides]] if.from-sdist = false build.requires = ["foo @ {root:uri}/foo"] ``` This example shows a common use-case where the package has a default `build-system.requires` pointing to the package `foo` in the PyPI index, but when built from the original git checkout or equivalent, the local folder is used as dependency instead by resolving the `{root:uri}` to a file uri pointing to the folder where the `pyproject.toml` is located. ```{note} In order to be compliant with the package index, when building from `sdist`, the `build.requires` **MUST NOT** have any `@` redirects. This rule may be later enforced explicitly. ``` ```{versionadded} 0.11 ``` ## Generate files with dynamic metadata You can write out metadata to file(s) as well. Other info might become available here in the future, but currently it supports anything available as strings in metadata. (Note that arrays like this are only supported in TOML configuration.) ```toml [[tool.scikit-build.generate]] path = "package/_version.py" template = ''' version = "${version}" ''' ``` `template` or `template-path` is required; this uses {class}`string.Template` formatting. There are three options for output location; `location = "install"` (the default) will go to the wheel, `location = "build"` will go to the CMake build directory, and `location = "source"` will write out to the source directory (be sure to .gitignore this file. It will automatically be added to your SDist includes. It will overwrite existing files). The path is generally relative to the base of the wheel / build dir / source dir, depending on which location you pick. scikit-build-core-0.11.1/docs/configuration/formatted.md000066400000000000000000000013071477275177200232350ustar00rootroot00000000000000# Formattable fields The following configure keys are formatted as Python f-strings: - `build-dir` - `build.requires` The available variables are documented in the members of {py:class}`scikit_build_core.format.PyprojectFormatter` copied here for visibility ```{eval-rst} .. autoattribute:: scikit_build_core.format.PyprojectFormatter.build_type :no-index: .. autoattribute:: scikit_build_core.format.PyprojectFormatter.cache_tag :no-index: .. autoattribute:: scikit_build_core.format.PyprojectFormatter.root :no-index: .. autoattribute:: scikit_build_core.format.PyprojectFormatter.state :no-index: .. autoattribute:: scikit_build_core.format.PyprojectFormatter.wheel_tag :no-index: ``` scikit-build-core-0.11.1/docs/configuration/index.md000066400000000000000000000434661477275177200223730ustar00rootroot00000000000000# Configuration Scikit-build-core supports a powerful unified configuration system. Every option in scikit-build-core can be specified in one of three ways: as a `pyproject.toml` option (preferred if static), as a config-settings options (preferred if dynamic), or as an environment variable. Note that config-settings options can optionally be prefixed with `skbuild.`, for example `-C skbuild.logging.level=INFO`. (verbosity)= ## Verbosity By default, the CMake configuration output is always shown, but it may be hidden behind the build frontend setting, e.g. `pip` requires including `-v` argument in order to display any output. You can increase the verbosity of the build with two settings - `build.verbose` is a shortcut for verbose build output (i.e. `cmake --build ... -v`), and `logging.level` controls scikit-build-core's internal logging. An example (with all configuration styles) of setting both is: ````{tab} pyproject.toml ```toml [tool.scikit-build] build.verbose = true logging.level = "INFO" ``` ```` `````{tab} config-settings ````{tab} pip ```console $ pip install . -v --config-settings=build.verbose=true --config-settings=logging.level=INFO ``` ```` ````{tab} build ```console $ pipx run build --wheel -Cbuild.verbose=true -Clogging.level=INFO ``` ```` ````{tab} cibuildwheel ```toml [tool.cibuildwheel.config-settings] "build.verbose" = true "logging.level" = "INFO" ``` ```` ````` ````{tab} Environment ```yaml SKBUILD_BUILD_VERBOSE: true SKBUILD_LOGGING_LEVEL: "INFO" ``` ```` :::{warning} In general, the environment variable method is intended as an emergency workaround for legacy tooling. ::: :::{versionchanged} 0.10 `cmake.verbose` was renamed to `build.verbose`. ::: ## Minimum version & defaults Scikit-build-core, like CMake, has a special minimum required version setting. If you set this, you get two benefits. First, if the version is less than this version, you get a nice error message. But, more importantly, if scikit-build-core is a newer version than the version set here, it will select older defaults to help ensure your package can continue to build, even if a default value changes in the future. This should help reduce the chance of ever needed an upper cap on the scikit-build-core version, as upper caps are discouraged. It is recommended you set this value as high as you feel comfortable with, and probably keep in sync with your build-system requirements. ```toml [tool.scikit-build] minimum-version = "0.2" ``` In your `pyproject.toml`, you can specify the special string `"build-system.requires"`, which will read the minimum version from your build-system requirements directly; you must specify a minimum there to use this automatic feature. ```toml [build-system] requires = ["scikit-build-core>=0.10"] [tool.scikit-build] minimum-version = "build-system.requires" ``` :::{versionchanged} 0.10 The `"build-system.requires"` option was added. ::: :::{warning} The following behaviors are affected by `minimum-version`: - `minimum-version` 0.5+ (or unset) provides the original name in metadata and properly normalized SDist names. - `minimum-version` 0.5+ (or unset) strips binaries by default. - `minimum-version` 0.8+ (or unset) `cmake.minimum-version` and `ninja.minimum-version` are replaced with `cmake.version` and `ninja.version`. - `minimum-version` 0.10+ (or unset) `cmake.targets` and `cmake.verbose` are replaced with `build.targets` and `build.verbose`. The CMake minimum version will be detected if not given. ::: ## CMake and Ninja minimum versions You can select a different minimum version for CMake and Ninja. Scikit-build-core will automatically decide to download a wheel for these (if possible) when the system version is less than this value. For example, to require a recent CMake and Ninja: ```toml [tool.scikit-build] cmake.version = ">=3.26.1" ninja.version = ">=1.11" ``` You can try to read the version from your CMakeLists.txt with the special string `"CMakeLists.txt"`. This is an error if the minimum version was not statically detectable in the file. If your `minimum-version` setting is unset or set to "0.10" or higher, scikit-build-core will still try to read this if possible, and will fall back on ">=3.15" if it can't read it. You can also enforce ninja to be required even if make is present on Unix: ```toml [tool.scikit-build] ninja.make-fallback = false ``` You can also control the FindPython backport; by default, a backport of CMake 3.26.1's FindPython will be used if the CMake version is less than 3.26.1; you can turn this down if you'd like ("3.15", scikit-build-core's minimum version, would turn it off). ```toml [tool.scikit-build] backport.find-python = "3.15" ``` ```{versionadded} 0.8 These used to be called `cmake.minimum-version` and `ninja.minimum-version`, and only took a single value. Now they are full specifier sets, allowing for more complex version requirements, like `>=3.15,!=3.18.0`. ``` ## Configuring source file inclusion Scikit-build-core defaults to using your `.gitignore` to select what to exclude from the source distribution. You can list files to explicitly include and exclude if you want: ```toml [tool.scikit-build] sdist.include = ["src/some_generated_file.txt"] sdist.exclude = [".github"] ``` By default, scikit-build-core will respect `SOURCE_DATE_EPOCH`, and will lock the modification time to a reproducible value if it's not set. You can disable reproducible builds if you prefer, however: ```toml [tool.scikit-build] sdist.reproducible = false ``` You can also request CMake to run during this step: ```toml [tool.scikit-build] sdist.cmake = true ``` :::{note} If you do this, you'll want to have some artifact from the configure in your source directory; for example: ```cmake include(FetchContent) set(PYBIND11_FINDPYTHON ON) if(NOT SKBUILD_STATE STREQUAL "sdist" AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/pybind11/CMakeLists.txt") message(STATUS "Using integrated pybind11") add_subdirectory(pybind11) else() FetchContent_Declare( pybind11 GIT_REPOSITORY https://github.com/pybind/pybind11.git GIT_TAG v2.12.0 SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/pybind11) FetchContent_MakeAvailable(pybind11) endif() ``` The `/pybind11` directory is in the `.gitignore` and important parts are in `sdist.include`: ```toml [tool.scikit-build] sdist.cmake = true sdist.include = [ "pybind11/tools", "pybind11/include", "pybind11/CMakeLists.txt", ] ``` ::: ## Customizing the built wheel The wheel will automatically look for Python packages at `src/`, `python/`, and ``, in that order. If you want to list packages explicitly, you can. The final path element is the package. ```toml [tool.scikit-build] wheel.packages = ["python/src/mypackage"] ``` This can also be a table, allowing full customization of where a source package maps to a wheel directory. The final components of both paths must match due to the way editable installs work. The equivalent of the above is: ```toml [tool.scikit-build.wheel.packages] mypackage = "python/src/mypackage" ``` But you can also do more complex moves: ```toml [tool.scikit-build.wheel.packages] "mypackage/subpackage" = "python/src/subpackage" ``` :::{versionadded} 0.10 Support for the table form. ::: You can disable Python file inclusion entirely, and rely only on CMake's install mechanism: ```toml [tool.scikit-build] wheel.packages = [] ``` The install directory is normally site-packages; however, you can manually set that to a different directory if you'd like to avoid changing your CMake files. For example, to mimic scikit-build classic: ```toml [tool.scikit-build] wheel.install-dir = "mypackage" ``` :::{warning} You can select a different wheel target directory, as well, but that syntax is experimental; install to `${SKBUILD_DATA_DIR}`, etc. from within CMake instead for now. ::: By default, any `LICEN[CS]E*`, `COPYING*`, `NOTICE*`, or `AUTHORS*` file in the root of the build directory will be picked up. You can specify an exact list of files if you prefer, or if your license file is in a different directory. Globbing patterns are supported. ```toml [tool.scikit-build] wheel.license-files = ["LICENSE"] ``` You can exclude files from the built wheel (on top of the `sdist.exclude` list) as well (not guaranteed to be respected by editable installs): ```toml [tool.scikit-build] wheel.exclude = ["**.pyx"] ``` :::{versionchanged} 0.9 Previously these were matched on the source path, rather than the wheel path, and didn't apply to CMake output. ::: :::{note} There are two more settings that are primarily intended for `overrides` (see below). `wheel.cmake` defaults to `true`, and this enables/disables building with CMake. It also changes the default of `wheel.platlib` unless it's set explicitly; CMake builds assume `wheel.platlib = true`, and CMake-less builds assume `wheel.platlib = false` (purelib targeted instead). ::: ## Customizing the output wheel The python API tags for your wheel will be correct assuming you are building a CPython extension. If you are building a Limited ABI extension, you should set the wheel tags for the version you support: ```toml [tool.scikit-build] wheel.py-api = "cp38" ``` Scikit-build-core will only target ABI3 if the version of Python is equal to or newer than the one you set. `${SKBUILD_SABI_COMPONENT}` is set to `Development.SABIModule` when targeting ABI3, and is an empty string otherwise. If you are not using CPython at all, you can specify any version of Python is fine: ```toml [tool.scikit-build] wheel.py-api = "py3" ``` Or even Python 2 + 3 (you still will need a version of Python scikit-build-core supports to build the initial wheel): ```toml [tool.scikit-build] wheel.py-api = "py2.py3" ``` Some older versions of pip are unable to load standard universal tags; scikit-build-core can expand the macOS universal tags for you for maximum historic compatibility if you'd like: ```toml [tool.scikit-build] wheel.expand-macos-universal-tags = true ``` You can also specify a build tag: ```{conftabs} wheel.build-tag 1 ``` You can select only specific components to install: ```{conftabs} install.components ["python"] ``` And you can turn off binary stripping: ```{conftabs} install.strip False ``` ## Configuring CMake arguments and defines You can select a different build type, such as `Debug`: ```{conftabs} cmake.build-type "Debug" ``` You can specify CMake defines as strings or bools: ````{tab} pyproject.toml ```toml [tool.scikit-build.cmake.define] SOME_DEFINE = "Foo" SOME_OPTION = true ``` ```` You can even specify a CMake define as a list of strings: ````{tab} pyproject.toml ```toml [tool.scikit-build.cmake.define] FOOD_GROUPS = [ "Apple", "Lemon;Lime", "Banana", "Pineapple;Mango", ] ``` ```` Semicolons inside the list elements will be escaped with a backslash (`\`) and the resulting list elements will be joined together with semicolons (`;`) before being converted to command-line arguments. :::{versionchanged} 0.11 Support for list of strings. ::: `````{tab} config-settings ````{tab} pip ```console $ pip install . --config-settings=cmake.define.SOME_DEFINE=ON ``` ```` ````{tab} build ```console $ pipx run build --wheel -Ccmake.define.SOME_DEFINE=ON ``` ```` ````{tab} cibuildwheel ```toml [tool.cibuildwheel.config-settings] "cmake.define.SOME_DEFINE" = "ON" ``` ```` ````` ````{tab} Environment ```yaml SKBUILD_CMAKE_DEFINE: SOME_DEFINE=ON ``` ```` You can also (`pyproject.toml` only) specify a dict, with `env=` to load a define from an environment variable, with optional `default=`. ```toml [tool.scikit-build.cmake.define] SOME_DEFINE = {env="SOME_DEFINE", default="EMPTY"} ``` You can also manually specify the exact CMake args. Beyond the normal `SKBUILD_CMAKE_ARGS`, the `CMAKE_ARGS` space-separated environment variable is also supported (with some filtering for options scikit-build-core doesn't support overriding). ```{conftabs} cmake.args ["-DSOME_DEFINE=ON", "-DOTHER=OFF"] ``` :::{warning} Setting defines through `cmake.args` in `pyproject.toml` is discouraged because this cannot be later altered via command line. Use `cmake.define` instead. ::: You can also specify this using `CMAKE_ARGS`, space separated: ```yaml CMAKE_ARGS: -DSOME_DEFINE=ON -DOTHER=OFF ``` You can also specify only specific targets to build (leaving this off builds the default targets): ```{conftabs} build.targets ["python"] ``` :::{versionchanged} 0.10 `cmake.targets` was renamed to `build.targets`. ::: You can pass raw arguments directly to the build tool, as well: ```{conftabs} build.tool-args ["-j12", "-l13"] ``` ```{versionadded} 0.9.4 ``` ## Editable installs Experimental support for editable installs is provided, with some caveats and configuration. Recommendations: - Use `--no-build-isolation` when doing an editable install is recommended; you should preinstall your dependencies. - Automatic rebuilds do not have the original isolated build dir (pip deletes it), so select a `build-dir` when using editable installs, especially if you also enable automatic rebuilds. - You need to reinstall to pick up new files. Known limitations: - Resources (via `importlib.resources`) are not properly supported (yet). Currently experimentally supported except on Python 3.9 (3.8, 3.10, 3.11, 3.12, and 3.13 work). `importlib_resources` may work on Python 3.9. ```console # Very experimental rebuild on initial import feature $ pip install --no-build-isolation --config-settings=editable.rebuild=true -Cbuild-dir=build -ve. ``` Due to the length of this line already being long, you do not need to set the `experimental` setting to use editable installs, but please consider them experimental and subject to change. You can disable the verbose rebuild output with `editable.verbose=false` if you want. (Also available as the `SKBUILD_EDITABLE_VERBOSE` envvar when importing; this will override if non-empty, and `"0"` will disable verbose output). The default `editable.mode`, `"redirect"`, uses a custom redirecting finder to combine the static CMake install dir with the original source code. Python code added via scikit-build-core's package discovery will be found in the original location, so changes there are picked up on import, regardless of the `editable.rebuild` setting. :::{note} A second experimental mode, `"inplace"`, is also available. This does an in-place CMake build, so all the caveats there apply too -- only one build per source directory, you can't change to an out-of-source builds without removing the build artifacts, your source directory will be littered with build artifacts, etc. Also, to make your binaries importable, you should set `LIBRARY_OUTPUT_DIRECTORY` (include a generator expression, like the empty one `$<0:>` for multi-config generator support, like MSVC, so you don't have to set all possible `*_` variations) to make sure they are placed inside your source directory inside the Python packages; this will be run from the build directory, rather than installed. This will also not support automatic rebuilds. The build directory setting will be ignored if you use this and perform an editable install. You can detect this mode by checking for an in-place build and checking `SKBUILD` being set. With all the caveats, this is very logically simple (one directory) and a near identical replacement for `python setup.py build_ext --inplace`. Some third party tooling might work better with this mode. Scikit-build-core will simply install a `.pth` file that points at your source package(s) and do an inplace CMake build. On the command line, you can pass `-Ceditable.mode=inplace` to enable this mode. ::: ## Messages You can add a message to be printed after a successful or failed build. For example: ```toml [tool.scikit-build] messages.after-sucesss = "{green}Wheel successfully built" messages.after-failure = """ {bold.red}Sorry{normal}, build failed. Your platform is {platform.platform}. """ ``` This will be run through Python's formatter, so escape curly brackets if you need them. Currently, there are several formatter-style keywords available: `sys`, `platform` (parenthesis will be added for items like `platform.platform` for you), `__version__` for scikit-build-core's version, and style keywords. For styles, the colors are `default`, `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, and `white`. These can be accessed as `fg.*` or `bg.*`, without a qualifier the foreground is assumed. Styles like `normal`, `bold`, `italic`, `underline`, `reverse` are also provided. A full clearing of all styles is possible with `reset`. These all can be chained, as well, so `bold.red.bg.blue` is valid, and will produce an optimized escape code. Remember that you need to set the environment variable `FORCE_COLOR` to see colors with pip. ```{versionadded} 0.10 ``` ## Other options You can select a custom build dir; by default scikit-build-core will use a temporary dir. If you select a persistent one, you can get major rebuild speedups. ```{conftabs} build-dir "build/{wheel_tag}" ``` There are several values you can access through Python's formatting syntax. See [](./formatted.md). Scikit-build-core also strictly validates configuration; if you need to disable this, you can: ```toml [tool.scikit-build] strict-config = false ``` Scikit-build-core also occasionally has experimental features. This is applied to features that do not yet carry the same forward compatibility (using minimum-version) guarantee that other scikit-build-core features have. These can only be used if you enable them: ```toml [tool.scikit-build] experimental = true ``` You can also fail the build with `fail = true`. This is useful with overrides if you want to make a specific configuration fail. If this is set, extra dependencies like `"cmake"` will not be requested. ```{versionadded} 0.10 ``` ## Overrides The overrides system allows you to customize for a wide variety of situations. It is described at [](#overrides). ## Full Schema You can see the full schema at [](#schema). scikit-build-core-0.11.1/docs/configuration/overrides.md000066400000000000000000000176561477275177200232700ustar00rootroot00000000000000# Overrides Scikit-build-core has an override system, similar to cibuildwheel and mypy. You specify a `tool.scikit-build.overrides` array with an `if` key. That `if` key can take several values, including several based on [PEP 508][]. Inside the override, you can set any value `tool.scikit-build` supports, and it will override if the `if` condition is true. ## If conditions There are three types of conditions. Booleans, strings, and version numbers. Booleans take a bool; if the boolean matches the bool you give, the override matches. If the value is a string (such as an environment variable), it will match non false-like values, and if the variable is unset or empty, that counts as false. Strings take a regex which will try to match. Version numbers take a specifier set, like `>=1.0`. If multiple conditions are given, they all must be true. Use `if.any` (below) if you would rather matching on any one of multiple conditions being true. At least one must be provided. Then you can specify any collection of valid options, and those will override if all the items in the `if` are true. They will match top to bottom, overriding previous matches. If an override does not match, it's contents are ignored, including invalid options. Combined with the `if.scikit-build-version` override, this allows using overrides to support a range of scikit-build-core versions that added settings you want to use. ### `scikit-build-version` (version) The version of scikit-build-core itself. Takes a specifier set. If this is provided, unknown overrides will not be validated unless it's a match. ### `python-version` (version) The two-digit Python version. Takes a specifier set. Example: ```toml [[tool.scikit-build.overrides]] if.python-version = ">=3.13" wheel.cmake = false ``` ### `platform-system` (string) The value of `sys.platform`. Takes a regex. Like `sys.platform`, you should allow suffixes. Common values: | System | `platform-system` (w/o suffix) | | -------------- | ------------------------------ | | AIX | `aix` | | Android[^1] | `android` | | FreeBSD | `freebsd` | | iOS | `ios` | | Linux | `linux` | | Mac OS X | `darwin` | | OpenBSD | `openbsd` | | Pyodide | `emscripten` | | WASI | `wasi` | | Windows | `win32` | | Windows/Cygwin | `cygwin` | | Windows/MSYS2 | `msys` | [^1]: Before CPython 3.13, this returned `linux`. Example: ```toml [[tool.scikit-build.overrides]] if.platform-system = "^darwin" cmake.version = ">=3.18" ``` ### `platform-machine` (string) The value of `platform.machine()`. Takes a regex. A few sample values: | OS | Machine | `platform-system` | | ------- | ------------ | ----------------- | | Unix | Intel 64-bit | `x86_64` | | Linux | Intel 32-bit | `i686` | | macOS | ARM | `arm64` | | Linux | ARM | `aarch64` | | Linux | Power PC | `ppc64le` | | Linux | s390x | `s390x` | | Windows | Intel 64-bit | `AMD64` | | Windows | Intel 32-bit | `x86` | | Windows | ARM | `ARM64` | ### `abi-flags` (string) A sorted list of the ABI flags. `t` is the free-threaded build. ### `platform-node` (string) The value of `platform.node()`. This is generally your computer's name. Takes a regex. ### `implementation-name` (string) The value of `sys.implementation.name`. Takes a regex. Some common values: | Implementation | `implementation-name` | | -------------- | --------------------- | | CPython | `cpython` | | PyPy | `pypy` | ### `implementation-version` (version) Derived from `sys.implementation.version`, following [PEP 508][]. Takes a specifier set. This is the PyPy version on PyPy, for example. ### `env.*` (string or bool) A table of environment variables mapped to either string regexs, or booleans. Valid "truthy" environment variables are case insensitive `true`, `on`, `yes`, `y`, `t`, or a number more than 0. Example: ```toml [[tool.scikit-build.overrides]] if.env.CI = true cmake.version = ">=3.30" ``` This is often combined with `if.any`. :::{versionadded} 0.7 ::: ### `state` (string) The state of the build, one of `sdist`, `wheel`, `editable`, `metadata_wheel`, and `metadata_editable`. Takes a regex. Note that you can build directly to wheel; you don't have to go through an SDist. :::{versionadded} 0.8 ::: ### `from-sdist` (bool) This will be true if the `PKG-INFO` file exists, that is, if this is coming from an SDist. Takes a bool. :::{versionadded} 0.10 ::: ### `system-cmake` (version) This will match if there's a system CMake matching this version specification. ```toml [[tool.scikit-build.overrides]] if.system-cmake = ">=3.15" cmake.version = "" message.after-success = "Built using a system CMake, not a wheel" ``` :::{versionadded} 0.10 ::: ### `cmake-wheel` (bool) This matches true if a wheel is known to be provided for this platform, and false otherwise. This is useful for specifying a pure Python fallback on systems that don't have provided CMake wheels. Ninja wheels are available on all platforms CMake is, so a separate override for Ninja isn't needed. Often combined with `system-cmake`. For example, this would be an optional build only on systems with CMake or supported by wheels: ```toml [tool.scikit-build] wheel.cmake = false [[tool.scikit-build.overrides]] if.any.system-cmake = ">=3.15" if.any.cmake-wheel = true wheel.cmake = true ``` :::{versionadded} 0.10 ::: ### `failed` (bool) This override is a bit special. If a build fails, scikit-build-core will check to see if there's a matching `failed = true` override. If there is, the the build will be retried once with the new settings. This can be used to build a pure-Python fallback if a build fails, for example: ```toml [[tool.scikit-build.overrides]] if.failed = true wheel.cmake = false ``` :::{versionadded} 0.10 ::: If this override is present in your pyproject.toml file, scikit-build-core will not provide the `prepare_metadata_*` hooks, as it can't know without building if the build will fail. ## Any matching condition If you use `if.any` instead of `if`, then the override is true if any one of the items in it are true. If you have both `if` and `if.any` conditions, then all the `if` conditions and one of the `if.any` conditions must match. Example: ```toml [tool.scikit-build] wheel.cmake = false [[tool.scikit-build.overrides]] if.any.env.CIBUILDWHEEL = true if.any.env.BUILD_MY_LIB = true wheel.cmake = true ``` Above, either `CIBUILDWHEEL` or `BUILD_MY_LIB` being truthy will trigger a binary build. :::{versionadded} 0.7 ::: ## Inheriting for tables and arrays If you specify `inherit. = "append"` or `"prepend"`, then an override will append or prepend tables and lists, either from the base configuration or a previous override. For a table, the difference is apparent when you have matching keys; `"append"` means the override replaces the old key, while `"prepend"` will leave the key alone. Example: ```toml [tool.scikit-build] cmake.define.FOO = "0" cmake.define.BAR = "0" [[tool.scikit-build.overrides]] if.env.SET_FOO = "ON" inherit.cmake.define = "append" cmake.define.FOO = "1" [[tool.scikit-build.overrides]] if.env.SET_BAR = "ON" inherit.cmake.define = "append" cmake.define.BAR = "1" ``` In the above example, setting `SET_FOO` will add `FOO` as a define, and likewise for `SET_BAR` and `BAR`. Without the inherit, setting one would remove the other, as the table would be replaced. And `"prepend"` wouldn't be useful at all, since FOO and BAR are already defined, so the original definition would win. :::{versionadded} 0.9 ::: [pep 508]: https://peps.python.org/pep-0508/#environment-markers scikit-build-core-0.11.1/docs/configuration/search_paths.md000066400000000000000000000102261477275177200237140ustar00rootroot00000000000000# Search paths Scikit-build-core populates CMake search paths to take into account any other CMake project installed in the same environment. In order to take advantage of this the dependent project must populate a `cmake.*` entry-point. ## `_ROOT` This is the recommended interface to be used for importing dependent packages using `find_package`. This variable is populated by the dependent project's entry-point `cmake.root`. To configure the `cmake.root` entry-point to export to other projects, you can use the CMake standard install paths in you `CMakeLists.txt` if you use `wheel.install-dir` option, e.g. ```{code-block} cmake :caption: CMakeLists.txt :emphasize-lines: 14-16 include(CMakePackageConfigHelpers) include(GNUInstallDirs) write_basic_package_version_file( MyProjectConfigVersion.cmake VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion ) configure_package_config_file( cmake/MyProjectConfig.cmake.in MyProjectConfig.cmake INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MyProject ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/MyProjectConfigVersion.cmake ${CMAKE_CURRENT_BINARY_DIR}/MyProjectConfig.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MyProject ) ``` ```{code-block} toml :caption: pyproject.toml :emphasize-lines: 2,5 [tool.scikit-build] wheel.install-dir = "myproject" [project.entry-points."cmake.root"] MyProject = "myproject" ``` With this any consuming project that depends on this would automatically work with `find_package(MyProject)` as long as it is in the `build-system.requires` list. ````{tab} pyproject.toml ```toml [tool.scikit-build.search] ignore_entry_point = ["MyProject"] [tool.scikit-build.search.roots] OtherProject = "/path/to/other_project" ``` ```` `````{tab} config-settings ````{tab} pip ```console $ pip install . -v --config-settings=search.ignore_entry_point="MyProject" --config-settings=search.roots.OtherProject="/path/to/other_project" ``` ```` ````{tab} build ```console $ pipx run build --wheel -Csearch.ignore_entry_point="MyProject" -Csearch.roots.OtherProject="/path/to/other_project" ``` ```` ````{tab} cibuildwheel ```toml [tool.cibuildwheel.config-settings] "search.ignore_entry_point" = ["MyProject"] "search.roots.OtherProject" = "/path/to/other_project" ``` ```` ````` ````{tab} Environment ```yaml SKBUILD_SEARCH_IGNORE_ENTRY_POINT: "MyProject" SKBUILD_SEARCH_ROOTS_OtherProject: "/path/to/other_project" ``` ```` ## `CMAKE_PREFIX_PATH` Another common search path that scikit-build-core populates is the `CMAKE_PREFIX_PATH` which is a common catch-all for all CMake search paths, e.g. `find_package`, `find_program`, `find_path`. This is populated by default with the `site-packages` folder where the project will be installed or the build isolation's `site-packages` folder. This default can be disabled by setting ```toml [tool.scikit-build] search.site-packages = false ``` Additionally, scikit-build-core reads the entry-point `cmake.prefix` of the dependent projects, which is similarly export as ```toml [project.entry-points."cmake.prefix"] MyProject = "myproject" ``` ````{tab} pyproject.toml ```toml [tool.scikit-build.search] ignore_entry_point = ["MyProject"] prefixes = ["/path/to/prefixA", "/path/to/prefixB"] ``` ```` `````{tab} config-settings ````{tab} pip ```console $ pip install . -v --config-settings=search.ignore_entry_point="MyProject" --config-settings=search.prefixes="/path/to/prefixA;/path/to/prefixB" ``` ```` ````{tab} build ```console $ pipx run build --wheel -Csearch.ignore_entry_point="MyProject" -Csearch.prefixes="/path/to/prefixA;/path/to/prefixB" ``` ```` ````{tab} cibuildwheel ```toml [tool.cibuildwheel.config-settings] "search.ignore_entry_point" = ["MyProject"] "search.prefixes" = ["/path/to/prefixA", "/path/to/prefixB"] ``` ```` ````` ````{tab} Environment ```yaml SKBUILD_SEARCH_IGNORE_ENTRY_POINT: "MyProject" SKBUILD_SEARCH_PREFIXES: "/path/to/prefixA;/path/to/prefixB" ``` ```` ## `CMAKE_MODULE_PATH` Scikit-build-core also populates `CMAKE_MODULE_PATH` variable used to search for CMake modules using the `include()` command (if the `.cmake` suffix is omitted). [`CMAKE_PREFIX_PATH`]: #cmake-prefix-path scikit-build-core-0.11.1/docs/data/000077500000000000000000000000001477275177200167675ustar00rootroot00000000000000scikit-build-core-0.11.1/docs/data/projects.toml000066400000000000000000000024541477275177200215220ustar00rootroot00000000000000[[project]] pypi = "cmake" github = "scikit-build/cmake-python-distributions" [[project]] pypi = "ninja" github = "scikit-build/ninja-python-distributions" [[project]] pypi = "pyzmq" github = "zeromq/pyzmq" [[project]] pypi = "lightgbm" github = "microsoft/LightGBM" path = "python-package/pyproject.toml" [[project]] pypi = "phik" github = "kaveio/phik" [[project]] pypi = "clang-format" github = "ssciwr/clang-format-wheel" [[project]] pypi = "llama-cpp-python" github = "abetlen/llama-cpp-python" [[project]] pypi = "coreforecast" github = "Nixtla/coreforecast" [[project]] pypi = "sparse-dot-topn" github = "ing-bank/sparse_dot_topn" [[project]] pypi = "spglib" github = "spglib/spglib" [[project]] pypi = "awkward-cpp" github = "scikit-hep/awkward" path = "awkward-cpp/pyproject.toml" [[project]] pypi = "OpenEXR" github = "AcademySoftwareFoundation/OpenEXR" [[project]] pypi = "iminuit" github = "scikit-hep/iminuit" [[project]] pypi = "boost-histogram" github = "scikit-hep/iminuit" [[project]] pypi = "astyle" github = "Freed-Wu/astyle-wheel" [[project]] pypi = "lammps" github = "njzjz/lammps-wheel" [[project]] pypi = "llamacpp" github = "thomasantony/llamacpp-python" [[project]] pypi = "nodejs-wheel" github = "njzjz/nodejs-wheel" [[project]] pypi = "pygram11" github = "douglasdavis/pygram11" scikit-build-core-0.11.1/docs/examples/000077500000000000000000000000001477275177200176745ustar00rootroot00000000000000scikit-build-core-0.11.1/docs/examples/downstream/000077500000000000000000000000001477275177200220575ustar00rootroot00000000000000scikit-build-core-0.11.1/docs/examples/downstream/main.fmf000066400000000000000000000001311477275177200234700ustar00rootroot00000000000000summary: Downstream example environment: HAS_PYTEST: true require+: - python3-pytest scikit-build-core-0.11.1/docs/examples/downstream/nanobind_example/000077500000000000000000000000001477275177200253625ustar00rootroot00000000000000scikit-build-core-0.11.1/docs/examples/downstream/nanobind_example/CMakeLists.txt000066400000000000000000000045121477275177200301240ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.26) project(nanobind_example LANGUAGES CXX) if(NOT SKBUILD) message( WARNING "\ This CMake file is meant to be executed using 'scikit-build'. Running it directly will almost certainly not produce the desired result. If you are a user trying to install this package, please use the command below, which will install all necessary build dependencies, compile the package in an isolated environment, and then install it. ===================================================================== $ pip install . ===================================================================== If you are a software developer, and this is your own package, then it is usually much more efficient to install the build dependencies in your environment once and use the following command that avoids a costly creation of a new virtual environment at every compilation: ===================================================================== $ pip install nanobind scikit-build-core $ pip install --no-build-isolation -ve . ===================================================================== You may optionally add -Ceditable.rebuild=true to auto-rebuild when the package is imported. Otherwise, you need to re-run the above after editing C++ files.") endif() # Try to import all Python components potentially needed by nanobind find_package( Python 3.8 REQUIRED COMPONENTS Interpreter Development.Module OPTIONAL_COMPONENTS Development.SABIModule) # Import nanobind through CMake's find_package mechanism find_package(nanobind CONFIG REQUIRED) # We are now ready to compile the actual extension module nanobind_add_module( # Name of the extension nanobind_example_ext # Target the stable ABI for Python 3.12+, which reduces the number of binary # wheels that must be built. This does nothing on older Python versions STABLE_ABI # Build libnanobind statically and merge it into the extension (which itself # remains a shared library) # # If your project builds multiple extensions, you can replace this flag by # NB_SHARED to conserve space by reusing a shared libnanobind across libraries NB_STATIC # Source code goes here src/nanobind_example_ext.cpp) # Install directive for scikit-build-core install(TARGETS nanobind_example_ext LIBRARY DESTINATION nanobind_example) scikit-build-core-0.11.1/docs/examples/downstream/nanobind_example/LICENSE000066400000000000000000000027611477275177200263750ustar00rootroot00000000000000Copyright (c) 2022 Wenzel Jakob , All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. scikit-build-core-0.11.1/docs/examples/downstream/nanobind_example/README.md000066400000000000000000000042161477275177200266440ustar00rootroot00000000000000# nanobind_example | CI | status | | ---------- | ------------------------------------------------------------------- | | pip builds | [![Pip Action Status][actions-pip-badge]][actions-pip-link] | | wheels | [![Wheel Action Status][actions-wheels-badge]][actions-wheels-link] | [actions-pip-link]: https://github.com/wjakob/nanobind_example/actions?query=workflow%3APip [actions-pip-badge]: https://github.com/wjakob/nanobind_example/workflows/Pip/badge.svg [actions-wheels-link]: https://github.com/wjakob/nanobind_example/actions?query=workflow%3AWheels [actions-wheels-badge]: https://github.com/wjakob/nanobind_example/workflows/Wheels/badge.svg This repository contains a tiny project showing how to create C++ bindings using [nanobind](https://github.com/wjakob/nanobind) and [scikit-build-core](https://scikit-build-core.readthedocs.io/en/latest/index.html). It was derived from the corresponding _pybind11_ [example project](https://github.com/pybind/scikit_build_example/) developed by [@henryiii](https://github.com/henryiii). ## Installation 1. Clone this repository 2. Run `pip install ./nanobind_example` Afterwards, you should be able to issue the following commands (shown in an interactive Python session): ```pycon >>> import nanobind_example >>> nanobind_example.add(1, 2) 3 ``` ## CI Examples The `.github/workflows` directory contains two continuous integration workflows for GitHub Actions. The first one (`pip`) runs automatically after each commit and ensures that packages can be built successfully and that tests pass. The `wheels` workflow uses [cibuildwheel](https://cibuildwheel.pypa.io) to automatically produce binary wheels for a large variety of platforms. If a `pypi_password` token is provided using GitHub Action's _secrets_ feature, this workflow can even automatically upload packages on PyPI. ## License _nanobind_ and this example repository are both provided under a BSD-style license that can be found in the [LICENSE](./LICENSE) file. By using, distributing, or contributing to this project, you agree to the terms and conditions of this license. scikit-build-core-0.11.1/docs/examples/downstream/nanobind_example/main.fmf000066400000000000000000000004041477275177200267760ustar00rootroot00000000000000summary+: " (nanobind)" require+: # TODO: These don't make sense to be packaged in a different package - python3-nanobind-devel adjust: when: distro < fedora-41 or distro == centos-stream enabled: false because: They have not packaged nanobind there scikit-build-core-0.11.1/docs/examples/downstream/nanobind_example/pyproject.toml000066400000000000000000000021621477275177200302770ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core >=0.4.3", "nanobind >=1.3.2"] build-backend = "scikit_build_core.build" [project] name = "nanobind-example" version = "0.0.1" description = "An example minimal project that compiles bindings using nanobind and scikit-build" readme = "README.md" requires-python = ">=3.8" authors = [ { name = "Wenzel Jakob", email = "wenzel.jakob@epfl.ch" }, ] classifiers = [ "License :: OSI Approved :: BSD License", ] [project.urls] Homepage = "https://github.com/wjakob/nanobind_example" [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" [tool.cibuildwheel] # Necessary to see build output from the actual compilation build-verbosity = 1 # Run pytest to ensure that the package was correctly built test-command = "pytest {project}/tests" test-requires = "pytest" # Needed for full C++17 support [tool.cibuildwheel.macos.environment] MACOSX_DEPLOYMENT_TARGET = "10.14" scikit-build-core-0.11.1/docs/examples/downstream/nanobind_example/src/000077500000000000000000000000001477275177200261515ustar00rootroot00000000000000scikit-build-core-0.11.1/docs/examples/downstream/nanobind_example/src/nanobind_example/000077500000000000000000000000001477275177200314545ustar00rootroot00000000000000scikit-build-core-0.11.1/docs/examples/downstream/nanobind_example/src/nanobind_example/__init__.py000066400000000000000000000000711477275177200335630ustar00rootroot00000000000000from .nanobind_example_ext import add __all__ = ["add"] scikit-build-core-0.11.1/docs/examples/downstream/nanobind_example/src/nanobind_example_ext.cpp000066400000000000000000000003041477275177200330350ustar00rootroot00000000000000#include namespace nb = nanobind; using namespace nb::literals; NB_MODULE(nanobind_example_ext, m) { m.def("add", [](int a, int b) { return a + b; }, "a"_a, "b"_a); } scikit-build-core-0.11.1/docs/examples/downstream/nanobind_example/tests/000077500000000000000000000000001477275177200265245ustar00rootroot00000000000000scikit-build-core-0.11.1/docs/examples/downstream/nanobind_example/tests/test_basic.py000066400000000000000000000001131477275177200312110ustar00rootroot00000000000000import nanobind_example as m def test_add(): assert m.add(1, 2) == 3 scikit-build-core-0.11.1/docs/examples/downstream/pybind11_example/000077500000000000000000000000001477275177200252215ustar00rootroot00000000000000scikit-build-core-0.11.1/docs/examples/downstream/pybind11_example/CMakeLists.txt000066400000000000000000000007521477275177200277650ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.26) project( ${SKBUILD_PROJECT_NAME} VERSION ${SKBUILD_PROJECT_VERSION} LANGUAGES CXX) find_package(Python REQUIRED COMPONENTS Interpreter Development.Module) find_package(pybind11 CONFIG REQUIRED) python_add_library(_core MODULE src/main.cpp WITH_SOABI) target_link_libraries(_core PRIVATE pybind11::headers) target_compile_definitions(_core PRIVATE VERSION_INFO=${PROJECT_VERSION}) install(TARGETS _core DESTINATION scikit_build_example) scikit-build-core-0.11.1/docs/examples/downstream/pybind11_example/LICENSE000066400000000000000000000042061477275177200262300ustar00rootroot00000000000000Copyright (c) 2016 The Pybind Development Team, All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You are under no obligation whatsoever to provide any bug fixes, patches, or upgrades to the features, functionality or performance of the source code ("Enhancements") to anyone; however, if you choose to make your Enhancements available either publicly, or directly to the author of this software, without imposing a separate written license agreement for such Enhancements, then you hereby grant the following license: a non-exclusive, royalty-free perpetual license to install, use, modify, prepare derivative works, incorporate into other computer software, distribute, and sublicense such enhancements or derivative works thereof, in binary and source code form. scikit-build-core-0.11.1/docs/examples/downstream/pybind11_example/README.md000066400000000000000000000037021477275177200265020ustar00rootroot00000000000000# scikit_build_example [![Gitter][gitter-badge]][gitter-link] | CI | status | | ------------ | ------------------------------------------------------------------ | | conda.recipe | [![Conda Actions Status][actions-conda-badge]][actions-conda-link] | | pip builds | [![Pip Actions Status][actions-pip-badge]][actions-pip-link] | An example project built with [pybind11](https://github.com/pybind/pybind11) and scikit-build-core. Python 3.8+ (see older commits for older versions of Python). [gitter-badge]: https://badges.gitter.im/pybind/Lobby.svg [gitter-link]: https://gitter.im/pybind/Lobby [actions-badge]: https://github.com/pybind/scikit_build_example/workflows/Tests/badge.svg [actions-conda-link]: https://github.com/pybind/scikit_build_example/actions?query=workflow%3AConda [actions-conda-badge]: https://github.com/pybind/scikit_build_example/workflows/Conda/badge.svg [actions-pip-link]: https://github.com/pybind/scikit_build_example/actions?query=workflow%3APip [actions-pip-badge]: https://github.com/pybind/scikit_build_example/workflows/Pip/badge.svg [actions-wheels-link]: https://github.com/pybind/scikit_build_example/actions?query=workflow%3AWheels [actions-wheels-badge]: https://github.com/pybind/scikit_build_example/workflows/Wheels/badge.svg ## Installation - clone this repository - `pip install ./scikit_build_example` ## CI Examples There are examples for CI in `.github/workflows`. A simple way to produces binary "wheels" for all platforms is illustrated in the "wheels.yml" file, using [`cibuildwheel`][]. ## License pybind11 is provided under a BSD-style license that can be found in the LICENSE file. By using, distributing, or contributing to this project, you agree to the terms and conditions of this license. ## Test call ```python import scikit_build_example scikit_build_example.add(1, 2) ``` [`cibuildwheel`]: https://cibuildwheel.pypa.io scikit-build-core-0.11.1/docs/examples/downstream/pybind11_example/main.fmf000066400000000000000000000001101477275177200266270ustar00rootroot00000000000000summary+: " (pybind11)" require+: - gcc-c++ - python3dist(pybind11) scikit-build-core-0.11.1/docs/examples/downstream/pybind11_example/pyproject.toml000066400000000000000000000024311477275177200301350ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core>=0.3.3", "pybind11"] build-backend = "scikit_build_core.build" [project] name = "scikit_build_example" version = "0.0.1" description="A minimal example package (with pybind11)" readme = "README.md" authors = [ { name = "My Name", email = "me@email.com" }, ] requires-python = ">=3.8" classifiers = [ "Development Status :: 4 - Beta", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", ] [project.optional-dependencies] test = ["pytest"] [tool.scikit-build] wheel.expand-macos-universal-tags = true [tool.pytest.ini_options] minversion = "6.0" addopts = ["-ra", "--showlocals", "--strict-markers", "--strict-config"] xfail_strict = true filterwarnings = [ "error", "ignore:(ast.Str|Attribute s|ast.NameConstant|ast.Num) is deprecated:DeprecationWarning:_pytest", # Python 3.12 ] testpaths = ["tests"] [tool.cibuildwheel] test-command = "pytest {project}/tests" test-extras = ["test"] test-skip = ["*universal2:arm64"] build-verbosity = 1 scikit-build-core-0.11.1/docs/examples/downstream/pybind11_example/src/000077500000000000000000000000001477275177200260105ustar00rootroot00000000000000scikit-build-core-0.11.1/docs/examples/downstream/pybind11_example/src/main.cpp000066400000000000000000000015431477275177200274430ustar00rootroot00000000000000#include #define STRINGIFY(x) #x #define MACRO_STRINGIFY(x) STRINGIFY(x) int add(int i, int j) { return i + j; } namespace py = pybind11; PYBIND11_MODULE(_core, m) { m.doc() = R"pbdoc( Pybind11 example plugin ----------------------- .. currentmodule:: scikit_build_example .. autosummary:: :toctree: _generate add subtract )pbdoc"; m.def("add", &add, R"pbdoc( Add two numbers Some other explanation about the add function. )pbdoc"); m.def("subtract", [](int i, int j) { return i - j; }, R"pbdoc( Subtract two numbers Some other explanation about the subtract function. )pbdoc"); #ifdef VERSION_INFO m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO); #else m.attr("__version__") = "dev"; #endif } scikit-build-core-0.11.1/docs/examples/downstream/pybind11_example/src/scikit_build_example/000077500000000000000000000000001477275177200321705ustar00rootroot00000000000000__init__.py000066400000000000000000000002241477275177200342200ustar00rootroot00000000000000scikit-build-core-0.11.1/docs/examples/downstream/pybind11_example/src/scikit_build_examplefrom __future__ import annotations from ._core import __doc__, __version__, add, subtract __all__ = ["__doc__", "__version__", "add", "subtract"] scikit-build-core-0.11.1/docs/examples/downstream/pybind11_example/tests/000077500000000000000000000000001477275177200263635ustar00rootroot00000000000000scikit-build-core-0.11.1/docs/examples/downstream/pybind11_example/tests/test_basic.py000066400000000000000000000003411477275177200310530ustar00rootroot00000000000000from __future__ import annotations import scikit_build_example as m def test_version(): assert m.__version__ == "0.0.1" def test_add(): assert m.add(1, 2) == 3 def test_sub(): assert m.subtract(1, 2) == -1 scikit-build-core-0.11.1/docs/examples/getting_started/000077500000000000000000000000001477275177200230635ustar00rootroot00000000000000scikit-build-core-0.11.1/docs/examples/getting_started/abi3/000077500000000000000000000000001477275177200237015ustar00rootroot00000000000000scikit-build-core-0.11.1/docs/examples/getting_started/abi3/CMakeLists.txt000066400000000000000000000004331477275177200264410ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.26) project(${SKBUILD_PROJECT_NAME} LANGUAGES C) find_package( Python COMPONENTS Interpreter Development.SABIModule REQUIRED) python_add_library(example MODULE example.c WITH_SOABI USE_SABI 3.8) install(TARGETS example DESTINATION .) scikit-build-core-0.11.1/docs/examples/getting_started/abi3/example.c000066400000000000000000000013461477275177200255040ustar00rootroot00000000000000#define PY_SSIZE_T_CLEAN #include float square(float x) { return x * x; } static PyObject *square_wrapper(PyObject *self, PyObject *args) { float input, result; if (!PyArg_ParseTuple(args, "f", &input)) { return NULL; } result = square(input); return PyFloat_FromDouble(result); } static PyMethodDef example_methods[] = { {"square", square_wrapper, METH_VARARGS, "Square function"}, {NULL, NULL, 0, NULL}}; static struct PyModuleDef example_module = {PyModuleDef_HEAD_INIT, "example", NULL, -1, example_methods}; /* name here must match extension name, with PyInit_ prefix */ PyMODINIT_FUNC PyInit_example(void) { return PyModule_Create(&example_module); } scikit-build-core-0.11.1/docs/examples/getting_started/abi3/main.fmf000066400000000000000000000000241477275177200253130ustar00rootroot00000000000000summary+: " (Abi3)" scikit-build-core-0.11.1/docs/examples/getting_started/abi3/pyproject.toml000066400000000000000000000002701477275177200266140ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core"] build-backend = "scikit_build_core.build" [project] name = "example" version = "0.0.1" [tool.scikit-build-core] wheel.py-api = "cp38" scikit-build-core-0.11.1/docs/examples/getting_started/c/000077500000000000000000000000001477275177200233055ustar00rootroot00000000000000scikit-build-core-0.11.1/docs/examples/getting_started/c/CMakeLists.txt000066400000000000000000000003761477275177200260530ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.26) project(${SKBUILD_PROJECT_NAME} LANGUAGES C) find_package( Python COMPONENTS Development.Module REQUIRED) python_add_library(example MODULE example.c WITH_SOABI) install(TARGETS example DESTINATION .) scikit-build-core-0.11.1/docs/examples/getting_started/c/example.c000066400000000000000000000013461477275177200251100ustar00rootroot00000000000000#define PY_SSIZE_T_CLEAN #include float square(float x) { return x * x; } static PyObject *square_wrapper(PyObject *self, PyObject *args) { float input, result; if (!PyArg_ParseTuple(args, "f", &input)) { return NULL; } result = square(input); return PyFloat_FromDouble(result); } static PyMethodDef example_methods[] = { {"square", square_wrapper, METH_VARARGS, "Square function"}, {NULL, NULL, 0, NULL}}; static struct PyModuleDef example_module = {PyModuleDef_HEAD_INIT, "example", NULL, -1, example_methods}; /* name here must match extension name, with PyInit_ prefix */ PyMODINIT_FUNC PyInit_example(void) { return PyModule_Create(&example_module); } scikit-build-core-0.11.1/docs/examples/getting_started/c/main.fmf000066400000000000000000000000211477275177200247140ustar00rootroot00000000000000summary+: " (C)" scikit-build-core-0.11.1/docs/examples/getting_started/c/pyproject.toml000066400000000000000000000002101477275177200262120ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core"] build-backend = "scikit_build_core.build" [project] name = "example" version = "0.0.1" scikit-build-core-0.11.1/docs/examples/getting_started/cython/000077500000000000000000000000001477275177200243675ustar00rootroot00000000000000scikit-build-core-0.11.1/docs/examples/getting_started/cython/CMakeLists.txt000066400000000000000000000010621477275177200271260ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.26) project(${SKBUILD_PROJECT_NAME} LANGUAGES C) find_package( Python COMPONENTS Interpreter Development.Module REQUIRED) add_custom_command( OUTPUT example.c COMMENT "Making ${CMAKE_CURRENT_BINARY_DIR}/example.c from ${CMAKE_CURRENT_SOURCE_DIR}/example.pyx" COMMAND Python::Interpreter -m cython "${CMAKE_CURRENT_SOURCE_DIR}/example.pyx" --output-file example.c DEPENDS example.pyx VERBATIM) python_add_library(example MODULE example.c WITH_SOABI) install(TARGETS example DESTINATION .) scikit-build-core-0.11.1/docs/examples/getting_started/cython/example.pyx000066400000000000000000000001021477275177200265550ustar00rootroot00000000000000# cython: language_level=3 def square(float x): return x * x scikit-build-core-0.11.1/docs/examples/getting_started/cython/main.fmf000066400000000000000000000000701477275177200260020ustar00rootroot00000000000000summary+: " (cython)" require+: - python3dist(cython) scikit-build-core-0.11.1/docs/examples/getting_started/cython/pyproject.toml000066400000000000000000000002221477275177200272770ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core", "cython"] build-backend = "scikit_build_core.build" [project] name = "example" version = "0.0.1" scikit-build-core-0.11.1/docs/examples/getting_started/fortran/000077500000000000000000000000001477275177200245365ustar00rootroot00000000000000scikit-build-core-0.11.1/docs/examples/getting_started/fortran/CMakeLists.txt000066400000000000000000000022161477275177200272770ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.17.2...3.29) project(${SKBUILD_PROJECT_NAME} LANGUAGES C Fortran) find_package( Python COMPONENTS Interpreter Development.Module NumPy REQUIRED) # F2PY headers execute_process( COMMAND "${PYTHON_EXECUTABLE}" -c "import numpy.f2py; print(numpy.f2py.get_include())" OUTPUT_VARIABLE F2PY_INCLUDE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) add_library(fortranobject OBJECT "${F2PY_INCLUDE_DIR}/fortranobject.c") target_link_libraries(fortranobject PUBLIC Python::NumPy) target_include_directories(fortranobject PUBLIC "${F2PY_INCLUDE_DIR}") set_property(TARGET fortranobject PROPERTY POSITION_INDEPENDENT_CODE ON) add_custom_command( OUTPUT examplemodule.c example-f2pywrappers.f DEPENDS example.f VERBATIM COMMAND "${Python_EXECUTABLE}" -m numpy.f2py "${CMAKE_CURRENT_SOURCE_DIR}/example.f" -m example --lower) python_add_library( example MODULE "${CMAKE_CURRENT_BINARY_DIR}/examplemodule.c" "${CMAKE_CURRENT_BINARY_DIR}/example-f2pywrappers.f" "${CMAKE_CURRENT_SOURCE_DIR}/example.f" WITH_SOABI) target_link_libraries(example PRIVATE fortranobject) install(TARGETS example DESTINATION .) scikit-build-core-0.11.1/docs/examples/getting_started/fortran/example.f000066400000000000000000000002401477275177200263340ustar00rootroot00000000000000C FILE: EXAMPLE.F SUBROUTINE SQUARE(B, X) REAL B REAL X Cf2py intent(in) x Cf2py intent(out) b B = X * X END C END FILE EXAMPLE.F scikit-build-core-0.11.1/docs/examples/getting_started/fortran/main.fmf000066400000000000000000000001351477275177200261530ustar00rootroot00000000000000summary+: " (F2PY)" require+: - gcc-gfortran - python3dist(numpy) - python3-numpy-f2py scikit-build-core-0.11.1/docs/examples/getting_started/fortran/pyproject.toml000066400000000000000000000003631477275177200274540ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core", "numpy"] build-backend = "scikit_build_core.build" [project] name = "example" version = "0.0.1" dependencies = ["numpy"] [tool.scikit-build] ninja.version = ">=1.10" cmake.version = ">=3.17.2" scikit-build-core-0.11.1/docs/examples/getting_started/main.fmf000066400000000000000000000000311477275177200244730ustar00rootroot00000000000000summary: Getting started scikit-build-core-0.11.1/docs/examples/getting_started/nanobind/000077500000000000000000000000001477275177200246535ustar00rootroot00000000000000scikit-build-core-0.11.1/docs/examples/getting_started/nanobind/CMakeLists.txt000066400000000000000000000004641477275177200274170ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.26) project(${SKBUILD_PROJECT_NAME} LANGUAGES CXX) find_package(Python 3.8 REQUIRED COMPONENTS Interpreter Development.Module) find_package(nanobind CONFIG REQUIRED) nanobind_add_module(example NB_STATIC example.cpp) install(TARGETS example LIBRARY DESTINATION .) scikit-build-core-0.11.1/docs/examples/getting_started/nanobind/example.cpp000066400000000000000000000002331477275177200270100ustar00rootroot00000000000000#include namespace nb = nanobind; float square(float x) { return x * x; } NB_MODULE(example, m) { m.def("square", &square); } scikit-build-core-0.11.1/docs/examples/getting_started/nanobind/main.fmf000066400000000000000000000004041477275177200262670ustar00rootroot00000000000000summary+: " (nanobind)" require+: # TODO: These don't make sense to be packaged in a different package - python3-nanobind-devel adjust: when: distro < fedora-41 or distro == centos-stream enabled: false because: They have not packaged nanobind there scikit-build-core-0.11.1/docs/examples/getting_started/nanobind/pyproject.toml000066400000000000000000000002241477275177200275650ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core", "nanobind"] build-backend = "scikit_build_core.build" [project] name = "example" version = "0.0.1" scikit-build-core-0.11.1/docs/examples/getting_started/pybind11/000077500000000000000000000000001477275177200245125ustar00rootroot00000000000000scikit-build-core-0.11.1/docs/examples/getting_started/pybind11/CMakeLists.txt000066400000000000000000000003711477275177200272530ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.26) project(${SKBUILD_PROJECT_NAME} LANGUAGES CXX) set(PYBIND11_FINDPYTHON ON) find_package(pybind11 CONFIG REQUIRED) pybind11_add_module(example example.cpp) install(TARGETS example LIBRARY DESTINATION .) scikit-build-core-0.11.1/docs/examples/getting_started/pybind11/example.cpp000066400000000000000000000002411477275177200266460ustar00rootroot00000000000000#include namespace py = pybind11; float square(float x) { return x * x; } PYBIND11_MODULE(example, m) { m.def("square", &square); } scikit-build-core-0.11.1/docs/examples/getting_started/pybind11/main.fmf000066400000000000000000000001101477275177200261200ustar00rootroot00000000000000summary+: " (pybind11)" require+: - gcc-c++ - python3dist(pybind11) scikit-build-core-0.11.1/docs/examples/getting_started/pybind11/pyproject.toml000066400000000000000000000002241477275177200274240ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core", "pybind11"] build-backend = "scikit_build_core.build" [project] name = "example" version = "0.0.1" scikit-build-core-0.11.1/docs/examples/getting_started/swig/000077500000000000000000000000001477275177200240345ustar00rootroot00000000000000scikit-build-core-0.11.1/docs/examples/getting_started/swig/CMakeLists.txt000066400000000000000000000012161477275177200265740ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.26) project(${SKBUILD_PROJECT_NAME} LANGUAGES C) find_package( Python COMPONENTS Interpreter Development.Module REQUIRED) find_package( SWIG COMPONENTS python REQUIRED) include(UseSWIG) swig_add_library( example LANGUAGE python OUTPUT_DIR "${SKBUILD_PLATLIB_DIR}" SOURCES example.i example.c) if(WIN32) set_property(TARGET example PROPERTY SUFFIX ".${Python_SOABI}.pyd") else() set_property(TARGET example PROPERTY SUFFIX ".${Python_SOABI}${CMAKE_SHARED_MODULE_SUFFIX}") endif() target_link_libraries(example PRIVATE Python::Module) install(TARGETS example DESTINATION .) scikit-build-core-0.11.1/docs/examples/getting_started/swig/example.c000066400000000000000000000000501477275177200256260ustar00rootroot00000000000000float square(float x) { return x * x; } scikit-build-core-0.11.1/docs/examples/getting_started/swig/example.i000066400000000000000000000002231477275177200256360ustar00rootroot00000000000000%module example %{ /* Put header files here or function declarations like below */ extern float square(float x); %} extern float square(float x); scikit-build-core-0.11.1/docs/examples/getting_started/swig/main.fmf000066400000000000000000000000471477275177200254530ustar00rootroot00000000000000summary+: " (Swig)" require+: - swig scikit-build-core-0.11.1/docs/examples/getting_started/swig/pyproject.toml000066400000000000000000000002201477275177200267420ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core", "swig"] build-backend = "scikit_build_core.build" [project] name = "example" version = "0.0.1" scikit-build-core-0.11.1/docs/examples/getting_started/test.py000066400000000000000000000001261477275177200244130ustar00rootroot00000000000000import example assert int(example.square(2)) == 4 assert int(example.square(3)) == 9 scikit-build-core-0.11.1/docs/examples/main.fmf000066400000000000000000000002311477275177200213060ustar00rootroot00000000000000tag: examples require: - python3-devel - python3-pip - cmake - gcc - rsync - tree framework: beakerlib path: / test: ./docs/examples/test.sh scikit-build-core-0.11.1/docs/examples/test.sh000077500000000000000000000022671477275177200212210ustar00rootroot00000000000000#!/bin/bash # vim: dict+=/usr/share/beakerlib/dictionary.vim cpt=.,w,b,u,t,i,k # shellcheck disable=all source /usr/share/beakerlib/beakerlib.sh || exit 1 rlJournalStart rlPhaseStartSetup rlRun "tmp=\$(mktemp -d)" 0 "Create tmp directory" rlRun "rsync -r .$TMT_TEST_NAME/ $tmp" 0 "Copy example project" if [ "${HAS_PYTEST}" != True ]; then rlRun "rsync -r ./docs/examples/getting_started/test.py $tmp" 0 "Copy test.py file" fi rlRun "pushd $tmp" rlRun "tree" 0 "Show directory tree" rlRun "python3 -m venv .venv --system-site-packages" 0 "Create venv with system packages" rlRun "source .venv/bin/activate" 0 "Activate venv" rlRun "set -o pipefail" rlPhaseEnd rlPhaseStartTest rlRun "pip install . -v --no-index --no-build-isolation" 0 "Build the python project" if [ "${HAS_PYTEST}" == True ]; then rlRun "python3 -m pytest" 0 "Run built-in pytest" else rlRun "python3 test.py" 0 "Test project is installed correctly" fi rlPhaseEnd rlPhaseStartCleanup rlRun "popd" rlRun "rm -r $tmp" 0 "Remove tmp directory" rlPhaseEnd rlJournalEnd scikit-build-core-0.11.1/docs/ext/000077500000000000000000000000001477275177200166565ustar00rootroot00000000000000scikit-build-core-0.11.1/docs/ext/conftabs.py000066400000000000000000000036461477275177200210400ustar00rootroot00000000000000from __future__ import annotations import ast import textwrap from typing import Any from docutils import nodes from docutils.statemachine import StringList from sphinx.util.docutils import SphinxDirective class ConfTabs(SphinxDirective): required_arguments = 2 final_argument_whitespace = True def run(self) -> list[nodes.Node]: name, result = self.arguments env_name = f"SKBUILD_{name.replace('.', '_').replace('-', '_').upper()}" value_result = ast.literal_eval(result) if isinstance(value_result, list): joined_result = ";".join(value_result) elif isinstance(value_result, bool): result = joined_result = "true" if value_result else "false" else: joined_result = result pyproject = textwrap.dedent( f"""\ ````{{tab}} pyproject.toml ```toml [tool.scikit-build] {name} = {result} ``` ```` `````{{tab}} config-settings ````{{tab}} pip ```console $ pip install . --config-settings={name}={joined_result} ``` ```` ````{{tab}} build ```console $ pipx run build --wheel -C{name}={joined_result} ``` ```` ````{{tab}} cibuildwheel ```toml [tool.cibuildwheel.config-settings] "{name}" = {result} ``` ```` ````` ````{{tab}} Environment ```yaml {env_name}: "{joined_result}" ``` ```` """ ) content = nodes.container("") self.state.nested_parse( StringList(pyproject.splitlines()), self.content_offset, content ) return [content] def setup(app: Any) -> dict[str, Any]: app.add_directive("conftabs", ConfTabs) return { "version": "0.1", "parallel_read_safe": True, "parallel_write_safe": True, } scikit-build-core-0.11.1/docs/guide/000077500000000000000000000000001477275177200171535ustar00rootroot00000000000000scikit-build-core-0.11.1/docs/guide/build.md000066400000000000000000000240711477275177200206000ustar00rootroot00000000000000# Build procedure ## Quickstart For any backend, you can make a SDist and then build a wheel from it with one command (choose your favorite way to run apps): ````{tab} pipx ```bash pipx run build ``` ```` ````{tab} uv ```bash uv build ``` ```` ````{tab} pip ```bash pip install build python -m build ``` ```` You can then check the file contents: ```bash tar -tf dist/*.tar.gz unzip -l dist/*.whl ``` The SDist should contain a copy of the repo with all the files you'll need (CI files and such are not required). And the wheel should look like the installed project with a few helper files. You can inspect any SDist or wheel on PyPI at . ## In-depth Modern Python build procedure is as follows: ### SDist The SDist is a tarfile with all the code required to build the project, along with a little bit of metadata. To build an SDist, you use the `build` tool with the `--sdist` flag. For example, `pipx run build --sdist`. This: 1. Reads `pyproject.toml` to get the `build-system` table. 2. Set up a new isolated environment with the packages listed in `build-system.requires`.. 3. Run `.get_requires_for_build_sdist(...)` inside the module listed in `build-system.build-backend`, if it exists. If this returns a list, install all the packages requested. This allows a backend to dynamically declare dependencies. 4. Run `.build_sdist(...)` inside the module listed in `build-system.build-backend`. The backend produces an SDist file and returns the filename. Details of the arguments are skipped above, but they allow arbitrary settings (called config-settings) to be passed to all the hook functions and handle directories. If you turn off isolated environment building (`--no-isolation` in `build`), then steps 2 and 3 are skipped. Note that pip cannot build SDists. Without build isolation, you can build an SDist manually with `python -c "from scikit_build_core.build import build_sdist; build_sdist('dist')"`. This will produce an SDist in the `dist` directory. For any other backend, substitute the backend above. #### File structure in the SDist Since you can build a wheel from the source or from the SDist, the structure should be identical to the source, though some files (like CI files) may be omitted. Files from git submodules should be included. It is best if the SDist can be installed without internet connection, but that's not always the case. There also is a `PKG-INFO` file with metadata in SDists. ### Wheel The wheel is a zip file (ending in `.whl`) with the built code of the project, along with required metadata. There is no code that executes on install; it is a simple unpack with a few rules about directories. Wheels do not contain `pyproject.toml` or other configuration files. To build an wheel, you use the `build` tool with the `--wheel` flag. For example, `pipx run build --wheel`. This: 1. Reads `pyproject.toml` to get the `build-system` table. 2. Set up a new isolated environment with the packages listed in `build-system.requires`.. 3. Run `.get_requires_for_build_wheel(...)` inside the module listed in `build-system.build-backend`, if it exists. If this returns a list, install all the packages requested. This allows a backend to dynamically declare dependencies. 4. Run `.build_wheel(...)` inside the module listed in `build-system.build-backend`. The backend produces an wheel file and returns the filename. Details of the arguments are skipped above, but they allow arbitrary settings (called config-settings) to be passed to all the hook functions and handle directories. If you turn off isolated environment building (`--no-build-isolation` in `pip` or `--no-isolation` in `build`), then steps 2 and 3 are skipped. :::{note} If you run build without arguments, it will build an SDist first, then will build a wheel from the SDist. This will error if you do not have a valid SDist. If you pass `--sdist --wheel`, it will build both directly from the source instead. ::: There are a few other hooks as well; one to allow metadata to be produced without building a wheel, and editable versions of the wheel build. Editable "wheels" are temporary wheels that are only produced to immediately install and discard, and are expected to provide mechanisms to link back to the source code. #### File structure in the wheel The basic structure of the wheel is what will be extracted to site-packages. This means most of the files are usually in `/...`, though if a top-level extension is present, then that could be something like `..so`. There's also a `-.dist-info/` directory with various metadata files in it (`METADATA`, `WHEEL`, and `RECORD`), along with license files. There are a few other metadata files that could be here too, like `entry_points.txt`. There are also several directories that installers can extract to different locations, namely: - `.data/scripts`: Goes to the `/bin` or `/Scripts` directory in the environment. Any file starting with `#!python` will get the correct path injected by the installer. Most build-backends (like setuptools and scikit-build-core) will convert normal Python shabang lines like `#!/usr/bin/env python` into `#!python` for you. Though if you are writing Python and placing them here, it's usually better to use entry points and let the installer generate the entire file. - `.data/headers`: Goes to the include directory for the current version of Python in the environment. - `.data/data`: Goes to the root of the environment. Note that if a user is not in a virtual environment, these folders install directly to the Python install's location, which could be `/` or `/usr`! In general, it's best to put data inside the package's folder in site-packages and then use `importlib.resources` to access it. ### Installing Installing simply unpacks a wheel into the target filesystem. No code is run, no configuration files are present. If pip tries to install a repo or an SDist, it will first build a wheel[^1] as shown above, then install that. `installer` is a standalone tool that is designed entirely to install wheels. If you want to run code on install, you either have to use an SDist, or depend on a package that is SDist only. However, this is quite rarely required. There are several directories supported, at least. Besides unpacking to the site-packages directory, wheels can also have folders that get unpacked to the root of the environment and the Python header locations. But these are generally discouraged, with including files in the package's site-package directory and using `importlib.resources` to access them is preferred. If someone is not working in a virtual environment, having items installed to `/` or `/usr/local` for example might be surprising! ## Binary wheels and distributing A wheel filename has several components: ``` scikit_build_core-0.1.2-py3-none-any.whl |_______________| |___| |_| |__| |_| | | | | \ name version | | platform python | abi ``` The three new items here (compared to SDists) are the [compatibility tags][]: - `python tag`: The first version of Python the wheel is compatible with. Often `py3` for pure Python wheels, or `py312` (etc) for compiled wheels. - `abi tag`: The interpreter ABI this was built for. `none` for pure Python wheels or compiled wheels that don't use the Python API, `abi3` for stable ABI / limited API wheels, and `cp312` (etc) for normal compiled wheels. - `platform tag`: This is the platform the wheel is valid on, such as `any`, `linux_x86_64`, or `manylinux_2_17_x86_64`. (repairing-wheels)= ## Repairing The wheels produced by default are not designed to be redistributable. Making them redistributable depends on platform: - Linux: The `linux_*` tags cannot be uploaded to PyPI. You have to build the wheels in a restricted environment (like the manylinux images) and run the wheels through `auditwheel` to produce redistributable wheels. This will verify you are only using the correct GLibC and restricted set of system libraries, and will bundle external libraries into the wheel with mangled symbols to avoid conflicts. These will have a `manylinux_*` or `musllinux_*` tag, and can be uploaded to PyPI. - macOS: The wheels should be build with the official CPython releases, and target a reasonable `MACOSX_DEPLOYMENT_TARGET` value (10.9 or newer). You should run the wheels through `develocate` to bundle external dependencies. You'll also want to (carefully) cross compile for Apple Silicon or build on Apple Silicon runners (`macos-14`+ on GHA). - Windows: this is the easiest, usually, as the wheels don't have special rules on what Python or OS is being used. However, if you want to bundle dependencies, you'll need `delvewheel`, which is a bit younger than the other two packages, and has to do a few more intrusive workarounds, but otherwise works like those packages. The easiest way to handle all the above for all Python versions, OSs, architectures, including testing, is to use [cibuildwheel][]. There's also a fairly new tool, [repairwheel][], that combines all these tools. Tools usually allow extra flags that can be used for trickier repairs, like ignoring CUDA libraries when bundling (which technically is not a true manylinux wheel, but is the current workaround). [^1]: This is the modern build mechanism. If no `pyproject.toml` is present, pip/build will trigger a legacy build/install that either pretends a basic `pyproject.toml` is present (build) or using legacy `setup.py ...` commands (pip). If **both** `pyproject.toml` is not provide and `wheel` is not present, `pip` will even fall back on using `setup.py install` instead of `setup.py bdist_wheel`! You can avoid this whole mess with scikit-build-core. [repairwheel]: https://github.com/jvolkman/repairwheel [cibuildwheel]: https://cibuildwheel.pypa.io [compatibility tags]: https://packaging.python.org/en/latest/specifications/binary-distribution-format scikit-build-core-0.11.1/docs/guide/cmakelists.md000066400000000000000000000150111477275177200216320ustar00rootroot00000000000000# Authoring your CMakeLists Scikit-build-core provides a variety of useful variables for your CMakeLists. ## Detecting Scikit-build-core You can write CMakeLists that support running inside and outside scikit-build-core using the `${SKBUILD}` variable. This will be defined to "2" for scikit-build-core (and "1" for classic scikit-build). You can also detect the version of scikit-build-core with `${SKBUILD_CORE_VERSION}`. ## Accessing information Scikit-build-core provides several useful variables: - `${SKBUILD_PROJECT_NAME}`: The name of the project. - `${SKBUILD_PROJECT_VERSION}`: The version of the project in a form CMake can use. - `${SKBUILD_PROJECT_VERSION_FULL}`: The exact version of the project including dev & local suffix. - `${SKBUILD_STATE}`: The run state, one of `sdist`, `wheel`, `metadata_wheel`, `editable`, or `metadata_editable`. ## Finding Python You can directly use FindPython: ```cmake find_package(Python COMPONENTS Interpreter Development.Module REQUIRED) ``` You always want to find at least `Interpreter` and the `Module` component of the `Development` package. You do not want to find the entire `Development` package, as that include `Embed` component, which is not always present and is not related to making Python extension modules. If you are making a Limited API / Stable ABI package, you'll need the `Development.SABIModule` component instead (CMake 3.26+). You can use the `SKBUILD_SABI_COMPONENT` variable to check to see if it was requested. You can get the version requested with `${SKBUILD_SABI_VERSION}`. :::{warning} :name: soabi If you want to cross-compile to Windows ARM, you'll need to use `${SKBUILD_SOABI}`, which is always correct, instead of trusting FindPython's `Python_SOABI` value. You can manually set the extension suffix after making a target: ```cmake if(CMAKE_SYSTEM_NAME STREQUAL "Windows") set_property (TARGET ${name} PROPERTY SUFFIX ".${SKBUILD_SOABI}.pyd") else() set_property (TARGET ${name} PROPERTY SUFFIX ".${SKBUILD_SOABI}${CMAKE_SHARED_MODULE_SUFFIX}") endif() ``` A quicker way to do this would be to instead override `Python_SOABI` after `find_package(Python)`: ```cmake set(Python_SOABI ${SKBUILD_SOABI}) ``` However, this isn't officially supported upstream, and only works due to the way this variable is used when creating targets. ::: If you want to use the old, deprecated FindPythonInterp and FindPythonLibs instead, you can. Though it should be noted that FindPythonLibs requires a trick to make it work properly if a Python library is not preset (like in manylinux): you have to set `PYTHON_LIBRARY` to something (doesn't matter what) to make it succeed. ## Finding other packages Scikit-build-core includes various pythonic paths to the CMake search paths by default so that usually you only need to include the dependent project inside the `build-system.requires` section. Note that `cmake` and `ninja` should not be included in that section. See [search paths section](../configuration/search_paths.md) for more details on how the search paths are constructed. ## Install directories Scikit-build-core will install directly into platlib, which will end up in site-packages. If you are used to scikit-build, you might find targeting `/` to be more natural. You can mimic the old behavior with a configuration option (`wheel.install-dir`). However, scikit-build-core is more powerful, and allows you to install multiple packages or top-level extension modules if you need to. You can access all of the possible output directories, regardless of configuration, with the variables: - `${SKBUILD_PLATLIB_DIR}`: The original platlib directory. Anything here goes directly to site-packages when a wheel is installed. - `${SKBUILD_DATA_DIR}`: The data directory. Anything here goes to the root of the environment when a wheel is installed (use with care). - `${SKBUILD_HEADERS_DIR}`: The header directory. Anything in here gets installed to Python's header directory. - `${SKBUILD_SCRIPTS_DIR}`: The scripts directory. Anything placed in here will go to `bin` (Unix) or `Scripts` (Windows). - `${SKBUILD_METADATA_DIR}`: The dist-info directory. Licenses go in the `licenses` subdirectory. _Note that CMake is not run in the `prepare_metadata_\*` hooks, so anything written to this directory will only be present when writing wheels.\_ - `${SKBUILD_NULL_DIR}`: Anything installed here will not be placed in the wheel. ## Limited API / Stable ABI You can activate the Stable ABI by setting `tool.scikit-build.wheel.py-api` equal to a valid CPython [Python Tag](https://packaging.python.org/en/latest/specifications/platform-compatibility-tags/#python-tag) in your `pyproject.toml`: ```toml [tool.scikit-build] wheel.py-api = "cp38" ``` When you do that, `${SKBUILD_SABI_COMPONENT}` will be set to `Development.SABIModule` if you can target this (new enough CPython), and will remain an empty string otherwise (PyPy). This allows the following idiom: ```cmake find_package(Python REQUIRED COMPONENTS Interpreter Development.Module ${SKBUILD_SABI_COMPONENT}) ``` This will require the `Development.SABIModule` component only if scikit-build-core is driving the compilation and is targeting ABI3. If you want to support Stable ABI from outside scikit-build-core, look into the `OPTIONAL_COMPONENTS` flag for `find_package`. When defining your module, if you only support the Stable ABI after some point, you should use (for example for 3.11): ```cmake if(NOT "${SKBUILD_SABI_VERSION}" STREQUAL "") python_add_library(some_ext MODULE WITH_SOABI USE_SABI ${SKBUILD_SABI_VERSION} ...) else() python_add_library(some_ext MODULE WITH_SOABI ...) endif() ``` If you have a lot of libraries, you can conditionally save these two items into a variable with `set(USE_SABI USE_SABI ${SKBUILD_SABI_VERSION})` and use it in all your `python_add_library` calls: ``` if(NOT "${SKBUILD_SABI_VERSION}" STREQUAL "") set(USE_SABI "USE_SABI ${SKBUILD_SABI_VERSION}") endif() python_add_library(some_ext MODULE WITH_SOABI ${USE_SABI} ...) ``` This will define `Py_LIMITED_API` for you. If you want to support building directly from CMake, you need to protect this for Python version, `Python_INTERPRETER_ID STREQUAL Python`, and free-threading Python 3.13+ doesn't support ABI3 either. If you are using `nanobind`'s `nanobind_add_module`, the `STABLE_ABI` flag does this automatically for you for 3.12+. ## Future additions Scikit-build-core does not include helpers for F2Py or Cython like scikit-build classic yet. These will be carefully reimagined soon. scikit-build-core-0.11.1/docs/guide/crosscompile.md000066400000000000000000000046001477275177200221770ustar00rootroot00000000000000# Cross-compiling ## macOS Unlike the other platforms, macOS has the ability to target older operating systems with the `MACOSX_DEPLOYMENT_TARGET` variable. If that is not set, you will get a wheel optimized for the current operating system. Popular redistributable builders like cibuildwheel will set this for you. :::{warning} While CMake also allows you to specify this a few other ways, scikit-build-core will not know you've set this and won't get the correct wheel name. ::: ### Intel to AppleSilicon On macOS, AppleClang has excellent support for making Apple Silicon and Universal2 binaries (both architectures in one binary). Scikit-build-core respects `ARCHFLAGS` if `CMAKE_SYSTEM_PROCESSOR` is not in the cmake args. These values are set by most redistributable builders like cibuildwheel when cross-compiling. :::{warning} If you link to any binaries, they need to be Universal2, so that you get the Apple Silicon component. This means you cannot use homebrew binaries (which are always native, and not designed to be used for building portable binaries anyway). Header-only dependencies, including NumPy, do not need to be Universal2. ::: :::{warning} If you manually set the arch flags in other ways besides `ARCHFLAGS`, or the one special case above, scikit-build-core will not get the right wheel name. ::: ## Windows ### Intel to ARM Scikit-build-core respects setuptools-style `DIST_EXTRA_CONFIG`. If is set to a file, then scikit-build-core reads the `build_ext.library_dirs` paths to find the library to link to. You will also need to set `SETUPTOOLS_EXT_SUFFIX` to the correct suffix. These values are set by cibuildwheel when cross-compiling. ## Linux It should be possible to cross-compile to Linux, but due to the challenges of getting the manylinux RHEL devtoolkit compilers, this is currently a TODO. See `py-build-cmake `\_ for an alternative package's usage of toolchain files. ### Intel to Emscripten (Pyodide) When using pyodide-build, Python is set up to report the cross-compiling values by setting `_PYTHON_SYSCONFIGDATA_NAME`. This causes values like `SOABI` and `EXT_SUFFIX` to be reported by `sysconfig` as the cross-compiling values. This is unfortunately incorrectly stripped from the cmake wrapper pyodide uses, so FindPython will report the wrong values, but pyodide-build will rename the .so's afterwards. scikit-build-core-0.11.1/docs/guide/faqs.md000066400000000000000000000120371477275177200204320ustar00rootroot00000000000000# FAQs This section covers common needs. ## Starting a new project The easiest way to get started is to use the [Scientific Python cookie][], which makes a new project following the [Scientific Python Development Guidelines][]. Scikit-build-core is one of the backends you can select. The project will have a lot of tooling prepared for you as well, including pre-commit checks and a noxfile; be sure to read the guidelines to see what is there and how it works. Another option is the [pybind11 example][]. In the future, a CLI interface with a new project generator is planned. ## Multithreaded builds For most generators, you can control the parallelization via a CMake define: ```bash pip install -Ccmake.define.CMAKE_BUILD_PARALLEL_LEVEL=8 . ``` or an environment variable: ```bash CMAKE_BUILD_PARALLEL_LEVEL=8 pip install . ``` The default generator on Unix-like platforms is Ninja, which automatically tries to run in parallel with the number of cores on your machine. ## Dynamic setup.py options While we will eventually have some dynamic options, most common needs can be moved into your `CMakeLists.txt`. For example, if you had a custom `setup.py` option (which setuptools has deprecated as well), you can make it a CMake option and then pass it with `-Ccmake.define.=`. If you need to customize configuration options, try `[[tool.scikit-build.overrides]]`. If that is missing some value you need, please open an issue and let us know. ## Finding Python One common mistake when using FindPython is to forget to only request the `Development.Module` component. If you request `Development`, you will also require the `Development.Embed` component, which will require the Python libraries to be found for linking. When building a module on Unix, you do not link to Python - the Python symbols are already loaded in the interpreter. What's more, the manylinux image (which is used to make redistributable Linux wheels) does not have the Python libraries, both to avoid this mistake, and to reduce size. ## Cross compiling When cross compiling, FindPython may not get the correct SOABI extension. Scikit-build-core does know the correct extension, however, and sets it as `SKBUILD_SOABI`. See [the SOABI docs](#soabi). ## Things to try If you want to debug a scikit-build-core build, you have several options. If you are using `pip`, make sure you are passing the `-v` flag, otherwise `pip` suppresses all output. You can [increase scikit-build-core's logging verbosity](#verbosity). You can also get a printout of the current settings using: ```bash python -m scikit_build_core.builder ``` ## Repairing wheels Like most other backends[^1], scikit-build-core produced `linux` wheels, which are not redistrubutable cannot be uploaded to PyPI[^2]. You have to run your wheels through `auditwheel` to make `manylinux` wheels. `cibuildwheel` automatically does this for you. See [repairing](#repairing-wheels). ## Making a Conda recipe `scikit-build-core` is available on conda-forge, and is used in [dozens of recipes][]. There are a few things to keep in mind. You need to recreate your `build-system.requires` in the `host` table, with the conda versions of your dependencies. You also need to add `cmake` and either `make` or `ninja` to your `build:` table. Conda-build hard-codes `CMAKE_GENERATOR="Unix Makefiles` on UNIX systems, so you have to set or unset this to use Ninja if you prefer Ninja. The `scikit-build-core` recipe cannot depend on `cmake`, `make`, or `ninja`, because that would add those to the wrong table (`host` instead of `build`). Here's an example: ```yaml build: script: - {{ PYTHON }} -m pip install . -vv requirements: build: - python # [build_platform != target_platform] - cross-python_{{ target_platform }} # [build_platform != target_platform] - {{ compiler('c') }} - {{ stdlib('c') }} - {{ compiler('cxx') }} - cmake >=3.15 - make # [not win] host: - python - pip - scikit-build-core >=0.2.0 run: - python ``` ## Supporting free-threaded builds on Windows Windows currently requires a little extra care. You should set the C define `Py_GIL_DISABLED` on Windows; due to the way the two builds share the same config files, Python cannot set it for you on the free-threaded variant. [^1]: Due to a [bug in packaging](https://github.com/pypa/packaging/issues/160), some backends may mistakenly produce the wrong tags (including scikit-build-core < 0.9), but the wheels are not actually manylinux/musllinux, just mistagged. [^2]: Platforms like ARMv6 that do not have a manylinux spec are exempt from this rule. [scientific python cookie]: https://github.com/scientific-python/cookie [scientific python development guidelines]: https://learn.scientific-python.org/development [pybind11 example]: https://github.com/pybind/scikit_build_example [dozens of recipes]: https://github.com/search?type=code&q=org%3Aconda-forge+path%3Arecipe%2Fmeta.yaml+scikit-build-core scikit-build-core-0.11.1/docs/guide/getting_started.md000066400000000000000000000267171477275177200227010ustar00rootroot00000000000000# Getting started If you've never made a Python package before, [packaging.python.org's tutorial][] is a great place to start. It walks you through creating a simple package in pure Python using modern tooling and configuration. Another great resource is the [Scientific Python Developer Guide][]. And a tutorial can be found at [INTERSECT Training: Packaging][]. ## Quick start There are several mechanisms to quickly get started with a package: - [uv][] has built-in support for scikit-build-core. Just make a directory for your package and run: `uv init --lib --build-backend=scikit`. - [scientific-python/cookie][] has a cookiecutter/copier template for making a package with all the suggestions in the [Scientific Python Developer Guide][]. - For pybind11, there's a example template at [pybind11/scikit_build_example][]. For nanobind, [nanobind example][] includes the Stable ABI on Python 3.12+! - There are several examples including scikit-build-core examples (including free-threading) at [scikit-build-sample-projects][]. ## Writing an extension We will be writing these files: ````{tab} pybind11 ``` example-project ├── example.cpp ├── pyproject.toml └── CMakeLists.txt ``` ```` ````{tab} nanobind ``` example-project ├── example.cpp ├── pyproject.toml └── CMakeLists.txt ``` ```` ````{tab} SWIG ``` example-project ├── example.c ├── example.i ├── pyproject.toml └── CMakeLists.txt ``` ```` ````{tab} Cython ``` example-project ├── example.pyx ├── pyproject.toml └── CMakeLists.txt ``` ```` ````{tab} C ``` example-project ├── example.c ├── pyproject.toml └── CMakeLists.txt ``` ```` ````{tab} ABI3 ``` example-project ├── example.c ├── pyproject.toml └── CMakeLists.txt ``` ```` ````{tab} Fortran ``` example-project ├── example.f ├── pyproject.toml └── CMakeLists.txt ``` ```` ### Source code For this tutorial, you can either write a C extension yourself, or you can use pybind11 and C++. Select your preferred version using the tabs - compare them! ````{tab} pybind11 ```{literalinclude} ../examples/getting_started/pybind11/example.cpp :language: cpp ``` ```` ````{tab} nanobind ```{literalinclude} ../examples/getting_started/nanobind/example.cpp :language: cpp ``` ```` ````{tab} SWIG ```{literalinclude} ../examples/getting_started/swig/example.c :language: c ``` ```{literalinclude} ../examples/getting_started/swig/example.i :language: swig ``` ```` ````{tab} Cython ```{literalinclude} ../examples/getting_started/cython/example.pyx :language: cython ``` ```` ````{tab} C ```{literalinclude} ../examples/getting_started/c/example.c :language: c ``` ```` ````{tab} ABI3 ```{literalinclude} ../examples/getting_started/abi3/example.c :language: c ``` ```` ````{tab} Fortran ```{literalinclude} ../examples/getting_started/fortran/example.f :language: fortran ``` ```` ### Python package configuration To create your first compiled package, start with a pyproject.toml like this: ````{tab} pybind11 ```{literalinclude} ../examples/getting_started/pybind11/pyproject.toml :language: toml ``` ```` ````{tab} nanobind ```{literalinclude} ../examples/getting_started/nanobind/pyproject.toml :language: toml ``` ```` ````{tab} SWIG ```{literalinclude} ../examples/getting_started/swig/pyproject.toml :language: toml ``` ```` ````{tab} Cython ```{literalinclude} ../examples/getting_started/cython/pyproject.toml :language: toml ``` ```` ````{tab} C ```{literalinclude} ../examples/getting_started/c/pyproject.toml :language: toml ``` ```` ````{tab} ABI3 ```{literalinclude} ../examples/getting_started/abi3/pyproject.toml :language: toml ``` ```` ````{tab} Fortran ```{literalinclude} ../examples/getting_started/fortran/pyproject.toml :language: toml ``` ```{warning} The module you build will require an equal or newer version to the version of NumPy it built with. You should use `oldest-supported-numpy` or manually set the NumPy version, though you will then be stuck with older versions of f2py. Also it's hard to compile Fortran on Windows as it's not supported by MSVC and macOS as it's not supported by Clang. ``` ```` Notice that you _do not_ include `cmake`, `ninja`, `setuptools`, or `wheel` in the requires list. Scikit-build-core will intelligently decide whether it needs `cmake` and/or `ninja` based on what versions are present in the environment - some environments can't install the Python versions of CMake and Ninja, like Android, FreeBSD, WebAssembly, and ClearLinux, but they may already have these tools installed. Setuptools is not used by scikit-build-core's native builder, and wheel should never be in this list. There are other keys you should include under `[project]` if you plan to publish a package, but this is enough to start for now. The [project metadata specification](https://packaging.python.org/en/latest/specifications/pyproject-toml) page covers what keys are available. Another example is available at [the Scientific Python Library Development Guide](https://learn.scientific-python.org/development/guides). ### CMake file Now, you'll need a file called `CMakeLists.txt`. This one will do: ````{tab} pybind11 ```{literalinclude} ../examples/getting_started/pybind11/CMakeLists.txt :language: cmake ``` Scikit-build requires CMake 3.15, so there's no need to set it lower than 3.15. The project line can optionally use `SKBUILD_PROJECT_NAME` and `SKBUILD_PROJECT_VERSION` variables to avoid repeating this information from your `pyproject.toml`. You should specify exactly what language you use to keep CMake from searching for both `C` and `CXX` compilers (the default). If you place find Python first, pybind11 will respect it instead of the classic FindPythonInterp/FindPythonLibs mechanisms, which work, but are not as modern. Here we set `PYBIND11_FINDPYTHON` to `ON` instead of doing the find Python ourselves. Pybind11 places its config file such that CMake can find it from site-packages. You can either use `pybind11_add_module` or `python_add_library` and then link to `pybind11::module`, your choice. ```` ````{tab} nanobind ```{literalinclude} ../examples/getting_started/nanobind/CMakeLists.txt :language: cmake ``` Scikit-build and nanobind require CMake 3.15, so there's no need to set it lower than 3.15. The project line can optionally use `SKBUILD_PROJECT_NAME` and `SKBUILD_PROJECT_VERSION` variables to avoid repeating this information from your `pyproject.toml`. You should specify exactly what language you use to keep CMake from searching for both `C` and `CXX` compilers (the default). Nanobind places its config file such that CMake can find it from site-packages. ```` ````{tab} SWIG ```{literalinclude} ../examples/getting_started/swig/CMakeLists.txt :language: cmake ``` Scikit-build requires CMake 3.15, so there's no need to set it lower than 3.15. The project line can optionally use `SKBUILD_PROJECT_NAME` and `SKBUILD_PROJECT_VERSION` variables to avoid repeating this information from your `pyproject.toml`. You should specify exactly what language you use to keep CMake from searching for both `C` and `CXX` compilers (the default). You'll need to handle the generation of files by SWIG directly. ```` ````{tab} Cython ```{literalinclude} ../examples/getting_started/cython/CMakeLists.txt :language: cmake ``` Scikit-build requires CMake 3.15, so there's no need to set it lower than 3.15. The project line can optionally use `SKBUILD_PROJECT_NAME` and `SKBUILD_PROJECT_VERSION` variables to avoid repeating this information from your `pyproject.toml`. You should specify exactly what language you use to keep CMake from searching for both `C` and `CXX` compilers (the default). You'll need to handle the generation of files by Cython directly at the moment. A helper (similar to scikit-build classic) might be added in the future. ```` ````{tab} C ```{literalinclude} ../examples/getting_started/c/CMakeLists.txt :language: cmake ``` Scikit-build requires CMake 3.15, so there's no need to set it lower than 3.15. The project line can optionally use `SKBUILD_PROJECT_NAME` and `SKBUILD_PROJECT_VERSION` variables to avoid repeating this information from your `pyproject.toml`. You should specify exactly what language you use to keep CMake from searching for both `C` and `CXX` compilers (the default). `find_package(Python ...)` should always include the `Development.Module` component instead of `Development`; the latter breaks if the embedding components are missing, such as when you are building redistributable wheels on Linux. You'll want `WITH_SOABI` when you make the module to ensure the full extension is included on Unix systems (PyPy won't even be able to open the extension without it). ```` ````{tab} ABI3 ```{literalinclude} ../examples/getting_started/abi3/CMakeLists.txt :language: cmake ``` Scikit-build requires CMake 3.15, so there's no need to set it lower than 3.15. The project line can optionally use `SKBUILD_PROJECT_NAME` and `SKBUILD_PROJECT_VERSION` variables to avoid repeating this information from your `pyproject.toml`. You should specify exactly what language you use to keep CMake from searching for both `C` and `CXX` compilers (the default). `find_package(Python ...)` needs `Development.SABIModule` for ABI3 extensions. You'll want `WITH_SOABI` when you make the module. You'll also need to set the `USE_SABI` argument to the minimum version to build with. This will also add a proper PRIVATE define of `Py_LIMITED_API` for you. ```{note} This will not support pypy, so you'll want to provide an alternative if you support PyPy). ``` ```` ````{tab} Fortran ```{literalinclude} ../examples/getting_started/fortran/CMakeLists.txt :language: cmake ``` Scikit-build requires CMake 3.15, so there's no need to set it lower than 3.15. The project line can optionally use `SKBUILD_PROJECT_NAME` and `SKBUILD_PROJECT_VERSION` variables to avoid repeating this information from your `pyproject.toml`. You should specify exactly what language you use to keep CMake from searching for both `C` and `CXX` compilers (the default). You'll need to handle the generation of files by NumPy directly at the moment. A helper (similar to scikti-build classic) might be added in the future. You'll need gfortran on macOS. ```` Finally, you install your module. The default install path will go directly to `site-packages`, so if you are creating anything other than a single c-extension, you will want to install to the package directory (possibly `${SKBUILD_PROJECT_NAME}`) instead. ### Building and installing That's it! You can try building it: ```console $ pipx run build ``` [pipx](https://pipx.pypa.io) allows you to install and run Python applications in isolated environments. Or installing it (in a virtualenv, ideally): ```console $ pip install . ``` That's it for a basic package! [scientific python developer guide]: https://github.com/scikit-build/scikit-build-sample-projects [scikit-build-sample-projects]: https://github.com/scikit-build/scikit-build-sample-projects [uv]: https://docs.astral.sh/uv/ [scientific-python/cookie]: https://github.com/scientific-python/cookie [pybind11/scikit_build_example]: https://github.com/pybind/scikit_build_example [INTERSECT Training: Packaging]: https://intersect-training.org/packaging [packaging.python.org's tutorial]: https://packaging.python.org/en/latest/tutorials/packaging-projects [nanobind example]: https://github.com/wjakob/nanobind_example scikit-build-core-0.11.1/docs/guide/migration_guide.md000066400000000000000000000063141477275177200226470ustar00rootroot00000000000000# Migrating from scikit-build ```{warning} scikit-build-core is under active development. This guidance will be updated on a best-effort basis, but if you are working at the bleeding edge some of it may be out of date. ``` ## Config changes - The `build-system.build-backend` key in pyproject.toml must be changed to `scikit_build_core.build`. - Replace `scikit-build` with `scikit-build-core` in `build-system.requires`. - You should remove `cmake` and `ninja` from `build-system.requires`. `scikit-build-core` will add these if necessary, but will respect existing installations of the tools by default, which allows compatibility with systems where binaries are not available on PyPI but can be installed from elsewhere. Instead, set the minimum required versions in the `[tool.scikit-build]` table: `cmake.minimum-version` and `ninja.minimum-version`. - You must fill out the `tool.scikit-build` table in pyproject.toml, see [getting started](getting_started.md) for more information. - If your project is primarily configured using setup.py or setup.cfg, you will need to move the configuration to pyproject.toml. The [project metadata spec](https://packaging.python.org/en/latest/specifications/pyproject-toml) shows the information that can be placed directly in the project table. For additional metadata, see [our configuration guide](../configuration/index.md). A useful trick for performing this migration is to change the `build-backend` from `skbuild` to `setuptools`, install `hatch`, and run `hatch new --init`. This should automatically migrate the configuration to pyproject.toml, after which you can change the `build-backend` to `scikit-build-core`. - If you specify files to include in sdists via MANIFEST.in, with `scikit-build-core` you should now instead use the `sdist.include` and `sdist.exclude` fields in the `tool.scikit-build` table. Note that scikit-build-core uses all non `.gitignore`'d files by default, so this is often minimal or not needed. ## CMake changes scikit-build users wishing to switch to scikit-build-core should be aware of the following changes that must be made to their CMake files: - The PythonExtensions CMake module distributed with scikit-build is not part of scikit-build-core. Due to improvements in CMake's built-in support for building Python extension modules, most of this module is no longer necessary. Change ```cmake find_package(PythonExtensions REQUIRED) add_library(${LIBRARY} MODULE ${FILENAME}) python_extension_module(${LIBRARY}) ``` to ```cmake find_package(Python COMPONENTS Interpreter Development.Module REQUIRED) python_add_library(${LIBRARY} MODULE WITH_SOABI ${FILENAME}) ``` - The UseCython CMake module distributed with scikit-build is not currently supported. For examples on how to use Cython, see [our getting started guide](getting_started.md) for now. - The `SKBUILD_CONFIGURE_OPTIONS` environment variable is now named `SKBUILD_CMAKE_ARGS` for consistency. - The `SKBUILD_BUILD_OPTIONS` environment variable is not supported. Some specific features are accessible using alternative variables. In particular, use `CMAKE_BUILD_PARALLEL_LEVEL` or `SKBUILD_CMAKE_VERBOSE` to control build parallelism or CMake verbosity directly. scikit-build-core-0.11.1/docs/index.md000066400000000000000000000030101477275177200175010ustar00rootroot00000000000000# Scikit-build-core Scikit-build-core is a complete ground-up rewrite of scikit-build on top of modern packaging APIs. It provides a bridge between CMake and the Python build system, allowing you to make Python modules with CMake. :::{admonition} Scikit-build community meeting We have a public Scikit-build community meeting every month! [Join us on Google Meet](https://meet.google.com/dvx-jkai-xhq) on the third Friday of every month at 12:00 PM EST. We also have a developer's meeting on the first Friday of every month at the same time. Our past meeting minutes are [available here](https://github.com/orgs/scikit-build/discussions/categories/community-meeting-notes). ::: ## Features ```{include} ../README.md :start-after: ``` ## Contents ```{toctree} :maxdepth: 2 :titlesonly: :caption: Guide guide/getting_started guide/cmakelists guide/crosscompile guide/migration_guide guide/build guide/faqs ``` ```{toctree} :maxdepth: 1 :titlesonly: :caption: Configuration configuration/index configuration/overrides configuration/dynamic configuration/formatted configuration/search_paths ``` ```{toctree} :maxdepth: 1 :titlesonly: :caption: Plugins plugins/setuptools plugins/hatchling ``` ```{toctree} :maxdepth: 1 :titlesonly: :caption: About project about/projects about/changelog ``` ```{toctree} :maxdepth: 1 :titlesonly: :caption: API docs api/scikit_build_core schema ``` ## Indices and tables - {ref}`genindex` - {ref}`modindex` - {ref}`search` Generated using scikit-build-core {{ version }}. scikit-build-core-0.11.1/docs/man.md000066400000000000000000000006051477275177200171540ustar00rootroot00000000000000--- orphan: nosearch: --- # Scikit-build-core Scikit-build-core is a complete ground-up rewrite of scikit-build on top of modern packaging APIs. It provides a bridge between CMake and the Python build system, allowing you to make Python modules with CMake. ## Features ```{include} ../README.md :start-after: ``` Generated using scikit-build-core {{ version }}. scikit-build-core-0.11.1/docs/plugins/000077500000000000000000000000001477275177200175375ustar00rootroot00000000000000scikit-build-core-0.11.1/docs/plugins/hatchling.md000066400000000000000000000044721477275177200220310ustar00rootroot00000000000000# Hatchling A [hatchling][] plugin is being developed for scikit-build-core. This is currently in a highly experimental state, but feedback is welcome. :::{warning} This plugin is experimental, and will probably be moved to a separate package. If using it, it is highly recommended to upper-cap scikit-build-core until it moves. ::: :::{versionadded} 0.9 ::: ## Basic usage To use the plugin, make sure hatchling and scikit-build-core are in your `build-system.requires`. A recent version of hatchling is best; you need 1.23 to get `cmake` and `ninja` auto-added if needed, and 1.24 if you want to write out custom scripts, metadata, or shared data. You need a `tool.hatch.build.targets.wheel.hooks.scikit-build` section to activate the plugin. Currently, you need at least the `experimental` option to use the plugin, which means you acknowledge that this might move in the next release of scikit-build-core. It was added in 0.9. ```toml [build-system] requires = ["hatchling", "scikit-build-core~=0.9.0"] build-backend = "hatchling.build" [project] name = "hatchling_example" version = "0.1.0" [tool.hatch.build.targets.wheel.hooks.scikit-build] experimental = true ``` :::{note} Note that this is equivalent: ```toml [tool.hatch.build.targets.wheel.hooks.scikit-build] [tool.scikit-build] experimental = true ``` ::: ## Options Most of scikit-build-core's configuration can be used with hatchling if it is applicable. Things like metadata and wheel options generally are not applicable, unless they pertain to setting the tag (which scikit-build-core controls). You can specify settings in either `tool.hatch.build.targets.wheel.hooks.scikit-build` or `tool.scikit-build` or via environment variables. You cannot use config-settings, as that's not supported by hatchling for plugins. Key limitations: - You need to leave `cmake.wheel` on. No `wheel.platlib = False` builds. - Using cmake in SDist step is not supported yet. - Editable installs are not supported yet. - `scikit-build.generate` and `scikit-build.metadata` is not supported. - `${SKBUILD_HEADER_DIR}` is not supported, request support in Hatching if needed. - Anything in `${SKBUILD_METADATA_DIR}` must be placed in an `extra_metadata` folder. ## Writing CMakeLists.txt The hatchling version is available as `${SKBUILD_HATCHLING}`. [hatchling]: https://hatch.pypa.io scikit-build-core-0.11.1/docs/plugins/setuptools.md000066400000000000000000000043541477275177200223100ustar00rootroot00000000000000# Setuptools A setuptools plugin is being developed for scikit-build-core, primarily to enable scikit-build-core to be the build backend for scikit-build (classic). :::{warning} This plugin is experimental, and will probably be moved to a separate package. If using it, it is probably best to upper-cap scikit-build-core until it moves. ::: ## Basic usage To use the plugin, make sure you have both setuptools and scikit-build-core in your `build-system.requires` table. You can use either `setuptools.build_meta` or `scikit-build-core.setuptools.build_meta` as `build-system.build-backend`, but the latter will give you the auto-inclusion of `cmake` and `ninja` as needed, so it is recommended. ```toml [build-system] requires = ["scikit-build-core", "setuptools"] build-backend = "scikit_build_core.setuptools.build_meta" ``` Depending on how you like configuring setuptools, you can specify a `project` table, or use `setup.cfg`, or `setup.py`. However, you need at least this minimal `setup.py` present: ```python from setuptools import setup setup(cmake_source_dir=".") ``` The presence of the `cmake_source_dir` option will tell the scikit-build setuptools plugin that it can activate for this package. ## Options These are the currently supported `setup.py` options: - `cmake_source_dir`: The location of your `CMakeLists.txt`. Required. - `cmake_args`: Arguments to include when configuring. These options from scikit-build (classic) are not currently supported: `cmake_install_dir`, `cmake_with_sdist`, `cmake_process_manifest_hook`, and `cmake_install_target`. `cmake_languages` has no effect. And `cmake_minimum_requires_version` is now specified via `pyproject.toml` config, so has no effect here. A compatibility shim, `scikit_build_core.setuptools.wrapper.setup` is provided; it will eventually behave as close to scikit-build (classic)'s `skbuild.setup` as possible. ## Configuration All other configuration is available as normal `tool.scikit-build` in `pyproject.toml` or environment variables as applicable. Config-settings is _not_ supported, as setuptools has very poor support for config-settings. Eventually, the build hook might pre-process options, but it's tricky to pass them through, so it will probably require use cases to be presented. scikit-build-core-0.11.1/docs/schema.md000066400000000000000000000002701477275177200176370ustar00rootroot00000000000000(schema)= # Schema The full schema for the `tool.scikit-build` table is below: ```{jsonschema} ../src/scikit_build_core/resources/scikit-build.schema.json :auto_reference: true ``` scikit-build-core-0.11.1/noxfile.py000066400000000000000000000223541477275177200171520ustar00rootroot00000000000000""" Scikit-build-core's nox configuration. Use `-t gen` to run all the generation jobs. """ from __future__ import annotations import argparse import json import shutil import sys import urllib.request from pathlib import Path from typing import TYPE_CHECKING import nox if TYPE_CHECKING: from collections.abc import Sequence nox.needs_version = ">=2024.4.15" nox.options.default_venv_backend = "uv|virtualenv" DIR = Path(__file__).parent.resolve() @nox.session(reuse_venv=True) def lint(session: nox.Session) -> None: """ Run the linter. """ session.install("pre-commit") session.run("pre-commit", "run", "--all-files", *session.posargs) @nox.session(reuse_venv=True) def pylint(session: nox.Session) -> None: """ Run PyLint. """ # This needs to be installed into the package environment, and is slower # than a pre-commit check session.install("-e.[dev,test,test-meta]", "pylint==3.3.*") session.run("pylint", "--version") session.run("pylint", "scikit_build_core", *session.posargs) def _prepare_cmake_ninja( session: nox.Session, ) -> None: # This will not work if system CMake is too old (<3.15) if shutil.which("cmake") is None and shutil.which("cmake3") is None: session.install("cmake") if shutil.which("ninja") is None: session.install("ninja") def _run_tests( session: nox.Session, *, install_args: Sequence[str] = (), run_args: Sequence[str] = (), extras: Sequence[str] = (), ) -> None: posargs = list(session.posargs) or ["-n", "auto"] env = {"PIP_DISABLE_PIP_VERSION_CHECK": "1"} _prepare_cmake_ninja(session) _extras = ["test", *extras] if "--cov" in posargs: _extras.append("cov") posargs.append("--cov-config=pyproject.toml") install_arg = f"-e.[{','.join(_extras)}]" session.install(install_arg, *install_args, silent=False) session.run("pytest", *run_args, *posargs, env=env) @nox.session(reuse_venv=True, tags=["gen"]) def generate_schema(session: nox.Session) -> None: """ (Re)generate src/scikit_build_core/resources/scikit-build.schema.json from model. """ session.install("-e.") schema_txt = session.run( "python", "-m", "scikit_build_core.settings.skbuild_schema", silent=True ) assert isinstance(schema_txt, str) schema_file = DIR / "src/scikit_build_core/resources/scikit-build.schema.json" schema_file.write_text(schema_txt) @nox.session def tests(session: nox.Session) -> None: """ Run the unit and regular tests. Includes coverage if --cov passed. """ _run_tests(session, extras=["test-meta,test-numpy,test-schema,test-hatchling"]) @nox.session(reuse_venv=True, tags=["gen"]) def readme(session: nox.Session) -> None: """ Update the readme with cog. Pass --check to check instead. """ args = session.posargs or ["-r"] session.install("-e.", "cogapp") session.run("cog", "-P", *args, "README.md") @nox.session(venv_backend="uv", default=False) def minimums(session: nox.Session) -> None: """ Test the minimum versions of dependencies. """ _run_tests( session, install_args=["--resolution=lowest-direct"], run_args=["-Wdefault"], ) @nox.session(reuse_venv=True, default=False) def docs(session: nox.Session) -> None: """ Build the docs. Use "--non-interactive" to avoid serving. Pass "-b linkcheck" to check links. """ parser = argparse.ArgumentParser() parser.add_argument( "-b", dest="builder", default="html", help="Build target (default: html)" ) args, posargs = parser.parse_known_args(session.posargs) serve = args.builder == "html" and session.interactive extra_installs = ["sphinx-autobuild"] if serve else [] session.install("-e.[docs]", *extra_installs) session.chdir("docs") shared_args = ( "-n", # nitpicky mode "-T", # full tracebacks f"-b={args.builder}", ".", f"_build/{args.builder}", *posargs, ) if serve: session.run("sphinx-autobuild", "--open-browser", *shared_args) else: session.run("sphinx-build", "--keep-going", *shared_args) @nox.session(tags=["gen"]) def build_api_docs(session: nox.Session) -> None: """ Build (regenerate) API docs. """ session.install("sphinx") session.chdir("docs") session.run( "sphinx-apidoc", "-o", "api/", "--no-toc", "--force", "--module-first", "../src/scikit_build_core", ) @nox.session(default=False) def build(session: nox.Session) -> None: """ Build an SDist and wheel. """ session.install("build") session.run("python", "-m", "build", *session.posargs) EXAMPLES = ["c", "abi3", "pybind11", "nanobind", "swig", "cython"] if not sys.platform.startswith("win") and shutil.which("gfortran"): EXAMPLES.append("fortran") EXAMPLES = [f"getting_started/{n}" for n in EXAMPLES] EXAMPLES += ["downstream/pybind11_example", "downstream/nanobind_example"] @nox.session @nox.parametrize("example", EXAMPLES, ids=EXAMPLES) def test_doc_examples(session: nox.Session, example: str) -> None: _prepare_cmake_ninja(session) session.install("-e.", "pip") session.chdir(f"docs/examples/{example}") reqs = nox.project.load_toml("pyproject.toml")["build-system"]["requires"] freqs = (r for r in reqs if "scikit-build-core" not in r.replace("_", "-")) session.install(*freqs, "pytest") session.install( ".", "--no-build-isolation", "--config-settings=cmake.verbose=true", env={"PYTHONWARNINGS": "error"}, ) session.run("pip", "list") if Path("../test.py").is_file(): session.run("python", "../test.py") else: session.run("pytest") @nox.session(default=False) def downstream(session: nox.Session) -> None: """ Build a downstream project. """ # If running in manylinux: # docker run --rm -v $PWD:/sk -w /sk -t quay.io/pypa/manylinux2014_x86_64:latest \ # pipx run nox -s downstream -- https://github.com/... parser = argparse.ArgumentParser(prog=f"{Path(sys.argv[0]).name} -s downstream") parser.add_argument("project", help="A project to build") parser.add_argument("--subdir", help="A subdirectory to build") parser.add_argument( "--editable", action="store_true", help="Install as editable wheel" ) parser.add_argument("-c", dest="code", help="Run some Python code") parser.add_argument("-C", help="config-settings", action="append", default=[]) args, remaining = parser.parse_known_args(session.posargs) tmp_dir = Path(session.create_tmp()) proj_dir = tmp_dir / "_".join(args.project.split("/")) session.install("build", "hatch-vcs", "hatchling") session.install(".", "--no-build-isolation") if proj_dir.is_dir(): session.chdir(proj_dir) session.run("git", "pull", external=True) else: session.run( "git", "clone", args.project, *remaining, str(proj_dir), "--recurse-submodules", external=True, ) session.chdir(proj_dir) # Read and strip requirements pyproject = nox.project.load_toml("pyproject.toml") requires = [ x for x in pyproject["build-system"]["requires"] if "scikit-build-core" not in x.replace("_", "-") ] if not shutil.which("ninja"): requires.append("ninja") if not shutil.which("cmake"): requires.append("cmake") if requires: session.install(*requires) if args.subdir: session.chdir(args.subdir) if args.editable: session.install("-e.", "--no-build-isolation") else: session.run( "python", "-m", "build", "--no-isolation", "--skip-dependency-check", "--wheel", ".", *(f"-C{x}" for x in args.C), ) if args.code: session.error("Must use editable install for code at the moment") if args.code: session.run("python", "-c", args.code) @nox.session(venv_backend="none", default=False) def vendor_pyproject_metadata(session: nox.Session) -> None: """ Vendor pyproject.toml metadata. """ parser = argparse.ArgumentParser( prog=f"{Path(sys.argv[0]).name} -s vendor_pyproject_metadata" ) parser.add_argument("version", help="A tag or ref to vendor") args = parser.parse_args(session.posargs) repo = "pypa/pyproject-metadata" branch = args.version with urllib.request.urlopen( f"https://api.github.com/repos/{repo}/git/trees/{branch}?recursive=1" ) as response: info = json.loads(response.read().decode("utf-8")) files = { y: y for x in info["tree"] if (y := x["path"]).startswith("pyproject_metadata/") } files["pyproject_metadata/LICENSE"] = "LICENSE" for tgt_path, remote_path in files.items(): local_path = Path("src/scikit_build_core/_vendor").joinpath(tgt_path) print(f"Vendoring: {remote_path} -> {local_path}") with urllib.request.urlopen( f"https://raw.githubusercontent.com/{repo}/{branch}/{remote_path}" ) as response: txt = response.read() local_path.write_bytes(txt) scikit-build-core-0.11.1/pyproject.toml000066400000000000000000000260041477275177200200440ustar00rootroot00000000000000[build-system] requires = ["hatchling", "hatch-vcs"] build-backend = "hatchling.build" [project] name = "scikit_build_core" authors = [ { name = "Henry Schreiner", email = "henryfs@princeton.edu" }, ] description = "Build backend for CMake based projects" readme = "README.md" requires-python = ">=3.8" classifiers = [ "Topic :: Scientific/Engineering", "Topic :: Software Development :: Build Tools", "Intended Audience :: Science/Research", "Intended Audience :: Developers", "Operating System :: OS Independent", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Development Status :: 4 - Beta", "Typing :: Typed", ] dynamic = ["version"] dependencies = [ "exceptiongroup >=1.0; python_version<'3.11'", "importlib-resources >=1.3; python_version<'3.9'", "packaging >=23.2", "pathspec >=0.10.1", "tomli >=1.2.2; python_version<'3.11'", "typing-extensions >=3.10.0; python_version<'3.9'", ] # Note: cmake and possibly ninja are also required if those are not already # present (user controllable) - but a system version is fine. [project.optional-dependencies] pyproject = [ ] test = [ "build >=0.8", "cattrs >=22.2.0", "pip>=23; python_version<'3.13'", "pip>=24.1; python_version>='3.13'", "pybind11 >=2.11", "pytest >=7.2", "pytest-subprocess >=1.5", 'pytest-xdist >=3.1', 'setuptools >=43; python_version<"3.9"', 'setuptools >=45; python_version=="3.9"', 'setuptools >=49; python_version>="3.10" and python_version<"3.12"', 'setuptools >=66.1; python_version>="3.12"', "virtualenv >=20.20", "wheel >=0.40", ] test-hatchling = [ "hatchling >=1.24.0", ] test-meta = [ "hatch-fancy-pypi-readme>=22.3", "setuptools-scm", ] test-numpy = [ "numpy; python_version<'3.13' and platform_python_implementation!='PyPy'", "numpy~=1.24.0; python_version=='3.8' and platform_python_implementation=='PyPy'", "numpy~=2.0.0; python_version=='3.9' and platform_python_implementation=='PyPy'", ] test-schema = [ "fastjsonschema", "validate-pyproject", ] cov = [ "pytest-cov", ] wheels = [ "cmake", "ninja; sys_platform!='win32'", ] dev = [ "rich", ] docs = [ "furo", "hatchling", "myst-parser >=0.13", "setuptools", "sphinx >=7.0", "sphinx-autodoc-typehints", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-jsonschema", "sphinx-tippy", ] wheel-free-setuptools = [ 'setuptools>=70.1; python_version>="3.8"', ] [project.urls] Changelog = "https://scikit-build-core.readthedocs.io/en/latest/changelog.html" Discussions = "https://github.com/orgs/scikit-build/discussions" Documentation = "https://scikit-build-core.readthedocs.io" Homepage = "https://github.com/scikit-build/scikit-build-core" Issues = "https://github.com/scikit-build/scikit-build-core/issues" [project.entry-points] "distutils.commands".build_cmake = "scikit_build_core.setuptools.build_cmake:BuildCMake" "distutils.setup_keywords".cmake_source_dir = "scikit_build_core.setuptools.build_cmake:cmake_source_dir" "distutils.setup_keywords".cmake_args = "scikit_build_core.setuptools.build_cmake:cmake_args" "distutils.setup_keywords".cmake_install_target = "scikit_build_core.setuptools.build_cmake:cmake_install_target" "setuptools.finalize_distribution_options".scikit_build_entry = "scikit_build_core.setuptools.build_cmake:finalize_distribution_options" "validate_pyproject.tool_schema".scikit-build = "scikit_build_core.settings.skbuild_schema:get_skbuild_schema" hatch.scikit-build = "scikit_build_core.hatch.hooks" [tool.hatch] version.source = "vcs" build.hooks.vcs.version-file = "src/scikit_build_core/_version.py" [tool.uv] dev-dependencies = ["scikit-build-core[test,test-hatchling,test-meta,test-numpy,test-schema,cov,dev]"] pip.reinstall-package = ["scikit-build-core"] workspace.members = ["tmp/hello/hello"] [tool.pytest.ini_options] minversion = "7.2" addopts = ["-ra", "--strict-markers", "--strict-config"] xfail_strict = true filterwarnings = [ "error", "ignore:Config variable '.*' is unset, Python ABI tag may be incorrect:RuntimeWarning", "default:pkg_resources is deprecated as an API:DeprecationWarning:wheel", # Caused by wheel<0.41 in tests "default:The 'wheel' package is no longer the canonical location:DeprecationWarning", # Caused by wheel also "default:onerror argument is deprecated, use onexc instead:DeprecationWarning:wheel", # Caused by wheel<0.41 & Python 3.12 "default:The distutils package is deprecated and slated for removal:DeprecationWarning", # Caused by setuptools sometimes "default:The distutils.sysconfig module is deprecated, use sysconfig instead:DeprecationWarning", # Caused by setuptools sometimes "default:check_home argument is deprecated and ignored.:DeprecationWarning", # Caused by setuptools sometimes "ignore::scikit_build_core._vendor.pyproject_metadata.errors.ConfigurationWarning", "ignore:'_UnionGenericAlias' is deprecated and slated for removal in Python 3.17:DeprecationWarning", # From cattrs 24.1.2 and other? ] log_cli_level = "info" pythonpath = ["tests/utils"] testpaths = ["tests"] markers = [ "broken_on_urct: Broken for now due to lib not found", "compile: Compiles code", "configure: Configures CMake code", "fortran: Fortran code", "integration: Full package build", "isolated: Needs an isolated virtualenv", "network: Needs a network connection to setup or run", "setuptools: Tests setuptools integration", "virtualenv: Needs a virtualenv", ] norecursedirs = ["tests/packages/**"] [tool.mypy] files = ["src", "tests", "noxfile.py"] mypy_path = ["$MYPY_CONFIG_FILE_DIR/src", "$MYPY_CONFIG_FILE_DIR/tests/utils"] python_version = "3.8" warn_unused_configs = true warn_unreachable = false enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] strict = true disallow_untyped_defs = false disallow_incomplete_defs = false [[tool.mypy.overrides]] module = ["scikit_build_core.*"] disallow_untyped_defs = true disallow_incomplete_defs = true [[tool.mypy.overrides]] module = ["numpy", "pathspec", "setuptools_scm", "hatch_fancy_pypi_readme", "virtualenv"] ignore_missing_imports = true [tool.pylint] py-version = "3.8" jobs = "0" reports.output-format = "colorized" good-names = ["f"] messages_control.disable = [ "design", "fixme", "import-outside-toplevel", "invalid-name", "line-too-long", "missing-class-docstring", "missing-function-docstring", "missing-function-docstring", "missing-module-docstring", "wrong-import-position", "unnecessary-ellipsis", # Conflicts with Protocols "broad-except", "unused-argument", # Handled by Ruff "redefined-builtin", # ExceptionGroup is a builtin "using-exception-groups-in-unsupported-version", # We are using a backport ] [tool.coverage] run.source = ["scikit_build_core"] run.omit = ["src/scikit_build_core/_vendor"] report.exclude_lines = [ 'pragma: no cover', '\.\.\.', 'if typing.TYPE_CHECKING:', 'if TYPE_CHECKING:', 'def __repr__', 'if __name__ == "main":', ] [tool.check-wheel-contents] ignore = ["W002"] # Triggers on __init__.py's [tool.ruff] exclude = ["src/scikit_build_core/_vendor/*"] [tool.ruff.lint] extend-select = [ "ANN", # flake8-annotations "ARG", # flake8-unused-arguments "B", # flake8-bugbear "C4", # flake8-comprehensions "EM", # flake8-errmsg "FBT", # flake8-boolean-trap "FLY", # flynt "I", # isort "ICN", # flake8-import-conventions "ISC", # flake8-implicit-str-concat "N", # flake8-naming "PERF", # perflint "PGH", # pygrep-hooks "PIE", # flake8-pie "PL", # pylint "PT", # flake8-pytest-style "PTH", # flake8-use-pathlib "PYI", # flake8-pyi "RET", # flake8-return "RUF", # Ruff-specific "S", # eval -> literal_eval "SIM", # flake8-simplify "T20", # flake8-print "TC", # flake8-type-checking "TID251", # flake8-tidy-imports.banned-api "TRY", # tryceratops "UP", # pyupgrade "YTT", # flake8-2020 "FURB", # refurb ] ignore = [ "PLE1205", # Format check doesn't work with our custom logger "PT013", # It's correct to import classes for typing! "RUF009", # Too easy to get a false positive "PYI025", # Wants Set to be renamed AbstractSet "ISC001", # Conflicts with formatter "PLR09", # Too many ... "PLR2004", # Magic value used in comparison "PLC0415", # Import should be at top of file "ANN401", # Disallow dynamically typed expressions "S101", # Use of assert detected "S603", # subprocess untrusted input "S607", # subprocess call "S404", # subprocess module is possibly insecure ] typing-modules = ["scikit_build_core._compat.typing"] [tool.ruff.format] docstring-code-format = true [tool.ruff.lint.isort] known-local-folder = ["pathutils"] [tool.ruff.lint.flake8-tidy-imports.banned-api] "typing.Callable".msg = "Use collections.abc.Callable instead." "typing.Iterator".msg = "Use collections.abc.Iterator instead." "typing.Mapping".msg = "Use collections.abc.Mapping instead." "typing.Sequence".msg = "Use collections.abc.Sequence instead." "typing.Set".msg = "Use collections.abc.Set instead." "typing.Self".msg = "Use scikit_build_core._compat.typing.Self instead." "typing_extensions.Self".msg = "Use scikit_build_core._compat.typing.Self instead." "typing.assert_never".msg = "Add scikit_build_core._compat.typing.assert_never instead." "tomli".msg = "Use scikit_build_core._compat.tomllib instead." "tomllib".msg = "Use scikit_build_core._compat.tomllib instead." "importlib_metadata".msg = "Use importlib.metadata directly instead." "importlib.metadata.entry_points".msg = "Use scikit_build_core._compat.importlib.metadata.entry_points instead." "importlib.resources".msg = "Use scikit_build_core._compat.importlib.resources instead." "importlib_resources".msg = "Use scikit_build_core._compat.importlib.resources instead." "pyproject_metadata".msg = "Use scikit_build_core._vendor.pyproject_metadata instead." [tool.ruff.lint.per-file-ignores] "tests/**" = ["T20", "ANN", "FBT001"] "noxfile.py" = ["T20", "TID251"] "src/scikit_build_core/resources/*.py" = ["PTH", "ARG002", "FBT", "TID251"] "src/scikit_build_core/_compat/**.py" = ["TID251"] "tests/conftest.py" = ["TID251"] "tests/packages/**.py" = ["TID251"] "docs/conf.py" = ["TID251"] "docs/examples/**" = ["ANN"] "src/scikit_build_core/file_api/model/*.py" = ["N"] [tool.check-sdist] sdist-only = ["src/scikit_build_core/_version.py"] scikit-build-core-0.11.1/src/000077500000000000000000000000001477275177200157155ustar00rootroot00000000000000scikit-build-core-0.11.1/src/scikit_build_core/000077500000000000000000000000001477275177200213725ustar00rootroot00000000000000scikit-build-core-0.11.1/src/scikit_build_core/__init__.py000066400000000000000000000003431477275177200235030ustar00rootroot00000000000000""" Copyright (c) 2022 Henry Schreiner. All rights reserved. scikit-build-core: PEP 517 builder for Scikit-Build """ from __future__ import annotations from ._version import version as __version__ __all__ = ["__version__"] scikit-build-core-0.11.1/src/scikit_build_core/_compat/000077500000000000000000000000001477275177200230145ustar00rootroot00000000000000scikit-build-core-0.11.1/src/scikit_build_core/_compat/__init__.py000066400000000000000000000000741477275177200251260ustar00rootroot00000000000000from __future__ import annotations __all__: list[str] = [] scikit-build-core-0.11.1/src/scikit_build_core/_compat/builtins.py000066400000000000000000000003721477275177200252210ustar00rootroot00000000000000from __future__ import annotations import sys if sys.version_info < (3, 11): from exceptiongroup import ExceptionGroup else: from builtins import ExceptionGroup __all__ = ["ExceptionGroup"] def __dir__() -> list[str]: return __all__ scikit-build-core-0.11.1/src/scikit_build_core/_compat/importlib/000077500000000000000000000000001477275177200250155ustar00rootroot00000000000000scikit-build-core-0.11.1/src/scikit_build_core/_compat/importlib/__init__.py000066400000000000000000000000741477275177200271270ustar00rootroot00000000000000from __future__ import annotations __all__: list[str] = [] scikit-build-core-0.11.1/src/scikit_build_core/_compat/importlib/metadata.py000066400000000000000000000011711477275177200271470ustar00rootroot00000000000000from __future__ import annotations import importlib.metadata import sys import typing if typing.TYPE_CHECKING: if sys.version_info < (3, 10): from importlib.metadata import EntryPoint EntryPoints = typing.List[EntryPoint] else: from importlib.metadata import EntryPoints __all__ = ["entry_points"] def entry_points(*, group: str) -> EntryPoints: if sys.version_info >= (3, 10): return importlib.metadata.entry_points(group=group) epg = importlib.metadata.entry_points() return epg.get(group, []) # pylint: disable=no-member def __dir__() -> list[str]: return __all__ scikit-build-core-0.11.1/src/scikit_build_core/_compat/importlib/resources.py000066400000000000000000000003561477275177200274050ustar00rootroot00000000000000from __future__ import annotations import sys if sys.version_info < (3, 9): from importlib_resources import files else: from importlib.resources import files __all__ = ["files"] def __dir__() -> list[str]: return __all__ scikit-build-core-0.11.1/src/scikit_build_core/_compat/tomllib.py000066400000000000000000000003511477275177200250270ustar00rootroot00000000000000from __future__ import annotations import sys if sys.version_info < (3, 11): from tomli import load, loads else: from tomllib import load, loads __all__ = ["load", "loads"] def __dir__() -> list[str]: return __all__ scikit-build-core-0.11.1/src/scikit_build_core/_compat/typing.py000066400000000000000000000012751477275177200247050ustar00rootroot00000000000000from __future__ import annotations import sys import typing if sys.version_info < (3, 9): from typing_extensions import Annotated, get_args, get_origin else: from typing import Annotated, get_args, get_origin if sys.version_info < (3, 11): if typing.TYPE_CHECKING: from typing_extensions import Self, assert_never else: Self = object def assert_never(_: object) -> None: msg = "Expected code to be unreachable" raise AssertionError(msg) else: from typing import Self, assert_never __all__ = [ "Annotated", "Self", "assert_never", "get_args", "get_origin", ] def __dir__() -> list[str]: return __all__ scikit-build-core-0.11.1/src/scikit_build_core/_logging.py000066400000000000000000000232201477275177200235300ustar00rootroot00000000000000from __future__ import annotations import contextlib import dataclasses import enum import functools import logging import os import platform import sys from collections.abc import Mapping from typing import TYPE_CHECKING, Any, Literal, NoReturn if TYPE_CHECKING: from collections.abc import Iterator from ._compat.typing import Self StrMapping = Mapping[str, "Style"] else: StrMapping = Mapping from . import __version__ __all__ = [ "LEVEL_VALUE", "ScikitBuildLogger", "Style", "logger", "raw_logger", "rich_error", "rich_print", "rich_warning", ] def __dir__() -> list[str]: return __all__ raw_logger = logging.getLogger("scikit_build_core") LEVEL_VALUE = { "CRITICAL": logging.CRITICAL, "ERROR": logging.ERROR, "WARNING": logging.WARNING, "INFO": logging.INFO, "DEBUG": logging.DEBUG, "NOTSET": logging.NOTSET, } class PlatformHelper: def __getattr__(self, name: str) -> Any: result = getattr(platform, name) return result() if callable(result) else result def __repr__(self) -> str: return repr(platform) class FStringMessage: "This class captures a formatted string message and only produces it on demand." def __init__(self, fmt: str, *args: object, **kwargs: object) -> None: self.fmt = fmt self.args = args self.kwargs = kwargs def __str__(self) -> str: return self.fmt.format(*self.args, **self.kwargs) def __repr__(self) -> str: return ( f"" ) class ScikitBuildLogger: # pylint: disable-next=redefined-outer-name def __init__(self, logger: logging.Logger) -> None: self.logger = logger def debug(self, msg: str, *args: object, **kwargs: object) -> None: self.logger.debug(FStringMessage(msg, *args, **kwargs), stacklevel=2) def info(self, msg: str, *args: object, **kwargs: object) -> None: self.logger.info(FStringMessage(msg, *args, **kwargs), stacklevel=2) def warning(self, msg: str, *args: object, **kwargs: object) -> None: self.logger.warning(FStringMessage(msg, *args, **kwargs), stacklevel=2) def error(self, msg: str, *args: object, **kwargs: object) -> None: self.logger.error(FStringMessage(msg, *args, **kwargs), stacklevel=2) def critical(self, msg: str, *args: object, **kwargs: object) -> None: self.logger.critical(FStringMessage(msg, *args, **kwargs), stacklevel=2) def exception(self, msg: str, *args: object, **kwargs: object) -> None: self.logger.exception(FStringMessage(msg, *args, **kwargs), stacklevel=2) def log(self, level: int, msg: str, *args: object, **kwargs: object) -> None: self.logger.log(level, FStringMessage(msg, *args, **kwargs), stacklevel=2) def setLevel(self, level: int) -> None: # noqa: N802 self.logger.setLevel(level) def addHandler(self, handler: logging.Handler) -> None: # noqa: N802 self.logger.addHandler(handler) logger = ScikitBuildLogger(raw_logger) def colors() -> bool: if "NO_COLOR" in os.environ: return False # Pip reroutes sys.stdout, so FORCE_COLOR is required there if os.environ.get("FORCE_COLOR", ""): return True # Avoid ValueError: I/O operation on closed file with contextlib.suppress(ValueError): # Assume sys.stderr is similar to sys.stdout isatty = sys.stdout.isatty() if isatty and not sys.platform.startswith("win"): return True return False class Colors(enum.Enum): black = 0 red = 1 green = 2 yellow = 3 blue = 4 magenta = 5 cyan = 6 white = 7 default = 9 class Styles(enum.Enum): bold = 1 italic = 3 underline = 4 reverse = 7 reset = 0 normal = 22 @dataclasses.dataclass(frozen=True) class Style(StrMapping): color: bool = dataclasses.field(default_factory=colors) styles: tuple[int, ...] = dataclasses.field(default_factory=tuple) current: int = 0 def __str__(self) -> str: styles = ";".join(str(x) for x in self.styles) return f"\33[{styles}m" if styles and self.color else "" @property def fg(self) -> Self: return dataclasses.replace(self, current=30) @property def bg(self) -> Self: return dataclasses.replace(self, current=40) @property def bold(self) -> Self: return dataclasses.replace(self, styles=(*self.styles, Styles.bold.value)) @property def italic(self) -> Self: return dataclasses.replace(self, styles=(*self.styles, Styles.italic.value)) @property def underline(self) -> Self: return dataclasses.replace(self, styles=(*self.styles, Styles.underline.value)) @property def reverse(self) -> Self: return dataclasses.replace(self, styles=(*self.styles, Styles.reverse.value)) @property def reset(self) -> Self: return dataclasses.replace(self, styles=(Styles.reset.value,), current=0) @property def normal(self) -> Self: return dataclasses.replace(self, styles=(*self.styles, Styles.normal.value)) @property def black(self) -> Self: return dataclasses.replace( self, styles=(*self.styles, Colors.black.value + (self.current or 30)) ) @property def red(self) -> Self: return dataclasses.replace( self, styles=(*self.styles, Colors.red.value + (self.current or 30)) ) @property def green(self) -> Self: return dataclasses.replace( self, styles=(*self.styles, Colors.green.value + (self.current or 30)) ) @property def yellow(self) -> Self: return dataclasses.replace( self, styles=(*self.styles, Colors.yellow.value + (self.current or 30)) ) @property def blue(self) -> Self: return dataclasses.replace( self, styles=(*self.styles, Colors.blue.value + (self.current or 30)) ) @property def magenta(self) -> Self: return dataclasses.replace( self, styles=(*self.styles, Colors.magenta.value + (self.current or 30)) ) @property def cyan(self) -> Self: return dataclasses.replace( self, styles=(*self.styles, Colors.cyan.value + (self.current or 30)) ) @property def white(self) -> Self: return dataclasses.replace( self, styles=(*self.styles, Colors.white.value + (self.current or 30)) ) @property def default(self) -> Self: return dataclasses.replace( self, styles=(*self.styles, Colors.default.value + (self.current or 30)) ) _keys = ( "bold", "italic", "underline", "reverse", "reset", "normal", "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white", "default", ) def __len__(self) -> int: return len(self._keys) def __getitem__(self, name: str) -> Self: return getattr(self, name) # type: ignore[no-any-return] def __iter__(self) -> Iterator[str]: return iter(self._keys) _style = Style() _nostyle = Style(color=False) def rich_print( *args: object, file: object = None, sep: str = " ", end: str = "\n", color: Literal[ "", "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white" ] = "", **kwargs: object, ) -> None: """ Print a message with style and useful common includes provided via formatting. This function will process every argument with the following formatting: - ``{__version__}``: The version of scikit-build-core. - ``{platform}``: The platform module. - ``{sys}``: The sys module. - Colors and styles. Any keyword arguments will be passed directly to the `str.format` method unless they conflict with the above. ``print`` arguments work as normal, and the output will be flushed. Each argument will clear the style afterwards if a style is applied. The ``color=`` argument will set a default color to apply to every argument, and is available to arguments as ``{color}``. """ if color: kwargs["color"] = _style[color] args_1 = tuple(str(arg) for arg in args) args_1_gen = ( arg.format( __version__=__version__, platform=PlatformHelper(), sys=sys, **_nostyle, **kwargs, ) for arg in args_1 ) args_2_gen = ( arg.format( __version__=__version__, platform=PlatformHelper(), sys=sys, **_style, **kwargs, ) for arg in args_1 ) if color: args_2 = (f"{_style[color]}{new}{_style.reset}" for new in args_2_gen) else: args_2 = ( new if new == orig else f"{new}{_style.reset}" for new, orig in zip(args_2_gen, args_1_gen) ) print(*args_2, flush=True, sep=sep, end=end, file=file) # type: ignore[call-overload] @functools.lru_cache(maxsize=None) def rich_warning( *args: str, color: Literal[ "", "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white" ] = "yellow", **kwargs: object, ) -> None: rich_print("{bold.yellow}WARNING:", *args, color=color, **kwargs) # type: ignore[arg-type] def rich_error( *args: str, color: Literal[ "", "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white" ] = "red", **kwargs: object, ) -> NoReturn: rich_print("{bold.red}ERROR:", *args, color=color, **kwargs) # type: ignore[arg-type] raise SystemExit(7) scikit-build-core-0.11.1/src/scikit_build_core/_shutil.py000066400000000000000000000055061477275177200234210ustar00rootroot00000000000000from __future__ import annotations import dataclasses import os import stat import subprocess from typing import TYPE_CHECKING, ClassVar from ._logging import logger if TYPE_CHECKING: from collections.abc import Iterable __all__ = ["Run"] def __dir__() -> list[str]: return __all__ @dataclasses.dataclass class Run: env: dict[str, str] | None = None cwd: os.PathLike[str] | None = None timeout: None | float = None # Stores last printout, for cleaner debug logging _prev_env: ClassVar[dict[str, str]] = {} def live(self, *args: str | os.PathLike[str]) -> None: """ Runs code and prints the results live. """ self._run(args, capture=False) def capture( self, *args: str | os.PathLike[str] ) -> subprocess.CompletedProcess[str]: """ Runs a command and captures the result. """ return self._run(args, capture=True) def _run( self, args: Iterable[str | os.PathLike[str]], *, capture: bool, ) -> subprocess.CompletedProcess[str]: options = [ os.fspath(arg) if isinstance(arg, os.PathLike) else arg for arg in args ] if self.env: if not self._prev_env: type(self)._prev_env = self.env.copy() msg = "\n ".join(f"{k}={v!r}" for k, v in sorted(self.env.items())) logger.debug("RUNENV:\n {}", msg) else: msg = "\n ".join( f"{self._key_diff(k)} {k}={self.env.get(k, '')!r}" for k in sorted(self.env.keys() | self._prev_env.keys()) if self._prev_env.get(k, None) != self.env.get(k, None) ) logger.debug("RUNENV - changes since last run only:\n {}", msg) type(self)._prev_env = self.env.copy() logger.info("RUN: {}", " ".join(options)) return subprocess.run( options, text=True, check=True, capture_output=capture, env=self.env, cwd=self.cwd, timeout=self.timeout, ) def _key_diff(self, k: str) -> str: assert self.env if k in self.env and k not in self._prev_env: return "+" if k in self._prev_env and k not in self.env: return "-" return " " def _fix_all_permissions(directory: str) -> None: """ Makes sure the write permission is set. Only run this on Windows. """ with os.scandir(directory) as it: for entry in it: if entry.is_dir(): _fix_all_permissions(entry.path) continue mode = stat.S_IMODE(entry.stat().st_mode) if not mode & stat.S_IWRITE: os.chmod(entry.path, mode | stat.S_IWRITE) # noqa: PTH101 scikit-build-core-0.11.1/src/scikit_build_core/_vendor/000077500000000000000000000000001477275177200230265ustar00rootroot00000000000000scikit-build-core-0.11.1/src/scikit_build_core/_vendor/pyproject_metadata/000077500000000000000000000000001477275177200267055ustar00rootroot00000000000000scikit-build-core-0.11.1/src/scikit_build_core/_vendor/pyproject_metadata/LICENSE000066400000000000000000000021311477275177200277070ustar00rootroot00000000000000Copyright © 2019 Filipe Laíns 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, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. 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. scikit-build-core-0.11.1/src/scikit_build_core/_vendor/pyproject_metadata/__init__.py000066400000000000000000000631331477275177200310240ustar00rootroot00000000000000# SPDX-License-Identifier: MIT """ This is pyproject_metadata, a library for working with PEP 621 metadata. Example usage: .. code-block:: python from pyproject_metadata import StandardMetadata metadata = StandardMetadata.from_pyproject( parsed_pyproject, allow_extra_keys=False, all_errors=True, metadata_version="2.3" ) pkg_info = metadata.as_rfc822() with open("METADATA", "wb") as f: f.write(pkg_info.as_bytes()) ep = self.metadata.entrypoints.copy() ep["console_scripts"] = self.metadata.scripts ep["gui_scripts"] = self.metadata.gui_scripts for group, entries in ep.items(): if entries: with open("entry_points.txt", "w", encoding="utf-8") as f: print(f"[{group}]", file=f) for name, target in entries.items(): print(f"{name} = {target}", file=f) print(file=f) """ from __future__ import annotations import copy import dataclasses import email.message import email.policy import email.utils import os import os.path import pathlib import sys import typing import warnings # Build backends may vendor this package, so all imports are relative. from . import constants from .errors import ConfigurationError, ConfigurationWarning, ErrorCollector from .pyproject import License, PyProjectReader, Readme if typing.TYPE_CHECKING: from collections.abc import Mapping from typing import Any from packaging.requirements import Requirement if sys.version_info < (3, 11): from typing_extensions import Self else: from typing import Self from .project_table import Dynamic, PyProjectTable import packaging.markers import packaging.specifiers import packaging.utils import packaging.version if sys.version_info < (3, 12, 4): import re RE_EOL_STR = re.compile(r"[\r\n]+") RE_EOL_BYTES = re.compile(rb"[\r\n]+") __version__ = "0.9.1" __all__ = [ "ConfigurationError", "License", "RFC822Message", "RFC822Policy", "Readme", "StandardMetadata", "extras_build_system", "extras_project", "extras_top_level", "field_to_metadata", ] def __dir__() -> list[str]: return __all__ def field_to_metadata(field: str) -> frozenset[str]: """ Return the METADATA fields that correspond to a project field. """ return frozenset(constants.PROJECT_TO_METADATA[field]) def extras_top_level(pyproject_table: Mapping[str, Any]) -> set[str]: """ Return any extra keys in the top-level of the pyproject table. """ return set(pyproject_table) - constants.KNOWN_TOPLEVEL_FIELDS def extras_build_system(pyproject_table: Mapping[str, Any]) -> set[str]: """ Return any extra keys in the build-system table. """ return ( set(pyproject_table.get("build-system", [])) - constants.KNOWN_BUILD_SYSTEM_FIELDS ) def extras_project(pyproject_table: Mapping[str, Any]) -> set[str]: """ Return any extra keys in the project table. """ return set(pyproject_table.get("project", [])) - constants.KNOWN_PROJECT_FIELDS @dataclasses.dataclass class _SmartMessageSetter: """ This provides a nice internal API for setting values in an Message to reduce boilerplate. If a value is None, do nothing. """ message: email.message.Message def __setitem__(self, name: str, value: str | None) -> None: if not value: return self.message[name] = value def set_payload(self, payload: str) -> None: self.message.set_payload(payload) @dataclasses.dataclass class _JSonMessageSetter: """ This provides an API to build a JSON message output in the same way as the classic Message. Line breaks are preserved this way. """ data: dict[str, str | list[str]] def __setitem__(self, name: str, value: str | None) -> None: name = name.lower() key = name.replace("-", "_") if value is None: return if name == "keywords": values = (x.strip() for x in value.split(",")) self.data[key] = [x for x in values if x] elif name in constants.KNOWN_MULTIUSE: entry = self.data.setdefault(key, []) assert isinstance(entry, list) entry.append(value) else: self.data[key] = value def set_payload(self, payload: str) -> None: self["description"] = payload class RFC822Policy(email.policy.EmailPolicy): """ This is :class:`email.policy.EmailPolicy`, but with a simple ``header_store_parse`` implementation that handles multiline values, and some nice defaults. """ utf8 = True mangle_from_ = False max_line_length = 0 def header_store_parse(self, name: str, value: str) -> tuple[str, str]: if name.lower() not in constants.KNOWN_METADATA_FIELDS: msg = f"Unknown field {name!r}" raise ConfigurationError(msg, key=name) size = len(name) + 2 value = value.replace("\n", "\n" + " " * size) return (name, value) if sys.version_info < (3, 12, 4): # Work around Python bug https://github.com/python/cpython/issues/117313 def _fold( self, name: str, value: Any, refold_binary: bool = False ) -> str: # pragma: no cover if hasattr(value, "name"): return value.fold(policy=self) # type: ignore[no-any-return] maxlen = self.max_line_length if self.max_line_length else sys.maxsize # this is from the library version, and it improperly breaks on chars like 0x0c, treating # them as 'form feed' etc. # we need to ensure that only CR/LF is used as end of line # lines = value.splitlines() # this is a workaround which splits only on CR/LF characters if isinstance(value, bytes): lines = RE_EOL_BYTES.split(value) else: lines = RE_EOL_STR.split(value) refold = self.refold_source == "all" or ( self.refold_source == "long" and ( (lines and len(lines[0]) + len(name) + 2 > maxlen) or any(len(x) > maxlen for x in lines[1:]) ) ) if refold or (refold_binary and email.policy._has_surrogates(value)): # type: ignore[attr-defined] return self.header_factory(name, "".join(lines)).fold(policy=self) # type: ignore[arg-type,no-any-return] return name + ": " + self.linesep.join(lines) + self.linesep # type: ignore[arg-type] class RFC822Message(email.message.EmailMessage): """ This is :class:`email.message.EmailMessage` with two small changes: it defaults to our `RFC822Policy`, and it correctly writes unicode when being called with `bytes()`. """ def __init__(self) -> None: super().__init__(policy=RFC822Policy()) def as_bytes( self, unixfrom: bool = False, policy: email.policy.Policy | None = None ) -> bytes: """ This handles unicode encoding. """ return self.as_string(unixfrom, policy=policy).encode("utf-8") @dataclasses.dataclass class StandardMetadata: """ This class represents the standard metadata fields for a project. It can be used to read metadata from a pyproject.toml table, validate it, and write it to an RFC822 message or JSON. """ name: str version: packaging.version.Version | None = None description: str | None = None license: License | str | None = None license_files: list[pathlib.Path] | None = None readme: Readme | None = None requires_python: packaging.specifiers.SpecifierSet | None = None dependencies: list[Requirement] = dataclasses.field(default_factory=list) optional_dependencies: dict[str, list[Requirement]] = dataclasses.field( default_factory=dict ) entrypoints: dict[str, dict[str, str]] = dataclasses.field(default_factory=dict) authors: list[tuple[str, str | None]] = dataclasses.field(default_factory=list) maintainers: list[tuple[str, str | None]] = dataclasses.field(default_factory=list) urls: dict[str, str] = dataclasses.field(default_factory=dict) classifiers: list[str] = dataclasses.field(default_factory=list) keywords: list[str] = dataclasses.field(default_factory=list) scripts: dict[str, str] = dataclasses.field(default_factory=dict) gui_scripts: dict[str, str] = dataclasses.field(default_factory=dict) dynamic: list[Dynamic] = dataclasses.field(default_factory=list) """ This field is used to track dynamic fields. You can't set a field not in this list. """ dynamic_metadata: list[str] = dataclasses.field(default_factory=list) """ This is a list of METADATA fields that can change in between SDist and wheel. Requires metadata_version 2.2+. """ metadata_version: str | None = None """ This is the target metadata version. If None, it will be computed as a minimum based on the fields set. """ all_errors: bool = False """ If True, all errors will be collected and raised in an ExceptionGroup. """ def __post_init__(self) -> None: self.validate() @property def auto_metadata_version(self) -> str: """ This computes the metadata version based on the fields set in the object if ``metadata_version`` is None. """ if self.metadata_version is not None: return self.metadata_version if isinstance(self.license, str) or self.license_files is not None: return "2.4" if self.dynamic_metadata: return "2.2" return "2.1" @property def canonical_name(self) -> str: """ Return the canonical name of the project. """ return packaging.utils.canonicalize_name(self.name) @classmethod def from_pyproject( # noqa: C901 cls, data: Mapping[str, Any], project_dir: str | os.PathLike[str] = os.path.curdir, metadata_version: str | None = None, dynamic_metadata: list[str] | None = None, *, allow_extra_keys: bool | None = None, all_errors: bool = False, ) -> Self: """ Read metadata from a pyproject.toml table. This is the main method for creating an instance of this class. It also supports two additional fields: ``allow_extra_keys`` to control what happens when extra keys are present in the pyproject table, and ``all_errors``, to raise all errors in an ExceptionGroup instead of raising the first one. """ pyproject = PyProjectReader(collect_errors=all_errors) pyproject_table: PyProjectTable = data # type: ignore[assignment] if "project" not in pyproject_table: msg = "Section {key} missing in pyproject.toml" pyproject.config_error(msg, key="project") pyproject.finalize("Failed to parse pyproject.toml") msg = "Unreachable code" # pragma: no cover raise AssertionError(msg) # pragma: no cover project = pyproject_table["project"] project_dir = pathlib.Path(project_dir) if not allow_extra_keys: extra_keys = extras_project(data) if extra_keys: extra_keys_str = ", ".join(sorted(f"{k!r}" for k in extra_keys)) msg = "Extra keys present in {key}: {extra_keys}" pyproject.config_error( msg, key="project", extra_keys=extra_keys_str, warn=allow_extra_keys is None, ) dynamic = pyproject.get_dynamic(project) for field in dynamic: if field in data["project"]: msg = 'Field {key} declared as dynamic in "project.dynamic" but is defined' pyproject.config_error(msg, key=f"project.{field}") raw_name = project.get("name") name = "UNKNOWN" if raw_name is None: msg = "Field {key} missing" pyproject.config_error(msg, key="project.name") else: tmp_name = pyproject.ensure_str(raw_name, "project.name") if tmp_name is not None: name = tmp_name version: packaging.version.Version | None = packaging.version.Version("0.0.0") raw_version = project.get("version") if raw_version is not None: version_string = pyproject.ensure_str(raw_version, "project.version") if version_string is not None: try: version = ( packaging.version.Version(version_string) if version_string else None ) except packaging.version.InvalidVersion: msg = "Invalid {key} value, expecting a valid PEP 440 version" pyproject.config_error( msg, key="project.version", got=version_string ) elif "version" not in dynamic: msg = ( "Field {key} missing and 'version' not specified in \"project.dynamic\"" ) pyproject.config_error(msg, key="project.version") # Description fills Summary, which cannot be multiline # However, throwing an error isn't backward compatible, # so leave it up to the users for now. project_description_raw = project.get("description") description = ( pyproject.ensure_str(project_description_raw, "project.description") if project_description_raw is not None else None ) requires_python_raw = project.get("requires-python") requires_python = None if requires_python_raw is not None: requires_python_string = pyproject.ensure_str( requires_python_raw, "project.requires-python" ) if requires_python_string is not None: try: requires_python = packaging.specifiers.SpecifierSet( requires_python_string ) except packaging.specifiers.InvalidSpecifier: msg = "Invalid {key} value, expecting a valid specifier set" pyproject.config_error( msg, key="project.requires-python", got=requires_python_string ) self = None with pyproject.collect(): self = cls( name=name, version=version, description=description, license=pyproject.get_license(project, project_dir), license_files=pyproject.get_license_files(project, project_dir), readme=pyproject.get_readme(project, project_dir), requires_python=requires_python, dependencies=pyproject.get_dependencies(project), optional_dependencies=pyproject.get_optional_dependencies(project), entrypoints=pyproject.get_entrypoints(project), authors=pyproject.ensure_people( project.get("authors", []), "project.authors" ), maintainers=pyproject.ensure_people( project.get("maintainers", []), "project.maintainers" ), urls=pyproject.ensure_dict(project.get("urls", {}), "project.urls") or {}, classifiers=pyproject.ensure_list( project.get("classifiers", []), "project.classifiers" ) or [], keywords=pyproject.ensure_list( project.get("keywords", []), "project.keywords" ) or [], scripts=pyproject.ensure_dict( project.get("scripts", {}), "project.scripts" ) or {}, gui_scripts=pyproject.ensure_dict( project.get("gui-scripts", {}), "project.gui-scripts" ) or {}, dynamic=dynamic, dynamic_metadata=dynamic_metadata or [], metadata_version=metadata_version, all_errors=all_errors, ) pyproject.finalize("Failed to parse pyproject.toml") assert self is not None return self def as_rfc822(self) -> RFC822Message: """ Return an RFC822 message with the metadata. """ message = RFC822Message() smart_message = _SmartMessageSetter(message) self._write_metadata(smart_message) return message def as_json(self) -> dict[str, str | list[str]]: """ Return a JSON message with the metadata. """ message: dict[str, str | list[str]] = {} smart_message = _JSonMessageSetter(message) self._write_metadata(smart_message) return message def validate(self, *, warn: bool = True) -> None: # noqa: C901 """ Validate metadata for consistency and correctness. Will also produce warnings if ``warn`` is given. Respects ``all_errors``. This is called when loading a pyproject.toml, and when making metadata. Checks: - ``metadata_version`` is a known version or None - ``name`` is a valid project name - ``license_files`` can't be used with classic ``license`` - License classifiers can't be used with SPDX license - ``description`` is a single line (warning) - ``license`` is not an SPDX license expression if metadata_version >= 2.4 (warning) - License classifiers deprecated for metadata_version >= 2.4 (warning) - ``license`` is an SPDX license expression if metadata_version >= 2.4 - ``license_files`` is supported only for metadata_version >= 2.4 - ``project_url`` can't contain keys over 32 characters """ errors = ErrorCollector(collect_errors=self.all_errors) if self.auto_metadata_version not in constants.KNOWN_METADATA_VERSIONS: msg = "The metadata_version must be one of {versions} or None (default)" errors.config_error(msg, versions=constants.KNOWN_METADATA_VERSIONS) try: packaging.utils.canonicalize_name(self.name, validate=True) except packaging.utils.InvalidName: msg = ( "Invalid project name {name!r}. A valid name consists only of ASCII letters and " "numbers, period, underscore and hyphen. It must start and end with a letter or number" ) errors.config_error(msg, key="project.name", name=self.name) if self.license_files is not None and isinstance(self.license, License): msg = '{key} must not be used when "project.license" is not a SPDX license expression' errors.config_error(msg, key="project.license-files") if isinstance(self.license, str) and any( c.startswith("License ::") for c in self.classifiers ): msg = "Setting {key} to an SPDX license expression is not compatible with 'License ::' classifiers" errors.config_error(msg, key="project.license") if warn: if self.description and "\n" in self.description: warnings.warn( 'The one-line summary "project.description" should not contain more than one line. Readers might merge or truncate newlines.', ConfigurationWarning, stacklevel=2, ) if self.auto_metadata_version not in constants.PRE_SPDX_METADATA_VERSIONS: if isinstance(self.license, License): warnings.warn( 'Set "project.license" to an SPDX license expression for metadata >= 2.4', ConfigurationWarning, stacklevel=2, ) elif any(c.startswith("License ::") for c in self.classifiers): warnings.warn( "'License ::' classifiers are deprecated for metadata >= 2.4, use a SPDX license expression for \"project.license\" instead", ConfigurationWarning, stacklevel=2, ) if ( isinstance(self.license, str) and self.auto_metadata_version in constants.PRE_SPDX_METADATA_VERSIONS ): msg = "Setting {key} to an SPDX license expression is supported only when emitting metadata version >= 2.4" errors.config_error(msg, key="project.license") if ( self.license_files is not None and self.auto_metadata_version in constants.PRE_SPDX_METADATA_VERSIONS ): msg = "{key} is supported only when emitting metadata version >= 2.4" errors.config_error(msg, key="project.license-files") for name in self.urls: if len(name) > 32: msg = "{key} names cannot be more than 32 characters long" errors.config_error(msg, key="project.urls", got=name) errors.finalize("Metadata validation failed") def _write_metadata( # noqa: C901 self, smart_message: _SmartMessageSetter | _JSonMessageSetter ) -> None: """ Write the metadata to the message. Handles JSON or Message. """ errors = ErrorCollector(collect_errors=self.all_errors) with errors.collect(): self.validate(warn=False) smart_message["Metadata-Version"] = self.auto_metadata_version smart_message["Name"] = self.name if not self.version: msg = "Field {key} missing" errors.config_error(msg, key="project.version") smart_message["Version"] = str(self.version) # skip 'Platform' # skip 'Supported-Platform' if self.description: smart_message["Summary"] = self.description smart_message["Keywords"] = ",".join(self.keywords) or None # skip 'Home-page' # skip 'Download-URL' smart_message["Author"] = _name_list(self.authors) smart_message["Author-Email"] = _email_list(self.authors) smart_message["Maintainer"] = _name_list(self.maintainers) smart_message["Maintainer-Email"] = _email_list(self.maintainers) if isinstance(self.license, License): smart_message["License"] = self.license.text elif isinstance(self.license, str): smart_message["License-Expression"] = self.license if self.license_files is not None: for license_file in sorted(set(self.license_files)): smart_message["License-File"] = os.fspath(license_file.as_posix()) elif ( self.auto_metadata_version not in constants.PRE_SPDX_METADATA_VERSIONS and isinstance(self.license, License) and self.license.file ): smart_message["License-File"] = os.fspath(self.license.file.as_posix()) for classifier in self.classifiers: smart_message["Classifier"] = classifier # skip 'Provides-Dist' # skip 'Obsoletes-Dist' # skip 'Requires-External' for name, url in self.urls.items(): smart_message["Project-URL"] = f"{name}, {url}" if self.requires_python: smart_message["Requires-Python"] = str(self.requires_python) for dep in self.dependencies: smart_message["Requires-Dist"] = str(dep) for extra, requirements in self.optional_dependencies.items(): norm_extra = extra.replace(".", "-").replace("_", "-").lower() smart_message["Provides-Extra"] = norm_extra for requirement in requirements: smart_message["Requires-Dist"] = str( _build_extra_req(norm_extra, requirement) ) if self.readme: if self.readme.content_type: smart_message["Description-Content-Type"] = self.readme.content_type smart_message.set_payload(self.readme.text) # Core Metadata 2.2 if self.auto_metadata_version != "2.1": for field in self.dynamic_metadata: if field.lower() in {"name", "version", "dynamic"}: msg = f"Metadata field {field!r} cannot be declared dynamic" errors.config_error(msg) if field.lower() not in constants.KNOWN_METADATA_FIELDS: msg = f"Unknown metadata field {field!r} cannot be declared dynamic" errors.config_error(msg) smart_message["Dynamic"] = field errors.finalize("Failed to write metadata") def _name_list(people: list[tuple[str, str | None]]) -> str | None: """ Build a comma-separated list of names. """ return ", ".join(name for name, email_ in people if not email_) or None def _email_list(people: list[tuple[str, str | None]]) -> str | None: """ Build a comma-separated list of emails. """ return ( ", ".join( email.utils.formataddr((name, _email)) for name, _email in people if _email ) or None ) def _build_extra_req( extra: str, requirement: Requirement, ) -> Requirement: """ Build a new requirement with an extra marker. """ requirement = copy.copy(requirement) if requirement.marker: if "or" in requirement.marker._markers: requirement.marker = packaging.markers.Marker( f"({requirement.marker}) and extra == {extra!r}" ) else: requirement.marker = packaging.markers.Marker( f"{requirement.marker} and extra == {extra!r}" ) else: requirement.marker = packaging.markers.Marker(f"extra == {extra!r}") return requirement scikit-build-core-0.11.1/src/scikit_build_core/_vendor/pyproject_metadata/constants.py000066400000000000000000000057711477275177200313050ustar00rootroot00000000000000# SPDX-License-Identifier: MIT """ Constants for the pyproject_metadata package, collected here to make them easy to update. These should be considered mostly private. """ from __future__ import annotations __all__ = [ "KNOWN_BUILD_SYSTEM_FIELDS", "KNOWN_METADATA_FIELDS", "KNOWN_METADATA_VERSIONS", "KNOWN_METADATA_VERSIONS", "KNOWN_MULTIUSE", "KNOWN_PROJECT_FIELDS", "KNOWN_TOPLEVEL_FIELDS", "PRE_SPDX_METADATA_VERSIONS", "PROJECT_TO_METADATA", ] def __dir__() -> list[str]: return __all__ KNOWN_METADATA_VERSIONS = {"2.1", "2.2", "2.3", "2.4"} PRE_SPDX_METADATA_VERSIONS = {"2.1", "2.2", "2.3"} PROJECT_TO_METADATA = { "authors": frozenset(["Author", "Author-Email"]), "classifiers": frozenset(["Classifier"]), "dependencies": frozenset(["Requires-Dist"]), "description": frozenset(["Summary"]), "dynamic": frozenset(), "entry-points": frozenset(), "gui-scripts": frozenset(), "keywords": frozenset(["Keywords"]), "license": frozenset(["License", "License-Expression"]), "license-files": frozenset(["License-File"]), "maintainers": frozenset(["Maintainer", "Maintainer-Email"]), "name": frozenset(["Name"]), "optional-dependencies": frozenset(["Provides-Extra", "Requires-Dist"]), "readme": frozenset(["Description", "Description-Content-Type"]), "requires-python": frozenset(["Requires-Python"]), "scripts": frozenset(), "urls": frozenset(["Project-URL"]), "version": frozenset(["Version"]), } KNOWN_TOPLEVEL_FIELDS = {"build-system", "project", "tool", "dependency-groups"} KNOWN_BUILD_SYSTEM_FIELDS = {"backend-path", "build-backend", "requires"} KNOWN_PROJECT_FIELDS = set(PROJECT_TO_METADATA) KNOWN_METADATA_FIELDS = { "author", "author-email", "classifier", "description", "description-content-type", "download-url", # Not specified via pyproject standards, deprecated by PEP 753 "dynamic", # Can't be in dynamic "home-page", # Not specified via pyproject standards, deprecated by PEP 753 "keywords", "license", "license-expression", "license-file", "maintainer", "maintainer-email", "metadata-version", "name", # Can't be in dynamic "obsoletes", # Deprecated "obsoletes-dist", # Rarely used "platform", # Not specified via pyproject standards "project-url", "provides", # Deprecated "provides-dist", # Rarely used "provides-extra", "requires", # Deprecated "requires-dist", "requires-external", # Not specified via pyproject standards "requires-python", "summary", "supported-platform", # Not specified via pyproject standards "version", # Can't be in dynamic } KNOWN_MULTIUSE = { "dynamic", "platform", "provides-extra", "supported-platform", "license-file", "classifier", "requires-dist", "requires-external", "project-url", "provides-dist", "obsoletes-dist", "requires", # Deprecated "obsoletes", # Deprecated "provides", # Deprecated } scikit-build-core-0.11.1/src/scikit_build_core/_vendor/pyproject_metadata/errors.py000066400000000000000000000066341477275177200306040ustar00rootroot00000000000000# SPDX-License-Identifier: MIT """ This module defines exceptions and error handling utilities. It is the recommend path to access ``ConfiguratonError``, ``ConfigurationWarning``, and ``ExceptionGroup``. For backward compatibility, ``ConfigurationError`` is re-exported in the top-level package. """ from __future__ import annotations import builtins import contextlib import dataclasses import sys import typing import warnings __all__ = [ "ConfigurationError", "ConfigurationWarning", "ExceptionGroup", ] def __dir__() -> list[str]: return __all__ class ConfigurationError(Exception): """Error in the backend metadata. Has an optional key attribute, which will be non-None if the error is related to a single key in the pyproject.toml file.""" def __init__(self, msg: str, *, key: str | None = None): super().__init__(msg) self._key = key @property def key(self) -> str | None: # pragma: no cover return self._key class ConfigurationWarning(UserWarning): """Warnings about backend metadata.""" if sys.version_info >= (3, 11): ExceptionGroup = builtins.ExceptionGroup else: class ExceptionGroup(Exception): """A minimal implementation of `ExceptionGroup` from Python 3.11. Users can replace this with a more complete implementation, such as from the exceptiongroup backport package, if better error messages and integration with tooling is desired and the addition of a dependency is acceptable. """ message: str exceptions: list[Exception] def __init__(self, message: str, exceptions: list[Exception]) -> None: self.message = message self.exceptions = exceptions def __repr__(self) -> str: return f"{self.__class__.__name__}({self.message!r}, {self.exceptions!r})" @dataclasses.dataclass class ErrorCollector: """ Collect errors and raise them as a group at the end (if collect_errors is True), otherwise raise them immediately. """ collect_errors: bool errors: list[Exception] = dataclasses.field(default_factory=list) def config_error( self, msg: str, *, key: str | None = None, got: typing.Any = None, got_type: type[typing.Any] | None = None, warn: bool = False, **kwargs: typing.Any, ) -> None: """Raise a configuration error, or add it to the error list.""" msg = msg.format(key=f'"{key}"', **kwargs) if got is not None: msg = f"{msg} (got {got!r})" if got_type is not None: msg = f"{msg} (got {got_type.__name__})" if warn: warnings.warn(msg, ConfigurationWarning, stacklevel=3) elif self.collect_errors: self.errors.append(ConfigurationError(msg, key=key)) else: raise ConfigurationError(msg, key=key) def finalize(self, msg: str) -> None: """Raise a group exception if there are any errors.""" if self.errors: raise ExceptionGroup(msg, self.errors) @contextlib.contextmanager def collect(self) -> typing.Generator[None, None, None]: """Support nesting; add any grouped errors to the error list.""" if self.collect_errors: try: yield except ExceptionGroup as error: self.errors.extend(error.exceptions) else: yield scikit-build-core-0.11.1/src/scikit_build_core/_vendor/pyproject_metadata/project_table.py000066400000000000000000000066121477275177200321010ustar00rootroot00000000000000# SPDX-License-Identifier: MIT """ This module contains type definitions for the tables used in the ``pyproject.toml``. You should either import this at type-check time only, or make sure ``typing_extensions`` is available for Python 3.10 and below. Documentation notice: the fields with hyphens are not shown due to a sphinx-autodoc bug. """ from __future__ import annotations import sys import typing from typing import Any, Dict, List, Union if sys.version_info < (3, 11): from typing_extensions import Required else: from typing import Required if sys.version_info < (3, 8): from typing_extensions import Literal, TypedDict else: from typing import Literal, TypedDict __all__ = [ "BuildSystemTable", "ContactTable", "Dynamic", "IncludeGroupTable", "LicenseTable", "ProjectTable", "PyProjectTable", "ReadmeTable", ] def __dir__() -> list[str]: return __all__ class ContactTable(TypedDict, total=False): name: str email: str class LicenseTable(TypedDict, total=False): text: str file: str ReadmeTable = TypedDict( "ReadmeTable", {"file": str, "text": str, "content-type": str}, total=False ) Dynamic = Literal[ "authors", "classifiers", "dependencies", "description", "dynamic", "entry-points", "gui-scripts", "keywords", "license", "maintainers", "optional-dependencies", "readme", "requires-python", "scripts", "urls", "version", ] ProjectTable = TypedDict( "ProjectTable", { "name": Required[str], "version": str, "description": str, "license": Union[LicenseTable, str], "license-files": List[str], "readme": Union[str, ReadmeTable], "requires-python": str, "dependencies": List[str], "optional-dependencies": Dict[str, List[str]], "entry-points": Dict[str, Dict[str, str]], "authors": List[ContactTable], "maintainers": List[ContactTable], "urls": Dict[str, str], "classifiers": List[str], "keywords": List[str], "scripts": Dict[str, str], "gui-scripts": Dict[str, str], "dynamic": List[Dynamic], }, total=False, ) BuildSystemTable = TypedDict( "BuildSystemTable", { "build-backend": str, "requires": List[str], "backend-path": List[str], }, total=False, ) # total=False here because this could be # extended in the future IncludeGroupTable = TypedDict( "IncludeGroupTable", {"include-group": str}, total=False, ) PyProjectTable = TypedDict( "PyProjectTable", { "build-system": BuildSystemTable, "project": ProjectTable, "tool": Dict[str, Any], "dependency-groups": Dict[str, List[Union[str, IncludeGroupTable]]], }, total=False, ) # Tests for type checking if typing.TYPE_CHECKING: PyProjectTable( { "build-system": BuildSystemTable( {"build-backend": "one", "requires": ["two"]} ), "project": ProjectTable( { "name": "one", "version": "0.1.0", } ), "tool": {"thing": object()}, "dependency-groups": { "one": [ "one", IncludeGroupTable({"include-group": "two"}), ] }, } ) scikit-build-core-0.11.1/src/scikit_build_core/_vendor/pyproject_metadata/py.typed000066400000000000000000000000001477275177200303720ustar00rootroot00000000000000scikit-build-core-0.11.1/src/scikit_build_core/_vendor/pyproject_metadata/pyproject.py000066400000000000000000000420341477275177200313010ustar00rootroot00000000000000# SPDX-License-Identifier: MIT """ This module focues on reading pyproject.toml fields with error collection. It is mostly internal, except for License and Readme classes, which are re-exported in the top-level package. """ from __future__ import annotations import dataclasses import pathlib import re import typing import packaging.requirements from .errors import ErrorCollector if typing.TYPE_CHECKING: from collections.abc import Generator, Iterable, Sequence from packaging.requirements import Requirement from .project_table import ContactTable, Dynamic, ProjectTable __all__ = [ "License", "Readme", ] def __dir__() -> list[str]: return __all__ @dataclasses.dataclass(frozen=True) class License: """ This represents a classic license, which contains text, and optionally a file path. Modern licenses are just SPDX identifiers, which are strings. """ text: str file: pathlib.Path | None @dataclasses.dataclass(frozen=True) class Readme: """ This represents a readme, which contains text and a content type, and optionally a file path. """ text: str file: pathlib.Path | None content_type: str T = typing.TypeVar("T") @dataclasses.dataclass class PyProjectReader(ErrorCollector): """Class for reading pyproject.toml fields with error collection. Unrelated errors are collected and raised at once if the `collect_errors` parameter is set to `True`. Some methods will return None if an error was raised. Most of them expect a non-None value as input to enforce the caller to handle missing vs. error correctly. The exact design is based on usage, as this is an internal class. """ def ensure_str(self, value: str, key: str) -> str | None: """Ensure that a value is a string.""" if isinstance(value, str): return value msg = "Field {key} has an invalid type, expecting a string" self.config_error(msg, key=key, got_type=type(value)) return None def ensure_list(self, val: list[T], key: str) -> list[T] | None: """Ensure that a value is a list of strings.""" if not isinstance(val, list): msg = "Field {key} has an invalid type, expecting a list of strings" self.config_error(msg, key=key, got_type=type(val)) return None for item in val: if not isinstance(item, str): msg = "Field {key} contains item with invalid type, expecting a string" self.config_error(msg, key=key, got_type=type(item)) return None return val def ensure_dict(self, val: dict[str, str], key: str) -> dict[str, str] | None: """Ensure that a value is a dictionary of strings.""" if not isinstance(val, dict): msg = "Field {key} has an invalid type, expecting a table of strings" self.config_error(msg, key=key, got_type=type(val)) return None for subkey, item in val.items(): if not isinstance(item, str): msg = "Field {key} has an invalid type, expecting a string" self.config_error(msg, key=f"{key}.{subkey}", got_type=type(item)) return None return val def ensure_people( self, val: Sequence[ContactTable], key: str ) -> list[tuple[str, str | None]]: """Ensure that a value is a list of tables with optional "name" and "email" keys.""" if not isinstance(val, list): msg = ( "Field {key} has an invalid type, expecting a list of " 'tables containing the "name" and/or "email" keys' ) self.config_error(msg, key=key, got_type=type(val)) return [] for each in val: if not isinstance(each, dict): msg = ( "Field {key} has an invalid type, expecting a list of " 'tables containing the "name" and/or "email" keys' " (got list with {type_name})" ) self.config_error(msg, key=key, type_name=type(each).__name__) return [] for value in each.values(): if not isinstance(value, str): msg = ( "Field {key} has an invalid type, expecting a list of " 'tables containing the "name" and/or "email" keys' " (got list with dict with {type_name})" ) self.config_error(msg, key=key, type_name=type(value).__name__) return [] extra_keys = set(each) - {"name", "email"} if extra_keys: msg = ( "Field {key} has an invalid type, expecting a list of " 'tables containing the "name" and/or "email" keys' " (got list with dict with extra keys {extra_keys})" ) self.config_error( msg, key=key, extra_keys=", ".join(sorted(f'"{k}"' for k in extra_keys)), ) return [] return [(entry.get("name", "Unknown"), entry.get("email")) for entry in val] def get_license( self, project: ProjectTable, project_dir: pathlib.Path ) -> License | str | None: """Get the license field from the project table. Handles PEP 639 style license too. None is returned if the license field is not present or if an error occurred. """ val = project.get("license") if val is None: return None if isinstance(val, str): return val if isinstance(val, dict): _license = self.ensure_dict(val, "project.license") # type: ignore[arg-type] if _license is None: return None else: msg = "Field {key} has an invalid type, expecting a string or table of strings" self.config_error(msg, key="project.license", got_type=type(val)) return None for field in _license: if field not in ("file", "text"): msg = "Unexpected field {key}" self.config_error(msg, key=f"project.license.{field}") return None file: pathlib.Path | None = None filename = _license.get("file") text = _license.get("text") if (filename and text) or (not filename and not text): msg = ( 'Invalid {key} contents, expecting a string or one key "file" or "text"' ) self.config_error(msg, key="project.license", got=_license) return None if filename: file = project_dir.joinpath(filename) if not file.is_file(): msg = f"License file not found ({filename!r})" self.config_error(msg, key="project.license.file") return None text = file.read_text(encoding="utf-8") assert text is not None return License(text, file) def get_license_files( self, project: ProjectTable, project_dir: pathlib.Path ) -> list[pathlib.Path] | None: """Get the license-files list of files from the project table. Returns None if an error occurred (including invalid globs, etc) or if not present. """ license_files = project.get("license-files") if license_files is None: return None if self.ensure_list(license_files, "project.license-files") is None: return None return list(self._get_files_from_globs(project_dir, license_files)) def get_readme( # noqa: C901 self, project: ProjectTable, project_dir: pathlib.Path ) -> Readme | None: """Get the text of the readme from the project table. Returns None if an error occurred or if the readme field is not present. """ if "readme" not in project: return None filename: str | None = None file: pathlib.Path | None = None text: str | None = None content_type: str | None = None readme = project["readme"] if isinstance(readme, str): # readme is a file text = None filename = readme if filename.endswith(".md"): content_type = "text/markdown" elif filename.endswith(".rst"): content_type = "text/x-rst" else: msg = "Could not infer content type for readme file {filename!r}" self.config_error(msg, key="project.readme", filename=filename) return None elif isinstance(readme, dict): # readme is a dict containing either 'file' or 'text', and content-type for field in readme: if field not in ("content-type", "file", "text"): msg = "Unexpected field {key}" self.config_error(msg, key=f"project.readme.{field}") return None content_type_raw = readme.get("content-type") if content_type_raw is not None: content_type = self.ensure_str( content_type_raw, "project.readme.content-type" ) if content_type is None: return None filename_raw = readme.get("file") if filename_raw is not None: filename = self.ensure_str(filename_raw, "project.readme.file") if filename is None: return None text_raw = readme.get("text") if text_raw is not None: text = self.ensure_str(text_raw, "project.readme.text") if text is None: return None if (filename and text) or (not filename and not text): msg = 'Invalid {key} contents, expecting either "file" or "text"' self.config_error(msg, key="project.readme", got=readme) return None if not content_type: msg = "Field {key} missing" self.config_error(msg, key="project.readme.content-type") return None else: msg = "Field {key} has an invalid type, expecting either a string or table of strings" self.config_error(msg, key="project.readme", got_type=type(readme)) return None if filename: file = project_dir.joinpath(filename) if not file.is_file(): msg = "Readme file not found ({filename!r})" self.config_error(msg, key="project.readme.file", filename=filename) return None text = file.read_text(encoding="utf-8") assert text is not None return Readme(text, file, content_type) def get_dependencies(self, project: ProjectTable) -> list[Requirement]: """Get the dependencies from the project table.""" requirement_strings: list[str] | None = None requirement_strings_raw = project.get("dependencies") if requirement_strings_raw is not None: requirement_strings = self.ensure_list( requirement_strings_raw, "project.dependencies" ) if requirement_strings is None: return [] requirements: list[Requirement] = [] for req in requirement_strings: try: requirements.append(packaging.requirements.Requirement(req)) except packaging.requirements.InvalidRequirement as e: msg = "Field {key} contains an invalid PEP 508 requirement string {req!r} ({error!r})" self.config_error(msg, key="project.dependencies", req=req, error=e) return [] return requirements def get_optional_dependencies( self, project: ProjectTable, ) -> dict[str, list[Requirement]]: """Get the optional dependencies from the project table.""" val = project.get("optional-dependencies") if not val: return {} requirements_dict: dict[str, list[Requirement]] = {} if not isinstance(val, dict): msg = "Field {key} has an invalid type, expecting a table of PEP 508 requirement strings" self.config_error( msg, key="project.optional-dependencies", got_type=type(val) ) return {} for extra, requirements in val.copy().items(): assert isinstance(extra, str) if not isinstance(requirements, list): msg = "Field {key} has an invalid type, expecting a table of PEP 508 requirement strings" self.config_error( msg, key=f"project.optional-dependencies.{extra}", got_type=type(requirements), ) return {} requirements_dict[extra] = [] for req in requirements: if not isinstance(req, str): msg = "Field {key} has an invalid type, expecting a PEP 508 requirement string" self.config_error( msg, key=f"project.optional-dependencies.{extra}", got_type=type(req), ) return {} try: requirements_dict[extra].append( packaging.requirements.Requirement(req) ) except packaging.requirements.InvalidRequirement as e: msg = ( "Field {key} contains " "an invalid PEP 508 requirement string {req!r} ({error!r})" ) self.config_error( msg, key=f"project.optional-dependencies.{extra}", req=req, error=e, ) return {} return dict(requirements_dict) def get_entrypoints(self, project: ProjectTable) -> dict[str, dict[str, str]]: """Get the entrypoints from the project table.""" val = project.get("entry-points", None) if val is None: return {} if not isinstance(val, dict): msg = "Field {key} has an invalid type, expecting a table of entrypoint sections" self.config_error(msg, key="project.entry-points", got_type=type(val)) return {} for section, entrypoints in val.items(): assert isinstance(section, str) if not re.match(r"^\w+(\.\w+)*$", section): msg = ( "Field {key} has an invalid value, expecting a name " "containing only alphanumeric, underscore, or dot characters" ) self.config_error(msg, key="project.entry-points", got=section) return {} if not isinstance(entrypoints, dict): msg = ( "Field {key} has an invalid type, expecting a table of entrypoints" ) self.config_error( msg, key=f"project.entry-points.{section}", got_type=type(entrypoints), ) return {} for name, entrypoint in entrypoints.items(): assert isinstance(name, str) if not isinstance(entrypoint, str): msg = "Field {key} has an invalid type, expecting a string" self.config_error( msg, key=f"project.entry-points.{section}.{name}", got_type=type(entrypoint), ) return {} return val def get_dynamic(self, project: ProjectTable) -> list[Dynamic]: """Get the dynamic fields from the project table. Returns an empty list if the field is not present or if an error occurred. """ dynamic = project.get("dynamic", []) self.ensure_list(dynamic, "project.dynamic") if "name" in dynamic: msg = "Unsupported field 'name' in {key}" self.config_error(msg, key="project.dynamic") return [] return dynamic def _get_files_from_globs( self, project_dir: pathlib.Path, globs: Iterable[str] ) -> Generator[pathlib.Path, None, None]: """Given a list of globs, get files that match.""" for glob in globs: if glob.startswith(("..", "/")): msg = "{glob!r} is an invalid {key} glob: the pattern must match files within the project directory" self.config_error(msg, key="project.license-files", glob=glob) break files = [f for f in project_dir.glob(glob) if f.is_file()] if not files: msg = "Every pattern in {key} must match at least one file: {glob!r} did not match any" self.config_error(msg, key="project.license-files", glob=glob) break for f in files: yield f.relative_to(project_dir) scikit-build-core-0.11.1/src/scikit_build_core/_version.pyi000066400000000000000000000004461477275177200237450ustar00rootroot00000000000000version: str # pylint: disable-next=unsupported-binary-operation version_tuple: tuple[int, int, int, str, str] | tuple[int, int, int] # Note that newer versions of setuptools_scm also add __version__, but we are # not forcing new versions of setuptools_scm, so only these imports are allowed. scikit-build-core-0.11.1/src/scikit_build_core/ast/000077500000000000000000000000001477275177200221615ustar00rootroot00000000000000scikit-build-core-0.11.1/src/scikit_build_core/ast/__init__.py000066400000000000000000000000741477275177200242730ustar00rootroot00000000000000from __future__ import annotations __all__: list[str] = [] scikit-build-core-0.11.1/src/scikit_build_core/ast/ast.py000066400000000000000000000055441477275177200233320ustar00rootroot00000000000000from __future__ import annotations import dataclasses import sys from pathlib import Path from typing import TYPE_CHECKING from .._logging import rich_print from .tokenizer import Token, TokenType, tokenize if TYPE_CHECKING: from collections.abc import Generator __all__ = ["Block", "Node", "parse"] def __dir__() -> list[str]: return __all__ @dataclasses.dataclass(frozen=True) class Node: __slots__ = ("name", "start", "stop", "value") name: str value: str start: int stop: int def __str__(self) -> str: return f"{self.name}({self.value})" @dataclasses.dataclass(frozen=True) class Block(Node): __slots__ = ("contents",) contents: list[Node] def __str__(self) -> str: return f"{super().__str__()} ... {len(self.contents)} children" def parse( tokens: Generator[Token, None, None], stop: str = "" ) -> Generator[Node, None, None]: """ Generate a stream of nodes from a stream of tokens. This currently bundles all block-like functions into a single `Block` node, but this could be changed to be more specific eventually if needed. """ try: while True: token = next(tokens) if token.type != TokenType.UNQUOTED: continue name = token.value.lower() start = token.start token = next(tokens) if token.type == TokenType.WHITESPACE: token = next(tokens) if token.type != TokenType.OPEN_PAREN: msg = f"Expected open paren after {name!r}, got {token!r}" raise AssertionError(msg) count = 1 value = "" while True: token = next(tokens) if token.type == TokenType.OPEN_PAREN: count += 1 elif token.type == TokenType.CLOSE_PAREN: count -= 1 if count == 0: break value += token.value if name in {"if", "foreach", "while", "macro", "function", "block"}: contents = list(parse(tokens, f"end{name}")) yield Block(name, value, start, contents[-1].stop, contents) else: yield Node(name, value, start, token.stop) if stop and name == stop: break except StopIteration: pass if __name__ == "__main__": with Path(sys.argv[1]).open(encoding="utf-8-sig") as f: for node in parse(tokenize(f.read())): node_name = node.name.replace("{", "{{").replace("}", "}}") node_value = node.value.replace("{", "{{").replace("}", "}}") cnode = dataclasses.replace( node, name=f"{{bold.blue}}{node_name}{{default}}", value=f"{{green}}{node_value}{{default}}", ) rich_print(cnode) scikit-build-core-0.11.1/src/scikit_build_core/ast/tokenizer.py000066400000000000000000000043171477275177200245520ustar00rootroot00000000000000from __future__ import annotations import dataclasses import enum import re import sys from pathlib import Path from typing import TYPE_CHECKING from .._logging import rich_print if TYPE_CHECKING: from collections.abc import Generator __all__ = ["Token", "TokenType", "tokenize"] def __dir__() -> list[str]: return __all__ TOKEN_EXPRS = { "BRACKET_COMMENT": r"\s*#\[(?P=*)\[(?s:.)*?\](?P=bc1)\]", "COMMENT": r"#.*$", "QUOTED": r'"(?:\\(?s:.)|[^"\\])*?"', "BRACKET_QUOTE": r"\[(?P=*)\[(?s:.)*?\](?P=bq1)\]", "OPEN_PAREN": r"\(", "CLOSE_PAREN": r"\)", "LEGACY": r'\b\w+=[^\s"()$\\]*(?:"[^"\\]*"[^\s"()$\\]*)*|"(?:[^"\\]*(?:\\.[^"\\]*)*)*"', "UNQUOTED": r"(?:\\.|[^\s()#\"\\])+", } class TokenType(enum.Enum): BRACKET_COMMENT = enum.auto() COMMENT = enum.auto() UNQUOTED = enum.auto() QUOTED = enum.auto() BRACKET_QUOTE = enum.auto() LEGACY = enum.auto() OPEN_PAREN = enum.auto() CLOSE_PAREN = enum.auto() WHITESPACE = enum.auto() @dataclasses.dataclass(frozen=True) class Token: __slots__ = ("start", "stop", "type", "value") type: TokenType start: int stop: int value: str def __str__(self) -> str: return f"{self.type.name}({self.value!r})" def tokenize(contents: str) -> Generator[Token, None, None]: tok_regex = "|".join(f"(?P<{n}>{v})" for n, v in TOKEN_EXPRS.items()) last = 0 for match in re.finditer(tok_regex, contents, re.MULTILINE): for typ, value in match.groupdict().items(): if typ in TOKEN_EXPRS and value is not None: if match.start() != last: yield Token( TokenType.WHITESPACE, last, match.start(), contents[last : match.start()], ) last = match.end() yield Token(TokenType[typ], match.start(), match.end(), value) if __name__ == "__main__": with Path(sys.argv[1]).open(encoding="utf-8-sig") as f: for token in tokenize(f.read()): rich_print( "{green}{token.type.name}{red}({default}{token.value}{red})", token=token, ) scikit-build-core-0.11.1/src/scikit_build_core/build/000077500000000000000000000000001477275177200224715ustar00rootroot00000000000000scikit-build-core-0.11.1/src/scikit_build_core/build/__init__.py000066400000000000000000000127071477275177200246110ustar00rootroot00000000000000""" This is the entry point for the build backend. Items in this module are designed for the build backend API. """ from __future__ import annotations import sys from .._compat import tomllib __all__ = [ "build_editable", "build_sdist", "build_wheel", "get_requires_for_build_editable", "get_requires_for_build_sdist", "get_requires_for_build_wheel", "prepare_metadata_for_build_editable", "prepare_metadata_for_build_wheel", ] def build_wheel( wheel_directory: str, config_settings: dict[str, list[str] | str] | None = None, metadata_directory: str | None = None, ) -> str: from .._logging import rich_print from ..errors import FailedLiveProcessError from .wheel import _build_wheel_impl try: return _build_wheel_impl( wheel_directory, config_settings, metadata_directory, editable=False, ).wheel_filename except FailedLiveProcessError as err: sys.stdout.flush() rich_print("\n{bold}***", *err.args, color="red", file=sys.stderr) if err.msg: rich_print(err.msg) raise SystemExit(1) from None def build_editable( wheel_directory: str, config_settings: dict[str, list[str] | str] | None = None, metadata_directory: str | None = None, ) -> str: from .._logging import rich_print from ..errors import FailedLiveProcessError from .wheel import _build_wheel_impl try: return _build_wheel_impl( wheel_directory, config_settings, metadata_directory, editable=True, ).wheel_filename except FailedLiveProcessError as err: sys.stdout.flush() rich_print("\n{bold}***", *err.args, color="red", file=sys.stderr) if err.msg: rich_print(err.msg) raise SystemExit(1) from None def _has_safe_metadata() -> bool: try: with open("pyproject.toml", "rb") as f: # noqa: PTH123 pyproject = tomllib.load(f) except FileNotFoundError: return True overrides = pyproject.get("tool", {}).get("scikit-build", {}).get("overrides", []) for override in overrides: if_override = override.get("if", {}) if "failed" in if_override or "failed" in if_override.get("any", {}): return False return True if _has_safe_metadata(): def prepare_metadata_for_build_wheel( metadata_directory: str, config_settings: dict[str, list[str] | str] | None = None, ) -> str: """Prepare metadata for building a wheel. Does not build the wheel. Returns the dist-info directory.""" from .wheel import _build_wheel_impl return _build_wheel_impl( None, config_settings, metadata_directory, editable=False ).wheel_filename # actually returns the dist-info directory def prepare_metadata_for_build_editable( metadata_directory: str, config_settings: dict[str, list[str] | str] | None = None, ) -> str: """Prepare metadata for building a wheel. Does not build the wheel. Returns the dist-info directory.""" from .wheel import _build_wheel_impl return _build_wheel_impl( None, config_settings, metadata_directory, editable=True ).wheel_filename # actually returns the dist-info directory __all__ += [ "prepare_metadata_for_build_editable", "prepare_metadata_for_build_wheel", ] def build_sdist( sdist_directory: str, config_settings: dict[str, list[str] | str] | None = None, ) -> str: from .._logging import rich_print from ..errors import FailedLiveProcessError from .sdist import build_sdist as skbuild_build_sdist try: return skbuild_build_sdist(sdist_directory, config_settings) except FailedLiveProcessError as err: sys.stdout.flush() rich_print("\n{bold}***", *err.args, color="red", file=sys.stderr) if err.msg: rich_print(err.msg) raise SystemExit(1) from None def get_requires_for_build_sdist( config_settings: dict[str, str | list[str]] | None = None, ) -> list[str]: from ..builder.get_requires import GetRequires requires = GetRequires.from_config_settings(config_settings) # These are only injected if cmake is required for the SDist step cmake_requires = ( [*requires.cmake(), *requires.ninja()] if requires.settings.sdist.cmake else [] ) return [ *cmake_requires, *requires.dynamic_metadata(), ] def get_requires_for_build_wheel( config_settings: dict[str, str | list[str]] | None = None, ) -> list[str]: from ..builder.get_requires import GetRequires requires = GetRequires.from_config_settings(config_settings) # These are only injected if cmake is required for the wheel step cmake_requires = ( [*requires.cmake(), *requires.ninja()] if requires.settings.wheel.cmake else [] ) return [ *cmake_requires, *requires.dynamic_metadata(), ] def get_requires_for_build_editable( config_settings: dict[str, str | list[str]] | None = None, ) -> list[str]: from ..builder.get_requires import GetRequires requires = GetRequires.from_config_settings(config_settings) # These are only injected if cmake is required for the wheel step cmake_requires = ( [*requires.cmake(), *requires.ninja()] if requires.settings.wheel.cmake else [] ) return [ *cmake_requires, *requires.dynamic_metadata(), ] scikit-build-core-0.11.1/src/scikit_build_core/build/_editable.py000066400000000000000000000035401477275177200247550ustar00rootroot00000000000000from __future__ import annotations import os import typing from pathlib import Path from ..resources import resources from ._pathutil import ( is_valid_module, path_to_module, scantree, ) if typing.TYPE_CHECKING: from collections.abc import Sequence __all__ = ["editable_redirect", "libdir_to_installed", "mapping_to_modules"] def __dir__() -> list[str]: return __all__ def editable_redirect( *, modules: dict[str, str], installed: dict[str, str], reload_dir: Path | None, rebuild: bool, verbose: bool, build_options: Sequence[str], install_options: Sequence[str], install_dir: str, ) -> str: """ Prepare the contents of the _editable_redirect.py file. """ editable_py = resources / "_editable_redirect.py" editable_txt: str = editable_py.read_text(encoding="utf-8") arguments = ( modules, installed, os.fspath(reload_dir) if reload_dir else None, rebuild, verbose, build_options, install_options, install_dir, ) arguments_str = ", ".join(repr(x) for x in arguments) editable_txt += f"\n\ninstall({arguments_str})\n" return editable_txt def mapping_to_modules(mapping: dict[str, str], libdir: Path) -> dict[str, str]: """ Convert a mapping of files to modules to a mapping of modules to installed files. """ return { path_to_module(Path(v).relative_to(libdir)): str(Path(k).resolve()) for k, v in mapping.items() if is_valid_module(Path(v).relative_to(libdir)) } def libdir_to_installed(libdir: Path) -> dict[str, str]: """ Convert a mapping of files to modules to a mapping of modules to installed files. """ return { path_to_module(pth): str(pth) for v in scantree(libdir) if is_valid_module(pth := v.relative_to(libdir)) } scikit-build-core-0.11.1/src/scikit_build_core/build/_file_processor.py000066400000000000000000000053351477275177200262260ustar00rootroot00000000000000from __future__ import annotations import contextlib import os from pathlib import Path from typing import TYPE_CHECKING import pathspec from scikit_build_core.format import pyproject_format if TYPE_CHECKING: from collections.abc import Generator, Sequence __all__ = ["each_unignored_file"] EXCLUDE_LINES = [ ".git", ".tox/", ".nox/", ".egg-info/", "__pycache__/", "__pypackages__/", "*.pyc", "*.dist-info/", ] def __dir__() -> list[str]: return __all__ def each_unignored_file( starting_path: Path, include: Sequence[str] = (), exclude: Sequence[str] = (), build_dir: str = "", ) -> Generator[Path, None, None]: """ Runs through all non-ignored files. Must be run from the root directory. """ global_exclude_lines = [] for gi in [Path(".git/info/exclude"), Path(".gitignore")]: ignore_errs = [FileNotFoundError, NotADirectoryError] with contextlib.suppress(*ignore_errs), gi.open(encoding="utf-8") as f: global_exclude_lines += f.readlines() nested_excludes = { p.parent: pathspec.GitIgnoreSpec.from_lines( p.read_text(encoding="utf-8").splitlines() ) for p in Path().rglob("**/.gitignore") if p != Path(".gitignore") } exclude_build_dir = build_dir.format(**pyproject_format(dummy=True)) exclude_lines = ( [*EXCLUDE_LINES, exclude_build_dir] if exclude_build_dir else EXCLUDE_LINES ) user_exclude_spec = pathspec.GitIgnoreSpec.from_lines(list(exclude)) global_exclude_spec = pathspec.GitIgnoreSpec.from_lines(global_exclude_lines) builtin_exclude_spec = pathspec.GitIgnoreSpec.from_lines(exclude_lines) include_spec = pathspec.GitIgnoreSpec.from_lines(include) for dirstr, _, filenames in os.walk(str(starting_path), followlinks=True): dirpath = Path(dirstr) all_paths = (dirpath / fn for fn in filenames) for p in all_paths: # Always include something included if include_spec.match_file(p): yield p continue # Always exclude something excluded if user_exclude_spec.match_file(p): continue # Ignore from global ignore if global_exclude_spec.match_file(p): continue # Ignore built-in patterns if builtin_exclude_spec.match_file(p): continue # Check relative ignores (Python 3.9's is_relative_to workaround) if any( nex.match_file(p.relative_to(np)) for np, nex in nested_excludes.items() if dirpath == np or np in dirpath.parents ): continue yield p scikit-build-core-0.11.1/src/scikit_build_core/build/_init.py000066400000000000000000000011471477275177200241500ustar00rootroot00000000000000from __future__ import annotations import functools import logging from .._logging import LEVEL_VALUE, logger __all__ = ["setup_logging"] def __dir__() -> list[str]: return __all__ @functools.lru_cache(1) def setup_logging(log_level: str) -> None: level_value = LEVEL_VALUE[log_level] logger.setLevel(level_value) ch = logging.StreamHandler() # create formatter and add it to the handlers formatter = logging.Formatter( "%(asctime)s - %(name)s - %(levelname)s - %(message)s" ) ch.setFormatter(formatter) # add the handlers to the logger logger.addHandler(ch) scikit-build-core-0.11.1/src/scikit_build_core/build/_pathutil.py000066400000000000000000000041451477275177200250400ustar00rootroot00000000000000from __future__ import annotations import os from pathlib import Path from typing import TYPE_CHECKING import pathspec from ._file_processor import each_unignored_file if TYPE_CHECKING: from collections.abc import Generator, Mapping, Sequence __all__ = ["is_valid_module", "packages_to_file_mapping", "path_to_module", "scantree"] def __dir__() -> list[str]: return __all__ def scantree(path: Path) -> Generator[Path, None, None]: """Recursively yield Path objects for given directory.""" for entry in os.scandir(path): if entry.is_dir(follow_symlinks=False): yield from scantree(Path(entry)) else: yield Path(entry) def path_to_module(path: Path) -> str: name, _, _ = path.name.partition(".") assert name, f"Empty name should be filtered by is_valid_module first, got {path}" path = path.with_name(name) if path.name == "__init__": path = path.parent return ".".join(path.parts) def packages_to_file_mapping( *, packages: Mapping[str, str], platlib_dir: Path, include: Sequence[str], src_exclude: Sequence[str], target_exclude: Sequence[str], build_dir: str, ) -> dict[str, str]: """ This will output a mapping of source files to target files. """ mapping = {} exclude_spec = pathspec.GitIgnoreSpec.from_lines(target_exclude) for package_str, source_str in packages.items(): package_dir = Path(package_str) source_dir = Path(source_str) for filepath in each_unignored_file( source_dir, include=include, exclude=src_exclude, build_dir=build_dir, ): rel_path = filepath.relative_to(source_dir) target_path = platlib_dir / package_dir / rel_path if not exclude_spec.match_file(rel_path) and not target_path.is_file(): mapping[str(filepath)] = str(target_path) return mapping def is_valid_module(path: Path) -> bool: parts = path.parts return ( all(p.isidentifier() for p in parts[:-1]) and parts[-1].split(".", 1)[0].isidentifier() ) scikit-build-core-0.11.1/src/scikit_build_core/build/_scripts.py000066400000000000000000000017221477275177200246730ustar00rootroot00000000000000from __future__ import annotations import contextlib import re from typing import TYPE_CHECKING if TYPE_CHECKING: from pathlib import Path __all__ = ["process_script_dir"] def __dir__() -> list[str]: return __all__ SHEBANG_PATTERN = re.compile(r"^#!.*(?:python|pythonw|pypy)[0-9.]*([ \t].*)?$") def process_script_dir(script_dir: Path) -> None: for item in script_dir.iterdir(): content = [] with contextlib.suppress(UnicodeDecodeError), item.open(encoding="utf-8") as f: file_iter = iter(f) try: # TODO: handle empty files first_line = next(file_iter) except StopIteration: first_line = "" match = SHEBANG_PATTERN.match(first_line) if match: content = [f"#!python{match.group(1) or ''}\n", *file_iter] if content: with item.open("w", encoding="utf-8") as f: f.writelines(content) scikit-build-core-0.11.1/src/scikit_build_core/build/_wheelfile.py000066400000000000000000000207051477275177200251520ustar00rootroot00000000000000from __future__ import annotations import base64 import csv import dataclasses import hashlib import io import os import stat import time import zipfile from email.message import Message from email.policy import EmailPolicy from pathlib import Path from typing import TYPE_CHECKING from zipfile import ZipInfo import pathspec from .. import __version__ if TYPE_CHECKING: from collections.abc import Mapping, Sequence, Set from packaging.tags import Tag from .._compat.typing import Self from .._vendor.pyproject_metadata import StandardMetadata EMAIL_POLICY = EmailPolicy(max_line_length=0, mangle_from_=False, utf8=True) MIN_TIMESTAMP = 315532800 # 1980-01-01 00:00:00 UTC def _b64encode(data: bytes) -> bytes: return base64.urlsafe_b64encode(data).rstrip(b"=") __all__ = ["WheelMetadata", "WheelWriter"] def __dir__() -> list[str]: return __all__ @dataclasses.dataclass class WheelMetadata: root_is_purelib: bool = False metadata_version: str = "1.0" generator: str = f"scikit-build-core {__version__}" tags: Set[Tag] = dataclasses.field(default_factory=frozenset) build_tag: str = "" def as_bytes(self) -> bytes: msg = Message(policy=EMAIL_POLICY) msg["Wheel-Version"] = self.metadata_version msg["Generator"] = self.generator msg["Root-Is-Purelib"] = str(self.root_is_purelib).lower() for tag in sorted(self.tags, key=lambda t: (t.interpreter, t.abi, t.platform)): msg["Tag"] = f"{tag.interpreter}-{tag.abi}-{tag.platform}" if self.build_tag: msg["Build"] = self.build_tag return msg.as_bytes() @dataclasses.dataclass class WheelWriter: """A general tool for writing wheels. Designed to look a little like ZipFile.""" metadata: StandardMetadata folder: Path tags: Set[Tag] wheel_metadata: WheelMetadata metadata_dir: Path | None _zipfile: zipfile.ZipFile | None = None @property def name_ver(self) -> str: name = self.metadata.canonical_name.replace("-", "_") # replace - with _ as a local version separator version = str(self.metadata.version).replace("-", "_") return f"{name}-{version}" @property def basename(self) -> str: pyver = ".".join(sorted({t.interpreter for t in self.tags})) abi = ".".join(sorted({t.abi for t in self.tags})) arch = ".".join(sorted({t.platform for t in self.tags})) optbuildver = ( [self.wheel_metadata.build_tag] if self.wheel_metadata.build_tag else [] ) return "-".join([self.name_ver, *optbuildver, pyver, abi, arch]) @property def wheelpath(self) -> Path: return self.folder / f"{self.basename}.whl" @property def dist_info(self) -> str: return f"{self.name_ver}.dist-info" @staticmethod def timestamp(mtime: float | None = None) -> tuple[int, int, int, int, int, int]: timestamp = int(os.environ.get("SOURCE_DATE_EPOCH", mtime or time.time())) # The ZIP file format does not support timestamps before 1980. timestamp = max(timestamp, MIN_TIMESTAMP) return time.gmtime(timestamp)[0:6] def dist_info_contents(self) -> dict[str, bytes]: entry_points = io.StringIO() ep = self.metadata.entrypoints.copy() ep["console_scripts"] = self.metadata.scripts ep["gui_scripts"] = self.metadata.gui_scripts for group, entries in ep.items(): if entries: entry_points.write(f"[{group}]\n") for name, target in entries.items(): entry_points.write(f"{name} = {target}\n") entry_points.write("\n") self.wheel_metadata.tags = self.tags rfc822 = self.metadata.as_rfc822() metadata_files = self.metadata_dir.rglob("*") if self.metadata_dir else [] extra_metadata = { str(f.relative_to(self.metadata_dir or Path())): f.read_bytes() for f in metadata_files if f.is_file() } if {"METADATA", "WHEEL", "RECORD", "entry_points.txt"} & extra_metadata.keys(): msg = "Cannot have METADATA, WHEEL, RECORD, or entry_points.txt in metadata_dir" raise ValueError(msg) entry_points_txt = entry_points.getvalue().encode("utf-8") entry_points_dict = ( {"entry_points.txt": entry_points_txt} if entry_points_txt else {} ) return { "METADATA": bytes(rfc822), "WHEEL": self.wheel_metadata.as_bytes(), **entry_points_dict, **extra_metadata, } def build( self, wheel_dirs: Mapping[str, Path], exclude: Sequence[str] = () ) -> None: (targetlib,) = {"platlib", "purelib"} & set(wheel_dirs) assert { targetlib, "data", "headers", "scripts", "null", "metadata", } >= wheel_dirs.keys() # The "main" directory (platlib usually for us) will be handled specially below plans = {"": wheel_dirs[targetlib]} data_dir = f"{self.name_ver}.data" for key in sorted({"data", "headers", "scripts"} & wheel_dirs.keys()): plans[key] = wheel_dirs[key] exclude_spec = pathspec.GitIgnoreSpec.from_lines(exclude) for key, path in plans.items(): for filename in sorted(path.glob("**/*")): if not filename.is_file(): continue if any(x.endswith(".dist-info") for x in filename.parts): continue if filename.suffix in {".pyc", ".pyo"}: continue relpath = filename.relative_to(path) if exclude_spec.match_file(relpath): continue target = Path(data_dir) / key / relpath if key else relpath self.write(str(filename), str(target)) dist_info_contents = self.dist_info_contents() for key, data in dist_info_contents.items(): self.writestr(f"{self.dist_info}/{key}", data) def write(self, filename: str, arcname: str | None = None) -> None: """Write a file to the archive. Paths are normalized to Posix paths.""" with Path(filename).open("rb") as f: st = os.fstat(f.fileno()) data = f.read() # Zipfiles require Posix paths for the arcname zinfo = ZipInfo( (arcname or filename).replace("\\", "/"), date_time=self.timestamp(st.st_mtime), ) zinfo.compress_type = zipfile.ZIP_DEFLATED zinfo.external_attr = (stat.S_IMODE(st.st_mode) | stat.S_IFMT(st.st_mode)) << 16 self.writestr(zinfo, data) def writestr(self, zinfo_or_arcname: str | ZipInfo, data: bytes) -> None: """Write bytes (not strings) to the archive.""" assert isinstance(data, bytes) assert self._zipfile is not None if isinstance(zinfo_or_arcname, zipfile.ZipInfo): zinfo = zinfo_or_arcname else: zinfo = zipfile.ZipInfo( zinfo_or_arcname.replace("\\", "/"), date_time=self.timestamp(), ) zinfo.compress_type = zipfile.ZIP_DEFLATED zinfo.external_attr = (0o664 | stat.S_IFREG) << 16 assert "\\" not in zinfo.filename, ( f"\\ not supported in zip; got {zinfo.filename!r}" ) self._zipfile.writestr(zinfo, data) def __enter__(self) -> Self: if not self.wheelpath.parent.exists(): self.wheelpath.parent.mkdir(parents=True) self._zipfile = zipfile.ZipFile( self.wheelpath, "w", compression=zipfile.ZIP_DEFLATED ) return self def __exit__(self, *args: object) -> None: assert self._zipfile is not None record = f"{self.dist_info}/RECORD" data = io.StringIO() writer = csv.writer(data, delimiter=",", quotechar='"', lineterminator="\n") for member in self._zipfile.infolist(): assert "\\" not in member.filename, ( f"Invalid zip contents: {member.filename}" ) with self._zipfile.open(member) as f: member_data = f.read() sha = _b64encode(hashlib.sha256(member_data).digest()).decode("ascii") writer.writerow((member.filename, f"sha256={sha}", member.file_size)) writer.writerow((record, "", "")) self.writestr(record, data.getvalue().encode("utf-8")) self._zipfile.close() self._zipfile = None scikit-build-core-0.11.1/src/scikit_build_core/build/generate.py000066400000000000000000000016061477275177200246400ustar00rootroot00000000000000from __future__ import annotations __all__ = ["generate_file_contents"] import dataclasses import string from typing import TYPE_CHECKING if TYPE_CHECKING: from .._vendor.pyproject_metadata import StandardMetadata from ..settings.skbuild_model import GenerateSettings def __dir__() -> list[str]: return __all__ def generate_file_contents(gen: GenerateSettings, metadata: StandardMetadata) -> str: """ Generate a file contents from a template. Input GeneratorSettings and metadata. Metadata is available inside the template. """ assert gen.template_path or gen.template, ( f"One of template or template-path must be set for {gen.path}" ) if gen.template_path: template = gen.template_path.read_text(encoding="utf-8") else: template = gen.template return string.Template(template).substitute(dataclasses.asdict(metadata)) scikit-build-core-0.11.1/src/scikit_build_core/build/metadata.py000066400000000000000000000074601477275177200246320ustar00rootroot00000000000000from __future__ import annotations import copy import sys from typing import TYPE_CHECKING, Any from packaging.version import Version from .._logging import logger from .._vendor.pyproject_metadata import ( StandardMetadata, errors, extras_build_system, extras_top_level, ) from ..settings._load_provider import load_dynamic_metadata if TYPE_CHECKING: from collections.abc import Mapping from ..settings.skbuild_model import ScikitBuildSettings __all__ = ["get_standard_metadata"] def __dir__() -> list[str]: return __all__ # Use exceptiongroup backport if sys.version_info < (3, 11): from exceptiongroup import ExceptionGroup errors.ExceptionGroup = ExceptionGroup # type: ignore[misc, assignment] # If pyproject-metadata eventually supports updates, this can be simplified def get_standard_metadata( pyproject_dict: Mapping[str, Any], settings: ScikitBuildSettings, ) -> StandardMetadata: new_pyproject_dict = copy.deepcopy(pyproject_dict) # Handle any dynamic metadata for field, provider, config in load_dynamic_metadata(settings.metadata): if provider is None: msg = f"{field} is missing provider" raise KeyError(msg) if field not in pyproject_dict.get("project", {}).get("dynamic", []): msg = f"{field} is not in project.dynamic" raise KeyError(msg) new_pyproject_dict["project"][field] = provider.dynamic_metadata(field, config) new_pyproject_dict["project"]["dynamic"].remove(field) if settings.strict_config: extra_keys_top = extras_top_level(new_pyproject_dict) if extra_keys_top: logger.warning( f"Unknown keys in top-level of pyproject.toml: {', '.join(extra_keys_top)}" ) extra_keys_build = extras_build_system(new_pyproject_dict) if extra_keys_build: logger.warning( f"Unknown keys in build-system of pyproject.toml: {', '.join(extra_keys_build)}" ) extra_validate = ( settings.minimum_version is None or settings.minimum_version >= Version("0.10") ) if extra_validate: allow_extra_keys: bool | None = not settings.strict_config else: allow_extra_keys = None if settings.strict_config else False metadata = StandardMetadata.from_pyproject( new_pyproject_dict, all_errors=True, allow_extra_keys=allow_extra_keys ) # For scikit-build-core < 0.5, we keep the normalized name for back-compat if settings.minimum_version is not None and settings.minimum_version < Version( "0.5" ): metadata.name = metadata.canonical_name # The description field is required to be one line. Instead of merging it # or cutting off subsequent lines (setuptools), we throw a nice error. # But we didn't validate before 0.9. if ( settings.minimum_version is None or settings.minimum_version >= Version("0.9") ) and "\n" in (metadata.description or ""): msg = "Multiple lines in project.description are not supported; this is supposed to be a one line summary" raise ValueError(msg) # Validate license if possible. if isinstance(metadata.license, str): try: import packaging.licenses metadata.license = packaging.licenses.canonicalize_license_expression( metadata.license ) except ImportError: logger.warning( "Packaging 24.2+ required for license normalization. Please update (Python 3.8+ required)" ) # For scikit-build-core >= 0.11, we set METADATA 2.2 as minimum if ( settings.minimum_version is None or settings.minimum_version >= Version("0.11") ) and metadata.auto_metadata_version == "2.1": metadata.metadata_version = "2.2" return metadata scikit-build-core-0.11.1/src/scikit_build_core/build/sdist.py000066400000000000000000000125351477275177200241770ustar00rootroot00000000000000# pylint: disable=duplicate-code from __future__ import annotations import contextlib import copy import gzip import io import os import tarfile from pathlib import Path from packaging.utils import canonicalize_name from packaging.version import Version from .. import __version__ from .._compat import tomllib from .._logging import rich_print from ..settings.skbuild_read_settings import SettingsReader from ._file_processor import each_unignored_file from ._init import setup_logging from .generate import generate_file_contents from .metadata import get_standard_metadata from .wheel import _build_wheel_impl __all__ = ["build_sdist"] def __dir__() -> list[str]: return __all__ def get_reproducible_epoch() -> int: """ Return an integer representing the integer number of seconds since the Unix epoch. If the `SOURCE_DATE_EPOCH` environment variable is set, use that value. Otherwise, always return `1667997441`. """ return int(os.environ.get("SOURCE_DATE_EPOCH", "1667997441")) def normalize_file_permissions(st_mode: int) -> int: """ Normalize the permission bits in the st_mode field from stat to 644/755 Popular VCSs only track whether a file is executable or not. The exact permissions can vary on systems with different umasks. Normalising to 644 (non executable) or 755 (executable) makes builds more reproducible. Taken from https://github.com/pypa/flit/blob/6a2a8c6462e49f584941c667b70a6f48a7b3f9ab/flit_core/flit_core/common.py#L257 """ # Set 644 permissions, leaving higher bits of st_mode unchanged new_mode = (st_mode | 0o644) & ~0o133 if st_mode & 0o100: new_mode |= 0o111 # Executable: 644 -> 755 return new_mode def normalize_tar_info(tar_info: tarfile.TarInfo) -> tarfile.TarInfo: """ Normalize the TarInfo associated with a file to improve reproducibility. Inspired by Hatch https://github.com/pypa/hatch/blob/573192f88022bb781c698dae2c0b84ef3fb9a7ad/backend/src/hatchling/builders/sdist.py#L51 """ tar_info = copy.copy(tar_info) tar_info.uname = "" tar_info.gname = "" tar_info.uid = 0 tar_info.gid = 0 tar_info.mode = normalize_file_permissions(tar_info.mode) tar_info.mtime = get_reproducible_epoch() return tar_info def add_bytes_to_tar( tar: tarfile.TarFile, data: bytes, name: str, *, normalize: bool ) -> None: """ Write ``data`` bytes to ``name`` in a tarfile ``tar``. Normalize the info if ``normalize`` is true. """ tarinfo = tarfile.TarInfo(name) if normalize: tarinfo = normalize_tar_info(tarinfo) with io.BytesIO(data) as bio: tarinfo.size = bio.getbuffer().nbytes tar.addfile(tarinfo, bio) def build_sdist( sdist_directory: str, config_settings: dict[str, list[str] | str] | None = None, ) -> str: rich_print( f"{{green}}***{{bold.green}} scikit-build-core {__version__}{{red}} (sdist)", ) with Path("pyproject.toml").open("rb") as f: pyproject = tomllib.load(f) settings_reader = SettingsReader(pyproject, config_settings or {}, state="sdist") settings = settings_reader.settings setup_logging(settings.logging.level) settings_reader.validate_may_exit() sdist_dir = Path(sdist_directory) reproducible = settings.sdist.reproducible timestamp = get_reproducible_epoch() if reproducible else None metadata = get_standard_metadata(pyproject, settings) # Using deepcopy here because of a bug in pyproject-metadata # https://github.com/FFY00/python-pyproject-metadata/pull/49 pkg_info = bytes(copy.deepcopy(metadata).as_rfc822()) # Only normalize SDist name if 0.5+ is requested for backwards compat should_normalize_name = ( settings.minimum_version is None or settings.minimum_version >= Version("0.5") ) sdist_name = ( canonicalize_name(metadata.name).replace("-", "_") if should_normalize_name else metadata.name ) srcdirname = f"{sdist_name}-{metadata.version}" filename = f"{srcdirname}.tar.gz" if settings.sdist.cmake: _build_wheel_impl( None, config_settings, None, exit_after_config=True, editable=False ) for gen in settings.generate: if gen.location == "source": contents = generate_file_contents(gen, metadata) gen.path.write_text(contents) settings.sdist.include.append(str(gen.path)) sdist_dir.mkdir(parents=True, exist_ok=True) with contextlib.ExitStack() as stack: gzip_container = stack.enter_context( gzip.GzipFile( sdist_dir / filename, mode="wb", compresslevel=9, mtime=timestamp ) ) tar = stack.enter_context( tarfile.TarFile(fileobj=gzip_container, mode="w", format=tarfile.PAX_FORMAT) ) paths = sorted( each_unignored_file( Path(), include=settings.sdist.include, exclude=settings.sdist.exclude, build_dir=settings.build_dir, ) ) for filepath in paths: tar.add( filepath, arcname=srcdirname / filepath, filter=normalize_tar_info if reproducible else lambda x: x, ) add_bytes_to_tar( tar, pkg_info, f"{srcdirname}/PKG-INFO", normalize=reproducible ) return filename scikit-build-core-0.11.1/src/scikit_build_core/build/wheel.py000066400000000000000000000454501477275177200241570ustar00rootroot00000000000000from __future__ import annotations import dataclasses import os import shutil import sysconfig import tempfile from collections.abc import Mapping from pathlib import Path from typing import TYPE_CHECKING, Any, Literal from packaging.requirements import Requirement from packaging.utils import canonicalize_name from .._compat import tomllib from .._compat.typing import assert_never from .._logging import LEVEL_VALUE, logger, rich_error, rich_print from ..builder.builder import Builder, archs_to_tags, get_archs from ..builder.wheel_tag import WheelTag from ..cmake import CMake, CMaker from ..errors import FailedLiveProcessError from ..format import pyproject_format from ..settings.skbuild_read_settings import SettingsReader from ._editable import editable_redirect, libdir_to_installed, mapping_to_modules from ._init import setup_logging from ._pathutil import ( packages_to_file_mapping, ) from ._scripts import process_script_dir from ._wheelfile import WheelMetadata, WheelWriter from .generate import generate_file_contents from .metadata import get_standard_metadata if TYPE_CHECKING: from collections.abc import Iterable, Sequence from ..settings.skbuild_model import ScikitBuildSettings __all__ = ["_build_wheel_impl"] def __dir__() -> list[str]: return __all__ def _make_editable( *, build_options: Sequence[str] = (), install_options: Sequence[str] = (), libdir: Path, mapping: dict[str, str], name: str, reload_dir: Path | None, settings: ScikitBuildSettings, wheel: WheelWriter, packages: Iterable[str], ) -> None: modules = mapping_to_modules(mapping, libdir) installed = libdir_to_installed(libdir) if settings.wheel.install_dir.startswith("/"): msg = "Editable installs cannot rebuild an absolute wheel.install-dir. Use an override to change if needed." raise AssertionError(msg) editable_txt = editable_redirect( modules=modules, installed=installed, reload_dir=reload_dir, rebuild=settings.editable.rebuild, verbose=settings.editable.verbose, build_options=build_options, install_options=install_options, install_dir=settings.wheel.install_dir, ) wheel.writestr( f"_{name}_editable.py", editable_txt.encode(), ) # Support Cython by adding the source directory directly to the path. # This is necessary because Cython does not support sys.meta_path for # cimports (as of 3.0.5). import_strings = [f"import _{name}_editable", *packages, ""] pth_import_paths = "\n".join(import_strings) wheel.writestr( f"_{name}_editable.pth", pth_import_paths.encode(), ) def _get_packages( *, packages: Sequence[str] | Mapping[str, str] | None, name: str, ) -> dict[str, str]: if packages is not None: if isinstance(packages, Mapping): return dict(packages) return {str(Path(p).name): p for p in packages} # Auto package discovery packages = {} for base_path in (Path("src"), Path("python"), Path()): path = base_path / name if path.is_dir() and ( (path / "__init__.py").is_file() or (path / "__init__.pyi").is_file() ): logger.info("Discovered Python package at {}", path) packages[name] = str(path) break else: logger.debug("Didn't find a Python package for {}", name) return packages @dataclasses.dataclass class WheelImplReturn: wheel_filename: str settings: ScikitBuildSettings mapping: dict[str, str] = dataclasses.field(default_factory=dict) def _build_wheel_impl( wheel_directory: str | None, config_settings: dict[str, list[str] | str] | None, metadata_directory: str | None, *, exit_after_config: bool = False, editable: bool, ) -> WheelImplReturn: """ Build a wheel or just prepare metadata (if wheel dir is None). Can be editable. Handles one retry attempt if "failed" override present. """ state: Literal["sdist", "wheel", "editable", "metadata_wheel", "metadata_editable"] if exit_after_config: state = "sdist" elif wheel_directory is None: state = "metadata_editable" if editable else "metadata_wheel" else: state = "editable" if editable else "wheel" pyproject_path = Path("pyproject.toml") with pyproject_path.open("rb") as ft: pyproject = tomllib.load(ft) settings_reader = SettingsReader( pyproject, config_settings or {}, state=state, retry=False ) setup_logging(settings_reader.settings.logging.level) settings_reader.validate_may_exit() if settings_reader.settings.fail: if settings_reader.settings.messages.after_failure: rich_print(settings_reader.settings.messages.after_failure) raise SystemExit(7) rich_error("scikit-build-core's fail setting was enabled. Exiting immediately.") # Warn if cmake or ninja is in build-system.requires requirements = [ canonicalize_name(Requirement(p).name) for p in pyproject.get("build-system", {}).get("requires", []) ] if "cmake" in requirements: logger.warning( "cmake should not be in build-system.requires - scikit-build-core will inject it as needed" ) if "ninja" in requirements: logger.warning( "ninja should not be in build-system.requires - scikit-build-core will inject it as needed" ) try: return _build_wheel_impl_impl( wheel_directory, metadata_directory, exit_after_config=exit_after_config, editable=editable, state=state, settings=settings_reader.settings, pyproject=pyproject, ) except FailedLiveProcessError as err: settings_reader = SettingsReader( pyproject, config_settings or {}, state=state, retry=True ) if "failed" not in settings_reader.overrides: err.msg = settings_reader.settings.messages.after_failure raise rich_print( "\n***", *err.args, "- retrying due to override...", color="yellow", ) logger.setLevel(LEVEL_VALUE[settings_reader.settings.logging.level]) settings_reader.validate_may_exit() try: return _build_wheel_impl_impl( wheel_directory, metadata_directory, exit_after_config=exit_after_config, editable=editable, state=state, settings=settings_reader.settings, pyproject=pyproject, ) except FailedLiveProcessError as err2: err2.msg = settings_reader.settings.messages.after_failure.format() raise def _build_wheel_impl_impl( wheel_directory: str | None, metadata_directory: str | None, *, exit_after_config: bool = False, editable: bool, state: Literal["sdist", "wheel", "editable", "metadata_wheel", "metadata_editable"], settings: ScikitBuildSettings, pyproject: dict[str, Any], ) -> WheelImplReturn: """ Build a wheel or just prepare metadata (if wheel dir is None). Can be editable. """ metadata = get_standard_metadata(pyproject, settings) if metadata.version is None: msg = "project.version is not specified, must be statically present or tool.scikit-build metadata.version.provider configured when dynamic" raise AssertionError(msg) # Verify PEP 639 replaces license-files if metadata.license_files is not None and settings.wheel.license_files: msg = "Both project.license-files and tool.scikit-build.wheel.license-files are set, use only one" raise AssertionError(msg) # Get the closest (normally) importable name normalized_name = metadata.name.replace("-", "_").replace(".", "_") if settings.wheel.cmake: cmake = CMake.default_search(version=settings.cmake.version, env=os.environ) cmake_msg = [f"using {{blue}}CMake {cmake.version}{{default}}"] else: cmake = None cmake_msg = [] if settings.wheel.platlib is None: targetlib = "platlib" if settings.wheel.cmake else "purelib" else: targetlib = "platlib" if settings.wheel.platlib else "purelib" rich_print( "{green}*** {bold}scikit-build-core {__version__}", *cmake_msg, f"{{red}}({state})", ) with tempfile.TemporaryDirectory() as tmpdir: build_tmp_folder = Path(tmpdir) wheel_dir = build_tmp_folder / "wheel" tags = WheelTag.compute_best( archs_to_tags(get_archs(os.environ)), settings.wheel.py_api, expand_macos=settings.wheel.expand_macos_universal_tags, root_is_purelib=targetlib == "purelib", build_tag=settings.wheel.build_tag, ) # A build dir can be specified, otherwise use a temporary directory if cmake is not None and editable and settings.editable.mode == "inplace": build_dir = settings.cmake.source_dir else: build_dir = ( Path( settings.build_dir.format( **pyproject_format( settings=settings, tags=tags, state=state, ) ) ) if settings.build_dir else build_tmp_folder / "build" ) logger.info("Build directory: {}", build_dir.resolve()) wheel_dirs = { targetlib: wheel_dir / targetlib, "data": wheel_dir / "data", "headers": wheel_dir / "headers", "scripts": wheel_dir / "scripts", "null": wheel_dir / "null", "metadata": wheel_dir / "metadata", } for d in wheel_dirs.values(): d.mkdir(parents=True) if ".." in settings.wheel.install_dir: msg = "wheel.install_dir must not contain '..'" raise AssertionError(msg) if settings.wheel.install_dir.startswith("/"): if not settings.experimental: msg = "Experimental features must be enabled to use absolute paths in wheel.install_dir" raise AssertionError(msg) if settings.wheel.install_dir[1:].split("/")[0] not in wheel_dirs: msg = "Must target a valid wheel directory" raise AssertionError(msg) install_dir = wheel_dir / settings.wheel.install_dir[1:] else: install_dir = wheel_dirs[targetlib] / settings.wheel.install_dir # Include the metadata license.file entry if provided if metadata.license_files: license_paths = metadata.license_files else: license_file_globs = settings.wheel.license_files or [ "LICEN[CS]E*", "COPYING*", "NOTICE*", "AUTHORS*", ] if ( metadata.license and not isinstance(metadata.license, str) and metadata.license.file ): license_file_globs.append(str(metadata.license.file)) license_paths = [ x for y in license_file_globs for x in Path().glob(y) if x.is_file() ] for x in license_paths: path = wheel_dirs["metadata"] / "licenses" / x path.parent.mkdir(parents=True, exist_ok=True) shutil.copy(x, path) if ( settings.wheel.license_files and not (wheel_dirs["metadata"] / "licenses").is_dir() ): logger.warning( "No license files found, set wheel.license-files to [] to suppress this warning" ) for gen in settings.generate: if gen.location == "source": contents = generate_file_contents(gen, metadata) gen.path.write_text(contents) settings.sdist.include.append(str(gen.path)) if wheel_directory is None and not exit_after_config: if metadata_directory is None: msg = "metadata_directory must be specified if wheel_directory is None" raise AssertionError(msg) wheel = WheelWriter( metadata, Path(metadata_directory), tags.as_tags_set(), WheelMetadata( root_is_purelib=targetlib == "purelib", build_tag=settings.wheel.build_tag, ), wheel_dirs["metadata"], ) dist_info_contents = wheel.dist_info_contents() dist_info = Path(metadata_directory) / f"{wheel.name_ver}.dist-info" dist_info.mkdir(parents=True) for key, data in dist_info_contents.items(): path = dist_info / key if not path.parent.is_dir(): path.parent.mkdir(exist_ok=True, parents=True) path.write_bytes(data) return WheelImplReturn(wheel_filename=dist_info.name, settings=settings) for gen in settings.generate: contents = generate_file_contents(gen, metadata) if gen.location == "source": continue if gen.location == "build": path = build_dir / gen.path elif gen.location == "install": path = wheel_dirs[targetlib] / gen.path else: assert_never(gen.location) path.parent.mkdir(parents=True, exist_ok=True) path.write_text(contents, encoding="utf-8") build_options = [] install_options = [] if cmake is not None: config = CMaker( cmake, source_dir=settings.cmake.source_dir, build_dir=build_dir, build_type=settings.cmake.build_type, ) builder = Builder( settings=settings, config=config, ) rich_print("{green}***", "{bold}Configuring CMake...") # Setting the install prefix because some libs hardcode CMAKE_INSTALL_PREFIX # Otherwise `cmake --install --prefix` would work by itself defines = {"CMAKE_INSTALL_PREFIX": install_dir} cache_entries: dict[str, str | Path] = { f"SKBUILD_{k.upper()}_DIR": v for k, v in wheel_dirs.items() } cache_entries["SKBUILD_STATE"] = state builder.configure( defines=defines, cache_entries=cache_entries, name=metadata.name, version=metadata.version, ) if exit_after_config: return WheelImplReturn("", settings=settings) default_gen = ( "MSVC" if sysconfig.get_platform().startswith("win") else "Default Generator" ) generator = builder.get_generator() or default_gen rich_print( "{green}***", f"{{bold}}Building project with {{blue}}{generator}{{default}}...", ) build_args: list[str] = [] builder.build(build_args=build_args) if not (editable and settings.editable.mode == "inplace"): rich_print( "{green}***", "{bold}Installing project into wheel...", ) builder.install(install_dir) if not builder.config.single_config and builder.config.build_type: build_options += ["--config", builder.config.build_type] install_options += ["--config", builder.config.build_type] if builder.settings.cmake.verbose: build_options.append("-v") assert wheel_directory is not None rich_print("{green}***", f"{{bold}}Making {state}...") packages = _get_packages( packages=settings.wheel.packages, name=normalized_name, ) mapping = packages_to_file_mapping( packages=packages, platlib_dir=wheel_dirs[targetlib], include=settings.sdist.include, src_exclude=settings.sdist.exclude, target_exclude=settings.wheel.exclude, build_dir=settings.build_dir, ) if not editable: for filepath, package_dir in mapping.items(): Path(package_dir).parent.mkdir(exist_ok=True, parents=True) shutil.copyfile(filepath, package_dir) process_script_dir(wheel_dirs["scripts"]) with WheelWriter( metadata, Path(wheel_directory), tags.as_tags_set(), WheelMetadata( root_is_purelib=targetlib == "purelib", build_tag=settings.wheel.build_tag, ), wheel_dirs["metadata"], ) as wheel: wheel.build(wheel_dirs, exclude=settings.wheel.exclude) str_pkgs = ( str(Path.cwd().joinpath(p).parent.resolve()) for p in packages.values() ) if editable and settings.editable.mode == "redirect": reload_dir = build_dir.resolve() if settings.build_dir else None _make_editable( build_options=build_options, install_options=install_options, libdir=wheel_dirs[targetlib], mapping=mapping, reload_dir=reload_dir, settings=settings, wheel=wheel, name=normalized_name, packages=str_pkgs, ) elif editable and settings.editable.mode == "inplace": if not packages: msg = "Editable inplace mode requires at least one package" raise AssertionError(msg) wheel.writestr( f"_{normalized_name}_editable.pth", "\n".join(str_pkgs).encode(), ) if metadata_directory is not None: dist_info_contents = wheel.dist_info_contents() dist_info = Path(metadata_directory) for key, data in dist_info_contents.items(): path = dist_info / key prevous_data = path.read_bytes() if prevous_data != data: msg = f"Metadata mismatch in {key}" logger.error("{}: {!r} != {!r}", msg, prevous_data, data) raise AssertionError(msg) wheel_filename: str = wheel.wheelpath.name rich_print("{green}***", f"{{bold}}Created{{normal}} {wheel_filename}") if settings.messages.after_success: rich_print(settings.messages.after_success) return WheelImplReturn( wheel_filename=wheel_filename, mapping=mapping, settings=settings ) scikit-build-core-0.11.1/src/scikit_build_core/builder/000077500000000000000000000000001477275177200230205ustar00rootroot00000000000000scikit-build-core-0.11.1/src/scikit_build_core/builder/__init__.py000066400000000000000000000002631477275177200251320ustar00rootroot00000000000000""" The items in this module are general tools for building wheels, useful by both the build backend and plugins. """ from __future__ import annotations __all__: list[str] = [] scikit-build-core-0.11.1/src/scikit_build_core/builder/__main__.py000066400000000000000000000020471477275177200251150ustar00rootroot00000000000000from __future__ import annotations import sys from pathlib import Path from .. import __version__ from .._logging import rich_print from ..program_search import info_print as ip_program_search from .get_requires import GetRequires from .sysconfig import info_print as ip_sysconfig from .wheel_tag import WheelTag __all__ = ["main"] def __dir__() -> list[str]: return __all__ def main() -> None: rich_print( f"{{bold}}Scikit-build-core {__version__}{{normal}} on Python {sys.version}" ) ip_sysconfig(color="green") rich_print(f"{{bold.blue}}Default Wheel Tag:{{normal}} {WheelTag.compute_best([])}") rich_print( "{blue} - Note: use {bold}python -m scikit_build_core.builder.wheel_tag -h{normal} for further options" ) if Path("pyproject.toml").is_file(): req = GetRequires() all_req = [*req.cmake(), *req.ninja(), *req.dynamic_metadata()] rich_print(f"{{bold.red}}Get Requires:{{normal}} {all_req!r}") ip_program_search(color="magenta") if __name__ == "__main__": main() scikit-build-core-0.11.1/src/scikit_build_core/builder/builder.py000066400000000000000000000260621477275177200250260ustar00rootroot00000000000000from __future__ import annotations import dataclasses import os import re import shlex import sys import sysconfig from pathlib import Path from typing import TYPE_CHECKING from .. import __version__ from .._compat.importlib import metadata, resources from .._logging import logger from ..resources import find_python from .generator import set_environment_for_gen from .sysconfig import ( get_numpy_include_dir, get_platform, get_python_include_dir, get_python_library, get_soabi, ) if TYPE_CHECKING: from collections.abc import Generator, Iterable, Mapping, Sequence from packaging.version import Version from ..cmake import CMaker from ..settings.skbuild_model import ScikitBuildSettings __all__ = ["Builder", "archs_to_tags", "get_archs"] DIR = Path(__file__).parent.resolve() def __dir__() -> list[str]: return __all__ # TODO: cross-compile support for other platforms def get_archs(env: Mapping[str, str], cmake_args: Sequence[str] = ()) -> list[str]: """ Takes macOS platform settings and returns a list of platforms. Example (macOS): ARCHFLAGS="-arch x86_64" -> ["x86_64"] ARCHFLAGS="-arch x86_64 -arch arm64" -> ["x86_64", "arm64"] Returns an empty list otherwise or if ARCHFLAGS is not set. """ if sys.platform.startswith("darwin"): for cmake_arg in cmake_args: if "CMAKE_SYSTEM_PROCESSOR" in cmake_arg: return [cmake_arg.split("=")[1]] return re.findall(r"-arch (\S+)", env.get("ARCHFLAGS", "")) if sys.platform.startswith("win") and get_platform(env) == "win-arm64": return ["win_arm64"] return [] def archs_to_tags(archs: list[str]) -> list[str]: """ Convert a list of architectures to a list of tags (e.g. "universal2"). """ if sys.platform.startswith("darwin") and set(archs) == {"arm64", "x86_64"}: return ["universal2"] return archs def _filter_env_cmake_args(env_cmake_args: list[str]) -> Generator[str, None, None]: """ Filter out CMake arguments that are not supported from CMAKE_ARGS. """ unsupported_args = ("-DCMAKE_BUILD_TYPE", "-DCMAKE_INSTALL_PREFIX") for arg in env_cmake_args: if arg.startswith(unsupported_args): logger.warning("Unsupported CMAKE_ARGS ignored: {}", arg) else: yield arg def _sanitize_path(path: os.PathLike[str]) -> list[Path]: # This handles classes like: # MultiplexedPath from importlib.resources.readers (3.11+) # MultiplexedPath from importlib.readers (3.10) # MultiplexedPath from importlib_resources.readers if hasattr(path, "_paths"): # pylint: disable-next=protected-access return [Path(os.fspath(p)) for p in path._paths] return [Path(os.fspath(path))] @dataclasses.dataclass class Builder: settings: ScikitBuildSettings config: CMaker def get_cmake_args(self) -> list[str]: """ Get CMake args from the settings and environment. """ # Adding CMake arguments set as environment variable # (needed e.g. to build for ARM OSX on conda-forge) env_cmake_args: list[str] = list( filter(None, shlex.split(self.config.env.get("CMAKE_ARGS", ""))) ) if env_cmake_args: logger.debug("Env CMAKE_ARGS: {}", env_cmake_args) return [*self.settings.cmake.args, *_filter_env_cmake_args(env_cmake_args)] def get_generator(self, *args: str) -> str | None: return self.config.get_generator(*self.get_cmake_args(), *args) def _get_entry_point_search_path(self, entry_point: str) -> dict[str, list[Path]]: """Get the search path dict from the entry points""" search_paths = {} eps = metadata.entry_points(group=entry_point) if eps: logger.debug( "Loading search paths {} from entry-points: {}", entry_point, len(eps) ) for ep in eps: ep_value = _sanitize_path(resources.files(ep.load())) logger.debug("{}: {} -> {}", ep.name, ep.value, ep_value) if ep_value: search_paths[ep.name] = ep_value return search_paths def configure( self, *, defines: Mapping[str, str | bool | Path], cache_entries: Mapping[str, str | Path] | None = None, name: str | None = None, version: Version | None = None, limited_api: bool | None = None, configure_args: Iterable[str] = (), ) -> None: cmake_defines = { k: ("TRUE" if v else "FALSE") if isinstance(v, bool) else str(v) for k, v in defines.items() } # Add any extra CMake modules self.config.module_dirs.extend( p for ep_paths in self._get_entry_point_search_path("cmake.module").values() for p in ep_paths ) logger.debug("cmake.modules: {}", self.config.module_dirs) # Add any extra CMake prefixes self.config.prefix_dirs.extend( p for ep_paths in self._get_entry_point_search_path("cmake.prefix").values() for p in ep_paths ) logger.debug("cmake.prefix: {}", self.config.prefix_dirs) # Add all CMake roots # TODO: Check for unique uppercase names self.config.prefix_roots.update(self._get_entry_point_search_path("cmake.root")) logger.debug("cmake.root: {}", self.config.prefix_roots) # Add site-packages to the prefix path for CMake site_packages = Path(sysconfig.get_path("purelib")) if self.settings.search.site_packages: self.config.prefix_dirs.append(site_packages) logger.debug("SITE_PACKAGES: {}", site_packages) if site_packages != DIR.parent.parent: self.config.prefix_dirs.append(DIR.parent.parent) logger.debug("Extra SITE_PACKAGES: {}", DIR.parent.parent) logger.debug("PATH: {}", sys.path) # Add the FindPython backport if needed if self.config.cmake.version < self.settings.backport.find_python: fp_dir = Path(find_python.__file__).parent.resolve() self.config.module_dirs.append(fp_dir) logger.debug("FindPython backport activated at {}", fp_dir) current_gen = self.get_generator(*configure_args) local_def = set_environment_for_gen( current_gen, self.config.cmake, self.config.env, self.settings.ninja ) cmake_defines.update(local_def) cache_config: dict[str, str | Path | bool] = { "SKBUILD": "2", "SKBUILD_CORE_VERSION": __version__, } if name is not None: canonical_name = name.replace("-", "_").replace(".", "_") cache_config["SKBUILD_PROJECT_NAME"] = canonical_name if version is not None: cache_config["SKBUILD_PROJECT_VERSION"] = ".".join( str(v) for v in version.release ) cache_config["SKBUILD_PROJECT_VERSION_FULL"] = str(version) if limited_api is None: if self.settings.wheel.py_api.startswith("cp3"): target_minor_version = int(self.settings.wheel.py_api[3:]) limited_api = target_minor_version <= sys.version_info.minor else: limited_api = False if limited_api and sys.implementation.name != "cpython": limited_api = False logger.info("PyPy doesn't support the Limited API, ignoring") if limited_api and sysconfig.get_config_var("Py_GIL_DISABLED"): limited_api = False logger.info( "Free-threaded Python doesn't support the Limited API currently, ignoring" ) python_library = get_python_library(self.config.env, abi3=False) python_sabi_library = ( get_python_library(self.config.env, abi3=True) if limited_api else None ) python_include_dir = get_python_include_dir() numpy_include_dir = get_numpy_include_dir() # Classic Find Python cache_config["PYTHON_EXECUTABLE"] = sys.executable cache_config["PYTHON_INCLUDE_DIR"] = python_include_dir if python_library: cache_config["PYTHON_LIBRARY"] = python_library # Modern Find Python for prefix in ("Python", "Python3"): cache_config[f"{prefix}_EXECUTABLE"] = sys.executable cache_config[f"{prefix}_ROOT_DIR"] = sys.prefix cache_config[f"{prefix}_INCLUDE_DIR"] = python_include_dir cache_config[f"{prefix}_FIND_REGISTRY"] = "NEVER" # FindPython may break if this is set - only useful on Windows if python_library and sysconfig.get_platform().startswith("win"): cache_config[f"{prefix}_LIBRARY"] = python_library if python_sabi_library and sysconfig.get_platform().startswith("win"): cache_config[f"{prefix}_SABI_LIBRARY"] = python_sabi_library if numpy_include_dir: cache_config[f"{prefix}_NumPy_INCLUDE_DIR"] = numpy_include_dir cache_config["SKBUILD_SOABI"] = get_soabi(self.config.env, abi3=limited_api) # Allow CMakeLists to detect this is supposed to be a limited ABI build cache_config["SKBUILD_SABI_COMPONENT"] = ( "Development.SABIModule" if limited_api else "" ) # Allow users to detect the version requested in settings py_api = self.settings.wheel.py_api cache_config["SKBUILD_SABI_VERSION"] = ( f"{py_api[2]}.{py_api[3:]}" if limited_api and py_api.startswith("cp") else "" ) if cache_entries: cache_config.update(cache_entries) self.config.init_cache(cache_config) if sys.platform.startswith("darwin"): # Cross-compile support for macOS - respect ARCHFLAGS if set archs = get_archs(self.config.env) if archs: cmake_defines["CMAKE_OSX_ARCHITECTURES"] = ";".join(archs) # Add the pre-defined or passed CMake defines cmake_defines.update(self.settings.cmake.define) self.config.configure( defines=cmake_defines, cmake_args=[*self.get_cmake_args(), *configure_args], ) def build(self, build_args: Sequence[str]) -> None: build_tool_args = self.settings.build.tool_args if build_tool_args: build_args = [*build_args, "--", *build_tool_args] self.config.build( build_args=build_args, targets=self.settings.build.targets, verbose=self.settings.build.verbose, ) def install(self, install_dir: Path | None) -> None: """ Install to a path. Warning: if a package hard-codes CMAKE_INSTALL_PREFIX in the install commands, this will not rewrite those; set that variable when configuring for maximum compatibility. """ components = self.settings.install.components strip = self.settings.install.strip assert strip is not None self.config.install(install_dir, strip=strip, components=components) scikit-build-core-0.11.1/src/scikit_build_core/builder/generator.py000066400000000000000000000100741477275177200253620ustar00rootroot00000000000000from __future__ import annotations import re import subprocess import sys import sysconfig from typing import TYPE_CHECKING from .._logging import logger from ..errors import NinjaNotFoundError from ..program_search import best_program, get_make_programs, get_ninja_programs from .sysconfig import get_cmake_platform if TYPE_CHECKING: from collections.abc import Mapping, MutableMapping from ..cmake import CMake from ..settings.skbuild_model import NinjaSettings __all__ = ["set_environment_for_gen"] def __dir__() -> list[str]: return __all__ def parse_help_default(txt: str) -> str | None: """ Parses the default generator from the output of cmake --help. """ lines: list[str] = re.findall( r"^\*\s*(.*?)(?:\s*\[arch\])?\s*= Generate", txt, re.MULTILINE ) if len(lines) != 1: return None return lines[0] def get_default_from_cmake(cmake: CMake) -> str | None: """ Returns the default generator for the current platform from CMake's output. None if it cannot be determined. """ result = subprocess.run( [str(cmake.cmake_path), "--help"], check=False, capture_output=True, encoding="utf-8", ) if result.returncode != 0: return None return parse_help_default(result.stdout) def get_default(cmake: CMake) -> str | None: """ Returns the computed default for the current platform. """ generator = get_default_from_cmake(cmake) # Non-MSVC Windows platforms require Ninja is_msvc_platform = sysconfig.get_platform().startswith("win") if sys.platform.startswith("win") and not is_msvc_platform: return "Ninja" # Try Ninja if it is available, even if make is CMake default if generator == "Unix Makefiles": return "Ninja" return generator def set_environment_for_gen( generator: str | None, cmake: CMake, env: MutableMapping[str, str], ninja_settings: NinjaSettings, ) -> Mapping[str, str]: """ This function modifies the environment as needed to safely set a generator. You should have used CMAKE_GENERATOR already to get the input generator string. A reasonable default generator is set if the environment does not already have one set; if ninja is present, ninja will be used over make on Unix. If gen is not None, then that will be the target generator. """ allow_make_fallback = ninja_settings.make_fallback if generator: logger.debug("Set generator: {}", generator) allow_make_fallback = False else: generator = get_default(cmake) or "" if generator: logger.debug("Default generator: {}", generator) if sysconfig.get_platform().startswith("win") and "Visual Studio" in generator: # This must also be set when *_PLATFORM is set. env.setdefault("CMAKE_GENERATOR", generator) env.setdefault("CMAKE_GENERATOR_PLATFORM", get_cmake_platform(env)) return {} # Set Python's recommended CC and CXX if not already set by the user if "CC" not in env: cc = sysconfig.get_config_var("CC") if cc: env["CC"] = cc if "CXX" not in env: cxx = sysconfig.get_config_var("CXX") if cxx: env["CXX"] = cxx if (generator or "Ninja") == "Ninja": ninja = best_program(get_ninja_programs(), version=ninja_settings.version) if ninja is not None: env.setdefault("CMAKE_GENERATOR", "Ninja") logger.debug("CMAKE_GENERATOR: Using ninja: {}", ninja.path) return {"CMAKE_MAKE_PROGRAM": str(ninja.path)} msg = "Ninja is required to build" if not allow_make_fallback: raise NinjaNotFoundError(msg) msg = "Ninja or make is required to build" make_programs = list(get_make_programs()) if not make_programs: raise NinjaNotFoundError(msg) env.setdefault("CMAKE_GENERATOR", "Unix Makefiles") logger.debug("CMAKE_GENERATOR: Using make: {}", make_programs[0]) return {"CMAKE_MAKE_PROGRAM": str(make_programs[0])} return {} scikit-build-core-0.11.1/src/scikit_build_core/builder/get_requires.py000066400000000000000000000121201477275177200260640ustar00rootroot00000000000000from __future__ import annotations import dataclasses import functools import importlib.util import os import sysconfig from typing import TYPE_CHECKING, Literal from packaging.tags import sys_tags from .._compat import tomllib from .._logging import logger from ..format import pyproject_format from ..program_search import ( best_program, get_cmake_programs, get_make_programs, get_ninja_programs, ) from ..resources import resources from ..settings._load_provider import load_provider from ..settings.skbuild_read_settings import SettingsReader if TYPE_CHECKING: from collections.abc import Generator, Mapping from .._compat.typing import Self from ..settings.skbuild_model import ScikitBuildSettings __all__ = ["GetRequires"] def __dir__() -> list[str]: return __all__ def _uses_ninja_generator(settings: ScikitBuildSettings) -> bool | None: """ Returns True if Ninja is set, False if something else is set, and None otherwise. """ gen_args = [arg[2:] for arg in settings.cmake.args if arg.startswith("-G")] if gen_args: return any("Ninja" in gen for gen in gen_args) if "CMAKE_GENERATOR" in os.environ: return "Ninja" in os.environ["CMAKE_GENERATOR"] return None @functools.lru_cache(maxsize=2) def known_wheels(name: Literal["ninja", "cmake"]) -> frozenset[str]: with resources.joinpath("known_wheels.toml").open("rb") as f: return frozenset(tomllib.load(f)["tool"]["scikit-build"][name]["known-wheels"]) @functools.lru_cache(maxsize=2) def is_known_platform(platforms: frozenset[str]) -> bool: return any(tag.platform in platforms for tag in sys_tags()) def _load_scikit_build_settings( config_settings: Mapping[str, list[str] | str] | None = None, ) -> ScikitBuildSettings: return SettingsReader.from_file("pyproject.toml", config_settings).settings @dataclasses.dataclass(frozen=True) class GetRequires: settings: ScikitBuildSettings = dataclasses.field( default_factory=_load_scikit_build_settings ) @classmethod def from_config_settings( cls, config_settings: Mapping[str, list[str] | str] | None ) -> Self: return cls(_load_scikit_build_settings(config_settings)) def cmake(self) -> Generator[str, None, None]: if self.settings.fail or os.environ.get("CMAKE_EXECUTABLE", ""): return cmake_verset = self.settings.cmake.version # If the module is already installed (via caching the build # environment, for example), we will use that if importlib.util.find_spec("cmake") is not None: yield f"cmake{cmake_verset}" return cmake = best_program(get_cmake_programs(module=False), version=cmake_verset) if cmake is None: yield f"cmake{cmake_verset}" return logger.debug("Found system CMake: {} - not requiring PyPI package", cmake) def ninja(self) -> Generator[str, None, None]: # Check to see if Ninja is clearly not used use_ninja = _uses_ninja_generator(self.settings) if use_ninja is False: return # On Windows MSVC, Ninja is not default if self.settings.fail or ( sysconfig.get_platform().startswith("win") and use_ninja is None ): return # If CMAKE_MAKE_PROGRAM is set, don't add anything, someone already knows what they want if os.environ.get("CMAKE_MAKE_PROGRAM", ""): return ninja_verset = self.settings.ninja.version # If the module is already installed (via caching the build # environment, for example), we will use that if importlib.util.find_spec("ninja") is not None: yield f"ninja{ninja_verset}" return ninja = best_program(get_ninja_programs(module=False), version=ninja_verset) if ninja is not None: logger.debug("Found system Ninja: {} - not requiring PyPI package", ninja) return if ( self.settings.ninja.make_fallback and not is_known_platform(known_wheels("ninja")) and list(get_make_programs()) ): logger.debug( "Found system Make & not on known platform - not requiring PyPI package for Ninja" ) return yield f"ninja{ninja_verset}" def dynamic_metadata(self) -> Generator[str, None, None]: if self.settings.fail: return for build_require in self.settings.build.requires: yield build_require.format( **pyproject_format( settings=self.settings, ) ) for dynamic_metadata in self.settings.metadata.values(): if "provider" in dynamic_metadata: config = dynamic_metadata.copy() provider = config.pop("provider") provider_path = config.pop("provider-path", None) module = load_provider(provider, provider_path) yield from getattr( module, "get_requires_for_dynamic_metadata", lambda _: [] )(config) scikit-build-core-0.11.1/src/scikit_build_core/builder/macos.py000066400000000000000000000034261477275177200245010ustar00rootroot00000000000000from __future__ import annotations import os import platform from typing import NamedTuple from .._logging import logger __all__ = ["MacOSVer", "get_macosx_deployment_target", "normalize_macos_version"] class MacOSVer(NamedTuple): major: int minor: int def __str__(self) -> str: return f"{self.major}.{self.minor}" def __dir__() -> list[str]: return __all__ def normalize_macos_version(version: str, *, arm: bool) -> MacOSVer: """ Set minor version to 0 if major is 11+. """ if "." not in version: version = f"{version}.0" major, minor = (int(d) for d in version.split(".")[:2]) major = max(major, 11) if arm else major minor = 0 if major >= 11 else minor return MacOSVer(major, minor) def get_macosx_deployment_target(*, arm: bool) -> MacOSVer: """ Get the MACOSX_DEPLOYMENT_TARGET environment variable. If not set, use the current macOS version. If arm=True, then this will always return at least (11, 0). Versions after 11 will be normalized to 0 for minor version. """ target = os.environ.get("MACOSX_DEPLOYMENT_TARGET", None) plat_ver_str, _, _ = platform.mac_ver() plat_target = normalize_macos_version(plat_ver_str, arm=arm) if target is None: logger.debug("MACOSX_DEPLOYMENT_TARGET not set, using {}", plat_target) return plat_target env_target = ".".join(target.split(".")[:2]) if "." in target else f"{target}.0" try: norm_env_target = normalize_macos_version(env_target, arm=arm) except ValueError: msg = "MACOSX_DEPLOYMENT_TARGET not readable ({}), using {} instead" logger.warning(msg, env_target, plat_target) return plat_target logger.debug("MACOSX_DEPLOYMENT_TARGET is set to {}", env_target) return norm_env_target scikit-build-core-0.11.1/src/scikit_build_core/builder/sysconfig.py000066400000000000000000000170621477275177200254040ustar00rootroot00000000000000from __future__ import annotations import configparser import os import sys import sysconfig from pathlib import Path from typing import TYPE_CHECKING, Literal import packaging.tags from .._logging import logger, rich_print if TYPE_CHECKING: from collections.abc import Mapping __all__ = [ "get_abi_flags", "get_cmake_platform", "get_numpy_include_dir", "get_python_include_dir", "get_python_library", "get_soabi", "info_print", ] TARGET_TO_PLAT = { "x86": "win32", "x64": "win-amd64", "arm": "win-arm32", "arm64": "win-arm64", } PLAT_TO_CMAKE = { "win32": "Win32", "win-amd64": "x64", "win-arm32": "ARM", "win-arm64": "ARM64", } def __dir__() -> list[str]: return __all__ def get_python_library(env: Mapping[str, str], *, abi3: bool = False) -> Path | None: # When cross-compiling, check DIST_EXTRA_CONFIG first config_file = env.get("DIST_EXTRA_CONFIG", None) if config_file and Path(config_file).is_file(): cp = configparser.ConfigParser() cp.read(config_file) result = cp.get("build_ext", "library_dirs", fallback="") if result: logger.info("Reading DIST_EXTRA_CONFIG:build_ext.library_dirs={}", result) minor = "" if abi3 else sys.version_info[1] if env.get("SETUPTOOLS_EXT_SUFFIX", "").endswith("t.pyd"): return Path(result) / f"python3{minor}t.lib" return Path(result) / f"python3{minor}.lib" libdirstr = sysconfig.get_config_var("LIBDIR") ldlibrarystr = sysconfig.get_config_var("LDLIBRARY") librarystr = sysconfig.get_config_var("LIBRARY") if abi3: if ldlibrarystr is not None: ldlibrarystr = ldlibrarystr.replace( f"python3{sys.version_info[1]}", "python3" ) if librarystr is not None: librarystr = librarystr.replace(f"python3{sys.version_info[1]}", "python3") libdir: Path | None = libdirstr and Path(libdirstr) ldlibrary: Path | None = ldlibrarystr and Path(ldlibrarystr) library: Path | None = librarystr and Path(librarystr) multiarch: str | None = sysconfig.get_config_var("MULTIARCH") masd: str | None = sysconfig.get_config_var("multiarchsubdir") log_func = logger.warning if sys.platform.startswith("win") else logger.debug if libdir and ldlibrary: try: libdir_is_dir = libdir.is_dir() except PermissionError: return None if libdir_is_dir: if multiarch and masd: if masd.startswith(os.sep): masd = masd[len(os.sep) :] libdir_masd = libdir / masd if libdir_masd.is_dir(): libdir = libdir_masd libpath = libdir / ldlibrary if Path(os.path.expandvars(libpath)).is_file(): return libpath if library: libpath = libdir / library if sys.platform.startswith("win") and libpath.suffix == ".dll": libpath = libpath.with_suffix(".lib") if Path(os.path.expandvars(libpath)).is_file(): return libpath log_func("libdir/(ld)library: {} is not a real file!", libpath) else: log_func("libdir: {} is not a directory", libdir) framework_prefix = sysconfig.get_config_var("PYTHONFRAMEWORKPREFIX") if framework_prefix and Path(framework_prefix).is_dir() and ldlibrary: libpath = Path(framework_prefix) / ldlibrary if libpath.is_file(): return libpath log_func( "Can't find a Python library, got libdir={}, ldlibrary={}, multiarch={}, masd={}", libdir, ldlibrary, multiarch, masd, ) return None def get_python_include_dir() -> Path: return Path(sysconfig.get_path("include")) def get_host_platform() -> str: """ Return a string that identifies the current platform. This mimics setuptools get_host_platform (without 3.8 aix compat). """ return sysconfig.get_platform() def get_platform(env: Mapping[str, str] | None = None) -> str: """ Return the Python platform name for a platform, respecting VSCMD_ARG_TGT_ARCH. """ if env is None: env = os.environ if sysconfig.get_platform().startswith("win"): if "VSCMD_ARG_TGT_ARCH" in env: logger.debug( "Selecting {} or {} due to VSCMD_ARG_TARGET_ARCH", TARGET_TO_PLAT.get(env["VSCMD_ARG_TGT_ARCH"]), get_host_platform(), ) return TARGET_TO_PLAT.get(env["VSCMD_ARG_TGT_ARCH"]) or get_host_platform() if "arm64" in env.get("SETUPTOOLS_EXT_SUFFIX", "").lower(): logger.debug("Windows ARM targeted via SETUPTOOLS_EXT_SUFFIX") return "win-arm64" return get_host_platform() def get_cmake_platform(env: Mapping[str, str] | None) -> str: """ Return the CMake platform name for a platform, respecting VSCMD_ARG_TGT_ARCH. """ plat = get_platform(env) return PLAT_TO_CMAKE.get(plat, plat) def get_soabi(env: Mapping[str, str], *, abi3: bool = False) -> str: if abi3: return "" if sysconfig.get_platform().startswith("win") else "abi3" # Cross-compile support setuptools_ext_suffix = env.get("SETUPTOOLS_EXT_SUFFIX", "") if setuptools_ext_suffix: return setuptools_ext_suffix.rsplit(".", 1)[0].lstrip(".") if sys.version_info < (3, 8, 7): # See https://github.com/python/cpython/issues/84006 import distutils.sysconfig # pylint: disable=deprecated-module ext_suffix = distutils.sysconfig.get_config_var("EXT_SUFFIX") else: ext_suffix = sysconfig.get_config_var("EXT_SUFFIX") assert isinstance(ext_suffix, str) return ext_suffix.rsplit(".", 1)[0].lstrip(".") def get_numpy_include_dir() -> Path | None: try: import numpy as np except ImportError: return None return Path(np.get_include()) def get_abi_flags() -> str: """ Return the ABI flags for the current Python interpreter. Derived from ``packaging.tags.sys_tags()`` since that works on Windows. """ most_compatible = next(iter(packaging.tags.sys_tags())) full_abi = most_compatible.abi vers = packaging.tags.interpreter_version() abi_flags = full_abi[full_abi.find(vers) + len(vers) :] return "".join(sorted(abi_flags)) def info_print( *, color: Literal[ "", "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white" ] = "", ) -> None: """ Print information about the Python environment. """ rich_print( "{bold}Detected Python Library:", get_python_library(os.environ, abi3=False), color=color, ) rich_print( "{bold}Detected ABI3 Python Library:", get_python_library(os.environ, abi3=True), color=color, ) rich_print( "{bold}Detected Python Include Directory:", get_python_include_dir(), color=color, ) rich_print( "{bold}Detected NumPy Include Directory:", get_numpy_include_dir(), color=color, ) rich_print( "{bold}Detected Platform:", get_platform(), color=color, ) rich_print( "{bold}Detected SOABI:", get_soabi(os.environ, abi3=False), color=color, ) rich_print( "{color}Detected ABI3 SOABI:", get_soabi(os.environ, abi3=True), color=color, ) rich_print( "{bold}Detected ABI flags:", get_abi_flags(), color=color, ) if __name__ == "__main__": info_print() scikit-build-core-0.11.1/src/scikit_build_core/builder/wheel_tag.py000066400000000000000000000132551477275177200253370ustar00rootroot00000000000000from __future__ import annotations import dataclasses import itertools import sys import sysconfig from typing import TYPE_CHECKING import packaging.tags from .._logging import logger from .macos import get_macosx_deployment_target if TYPE_CHECKING: from collections.abc import Iterable, Sequence from .._compat.typing import Self __all__ = ["WheelTag"] def __dir__() -> list[str]: return __all__ @dataclasses.dataclass(frozen=True) class WheelTag: pyvers: list[str] abis: list[str] archs: list[str] build_tag: str = "" # TODO: plats only used on macOS & Windows @classmethod def compute_best( cls, archs: Sequence[str], py_api: str = "", *, expand_macos: bool = False, root_is_purelib: bool = False, build_tag: str = "", ) -> Self: if build_tag: if not build_tag[0].isdigit(): msg = f"Unexpected build-tag, must start with a digit, {build_tag!r} invalid" raise AssertionError(msg) if "-" in build_tag: msg = f"Unexpected build-tag, {build_tag!r} cannot contain dashes" raise AssertionError(msg) # manylinux sometimes comes before linux, but can't assume manylinux, use auditwheel instead best_tag = next( iter( p for p in packaging.tags.sys_tags() if "manylinux" not in p.platform and "musllinux" not in p.platform ) ) interp, abi, *plats = (best_tag.interpreter, best_tag.abi, best_tag.platform) pyvers = [interp] if sys.platform.startswith("win") and archs: plats = [x.replace("-", "_") for x in archs] elif sys.platform.startswith("darwin"): pairs: Iterable[tuple[str | None, bool]] if expand_macos and archs == ["universal2"]: pairs = zip( ["universal2", "universal2", "x86_64", "arm64"], [False, True, False, True], ) elif not archs: # It's okay to set arm to False, since this would be a native build, # and that will already be 11+ for ARM anyway. pairs = zip([None], [False]) else: pairs = zip(archs, [a == "arm64" for a in archs]) plats = [ next( packaging.tags.mac_platforms( get_macosx_deployment_target(arm=arm), arch ) ) for arch, arm in pairs ] # Remove duplicates (e.g. universal2 if macOS > 11.0 and expanded) plats = list(dict.fromkeys(plats)) if root_is_purelib: plats = ["any"] abi = "none" pyvers = ["py3"] if py_api: pyvers_new = py_api.split(".") if all(x.startswith("cp3") and x[3:].isdecimal() for x in pyvers_new): if len(pyvers_new) != 1: msg = "Unexpected py-api, must be a single cp version (e.g. cp39), not {py_api}" raise AssertionError(msg) if root_is_purelib: msg = f"Unexpected py-api, since platlib is set to false, must be Pythonless (e.g. py2.py3), not {py_api}" raise AssertionError(msg) minor = int(pyvers_new[0][3:]) if ( sys.implementation.name == "cpython" and minor <= sys.version_info.minor and not sysconfig.get_config_var("Py_GIL_DISABLED") ): pyvers = pyvers_new abi = "abi3" else: msg = "Ignoring py-api, not a CPython interpreter ({}) or version (3.{}) is too high or free-threaded" logger.debug(msg, sys.implementation.name, minor) elif all(x.startswith("py") and x[2:].isdecimal() for x in pyvers_new): pyvers = pyvers_new abi = "none" else: msg = f"Unexpected py-api, must be abi3 (e.g. cp39) or Pythonless (e.g. py2.py3), not {py_api}" raise AssertionError(msg) return cls(pyvers=pyvers, abis=[abi], archs=plats, build_tag=build_tag) @property def pyver(self) -> str: return ".".join(self.pyvers) @property def abi(self) -> str: return ".".join(self.abis) @property def arch(self) -> str: return ".".join(self.archs) def __str__(self) -> str: if self.build_tag: return f"{self.build_tag}-{self.pyver}-{self.abi}-{self.arch}" return f"{self.pyver}-{self.abi}-{self.arch}" def tags_dict(self) -> dict[str, list[str]]: return { "pyver": self.pyvers, "abi": self.abis, "arch": self.archs, } def as_tags_set(self) -> frozenset[packaging.tags.Tag]: vals = itertools.product(self.pyvers, self.abis, self.archs) return frozenset(itertools.starmap(packaging.tags.Tag, vals)) if __name__ == "__main__": import argparse parser = argparse.ArgumentParser() parser.add_argument( "--archs", nargs="*", default=[], help="Specify one or more archs (macOS only currently)", ) parser.add_argument( "--abi", default="", help="Specify py-api, like 'cp38' or 'py3'", ) parser.add_argument( "--purelib", action="store_true", help="Specify a non-platlib (pure) tag", ) args = parser.parse_args() tag = WheelTag.compute_best(args.archs, args.abi, root_is_purelib=args.purelib) print(tag) # noqa: T201 scikit-build-core-0.11.1/src/scikit_build_core/cmake.py000066400000000000000000000260231477275177200230270ustar00rootroot00000000000000from __future__ import annotations import contextlib import dataclasses import json import os import shutil import subprocess import sys import sysconfig import textwrap from pathlib import Path from typing import TYPE_CHECKING, Any from . import __version__ from ._logging import logger from ._shutil import Run from .errors import CMakeConfigError, CMakeNotFoundError, FailedLiveProcessError from .program_search import Program, best_program, get_cmake_program, get_cmake_programs if TYPE_CHECKING: from collections.abc import Generator, Iterable, Mapping, Sequence from packaging.specifiers import SpecifierSet from packaging.version import Version from ._compat.typing import Self __all__ = ["CMake", "CMaker"] def __dir__() -> list[str]: return __all__ DIR = Path(__file__).parent.resolve() @dataclasses.dataclass(frozen=True) class CMake: version: Version cmake_path: Path @classmethod def default_search( cls, *, version: SpecifierSet | None = None, module: bool = True, env: Mapping[str, Any] | None = None, ) -> Self: env = env or {} cmake_executable = env.get("CMAKE_EXECUTABLE", "") candidates: Iterable[Program] = ( [get_cmake_program(Path(cmake_executable))] if cmake_executable else get_cmake_programs(module=module) ) cmake_program = best_program(candidates, version=version) if cmake_program is None: msg = f"Could not find CMake with version {version}" raise CMakeNotFoundError(msg) if cmake_program.version is None: msg = "CMake version undetermined @ {program.path}" raise CMakeNotFoundError(msg) return cls(version=cmake_program.version, cmake_path=cmake_program.path) def __fspath__(self) -> str: return os.fspath(self.cmake_path) @dataclasses.dataclass class CMaker: cmake: CMake source_dir: Path build_dir: Path build_type: str module_dirs: list[Path] = dataclasses.field(default_factory=list) prefix_dirs: list[Path] = dataclasses.field(default_factory=list) prefix_roots: dict[str, list[Path]] = dataclasses.field(default_factory=dict) init_cache_file: Path = dataclasses.field(init=False, default=Path()) env: dict[str, str] = dataclasses.field(init=False, default_factory=os.environ.copy) single_config: bool = not sysconfig.get_platform().startswith("win") def __post_init__(self) -> None: self.init_cache_file = self.build_dir / "CMakeInit.txt" source_dir = self.source_dir.resolve() if not self.source_dir.is_dir(): msg = f"source directory {self.source_dir} does not exist" raise CMakeConfigError(msg) self.build_dir.mkdir(parents=True, exist_ok=True) if not self.build_dir.is_dir(): msg = f"build directory {self.build_dir} must be a (creatable) directory" raise CMakeConfigError(msg) skbuild_info = self.build_dir / ".skbuild-info.json" stale = False info: dict[str, str] = {} with contextlib.suppress(FileNotFoundError), skbuild_info.open( "r", encoding="utf-8" ) as f: info = json.load(f) if info: # If building via SDist, this could be pre-filled cached_source_dir = Path(info["source_dir"]) if cached_source_dir != source_dir: logger.warning( "Original src {} != {}, clearing cache", cached_source_dir, source_dir, ) stale = True # Isolated environments can cause this cached_skbuild_dir = Path(info["skbuild_path"]) if cached_skbuild_dir != DIR: logger.info( "New isolated environment {} -> {}, clearing cache", cached_skbuild_dir, DIR, ) stale = True # Not using --fresh here, not just due to CMake 3.24+, but also just in # case it triggers an extra FetchContent pull in CMake 3.30+ if stale: # Python 3.8+ can use missing_ok=True with contextlib.suppress(FileNotFoundError): self.build_dir.joinpath("CMakeCache.txt").unlink() shutil.rmtree(self.build_dir.joinpath("CMakeFiles"), ignore_errors=True) with skbuild_info.open("w", encoding="utf-8") as f: json.dump(self._info_dict(), f, indent=2) def _info_dict(self) -> dict[str, str]: """ Produce an information dict about the current run that can be stored in a json file. """ return { "source_dir": os.fspath(self.source_dir.resolve()), "build_dir": os.fspath(self.build_dir.resolve()), "cmake_path": os.fspath(self.cmake), "skbuild_path": os.fspath(DIR), "skbuild_version": __version__, "python_executable": sys.executable, } def init_cache( self, cache_settings: Mapping[str, str | os.PathLike[str] | bool] ) -> None: with self.init_cache_file.open("w", encoding="utf-8") as f: for key, value in cache_settings.items(): if isinstance(value, bool): str_value = "ON" if value else "OFF" f.write(f'set({key} {str_value} CACHE BOOL "" FORCE)\n') elif isinstance(value, os.PathLike): # Convert to CMake's internal path format str_value = str(value).replace("\\", "/") f.write(f'set({key} [===[{str_value}]===] CACHE PATH "" FORCE)\n') else: f.write(f'set({key} [===[{value}]===] CACHE STRING "" FORCE)\n') if self.module_dirs: # Convert to CMake's internal path format, otherwise this breaks try_compile on Windows module_dirs_str = ";".join(map(str, self.module_dirs)).replace( "\\", "/" ) f.write( f'set(CMAKE_MODULE_PATH [===[{module_dirs_str}]===] CACHE PATH "" FORCE)\n' ) if self.prefix_dirs: prefix_dirs_str = ";".join(map(str, self.prefix_dirs)).replace( "\\", "/" ) f.write( f'set(CMAKE_PREFIX_PATH [===[{prefix_dirs_str}]===] CACHE PATH "" FORCE)\n' ) f.write('set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE "BOTH" CACHE PATH "")\n') if self.prefix_roots: for pkg, path_list in self.prefix_roots.items(): paths_str = ";".join(map(str, path_list)).replace("\\", "/") f.write( f'set({pkg}_ROOT [===[{paths_str}]===] CACHE PATH "" FORCE)\n' ) # Available since CMake 3.27 with CMP0144 f.write( f'set({pkg.upper()}_ROOT [===[{paths_str}]===] CACHE PATH "" FORCE)\n' ) contents = self.init_cache_file.read_text(encoding="utf-8").strip() logger.debug( "{}:\n{}", self.init_cache_file, textwrap.indent(contents.strip(), " "), ) def _compute_cmake_args( self, defines: Mapping[str, str | os.PathLike[str] | bool] ) -> Generator[str, None, None]: yield f"-S{self.source_dir}" yield f"-B{self.build_dir}" if self.init_cache_file.is_file(): yield f"-C{self.init_cache_file}" for key, value in defines.items(): if isinstance(value, bool): str_value = "ON" if value else "OFF" yield f"-D{key}:BOOL={str_value}" elif isinstance(value, os.PathLike): str_value = str(value).replace("\\", "/") yield f"-D{key}:PATH={str_value}" else: yield f"-D{key}={value}" def get_generator(self, *args: str) -> str | None: """ Try to get the generator that will be used to build the project. If it's not set, return None (default generator will be used). """ generators = [g for g in args if g.startswith("-G")] if generators: return generators[-1][2:].strip() return self.env.get("CMAKE_GENERATOR", None) def configure( self, *, defines: Mapping[str, str | os.PathLike[str] | bool] | None = None, cmake_args: Sequence[str] = (), ) -> None: _cmake_args = self._compute_cmake_args(defines or {}) all_args = [*_cmake_args, *cmake_args] gen = self.get_generator(*all_args) if gen: self.single_config = gen == "Ninja" or "Makefiles" in gen if self.single_config and self.build_type: all_args.insert(2, f"-DCMAKE_BUILD_TYPE:STRING={self.build_type}") try: Run(env=self.env).live(self.cmake, *all_args) except subprocess.CalledProcessError: msg = "CMake configuration failed" raise FailedLiveProcessError(msg) from None def _compute_build_args( self, *, verbose: bool, ) -> Generator[str, None, None]: if verbose: yield "-v" if self.build_type and not self.single_config: yield "--config" yield self.build_type def build( self, build_args: Sequence[str] = (), *, targets: Sequence[str] = (), verbose: bool = False, ) -> None: local_args = list(self._compute_build_args(verbose=verbose)) if not targets: self._build(*local_args, *build_args) return for target in targets: self._build(*local_args, "--target", target, *build_args) def _build(self, *args: str) -> None: try: Run(env=self.env).live(self.cmake, "--build", self.build_dir, *args) except subprocess.CalledProcessError: msg = "CMake build failed" raise FailedLiveProcessError(msg) from None def install( self, prefix: Path | None, *, strip: bool = False, components: Sequence[str] = (), ) -> None: opts = ["--prefix", str(prefix)] if prefix else [] if not self.single_config and self.build_type: opts += ["--config", self.build_type] if strip: opts.append("--strip") if not components: self._install(opts) return for comp in components: opts_with_comp = [*opts, "--component", comp] logger.info("Installing component {}", comp) self._install(opts_with_comp) def _install(self, opts: Sequence[str]) -> None: try: Run(env=self.env).live( self.cmake, "--install", self.build_dir, *opts, ) except subprocess.CalledProcessError: msg = "CMake install failed" raise FailedLiveProcessError(msg) from None scikit-build-core-0.11.1/src/scikit_build_core/errors.py000066400000000000000000000043331477275177200232630ustar00rootroot00000000000000from __future__ import annotations import textwrap from typing import TYPE_CHECKING if TYPE_CHECKING: import subprocess __all__ = [ "CMakeAccessError", "CMakeConfigError", "CMakeNotFoundError", "CMakeVersionError", "FailedLiveProcessError", "FailedProcessError", "NinjaNotFoundError", "NinjaVersionError", "NotFoundError", "ScikitBuildError", ] def __dir__() -> list[str]: return __all__ class ScikitBuildError(Exception): """ Base class for all ScikitBuildError errors. """ class NotFoundError(ScikitBuildError): """ Raised when a program is not found. """ class CMakeNotFoundError(NotFoundError): """ Raised when cmake is not found. """ class NinjaNotFoundError(NotFoundError): """ Raised when ninja is not found. """ class FailedProcessError(Exception): """ Exception raised when an call fails. """ def __init__( self, exception: subprocess.CalledProcessError, description: str ) -> None: super().__init__() self.exception = exception self._description = description def __str__(self) -> str: cmd = " ".join(self.exception.cmd) description = f"{self._description}\n Command {cmd!r} failed with return code {self.exception.returncode}" for stream_name in ("stdout", "stderr"): stream = getattr(self.exception, stream_name) if stream: description += f"\n {stream_name}:\n" description += textwrap.indent(stream.decode(), " ") return description class FailedLiveProcessError(Exception): """ Exception for when output was not being redirected. """ def __init__(self, *args: object, msg: str = "") -> None: super().__init__(*args) self.msg = msg class CMakeAccessError(FailedProcessError): """ Error raised when CMake access fails. """ class CMakeVersionError(ScikitBuildError): """ Error raised when CMake version is not supported. """ class NinjaVersionError(ScikitBuildError): """ Error raised when CMake version is not supported. """ class CMakeConfigError(ScikitBuildError): """ Something is misconfigured. """ scikit-build-core-0.11.1/src/scikit_build_core/file_api/000077500000000000000000000000001477275177200231425ustar00rootroot00000000000000scikit-build-core-0.11.1/src/scikit_build_core/file_api/__init__.py000066400000000000000000000000741477275177200252540ustar00rootroot00000000000000from __future__ import annotations __all__: list[str] = [] scikit-build-core-0.11.1/src/scikit_build_core/file_api/_cattrs_converter.py000066400000000000000000000046341477275177200272510ustar00rootroot00000000000000# pylint: disable=duplicate-code import builtins import json from pathlib import Path from typing import Any, Callable, Dict, Type, TypeVar # noqa: TID251 import cattr import cattr.preconf.json from .model.cache import Cache from .model.cmakefiles import CMakeFiles from .model.codemodel import CodeModel, Target from .model.index import Index, Reply T = TypeVar("T") __all__ = ["load_reply_dir", "make_converter"] def to_path(path: str, _: Type[Path]) -> Path: return Path(path) def make_converter(base_dir: Path) -> cattr.preconf.json.JsonConverter: converter = cattr.preconf.json.make_converter() converter.register_structure_hook(Path, to_path) st_hook = cattr.gen.make_dict_structure_fn( Reply, converter, codemodel_v2=cattr.gen.override(rename="codemodel-v2"), cache_v2=cattr.gen.override(rename="cache-v2"), cmakefiles_v1=cattr.gen.override(rename="cmakeFiles-v1"), toolchains_v1=cattr.gen.override(rename="toolchains-v1"), ) converter.register_structure_hook(Reply, st_hook) def from_json_file(with_path: Dict[str, Any], t: Type[T]) -> T: if with_path["jsonFile"] is None: return converter.structure_attrs_fromdict({}, t) path = base_dir / Path(with_path["jsonFile"]) raw = json.loads(path.read_text(encoding="utf-8")) return converter.structure_attrs_fromdict(raw, t) converter.register_structure_hook(CodeModel, from_json_file) converter.register_structure_hook(Target, from_json_file) converter.register_structure_hook(Cache, from_json_file) converter.register_structure_hook(CMakeFiles, from_json_file) return converter def load_reply_dir(reply_dir: Path) -> Index: converter = make_converter(reply_dir) indexes = sorted(reply_dir.glob("index-*")) if not indexes: msg = f"index file not found in {reply_dir}" raise IndexError(msg) index_file = indexes[-1] return converter.loads(index_file.read_text("utf-8"), Index) if __name__ == "__main__": import argparse rich_print: Callable[[object], None] try: from rich import print as rich_print except ModuleNotFoundError: rich_print = builtins.print parser = argparse.ArgumentParser() parser.add_argument("reply_dir", type=Path, help="Path to the reply directory") args = parser.parse_args() reply = Path(args.reply_dir) rich_print(load_reply_dir(reply)) scikit-build-core-0.11.1/src/scikit_build_core/file_api/model/000077500000000000000000000000001477275177200242425ustar00rootroot00000000000000scikit-build-core-0.11.1/src/scikit_build_core/file_api/model/__init__.py000066400000000000000000000000741477275177200263540ustar00rootroot00000000000000from __future__ import annotations __all__: list[str] = [] scikit-build-core-0.11.1/src/scikit_build_core/file_api/model/cache.py000066400000000000000000000007501477275177200256610ustar00rootroot00000000000000import dataclasses from typing import List from .common import APIVersion __all__ = ["Cache", "Entry", "Property"] def __dir__() -> List[str]: return __all__ @dataclasses.dataclass(frozen=True) class Property: name: str value: str @dataclasses.dataclass(frozen=True) class Entry: name: str value: str type: str properties: List[Property] @dataclasses.dataclass(frozen=True) class Cache: kind: str version: APIVersion entries: List[Entry] scikit-build-core-0.11.1/src/scikit_build_core/file_api/model/cmakefiles.py000066400000000000000000000006711477275177200267230ustar00rootroot00000000000000import dataclasses from pathlib import Path from typing import List from .common import Paths __all__ = ["CMakeFiles", "Input"] def __dir__() -> List[str]: return __all__ @dataclasses.dataclass(frozen=True) class Input: path: Path isGenerated: bool = False isExternal: bool = False isCMake: bool = False @dataclasses.dataclass(frozen=True) class CMakeFiles: kind: str paths: Paths inputs: List[Input] scikit-build-core-0.11.1/src/scikit_build_core/file_api/model/codemodel.py000066400000000000000000000063051477275177200265530ustar00rootroot00000000000000import dataclasses from pathlib import Path from typing import List, Optional from .common import APIVersion, Paths __all__ = [ "Archive", "Artifact", "CodeModel", "CommandFragment", "Configuration", "Dependency", "Destination", "Directory", "Install", "Link", "Prefix", "Project", "Source", "StringCMakeVersion", "Sysroot", "Target", ] def __dir__() -> List[str]: return __all__ @dataclasses.dataclass(frozen=True) class StringCMakeVersion: string: str @dataclasses.dataclass(frozen=True) class Directory: source: Path build: Path projectIndex: int jsonFile: Optional[Path] = None parentIndex: Optional[int] = None childIndexes: List[int] = dataclasses.field(default_factory=list) targetIndexes: List[int] = dataclasses.field(default_factory=list) minimumCMakeVersion: Optional[StringCMakeVersion] = None hasInstallRule: bool = False # Directory is currently not resolved automatically. @dataclasses.dataclass(frozen=True) class Project: name: str directoryIndexes: List[int] parentIndex: Optional[int] = None childIndexes: List[int] = dataclasses.field(default_factory=list) targetIndexes: List[int] = dataclasses.field(default_factory=list) @dataclasses.dataclass(frozen=True) class Artifact: path: Path @dataclasses.dataclass(frozen=True) class Prefix: path: Path @dataclasses.dataclass(frozen=True) class Destination: path: Path backtrace: Optional[int] = None @dataclasses.dataclass(frozen=True) class Install: prefix: Prefix destinations: List[Destination] @dataclasses.dataclass(frozen=True) class CommandFragment: fragment: str role: str @dataclasses.dataclass(frozen=True) class Sysroot: path: Path @dataclasses.dataclass(frozen=True) class Link: language: str commandFragments: List[CommandFragment] lto: Optional[bool] = None sysroot: Optional[Sysroot] = None @dataclasses.dataclass(frozen=True) class Archive: commandFragments: List[CommandFragment] = dataclasses.field(default_factory=list) lto: Optional[bool] = None @dataclasses.dataclass(frozen=True) class Dependency: id: str backtrace: Optional[int] = None @dataclasses.dataclass(frozen=True) class Source: path: Path compileGroupIndex: Optional[int] = None sourceGroupIndex: Optional[int] = None isGenerated: Optional[bool] = None backtrace: Optional[int] = None @dataclasses.dataclass(frozen=True) class Target: name: str id: str type: str paths: Paths sources = List[Source] nameOnDisk: Optional[Path] = None artifacts: List[Artifact] = dataclasses.field(default_factory=list) isGeneratorProvided: Optional[bool] = None install: Optional[Install] = None link: Optional[Link] = None archive: Optional[Archive] = None dependencies: List[Dependency] = dataclasses.field(default_factory=list) @dataclasses.dataclass(frozen=True) class Configuration: name: str projects: List[Project] targets: List[Target] directories: List[Directory] @dataclasses.dataclass(frozen=True) class CodeModel: kind: str version: APIVersion paths: Paths configurations: List[Configuration] scikit-build-core-0.11.1/src/scikit_build_core/file_api/model/common.py000066400000000000000000000005021477275177200261010ustar00rootroot00000000000000import dataclasses from pathlib import Path from typing import List __all__ = ["APIVersion", "Paths"] def __dir__() -> List[str]: return __all__ @dataclasses.dataclass(frozen=True) class APIVersion: major: int minor: int @dataclasses.dataclass(frozen=True) class Paths: source: Path build: Path scikit-build-core-0.11.1/src/scikit_build_core/file_api/model/directory.py000066400000000000000000000032431477275177200266220ustar00rootroot00000000000000import dataclasses from pathlib import Path from typing import List, Optional, Union from .common import Paths __all__ = ["BacktraceGraph", "Directory", "InstallRule", "Node", "Target"] def __dir__() -> List[str]: return __all__ @dataclasses.dataclass(frozen=True) class Target: id: str index: int @dataclasses.dataclass(frozen=True) class InstallRule: component: str type = str destination: Optional[Path] = None paths: List[Union[str, Paths]] = dataclasses.field(default_factory=list) isExcludeFromAll: bool = False isForAllComponents: bool = False isOptional: bool = False targetId: Optional[str] = None targetIndex: Optional[int] = None targetIsImportLibrary: bool = False targetInstallNameLink: Optional[str] = None exportName: Optional[str] = None exportTargets: List[Target] = dataclasses.field(default_factory=list) runtimeDependencySetName: Optional[str] = None runtimeDependencySetType: Optional[str] = None fileSetName: Optional[str] = None fileSetType: Optional[str] = None fileSetDirectories: List[Path] = dataclasses.field(default_factory=list) fileSetTarget: Optional[Target] = None scriptFile: Optional[Path] = None backtrace: Optional[int] = None @dataclasses.dataclass(frozen=True) class Node: file: int line: Optional[int] = None command: Optional[int] = None parent: Optional[int] = None @dataclasses.dataclass(frozen=True) class BacktraceGraph: nodes: List[Node] commands: List[str] files: List[Path] @dataclasses.dataclass(frozen=True) class Directory: paths: Paths installers: List[InstallRule] backtraceGraph: BacktraceGraph scikit-build-core-0.11.1/src/scikit_build_core/file_api/model/index.py000066400000000000000000000025301477275177200257230ustar00rootroot00000000000000import dataclasses from pathlib import Path from typing import List, Optional from .cache import Cache from .cmakefiles import CMakeFiles from .codemodel import CodeModel from .common import APIVersion from .toolchains import Toolchains __all__ = [ "CMake", "CMakePaths", "CMakeVersion", "Generator", "Index", "Object", "Reply", ] def __dir__() -> List[str]: return __all__ @dataclasses.dataclass(frozen=True) class CMakeVersion: major: int minor: int patch: int suffix: str string: str isDirty: bool @dataclasses.dataclass(frozen=True) class CMakePaths: cmake: Path ctest: Path cpack: Path root: Path @dataclasses.dataclass(frozen=True) class Generator: name: str multiConfig: Optional[bool] = None platform: Optional[str] = None @dataclasses.dataclass(frozen=True) class CMake: version: CMakeVersion paths: CMakePaths generator: Generator @dataclasses.dataclass(frozen=True) class Reply: codemodel_v2: Optional[CodeModel] cache_v2: Optional[Cache] cmakefiles_v1: Optional[CMakeFiles] toolchains_v1: Optional[Toolchains] @dataclasses.dataclass(frozen=True) class Object: kind: str version: APIVersion jsonFile: Path @dataclasses.dataclass(frozen=True) class Index: cmake: CMake objects: List[Object] reply: Reply scikit-build-core-0.11.1/src/scikit_build_core/file_api/model/toolchains.py000066400000000000000000000023061477275177200267600ustar00rootroot00000000000000import dataclasses from pathlib import Path from typing import List, Optional from .common import APIVersion __all__ = ["Compiler", "Implicit", "Toolchain", "Toolchains"] def __dir__() -> List[str]: return __all__ @dataclasses.dataclass(frozen=True) class Implicit: includeDirectories: List[Path] = dataclasses.field(default_factory=list) linkDirectories: List[Path] = dataclasses.field(default_factory=list) linkFrameworkDirectories: List[Path] = dataclasses.field(default_factory=list) linkLibraries: List[Path] = dataclasses.field(default_factory=list) @dataclasses.dataclass(frozen=True) class Compiler: implicit: Implicit path: Optional[Path] = None id: Optional[str] = None version: Optional[str] = None target: Optional[str] = None @dataclasses.dataclass(frozen=True) class Toolchain: language: str # Unique, since CMake supports one toolchain per language compiler: Compiler sourceFileExtensions: List[str] = dataclasses.field(default_factory=list) @dataclasses.dataclass(frozen=True) class Toolchains: kind: str = "toolchains" version: APIVersion = APIVersion(1, 0) toolchains: List[Toolchain] = dataclasses.field(default_factory=list) scikit-build-core-0.11.1/src/scikit_build_core/file_api/query.py000066400000000000000000000014531477275177200246640ustar00rootroot00000000000000from __future__ import annotations import sys from pathlib import Path __all__ = ["stateless_query"] def __dir__() -> list[str]: return __all__ def stateless_query(build_dir: Path) -> Path: api_dir = build_dir / ".cmake/api/v1" query = api_dir.joinpath("query") query.mkdir(parents=True, exist_ok=True) query.joinpath("codemodel-v2").touch() query.joinpath("cache-v2").touch() query.joinpath("cmakeFiles-v1").touch() query.joinpath("toolchains-v1").touch() return api_dir / "reply" if __name__ == "__main__": import argparse parser = argparse.ArgumentParser() parser.add_argument("build_dir", type=Path, help="Path to the build directory") args = parser.parse_args() result = stateless_query(args.build_dir) sys.stdout.write(f"{result}\n") scikit-build-core-0.11.1/src/scikit_build_core/file_api/reply.py000066400000000000000000000101171477275177200246470ustar00rootroot00000000000000import builtins import dataclasses import json import sys import typing from pathlib import Path from typing import Any, Callable, Dict, List, Type, TypeVar, Union # noqa: TID251 from .._compat.builtins import ExceptionGroup from .._compat.typing import get_args, get_origin from .model.cache import Cache from .model.cmakefiles import CMakeFiles from .model.codemodel import CodeModel, Target from .model.directory import Directory from .model.index import Index __all__ = ["load_reply_dir"] def __dir__() -> List[str]: return __all__ T = TypeVar("T") InputDict = Dict[str, Any] class Converter: def __init__(self, base_dir: Path) -> None: self.base_dir = base_dir def load(self) -> Index: """ Load the newest index.json file and return the Index object. """ index_file = sorted(self.base_dir.glob("index-*"))[-1] with index_file.open(encoding="utf-8") as f: data = json.load(f) return self.make_class(data, Index) def _load_from_json(self, name: Path, target: Type[T]) -> T: with self.base_dir.joinpath(name).open(encoding="utf-8") as f: data = json.load(f) return self.make_class(data, target) def make_class(self, data: InputDict, target: Type[T]) -> T: """ Convert a dict to a dataclass. Automatically load a few nested jsonFile classes. """ if ( target in {CodeModel, Target, Cache, CMakeFiles, Directory} and "jsonFile" in data and data["jsonFile"] is not None ): return self._load_from_json(Path(data["jsonFile"]), target) input_dict: Dict[str, Type[Any]] = {} exceptions: List[Exception] = [] # We don't have DataclassInstance exposed in typing yet for field in dataclasses.fields(target): # type: ignore[arg-type] json_field = field.name.replace("_v", "-v").replace( "cmakefiles", "cmakeFiles" ) if json_field in data: field_type = field.type try: input_dict[field.name] = self._convert_any( data[json_field], field_type ) except TypeError as err: msg = f"Failed to convert field {field.name!r} of type {field_type}" if sys.version_info < (3, 11): err.__notes__ = [*getattr(err, "__notes__", []), msg] # type: ignore[attr-defined] else: err.add_note(msg) # pylint: disable=no-member exceptions.append(err) except ExceptionGroup as err: exceptions.append(err) if exceptions: msg = f"Failed converting {target}" raise ExceptionGroup(msg, exceptions) return target(**input_dict) @typing.overload def _convert_any(self, item: Any, target: Type[T]) -> T: ... @typing.overload def _convert_any(self, item: Any, target: Any) -> Any: ... def _convert_any(self, item: Any, target: Union[Type[T], Any]) -> Any: if isinstance(target, type) and dataclasses.is_dataclass(target): # We don't have DataclassInstance exposed in typing yet return self.make_class(item, target) origin = get_origin(target) if origin is not None: if origin is list: return [self._convert_any(i, get_args(target)[0]) for i in item] if origin is Union: return self._convert_any(item, get_args(target)[0]) return target(item) def load_reply_dir(path: Path) -> Index: return Converter(path).load() if __name__ == "__main__": import argparse rich_print: Callable[[object], None] try: from rich import print as rich_print except ModuleNotFoundError: rich_print = builtins.print parser = argparse.ArgumentParser() parser.add_argument("reply_dir", type=Path, help="Path to the reply directory") args = parser.parse_args() reply = Path(args.reply_dir) rich_print(load_reply_dir(reply)) scikit-build-core-0.11.1/src/scikit_build_core/format.py000066400000000000000000000066401477275177200232420ustar00rootroot00000000000000"""Format variables available in the ``pyproject.toml`` evaluation""" from __future__ import annotations import dataclasses import sys import typing from pathlib import Path from typing import TYPE_CHECKING, TypedDict if TYPE_CHECKING: from typing import Literal from scikit_build_core.builder.wheel_tag import WheelTag from scikit_build_core.settings.skbuild_model import ScikitBuildSettings __all__ = [ "PyprojectFormatter", "RootPathResolver", "pyproject_format", ] def __dir__() -> list[str]: return __all__ class PyprojectFormatter(TypedDict, total=False): """Format helper for pyproject.toml. Stores all known variables that can be used for evaluating a formatted string in the pyproject.toml config file. """ cache_tag: str """Tag used by the import machinery in the filenames of cached modules, i.e. ``sys.implementation.cache_tag``.""" wheel_tag: str """The tags as computed for the wheel.""" build_type: str """Build type passed as ``cmake.build_type``.""" state: Literal["sdist", "wheel", "editable", "metadata_wheel", "metadata_editable"] """The state of the build.""" root: RootPathResolver """Root path of the current project.""" @typing.overload def pyproject_format( *, settings: ScikitBuildSettings, state: Literal["sdist", "wheel", "editable", "metadata_wheel", "metadata_editable"] | None = ..., tags: WheelTag | None = ..., ) -> PyprojectFormatter: ... @typing.overload def pyproject_format(*, dummy: Literal[True]) -> dict[str, str]: ... def pyproject_format( *, settings: ScikitBuildSettings | None = None, state: ( Literal["sdist", "wheel", "editable", "metadata_wheel", "metadata_editable"] | None ) = None, tags: WheelTag | None = None, dummy: bool = False, ) -> PyprojectFormatter | dict[str, str]: """Generate :py:class:`PyprojectFormatter` dictionary to use in f-string format.""" if dummy: # Return a dict with all the known keys but with values replaced with dummy values return dict.fromkeys(PyprojectFormatter.__annotations__, "*") assert settings is not None # First set all known values res = PyprojectFormatter( cache_tag=sys.implementation.cache_tag, # We are assuming the Path.cwd always evaluates to the folder containing pyproject.toml # as part of PEP517 standard. root=RootPathResolver(), build_type=settings.cmake.build_type, ) # Then compute all optional keys depending on the function input if tags is not None: res["wheel_tag"] = str(tags) if state is not None: res["state"] = state # Construct the final dict including the always known keys return res @dataclasses.dataclass() class RootPathResolver: """Handle ``{root:uri}`` like formatting similar to ``hatchling``.""" path: Path = dataclasses.field(default_factory=Path) def __post_init__(self) -> None: self.path = self.path.resolve() def __format__(self, fmt: str) -> str: command, _, rest = fmt.partition(":") if command == "parent": parent = RootPathResolver(self.path.parent) return parent.__format__(rest) if command == "uri" and rest == "": return self.path.as_uri() if command == "" and rest == "": return str(self) msg = f"Could not handle format: {fmt}" raise ValueError(msg) scikit-build-core-0.11.1/src/scikit_build_core/hatch/000077500000000000000000000000001477275177200224615ustar00rootroot00000000000000scikit-build-core-0.11.1/src/scikit_build_core/hatch/__init__.py000066400000000000000000000000741477275177200245730ustar00rootroot00000000000000from __future__ import annotations __all__: list[str] = [] scikit-build-core-0.11.1/src/scikit_build_core/hatch/hooks.py000066400000000000000000000004601477275177200241560ustar00rootroot00000000000000from __future__ import annotations from typing import Any from hatchling.plugin import hookimpl from .plugin import ScikitBuildHook __all__ = ["hatch_register_build_hook"] def __dir__() -> list[str]: return __all__ @hookimpl def hatch_register_build_hook() -> Any: return ScikitBuildHook scikit-build-core-0.11.1/src/scikit_build_core/hatch/plugin.py000066400000000000000000000252341477275177200243370ustar00rootroot00000000000000# pylint: disable=duplicate-code from __future__ import annotations import copy import importlib.metadata import os import shutil import sysconfig import tempfile import typing from pathlib import Path from typing import Any from hatchling.builders.hooks.plugin.interface import BuildHookInterface from packaging.version import Version from scikit_build_core.settings.skbuild_model import ScikitBuildSettings from .._logging import logger, rich_print from ..build._init import setup_logging from ..builder.builder import Builder, archs_to_tags, get_archs from ..builder.get_requires import GetRequires from ..builder.wheel_tag import WheelTag from ..cmake import CMake, CMaker from ..format import pyproject_format from ..settings.skbuild_read_settings import SettingsReader __all__ = ["ScikitBuildHook"] def __dir__() -> list[str]: return __all__ class ScikitBuildHook(BuildHookInterface): # type: ignore[type-arg] PLUGIN_NAME = "scikit-build" def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self.__tmp_dir: Path | None = None def _read_config(self) -> SettingsReader: config_dict = copy.deepcopy(self.config) config_dict.pop("dependencies", None) config_dict.pop("require-runtime-dependencies", None) config_dict.pop("require-runtime-features", None) state = typing.cast( "typing.Literal['sdist', 'wheel', 'editable']", self.target_name ) return SettingsReader.from_file( "pyproject.toml", state=state, extra_settings=config_dict ) def _validate(self, settings_reader: SettingsReader) -> None: settings = settings_reader.settings settings_reader.validate_may_exit() if not settings.experimental: msg = "Hatch support is experimental, must enable the experimental flag" raise ValueError(msg) if not settings.wheel.cmake or settings.sdist.cmake: msg = "CMake is required for scikit-build" raise ValueError(msg) if settings.sdist.include or settings.sdist.exclude: msg = "include and exclude are not supported for hatch builds" raise ValueError(msg) if settings.sdist.cmake: msg = "Not currently supported for SDist builds" raise ValueError(msg) if settings.wheel.packages: msg = f"Packages ({settings.wheel.packages!r}) are not supported for hatch builds" raise ValueError(msg) if ( settings.wheel.license_files and settings.wheel.license_files != ScikitBuildSettings().wheel.license_files ): msg = f"License files ({settings.wheel.license_files!r}) are not supported for hatch builds" raise ValueError(msg) if settings.wheel.platlib is not None and not settings.wheel.platlib: msg = "Purelib builds not supported for hatch builds" raise ValueError(msg) if settings.generate: msg = "Generate is not supported for hatch builds" raise ValueError(msg) if settings.metadata: msg = "Metadata is not supported for hatch builds" raise ValueError(msg) # Requires Hatchling 1.22.0 to have an effect def dependencies(self) -> list[str]: settings = self._read_config().settings requires = GetRequires(settings) if self.target_name == "sdist": required = requires.settings.sdist.cmake elif self.target_name == "wheel": required = requires.settings.wheel.cmake else: msg = f"Unknown target: {self.target_name!r}, only 'sdist' and 'wheel' are supported" raise ValueError(msg) # These are only injected if cmake is required cmake_requires = [*requires.cmake(), *requires.ninja()] if required else [] return [*cmake_requires, *requires.dynamic_metadata()] def initialize(self, version: str, build_data: dict[str, Any]) -> None: if version == "editable": msg = "Editable installs are not yet supported" raise ValueError(msg) self.__tmp_dir = Path(tempfile.mkdtemp()).resolve() try: self._initialize(build_data=build_data) except Exception: self._cleanup() raise def _initialize(self, *, build_data: dict[str, Any]) -> None: settings_reader = self._read_config() settings = settings_reader.settings state = settings_reader.state self._validate(settings_reader) if state == "sdist": build_data["artifacts"].append("CMakeLists.txt") # Needs full list, etc. return setup_logging(settings.logging.level) cmake = CMake.default_search(version=settings.cmake.version, env=os.environ) rich_print( "{green}***", "{bold.green}scikit-build-core {__version__}", f"using {{blue}}CMake {cmake.version}", f"{{red}}({state})", ) self.__tmp_dir = Path(tempfile.mkdtemp()).resolve() wheel_dir = self.__tmp_dir / "wheel" tags = WheelTag.compute_best( archs_to_tags(get_archs(os.environ)), settings.wheel.py_api, expand_macos=settings.wheel.expand_macos_universal_tags, build_tag=settings.wheel.build_tag, ) build_data["tag"] = str(tags) build_data["pure_python"] = not settings.wheel.platlib build_dir = ( Path( settings.build_dir.format( **pyproject_format( settings=settings, tags=tags, state=state, ) ) ) if settings.build_dir else self.__tmp_dir / "build" ) logger.info("Build directory: {}", build_dir.resolve()) targetlib = "platlib" wheel_dirs = { targetlib: wheel_dir / targetlib, "data": wheel_dir / "data", "headers": wheel_dir / "headers", "scripts": wheel_dir / "scripts", "null": wheel_dir / "null", "metadata": wheel_dir / "metadata", } for d in wheel_dirs.values(): d.mkdir(parents=True) if ".." in settings.wheel.install_dir: msg = "wheel.install_dir must not contain '..'" raise AssertionError(msg) if settings.wheel.install_dir.startswith("/"): if not settings.experimental: msg = "Experimental features must be enabled to use absolute paths in wheel.install_dir" raise AssertionError(msg) if settings.wheel.install_dir[1:].split("/")[0] not in wheel_dirs: msg = "Must target a valid wheel directory" raise AssertionError(msg) install_dir = wheel_dir / settings.wheel.install_dir[1:] else: install_dir = wheel_dirs[targetlib] / settings.wheel.install_dir config = CMaker( cmake, source_dir=settings.cmake.source_dir, build_dir=build_dir, build_type=settings.cmake.build_type, ) builder = Builder( settings=settings, config=config, ) rich_print("{green}***", "{bold}Configuring CMake...") # Setting the install prefix because some libs hardcode CMAKE_INSTALL_PREFIX # Otherwise `cmake --install --prefix` would work by itself defines = {"CMAKE_INSTALL_PREFIX": install_dir} cache_entries: dict[str, str | Path] = { f"SKBUILD_{k.upper()}_DIR": v for k, v in wheel_dirs.items() } cache_entries["SKBUILD_STATE"] = state cache_entries["SKBUILD_HATCHLING"] = importlib.metadata.version("hatchling") builder.configure( defines=defines, cache_entries=cache_entries, name=self.build_config.builder.metadata.name, version=Version(self.build_config.builder.metadata.version), ) default_gen = ( "MSVC" if sysconfig.get_platform().startswith("win") else "Default Generator" ) generator = builder.get_generator() or default_gen rich_print( "{green}***", f"{{bold}}Building project with {{blue}}{generator}{{default}}...", ) build_args: list[str] = [] builder.build(build_args=build_args) rich_print("{green}***", "{bold}Installing project into wheel...") builder.install(install_dir) files = list(wheel_dirs["headers"].iterdir()) if files: msg = ( f"Unsupported files found in 'headers' directory: {files}\n" "Please report use case to https://github.com/pypa/hatch/issues/1291 if you need it." ) raise ValueError(msg) for path in wheel_dirs[targetlib].iterdir(): build_data["force_include"][f"{path}"] = str( settings.wheel.install_dir / path.relative_to(wheel_dirs[targetlib]) ) try: for path in wheel_dirs["data"].iterdir(): build_data["shared_data"][f"{path.resolve()}"] = str( path.relative_to(wheel_dirs["data"]) ) except KeyError: logger.error("SKBUILD_DATA_DIR not supported by Hatchling < 1.24.0") raise try: for path in wheel_dirs["scripts"].iterdir(): build_data["shared_scripts"][f"{path.resolve()}"] = str( path.relative_to(wheel_dirs["scripts"]) ) except KeyError: logger.error("SKBUILD_SCRIPTS_DIR not supported by Hatchling < 1.24.0") raise for path_root in wheel_dirs["metadata"].iterdir(): if path_root.name != "extra_metadata": msg = f"Hatchling metadata must be in an extra_metadata folder, got {path_root}" raise ValueError(msg) for path in path_root.iterdir(): location = path.relative_to(path_root) try: build_data["extra_metadata"][f"{path.resolve()}"] = str(location) except KeyError: logger.error("SKBUILD_METADATA_DIR needs a newer Hatchling") raise def finalize( self, version: str, build_data: dict[str, Any], artifact_path: str ) -> None: self._cleanup() return super().finalize(version, build_data, artifact_path) def _cleanup(self) -> None: if self.__tmp_dir: shutil.rmtree(self.__tmp_dir, ignore_errors=True) self.__tmp_dir = None scikit-build-core-0.11.1/src/scikit_build_core/metadata/000077500000000000000000000000001477275177200231525ustar00rootroot00000000000000scikit-build-core-0.11.1/src/scikit_build_core/metadata/__init__.py000066400000000000000000000000741477275177200252640ustar00rootroot00000000000000from __future__ import annotations __all__: list[str] = [] scikit-build-core-0.11.1/src/scikit_build_core/metadata/fancy_pypi_readme.py000066400000000000000000000033071477275177200272050ustar00rootroot00000000000000from __future__ import annotations from pathlib import Path from .._compat import tomllib __all__ = ["dynamic_metadata", "get_requires_for_dynamic_metadata"] def __dir__() -> list[str]: return __all__ def dynamic_metadata( field: str, settings: dict[str, list[str] | str] | None = None, ) -> str | dict[str, str]: from hatch_fancy_pypi_readme._builder import build_text from hatch_fancy_pypi_readme._config import load_and_validate_config if field != "readme": msg = "Only the 'readme' field is supported" raise ValueError(msg) if settings: msg = "No inline configuration is supported" raise ValueError(msg) with Path("pyproject.toml").open("rb") as f: pyproject_dict = tomllib.load(f) config = load_and_validate_config( pyproject_dict["tool"]["hatch"]["metadata"]["hooks"]["fancy-pypi-readme"] ) if hasattr(config, "substitutions"): try: # We don't have access to the version at this point text = build_text(config.fragments, config.substitutions, "") except TypeError: # Version 23.2.0 and before don't have a version field # pylint: disable-next=no-value-for-parameter text = build_text(config.fragments, config.substitutions) else: # Version 22.3 does not have fragment support # pylint: disable-next=no-value-for-parameter text = build_text(config.fragments) # type: ignore[call-arg] return { "content-type": config.content_type, "text": text, } def get_requires_for_dynamic_metadata( _settings: dict[str, object] | None = None, ) -> list[str]: return ["hatch-fancy-pypi-readme>=22.3"] scikit-build-core-0.11.1/src/scikit_build_core/metadata/regex.py000066400000000000000000000034401477275177200246370ustar00rootroot00000000000000from __future__ import annotations import re from pathlib import Path from typing import TYPE_CHECKING, Any if TYPE_CHECKING: from collections.abc import Mapping __all__ = ["dynamic_metadata"] def __dir__() -> list[str]: return __all__ KEYS = {"input", "regex", "result", "remove"} def dynamic_metadata( field: str, settings: Mapping[str, Any], ) -> str: # Input validation if field not in {"version", "description", "requires-python"}: msg = "Only string fields supported by this plugin" raise RuntimeError(msg) if settings.keys() > KEYS: msg = f"Only {KEYS} settings allowed by this plugin" raise RuntimeError(msg) if "input" not in settings: msg = "Must contain the 'input' setting to perform a regex on" raise RuntimeError(msg) if field != "version" and "regex" not in settings: msg = "Must contain the 'regex' setting if not getting version" raise RuntimeError(msg) for key in KEYS: if key in settings and not isinstance(settings[key], str): msg = f"Setting {key!r} must be a string" raise RuntimeError(msg) input_filename = settings["input"] regex = settings.get( "regex", r'(?i)^(__version__|VERSION)(?: ?\: ?str)? *= *([\'"])v?(?P.+?)\2', ) result = settings.get("result", "{value}") assert isinstance(result, str) remove = settings.get("remove", "") with Path(input_filename).open(encoding="utf-8") as f: match = re.search(regex, f.read(), re.MULTILINE) if not match: msg = f"Couldn't find {regex!r} in {input_filename}" raise RuntimeError(msg) retval = result.format(*match.groups(), **match.groupdict()) if remove: retval = re.sub(remove, "", retval) return retval scikit-build-core-0.11.1/src/scikit_build_core/metadata/setuptools_scm.py000066400000000000000000000032361477275177200266130ustar00rootroot00000000000000from __future__ import annotations __all__ = ["dynamic_metadata", "get_requires_for_dynamic_metadata"] def __dir__() -> list[str]: return __all__ def dynamic_metadata( field: str, settings: dict[str, object] | None = None, ) -> str: # this is a classic implementation, waiting for the release of # vcs-versioning and an improved public interface if field != "version": msg = "Only the 'version' field is supported" raise ValueError(msg) if settings: msg = "No inline configuration is supported" raise ValueError(msg) from setuptools_scm import Configuration, _get_version config = Configuration.from_file("pyproject.toml") version: str | None try: version = _get_version(config, force_write_version_files=True) except TypeError: # setuptools_scm < 8 version = _get_version(config) if version is None: msg = ( f"setuptools-scm was unable to detect version for {config.absolute_root}.\n\n" "Make sure you're either building from a fully intact git repository " "or PyPI tarballs. Most other sources (such as GitHub's tarballs, a " "git checkout without the .git folder) don't contain the necessary " "metadata and will not work.\n\n" "For example, if you're using pip, instead of " "https://github.com/user/proj/archive/master.zip " "use git+https://github.com/user/proj.git#egg=proj" ) raise ValueError(msg) return version def get_requires_for_dynamic_metadata( _settings: dict[str, object] | None = None, ) -> list[str]: return ["setuptools-scm"] scikit-build-core-0.11.1/src/scikit_build_core/program_search.py000066400000000000000000000145421477275177200247460ustar00rootroot00000000000000from __future__ import annotations import contextlib import json import os import shutil import subprocess import sys from pathlib import Path from typing import TYPE_CHECKING, Literal, NamedTuple from packaging.version import InvalidVersion, Version from ._logging import logger, rich_print from ._shutil import Run if TYPE_CHECKING: from collections.abc import Generator, Iterable from packaging.specifiers import SpecifierSet __all__ = [ "Program", "best_program", "get_cmake_program", "get_cmake_programs", "get_ninja_programs", ] def __dir__() -> list[str]: return __all__ # Make sure we don't wait forever for programs to respond # CI services can be really slow under load if os.environ.get("CI", ""): TIMEOUT = 20 else: TIMEOUT = 10 if sys.platform.startswith("win") else 5 class Program(NamedTuple): path: Path version: Version | None def _get_cmake_path(*, module: bool = True) -> Generator[Path, None, None]: """ Get the path to CMake. """ if module: with contextlib.suppress(ImportError): # If a "cmake" directory exists, this will also ImportError from cmake import CMAKE_BIN_DIR yield Path(CMAKE_BIN_DIR) / "cmake" candidates = ("cmake3", "cmake") for candidate in candidates: cmake_path = shutil.which(candidate) if cmake_path is not None: yield Path(cmake_path) def _get_ninja_path(*, module: bool = True) -> Generator[Path, None, None]: """ Get the path to ninja. """ if module: with contextlib.suppress(ImportError): from ninja import BIN_DIR yield Path(BIN_DIR) / "ninja" # Matches https://gitlab.kitware.com/cmake/cmake/-/blob/master/Modules/CMakeNinjaFindMake.cmake candidates = ("ninja-build", "ninja", "samu") for candidate in candidates: ninja_path = shutil.which(candidate) if ninja_path is not None: yield Path(ninja_path) def get_cmake_program(cmake_path: Path) -> Program: """ Get the Program (with version) for CMake given a path. The version will be None if it cannot be determined. """ try: result = Run(timeout=TIMEOUT).capture(cmake_path, "-E", "capabilities") try: version = Version( json.loads(result.stdout)["version"]["string"].split("-")[0] ) logger.info("CMake version: {}", version) return Program(cmake_path, version) except (json.decoder.JSONDecodeError, KeyError, InvalidVersion): logger.warning("Could not determine CMake version, got {!r}", result.stdout) except subprocess.CalledProcessError: try: result = Run(timeout=TIMEOUT).capture(cmake_path, "--version") try: version = Version( result.stdout.splitlines()[0].split()[-1].split("-")[0] ) logger.info("CMake version via --version: {}", version) return Program(cmake_path, version) except (IndexError, InvalidVersion): logger.warning( "Could not determine CMake version via --version, got {!r}", result.stdout, ) except subprocess.CalledProcessError as err: logger.warning( "Could not determine CMake version via --version, got {!r} {!r}", err.stdout, err.stderr, ) except PermissionError: logger.warning("Permissions Error getting CMake's version") except subprocess.TimeoutExpired: logger.warning("Accessing CMake timed out, ignoring") return Program(cmake_path, None) def get_cmake_programs(*, module: bool = True) -> Generator[Program, None, None]: """ Get the path and version for CMake. If the version cannot be determined, yiels (path, None). Otherwise, yields (path, version). Best matches are yielded first. """ for cmake_path in _get_cmake_path(module=module): yield get_cmake_program(cmake_path) def get_ninja_programs(*, module: bool = True) -> Generator[Program, None, None]: """ Get the path and version for Ninja. If the version cannot be determined, yields (path, None). Otherwise, yields (path, version). Best matches are yielded first. """ for ninja_path in _get_ninja_path(module=module): try: result = Run(timeout=TIMEOUT).capture(ninja_path, "--version") except ( subprocess.CalledProcessError, PermissionError, subprocess.TimeoutExpired, ): yield Program(ninja_path, None) continue try: version = Version(".".join(result.stdout.strip().split(".")[:3])) except ValueError: yield Program(ninja_path, None) continue logger.info("Ninja version: {}", version) yield Program(ninja_path, version) def get_make_programs() -> Generator[Path, None, None]: """ Get the path to make. """ candidates = ("gmake", "make") for candidate in candidates: make_path = shutil.which(candidate) if make_path is not None: yield Path(make_path) def best_program( programs: Iterable[Program], *, version: SpecifierSet | None ) -> Program | None: """ Select the first program entry that is of a supported version, or None if not found. """ for program in programs: if version is None: return program if program.version is not None and version.contains(program.version): return program return None def info_print( *, color: Literal[ "", "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white" ] = "", ) -> None: """ Print information about the program search. """ rich_print("{bold}Detected CMake and Ninja{normal} (all versions):", color=color) for n, prog in enumerate(get_cmake_programs()): s = " " if n else "{default}*{color}" rich_print( f"{s} {{bold}}CMake:{{normal}} {prog.path} {prog.version!r}", color=color ) for n, prog in enumerate(get_ninja_programs()): s = " " if n else "{default}*{color}" rich_print( f"{s} {{bold}}Ninja:{{normal}} {prog.path} {prog.version!r}", color=color ) if __name__ == "__main__": info_print() scikit-build-core-0.11.1/src/scikit_build_core/py.typed000066400000000000000000000000001477275177200230570ustar00rootroot00000000000000scikit-build-core-0.11.1/src/scikit_build_core/resources/000077500000000000000000000000001477275177200234045ustar00rootroot00000000000000scikit-build-core-0.11.1/src/scikit_build_core/resources/__init__.py000066400000000000000000000002361477275177200255160ustar00rootroot00000000000000from __future__ import annotations from .._compat.importlib import resources as _resources __all__ = ["resources"] resources = _resources.files(__name__) scikit-build-core-0.11.1/src/scikit_build_core/resources/_editable_redirect.py000066400000000000000000000214661477275177200275600ustar00rootroot00000000000000from __future__ import annotations import importlib.abc import importlib.util import os import subprocess import sys # Importing as little as possible is important, since every usage of Python # imports this file. That's why we use this trick for TYPE_CHECKING TYPE_CHECKING = False if TYPE_CHECKING: from importlib.machinery import ModuleSpec DIR = os.path.abspath(os.path.dirname(__file__)) MARKER = "SKBUILD_EDITABLE_SKIP" VERBOSE = "SKBUILD_EDITABLE_VERBOSE" __all__ = ["install"] def __dir__() -> list[str]: return __all__ class FileLockIfUnix: def __init__(self, lock_file: str) -> None: self.lock_file = lock_file self.lock_file_fd: int | None = None def acquire(self) -> None: # Based on filelock.BaseFileLock.acquire and filelock.UnixFileLock._acquire try: import fcntl except ImportError: return import contextlib import time poll_interval = 0.05 log_interval = 60 last_log = time.perf_counter() while True: os.makedirs(os.path.dirname(self.lock_file), exist_ok=True) open_flags = os.O_RDWR | os.O_TRUNC if not os.path.exists(self.lock_file): open_flags |= os.O_CREAT fd = os.open(self.lock_file, open_flags, 0o644) with contextlib.suppress(PermissionError): # Lock is not owned by this UID os.fchmod(fd, 0o644) try: fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) except OSError: os.close(fd) else: self.lock_file_fd = fd return now = time.perf_counter() if now - last_log > log_interval: last_log = now print(f"Still waiting to acquire lock {self.lock_file}...") # noqa: T201 time.sleep(poll_interval) def release(self) -> None: try: import fcntl except ImportError: return assert isinstance(self.lock_file_fd, int) fcntl.flock(self.lock_file_fd, fcntl.LOCK_UN) os.close(self.lock_file_fd) class ScikitBuildRedirectingFinder(importlib.abc.MetaPathFinder): def __init__( self, known_source_files: dict[str, str], known_wheel_files: dict[str, str], path: str | None, rebuild: bool, verbose: bool, build_options: list[str], install_options: list[str], dir: str, install_dir: str, ) -> None: self.known_source_files = known_source_files self.known_wheel_files = known_wheel_files self.path = path self.rebuild_flag = rebuild self.verbose = verbose self.build_options = build_options self.install_options = install_options self.dir = dir self.install_dir = os.path.join(DIR, install_dir) # Construct the __path__ of all resource files # I.e. the paths of all package-like objects submodule_search_locations: dict[str, set[str]] = {} pkgs: list[str] = [] # Loop over both python native source files and cmake installed ones for tree in (known_source_files, known_wheel_files): for module, file in tree.items(): # Strip the last element of the module parent = ".".join(module.split(".")[:-1]) # Check if it is a package if "__init__.py" in file: parent = module pkgs.append(parent) # Skip if it's a root module (there are no search paths for these) if not parent: continue # Initialize the tree element if needed submodule_search_locations.setdefault(parent, set()) # Add the parent path to the dictionary values parent_path = os.path.dirname(file) if not parent_path: # root modules are skipped so all files should be in a parent package msg = f"Unexpected path to source file: {file} [{module}]" raise ImportError(msg) if not os.path.isabs(parent_path): parent_path = os.path.join(self.dir, parent_path) submodule_search_locations[parent].add(parent_path) self.submodule_search_locations = submodule_search_locations self.pkgs = pkgs def find_spec( self, fullname: str, path: object = None, target: object = None, ) -> ModuleSpec | None: # If no known submodule_search_locations is found, it means it is a root # module. if fullname in self.submodule_search_locations: submodule_search_locations = list(self.submodule_search_locations[fullname]) else: submodule_search_locations = None if fullname in self.known_wheel_files: redir = self.known_wheel_files[fullname] if self.rebuild_flag: self.rebuild() return importlib.util.spec_from_file_location( fullname, os.path.join(self.dir, redir), submodule_search_locations=submodule_search_locations if redir.endswith(("__init__.py", "__init__.pyc")) else None, ) if fullname in self.known_source_files: redir = self.known_source_files[fullname] return importlib.util.spec_from_file_location( fullname, redir, submodule_search_locations=submodule_search_locations if redir.endswith(("__init__.py", "__init__.pyc")) else None, ) return None def rebuild(self) -> None: # Don't rebuild if not set to a local path if not self.path: return env = os.environ.copy() # Protect against recursion if self.path in env.get(MARKER, "").split(os.pathsep): return env[MARKER] = os.pathsep.join((env.get(MARKER, ""), self.path)) verbose = self.verbose or bool(env.get(VERBOSE, "")) if env.get(VERBOSE, "") == "0": verbose = False if verbose: print(f"Running cmake --build & --install in {self.path}") # noqa: T201 lock = FileLockIfUnix(os.path.join(self.path, "editable_rebuild.lock")) try: lock.acquire() result = subprocess.run( ["cmake", "--build", ".", *self.build_options], cwd=self.path, stdout=sys.stderr if verbose else subprocess.PIPE, env=env, check=False, text=True, ) if result.returncode and verbose: print( # noqa: T201 f"ERROR: {result.stdout}", file=sys.stderr, ) result.check_returncode() result = subprocess.run( [ "cmake", "--install", ".", "--prefix", self.install_dir, *self.install_options, ], cwd=self.path, stdout=sys.stderr if verbose else subprocess.PIPE, env=env, check=False, text=True, ) if result.returncode and verbose: print( # noqa: T201 f"ERROR: {result.stdout}", file=sys.stderr, ) result.check_returncode() finally: lock.release() def install( known_source_files: dict[str, str], known_wheel_files: dict[str, str], path: str | None, rebuild: bool = False, verbose: bool = False, build_options: list[str] | None = None, install_options: list[str] | None = None, install_dir: str = "", ) -> None: """ Install a meta path finder that redirects imports to the source files, and optionally rebuilds if path is given. :param known_source_files: A mapping of module names to source files :param known_wheel_files: A mapping of module names to wheel files :param path: The path to the build directory, or None :param verbose: Whether to print the cmake commands (also controlled by the SKBUILD_EDITABLE_VERBOSE environment variable) :param install_dir: The wheel install directory override, if one was specified """ sys.meta_path.insert( 0, ScikitBuildRedirectingFinder( known_source_files, known_wheel_files, path, rebuild, verbose, build_options or [], install_options or [], DIR, install_dir, ), ) scikit-build-core-0.11.1/src/scikit_build_core/resources/find_python/000077500000000000000000000000001477275177200257255ustar00rootroot00000000000000scikit-build-core-0.11.1/src/scikit_build_core/resources/find_python/Copyright.txt000066400000000000000000000125451477275177200304450ustar00rootroot00000000000000CMake - Cross Platform Makefile Generator Copyright 2000-2023 Kitware, Inc. and Contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Kitware, Inc. nor the names of Contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------------ The following individuals and institutions are among the Contributors: * Aaron C. Meadows * Adriaan de Groot * Aleksey Avdeev * Alexander Neundorf * Alexander Smorkalov * Alexey Sokolov * Alex Merry * Alex Turbov * Andreas Pakulat * Andreas Schneider * André Rigland Brodtkorb * Axel Huebl, Helmholtz-Zentrum Dresden - Rossendorf * Benjamin Eikel * Bjoern Ricks * Brad Hards * Christopher Harvey * Christoph Grüninger * Clement Creusot * Daniel Blezek * Daniel Pfeifer * Dawid Wróbel * Enrico Scholz * Eran Ifrah * Esben Mose Hansen, Ange Optimization ApS * Geoffrey Viola * Google Inc * Gregor Jasny * Helio Chissini de Castro * Ilya Lavrenov * Insight Software Consortium * Intel Corporation * Jan Woetzel * Jordan Williams * Julien Schueller * Kelly Thompson * Konstantin Podsvirov * Laurent Montel * Mario Bensi * Martin Gräßlin * Mathieu Malaterre * Matthaeus G. Chajdas * Matthias Kretz * Matthias Maennich * Michael Hirsch, Ph.D. * Michael Stürmer * Miguel A. Figueroa-Villanueva * Mike Durso * Mike Jackson * Mike McQuaid * Nicolas Bock * Nicolas Despres * Nikita Krupen'ko * NVIDIA Corporation * OpenGamma Ltd. * Patrick Stotko * Per Øyvind Karlsen * Peter Collingbourne * Petr Gotthard * Philip Lowman * Philippe Proulx * Raffi Enficiaud, Max Planck Society * Raumfeld * Roger Leigh * Rolf Eike Beer * Roman Donchenko * Roman Kharitonov * Ruslan Baratov * Sebastian Holtermann * Stephen Kelly * Sylvain Joubert * The Qt Company Ltd. * Thomas Sondergaard * Tobias Hunger * Todd Gamblin * Tristan Carel * University of Dundee * Vadim Zhukov * Will Dicharry See version control history for details of individual contributions. The above copyright and license notice applies to distributions of CMake in source and binary form. Third-party software packages supplied with CMake under compatible licenses provide their own copyright notices documented in corresponding subdirectories or source files. ------------------------------------------------------------------------------ CMake was initially developed by Kitware with the following sponsorship: * National Library of Medicine at the National Institutes of Health as part of the Insight Segmentation and Registration Toolkit (ITK). * US National Labs (Los Alamos, Livermore, Sandia) ASC Parallel Visualization Initiative. * National Alliance for Medical Image Computing (NAMIC) is funded by the National Institutes of Health through the NIH Roadmap for Medical Research, Grant U54 EB005149. * Kitware, Inc. FindPackageHandleStandardArgs.cmake000066400000000000000000000552061477275177200344260ustar00rootroot00000000000000scikit-build-core-0.11.1/src/scikit_build_core/resources/find_python# Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. #[=======================================================================[.rst: FindPackageHandleStandardArgs ----------------------------- This module provides functions intended to be used in :ref:`Find Modules` implementing :command:`find_package()` calls. .. command:: find_package_handle_standard_args This command handles the ``REQUIRED``, ``QUIET`` and version-related arguments of :command:`find_package`. It also sets the ``_FOUND`` variable. The package is considered found if all variables listed contain valid results, e.g. valid filepaths. There are two signatures: .. code-block:: cmake find_package_handle_standard_args( (DEFAULT_MSG|) ... ) find_package_handle_standard_args( [FOUND_VAR ] [REQUIRED_VARS ...] [VERSION_VAR ] [HANDLE_VERSION_RANGE] [HANDLE_COMPONENTS] [CONFIG_MODE] [NAME_MISMATCHED] [REASON_FAILURE_MESSAGE ] [FAIL_MESSAGE ] ) The ``_FOUND`` variable will be set to ``TRUE`` if all the variables ``...`` are valid and any optional constraints are satisfied, and ``FALSE`` otherwise. A success or failure message may be displayed based on the results and on whether the ``REQUIRED`` and/or ``QUIET`` option was given to the :command:`find_package` call. The options are: ``(DEFAULT_MSG|)`` In the simple signature this specifies the failure message. Use ``DEFAULT_MSG`` to ask for a default message to be computed (recommended). Not valid in the full signature. ``FOUND_VAR `` .. deprecated:: 3.3 Specifies either ``_FOUND`` or ``_FOUND`` as the result variable. This exists only for compatibility with older versions of CMake and is now ignored. Result variables of both names are always set for compatibility. ``REQUIRED_VARS ...`` Specify the variables which are required for this package. These may be named in the generated failure message asking the user to set the missing variable values. Therefore these should typically be cache entries such as ``FOO_LIBRARY`` and not output variables like ``FOO_LIBRARIES``. .. versionchanged:: 3.18 If ``HANDLE_COMPONENTS`` is specified, this option can be omitted. ``VERSION_VAR `` Specify the name of a variable that holds the version of the package that has been found. This version will be checked against the (potentially) specified required version given to the :command:`find_package` call, including its ``EXACT`` option. The default messages include information about the required version and the version which has been actually found, both if the version is ok or not. ``HANDLE_VERSION_RANGE`` .. versionadded:: 3.19 Enable handling of a version range, if one is specified. Without this option, a developer warning will be displayed if a version range is specified. ``HANDLE_COMPONENTS`` Enable handling of package components. In this case, the command will report which components have been found and which are missing, and the ``_FOUND`` variable will be set to ``FALSE`` if any of the required components (i.e. not the ones listed after the ``OPTIONAL_COMPONENTS`` option of :command:`find_package`) are missing. ``CONFIG_MODE`` Specify that the calling find module is a wrapper around a call to ``find_package( NO_MODULE)``. This implies a ``VERSION_VAR`` value of ``_VERSION``. The command will automatically check whether the package configuration file was found. ``REASON_FAILURE_MESSAGE `` .. versionadded:: 3.16 Specify a custom message of the reason for the failure which will be appended to the default generated message. ``FAIL_MESSAGE `` Specify a custom failure message instead of using the default generated message. Not recommended. ``NAME_MISMATCHED`` .. versionadded:: 3.17 Indicate that the ```` does not match ``${CMAKE_FIND_PACKAGE_NAME}``. This is usually a mistake and raises a warning, but it may be intentional for usage of the command for components of a larger package. Example for the simple signature: .. code-block:: cmake find_package_handle_standard_args(LibXml2 DEFAULT_MSG LIBXML2_LIBRARY LIBXML2_INCLUDE_DIR) The ``LibXml2`` package is considered to be found if both ``LIBXML2_LIBRARY`` and ``LIBXML2_INCLUDE_DIR`` are valid. Then also ``LibXml2_FOUND`` is set to ``TRUE``. If it is not found and ``REQUIRED`` was used, it fails with a :command:`message(FATAL_ERROR)`, independent whether ``QUIET`` was used or not. If it is found, success will be reported, including the content of the first ````. On repeated CMake runs, the same message will not be printed again. .. note:: If ```` does not match ``CMAKE_FIND_PACKAGE_NAME`` for the calling module, a warning that there is a mismatch is given. The ``FPHSA_NAME_MISMATCHED`` variable may be set to bypass the warning if using the old signature and the ``NAME_MISMATCHED`` argument using the new signature. To avoid forcing the caller to require newer versions of CMake for usage, the variable's value will be used if defined when the ``NAME_MISMATCHED`` argument is not passed for the new signature (but using both is an error).. Example for the full signature: .. code-block:: cmake find_package_handle_standard_args(LibArchive REQUIRED_VARS LibArchive_LIBRARY LibArchive_INCLUDE_DIR VERSION_VAR LibArchive_VERSION) In this case, the ``LibArchive`` package is considered to be found if both ``LibArchive_LIBRARY`` and ``LibArchive_INCLUDE_DIR`` are valid. Also the version of ``LibArchive`` will be checked by using the version contained in ``LibArchive_VERSION``. Since no ``FAIL_MESSAGE`` is given, the default messages will be printed. Another example for the full signature: .. code-block:: cmake find_package(Automoc4 QUIET NO_MODULE HINTS /opt/automoc4) find_package_handle_standard_args(Automoc4 CONFIG_MODE) In this case, a ``FindAutmoc4.cmake`` module wraps a call to ``find_package(Automoc4 NO_MODULE)`` and adds an additional search directory for ``automoc4``. Then the call to ``find_package_handle_standard_args`` produces a proper success/failure message. .. command:: find_package_check_version .. versionadded:: 3.19 Helper function which can be used to check if a ```` is valid against version-related arguments of :command:`find_package`. .. code-block:: cmake find_package_check_version( [HANDLE_VERSION_RANGE] [RESULT_MESSAGE_VARIABLE ] ) The ```` will hold a boolean value giving the result of the check. The options are: ``HANDLE_VERSION_RANGE`` Enable handling of a version range, if one is specified. Without this option, a developer warning will be displayed if a version range is specified. ``RESULT_MESSAGE_VARIABLE `` Specify a variable to get back a message describing the result of the check. Example for the usage: .. code-block:: cmake find_package_check_version(1.2.3 result HANDLE_VERSION_RANGE RESULT_MESSAGE_VARIABLE reason) if (result) message (STATUS "${reason}") else() message (FATAL_ERROR "${reason}") endif() #]=======================================================================] include(${CMAKE_CURRENT_LIST_DIR}/FindPackageMessage.cmake) cmake_policy(PUSH) # numbers and boolean constants cmake_policy (SET CMP0012 NEW) # IN_LIST operator cmake_policy (SET CMP0057 NEW) # internal helper macro macro(_FPHSA_FAILURE_MESSAGE _msg) set (__msg "${_msg}") if (FPHSA_REASON_FAILURE_MESSAGE) string(APPEND __msg "\n Reason given by package: ${FPHSA_REASON_FAILURE_MESSAGE}\n") endif() if (${_NAME}_FIND_REQUIRED) message(FATAL_ERROR "${__msg}") else () if (NOT ${_NAME}_FIND_QUIETLY) message(STATUS "${__msg}") endif () endif () endmacro() # internal helper macro to generate the failure message when used in CONFIG_MODE: macro(_FPHSA_HANDLE_FAILURE_CONFIG_MODE) # _CONFIG is set, but FOUND is false, this means that some other of the REQUIRED_VARS was not found: if(${_NAME}_CONFIG) _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: missing:${MISSING_VARS} (found ${${_NAME}_CONFIG} ${VERSION_MSG})") else() # If _CONSIDERED_CONFIGS is set, the config-file has been found, but no suitable version. # List them all in the error message: if(${_NAME}_CONSIDERED_CONFIGS) set(configsText "") list(LENGTH ${_NAME}_CONSIDERED_CONFIGS configsCount) math(EXPR configsCount "${configsCount} - 1") foreach(currentConfigIndex RANGE ${configsCount}) list(GET ${_NAME}_CONSIDERED_CONFIGS ${currentConfigIndex} filename) list(GET ${_NAME}_CONSIDERED_VERSIONS ${currentConfigIndex} version) string(APPEND configsText "\n ${filename} (version ${version})") endforeach() if (${_NAME}_NOT_FOUND_MESSAGE) if (FPHSA_REASON_FAILURE_MESSAGE) string(PREPEND FPHSA_REASON_FAILURE_MESSAGE "${${_NAME}_NOT_FOUND_MESSAGE}\n ") else() set(FPHSA_REASON_FAILURE_MESSAGE "${${_NAME}_NOT_FOUND_MESSAGE}") endif() else() string(APPEND configsText "\n") endif() _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} ${VERSION_MSG}, checked the following files:${configsText}") else() # Simple case: No Config-file was found at all: _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: found neither ${_NAME}Config.cmake nor ${_NAME_LOWER}-config.cmake ${VERSION_MSG}") endif() endif() endmacro() function(FIND_PACKAGE_CHECK_VERSION version result) cmake_parse_arguments (PARSE_ARGV 2 FPCV "HANDLE_VERSION_RANGE;NO_AUTHOR_WARNING_VERSION_RANGE" "RESULT_MESSAGE_VARIABLE" "") if (FPCV_UNPARSED_ARGUMENTS) message (FATAL_ERROR "find_package_check_version(): ${FPCV_UNPARSED_ARGUMENTS}: unexpected arguments") endif() if ("RESULT_MESSAGE_VARIABLE" IN_LIST FPCV_KEYWORDS_MISSING_VALUES) message (FATAL_ERROR "find_package_check_version(): RESULT_MESSAGE_VARIABLE expects an argument") endif() set (${result} FALSE PARENT_SCOPE) if (FPCV_RESULT_MESSAGE_VARIABLE) unset (${FPCV_RESULT_MESSAGE_VARIABLE} PARENT_SCOPE) endif() if (_CMAKE_FPHSA_PACKAGE_NAME) set (package "${_CMAKE_FPHSA_PACKAGE_NAME}") elseif (CMAKE_FIND_PACKAGE_NAME) set (package "${CMAKE_FIND_PACKAGE_NAME}") else() message (FATAL_ERROR "find_package_check_version(): Cannot be used outside a 'Find Module'") endif() if (NOT FPCV_NO_AUTHOR_WARNING_VERSION_RANGE AND ${package}_FIND_VERSION_RANGE AND NOT FPCV_HANDLE_VERSION_RANGE) message(AUTHOR_WARNING "`find_package()` specify a version range but the option " "HANDLE_VERSION_RANGE` is not passed to `find_package_check_version()`. " "Only the lower endpoint of the range will be used.") endif() set (version_ok FALSE) unset (version_msg) if (FPCV_HANDLE_VERSION_RANGE AND ${package}_FIND_VERSION_RANGE) if ((${package}_FIND_VERSION_RANGE_MIN STREQUAL "INCLUDE" AND version VERSION_GREATER_EQUAL ${package}_FIND_VERSION_MIN) AND ((${package}_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND version VERSION_LESS_EQUAL ${package}_FIND_VERSION_MAX) OR (${package}_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND version VERSION_LESS ${package}_FIND_VERSION_MAX))) set (version_ok TRUE) set(version_msg "(found suitable version \"${version}\", required range is \"${${package}_FIND_VERSION_RANGE}\")") else() set(version_msg "Found unsuitable version \"${version}\", required range is \"${${package}_FIND_VERSION_RANGE}\"") endif() elseif (DEFINED ${package}_FIND_VERSION) if(${package}_FIND_VERSION_EXACT) # exact version required # count the dots in the version string string(REGEX REPLACE "[^.]" "" version_dots "${version}") # add one dot because there is one dot more than there are components string(LENGTH "${version_dots}." version_dots) if (version_dots GREATER ${package}_FIND_VERSION_COUNT) # Because of the C++ implementation of find_package() ${package}_FIND_VERSION_COUNT # is at most 4 here. Therefore a simple lookup table is used. if (${package}_FIND_VERSION_COUNT EQUAL 1) set(version_regex "[^.]*") elseif (${package}_FIND_VERSION_COUNT EQUAL 2) set(version_regex "[^.]*\\.[^.]*") elseif (${package}_FIND_VERSION_COUNT EQUAL 3) set(version_regex "[^.]*\\.[^.]*\\.[^.]*") else() set(version_regex "[^.]*\\.[^.]*\\.[^.]*\\.[^.]*") endif() string(REGEX REPLACE "^(${version_regex})\\..*" "\\1" version_head "${version}") if (NOT ${package}_FIND_VERSION VERSION_EQUAL version_head) set(version_msg "Found unsuitable version \"${version}\", but required is exact version \"${${package}_FIND_VERSION}\"") else () set(version_ok TRUE) set(version_msg "(found suitable exact version \"${_FOUND_VERSION}\")") endif () else () if (NOT ${package}_FIND_VERSION VERSION_EQUAL version) set(version_msg "Found unsuitable version \"${version}\", but required is exact version \"${${package}_FIND_VERSION}\"") else () set(version_ok TRUE) set(version_msg "(found suitable exact version \"${version}\")") endif () endif () else() # minimum version if (${package}_FIND_VERSION VERSION_GREATER version) set(version_msg "Found unsuitable version \"${version}\", but required is at least \"${${package}_FIND_VERSION}\"") else() set(version_ok TRUE) set(version_msg "(found suitable version \"${version}\", minimum required is \"${${package}_FIND_VERSION}\")") endif() endif() else () set(version_ok TRUE) set(version_msg "(found version \"${version}\")") endif() set (${result} ${version_ok} PARENT_SCOPE) if (FPCV_RESULT_MESSAGE_VARIABLE) set (${FPCV_RESULT_MESSAGE_VARIABLE} "${version_msg}" PARENT_SCOPE) endif() endfunction() function(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG) # Set up the arguments for `cmake_parse_arguments`. set(options CONFIG_MODE HANDLE_COMPONENTS NAME_MISMATCHED HANDLE_VERSION_RANGE) set(oneValueArgs FAIL_MESSAGE REASON_FAILURE_MESSAGE VERSION_VAR FOUND_VAR) set(multiValueArgs REQUIRED_VARS) # Check whether we are in 'simple' or 'extended' mode: set(_KEYWORDS_FOR_EXTENDED_MODE ${options} ${oneValueArgs} ${multiValueArgs} ) list(FIND _KEYWORDS_FOR_EXTENDED_MODE "${_FIRST_ARG}" INDEX) unset(FPHSA_NAME_MISMATCHED_override) if (DEFINED FPHSA_NAME_MISMATCHED) # If the variable NAME_MISMATCHED variable is set, error if it is passed as # an argument. The former is for old signatures, the latter is for new # signatures. list(FIND ARGN "NAME_MISMATCHED" name_mismatched_idx) if (NOT name_mismatched_idx EQUAL "-1") message(FATAL_ERROR "The `NAME_MISMATCHED` argument may only be specified by the argument or " "the variable, not both.") endif () # But use the variable if it is not an argument to avoid forcing minimum # CMake version bumps for calling modules. set(FPHSA_NAME_MISMATCHED_override "${FPHSA_NAME_MISMATCHED}") endif () if(${INDEX} EQUAL -1) set(FPHSA_FAIL_MESSAGE ${_FIRST_ARG}) set(FPHSA_REQUIRED_VARS ${ARGN}) set(FPHSA_VERSION_VAR) else() cmake_parse_arguments(FPHSA "${options}" "${oneValueArgs}" "${multiValueArgs}" ${_FIRST_ARG} ${ARGN}) if(FPHSA_UNPARSED_ARGUMENTS) message(FATAL_ERROR "Unknown keywords given to FIND_PACKAGE_HANDLE_STANDARD_ARGS(): \"${FPHSA_UNPARSED_ARGUMENTS}\"") endif() if(NOT FPHSA_FAIL_MESSAGE) set(FPHSA_FAIL_MESSAGE "DEFAULT_MSG") endif() # In config-mode, we rely on the variable _CONFIG, which is set by find_package() # when it successfully found the config-file, including version checking: if(FPHSA_CONFIG_MODE) list(INSERT FPHSA_REQUIRED_VARS 0 ${_NAME}_CONFIG) list(REMOVE_DUPLICATES FPHSA_REQUIRED_VARS) set(FPHSA_VERSION_VAR ${_NAME}_VERSION) endif() if(NOT FPHSA_REQUIRED_VARS AND NOT FPHSA_HANDLE_COMPONENTS) message(FATAL_ERROR "No REQUIRED_VARS specified for FIND_PACKAGE_HANDLE_STANDARD_ARGS()") endif() endif() if (DEFINED FPHSA_NAME_MISMATCHED_override) set(FPHSA_NAME_MISMATCHED "${FPHSA_NAME_MISMATCHED_override}") endif () if (DEFINED CMAKE_FIND_PACKAGE_NAME AND NOT FPHSA_NAME_MISMATCHED AND NOT _NAME STREQUAL CMAKE_FIND_PACKAGE_NAME) message(AUTHOR_WARNING "The package name passed to `find_package_handle_standard_args` " "(${_NAME}) does not match the name of the calling package " "(${CMAKE_FIND_PACKAGE_NAME}). This can lead to problems in calling " "code that expects `find_package` result variables (e.g., `_FOUND`) " "to follow a certain pattern.") endif () if (${_NAME}_FIND_VERSION_RANGE AND NOT FPHSA_HANDLE_VERSION_RANGE) message(AUTHOR_WARNING "`find_package()` specify a version range but the module ${_NAME} does " "not support this capability. Only the lower endpoint of the range " "will be used.") endif() # to propagate package name to FIND_PACKAGE_CHECK_VERSION set(_CMAKE_FPHSA_PACKAGE_NAME "${_NAME}") # now that we collected all arguments, process them if("x${FPHSA_FAIL_MESSAGE}" STREQUAL "xDEFAULT_MSG") set(FPHSA_FAIL_MESSAGE "Could NOT find ${_NAME}") endif() if (FPHSA_REQUIRED_VARS) list(GET FPHSA_REQUIRED_VARS 0 _FIRST_REQUIRED_VAR) endif() string(TOUPPER ${_NAME} _NAME_UPPER) string(TOLOWER ${_NAME} _NAME_LOWER) if(FPHSA_FOUND_VAR) set(_FOUND_VAR_UPPER ${_NAME_UPPER}_FOUND) set(_FOUND_VAR_MIXED ${_NAME}_FOUND) if(FPHSA_FOUND_VAR STREQUAL _FOUND_VAR_MIXED OR FPHSA_FOUND_VAR STREQUAL _FOUND_VAR_UPPER) set(_FOUND_VAR ${FPHSA_FOUND_VAR}) else() message(FATAL_ERROR "The argument for FOUND_VAR is \"${FPHSA_FOUND_VAR}\", but only \"${_FOUND_VAR_MIXED}\" and \"${_FOUND_VAR_UPPER}\" are valid names.") endif() else() set(_FOUND_VAR ${_NAME_UPPER}_FOUND) endif() # collect all variables which were not found, so they can be printed, so the # user knows better what went wrong (#6375) set(MISSING_VARS "") set(DETAILS "") # check if all passed variables are valid set(FPHSA_FOUND_${_NAME} TRUE) foreach(_CURRENT_VAR ${FPHSA_REQUIRED_VARS}) if(NOT ${_CURRENT_VAR}) set(FPHSA_FOUND_${_NAME} FALSE) string(APPEND MISSING_VARS " ${_CURRENT_VAR}") else() string(APPEND DETAILS "[${${_CURRENT_VAR}}]") endif() endforeach() if(FPHSA_FOUND_${_NAME}) set(${_NAME}_FOUND TRUE) set(${_NAME_UPPER}_FOUND TRUE) else() set(${_NAME}_FOUND FALSE) set(${_NAME_UPPER}_FOUND FALSE) endif() # component handling unset(FOUND_COMPONENTS_MSG) unset(MISSING_COMPONENTS_MSG) if(FPHSA_HANDLE_COMPONENTS) foreach(comp ${${_NAME}_FIND_COMPONENTS}) if(${_NAME}_${comp}_FOUND) if(NOT DEFINED FOUND_COMPONENTS_MSG) set(FOUND_COMPONENTS_MSG "found components:") endif() string(APPEND FOUND_COMPONENTS_MSG " ${comp}") else() if(NOT DEFINED MISSING_COMPONENTS_MSG) set(MISSING_COMPONENTS_MSG "missing components:") endif() string(APPEND MISSING_COMPONENTS_MSG " ${comp}") if(${_NAME}_FIND_REQUIRED_${comp}) set(${_NAME}_FOUND FALSE) string(APPEND MISSING_VARS " ${comp}") endif() endif() endforeach() set(COMPONENT_MSG "${FOUND_COMPONENTS_MSG} ${MISSING_COMPONENTS_MSG}") string(APPEND DETAILS "[c${COMPONENT_MSG}]") endif() # version handling: set(VERSION_MSG "") set(VERSION_OK TRUE) # check that the version variable is not empty to avoid emitting a misleading # message (i.e. `Found unsuitable version ""`) if (DEFINED ${_NAME}_FIND_VERSION) if(DEFINED ${FPHSA_VERSION_VAR}) if(NOT "${${FPHSA_VERSION_VAR}}" STREQUAL "") set(_FOUND_VERSION ${${FPHSA_VERSION_VAR}}) if (FPHSA_HANDLE_VERSION_RANGE) set (FPCV_HANDLE_VERSION_RANGE HANDLE_VERSION_RANGE) else() set(FPCV_HANDLE_VERSION_RANGE NO_AUTHOR_WARNING_VERSION_RANGE) endif() find_package_check_version ("${_FOUND_VERSION}" VERSION_OK RESULT_MESSAGE_VARIABLE VERSION_MSG ${FPCV_HANDLE_VERSION_RANGE}) else() set(VERSION_OK FALSE) endif() endif() if("${${FPHSA_VERSION_VAR}}" STREQUAL "") # if the package was not found, but a version was given, add that to the output: if(${_NAME}_FIND_VERSION_EXACT) set(VERSION_MSG "(Required is exact version \"${${_NAME}_FIND_VERSION}\")") elseif (FPHSA_HANDLE_VERSION_RANGE AND ${_NAME}_FIND_VERSION_RANGE) set(VERSION_MSG "(Required is version range \"${${_NAME}_FIND_VERSION_RANGE}\")") else() set(VERSION_MSG "(Required is at least version \"${${_NAME}_FIND_VERSION}\")") endif() endif() else () # Check with DEFINED as the found version may be 0. if(DEFINED ${FPHSA_VERSION_VAR}) set(VERSION_MSG "(found version \"${${FPHSA_VERSION_VAR}}\")") endif() endif () if(VERSION_OK) string(APPEND DETAILS "[v${${FPHSA_VERSION_VAR}}(${${_NAME}_FIND_VERSION})]") else() set(${_NAME}_FOUND FALSE) endif() # print the result: if (${_NAME}_FOUND) FIND_PACKAGE_MESSAGE(${_NAME} "Found ${_NAME}: ${${_FIRST_REQUIRED_VAR}} ${VERSION_MSG} ${COMPONENT_MSG}" "${DETAILS}") else () if(FPHSA_CONFIG_MODE) _FPHSA_HANDLE_FAILURE_CONFIG_MODE() else() if(NOT VERSION_OK) set(RESULT_MSG) if (_FIRST_REQUIRED_VAR) string (APPEND RESULT_MSG "found ${${_FIRST_REQUIRED_VAR}}") endif() if (COMPONENT_MSG) if (RESULT_MSG) string (APPEND RESULT_MSG ", ") endif() string (APPEND RESULT_MSG "${FOUND_COMPONENTS_MSG}") endif() _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: ${VERSION_MSG} (${RESULT_MSG})") else() _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} (missing:${MISSING_VARS}) ${VERSION_MSG}") endif() endif() endif () set(${_NAME}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE) set(${_NAME_UPPER}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE) endfunction() cmake_policy(POP) scikit-build-core-0.11.1/src/scikit_build_core/resources/find_python/FindPackageMessage.cmake000066400000000000000000000032571477275177200323770ustar00rootroot00000000000000# Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. #[=======================================================================[.rst: FindPackageMessage ------------------ .. code-block:: cmake find_package_message( "message for user" "find result details") This function is intended to be used in FindXXX.cmake modules files. It will print a message once for each unique find result. This is useful for telling the user where a package was found. The first argument specifies the name (XXX) of the package. The second argument specifies the message to display. The third argument lists details about the find result so that if they change the message will be displayed again. The macro also obeys the QUIET argument to the find_package command. Example: .. code-block:: cmake if(X11_FOUND) find_package_message(X11 "Found X11: ${X11_X11_LIB}" "[${X11_X11_LIB}][${X11_INCLUDE_DIR}]") else() ... endif() #]=======================================================================] function(find_package_message pkg msg details) # Avoid printing a message repeatedly for the same find result. if(NOT ${pkg}_FIND_QUIETLY) string(REPLACE "\n" "" details "${details}") set(DETAILS_VAR FIND_PACKAGE_MESSAGE_DETAILS_${pkg}) if(NOT "${details}" STREQUAL "${${DETAILS_VAR}}") # The message has not yet been printed. message(STATUS "${msg}") # Save the find details in the cache to avoid printing the same # message again. set("${DETAILS_VAR}" "${details}" CACHE INTERNAL "Details about finding ${pkg}") endif() endif() endfunction() scikit-build-core-0.11.1/src/scikit_build_core/resources/find_python/FindPython.cmake000066400000000000000000000540101477275177200310110ustar00rootroot00000000000000# Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. #[=======================================================================[.rst: FindPython ---------- .. versionadded:: 3.12 Find Python interpreter, compiler and development environment (include directories and libraries). .. versionadded:: 3.19 When a version is requested, it can be specified as a simple value or as a range. For a detailed description of version range usage and capabilities, refer to the :command:`find_package` command. The following components are supported: * ``Interpreter``: search for Python interpreter. * ``Compiler``: search for Python compiler. Only offered by IronPython. * ``Development``: search for development artifacts (include directories and libraries). .. versionadded:: 3.18 This component includes two sub-components which can be specified independently: * ``Development.Module``: search for artifacts for Python module developments. * ``Development.Embed``: search for artifacts for Python embedding developments. .. versionadded:: 3.26 * ``Development.SABIModule``: search for artifacts for Python module developments using the `Stable Application Binary Interface `_. This component is available only for version ``3.2`` and upper. * ``NumPy``: search for NumPy include directories. .. versionadded:: 3.14 Added the ``NumPy`` component. If no ``COMPONENTS`` are specified, ``Interpreter`` is assumed. If component ``Development`` is specified, it implies sub-components ``Development.Module`` and ``Development.Embed``. To ensure consistent versions between components ``Interpreter``, ``Compiler``, ``Development`` (or one of its sub-components) and ``NumPy``, specify all components at the same time:: find_package (Python COMPONENTS Interpreter Development) This module looks preferably for version 3 of Python. If not found, version 2 is searched. To manage concurrent versions 3 and 2 of Python, use :module:`FindPython3` and :module:`FindPython2` modules rather than this one. .. note:: If components ``Interpreter`` and ``Development`` (or one of its sub-components) are both specified, this module search only for interpreter with same platform architecture as the one defined by CMake configuration. This constraint does not apply if only ``Interpreter`` component is specified. Imported Targets ^^^^^^^^^^^^^^^^ This module defines the following :ref:`Imported Targets `: .. versionchanged:: 3.14 :ref:`Imported Targets ` are only created when :prop_gbl:`CMAKE_ROLE` is ``PROJECT``. ``Python::Interpreter`` Python interpreter. Target defined if component ``Interpreter`` is found. ``Python::Compiler`` Python compiler. Target defined if component ``Compiler`` is found. ``Python::Module`` .. versionadded:: 3.15 Python library for Python module. Target defined if component ``Development.Module`` is found. ``Python::SABIModule`` .. versionadded:: 3.26 Python library for Python module using the Stable Application Binary Interface. Target defined if component ``Development.SABIModule`` is found. ``Python::Python`` Python library for Python embedding. Target defined if component ``Development.Embed`` is found. ``Python::NumPy`` .. versionadded:: 3.14 NumPy Python library. Target defined if component ``NumPy`` is found. Result Variables ^^^^^^^^^^^^^^^^ This module will set the following variables in your project (see :ref:`Standard Variable Names `): ``Python_FOUND`` System has the Python requested components. ``Python_Interpreter_FOUND`` System has the Python interpreter. ``Python_EXECUTABLE`` Path to the Python interpreter. ``Python_INTERPRETER_ID`` A short string unique to the interpreter. Possible values include: * Python * ActivePython * Anaconda * Canopy * IronPython * PyPy ``Python_STDLIB`` Standard platform independent installation directory. Information returned by ``distutils.sysconfig.get_python_lib(plat_specific=False,standard_lib=True)`` or else ``sysconfig.get_path('stdlib')``. ``Python_STDARCH`` Standard platform dependent installation directory. Information returned by ``distutils.sysconfig.get_python_lib(plat_specific=True,standard_lib=True)`` or else ``sysconfig.get_path('platstdlib')``. ``Python_SITELIB`` Third-party platform independent installation directory. Information returned by ``distutils.sysconfig.get_python_lib(plat_specific=False,standard_lib=False)`` or else ``sysconfig.get_path('purelib')``. ``Python_SITEARCH`` Third-party platform dependent installation directory. Information returned by ``distutils.sysconfig.get_python_lib(plat_specific=True,standard_lib=False)`` or else ``sysconfig.get_path('platlib')``. ``Python_SOABI`` .. versionadded:: 3.17 Extension suffix for modules. Information computed from ``distutils.sysconfig.get_config_var('EXT_SUFFIX')`` or ``distutils.sysconfig.get_config_var('SOABI')`` or ``python3-config --extension-suffix``. If package ``distutils.sysconfig`` is not available, ``sysconfig.get_config_var('EXT_SUFFIX')`` or ``sysconfig.get_config_var('SOABI')`` are used. ``Python_SOSABI`` .. versionadded:: 3.26 Extension suffix for modules using the Stable Application Binary Interface. Information computed from ``importlib.machinery.EXTENSION_SUFFIXES`` if the COMPONENT ``Interpreter`` was specified. Otherwise, the extension is ``abi3`` except for ``Windows``, ``MSYS`` and ``CYGWIN`` for which this is an empty string. ``Python_Compiler_FOUND`` System has the Python compiler. ``Python_COMPILER`` Path to the Python compiler. Only offered by IronPython. ``Python_COMPILER_ID`` A short string unique to the compiler. Possible values include: * IronPython ``Python_DOTNET_LAUNCHER`` .. versionadded:: 3.18 The ``.Net`` interpreter. Only used by ``IronPython`` implementation. ``Python_Development_FOUND`` System has the Python development artifacts. ``Python_Development.Module_FOUND`` .. versionadded:: 3.18 System has the Python development artifacts for Python module. ``Python_Development.SABIModule_FOUND`` .. versionadded:: 3.26 System has the Python development artifacts for Python module using the Stable Application Binary Interface. ``Python_Development.Embed_FOUND`` .. versionadded:: 3.18 System has the Python development artifacts for Python embedding. ``Python_INCLUDE_DIRS`` The Python include directories. ``Python_LINK_OPTIONS`` .. versionadded:: 3.19 The Python link options. Some configurations require specific link options for a correct build and execution. ``Python_LIBRARIES`` The Python libraries. ``Python_LIBRARY_DIRS`` The Python library directories. ``Python_RUNTIME_LIBRARY_DIRS`` The Python runtime library directories. ``Python_SABI_LIBRARIES`` .. versionadded:: 3.26 The Python libraries for the Stable Application Binary Interface. ``Python_SABI_LIBRARY_DIRS`` .. versionadded:: 3.26 The Python ``SABI`` library directories. ``Python_RUNTIME_SABI_LIBRARY_DIRS`` .. versionadded:: 3.26 The Python runtime ``SABI`` library directories. ``Python_VERSION`` Python version. ``Python_VERSION_MAJOR`` Python major version. ``Python_VERSION_MINOR`` Python minor version. ``Python_VERSION_PATCH`` Python patch version. ``Python_PyPy_VERSION`` .. versionadded:: 3.18 Python PyPy version. ``Python_NumPy_FOUND`` .. versionadded:: 3.14 System has the NumPy. ``Python_NumPy_INCLUDE_DIRS`` .. versionadded:: 3.14 The NumPy include directories. ``Python_NumPy_VERSION`` .. versionadded:: 3.14 The NumPy version. Hints ^^^^^ ``Python_ROOT_DIR`` Define the root directory of a Python installation. ``Python_USE_STATIC_LIBS`` * If not defined, search for shared libraries and static libraries in that order. * If set to TRUE, search **only** for static libraries. * If set to FALSE, search **only** for shared libraries. .. note:: This hint will be ignored on ``Windows`` because static libraries are not available on this platform. ``Python_FIND_ABI`` .. versionadded:: 3.16 This variable defines which ABIs, as defined in :pep:`3149`, should be searched. .. note:: This hint will be honored only when searched for ``Python`` version 3. .. note:: If ``Python_FIND_ABI`` is not defined, any ABI will be searched. The ``Python_FIND_ABI`` variable is a 3-tuple specifying, in that order, ``pydebug`` (``d``), ``pymalloc`` (``m``) and ``unicode`` (``u``) flags. Each element can be set to one of the following: * ``ON``: Corresponding flag is selected. * ``OFF``: Corresponding flag is not selected. * ``ANY``: The two possibilities (``ON`` and ``OFF``) will be searched. From this 3-tuple, various ABIs will be searched starting from the most specialized to the most general. Moreover, ``debug`` versions will be searched **after** ``non-debug`` ones. For example, if we have:: set (Python_FIND_ABI "ON" "ANY" "ANY") The following flags combinations will be appended, in that order, to the artifact names: ``dmu``, ``dm``, ``du``, and ``d``. And to search any possible ABIs:: set (Python_FIND_ABI "ANY" "ANY" "ANY") The following combinations, in that order, will be used: ``mu``, ``m``, ``u``, ````, ``dmu``, ``dm``, ``du`` and ``d``. .. note:: This hint is useful only on ``POSIX`` systems. So, on ``Windows`` systems, when ``Python_FIND_ABI`` is defined, ``Python`` distributions from `python.org `_ will be found only if value for each flag is ``OFF`` or ``ANY``. ``Python_FIND_STRATEGY`` .. versionadded:: 3.15 This variable defines how lookup will be done. The ``Python_FIND_STRATEGY`` variable can be set to one of the following: * ``VERSION``: Try to find the most recent version in all specified locations. This is the default if policy :policy:`CMP0094` is undefined or set to ``OLD``. * ``LOCATION``: Stops lookup as soon as a version satisfying version constraints is founded. This is the default if policy :policy:`CMP0094` is set to ``NEW``. ``Python_FIND_REGISTRY`` .. versionadded:: 3.13 On Windows the ``Python_FIND_REGISTRY`` variable determine the order of preference between registry and environment variables. the ``Python_FIND_REGISTRY`` variable can be set to one of the following: * ``FIRST``: Try to use registry before environment variables. This is the default. * ``LAST``: Try to use registry after environment variables. * ``NEVER``: Never try to use registry. ``Python_FIND_FRAMEWORK`` .. versionadded:: 3.15 On macOS the ``Python_FIND_FRAMEWORK`` variable determine the order of preference between Apple-style and unix-style package components. This variable can take same values as :variable:`CMAKE_FIND_FRAMEWORK` variable. .. note:: Value ``ONLY`` is not supported so ``FIRST`` will be used instead. If ``Python_FIND_FRAMEWORK`` is not defined, :variable:`CMAKE_FIND_FRAMEWORK` variable will be used, if any. ``Python_FIND_VIRTUALENV`` .. versionadded:: 3.15 This variable defines the handling of virtual environments managed by ``virtualenv`` or ``conda``. It is meaningful only when a virtual environment is active (i.e. the ``activate`` script has been evaluated). In this case, it takes precedence over ``Python_FIND_REGISTRY`` and ``CMAKE_FIND_FRAMEWORK`` variables. The ``Python_FIND_VIRTUALENV`` variable can be set to one of the following: * ``FIRST``: The virtual environment is used before any other standard paths to look-up for the interpreter. This is the default. * ``ONLY``: Only the virtual environment is used to look-up for the interpreter. * ``STANDARD``: The virtual environment is not used to look-up for the interpreter but environment variable ``PATH`` is always considered. In this case, variable ``Python_FIND_REGISTRY`` (Windows) or ``CMAKE_FIND_FRAMEWORK`` (macOS) can be set with value ``LAST`` or ``NEVER`` to select preferably the interpreter from the virtual environment. .. versionadded:: 3.17 Added support for ``conda`` environments. .. note:: If the component ``Development`` is requested, it is **strongly** recommended to also include the component ``Interpreter`` to get expected result. ``Python_FIND_IMPLEMENTATIONS`` .. versionadded:: 3.18 This variable defines, in an ordered list, the different implementations which will be searched. The ``Python_FIND_IMPLEMENTATIONS`` variable can hold the following values: * ``CPython``: this is the standard implementation. Various products, like ``Anaconda`` or ``ActivePython``, rely on this implementation. * ``IronPython``: This implementation use the ``CSharp`` language for ``.NET Framework`` on top of the `Dynamic Language Runtime` (``DLR``). See `IronPython `_. * ``PyPy``: This implementation use ``RPython`` language and ``RPython translation toolchain`` to produce the python interpreter. See `PyPy `_. The default value is: * Windows platform: ``CPython``, ``IronPython`` * Other platforms: ``CPython`` .. note:: This hint has the lowest priority of all hints, so even if, for example, you specify ``IronPython`` first and ``CPython`` in second, a python product based on ``CPython`` can be selected because, for example with ``Python_FIND_STRATEGY=LOCATION``, each location will be search first for ``IronPython`` and second for ``CPython``. .. note:: When ``IronPython`` is specified, on platforms other than ``Windows``, the ``.Net`` interpreter (i.e. ``mono`` command) is expected to be available through the ``PATH`` variable. ``Python_FIND_UNVERSIONED_NAMES`` .. versionadded:: 3.20 This variable defines how the generic names will be searched. Currently, it only applies to the generic names of the interpreter, namely, ``python3`` or ``python2`` and ``python``. The ``Python_FIND_UNVERSIONED_NAMES`` variable can be set to one of the following values: * ``FIRST``: The generic names are searched before the more specialized ones (such as ``python2.5`` for example). * ``LAST``: The generic names are searched after the more specialized ones. This is the default. * ``NEVER``: The generic name are not searched at all. Artifacts Specification ^^^^^^^^^^^^^^^^^^^^^^^ .. versionadded:: 3.16 To solve special cases, it is possible to specify directly the artifacts by setting the following variables: ``Python_EXECUTABLE`` The path to the interpreter. ``Python_COMPILER`` The path to the compiler. ``Python_DOTNET_LAUNCHER`` .. versionadded:: 3.18 The ``.Net`` interpreter. Only used by ``IronPython`` implementation. ``Python_LIBRARY`` The path to the library. It will be used to compute the variables ``Python_LIBRARIES``, ``Python_LIBRARY_DIRS`` and ``Python_RUNTIME_LIBRARY_DIRS``. ``Python_SABI_LIBRARY`` .. versionadded:: 3.26 The path to the library for Stable Application Binary Interface. It will be used to compute the variables ``Python_SABI_LIBRARIES``, ``Python_SABI_LIBRARY_DIRS`` and ``Python_RUNTIME_SABI_LIBRARY_DIRS``. ``Python_INCLUDE_DIR`` The path to the directory of the ``Python`` headers. It will be used to compute the variable ``Python_INCLUDE_DIRS``. ``Python_NumPy_INCLUDE_DIR`` The path to the directory of the ``NumPy`` headers. It will be used to compute the variable ``Python_NumPy_INCLUDE_DIRS``. .. note:: All paths must be absolute. Any artifact specified with a relative path will be ignored. .. note:: When an artifact is specified, all ``HINTS`` will be ignored and no search will be performed for this artifact. If more than one artifact is specified, it is the user's responsibility to ensure the consistency of the various artifacts. By default, this module supports multiple calls in different directories of a project with different version/component requirements while providing correct and consistent results for each call. To support this behavior, CMake cache is not used in the traditional way which can be problematic for interactive specification. So, to enable also interactive specification, module behavior can be controlled with the following variable: ``Python_ARTIFACTS_INTERACTIVE`` .. versionadded:: 3.18 Selects the behavior of the module. This is a boolean variable: * If set to ``TRUE``: Create CMake cache entries for the above artifact specification variables so that users can edit them interactively. This disables support for multiple version/component requirements. * If set to ``FALSE`` or undefined: Enable multiple version/component requirements. Commands ^^^^^^^^ This module defines the command ``Python_add_library`` (when :prop_gbl:`CMAKE_ROLE` is ``PROJECT``), which has the same semantics as :command:`add_library` and adds a dependency to target ``Python::Python`` or, when library type is ``MODULE``, to target ``Python::Module`` or ``Python::SABIModule`` (when ``USE_SABI`` option is specified) and takes care of Python module naming rules:: Python_add_library ( [STATIC | SHARED | MODULE [USE_SABI ] [WITH_SOABI]] [ ...]) If the library type is not specified, ``MODULE`` is assumed. .. versionadded:: 3.17 For ``MODULE`` library type, if option ``WITH_SOABI`` is specified, the module suffix will include the ``Python_SOABI`` value, if any. .. versionadded:: 3.26 For ``MODULE`` type, if the option ``USE_SABI`` is specified, the preprocessor definition ``Py_LIMITED_API`` will be specified, as ``PRIVATE``, for the target ```` with the value computed from ```` argument. The expected format for ```` is ``major[.minor]``, where each component is a numeric value. If ``minor`` component is specified, the version should be, at least, ``3.2`` which is the version where the `Stable Application Binary Interface `_ was introduced. Specifying only major version ``3`` is equivalent to ``3.2``. When option ``WITH_SOABI`` is also specified, the module suffix will include the ``Python3_SOSABI`` value, if any. #]=======================================================================] cmake_policy(PUSH) # numbers and boolean constants cmake_policy (SET CMP0012 NEW) set (_PYTHON_PREFIX Python) unset (_Python_REQUIRED_VERSION_MAJOR) unset (_Python_REQUIRED_VERSIONS) if (Python_FIND_VERSION_RANGE) # compute list of major versions foreach (_Python_MAJOR IN ITEMS 3 2) if (_Python_MAJOR VERSION_GREATER_EQUAL Python_FIND_VERSION_MIN_MAJOR AND ((Python_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND _Python_MAJOR VERSION_LESS_EQUAL Python_FIND_VERSION_MAX) OR (Python_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND _Python_MAJOR VERSION_LESS Python_FIND_VERSION_MAX))) list (APPEND _Python_REQUIRED_VERSIONS ${_Python_MAJOR}) endif() endforeach() list (LENGTH _Python_REQUIRED_VERSIONS _Python_VERSION_COUNT) if (_Python_VERSION_COUNT EQUAL 0) unset (_Python_REQUIRED_VERSIONS) elseif (_Python_VERSION_COUNT EQUAL 1) set (_Python_REQUIRED_VERSION_MAJOR ${_Python_REQUIRED_VERSIONS}) endif() elseif (DEFINED Python_FIND_VERSION) set (_Python_REQUIRED_VERSION_MAJOR ${Python_FIND_VERSION_MAJOR}) else() set (_Python_REQUIRED_VERSIONS 3 2) endif() if (_Python_REQUIRED_VERSION_MAJOR) include (${CMAKE_CURRENT_LIST_DIR}/FindPython/Support.cmake) elseif (_Python_REQUIRED_VERSIONS) # iterate over versions in quiet and NOT required modes to avoid multiple # "Found" messages and prematurally failure. set (_Python_QUIETLY ${Python_FIND_QUIETLY}) set (_Python_REQUIRED ${Python_FIND_REQUIRED}) set (Python_FIND_QUIETLY TRUE) set (Python_FIND_REQUIRED FALSE) set (_Python_REQUIRED_VERSION_LAST 2) unset (_Python_INPUT_VARS) foreach (_Python_ITEM IN ITEMS Python_EXECUTABLE Python_COMPILER Python_LIBRARY Python_INCLUDE_DIR Python_NumPy_INCLUDE_DIR) if (NOT DEFINED ${_Python_ITEM}) list (APPEND _Python_INPUT_VARS ${_Python_ITEM}) endif() endforeach() foreach (_Python_REQUIRED_VERSION_MAJOR IN LISTS _Python_REQUIRED_VERSIONS) set (Python_FIND_VERSION ${_Python_REQUIRED_VERSION_MAJOR}) include (${CMAKE_CURRENT_LIST_DIR}/FindPython/Support.cmake) if (Python_FOUND OR _Python_REQUIRED_VERSION_MAJOR EQUAL _Python_REQUIRED_VERSION_LAST) break() endif() # clean-up INPUT variables not set by the user foreach (_Python_ITEM IN LISTS _Python_INPUT_VARS) unset (${_Python_ITEM}) endforeach() # clean-up some CACHE variables to ensure look-up restart from scratch foreach (_Python_ITEM IN LISTS _Python_CACHED_VARS) unset (${_Python_ITEM} CACHE) endforeach() endforeach() unset (Python_FIND_VERSION) set (Python_FIND_QUIETLY ${_Python_QUIETLY}) set (Python_FIND_REQUIRED ${_Python_REQUIRED}) if (Python_FIND_REQUIRED OR NOT Python_FIND_QUIETLY) # call again validation command to get "Found" or error message find_package_handle_standard_args (Python HANDLE_COMPONENTS HANDLE_VERSION_RANGE REQUIRED_VARS ${_Python_REQUIRED_VARS} VERSION_VAR Python_VERSION) endif() else() # supported versions not in the specified range. Call final check if (NOT Python_FIND_COMPONENTS) set (Python_FIND_COMPONENTS Interpreter) set (Python_FIND_REQUIRED_Interpreter TRUE) endif() include (${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) find_package_handle_standard_args (Python HANDLE_COMPONENTS HANDLE_VERSION_RANGE VERSION_VAR Python_VERSION REASON_FAILURE_MESSAGE "Version range specified \"${Python_FIND_VERSION_RANGE}\" does not include supported versions") endif() if (COMMAND __Python_add_library) macro (Python_add_library) __Python_add_library (Python ${ARGV}) endmacro() endif() unset (_PYTHON_PREFIX) cmake_policy(POP) scikit-build-core-0.11.1/src/scikit_build_core/resources/find_python/FindPython/000077500000000000000000000000001477275177200300075ustar00rootroot00000000000000scikit-build-core-0.11.1/src/scikit_build_core/resources/find_python/FindPython/Support.cmake000066400000000000000000006036121477275177200324750ustar00rootroot00000000000000# Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. # # This file is a "template" file used by various FindPython modules. # # # Initial configuration # cmake_policy(PUSH) # list supports empty elements cmake_policy (SET CMP0007 NEW) # numbers and boolean constants cmake_policy (SET CMP0012 NEW) # IN_LIST operator cmake_policy (SET CMP0057 NEW) # XXX(cmake 3.20) if (NOT CMAKE_VERSION VERSION_LESS 3.21) # foreach loop variable scope cmake_policy (SET CMP0124 NEW) endif () if (NOT CMAKE_VERSION VERSION_LESS 3.24) # registry view behavior cmake_policy (SET CMP0134 NEW) endif () if (NOT DEFINED _PYTHON_PREFIX) message (FATAL_ERROR "FindPython: INTERNAL ERROR") endif() if (NOT DEFINED _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) message (FATAL_ERROR "FindPython: INTERNAL ERROR") endif() if (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR EQUAL "3") set(_${_PYTHON_PREFIX}_VERSIONS 3.14 3.13 3.12 3.11 3.10 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0) elseif (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR EQUAL "2") set(_${_PYTHON_PREFIX}_VERSIONS 2.7 2.6 2.5 2.4 2.3 2.2 2.1 2.0) else() message (FATAL_ERROR "FindPython: INTERNAL ERROR") endif() get_property(_${_PYTHON_PREFIX}_CMAKE_ROLE GLOBAL PROPERTY CMAKE_ROLE) include (${CMAKE_CURRENT_LIST_DIR}/../FindPackageHandleStandardArgs.cmake) # # helper commands # macro (_PYTHON_DISPLAY_FAILURE _PYTHON_MSG) if (${_PYTHON_PREFIX}_FIND_REQUIRED) message (FATAL_ERROR "${_PYTHON_MSG}") else() if (NOT ${_PYTHON_PREFIX}_FIND_QUIETLY) message(STATUS "${_PYTHON_MSG}") endif () endif() set (${_PYTHON_PREFIX}_FOUND FALSE) string (TOUPPER "${_PYTHON_PREFIX}" _${_PYTHON_PREFIX}_UPPER_PREFIX) set (${_PYTHON_UPPER_PREFIX}_FOUND FALSE) endmacro() function (_PYTHON_ADD_REASON_FAILURE module message) if (_${_PYTHON_PREFIX}_${module}_REASON_FAILURE) string (LENGTH "${_${_PYTHON_PREFIX}_${module}_REASON_FAILURE}" length) math (EXPR length "${length} + 10") string (REPEAT " " ${length} shift) set_property (CACHE _${_PYTHON_PREFIX}_${module}_REASON_FAILURE PROPERTY VALUE "${_${_PYTHON_PREFIX}_${module}_REASON_FAILURE}\n${shift}${message}") else() set_property (CACHE _${_PYTHON_PREFIX}_${module}_REASON_FAILURE PROPERTY VALUE "${message}") endif() endfunction() function (_PYTHON_MARK_AS_INTERNAL) foreach (var IN LISTS ARGV) if (DEFINED CACHE{${var}}) set_property (CACHE ${var} PROPERTY TYPE INTERNAL) endif() endforeach() endfunction() macro (_PYTHON_SELECT_LIBRARY_CONFIGURATIONS _PYTHON_BASENAME) if(NOT DEFINED ${_PYTHON_BASENAME}_LIBRARY_RELEASE) set(${_PYTHON_BASENAME}_LIBRARY_RELEASE "${_PYTHON_BASENAME}_LIBRARY_RELEASE-NOTFOUND") endif() if(NOT DEFINED ${_PYTHON_BASENAME}_LIBRARY_DEBUG) set(${_PYTHON_BASENAME}_LIBRARY_DEBUG "${_PYTHON_BASENAME}_LIBRARY_DEBUG-NOTFOUND") endif() get_property(_PYTHON_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if (${_PYTHON_BASENAME}_LIBRARY_DEBUG AND ${_PYTHON_BASENAME}_LIBRARY_RELEASE AND NOT ${_PYTHON_BASENAME}_LIBRARY_DEBUG STREQUAL ${_PYTHON_BASENAME}_LIBRARY_RELEASE AND (_PYTHON_isMultiConfig OR CMAKE_BUILD_TYPE)) # if the generator is multi-config or if CMAKE_BUILD_TYPE is set for # single-config generators, set optimized and debug libraries set (${_PYTHON_BASENAME}_LIBRARIES "") foreach (_PYTHON_libname IN LISTS ${_PYTHON_BASENAME}_LIBRARY_RELEASE) list( APPEND ${_PYTHON_BASENAME}_LIBRARIES optimized "${_PYTHON_libname}") endforeach() foreach (_PYTHON_libname IN LISTS ${_PYTHON_BASENAME}_LIBRARY_DEBUG) list( APPEND ${_PYTHON_BASENAME}_LIBRARIES debug "${_PYTHON_libname}") endforeach() elseif (${_PYTHON_BASENAME}_LIBRARY_RELEASE) set (${_PYTHON_BASENAME}_LIBRARIES "${${_PYTHON_BASENAME}_LIBRARY_RELEASE}") elseif (${_PYTHON_BASENAME}_LIBRARY_DEBUG) set (${_PYTHON_BASENAME}_LIBRARIES "${${_PYTHON_BASENAME}_LIBRARY_DEBUG}") else() set (${_PYTHON_BASENAME}_LIBRARIES "${_PYTHON_BASENAME}_LIBRARY-NOTFOUND") endif() endmacro() macro (_PYTHON_FIND_FRAMEWORKS) if (CMAKE_HOST_APPLE OR APPLE) file(TO_CMAKE_PATH "$ENV{CMAKE_FRAMEWORK_PATH}" _pff_CMAKE_FRAMEWORK_PATH) set (_pff_frameworks ${CMAKE_FRAMEWORK_PATH} ${_pff_CMAKE_FRAMEWORK_PATH} ~/Library/Frameworks /usr/local/Frameworks /opt/homebrew/Frameworks ${CMAKE_SYSTEM_FRAMEWORK_PATH}) list (REMOVE_DUPLICATES _pff_frameworks) foreach (_pff_implementation IN LISTS _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS) unset (_${_PYTHON_PREFIX}_${_pff_implementation}_FRAMEWORKS) if (_pff_implementation STREQUAL "CPython") foreach (_pff_framework IN LISTS _pff_frameworks) if (EXISTS ${_pff_framework}/Python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}.framework) list (APPEND _${_PYTHON_PREFIX}_${_pff_implementation}_FRAMEWORKS ${_pff_framework}/Python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}.framework) endif() if (EXISTS ${_pff_framework}/Python.framework) list (APPEND _${_PYTHON_PREFIX}_${_pff_implementation}_FRAMEWORKS ${_pff_framework}/Python.framework) endif() endforeach() elseif (_pff_implementation STREQUAL "IronPython") foreach (_pff_framework IN LISTS _pff_frameworks) if (EXISTS ${_pff_framework}/IronPython.framework) list (APPEND _${_PYTHON_PREFIX}_${_pff_implementation}_FRAMEWORKS ${_pff_framework}/IronPython.framework) endif() endforeach() endif() endforeach() unset (_pff_implementation) unset (_pff_frameworks) unset (_pff_framework) endif() endmacro() function (_PYTHON_GET_FRAMEWORKS _PYTHON_PGF_FRAMEWORK_PATHS) cmake_parse_arguments (PARSE_ARGV 1 _PGF "" "" "IMPLEMENTATIONS;VERSION") if (NOT _PGF_IMPLEMENTATIONS) set (_PGF_IMPLEMENTATIONS ${_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS}) endif() set (framework_paths) foreach (implementation IN LISTS _PGF_IMPLEMENTATIONS) if (implementation STREQUAL "CPython") foreach (version IN LISTS _PGF_VERSION) foreach (framework IN LISTS _${_PYTHON_PREFIX}_${implementation}_FRAMEWORKS) if (EXISTS "${framework}/Versions/${version}") list (APPEND framework_paths "${framework}/Versions/${version}") endif() endforeach() endforeach() elseif (implementation STREQUAL "IronPython") foreach (version IN LISTS _PGF_VERSION) foreach (framework IN LISTS _${_PYTHON_PREFIX}_${implementation}_FRAMEWORKS) # pick-up all available versions file (GLOB versions LIST_DIRECTORIES true RELATIVE "${framework}/Versions/" "${framework}/Versions/${version}*") list (SORT versions ORDER DESCENDING) list (TRANSFORM versions PREPEND "${framework}/Versions/") list (APPEND framework_paths ${versions}) endforeach() endforeach() endif() endforeach() set (${_PYTHON_PGF_FRAMEWORK_PATHS} ${framework_paths} PARENT_SCOPE) endfunction() function (_PYTHON_GET_REGISTRIES _PYTHON_PGR_REGISTRY_PATHS) cmake_parse_arguments (PARSE_ARGV 1 _PGR "" "" "IMPLEMENTATIONS;VERSION") if (NOT _PGR_IMPLEMENTATIONS) set (_PGR_IMPLEMENTATIONS ${_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS}) endif() set (registries) foreach (implementation IN LISTS _PGR_IMPLEMENTATIONS) if (implementation STREQUAL "CPython") foreach (version IN LISTS _PGR_VERSION) string (REPLACE "." "" version_no_dots ${version}) list (APPEND registries [HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}-${_${_PYTHON_PREFIX}_ARCH}/InstallPath] [HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}-${_${_PYTHON_PREFIX}_ARCH2}/InstallPath]) if (version VERSION_GREATER_EQUAL "3.5") # cmake_host_system_information is not usable in bootstrap get_filename_component (arch "[HKEY_CURRENT_USER\\Software\\Python\\PythonCore\\${version};SysArchitecture]" NAME) if (arch MATCHES "(${_${_PYTHON_PREFIX}_ARCH}|${_${_PYTHON_PREFIX}_ARCH2})bit") list (APPEND registries [HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}/InstallPath]) endif() else() list (APPEND registries [HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}/InstallPath]) endif() list (APPEND registries [HKEY_CURRENT_USER/SOFTWARE/Python/ContinuumAnalytics/Anaconda${version_no_dots}-${_${_PYTHON_PREFIX}_ARCH}/InstallPath] [HKEY_CURRENT_USER/SOFTWARE/Python/ContinuumAnalytics/Anaconda${version_no_dots}-${_${_PYTHON_PREFIX}_ARCH2}/InstallPath] [HKEY_LOCAL_MACHINE/SOFTWARE/Python/PythonCore/${version}-${_${_PYTHON_PREFIX}_ARCH}/InstallPath] [HKEY_LOCAL_MACHINE/SOFTWARE/Python/PythonCore/${version}-${_${_PYTHON_PREFIX}_ARCH2}/InstallPath] [HKEY_LOCAL_MACHINE/SOFTWARE/Python/PythonCore/${version}/InstallPath] [HKEY_LOCAL_MACHINE/SOFTWARE/Python/ContinuumAnalytics/Anaconda${version_no_dots}-${_${_PYTHON_PREFIX}_ARCH}/InstallPath] [HKEY_LOCAL_MACHINE/SOFTWARE/Python/ContinuumAnalytics/Anaconda${version_no_dots}-${_${_PYTHON_PREFIX}_ARCH2}/InstallPath]) endforeach() elseif (implementation STREQUAL "IronPython") foreach (version IN LISTS _PGR_VERSION) list (APPEND registries [HKEY_LOCAL_MACHINE/SOFTWARE/IronPython/${version}/InstallPath]) endforeach() endif() endforeach() set (${_PYTHON_PGR_REGISTRY_PATHS} "${registries}" PARENT_SCOPE) endfunction() function (_PYTHON_GET_ABIFLAGS _PGABIFLAGS) set (abiflags) list (GET _${_PYTHON_PREFIX}_FIND_ABI 0 pydebug) list (GET _${_PYTHON_PREFIX}_FIND_ABI 1 pymalloc) list (GET _${_PYTHON_PREFIX}_FIND_ABI 2 unicode) if (pymalloc STREQUAL "ANY" AND unicode STREQUAL "ANY") set (abiflags "mu" "m" "u" "") elseif (pymalloc STREQUAL "ANY" AND unicode STREQUAL "ON") set (abiflags "mu" "u") elseif (pymalloc STREQUAL "ANY" AND unicode STREQUAL "OFF") set (abiflags "m" "") elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "ANY") set (abiflags "mu" "m") elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "ON") set (abiflags "mu") elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "OFF") set (abiflags "m") elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "ANY") set (abiflags "u" "") elseif (pymalloc STREQUAL "OFF" AND unicode STREQUAL "ON") set (abiflags "u") endif() if (pydebug STREQUAL "ON") if (abiflags) list (TRANSFORM abiflags PREPEND "d") else() set (abiflags "d") endif() elseif (pydebug STREQUAL "ANY") if (abiflags) set (flags "${abiflags}") list (TRANSFORM flags PREPEND "d") list (APPEND abiflags "${flags}") else() set (abiflags "" "d") endif() endif() set (${_PGABIFLAGS} "${abiflags}" PARENT_SCOPE) endfunction() function (_PYTHON_GET_PATH_SUFFIXES _PYTHON_PGPS_PATH_SUFFIXES) cmake_parse_arguments (PARSE_ARGV 1 _PGPS "INTERPRETER;COMPILER;LIBRARY;INCLUDE" "" "IMPLEMENTATIONS;VERSION") if (NOT _PGPS_IMPLEMENTATIONS) set (_PGPS_IMPLEMENTATIONS ${_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS}) endif() if (DEFINED _${_PYTHON_PREFIX}_ABIFLAGS) set (abi "${_${_PYTHON_PREFIX}_ABIFLAGS}") else() set (abi "mu" "m" "u" "") endif() set (path_suffixes) foreach (implementation IN LISTS _PGPS_IMPLEMENTATIONS) if (implementation STREQUAL "CPython") if (_PGPS_INTERPRETER) list (APPEND path_suffixes bin Scripts) else() foreach (version IN LISTS _PGPS_VERSION) if (_PGPS_LIBRARY) if (CMAKE_LIBRARY_ARCHITECTURE) list (APPEND path_suffixes lib/${CMAKE_LIBRARY_ARCHITECTURE}) endif() list (APPEND path_suffixes lib libs) if (CMAKE_LIBRARY_ARCHITECTURE) set (suffixes "${abi}") if (suffixes) list (TRANSFORM suffixes PREPEND "lib/python${version}/config-${version}") list (TRANSFORM suffixes APPEND "-${CMAKE_LIBRARY_ARCHITECTURE}") else() set (suffixes "lib/python${version}/config-${version}-${CMAKE_LIBRARY_ARCHITECTURE}") endif() list (APPEND path_suffixes ${suffixes}) endif() set (suffixes "${abi}") if (suffixes) list (TRANSFORM suffixes PREPEND "lib/python${version}/config-${version}") else() set (suffixes "lib/python${version}/config-${version}") endif() list (APPEND path_suffixes ${suffixes}) elseif (_PGPS_INCLUDE) set (suffixes "${abi}") if (suffixes) list (TRANSFORM suffixes PREPEND "include/python${version}") else() set (suffixes "include/python${version}") endif() list (APPEND path_suffixes ${suffixes} include) endif() endforeach() endif() elseif (implementation STREQUAL "IronPython") if (_PGPS_INTERPRETER OR _PGPS_COMPILER) foreach (version IN LISTS _PGPS_VERSION) list (APPEND path_suffixes "share/ironpython${version}") endforeach() list (APPEND path_suffixes ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}) endif() elseif (implementation STREQUAL "PyPy") if (_PGPS_INTERPRETER) list (APPEND path_suffixes ${_${_PYTHON_PREFIX}_PYPY_EXECUTABLE_PATH_SUFFIXES}) elseif (_PGPS_LIBRARY) list (APPEND path_suffixes ${_${_PYTHON_PREFIX}_PYPY_LIBRARY_PATH_SUFFIXES}) elseif (_PGPS_INCLUDE) foreach (version IN LISTS _PGPS_VERSION) list (APPEND path_suffixes lib/pypy${version}/include pypy${version}/include) endforeach() list (APPEND path_suffixes ${_${_PYTHON_PREFIX}_PYPY_INCLUDE_PATH_SUFFIXES}) endif() endif() endforeach() list (REMOVE_DUPLICATES path_suffixes) set (${_PYTHON_PGPS_PATH_SUFFIXES} ${path_suffixes} PARENT_SCOPE) endfunction() function (_PYTHON_GET_NAMES _PYTHON_PGN_NAMES) cmake_parse_arguments (PARSE_ARGV 1 _PGN "POSIX;INTERPRETER;COMPILER;CONFIG;LIBRARY;WIN32;DEBUG" "" "IMPLEMENTATIONS;VERSION") if (NOT _PGN_IMPLEMENTATIONS) set (_PGN_IMPLEMENTATIONS ${_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS}) endif() set (names) foreach (implementation IN LISTS _PGN_IMPLEMENTATIONS) if (implementation STREQUAL "CPython") if (_PGN_INTERPRETER AND _${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES STREQUAL "FIRST") list (APPEND names python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} python) endif() foreach (version IN LISTS _PGN_VERSION) if (_PGN_WIN32) string (REPLACE "." "" version_no_dots ${version}) set (name python${version_no_dots}) if (_PGN_DEBUG) string (APPEND name "_d") endif() list (APPEND names "${name}") endif() if (_PGN_POSIX) if (DEFINED _${_PYTHON_PREFIX}_ABIFLAGS) set (abi "${_${_PYTHON_PREFIX}_ABIFLAGS}") else() if (_PGN_INTERPRETER OR _PGN_CONFIG) set (abi "") else() set (abi "mu" "m" "u" "") endif() endif() if (abi) if (_PGN_CONFIG AND DEFINED CMAKE_LIBRARY_ARCHITECTURE) set (abinames "${abi}") list (TRANSFORM abinames PREPEND "${CMAKE_LIBRARY_ARCHITECTURE}-python${version}") list (TRANSFORM abinames APPEND "-config") list (APPEND names ${abinames}) endif() set (abinames "${abi}") list (TRANSFORM abinames PREPEND "python${version}") if (_PGN_CONFIG) list (TRANSFORM abinames APPEND "-config") endif() list (APPEND names ${abinames}) else() unset (abinames) if (_PGN_CONFIG AND DEFINED CMAKE_LIBRARY_ARCHITECTURE) set (abinames "${CMAKE_LIBRARY_ARCHITECTURE}-python${version}") endif() list (APPEND abinames "python${version}") if (_PGN_CONFIG) list (TRANSFORM abinames APPEND "-config") endif() list (APPEND names ${abinames}) endif() endif() endforeach() if (_PGN_INTERPRETER AND _${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES STREQUAL "LAST") list (APPEND names python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} python) endif() elseif (implementation STREQUAL "IronPython") if (_PGN_INTERPRETER) if (NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") # Do not use wrapper script on Linux because it is buggy: -c interpreter option cannot be used foreach (version IN LISTS _PGN_VERSION) list (APPEND names "ipy${version}") endforeach() endif() list (APPEND names ${_${_PYTHON_PREFIX}_IRON_PYTHON_INTERPRETER_NAMES}) elseif (_PGN_COMPILER) list (APPEND names ${_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_NAMES}) endif() elseif (implementation STREQUAL "PyPy") if (_PGN_INTERPRETER) list (APPEND names ${_${_PYTHON_PREFIX}_PYPY_NAMES}) elseif (_PGN_LIBRARY) if (_PGN_WIN32) foreach (version IN LISTS _PGN_VERSION) string (REPLACE "." "" version_no_dots ${version}) set (name "python${version_no_dots}") if (_PGN_DEBUG) string (APPEND name "_d") endif() list (APPEND names "${name}") endforeach() endif() if (_PGN_POSIX) foreach(version IN LISTS _PGN_VERSION) list (APPEND names "pypy${version}-c") endforeach() endif() list (APPEND names ${_${_PYTHON_PREFIX}_PYPY_LIB_NAMES}) endif() endif() endforeach() set (${_PYTHON_PGN_NAMES} ${names} PARENT_SCOPE) endfunction() function (_PYTHON_GET_CONFIG_VAR _PYTHON_PGCV_VALUE NAME) unset (${_PYTHON_PGCV_VALUE} PARENT_SCOPE) if (NOT NAME MATCHES "^(PREFIX|ABIFLAGS|CONFIGDIR|INCLUDES|LIBS|SOABI|SOSABI)$") return() endif() if (NAME STREQUAL "SOSABI") # assume some default if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR CMAKE_SYSTEM_NAME MATCHES "MSYS|CYGWIN") set (_values "") else() set (_values "abi${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}") endif() elseif (_${_PYTHON_PREFIX}_CONFIG) if (NAME STREQUAL "SOABI") set (config_flag "--extension-suffix") else() set (config_flag "--${NAME}") endif() string (TOLOWER "${config_flag}" config_flag) execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" ${config_flag} RESULT_VARIABLE _result OUTPUT_VARIABLE _values ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (_result) unset (_values) else() if (NAME STREQUAL "INCLUDES") # do some clean-up string (REGEX MATCHALL "(-I|-iwithsysroot)[ ]*[^ ]+" _values "${_values}") string (REGEX REPLACE "(-I|-iwithsysroot)[ ]*" "" _values "${_values}") list (REMOVE_DUPLICATES _values) elseif (NAME STREQUAL "SOABI") # clean-up: remove prefix character and suffix if (_values MATCHES "^(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.so|\\.pyd)$") set(_values "") else() string (REGEX REPLACE "^[.-](.+)(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.(so|pyd))$" "\\1" _values "${_values}") endif() endif() endif() endif() if (_${_PYTHON_PREFIX}_EXECUTABLE AND NOT CMAKE_CROSSCOMPILING) if (NAME STREQUAL "PREFIX") execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys\ntry:\n from distutils import sysconfig\n sys.stdout.write(';'.join([sysconfig.PREFIX,sysconfig.EXEC_PREFIX,sysconfig.BASE_EXEC_PREFIX]))\nexcept Exception:\n import sysconfig\n sys.stdout.write(';'.join([sysconfig.get_config_var('base') or '', sysconfig.get_config_var('installed_base') or '']))" RESULT_VARIABLE _result OUTPUT_VARIABLE _values ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (_result) unset (_values) else() list (REMOVE_DUPLICATES _values) endif() elseif (NAME STREQUAL "INCLUDES") if (WIN32) set (_scheme "nt") else() set (_scheme "posix_prefix") endif() execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys\ntry:\n from distutils import sysconfig\n sys.stdout.write(';'.join([sysconfig.get_python_inc(plat_specific=True),sysconfig.get_python_inc(plat_specific=False)]))\nexcept Exception:\n import sysconfig\n sys.stdout.write(';'.join([sysconfig.get_path('platinclude'),sysconfig.get_path('platinclude','${_scheme}'),sysconfig.get_path('include'),sysconfig.get_path('include','${_scheme}')]))" RESULT_VARIABLE _result OUTPUT_VARIABLE _values ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (_result) unset (_values) else() list (REMOVE_DUPLICATES _values) endif() elseif (NAME STREQUAL "SOABI") # first step: compute SOABI form EXT_SUFFIX config variable execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys\ntry:\n from distutils import sysconfig\n sys.stdout.write(sysconfig.get_config_var('EXT_SUFFIX') or '')\nexcept Exception:\n import sysconfig;sys.stdout.write(sysconfig.get_config_var('EXT_SUFFIX') or '')" RESULT_VARIABLE _result OUTPUT_VARIABLE _values ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (_result) unset (_values) else() if (_values) # clean-up: remove prefix character and suffix if (_values MATCHES "^(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.so|\\.pyd)$") set(_values "") else() string (REGEX REPLACE "^[.-](.+)(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.(so|pyd))$" "\\1" _values "${_values}") endif() endif() endif() # second step: use SOABI or SO config variables as fallback if (NOT _values) execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys\ntry:\n from distutils import sysconfig\n sys.stdout.write(';'.join([sysconfig.get_config_var('SOABI') or '',sysconfig.get_config_var('SO') or '']))\nexcept Exception:\n import sysconfig;sys.stdout.write(';'.join([sysconfig.get_config_var('SOABI') or '',sysconfig.get_config_var('SO') or '']))" RESULT_VARIABLE _result OUTPUT_VARIABLE _soabi ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (_result) unset (_values) else() foreach (_item IN LISTS _soabi) if (_item) set (_values "${_item}") break() endif() endforeach() if (_values) # clean-up: remove prefix character and suffix if (_values MATCHES "^(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.so|\\.pyd)$") set(_values "") else() string (REGEX REPLACE "^[.-](.+)(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.(so|pyd))$" "\\1" _values "${_values}") endif() endif() endif() endif() elseif (NAME STREQUAL "SOSABI") execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys\nimport re\nimport importlib.machinery\nsys.stdout.write(next(filter(lambda x: re.search('^\\.abi', x), importlib.machinery.EXTENSION_SUFFIXES)))" RESULT_VARIABLE _result OUTPUT_VARIABLE _values ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (_result) unset (_values) else() string (REGEX REPLACE "^\\.(.+)\\.[^.]+$" "\\1" _values "${_values}") endif() else() set (config_flag "${NAME}") if (NAME STREQUAL "CONFIGDIR") set (config_flag "LIBPL") endif() execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys\ntry:\n from distutils import sysconfig\n sys.stdout.write(sysconfig.get_config_var('${config_flag}'))\nexcept Exception:\n import sysconfig\n sys.stdout.write(sysconfig.get_config_var('${config_flag}'))" RESULT_VARIABLE _result OUTPUT_VARIABLE _values ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (_result) unset (_values) endif() endif() endif() if (NAME STREQUAL "ABIFLAGS" OR NAME STREQUAL "SOABI" OR NAME STREQUAL "SOSABI") set (${_PYTHON_PGCV_VALUE} "${_values}" PARENT_SCOPE) return() endif() if (NOT _values OR _values STREQUAL "None") return() endif() if (NAME STREQUAL "LIBS") # do some clean-up string (REGEX MATCHALL "-(l|framework)[ ]*[^ ]+" _values "${_values}") # remove elements relative to python library itself list (FILTER _values EXCLUDE REGEX "-lpython") list (REMOVE_DUPLICATES _values) endif() if (WIN32 AND NAME MATCHES "^(PREFIX|CONFIGDIR|INCLUDES)$") file (TO_CMAKE_PATH "${_values}" _values) endif() set (${_PYTHON_PGCV_VALUE} "${_values}" PARENT_SCOPE) endfunction() function (_PYTHON_GET_VERSION) cmake_parse_arguments (PARSE_ARGV 0 _PGV "LIBRARY;SABI_LIBRARY;INCLUDE" "PREFIX" "") unset (${_PGV_PREFIX}VERSION PARENT_SCOPE) unset (${_PGV_PREFIX}VERSION_MAJOR PARENT_SCOPE) unset (${_PGV_PREFIX}VERSION_MINOR PARENT_SCOPE) unset (${_PGV_PREFIX}VERSION_PATCH PARENT_SCOPE) unset (${_PGV_PREFIX}ABI PARENT_SCOPE) if (_PGV_LIBRARY) # retrieve version and abi from library name if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE) get_filename_component (library_name "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" NAME) # extract version from library name if (library_name MATCHES "python([23])([0-9]+)") set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE) set (${_PGV_PREFIX}VERSION_MINOR "${CMAKE_MATCH_2}" PARENT_SCOPE) set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}" PARENT_SCOPE) set (${_PGV_PREFIX}ABI "" PARENT_SCOPE) elseif (library_name MATCHES "python([23])\\.([0-9]+)([dmu]*)") set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE) set (${_PGV_PREFIX}VERSION_MINOR "${CMAKE_MATCH_2}" PARENT_SCOPE) set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}" PARENT_SCOPE) set (${_PGV_PREFIX}ABI "${CMAKE_MATCH_3}" PARENT_SCOPE) elseif (library_name MATCHES "pypy([23])\\.([0-9]+)-c") set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE) set (${_PGV_PREFIX}VERSION_MINOR "${CMAKE_MATCH_2}" PARENT_SCOPE) set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}" PARENT_SCOPE) set (${_PGV_PREFIX}ABI "" PARENT_SCOPE) elseif (library_name MATCHES "pypy(3)?-c") set (version "${CMAKE_MATCH_1}") # try to pick-up a more precise version from the path get_filename_component (library_dir "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY) if (library_dir MATCHES "/pypy([23])\\.([0-9]+)/") set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE) set (${_PGV_PREFIX}VERSION_MINOR "${CMAKE_MATCH_2}" PARENT_SCOPE) set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}" PARENT_SCOPE) elseif (version EQUAL "3") set (${_PGV_PREFIX}VERSION_MAJOR "3" PARENT_SCOPE) set (${_PGV_PREFIX}VERSION "3" PARENT_SCOPE) else() set (${_PGV_PREFIX}VERSION_MAJOR "2" PARENT_SCOPE) set (${_PGV_PREFIX}VERSION "2" PARENT_SCOPE) endif() set (${_PGV_PREFIX}ABI "" PARENT_SCOPE) endif() endif() elseif (_PGV_SABI_LIBRARY) # retrieve version and abi from library name if (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE) get_filename_component (library_name "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}" NAME) # extract version from library name if (library_name MATCHES "python([23])([dmu]*)") set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE) set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}" PARENT_SCOPE) set (${_PGV_PREFIX}ABI "${CMAKE_MATCH_2}" PARENT_SCOPE) elseif (library_name MATCHES "pypy([23])-c") set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE) set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}" PARENT_SCOPE) set (${_PGV_PREFIX}ABI "" PARENT_SCOPE) elseif (library_name MATCHES "pypy-c") # try to pick-up a more precise version from the path get_filename_component (library_dir "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}" DIRECTORY) if (library_dir MATCHES "/pypy([23])\\.([0-9]+)/") set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE) set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}" PARENT_SCOPE) endif() set (${_PGV_PREFIX}ABI "" PARENT_SCOPE) endif() endif() else() if (_${_PYTHON_PREFIX}_INCLUDE_DIR) # retrieve version from header file file (STRINGS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}/patchlevel.h" version REGEX "^#define[ \t]+PY_VERSION[ \t]+\"[^\"]+\"") string (REGEX REPLACE "^#define[ \t]+PY_VERSION[ \t]+\"([^\"]+)\".*" "\\1" version "${version}") string (REGEX MATCHALL "[0-9]+" versions "${version}") list (GET versions 0 version_major) list (GET versions 1 version_minor) list (GET versions 2 version_patch) set (${_PGV_PREFIX}VERSION "${version_major}.${version_minor}.${version_patch}" PARENT_SCOPE) set (${_PGV_PREFIX}VERSION_MAJOR ${version_major} PARENT_SCOPE) set (${_PGV_PREFIX}VERSION_MINOR ${version_minor} PARENT_SCOPE) set (${_PGV_PREFIX}VERSION_PATCH ${version_patch} PARENT_SCOPE) # compute ABI flags if (version_major VERSION_GREATER "2") file (STRINGS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}/pyconfig.h" config REGEX "(Py_DEBUG|WITH_PYMALLOC|Py_UNICODE_SIZE|MS_WIN32)") set (abi) if (config MATCHES "#[ ]*define[ ]+MS_WIN32") # ABI not used on Windows set (abi "") else() if (NOT config) # pyconfig.h can be a wrapper to a platform specific pyconfig.h # In this case, try to identify ABI from include directory if (_${_PYTHON_PREFIX}_INCLUDE_DIR MATCHES "python${version_major}\\.${version_minor}+([dmu]*)") set (abi "${CMAKE_MATCH_1}") else() set (abi "") endif() else() if (config MATCHES "#[ ]*define[ ]+Py_DEBUG[ ]+1") string (APPEND abi "d") endif() if (config MATCHES "#[ ]*define[ ]+WITH_PYMALLOC[ ]+1") string (APPEND abi "m") endif() if (config MATCHES "#[ ]*define[ ]+Py_UNICODE_SIZE[ ]+4") string (APPEND abi "u") endif() endif() set (${_PGV_PREFIX}ABI "${abi}" PARENT_SCOPE) endif() else() # ABI not supported set (${_PGV_PREFIX}ABI "" PARENT_SCOPE) endif() endif() endif() endfunction() function (_PYTHON_GET_LAUNCHER _PYTHON_PGL_NAME) cmake_parse_arguments (PARSE_ARGV 1 _PGL "INTERPRETER;COMPILER" "" "") unset (${_PYTHON_PGL_NAME} PARENT_SCOPE) if ((_PGL_INTERPRETER AND NOT _${_PYTHON_PREFIX}_EXECUTABLE) OR (_PGL_COMPILER AND NOT _${_PYTHON_PREFIX}_COMPILER)) return() endif() if ("IronPython" IN_LIST _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS AND NOT SYSTEM_NAME MATCHES "Windows|Linux") if (_PGL_INTERPRETER) get_filename_component (name "${_${_PYTHON_PREFIX}_EXECUTABLE}" NAME) get_filename_component (ext "${_${_PYTHON_PREFIX}_EXECUTABLE}" LAST_EXT) if (name IN_LIST _${_PYTHON_PREFIX}_IRON_PYTHON_INTERPRETER_NAMES AND ext STREQUAL ".exe") set (${_PYTHON_PGL_NAME} "${${_PYTHON_PREFIX}_DOTNET_LAUNCHER}" PARENT_SCOPE) endif() else() get_filename_component (name "${_${_PYTHON_PREFIX}_COMPILER}" NAME) get_filename_component (ext "${_${_PYTHON_PREFIX}_COMPILER}" LAST_EXT) if (name IN_LIST _${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_NAMES AND ext STREQUAL ".exe") set (${_PYTHON_PGL_NAME} "${${_PYTHON_PREFIX}_DOTNET_LAUNCHER}" PARENT_SCOPE) endif() endif() endif() endfunction() function (_PYTHON_VALIDATE_INTERPRETER) if (NOT _${_PYTHON_PREFIX}_EXECUTABLE) return() endif() cmake_parse_arguments (PARSE_ARGV 0 _PVI "IN_RANGE;EXACT;CHECK_EXISTS" "VERSION" "") if (_PVI_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_EXECUTABLE}") # interpreter does not exist anymore set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Cannot find the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") return() endif() _python_get_launcher (launcher INTERPRETER) # validate ABI compatibility if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI) execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; sys.stdout.write(sys.abiflags)" RESULT_VARIABLE result OUTPUT_VARIABLE abi ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (result) # assume ABI is not supported set (abi "") endif() if (NOT abi IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) # incompatible ABI set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong ABI for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") return() endif() endif() if (_PVI_IN_RANGE OR _PVI_VERSION) # retrieve full version execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:3]]))" RESULT_VARIABLE result OUTPUT_VARIABLE version ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (result) # interpreter is not usable set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Cannot use the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") return() endif() if (_PVI_VERSION) # check against specified version ## compute number of components for version string (REGEX REPLACE "[^.]" "" dots "${_PVI_VERSION}") ## add one dot because there is one dot less than there are components string (LENGTH "${dots}." count) if (count GREATER 3) set (count 3) endif() set (version_regex "^[0-9]+") if (count EQUAL 3) string (APPEND version_regex "\\.[0-9]+\\.[0-9]+") elseif (count EQUAL 2) string (APPEND version_regex "\\.[0-9]+") endif() # extract needed range string (REGEX MATCH "${version_regex}" version "${version}") if (_PVI_EXACT AND NOT version VERSION_EQUAL _PVI_VERSION) # interpreter has wrong version set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") return() else() # check that version is OK string(REGEX REPLACE "^([0-9]+)\\.?.*$" "\\1" major_version "${version}") string(REGEX REPLACE "^([0-9]+)\\.?.*$" "\\1" expected_major_version "${_PVI_VERSION}") if (NOT major_version VERSION_EQUAL expected_major_version OR NOT version VERSION_GREATER_EQUAL _PVI_VERSION) set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") return() endif() endif() endif() if (_PVI_IN_RANGE) # check if version is in the requested range find_package_check_version ("${version}" in_range HANDLE_VERSION_RANGE) if (NOT in_range) # interpreter has invalid version set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") return() endif() endif() else() get_filename_component (python_name "${_${_PYTHON_PREFIX}_EXECUTABLE}" NAME) if (NOT python_name STREQUAL "python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}${CMAKE_EXECUTABLE_SUFFIX}") # executable found do not have version in name # ensure major version is OK execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; sys.stdout.write(str(sys.version_info[0]))" RESULT_VARIABLE result OUTPUT_VARIABLE version ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (result OR NOT version EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) # interpreter not usable or has wrong major version if (result) set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Cannot use the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") else() set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong major version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") endif() set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") return() endif() endif() endif() if (CMAKE_SIZEOF_VOID_P AND ("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS OR "Development.SABIModule" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS OR "Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) AND NOT CMAKE_CROSSCOMPILING) # In this case, interpreter must have same architecture as environment execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys, struct; sys.stdout.write(str(struct.calcsize(\"P\")))" RESULT_VARIABLE result OUTPUT_VARIABLE size ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (result OR NOT size EQUAL CMAKE_SIZEOF_VOID_P) # interpreter not usable or has wrong architecture if (result) set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Cannot use the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") else() set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong architecture for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") endif() set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") return() endif() endif() endfunction() function(_python_validate_find_interpreter status interpreter) set(_${_PYTHON_PREFIX}_EXECUTABLE "${interpreter}" CACHE FILEPATH "" FORCE) _python_validate_interpreter (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) if (NOT _${_PYTHON_PREFIX}_EXECUTABLE) set (${status} FALSE PARENT_SCOPE) endif() endfunction() function (_PYTHON_VALIDATE_COMPILER) if (NOT _${_PYTHON_PREFIX}_COMPILER) return() endif() cmake_parse_arguments (PARSE_ARGV 0 _PVC "IN_RANGE;EXACT;CHECK_EXISTS" "VERSION" "") if (_PVC_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_COMPILER}") # Compiler does not exist anymore set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Cannot find the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"") set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND") return() endif() _python_get_launcher (launcher COMPILER) # retrieve python environment version from compiler set (working_dir "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/PythonCompilerVersion.dir") file (WRITE "${working_dir}/version.py" "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:3]]))\n") execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_COMPILER}" ${_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_ARCH_FLAGS} /target:exe /embed "${working_dir}/version.py" WORKING_DIRECTORY "${working_dir}" OUTPUT_QUIET ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) get_filename_component (ir_dir "${_${_PYTHON_PREFIX}_COMPILER}" DIRECTORY) execute_process (COMMAND "${CMAKE_COMMAND}" -E env "MONO_PATH=${ir_dir}" ${${_PYTHON_PREFIX}_DOTNET_LAUNCHER} "${working_dir}/version.exe" WORKING_DIRECTORY "${working_dir}" RESULT_VARIABLE result OUTPUT_VARIABLE version ERROR_QUIET) file (REMOVE_RECURSE "${working_dir}") if (result) # compiler is not usable set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Cannot use the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"") set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND") return() endif() if (_PVC_VERSION OR _PVC_IN_RANGE) if (_PVC_VERSION) # check against specified version ## compute number of components for version string (REGEX REPLACE "[^.]" "" dots "${_PVC_VERSION}") ## add one dot because there is one dot less than there are components string (LENGTH "${dots}." count) if (count GREATER 3) set (count 3) endif() set (version_regex "^[0-9]+") if (count EQUAL 3) string (APPEND version_regex "\\.[0-9]+\\.[0-9]+") elseif (count EQUAL 2) string (APPEND version_regex "\\.[0-9]+") endif() # extract needed range string (REGEX MATCH "${version_regex}" version "${version}") if (_PVC_EXACT AND NOT version VERSION_EQUAL _PVC_VERSION) # interpreter has wrong version set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Wrong version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"") set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND") return() else() # check that version is OK string(REGEX REPLACE "^([0-9]+)\\.?.*$" "\\1" major_version "${version}") string(REGEX REPLACE "^([0-9]+)\\.?.*$" "\\1" expected_major_version "${_PVC_VERSION}") if (NOT major_version VERSION_EQUAL expected_major_version OR NOT version VERSION_GREATER_EQUAL _PVC_VERSION) set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Wrong version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"") set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND") return() endif() endif() endif() if (_PVC_IN_RANGE) # check if version is in the requested range find_package_check_version ("${version}" in_range HANDLE_VERSION_RANGE) if (NOT in_range) # interpreter has invalid version set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Wrong version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"") set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND") return() endif() endif() else() string(REGEX REPLACE "^([0-9]+)\\.?.*$" "\\1" major_version "${version}") if (NOT major_version EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) # Compiler has wrong major version set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Wrong major version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"") set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND") return() endif() endif() endfunction() function(_python_validate_find_compiler status compiler) set(_${_PYTHON_PREFIX}_COMPILER "${compiler}" CACHE FILEPATH "" FORCE) _python_validate_compiler (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) if (NOT _${_PYTHON_PREFIX}_COMPILER) set (${status} FALSE PARENT_SCOPE) endif() endfunction() function (_PYTHON_VALIDATE_LIBRARY) if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) unset (_${_PYTHON_PREFIX}_LIBRARY_DEBUG) return() endif() cmake_parse_arguments (PARSE_ARGV 0 _PVL "IN_RANGE;EXACT;CHECK_EXISTS" "VERSION" "") if (_PVL_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}") # library does not exist anymore set_property (CACHE _${_PYTHON_PREFIX}_Development_LIBRARY_REASON_FAILURE PROPERTY VALUE "Cannot find the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"") set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND") if (WIN32) set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_DEBUG PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_DEBUG-NOTFOUND") endif() set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") return() endif() # retrieve version and abi from library name _python_get_version (LIBRARY PREFIX lib_) if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT lib_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) # incompatible ABI set_property (CACHE _${_PYTHON_PREFIX}_Development_LIBRARY_REASON_FAILURE PROPERTY VALUE "Wrong ABI for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"") set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND") else() if (_PVL_VERSION OR _PVL_IN_RANGE) if (_PVL_VERSION) # library have only major.minor information string (REGEX MATCH "[0-9](\\.[0-9]+)?" version "${_PVL_VERSION}") if ((_PVL_EXACT AND NOT lib_VERSION VERSION_EQUAL version) OR (lib_VERSION VERSION_LESS version)) # library has wrong version set_property (CACHE _${_PYTHON_PREFIX}_Development_LIBRARY_REASON_FAILURE PROPERTY VALUE "Wrong version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"") set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND") endif() endif() if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE AND _PVL_IN_RANGE) # check if library version is in the requested range find_package_check_version ("${lib_VERSION}" in_range HANDLE_VERSION_RANGE) if (NOT in_range) # library has wrong version set_property (CACHE _${_PYTHON_PREFIX}_Development_LIBRARY_REASON_FAILURE PROPERTY VALUE "Wrong version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"") set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND") endif() endif() else() if (NOT lib_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) # library has wrong major version set_property (CACHE _${_PYTHON_PREFIX}_Development_LIBRARY_REASON_FAILURE PROPERTY VALUE "Wrong major version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"") set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND") endif() endif() endif() if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) if (WIN32) set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_DEBUG PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_DEBUG-NOTFOUND") endif() unset (_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE CACHE) unset (_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG CACHE) set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") endif() endfunction() function (_PYTHON_VALIDATE_SABI_LIBRARY) if (NOT _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE) unset (_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG) return() endif() cmake_parse_arguments (PARSE_ARGV 0 _PVL "CHECK_EXISTS" "" "") if (_PVL_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}") # library does not exist anymore set_property (CACHE _${_PYTHON_PREFIX}_Development_SABI_LIBRARY_REASON_FAILURE PROPERTY VALUE "Cannot find the library \"${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}\"") set_property (CACHE _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE-NOTFOUND") if (WIN32) set_property (CACHE _${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG PROPERTY VALUE "${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG-NOTFOUND") endif() set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") return() endif() # retrieve version and abi from library name _python_get_version (SABI_LIBRARY PREFIX lib_) if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT lib_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) # incompatible ABI set_property (CACHE _${_PYTHON_PREFIX}_Development_SABI_LIBRARY_REASON_FAILURE PROPERTY VALUE "Wrong ABI for the library \"${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}\"") set_property (CACHE _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE-NOTFOUND") else() if (NOT lib_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) # library has wrong major version set_property (CACHE _${_PYTHON_PREFIX}_Development_SABI_LIBRARY_REASON_FAILURE PROPERTY VALUE "Wrong major version for the library \"${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}\"") set_property (CACHE _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE-NOTFOUND") endif() endif() if (NOT _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE) if (WIN32) set_property (CACHE _${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_DEBUG-NOTFOUND") endif() unset (_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE CACHE) unset (_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG CACHE) set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") endif() endfunction() function (_PYTHON_VALIDATE_INCLUDE_DIR) if (NOT _${_PYTHON_PREFIX}_INCLUDE_DIR) return() endif() cmake_parse_arguments (PARSE_ARGV 0 _PVID "IN_RANGE;EXACT;CHECK_EXISTS" "VERSION" "") if (_PVID_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}") # include file does not exist anymore set_property (CACHE _${_PYTHON_PREFIX}_Development_INCLUDE_DIR_REASON_FAILURE PROPERTY VALUE "Cannot find the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"") set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") return() endif() # retrieve version from header file _python_get_version (INCLUDE PREFIX inc_) if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT inc_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) # incompatible ABI set_property (CACHE _${_PYTHON_PREFIX}_Development_INCLUDE_DIR_REASON_FAILURE PROPERTY VALUE "Wrong ABI for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"") set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") else() if (_PVID_VERSION OR _PVID_IN_RANGE) if (_PVID_VERSION) if ((_PVID_EXACT AND NOT inc_VERSION VERSION_EQUAL expected_version) OR (inc_VERSION VERSION_LESS expected_version)) # include dir has wrong version set_property (CACHE _${_PYTHON_PREFIX}_Development_INCLUDE_DIR_REASON_FAILURE PROPERTY VALUE "Wrong version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"") set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") endif() endif() if (_${_PYTHON_PREFIX}_INCLUDE_DIR AND PVID_IN_RANGE) # check if include dir is in the request range find_package_check_version ("${inc_VERSION}" in_range HANDLE_VERSION_RANGE) if (NOT in_range) # include dir has wrong version set_property (CACHE _${_PYTHON_PREFIX}_Development_INCLUDE_DIR_REASON_FAILURE PROPERTY VALUE "Wrong version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"") set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") endif() endif() else() if (NOT inc_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) # include dir has wrong major version set_property (CACHE _${_PYTHON_PREFIX}_Development_INCLUDE_DIR_REASON_FAILURE PROPERTY VALUE "Wrong major version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"") set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") endif() endif() endif() endfunction() function (_PYTHON_FIND_RUNTIME_LIBRARY _PYTHON_LIB) string (REPLACE "_RUNTIME" "" _PYTHON_LIB "${_PYTHON_LIB}") # look at runtime part on systems supporting it if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR (CMAKE_SYSTEM_NAME MATCHES "MSYS|CYGWIN" AND ${_PYTHON_LIB} MATCHES "${CMAKE_IMPORT_LIBRARY_SUFFIX}$")) set (CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_SHARED_LIBRARY_SUFFIX}) # MSYS has a special syntax for runtime libraries if (CMAKE_SYSTEM_NAME MATCHES "MSYS") list (APPEND CMAKE_FIND_LIBRARY_PREFIXES "msys-") endif() find_library (${ARGV}) endif() endfunction() function (_PYTHON_SET_LIBRARY_DIRS _PYTHON_SLD_RESULT) unset (_PYTHON_DIRS) set (_PYTHON_LIBS ${ARGN}) foreach (_PYTHON_LIB IN LISTS _PYTHON_LIBS) if (${_PYTHON_LIB}) get_filename_component (_PYTHON_DIR "${${_PYTHON_LIB}}" DIRECTORY) list (APPEND _PYTHON_DIRS "${_PYTHON_DIR}") endif() endforeach() list (REMOVE_DUPLICATES _PYTHON_DIRS) set (${_PYTHON_SLD_RESULT} ${_PYTHON_DIRS} PARENT_SCOPE) endfunction() function (_PYTHON_SET_DEVELOPMENT_MODULE_FOUND module) if ("Development.${module}" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) if (module STREQUAL "SABIModule" AND "${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR}" VERSION_LESS "3.2") # Stable API was introduced in version 3.2 set (${_PYTHON_PREFIX}_Development.SABIModule_FOUND FALSE PARENT_SCOPE) _python_add_reason_failure ("Development" "SABIModule requires version 3.2 or upper.") return() endif() string(TOUPPER "${module}" id) set (module_found TRUE) if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS AND NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) set (module_found FALSE) endif() if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS AND NOT _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE) set (module_found FALSE) endif() if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS AND NOT _${_PYTHON_PREFIX}_INCLUDE_DIR) set (module_found FALSE) endif() set (${_PYTHON_PREFIX}_Development.${module}_FOUND ${module_found} PARENT_SCOPE) endif() endfunction() if (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) # range must include internal major version if (${_PYTHON_PREFIX}_FIND_VERSION_MIN_MAJOR VERSION_GREATER _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR OR ((${_PYTHON_PREFIX}_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND ${_PYTHON_PREFIX}_FIND_VERSION_MAX VERSION_LESS _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) OR (${_PYTHON_PREFIX}_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND ${_PYTHON_PREFIX}_FIND_VERSION_MAX VERSION_LESS_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR))) _python_display_failure ("Could NOT find ${_PYTHON_PREFIX}: Wrong version range specified is \"${${_PYTHON_PREFIX}_FIND_VERSION_RANGE}\", but expected version range must include major version \"${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}\"") cmake_policy(POP) return() endif() else() if (DEFINED ${_PYTHON_PREFIX}_FIND_VERSION_MAJOR AND NOT ${_PYTHON_PREFIX}_FIND_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) # If major version is specified, it must be the same as internal major version _python_display_failure ("Could NOT find ${_PYTHON_PREFIX}: Wrong major version specified is \"${${_PYTHON_PREFIX}_FIND_VERSION_MAJOR}\", but expected major version is \"${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}\"") cmake_policy(POP) return() endif() endif() # handle components if (NOT ${_PYTHON_PREFIX}_FIND_COMPONENTS) set (${_PYTHON_PREFIX}_FIND_COMPONENTS Interpreter) set (${_PYTHON_PREFIX}_FIND_REQUIRED_Interpreter TRUE) endif() if ("NumPy" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) list (APPEND ${_PYTHON_PREFIX}_FIND_COMPONENTS "Interpreter" "Development.Module") endif() if ("Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) list (APPEND ${_PYTHON_PREFIX}_FIND_COMPONENTS "Development.Module" "Development.Embed") endif() list (REMOVE_DUPLICATES ${_PYTHON_PREFIX}_FIND_COMPONENTS) foreach (_${_PYTHON_PREFIX}_COMPONENT IN ITEMS Interpreter Compiler Development Development.Module Development.SABIModule Development.Embed NumPy) set (${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_COMPONENT}_FOUND FALSE) endforeach() if (${_PYTHON_PREFIX}_FIND_REQUIRED_Development) set (${_PYTHON_PREFIX}_FIND_REQUIRED_Development.Module TRUE) set (${_PYTHON_PREFIX}_FIND_REQUIRED_Development.Embed TRUE) endif() unset (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) unset (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS) unset (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_SABIMODULE_ARTIFACTS) unset (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_EMBED_ARTIFACTS) if ("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) if (CMAKE_SYSTEM_NAME MATCHES "^(Windows.*|CYGWIN|MSYS)$") list (APPEND _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS "LIBRARY") endif() list (APPEND _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS "INCLUDE_DIR") endif() if ("Development.SABIModule" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) if (CMAKE_SYSTEM_NAME MATCHES "^(Windows.*|CYGWIN|MSYS)$") list (APPEND _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_SABIMODULE_ARTIFACTS "SABI_LIBRARY") endif() list (APPEND _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_SABIMODULE_ARTIFACTS "INCLUDE_DIR") endif() if ("Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) list (APPEND _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_EMBED_ARTIFACTS "LIBRARY" "INCLUDE_DIR") endif() set (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS ${_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS} ${_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_SABIMODULE_ARTIFACTS} ${_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_EMBED_ARTIFACTS}) list (REMOVE_DUPLICATES _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) # Set versions to search ## default: search any version set (_${_PYTHON_PREFIX}_FIND_VERSIONS ${_${_PYTHON_PREFIX}_VERSIONS}) unset (_${_PYTHON_PREFIX}_FIND_VERSION_EXACT) if (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) unset (_${_PYTHON_PREFIX}_FIND_VERSIONS) foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_VERSIONS) if ((${_PYTHON_PREFIX}_FIND_VERSION_RANGE_MIN STREQUAL "INCLUDE" AND _${_PYTHON_PREFIX}_VERSION VERSION_GREATER_EQUAL ${_PYTHON_PREFIX}_FIND_VERSION_MIN) AND ((${_PYTHON_PREFIX}_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND _${_PYTHON_PREFIX}_VERSION VERSION_LESS_EQUAL ${_PYTHON_PREFIX}_FIND_VERSION_MAX) OR (${_PYTHON_PREFIX}_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND _${_PYTHON_PREFIX}_VERSION VERSION_LESS ${_PYTHON_PREFIX}_FIND_VERSION_MAX))) list (APPEND _${_PYTHON_PREFIX}_FIND_VERSIONS ${_${_PYTHON_PREFIX}_VERSION}) endif() endforeach() else() if (${_PYTHON_PREFIX}_FIND_VERSION_COUNT GREATER 1) if (${_PYTHON_PREFIX}_FIND_VERSION_EXACT) set (_${_PYTHON_PREFIX}_FIND_VERSION_EXACT "EXACT") set (_${_PYTHON_PREFIX}_FIND_VERSIONS ${${_PYTHON_PREFIX}_FIND_VERSION_MAJOR}.${${_PYTHON_PREFIX}_FIND_VERSION_MINOR}) else() unset (_${_PYTHON_PREFIX}_FIND_VERSIONS) # add all compatible versions foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_VERSIONS) if (_${_PYTHON_PREFIX}_VERSION VERSION_GREATER_EQUAL "${${_PYTHON_PREFIX}_FIND_VERSION_MAJOR}.${${_PYTHON_PREFIX}_FIND_VERSION_MINOR}") list (APPEND _${_PYTHON_PREFIX}_FIND_VERSIONS ${_${_PYTHON_PREFIX}_VERSION}) endif() endforeach() endif() endif() endif() # Set ABIs to search ## default: search any ABI if (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR VERSION_LESS "3") # ABI not supported unset (_${_PYTHON_PREFIX}_FIND_ABI) set (_${_PYTHON_PREFIX}_ABIFLAGS "") else() unset (_${_PYTHON_PREFIX}_FIND_ABI) unset (_${_PYTHON_PREFIX}_ABIFLAGS) if (DEFINED ${_PYTHON_PREFIX}_FIND_ABI) # normalization string (TOUPPER "${${_PYTHON_PREFIX}_FIND_ABI}" _${_PYTHON_PREFIX}_FIND_ABI) list (TRANSFORM _${_PYTHON_PREFIX}_FIND_ABI REPLACE "^(TRUE|Y(ES)?|1)$" "ON") list (TRANSFORM _${_PYTHON_PREFIX}_FIND_ABI REPLACE "^(FALSE|N(O)?|0)$" "OFF") if (NOT _${_PYTHON_PREFIX}_FIND_ABI MATCHES "^(ON|OFF|ANY);(ON|OFF|ANY);(ON|OFF|ANY)$") message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${${_PYTHON_PREFIX}_FIND_ABI}: invalid value for '${_PYTHON_PREFIX}_FIND_ABI'. Ignore it") unset (_${_PYTHON_PREFIX}_FIND_ABI) endif() _python_get_abiflags (_${_PYTHON_PREFIX}_ABIFLAGS) endif() endif() unset (${_PYTHON_PREFIX}_SOABI) unset (${_PYTHON_PREFIX}_SOSABI) # Define lookup strategy cmake_policy (GET CMP0094 _${_PYTHON_PREFIX}_LOOKUP_POLICY) if (_${_PYTHON_PREFIX}_LOOKUP_POLICY STREQUAL "NEW") set (_${_PYTHON_PREFIX}_FIND_STRATEGY "LOCATION") else() set (_${_PYTHON_PREFIX}_FIND_STRATEGY "VERSION") endif() if (DEFINED ${_PYTHON_PREFIX}_FIND_STRATEGY) if (NOT ${_PYTHON_PREFIX}_FIND_STRATEGY MATCHES "^(VERSION|LOCATION)$") message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${${_PYTHON_PREFIX}_FIND_STRATEGY}: invalid value for '${_PYTHON_PREFIX}_FIND_STRATEGY'. 'VERSION' or 'LOCATION' expected.") set (_${_PYTHON_PREFIX}_FIND_STRATEGY "VERSION") else() set (_${_PYTHON_PREFIX}_FIND_STRATEGY "${${_PYTHON_PREFIX}_FIND_STRATEGY}") endif() endif() # Python and Anaconda distributions: define which architectures can be used unset (_${_PYTHON_PREFIX}_REGISTRY_VIEW) if (CMAKE_SIZEOF_VOID_P) math (EXPR _${_PYTHON_PREFIX}_ARCH "${CMAKE_SIZEOF_VOID_P} * 8") if ("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS OR "Development.SABIModule" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS OR "Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) # In this case, search only for 64bit or 32bit set (_${_PYTHON_PREFIX}_ARCH2 ${_${_PYTHON_PREFIX}_ARCH}) # XXX(cmake-3.25) if (CMAKE_VERSION VERSION_LESS 3.25) set (_${_PYTHON_PREFIX}_REGISTRY_VIEW "") else() set (_${_PYTHON_PREFIX}_REGISTRY_VIEW REGISTRY_VIEW ${_${_PYTHON_PREFIX}_ARCH}) endif() else() if (_${_PYTHON_PREFIX}_ARCH EQUAL "32") set (_${_PYTHON_PREFIX}_ARCH2 64) else() set (_${_PYTHON_PREFIX}_ARCH2 32) endif() endif() else() # architecture unknown, search for both 64bit and 32bit set (_${_PYTHON_PREFIX}_ARCH 64) set (_${_PYTHON_PREFIX}_ARCH2 32) endif() # IronPython support unset (_${_PYTHON_PREFIX}_IRON_PYTHON_INTERPRETER_NAMES) unset (_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_NAMES) unset (_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_ARCH_FLAGS) if (CMAKE_SIZEOF_VOID_P) if (_${_PYTHON_PREFIX}_ARCH EQUAL "32") set (_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_ARCH_FLAGS "/platform:x86") else() set (_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_ARCH_FLAGS "/platform:x64") endif() endif() if (NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") # Do not use wrapper script on Linux because it is buggy: -c interpreter option cannot be used list (APPEND _${_PYTHON_PREFIX}_IRON_PYTHON_INTERPRETER_NAMES "ipy${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}" "ipy64" "ipy32" "ipy") list (APPEND _${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_NAMES "ipyc") endif() list (APPEND _${_PYTHON_PREFIX}_IRON_PYTHON_INTERPRETER_NAMES "ipy.exe") list (APPEND _${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_NAMES "ipyc.exe") set (_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES net45 net40 bin) # PyPy support if (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR EQUAL "3") set (_${_PYTHON_PREFIX}_PYPY_NAMES pypy3) set (_${_PYTHON_PREFIX}_PYPY_LIB_NAMES pypy3-c) if (WIN32) # special name for runtime part list (APPEND _${_PYTHON_PREFIX}_PYPY_LIB_NAMES libpypy3-c) endif() set (_${_PYTHON_PREFIX}_PYPY_INCLUDE_PATH_SUFFIXES lib/pypy3/include pypy3/include) else() set (_${_PYTHON_PREFIX}_PYPY_NAMES pypy) set (_${_PYTHON_PREFIX}_PYPY_LIB_NAMES pypy-c) if (WIN32) # special name for runtime part list (APPEND _${_PYTHON_PREFIX}_PYPY_LIB_NAMES libpypy-c) endif() set (_${_PYTHON_PREFIX}_PYPY_INCLUDE_PATH_SUFFIXES lib/pypy/include pypy/include) endif() list (APPEND _${_PYTHON_PREFIX}_PYPY_INCLUDE_PATH_SUFFIXES libexec/include) set (_${_PYTHON_PREFIX}_PYPY_EXECUTABLE_PATH_SUFFIXES bin) set (_${_PYTHON_PREFIX}_PYPY_LIBRARY_PATH_SUFFIXES lib libs bin) list (APPEND _${_PYTHON_PREFIX}_PYPY_INCLUDE_PATH_SUFFIXES include) # Python Implementations handling unset (_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS) if (DEFINED ${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS) foreach (_${_PYTHON_PREFIX}_IMPLEMENTATION IN LISTS ${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS) if (NOT _${_PYTHON_PREFIX}_IMPLEMENTATION MATCHES "^(CPython|IronPython|PyPy)$") message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${_${_PYTHON_PREFIX}_IMPLEMENTATION}: invalid value for '${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS'. 'CPython', 'IronPython' or 'PyPy' expected. Value will be ignored.") else() list (APPEND _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS ${_${_PYTHON_PREFIX}_IMPLEMENTATION}) endif() endforeach() else() if (WIN32) set (_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS CPython IronPython) else() set (_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS CPython) endif() endif() # compute list of names for header file unset (_${_PYTHON_PREFIX}_INCLUDE_NAMES) foreach (_${_PYTHON_PREFIX}_IMPLEMENTATION IN LISTS _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS) if (_${_PYTHON_PREFIX}_IMPLEMENTATION STREQUAL "CPython") list (APPEND _${_PYTHON_PREFIX}_INCLUDE_NAMES "Python.h") elseif (_${_PYTHON_PREFIX}_IMPLEMENTATION STREQUAL "PyPy") list (APPEND _${_PYTHON_PREFIX}_INCLUDE_NAMES "PyPy.h" "pypy_decl.h") endif() endforeach() # Apple frameworks handling _python_find_frameworks () set (_${_PYTHON_PREFIX}_FIND_FRAMEWORK "FIRST") if (DEFINED ${_PYTHON_PREFIX}_FIND_FRAMEWORK) if (NOT ${_PYTHON_PREFIX}_FIND_FRAMEWORK MATCHES "^(FIRST|LAST|NEVER)$") message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${${_PYTHON_PREFIX}_FIND_FRAMEWORK}: invalid value for '${_PYTHON_PREFIX}_FIND_FRAMEWORK'. 'FIRST', 'LAST' or 'NEVER' expected. 'FIRST' will be used instead.") else() set (_${_PYTHON_PREFIX}_FIND_FRAMEWORK ${${_PYTHON_PREFIX}_FIND_FRAMEWORK}) endif() elseif (DEFINED CMAKE_FIND_FRAMEWORK) if (CMAKE_FIND_FRAMEWORK STREQUAL "ONLY") message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: CMAKE_FIND_FRAMEWORK: 'ONLY' value is not supported. 'FIRST' will be used instead.") elseif (NOT CMAKE_FIND_FRAMEWORK MATCHES "^(FIRST|LAST|NEVER)$") message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${CMAKE_FIND_FRAMEWORK}: invalid value for 'CMAKE_FIND_FRAMEWORK'. 'FIRST', 'LAST' or 'NEVER' expected. 'FIRST' will be used instead.") else() set (_${_PYTHON_PREFIX}_FIND_FRAMEWORK ${CMAKE_FIND_FRAMEWORK}) endif() endif() # Save CMAKE_FIND_APPBUNDLE if (DEFINED CMAKE_FIND_APPBUNDLE) set (_${_PYTHON_PREFIX}_CMAKE_FIND_APPBUNDLE ${CMAKE_FIND_APPBUNDLE}) else() unset (_${_PYTHON_PREFIX}_CMAKE_FIND_APPBUNDLE) endif() # To avoid app bundle lookup set (CMAKE_FIND_APPBUNDLE "NEVER") # Save CMAKE_FIND_FRAMEWORK if (DEFINED CMAKE_FIND_FRAMEWORK) set (_${_PYTHON_PREFIX}_CMAKE_FIND_FRAMEWORK ${CMAKE_FIND_FRAMEWORK}) else() unset (_${_PYTHON_PREFIX}_CMAKE_FIND_FRAMEWORK) endif() # To avoid framework lookup set (CMAKE_FIND_FRAMEWORK "NEVER") # Windows Registry handling if (DEFINED ${_PYTHON_PREFIX}_FIND_REGISTRY) if (NOT ${_PYTHON_PREFIX}_FIND_REGISTRY MATCHES "^(FIRST|LAST|NEVER)$") message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${${_PYTHON_PREFIX}_FIND_REGISTRY}: invalid value for '${_PYTHON_PREFIX}_FIND_REGISTRY'. 'FIRST', 'LAST' or 'NEVER' expected. 'FIRST' will be used instead.") set (_${_PYTHON_PREFIX}_FIND_REGISTRY "FIRST") else() set (_${_PYTHON_PREFIX}_FIND_REGISTRY ${${_PYTHON_PREFIX}_FIND_REGISTRY}) endif() else() set (_${_PYTHON_PREFIX}_FIND_REGISTRY "FIRST") endif() # virtual environments recognition if (DEFINED ENV{VIRTUAL_ENV} OR DEFINED ENV{CONDA_PREFIX}) if (DEFINED ${_PYTHON_PREFIX}_FIND_VIRTUALENV) if (NOT ${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY|STANDARD)$") message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${${_PYTHON_PREFIX}_FIND_VIRTUALENV}: invalid value for '${_PYTHON_PREFIX}_FIND_VIRTUALENV'. 'FIRST', 'ONLY' or 'STANDARD' expected. 'FIRST' will be used instead.") set (_${_PYTHON_PREFIX}_FIND_VIRTUALENV "FIRST") else() set (_${_PYTHON_PREFIX}_FIND_VIRTUALENV ${${_PYTHON_PREFIX}_FIND_VIRTUALENV}) endif() else() set (_${_PYTHON_PREFIX}_FIND_VIRTUALENV FIRST) endif() else() set (_${_PYTHON_PREFIX}_FIND_VIRTUALENV STANDARD) endif() # Python naming handling if (DEFINED ${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES) if (NOT ${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES MATCHES "^(FIRST|LAST|NEVER)$") message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${_${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES}: invalid value for '${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES'. 'FIRST', 'LAST' or 'NEVER' expected. 'LAST' will be used instead.") set (_${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES LAST) else() set (_${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES ${${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES}) endif() else() set (_${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES LAST) endif() # Compute search signature # This signature will be used to check validity of cached variables on new search set (_${_PYTHON_PREFIX}_SIGNATURE "${${_PYTHON_PREFIX}_ROOT_DIR}:${_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS}:${_${_PYTHON_PREFIX}_FIND_STRATEGY}:${${_PYTHON_PREFIX}_FIND_VIRTUALENV}${_${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES}") if (NOT WIN32) string (APPEND _${_PYTHON_PREFIX}_SIGNATURE ":${${_PYTHON_PREFIX}_USE_STATIC_LIBS}:") endif() if (CMAKE_HOST_APPLE) string (APPEND _${_PYTHON_PREFIX}_SIGNATURE ":${_${_PYTHON_PREFIX}_FIND_FRAMEWORK}") endif() if (CMAKE_HOST_WIN32) string (APPEND _${_PYTHON_PREFIX}_SIGNATURE ":${_${_PYTHON_PREFIX}_FIND_REGISTRY}") endif() function (_PYTHON_CHECK_DEVELOPMENT_SIGNATURE module) if ("Development.${module}" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) string (TOUPPER "${module}" id) set (signature "${_${_PYTHON_PREFIX}_SIGNATURE}:") if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) list (APPEND signature "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}:") endif() if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) list (APPEND signature "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}:") endif() if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) list (APPEND signature "${_${_PYTHON_PREFIX}_INCLUDE_DIR}:") endif() string (MD5 signature "${signature}") if (signature STREQUAL _${_PYTHON_PREFIX}_DEVELOPMENT_${id}_SIGNATURE) if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) if (${_PYTHON_PREFIX}_FIND_VERSION_EXACT) _python_validate_library (VERSION ${${_PYTHON_PREFIX}_FIND_VERSION} EXACT CHECK_EXISTS) elseif (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) _python_validate_library (IN_RANGE CHECK_EXISTS) elseif (DEFINED ${_PYTHON_PREFIX}_FIND_VERSION) _python_validate_library (VERSION ${${_PYTHON_PREFIX}_FIND_VERSION} CHECK_EXISTS) else() _python_validate_library (CHECK_EXISTS) endif() endif() if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) _python_validate_sabi_library (CHECK_EXISTS) endif() if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) if (${_PYTHON_PREFIX}_FIND_VERSION_EXACT) _python_validate_include_dir (VERSION ${${_PYTHON_PREFIX}_FIND_VERSION} EXACT CHECK_EXISTS) elseif (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) _python_validate_include_dir (IN_RANGE CHECK_EXISTS) elseif (${_PYTHON_PREFIX}_FIND_VERSION) _python_validate_include_dir (VERSION ${${_PYTHON_PREFIX}_FIND_VERSION} CHECK_EXISTS) else() _python_validate_include_dir (CHECK_EXISTS) endif() endif() else() if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) unset (_${_PYTHON_PREFIX}_LIBRARY_RELEASE CACHE) unset (_${_PYTHON_PREFIX}_LIBRARY_DEBUG CACHE) endif() if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) unset (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE CACHE) unset (_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG CACHE) endif() if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) unset (_${_PYTHON_PREFIX}_INCLUDE_DIR CACHE) endif() endif() if (("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS AND NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) OR ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS AND NOT _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE) OR ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS AND NOT _${_PYTHON_PREFIX}_INCLUDE_DIR)) unset (_${_PYTHON_PREFIX}_CONFIG CACHE) unset (_${_PYTHON_PREFIX}_DEVELOPMENT_${id}_SIGNATURE CACHE) endif() endif() endfunction() function (_PYTHON_COMPUTE_DEVELOPMENT_SIGNATURE module) string (TOUPPER "${module}" id) if (${_PYTHON_PREFIX}_Development.${module}_FOUND) set (signature "${_${_PYTHON_PREFIX}_SIGNATURE}:") if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) list (APPEND signature "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}:") endif() if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) list (APPEND signature "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}:") endif() if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) list (APPEND signature "${_${_PYTHON_PREFIX}_INCLUDE_DIR}:") endif() string (MD5 signature "${signature}") set (_${_PYTHON_PREFIX}_DEVELOPMENT_${id}_SIGNATURE "${signature}" CACHE INTERNAL "") else() unset (_${_PYTHON_PREFIX}_DEVELOPMENT_${id}_SIGNATURE CACHE) endif() endfunction() unset (_${_PYTHON_PREFIX}_REQUIRED_VARS) unset (_${_PYTHON_PREFIX}_CACHED_VARS) unset (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE) set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE CACHE INTERNAL "Interpreter reason failure") unset (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE) set (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE CACHE INTERNAL "Compiler reason failure") foreach (artifact IN LISTS _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) unset (_${_PYTHON_PREFIX}_Development_${artifact}_REASON_FAILURE) set (_${_PYTHON_PREFIX}_Development_${artifact}_REASON_FAILURE CACHE INTERNAL "Development ${artifact} reason failure") endforeach() unset (_${_PYTHON_PREFIX}_Development_REASON_FAILURE) set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE CACHE INTERNAL "Development reason failure") unset (_${_PYTHON_PREFIX}_NumPy_REASON_FAILURE) set (_${_PYTHON_PREFIX}_NumPy_REASON_FAILURE CACHE INTERNAL "NumPy reason failure") # preamble ## For IronPython on platforms other than Windows, search for the .Net interpreter if ("IronPython" IN_LIST _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS AND NOT WIN32) find_program (${_PYTHON_PREFIX}_DOTNET_LAUNCHER NAMES "mono") endif() # first step, search for the interpreter if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS _${_PYTHON_PREFIX}_EXECUTABLE _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES) if (${_PYTHON_PREFIX}_FIND_REQUIRED_Interpreter) list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_EXECUTABLE) endif() if (DEFINED ${_PYTHON_PREFIX}_EXECUTABLE AND IS_ABSOLUTE "${${_PYTHON_PREFIX}_EXECUTABLE}") if (NOT ${_PYTHON_PREFIX}_EXECUTABLE STREQUAL _${_PYTHON_PREFIX}_EXECUTABLE) # invalidate cache properties unset (_${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES CACHE) endif() set (_${_PYTHON_PREFIX}_EXECUTABLE "${${_PYTHON_PREFIX}_EXECUTABLE}" CACHE INTERNAL "") elseif (DEFINED _${_PYTHON_PREFIX}_EXECUTABLE) # compute interpreter signature and check validity of definition string (MD5 __${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE "${_${_PYTHON_PREFIX}_SIGNATURE}:${_${_PYTHON_PREFIX}_EXECUTABLE}") if (__${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE STREQUAL _${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE) # check version validity if (${_PYTHON_PREFIX}_FIND_VERSION_EXACT) _python_validate_interpreter (VERSION ${${_PYTHON_PREFIX}_FIND_VERSION} EXACT CHECK_EXISTS) elseif (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) _python_validate_interpreter (IN_RANGE CHECK_EXISTS) elseif (DEFINED ${_PYTHON_PREFIX}_FIND_VERSION) _python_validate_interpreter (VERSION ${${_PYTHON_PREFIX}_FIND_VERSION} CHECK_EXISTS) else() _python_validate_interpreter (CHECK_EXISTS) endif() else() unset (_${_PYTHON_PREFIX}_EXECUTABLE CACHE) endif() if (NOT _${_PYTHON_PREFIX}_EXECUTABLE) unset (_${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE CACHE) unset (_${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES CACHE) endif() endif() if (NOT _${_PYTHON_PREFIX}_EXECUTABLE) set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION") # build all executable names _python_get_names (_${_PYTHON_PREFIX}_NAMES VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} POSIX INTERPRETER) _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} INTERPRETER) # Framework Paths _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS}) # Registry Paths _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS}) set (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS ${_${_PYTHON_PREFIX}_FIND_VERSION_EXACT}) if (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) list (APPEND _${_PYTHON_PREFIX}_VALIDATE_OPTIONS IN_RANGE) elseif (DEFINED ${_PYTHON_PREFIX}_FIND_VERSION) list (APPEND _${_PYTHON_PREFIX}_VALIDATE_OPTIONS VERSION ${${_PYTHON_PREFIX}_FIND_VERSION}) endif() # XXX(cmake-3.25) VALIDATOR if (CMAKE_VERSION VERSION_LESS 3.25) set (_SKBUILD_PYTHONO_VALIDATOR "") else () set (_SKBUILD_PYTHON_VALIDATOR "VALIDATOR _python_validate_find_interpreter") endif () while (TRUE) # Virtual environments handling if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$") find_program (_${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ENV VIRTUAL_ENV ENV CONDA_PREFIX PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH ${_SKBUILD_PYTHON_VALIDATOR}) if (_${_PYTHON_PREFIX}_EXECUTABLE) break() endif() if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV STREQUAL "ONLY") break() endif() endif() # Apple frameworks handling if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") find_program (_${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH ${_SKBUILD_PYTHON_VALIDATOR}) if (_${_PYTHON_PREFIX}_EXECUTABLE) break() endif() endif() # Windows registry if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") find_program (_${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} ${_${_PYTHON_PREFIX}_REGISTRY_VIEW} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH ${_SKBUILD_PYTHON_VALIDATOR}) if (_${_PYTHON_PREFIX}_EXECUTABLE) break() endif() endif() # try using HINTS find_program (_${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH ${_SKBUILD_PYTHON_VALIDATOR}) if (_${_PYTHON_PREFIX}_EXECUTABLE) break() endif() # try using standard paths find_program (_${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} NAMES_PER_DIR PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} ${_SKBUILD_PYTHON_VALIDATOR}) if (_${_PYTHON_PREFIX}_EXECUTABLE) break() endif() # Apple frameworks handling if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") find_program (_${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} NAMES_PER_DIR PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_DEFAULT_PATH ${_SKBUILD_PYTHON_VALIDATOR}) if (_${_PYTHON_PREFIX}_EXECUTABLE) break() endif() endif() # Windows registry if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") find_program (_${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} NAMES_PER_DIR PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} ${_${_PYTHON_PREFIX}_REGISTRY_VIEW} NO_DEFAULT_PATH ${_SKBUILD_PYTHON_VALIDATOR}) if (_${_PYTHON_PREFIX}_EXECUTABLE) break() endif() endif() break() endwhile() else() # look-up for various versions and locations set (_${_PYTHON_PREFIX}_COMMON_VALIDATE_OPTIONS EXACT) if (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) list (APPEND _${_PYTHON_PREFIX}_COMMON_VALIDATE_OPTIONS IN_RANGE) endif() foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) _python_get_names (_${_PYTHON_PREFIX}_NAMES VERSION ${_${_PYTHON_PREFIX}_VERSION} POSIX INTERPRETER) _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_VERSION} INTERPRETER) _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_VERSION}) _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS VERSION ${_${_PYTHON_PREFIX}_VERSION}) set (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_COMMON_VALIDATE_OPTIONS}) # Virtual environments handling if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$") find_program (_${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ENV VIRTUAL_ENV ENV CONDA_PREFIX PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH ${_SKBUILD_PYTHON_VALIDATOR}) if (_${_PYTHON_PREFIX}_EXECUTABLE) break() endif() if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV STREQUAL "ONLY") continue() endif() endif() # Apple frameworks handling if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") find_program (_${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH ${_SKBUILD_PYTHON_VALIDATOR}) endif() # Windows registry if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") find_program (_${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} ${_${_PYTHON_PREFIX}_REGISTRY_VIEW} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH ${_SKBUILD_PYTHON_VALIDATOR}) endif() if (_${_PYTHON_PREFIX}_EXECUTABLE) break() endif() # try using HINTS find_program (_${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH ${_SKBUILD_PYTHON_VALIDATOR}) if (_${_PYTHON_PREFIX}_EXECUTABLE) break() endif() # try using standard paths. find_program (_${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} NAMES_PER_DIR PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} ${_SKBUILD_PYTHON_VALIDATOR}) if (_${_PYTHON_PREFIX}_EXECUTABLE) break() endif() # Apple frameworks handling if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") find_program (_${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} NAMES_PER_DIR PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_DEFAULT_PATH ${_SKBUILD_PYTHON_VALIDATOR}) endif() # Windows registry if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") find_program (_${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} NAMES_PER_DIR PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} ${_${_PYTHON_PREFIX}_REGISTRY_VIEW} NO_DEFAULT_PATH ${_SKBUILD_PYTHON_VALIDATOR}) endif() if (_${_PYTHON_PREFIX}_EXECUTABLE) break() endif() endforeach() if (NOT _${_PYTHON_PREFIX}_EXECUTABLE AND NOT _${_PYTHON_PREFIX}_FIND_VIRTUALENV STREQUAL "ONLY") # No specific version found. Retry with generic names and standard paths. _python_get_names (_${_PYTHON_PREFIX}_NAMES POSIX INTERPRETER) unset (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS) find_program (_${_PYTHON_PREFIX}_EXECUTABLE NAMES ${_${_PYTHON_PREFIX}_NAMES} NAMES_PER_DIR ${_SKBUILD_PYTHON_VALIDATOR}) endif() endif() endif() set (${_PYTHON_PREFIX}_EXECUTABLE "${_${_PYTHON_PREFIX}_EXECUTABLE}") _python_get_launcher (_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER INTERPRETER) # retrieve exact version of executable found if (_${_PYTHON_PREFIX}_EXECUTABLE) execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:3]]))" RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE ${_PYTHON_PREFIX}_VERSION ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (NOT _${_PYTHON_PREFIX}_RESULT) set (_${_PYTHON_PREFIX}_EXECUTABLE_USABLE TRUE) else() # Interpreter is not usable set (_${_PYTHON_PREFIX}_EXECUTABLE_USABLE FALSE) unset (${_PYTHON_PREFIX}_VERSION) set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Cannot run the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") endif() endif() if (_${_PYTHON_PREFIX}_EXECUTABLE AND _${_PYTHON_PREFIX}_EXECUTABLE_USABLE) list (LENGTH _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES _properties_length) if (NOT _properties_length EQUAL "12") # cache variable comes from some older Python module version: not usable unset (_${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES CACHE) endif() unset (_properties_length) if (_${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES) set (${_PYTHON_PREFIX}_Interpreter_FOUND TRUE) list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 0 ${_PYTHON_PREFIX}_INTERPRETER_ID) list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 1 ${_PYTHON_PREFIX}_VERSION_MAJOR) list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 2 ${_PYTHON_PREFIX}_VERSION_MINOR) list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 3 ${_PYTHON_PREFIX}_VERSION_PATCH) list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 4 _${_PYTHON_PREFIX}_ARCH) set (_${_PYTHON_PREFIX}_ARCH2 ${_${_PYTHON_PREFIX}_ARCH}) list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 5 _${_PYTHON_PREFIX}_ABIFLAGS) list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 6 ${_PYTHON_PREFIX}_SOABI) list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 7 ${_PYTHON_PREFIX}_SOSABI) list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 8 ${_PYTHON_PREFIX}_STDLIB) list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 9 ${_PYTHON_PREFIX}_STDARCH) list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 10 ${_PYTHON_PREFIX}_SITELIB) list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 11 ${_PYTHON_PREFIX}_SITEARCH) else() string (REGEX MATCHALL "[0-9]+" _${_PYTHON_PREFIX}_VERSIONS "${${_PYTHON_PREFIX}_VERSION}") list (GET _${_PYTHON_PREFIX}_VERSIONS 0 ${_PYTHON_PREFIX}_VERSION_MAJOR) list (GET _${_PYTHON_PREFIX}_VERSIONS 1 ${_PYTHON_PREFIX}_VERSION_MINOR) list (GET _${_PYTHON_PREFIX}_VERSIONS 2 ${_PYTHON_PREFIX}_VERSION_PATCH) if (${_PYTHON_PREFIX}_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) set (${_PYTHON_PREFIX}_Interpreter_FOUND TRUE) # Use interpreter version and ABI for future searches to ensure consistency set (_${_PYTHON_PREFIX}_FIND_VERSIONS ${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}) execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETR_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; sys.stdout.write(sys.abiflags)" RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE _${_PYTHON_PREFIX}_ABIFLAGS ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (_${_PYTHON_PREFIX}_RESULT) # assunme ABI is not supported set (_${_PYTHON_PREFIX}_ABIFLAGS "") endif() endif() if (${_PYTHON_PREFIX}_Interpreter_FOUND) unset (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE CACHE) # compute and save interpreter signature string (MD5 __${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE "${_${_PYTHON_PREFIX}_SIGNATURE}:${_${_PYTHON_PREFIX}_EXECUTABLE}") set (_${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE "${__${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE}" CACHE INTERNAL "") if (NOT CMAKE_SIZEOF_VOID_P) # determine interpreter architecture execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; sys.stdout.write(str(sys.maxsize > 2**32))" RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE ${_PYTHON_PREFIX}_IS64BIT ERROR_VARIABLE ${_PYTHON_PREFIX}_IS64BIT) if (NOT _${_PYTHON_PREFIX}_RESULT) if (${_PYTHON_PREFIX}_IS64BIT) set (_${_PYTHON_PREFIX}_ARCH 64) set (_${_PYTHON_PREFIX}_ARCH2 64) else() set (_${_PYTHON_PREFIX}_ARCH 32) set (_${_PYTHON_PREFIX}_ARCH2 32) endif() endif() endif() # retrieve interpreter identity execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -V RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE ${_PYTHON_PREFIX}_INTERPRETER_ID ERROR_VARIABLE ${_PYTHON_PREFIX}_INTERPRETER_ID) if (NOT _${_PYTHON_PREFIX}_RESULT) if (${_PYTHON_PREFIX}_INTERPRETER_ID MATCHES "Anaconda") set (${_PYTHON_PREFIX}_INTERPRETER_ID "Anaconda") elseif (${_PYTHON_PREFIX}_INTERPRETER_ID MATCHES "Enthought") set (${_PYTHON_PREFIX}_INTERPRETER_ID "Canopy") elseif (${_PYTHON_PREFIX}_INTERPRETER_ID MATCHES "PyPy ([0-9.]+)") set (${_PYTHON_PREFIX}_INTERPRETER_ID "PyPy") set (${_PYTHON_PREFIX}_PyPy_VERSION "${CMAKE_MATCH_1}") else() string (REGEX REPLACE "^([^ ]+).*" "\\1" ${_PYTHON_PREFIX}_INTERPRETER_ID "${${_PYTHON_PREFIX}_INTERPRETER_ID}") if (${_PYTHON_PREFIX}_INTERPRETER_ID STREQUAL "Python") # try to get a more precise ID execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; sys.stdout.write(sys.copyright)" RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE ${_PYTHON_PREFIX}_COPYRIGHT ERROR_QUIET) if (${_PYTHON_PREFIX}_COPYRIGHT MATCHES "ActiveState") set (${_PYTHON_PREFIX}_INTERPRETER_ID "ActivePython") endif() endif() endif() else() set (${_PYTHON_PREFIX}_INTERPRETER_ID Python) endif() # retrieve various package installation directories execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys\ntry:\n from distutils import sysconfig\n sys.stdout.write(';'.join([sysconfig.get_python_lib(plat_specific=False,standard_lib=True),sysconfig.get_python_lib(plat_specific=True,standard_lib=True),sysconfig.get_python_lib(plat_specific=False,standard_lib=False),sysconfig.get_python_lib(plat_specific=True,standard_lib=False)]))\nexcept Exception:\n import sysconfig\n sys.stdout.write(';'.join([sysconfig.get_path('stdlib'),sysconfig.get_path('platstdlib'),sysconfig.get_path('purelib'),sysconfig.get_path('platlib')]))" RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE _${_PYTHON_PREFIX}_LIBPATHS ERROR_QUIET) if (NOT _${_PYTHON_PREFIX}_RESULT) list (GET _${_PYTHON_PREFIX}_LIBPATHS 0 ${_PYTHON_PREFIX}_STDLIB) list (GET _${_PYTHON_PREFIX}_LIBPATHS 1 ${_PYTHON_PREFIX}_STDARCH) list (GET _${_PYTHON_PREFIX}_LIBPATHS 2 ${_PYTHON_PREFIX}_SITELIB) list (GET _${_PYTHON_PREFIX}_LIBPATHS 3 ${_PYTHON_PREFIX}_SITEARCH) else() unset (${_PYTHON_PREFIX}_STDLIB) unset (${_PYTHON_PREFIX}_STDARCH) unset (${_PYTHON_PREFIX}_SITELIB) unset (${_PYTHON_PREFIX}_SITEARCH) endif() _python_get_config_var (${_PYTHON_PREFIX}_SOABI SOABI) _python_get_config_var (${_PYTHON_PREFIX}_SOSABI SOSABI) # store properties in the cache to speed-up future searches set (_${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES "${${_PYTHON_PREFIX}_INTERPRETER_ID};${${_PYTHON_PREFIX}_VERSION_MAJOR};${${_PYTHON_PREFIX}_VERSION_MINOR};${${_PYTHON_PREFIX}_VERSION_PATCH};${_${_PYTHON_PREFIX}_ARCH};${_${_PYTHON_PREFIX}_ABIFLAGS};${${_PYTHON_PREFIX}_SOABI};${${_PYTHON_PREFIX}_SOSABI};${${_PYTHON_PREFIX}_STDLIB};${${_PYTHON_PREFIX}_STDARCH};${${_PYTHON_PREFIX}_SITELIB};${${_PYTHON_PREFIX}_SITEARCH}" CACHE INTERNAL "${_PYTHON_PREFIX} Properties") else() unset (_${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE CACHE) unset (${_PYTHON_PREFIX}_INTERPRETER_ID) endif() endif() endif() if (${_PYTHON_PREFIX}_ARTIFACTS_INTERACTIVE) set (${_PYTHON_PREFIX}_EXECUTABLE "${_${_PYTHON_PREFIX}_EXECUTABLE}" CACHE FILEPATH "${_PYTHON_PREFIX} Interpreter") endif() _python_mark_as_internal (_${_PYTHON_PREFIX}_EXECUTABLE _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES _${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE) endif() # second step, search for compiler (IronPython) if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS _${_PYTHON_PREFIX}_COMPILER) if (${_PYTHON_PREFIX}_FIND_REQUIRED_Compiler) list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_COMPILER) endif() if (NOT "IronPython" IN_LIST _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS) unset (_${_PYTHON_PREFIX}_COMPILER CACHE) unset (_${_PYTHON_PREFIX}_COMPILER_SIGNATURE CACHE) elseif (DEFINED ${_PYTHON_PREFIX}_COMPILER AND IS_ABSOLUTE "${${_PYTHON_PREFIX}_COMPILER}") set (_${_PYTHON_PREFIX}_COMPILER "${${_PYTHON_PREFIX}_COMPILER}" CACHE INTERNAL "") elseif (DEFINED _${_PYTHON_PREFIX}_COMPILER) # compute compiler signature and check validity of definition string (MD5 __${_PYTHON_PREFIX}_COMPILER_SIGNATURE "${_${_PYTHON_PREFIX}_SIGNATURE}:${_${_PYTHON_PREFIX}_COMPILER}") if (__${_PYTHON_PREFIX}_COMPILER_SIGNATURE STREQUAL _${_PYTHON_PREFIX}_COMPILER_SIGNATURE) # check version validity if (${_PYTHON_PREFIX}_FIND_VERSION_EXACT) _python_validate_compiler (VERSION ${${_PYTHON_PREFIX}_FIND_VERSION} EXACT CHECK_EXISTS) elseif (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) _python_validate_compiler (IN_RANGE CHECK_EXISTS) elseif (DEFINED ${_PYTHON_PREFIX}_FIND_VERSION) _python_validate_compiler (VERSION ${${_PYTHON_PREFIX}_FIND_VERSION} CHECK_EXISTS) else() _python_validate_compiler (CHECK_EXISTS) endif() else() unset (_${_PYTHON_PREFIX}_COMPILER CACHE) unset (_${_PYTHON_PREFIX}_COMPILER_SIGNATURE CACHE) endif() endif() if ("IronPython" IN_LIST _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS AND NOT _${_PYTHON_PREFIX}_COMPILER) # IronPython specific artifacts # If IronPython interpreter is found, use its path unset (_${_PYTHON_PREFIX}_IRON_ROOT) if (${_PYTHON_PREFIX}_Interpreter_FOUND AND ${_PYTHON_PREFIX}_INTERPRETER_ID STREQUAL "IronPython") get_filename_component (_${_PYTHON_PREFIX}_IRON_ROOT "${${_PYTHON_PREFIX}_EXECUTABLE}" DIRECTORY) endif() if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION") _python_get_names (_${_PYTHON_PREFIX}_COMPILER_NAMES IMPLEMENTATIONS IronPython VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} COMPILER) _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES IMPLEMENTATIONS IronPython VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} COMPILER) _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS IMPLEMENTATIONS IronPython VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS}) _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS IMPLEMENTATIONS IronPython VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS}) set (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS ${_${_PYTHON_PREFIX}_FIND_VERSION_EXACT}) if (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) list (APPEND _${_PYTHON_PREFIX}_VALIDATE_OPTIONS IN_RANGE) elseif (DEFINED ${_PYTHON_PREFIX}_FIND_VERSION) list (APPEND _${_PYTHON_PREFIX}_VALIDATE_OPTIONS VERSION ${${_PYTHON_PREFIX}_FIND_VERSION}) endif() while (TRUE) # Apple frameworks handling if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") find_program (_${_PYTHON_PREFIX}_COMPILER NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH ${_SKBUILD_PYTHON_VALIDATOR}) if (_${_PYTHON_PREFIX}_COMPILER) break() endif() endif() # Windows registry if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") find_program (_${_PYTHON_PREFIX}_COMPILER NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} ${_${_PYTHON_PREFIX}_REGISTRY_VIEW} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH ${_SKBUILD_PYTHON_VALIDATOR}) if (_${_PYTHON_PREFIX}_COMPILER) break() endif() endif() # try using HINTS find_program (_${_PYTHON_PREFIX}_COMPILER NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH ${_SKBUILD_PYTHON_VALIDATOR}) if (_${_PYTHON_PREFIX}_COMPILER) break() endif() # try using standard paths find_program (_${_PYTHON_PREFIX}_COMPILER NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} NAMES_PER_DIR PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} ${_SKBUILD_PYTHON_VALIDATOR}) if (_${_PYTHON_PREFIX}_COMPILER) break() endif() # Apple frameworks handling if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") find_program (_${_PYTHON_PREFIX}_COMPILER NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} NAMES_PER_DIR PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_DEFAULT_PATH ${_SKBUILD_PYTHON_VALIDATOR}) if (_${_PYTHON_PREFIX}_COMPILER) break() endif() endif() # Windows registry if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") find_program (_${_PYTHON_PREFIX}_COMPILER NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} NAMES_PER_DIR PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} ${_${_PYTHON_PREFIX}_REGISTRY_VIEW} NO_DEFAULT_PATH ${_SKBUILD_PYTHON_VALIDATOR}) if (_${_PYTHON_PREFIX}_COMPILER) break() endif() endif() break() endwhile() else() # try using root dir and registry set (_${_PYTHON_PREFIX}_COMMON_VALIDATE_OPTIONS EXACT) if (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) list (APPEND _${_PYTHON_PREFIX}_COMMON_VALIDATE_OPTIONS IN_RANGE) endif() foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) _python_get_names (_${_PYTHON_PREFIX}_COMPILER_NAMES IMPLEMENTATIONS IronPython VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} COMPILER) _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES IMPLEMENTATIONS IronPython VERSION ${_${_PYTHON_PREFIX}_FIND_VERSION} COMPILER) _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS IMPLEMENTATIONS IronPython VERSION ${_${_PYTHON_PREFIX}_VERSION}) _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS IMPLEMENTATIONS IronPython VERSION ${_${_PYTHON_PREFIX}_VERSION}) set (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_COMMON_VALIDATE_OPTIONS}) # Apple frameworks handling if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") find_program (_${_PYTHON_PREFIX}_COMPILER NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH ${_SKBUILD_PYTHON_VALIDATOR}) if (_${_PYTHON_PREFIX}_COMPILER) break() endif() endif() # Windows registry if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") find_program (_${_PYTHON_PREFIX}_COMPILER NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} ${_${_PYTHON_PREFIX}_REGISTRY_VIEW} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH ${_SKBUILD_PYTHON_VALIDATOR}) if (_${_PYTHON_PREFIX}_COMPILER) break() endif() endif() # try using HINTS find_program (_${_PYTHON_PREFIX}_COMPILER NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH ${_SKBUILD_PYTHON_VALIDATOR}) if (_${_PYTHON_PREFIX}_COMPILER) break() endif() # Apple frameworks handling if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") find_program (_${_PYTHON_PREFIX}_COMPILER NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} NAMES_PER_DIR PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_DEFAULT_PATH ${_SKBUILD_PYTHON_VALIDATOR}) if (_${_PYTHON_PREFIX}_COMPILER) break() endif() endif() # Windows registry if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") find_program (_${_PYTHON_PREFIX}_COMPILER NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} NAMES_PER_DIR PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} ${_${_PYTHON_PREFIX}_REGISTRY_VIEW} NO_DEFAULT_PATH ${_SKBUILD_PYTHON_VALIDATOR}) if (_${_PYTHON_PREFIX}_COMPILER) break() endif() endif() endforeach() # no specific version found, re-try in standard paths _python_get_names (_${_PYTHON_PREFIX}_COMPILER_NAMES IMPLEMENTATIONS IronPython VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} COMPILER) _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES IMPLEMENTATIONS IronPython VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} COMPILER) unset (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS) find_program (_${_PYTHON_PREFIX}_COMPILER NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} ${_SKBUILD_PYTHON_VALIDATOR}) endif() endif() set (${_PYTHON_PREFIX}_COMPILER "${_${_PYTHON_PREFIX}_COMPILER}") if (_${_PYTHON_PREFIX}_COMPILER) # retrieve python environment version from compiler _python_get_launcher (_${_PYTHON_PREFIX}_COMPILER_LAUNCHER COMPILER) set (_${_PYTHON_PREFIX}_VERSION_DIR "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/PythonCompilerVersion.dir") file (WRITE "${_${_PYTHON_PREFIX}_VERSION_DIR}/version.py" "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:3]]))\n") execute_process (COMMAND ${_${_PYTHON_PREFIX}_COMPILER_LAUNCHER} "${_${_PYTHON_PREFIX}_COMPILER}" ${_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_ARCH_FLAGS} /target:exe /embed "${_${_PYTHON_PREFIX}_VERSION_DIR}/version.py" WORKING_DIRECTORY "${_${_PYTHON_PREFIX}_VERSION_DIR}" OUTPUT_QUIET ERROR_QUIET) get_filename_component (_${_PYTHON_PREFIX}_IR_DIR "${_${_PYTHON_PREFIX}_COMPILER}" DIRECTORY) execute_process (COMMAND "${CMAKE_COMMAND}" -E env "MONO_PATH=${_${_PYTHON_PREFIX}_IR_DIR}" ${${_PYTHON_PREFIX}_DOTNET_LAUNCHER} "${_${_PYTHON_PREFIX}_VERSION_DIR}/version.exe" WORKING_DIRECTORY "${_${_PYTHON_PREFIX}_VERSION_DIR}" RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE _${_PYTHON_PREFIX}_VERSION ERROR_QUIET) if (NOT _${_PYTHON_PREFIX}_RESULT) set (_${_PYTHON_PREFIX}_COMPILER_USABLE TRUE) string (REGEX MATCHALL "[0-9]+" _${_PYTHON_PREFIX}_VERSIONS "${_${_PYTHON_PREFIX}_VERSION}") list (GET _${_PYTHON_PREFIX}_VERSIONS 0 _${_PYTHON_PREFIX}_VERSION_MAJOR) list (GET _${_PYTHON_PREFIX}_VERSIONS 1 _${_PYTHON_PREFIX}_VERSION_MINOR) list (GET _${_PYTHON_PREFIX}_VERSIONS 2 _${_PYTHON_PREFIX}_VERSION_PATCH) if (NOT ${_PYTHON_PREFIX}_Interpreter_FOUND) # set public version information set (${_PYTHON_PREFIX}_VERSION ${_${_PYTHON_PREFIX}_VERSION}) set (${_PYTHON_PREFIX}_VERSION_MAJOR ${_${_PYTHON_PREFIX}_VERSION_MAJOR}) set (${_PYTHON_PREFIX}_VERSION_MINOR ${_${_PYTHON_PREFIX}_VERSION_MINOR}) set (${_PYTHON_PREFIX}_VERSION_PATCH ${_${_PYTHON_PREFIX}_VERSION_PATCH}) endif() else() # compiler not usable set (_${_PYTHON_PREFIX}_COMPILER_USABLE FALSE) set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Cannot run the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"") endif() file (REMOVE_RECURSE "${_${_PYTHON_PREFIX}_VERSION_DIR}") endif() if (_${_PYTHON_PREFIX}_COMPILER AND _${_PYTHON_PREFIX}_COMPILER_USABLE) if (${_PYTHON_PREFIX}_Interpreter_FOUND) # Compiler must be compatible with interpreter if ("${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR}" VERSION_EQUAL "${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}") set (${_PYTHON_PREFIX}_Compiler_FOUND TRUE) endif() elseif (${_PYTHON_PREFIX}_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) set (${_PYTHON_PREFIX}_Compiler_FOUND TRUE) # Use compiler version for future searches to ensure consistency set (_${_PYTHON_PREFIX}_FIND_VERSIONS ${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}) endif() endif() if (${_PYTHON_PREFIX}_Compiler_FOUND) unset (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE CACHE) # compute and save compiler signature string (MD5 __${_PYTHON_PREFIX}_COMPILER_SIGNATURE "${_${_PYTHON_PREFIX}_SIGNATURE}:${_${_PYTHON_PREFIX}_COMPILER}") set (_${_PYTHON_PREFIX}_COMPILER_SIGNATURE "${__${_PYTHON_PREFIX}_COMPILER_SIGNATURE}" CACHE INTERNAL "") set (${_PYTHON_PREFIX}_COMPILER_ID IronPython) else() unset (_${_PYTHON_PREFIX}_COMPILER_SIGNATURE CACHE) unset (${_PYTHON_PREFIX}_COMPILER_ID) endif() if (${_PYTHON_PREFIX}_ARTIFACTS_INTERACTIVE) set (${_PYTHON_PREFIX}_COMPILER "${_${_PYTHON_PREFIX}_COMPILER}" CACHE FILEPATH "${_PYTHON_PREFIX} Compiler") endif() _python_mark_as_internal (_${_PYTHON_PREFIX}_COMPILER _${_PYTHON_PREFIX}_COMPILER_SIGNATURE) endif() # third step, search for the development artifacts if (${_PYTHON_PREFIX}_FIND_REQUIRED_Development.Module) if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS) list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_LIBRARIES) endif() if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS) list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_INCLUDE_DIRS) endif() endif() if (${_PYTHON_PREFIX}_FIND_REQUIRED_Development.SABIModule) if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_SABIMODULE_ARTIFACTS) list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_SABI_LIBRARIES) endif() if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_SABIMODULE_ARTIFACTS) list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_INCLUDE_DIRS) endif() endif() if (${_PYTHON_PREFIX}_FIND_REQUIRED_Development.Embed) if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_EMBED_ARTIFACTS) list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_LIBRARIES) endif() if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_EMBED_ARTIFACTS) list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_INCLUDE_DIRS) endif() endif() list (REMOVE_DUPLICATES _${_PYTHON_PREFIX}_REQUIRED_VARS) ## Development environment is not compatible with IronPython interpreter if (("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS OR "Development.SABIModule" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS OR "Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) AND ((${_PYTHON_PREFIX}_Interpreter_FOUND AND NOT ${_PYTHON_PREFIX}_INTERPRETER_ID STREQUAL "IronPython") OR NOT ${_PYTHON_PREFIX}_Interpreter_FOUND)) if (${_PYTHON_PREFIX}_Interpreter_FOUND) # reduce possible implementations to the interpreter one if (${_PYTHON_PREFIX}_INTERPRETER_ID STREQUAL "PyPy") set (_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS "PyPy") else() set (_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS "CPython") endif() else() list (REMOVE_ITEM _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS "IronPython") endif() if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS _${_PYTHON_PREFIX}_LIBRARY_RELEASE _${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE _${_PYTHON_PREFIX}_LIBRARY_DEBUG _${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG) endif() if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE _${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE _${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG _${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG) endif() if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS _${_PYTHON_PREFIX}_INCLUDE_DIR) endif() _python_check_development_signature (Module) _python_check_development_signature (SABIModule) _python_check_development_signature (Embed) if (DEFINED ${_PYTHON_PREFIX}_LIBRARY AND IS_ABSOLUTE "${${_PYTHON_PREFIX}_LIBRARY}") set (_${_PYTHON_PREFIX}_LIBRARY_RELEASE "${${_PYTHON_PREFIX}_LIBRARY}" CACHE INTERNAL "") unset (_${_PYTHON_PREFIX}_LIBRARY_DEBUG CACHE) unset (_${_PYTHON_PREFIX}_INCLUDE_DIR CACHE) endif() if (DEFINED ${_PYTHON_PREFIX}_SABI_LIBRARY AND IS_ABSOLUTE "${${_PYTHON_PREFIX}_SABI_LIBRARY}") set (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE "${${_PYTHON_PREFIX}_SABI_LIBRARY}" CACHE INTERNAL "") unset (_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG CACHE) unset (_${_PYTHON_PREFIX}_INCLUDE_DIR CACHE) endif() if (DEFINED ${_PYTHON_PREFIX}_INCLUDE_DIR AND IS_ABSOLUTE "${${_PYTHON_PREFIX}_INCLUDE_DIR}") set (_${_PYTHON_PREFIX}_INCLUDE_DIR "${${_PYTHON_PREFIX}_INCLUDE_DIR}" CACHE INTERNAL "") endif() # Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES unset (_${_PYTHON_PREFIX}_CMAKE_FIND_LIBRARY_SUFFIXES) if (DEFINED ${_PYTHON_PREFIX}_USE_STATIC_LIBS AND NOT WIN32) set(_${_PYTHON_PREFIX}_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) if(${_PYTHON_PREFIX}_USE_STATIC_LIBS) set (CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX}) else() list (REMOVE_ITEM CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX}) endif() endif() if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE OR NOT _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE OR NOT _${_PYTHON_PREFIX}_INCLUDE_DIR) # if python interpreter is found, use it to look-up for artifacts # to ensure consistency between interpreter and development environments. # If not, try to locate a compatible config tool if ((NOT ${_PYTHON_PREFIX}_Interpreter_FOUND OR CMAKE_CROSSCOMPILING) AND "CPython" IN_LIST _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS) set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) unset (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS) if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$") set (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS ENV VIRTUAL_ENV ENV CONDA_PREFIX) endif() if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION") _python_get_names (_${_PYTHON_PREFIX}_CONFIG_NAMES VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} POSIX CONFIG) # Framework Paths _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS}) # Apple frameworks handling if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") find_program (_${_PYTHON_PREFIX}_CONFIG NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES bin NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() find_program (_${_PYTHON_PREFIX}_CONFIG NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} PATH_SUFFIXES bin) # Apple frameworks handling if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") find_program (_${_PYTHON_PREFIX}_CONFIG NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES} NAMES_PER_DIR PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES bin NO_DEFAULT_PATH) endif() if (_${_PYTHON_PREFIX}_CONFIG) execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --help RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE __${_PYTHON_PREFIX}_HELP ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (_${_PYTHON_PREFIX}_RESULT) # assume config tool is not usable unset (_${_PYTHON_PREFIX}_CONFIG CACHE) endif() endif() if (_${_PYTHON_PREFIX}_CONFIG) execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --abiflags RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE __${_PYTHON_PREFIX}_ABIFLAGS ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (_${_PYTHON_PREFIX}_RESULT) # assume ABI is not supported set (__${_PYTHON_PREFIX}_ABIFLAGS "") endif() if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT __${_PYTHON_PREFIX}_ABIFLAGS IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) # Wrong ABI unset (_${_PYTHON_PREFIX}_CONFIG CACHE) endif() endif() if (_${_PYTHON_PREFIX}_CONFIG AND DEFINED CMAKE_LIBRARY_ARCHITECTURE) # check that config tool match library architecture execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --configdir RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE _${_PYTHON_PREFIX}_CONFIGDIR ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (_${_PYTHON_PREFIX}_RESULT) unset (_${_PYTHON_PREFIX}_CONFIG CACHE) else() string(FIND "${_${_PYTHON_PREFIX}_CONFIGDIR}" "${CMAKE_LIBRARY_ARCHITECTURE}" _${_PYTHON_PREFIX}_RESULT) if (_${_PYTHON_PREFIX}_RESULT EQUAL -1) unset (_${_PYTHON_PREFIX}_CONFIG CACHE) endif() endif() endif() else() foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) # try to use pythonX.Y-config tool _python_get_names (_${_PYTHON_PREFIX}_CONFIG_NAMES VERSION ${_${_PYTHON_PREFIX}_VERSION} POSIX CONFIG) # Framework Paths _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_VERSION}) # Apple frameworks handling if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") find_program (_${_PYTHON_PREFIX}_CONFIG NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES bin NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() find_program (_${_PYTHON_PREFIX}_CONFIG NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} PATH_SUFFIXES bin) # Apple frameworks handling if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") find_program (_${_PYTHON_PREFIX}_CONFIG NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES} NAMES_PER_DIR PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES bin NO_DEFAULT_PATH) endif() unset (_${_PYTHON_PREFIX}_CONFIG_NAMES) if (_${_PYTHON_PREFIX}_CONFIG) execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --help RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE __${_PYTHON_PREFIX}_HELP ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (_${_PYTHON_PREFIX}_RESULT) # assume config tool is not usable unset (_${_PYTHON_PREFIX}_CONFIG CACHE) endif() endif() if (NOT _${_PYTHON_PREFIX}_CONFIG) continue() endif() execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --abiflags RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE __${_PYTHON_PREFIX}_ABIFLAGS ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (_${_PYTHON_PREFIX}_RESULT) # assume ABI is not supported set (__${_PYTHON_PREFIX}_ABIFLAGS "") endif() if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT __${_PYTHON_PREFIX}_ABIFLAGS IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) # Wrong ABI unset (_${_PYTHON_PREFIX}_CONFIG CACHE) continue() endif() if (_${_PYTHON_PREFIX}_CONFIG AND DEFINED CMAKE_LIBRARY_ARCHITECTURE) # check that config tool match library architecture execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --configdir RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE _${_PYTHON_PREFIX}_CONFIGDIR ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (_${_PYTHON_PREFIX}_RESULT) unset (_${_PYTHON_PREFIX}_CONFIG CACHE) continue() endif() string (FIND "${_${_PYTHON_PREFIX}_CONFIGDIR}" "${CMAKE_LIBRARY_ARCHITECTURE}" _${_PYTHON_PREFIX}_RESULT) if (_${_PYTHON_PREFIX}_RESULT EQUAL -1) unset (_${_PYTHON_PREFIX}_CONFIG CACHE) continue() endif() endif() if (_${_PYTHON_PREFIX}_CONFIG) break() endif() endforeach() endif() endif() endif() if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) if ((${_PYTHON_PREFIX}_Interpreter_FOUND AND NOT CMAKE_CROSSCOMPILING) OR _${_PYTHON_PREFIX}_CONFIG) # retrieve root install directory _python_get_config_var (_${_PYTHON_PREFIX}_PREFIX PREFIX) # enforce current ABI _python_get_config_var (_${_PYTHON_PREFIX}_ABIFLAGS ABIFLAGS) set (_${_PYTHON_PREFIX}_HINTS "${_${_PYTHON_PREFIX}_PREFIX}") # retrieve library ## compute some paths and artifact names if (_${_PYTHON_PREFIX}_CONFIG) string (REGEX REPLACE "^.+python([0-9.]+)[a-z]*-config" "\\1" _${_PYTHON_PREFIX}_VERSION "${_${_PYTHON_PREFIX}_CONFIG}") else() set (_${_PYTHON_PREFIX}_VERSION "${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}") endif() _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_VERSION} LIBRARY) _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES VERSION ${_${_PYTHON_PREFIX}_VERSION} WIN32 POSIX LIBRARY) _python_get_config_var (_${_PYTHON_PREFIX}_CONFIGDIR CONFIGDIR) list (APPEND _${_PYTHON_PREFIX}_HINTS "${_${_PYTHON_PREFIX}_CONFIGDIR}") list (APPEND _${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() # Rely on HINTS and standard paths if interpreter or config tool failed to locate artifacts if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) unset (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS) if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$") set (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS ENV VIRTUAL_ENV ENV CONDA_PREFIX) endif() if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION") # library names _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} WIN32 POSIX LIBRARY) _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} WIN32 DEBUG) # Paths suffixes _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} LIBRARY) # Framework Paths _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_LIB_FIND_VERSIONS}) # Registry Paths _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} ) if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() # search in HINTS locations find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") set (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}) else() unset (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS) endif() if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") set (__${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}) else() unset (__${_PYTHON_PREFIX}_REGISTRY_PATHS) endif() # search in all default paths find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR PATHS ${__${_PYTHON_PREFIX}_FRAMEWORK_PATHS} ${__${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}) else() foreach (_${_PYTHON_PREFIX}_LIB_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION} WIN32 POSIX LIBRARY) _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION} WIN32 DEBUG) _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION}) _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION}) _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION} LIBRARY) if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() # search in HINTS locations find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") set (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}) else() unset (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS) endif() if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") set (__${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}) else() unset (__${_PYTHON_PREFIX}_REGISTRY_PATHS) endif() # search in all default paths find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR PATHS ${__${_PYTHON_PREFIX}_FRAMEWORK_PATHS} ${__${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}) if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE) break() endif() endforeach() endif() endif() endif() # finalize library version information _python_get_version (LIBRARY PREFIX _${_PYTHON_PREFIX}_) if (_${_PYTHON_PREFIX}_VERSION EQUAL "${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}") # not able to extract full version from library name if (${_PYTHON_PREFIX}_Interpreter_FOUND) # update from interpreter set (_${_PYTHON_PREFIX}_VERSION ${${_PYTHON_PREFIX}_VERSION}) set (_${_PYTHON_PREFIX}_VERSION_MAJOR ${${_PYTHON_PREFIX}_VERSION_MAJOR}) set (_${_PYTHON_PREFIX}_VERSION_MINOR ${${_PYTHON_PREFIX}_VERSION_MINOR}) set (_${_PYTHON_PREFIX}_VERSION_PATCH ${${_PYTHON_PREFIX}_VERSION_PATCH}) endif() endif() set (${_PYTHON_PREFIX}_LIBRARY_RELEASE "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}") if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE AND NOT EXISTS "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}") set_property (CACHE _${_PYTHON_PREFIX}_Development_LIBRARY_REASON_FAILURE PROPERTY VALUE "Cannot find the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"") set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND") else() unset (_${_PYTHON_PREFIX}_Development_LIBRARY_REASON_FAILURE CACHE) endif() set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) if (WIN32 AND _${_PYTHON_PREFIX}_LIBRARY_RELEASE) # search for debug library # use release library location as a hint _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG VERSION ${_${_PYTHON_PREFIX}_VERSION} WIN32 DEBUG) get_filename_component (_${_PYTHON_PREFIX}_PATH "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY) find_library (_${_PYTHON_PREFIX}_LIBRARY_DEBUG NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG} NAMES_PER_DIR HINTS "${_${_PYTHON_PREFIX}_PATH}" ${_${_PYTHON_PREFIX}_HINTS} NO_DEFAULT_PATH) # second try including CMAKE variables to catch-up non conventional layouts find_library (_${_PYTHON_PREFIX}_LIBRARY_DEBUG NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG} NAMES_PER_DIR NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() # retrieve runtime libraries if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE) _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES VERSION ${_${_PYTHON_PREFIX}_VERSION} WIN32 POSIX LIBRARY) get_filename_component (_${_PYTHON_PREFIX}_PATH "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY) get_filename_component (_${_PYTHON_PREFIX}_PATH2 "${_${_PYTHON_PREFIX}_PATH}" DIRECTORY) _python_find_runtime_library (_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR HINTS "${_${_PYTHON_PREFIX}_PATH}" "${_${_PYTHON_PREFIX}_PATH2}" ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES bin) endif() if (_${_PYTHON_PREFIX}_LIBRARY_DEBUG) _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG VERSION ${_${_PYTHON_PREFIX}_VERSION} WIN32 DEBUG) get_filename_component (_${_PYTHON_PREFIX}_PATH "${_${_PYTHON_PREFIX}_LIBRARY_DEBUG}" DIRECTORY) get_filename_component (_${_PYTHON_PREFIX}_PATH2 "${_${_PYTHON_PREFIX}_PATH}" DIRECTORY) _python_find_runtime_library (_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG} NAMES_PER_DIR HINTS "${_${_PYTHON_PREFIX}_PATH}" "${_${_PYTHON_PREFIX}_PATH2}" ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES bin) endif() endif() if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) if (NOT _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE) ## compute artifact names _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES VERSION ${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} WIN32 POSIX LIBRARY) _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG VERSION ${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} WIN32 DEBUG) if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS AND _${_PYTHON_PREFIX}_LIBRARY_RELEASE) # SABI_LIBRARY_RELEASE search is based on LIBRARY_RELEASE set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) get_filename_component (_${_PYTHON_PREFIX}_PATH "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY) find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} HINTS "${_${_PYTHON_PREFIX}_PATH}" ${_${_PYTHON_PREFIX}_HINTS} NO_DEFAULT_PATH) else() if ((${_PYTHON_PREFIX}_Interpreter_FOUND AND NOT CMAKE_CROSSCOMPILING) OR _${_PYTHON_PREFIX}_CONFIG) # retrieve root install directory _python_get_config_var (_${_PYTHON_PREFIX}_PREFIX PREFIX) # enforce current ABI _python_get_config_var (_${_PYTHON_PREFIX}_ABIFLAGS ABIFLAGS) set (_${_PYTHON_PREFIX}_HINTS "${_${_PYTHON_PREFIX}_PREFIX}") # retrieve SABI library ## compute some paths if (_${_PYTHON_PREFIX}_CONFIG) string (REGEX REPLACE "^.+python([0-9.]+)[a-z]*-config" "\\1" _${_PYTHON_PREFIX}_VERSION "${_${_PYTHON_PREFIX}_CONFIG}") else() set (_${_PYTHON_PREFIX}_VERSION "${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}") endif() _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_VERSION} LIBRARY) _python_get_config_var (_${_PYTHON_PREFIX}_CONFIGDIR CONFIGDIR) list (APPEND _${_PYTHON_PREFIX}_HINTS "${_${_PYTHON_PREFIX}_CONFIGDIR}") list (APPEND _${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() # Rely on HINTS and standard paths if interpreter or config tool failed to locate artifacts if (NOT _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE) set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) unset (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS) if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$") set (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS ENV VIRTUAL_ENV ENV CONDA_PREFIX) endif() if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION") # Paths suffixes _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} LIBRARY) # Framework Paths _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_LIB_FIND_VERSIONS}) # Registry Paths _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} ) if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() # search in HINTS locations find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") set (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}) else() unset (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS) endif() if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") set (__${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}) else() unset (__${_PYTHON_PREFIX}_REGISTRY_PATHS) endif() # search in all default paths find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR PATHS ${__${_PYTHON_PREFIX}_FRAMEWORK_PATHS} ${__${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}) else() foreach (_${_PYTHON_PREFIX}_LIB_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION}) _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION}) _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION} LIBRARY) if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() # search in HINTS locations find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") set (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}) else() unset (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS) endif() if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") set (__${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}) else() unset (__${_PYTHON_PREFIX}_REGISTRY_PATHS) endif() # search in all default paths find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR PATHS ${__${_PYTHON_PREFIX}_FRAMEWORK_PATHS} ${__${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}) if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE) break() endif() endforeach() endif() endif() endif() endif() # finalize library version information _python_get_version (SABI_LIBRARY PREFIX _${_PYTHON_PREFIX}_) # ABI library does not have the full version information if (${_PYTHON_PREFIX}_Interpreter_FOUND OR _${_PYTHON_PREFIX}_LIBRARY_RELEASE) # update from interpreter or library set (_${_PYTHON_PREFIX}_VERSION ${${_PYTHON_PREFIX}_VERSION}) set (_${_PYTHON_PREFIX}_VERSION_MAJOR ${${_PYTHON_PREFIX}_VERSION_MAJOR}) set (_${_PYTHON_PREFIX}_VERSION_MINOR ${${_PYTHON_PREFIX}_VERSION_MINOR}) set (_${_PYTHON_PREFIX}_VERSION_PATCH ${${_PYTHON_PREFIX}_VERSION_PATCH}) endif() set (${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}") if (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE AND NOT EXISTS "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}") set_property (CACHE _${_PYTHON_PREFIX}_Development_SABI_LIBRARY_REASON_FAILURE PROPERTY VALUE "Cannot find the library \"${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}\"") set_property (CACHE _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE-NOTFOUND") else() unset (_${_PYTHON_PREFIX}_Development_SABI_LIBRARY_REASON_FAILURE CACHE) endif() if (WIN32 AND _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE) # search for debug library get_filename_component (_${_PYTHON_PREFIX}_PATH "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}" DIRECTORY) find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG} NAMES_PER_DIR HINTS "${_${_PYTHON_PREFIX}_PATH}" ${_${_PYTHON_PREFIX}_HINTS} NO_DEFAULT_PATH) # second try including CMAKE variables to catch-up non conventional layouts find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG} NAMES_PER_DIR NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() # retrieve runtime libraries if (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE) get_filename_component (_${_PYTHON_PREFIX}_PATH "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}" DIRECTORY) get_filename_component (_${_PYTHON_PREFIX}_PATH2 "${_${_PYTHON_PREFIX}_PATH}" DIRECTORY) _python_find_runtime_library (_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR HINTS "${_${_PYTHON_PREFIX}_PATH}" "${_${_PYTHON_PREFIX}_PATH2}" ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES bin) endif() if (_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG) get_filename_component (_${_PYTHON_PREFIX}_PATH "${_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG}" DIRECTORY) get_filename_component (_${_PYTHON_PREFIX}_PATH2 "${_${_PYTHON_PREFIX}_PATH}" DIRECTORY) _python_find_runtime_library (_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG} NAMES_PER_DIR HINTS "${_${_PYTHON_PREFIX}_PATH}" "${_${_PYTHON_PREFIX}_PATH2}" ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES bin) endif() endif() if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) while (NOT _${_PYTHON_PREFIX}_INCLUDE_DIR) set (_${_PYTHON_PREFIX}_LIBRARY_REQUIRED FALSE) set (_${_PYTHON_PREFIX}_SABI_LIBRARY_REQUIRED FALSE) foreach (_${_PYTHON_PREFIX}_COMPONENT IN ITEMS Module SABIModule Embed) string (TOUPPER "${_${_PYTHON_PREFIX}_COMPONENT}" _${_PYTHON_PREFIX}_ID) if ("Development.${_${_PYTHON_PREFIX}_COMPONENT}" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND "LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${_${_PYTHON_PREFIX}_ID}_ARTIFACTS) set (_${_PYTHON_PREFIX}_LIBRARY_REQUIRED TRUE) endif() if ("Development.${_${_PYTHON_PREFIX}_COMPONENT}" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND "SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${_${_PYTHON_PREFIX}_ID}_ARTIFACTS) set (_${_PYTHON_PREFIX}_SABI_LIBRARY_REQUIRED TRUE) endif() endforeach() if ((_${_PYTHON_PREFIX}_LIBRARY_REQUIRED AND NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) AND (_${_PYTHON_PREFIX}_SABI_LIBRARY_REQUIRED AND NOT _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE)) # Don't search for include dir if no library was founded break() endif() if ((${_PYTHON_PREFIX}_Interpreter_FOUND AND NOT CMAKE_CROSSCOMPILING) OR _${_PYTHON_PREFIX}_CONFIG) _python_get_config_var (_${_PYTHON_PREFIX}_INCLUDE_DIRS INCLUDES) find_path (_${_PYTHON_PREFIX}_INCLUDE_DIR NAMES ${_${_PYTHON_PREFIX}_INCLUDE_NAMES} HINTS ${_${_PYTHON_PREFIX}_INCLUDE_DIRS} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() # Rely on HINTS and standard paths if interpreter or config tool failed to locate artifacts if (NOT _${_PYTHON_PREFIX}_INCLUDE_DIR) unset (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS) if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$") set (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS ENV VIRTUAL_ENV ENV CONDA_PREFIX) endif() unset (_${_PYTHON_PREFIX}_INCLUDE_HINTS) if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS AND _${_PYTHON_PREFIX}_LIBRARY_RELEASE) # Use the library's install prefix as a hint if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "^(.+/Frameworks/Python.framework/Versions/[0-9.]+)") list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}") elseif (_${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "^(.+)/lib(64|32)?/python[0-9.]+/config") list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}") elseif (DEFINED CMAKE_LIBRARY_ARCHITECTURE AND ${_${_PYTHON_PREFIX}_LIBRARY_RELEASE} MATCHES "^(.+)/lib/${CMAKE_LIBRARY_ARCHITECTURE}") list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}") else() # assume library is in a directory under root get_filename_component (_${_PYTHON_PREFIX}_PREFIX "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY) get_filename_component (_${_PYTHON_PREFIX}_PREFIX "${_${_PYTHON_PREFIX}_PREFIX}" DIRECTORY) list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${_${_PYTHON_PREFIX}_PREFIX}") endif() elseif ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS AND _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE) # Use the library's install prefix as a hint if (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE MATCHES "^(.+/Frameworks/Python.framework/Versions/[0-9.]+)") list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}") elseif (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE MATCHES "^(.+)/lib(64|32)?/python[0-9.]+/config") list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}") elseif (DEFINED CMAKE_LIBRARY_ARCHITECTURE AND ${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE} MATCHES "^(.+)/lib/${CMAKE_LIBRARY_ARCHITECTURE}") list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}") else() # assume library is in a directory under root get_filename_component (_${_PYTHON_PREFIX}_PREFIX "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}" DIRECTORY) get_filename_component (_${_PYTHON_PREFIX}_PREFIX "${_${_PYTHON_PREFIX}_PREFIX}" DIRECTORY) list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${_${_PYTHON_PREFIX}_PREFIX}") endif() endif() _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_VERSION}) _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS VERSION ${_${_PYTHON_PREFIX}_VERSION}) _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_VERSION} INCLUDE) if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") find_path (_${_PYTHON_PREFIX}_INCLUDE_DIR NAMES ${_${_PYTHON_PREFIX}_INCLUDE_NAMES} HINTS ${_${_PYTHON_PREFIX}_INCLUDE_HINTS} ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") find_path (_${_PYTHON_PREFIX}_INCLUDE_DIR NAMES ${_${_PYTHON_PREFIX}_INCLUDE_NAMES} HINTS ${_${_PYTHON_PREFIX}_INCLUDE_HINTS} ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") set (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}) else() unset (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS) endif() if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") set (__${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}) else() unset (__${_PYTHON_PREFIX}_REGISTRY_PATHS) endif() find_path (_${_PYTHON_PREFIX}_INCLUDE_DIR NAMES ${_${_PYTHON_PREFIX}_INCLUDE_NAMES} HINTS ${_${_PYTHON_PREFIX}_INCLUDE_HINTS} ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} ${__${_PYTHON_PREFIX}_FRAMEWORK_PATHS} ${__${_PYTHON_PREFIX}_REGISTRY_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() # search header file in standard locations find_path (_${_PYTHON_PREFIX}_INCLUDE_DIR NAMES ${_${_PYTHON_PREFIX}_INCLUDE_NAMES}) break() endwhile() set (${_PYTHON_PREFIX}_INCLUDE_DIRS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}") if (_${_PYTHON_PREFIX}_INCLUDE_DIR AND NOT EXISTS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}") set_property (CACHE _${_PYTHON_PREFIX}_Development_INCLUDE_DIR_REASON_FAILURE PROPERTY VALUE "Cannot find the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"") set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") else() unset (_${_PYTHON_PREFIX}_Development_INCLUDE_DIR_REASON_FAILURE CACHE) endif() if (_${_PYTHON_PREFIX}_INCLUDE_DIR) # retrieve version from header file _python_get_version (INCLUDE PREFIX _${_PYTHON_PREFIX}_INC_) if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS AND _${_PYTHON_PREFIX}_LIBRARY_RELEASE) if ("${_${_PYTHON_PREFIX}_INC_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_INC_VERSION_MINOR}" VERSION_EQUAL _${_PYTHON_PREFIX}_VERSION) # update versioning set (_${_PYTHON_PREFIX}_VERSION ${_${_PYTHON_PREFIX}_INC_VERSION}) set (_${_PYTHON_PREFIX}_VERSION_PATCH ${_${_PYTHON_PREFIX}_INC_VERSION_PATCH}) elseif (_${_PYTHON_PREFIX}_VERSION VERSION_EQUAL _${_PYTHON_PREFIX}_INC_VERSION_MAJOR) # library specify only major version, use include file for full version information set (_${_PYTHON_PREFIX}_VERSION ${_${_PYTHON_PREFIX}_INC_VERSION}) set (_${_PYTHON_PREFIX}_VERSION_MINOR ${_${_PYTHON_PREFIX}_INC_VERSION_MINOR}) set (_${_PYTHON_PREFIX}_VERSION_PATCH ${_${_PYTHON_PREFIX}_INC_VERSION_PATCH}) endif() else() set (_${_PYTHON_PREFIX}_VERSION ${_${_PYTHON_PREFIX}_INC_VERSION}) set (_${_PYTHON_PREFIX}_VERSION_MAJOR ${_${_PYTHON_PREFIX}_INC_VERSION_MAJOR}) set (_${_PYTHON_PREFIX}_VERSION_MINOR ${_${_PYTHON_PREFIX}_INC_VERSION_MINOR}) set (_${_PYTHON_PREFIX}_VERSION_PATCH ${_${_PYTHON_PREFIX}_INC_VERSION_PATCH}) endif() endif() endif() if (NOT ${_PYTHON_PREFIX}_Interpreter_FOUND AND NOT ${_PYTHON_PREFIX}_Compiler_FOUND) # set public version information set (${_PYTHON_PREFIX}_VERSION ${_${_PYTHON_PREFIX}_VERSION}) set (${_PYTHON_PREFIX}_VERSION_MAJOR ${_${_PYTHON_PREFIX}_VERSION_MAJOR}) set (${_PYTHON_PREFIX}_VERSION_MINOR ${_${_PYTHON_PREFIX}_VERSION_MINOR}) set (${_PYTHON_PREFIX}_VERSION_PATCH ${_${_PYTHON_PREFIX}_VERSION_PATCH}) endif() # define public variables if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) set (${_PYTHON_PREFIX}_LIBRARY_DEBUG "${_${_PYTHON_PREFIX}_LIBRARY_DEBUG}") _python_select_library_configurations (${_PYTHON_PREFIX}) set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE "${_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE}") set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG "${_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG}") if (_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE) set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY "${_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE}") elseif (_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG) set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY "${_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG}") else() set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY "${_PYTHON_PREFIX}_RUNTIME_LIBRARY-NOTFOUND") endif() _python_set_library_dirs (${_PYTHON_PREFIX}_LIBRARY_DIRS _${_PYTHON_PREFIX}_LIBRARY_RELEASE _${_PYTHON_PREFIX}_LIBRARY_DEBUG) if (UNIX) if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$") set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DIRS ${${_PYTHON_PREFIX}_LIBRARY_DIRS}) endif() else() _python_set_library_dirs (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DIRS _${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE _${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG) endif() endif() if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) set (${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG "${_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG}") _python_select_library_configurations (${_PYTHON_PREFIX}_SABI) set (${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE "${_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE}") set (${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG "${_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG}") if (_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE) set (${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY "${_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE}") elseif (_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG) set (${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY "${_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG}") else() set (${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY "${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY-NOTFOUND") endif() _python_set_library_dirs (${_PYTHON_PREFIX}_SABI_LIBRARY_DIRS _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE _${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG) if (UNIX) if (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$") set (${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DIRS ${${_PYTHON_PREFIX}_LIBRARY_DIRS}) endif() else() _python_set_library_dirs (${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DIRS _${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE _${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG) endif() endif() if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE OR _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE OR _${_PYTHON_PREFIX}_INCLUDE_DIR) if (${_PYTHON_PREFIX}_Interpreter_FOUND OR ${_PYTHON_PREFIX}_Compiler_FOUND) # development environment must be compatible with interpreter/compiler if ("${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR}" VERSION_EQUAL "${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}" AND "${_${_PYTHON_PREFIX}_INC_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_INC_VERSION_MINOR}" VERSION_EQUAL "${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR}") _python_set_development_module_found (Module) _python_set_development_module_found (SABIModule) _python_set_development_module_found (Embed) endif() elseif (${_PYTHON_PREFIX}_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR AND "${_${_PYTHON_PREFIX}_INC_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_INC_VERSION_MINOR}" VERSION_EQUAL "${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR}") _python_set_development_module_found (Module) _python_set_development_module_found (SABIModule) _python_set_development_module_found (Embed) endif() if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND (NOT _${_PYTHON_PREFIX}_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS OR NOT _${_PYTHON_PREFIX}_INC_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS)) set (${_PYTHON_PREFIX}_Development.Module_FOUND FALSE) set (${_PYTHON_PREFIX}_Development.SABIModule_FOUND FALSE) set (${_PYTHON_PREFIX}_Development.Embed_FOUND FALSE) endif() endif() if ("Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND ${_PYTHON_PREFIX}_Development.Module_FOUND AND ${_PYTHON_PREFIX}_Development.Embed_FOUND) set (${_PYTHON_PREFIX}_Development_FOUND TRUE) endif() if ((${_PYTHON_PREFIX}_Development.Module_FOUND OR ${_PYTHON_PREFIX}_Development.SABIModule_FOUND OR ${_PYTHON_PREFIX}_Development.Embed_FOUND) AND EXISTS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}/PyPy.h") # retrieve PyPy version file (STRINGS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}/patchlevel.h" ${_PYTHON_PREFIX}_PyPy_VERSION REGEX "^#define[ \t]+PYPY_VERSION[ \t]+\"[^\"]+\"") string (REGEX REPLACE "^#define[ \t]+PYPY_VERSION[ \t]+\"([^\"]+)\".*" "\\1" ${_PYTHON_PREFIX}_PyPy_VERSION "${${_PYTHON_PREFIX}_PyPy_VERSION}") endif() unset(${_PYTHON_PREFIX}_LINK_OPTIONS) if (${_PYTHON_PREFIX}_Development.Embed_FOUND AND APPLE AND ${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$") # rpath must be specified if python is part of a framework unset(_${_PYTHON_PREFIX}_is_prefix) foreach (_${_PYTHON_PREFIX}_implementation IN LISTS _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS) foreach (_${_PYTHON_PREFIX}_framework IN LISTS _${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_implementation}_FRAMEWORKS) # XXX(cmake-3.20) cmake_path if (CMAKE_VERSION VERSION_LESS "3.20") file(RELATIVE_PATH _${_PYTHON_PREFIX}_fw_rel "${_${_PYTHON_PREFIX}_framework}" "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}") if (_${_PYTHON_PREFIX}_fw_rel MATCHES "^\.\.[/\\\\]?") set(_${_PYTHON_PREFIX}_is_prefix 0) else () set(_${_PYTHON_PREFIX}_is_prefix 1) endif () unset(_${_PYTHON_PREFIX}_fw_rel) else () cmake_path (IS_PREFIX _${_PYTHON_PREFIX}_framework "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}" _${_PYTHON_PREFIX}_is_prefix) endif () if (_${_PYTHON_PREFIX}_is_prefix) # XXX(cmake-3.20): cmake_path if (CMAKE_VERSION VERSION_LESS "3.20") get_filename_component("_${_PYTHON_PREFIX}_framework" "${_${_PYTHON_PREFIX}_framework}" DIRECTORY) else() cmake_path (GET _${_PYTHON_PREFIX}_framework PARENT_PATH _${_PYTHON_PREFIX}_framework) endif() set (${_PYTHON_PREFIX}_LINK_OPTIONS "LINKER:-rpath,${_${_PYTHON_PREFIX}_framework}") break() endif() endforeach() if (_${_PYTHON_PREFIX}_is_prefix) break() endif() endforeach() unset(_${_PYTHON_PREFIX}_implementation) unset(_${_PYTHON_PREFIX}_framework) unset(_${_PYTHON_PREFIX}_is_prefix) endif() if (NOT DEFINED ${_PYTHON_PREFIX}_SOABI) _python_get_config_var (${_PYTHON_PREFIX}_SOABI SOABI) endif() if (NOT DEFINED ${_PYTHON_PREFIX}_SOSABI) _python_get_config_var (${_PYTHON_PREFIX}_SOSABI SOSABI) endif() _python_compute_development_signature (Module) _python_compute_development_signature (SABIModule) _python_compute_development_signature (Embed) # Restore the original find library ordering if (DEFINED _${_PYTHON_PREFIX}_CMAKE_FIND_LIBRARY_SUFFIXES) set (CMAKE_FIND_LIBRARY_SUFFIXES ${_${_PYTHON_PREFIX}_CMAKE_FIND_LIBRARY_SUFFIXES}) endif() if (${_PYTHON_PREFIX}_ARTIFACTS_INTERACTIVE) if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) set (${_PYTHON_PREFIX}_LIBRARY "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" CACHE FILEPATH "${_PYTHON_PREFIX} Library") endif() if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) set (${_PYTHON_PREFIX}_SABI_LIBRARY "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}" CACHE FILEPATH "${_PYTHON_PREFIX} ABI Library") endif() if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) set (${_PYTHON_PREFIX}_INCLUDE_DIR "${_${_PYTHON_PREFIX}_INCLUDE_DIR}" CACHE FILEPATH "${_PYTHON_PREFIX} Include Directory") endif() endif() _python_mark_as_internal (_${_PYTHON_PREFIX}_LIBRARY_RELEASE _${_PYTHON_PREFIX}_LIBRARY_DEBUG _${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE _${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE _${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG _${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE _${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG _${_PYTHON_PREFIX}_INCLUDE_DIR _${_PYTHON_PREFIX}_CONFIG _${_PYTHON_PREFIX}_DEVELOPMENT_MODULE_SIGNATURE _${_PYTHON_PREFIX}_DEVELOPMENT_EMBED_SIGNATURE) endif() if (${_PYTHON_PREFIX}_FIND_REQUIRED_NumPy) list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_NumPy_INCLUDE_DIRS) endif() if ("NumPy" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND ${_PYTHON_PREFIX}_Interpreter_FOUND) list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS _${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR) if (DEFINED ${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR AND IS_ABSOLUTE "${${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}") set (_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR "${${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}" CACHE INTERNAL "") elseif (DEFINED _${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR) # compute numpy signature. Depends on interpreter and development signatures string (MD5 __${_PYTHON_PREFIX}_NUMPY_SIGNATURE "${_${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE}:${_${_PYTHON_PREFIX}_DEVELOPMENT_MODULE_SIGNATURE}:${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}") if (NOT __${_PYTHON_PREFIX}_NUMPY_SIGNATURE STREQUAL _${_PYTHON_PREFIX}_NUMPY_SIGNATURE OR NOT EXISTS "${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}") unset (_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR CACHE) unset (_${_PYTHON_PREFIX}_NUMPY_SIGNATURE CACHE) endif() endif() if (NOT _${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR) execute_process(COMMAND ${${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys\ntry: import numpy; sys.stdout.write(numpy.get_include())\nexcept:pass\n" RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE _${_PYTHON_PREFIX}_NumPy_PATH ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (NOT _${_PYTHON_PREFIX}_RESULT) find_path (_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR NAMES "numpy/arrayobject.h" "numpy/numpyconfig.h" HINTS "${_${_PYTHON_PREFIX}_NumPy_PATH}" NO_DEFAULT_PATH) endif() endif() set (${_PYTHON_PREFIX}_NumPy_INCLUDE_DIRS "${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}") if(_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR AND NOT EXISTS "${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}") set_property (CACHE _${_PYTHON_PREFIX}_NumPy_REASON_FAILURE PROPERTY VALUE "Cannot find the directory \"${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}\"") set_property (CACHE _${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR-NOTFOUND") endif() if (_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR) execute_process (COMMAND ${${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys\ntry: import numpy; sys.stdout.write(numpy.__version__)\nexcept:pass\n" RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE _${_PYTHON_PREFIX}_NumPy_VERSION) if (NOT _${_PYTHON_PREFIX}_RESULT) set (${_PYTHON_PREFIX}_NumPy_VERSION "${_${_PYTHON_PREFIX}_NumPy_VERSION}") else() unset (${_PYTHON_PREFIX}_NumPy_VERSION) endif() # final step: set NumPy founded only if Development.Module component is founded as well set(${_PYTHON_PREFIX}_NumPy_FOUND ${${_PYTHON_PREFIX}_Development.Module_FOUND}) else() set (${_PYTHON_PREFIX}_NumPy_FOUND FALSE) endif() if (${_PYTHON_PREFIX}_NumPy_FOUND) unset (_${_PYTHON_PREFIX}_NumPy_REASON_FAILURE CACHE) # compute and save numpy signature string (MD5 __${_PYTHON_PREFIX}_NUMPY_SIGNATURE "${_${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE}:${_${_PYTHON_PREFIX}_DEVELOPMENT_MODULE_SIGNATURE}:${${_PYTHON_PREFIX}_NumPyINCLUDE_DIR}") set (_${_PYTHON_PREFIX}_NUMPY_SIGNATURE "${__${_PYTHON_PREFIX}_NUMPY_SIGNATURE}" CACHE INTERNAL "") else() unset (_${_PYTHON_PREFIX}_NUMPY_SIGNATURE CACHE) endif() if (${_PYTHON_PREFIX}_ARTIFACTS_INTERACTIVE) set (${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR "${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}" CACHE FILEPATH "${_PYTHON_PREFIX} NumPy Include Directory") endif() _python_mark_as_internal (_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR _${_PYTHON_PREFIX}_NUMPY_SIGNATURE) endif() # final validation if (${_PYTHON_PREFIX}_VERSION_MAJOR AND NOT ${_PYTHON_PREFIX}_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) _python_display_failure ("Could NOT find ${_PYTHON_PREFIX}: Found unsuitable major version \"${${_PYTHON_PREFIX}_VERSION_MAJOR}\", but required major version is exact version \"${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}\"") cmake_policy(POP) return() endif() unset (_${_PYTHON_PREFIX}_REASON_FAILURE) foreach (_${_PYTHON_PREFIX}_COMPONENT IN ITEMS Interpreter Compiler Development NumPy) if (_${_PYTHON_PREFIX}_COMPONENT STREQUAL "Development") foreach (artifact IN LISTS _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) if (_${_PYTHON_PREFIX}_Development_${artifact}_REASON_FAILURE) _python_add_reason_failure ("Development" "${_${_PYTHON_PREFIX}_Development_${artifact}_REASON_FAILURE}") endif() endforeach() endif() if (_${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_COMPONENT}_REASON_FAILURE) string (APPEND _${_PYTHON_PREFIX}_REASON_FAILURE "\n ${_${_PYTHON_PREFIX}_COMPONENT}: ${_${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_COMPONENT}_REASON_FAILURE}") unset (_${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_COMPONENT}_REASON_FAILURE CACHE) endif() endforeach() find_package_handle_standard_args (${_PYTHON_PREFIX} REQUIRED_VARS ${_${_PYTHON_PREFIX}_REQUIRED_VARS} VERSION_VAR ${_PYTHON_PREFIX}_VERSION HANDLE_VERSION_RANGE HANDLE_COMPONENTS REASON_FAILURE_MESSAGE "${_${_PYTHON_PREFIX}_REASON_FAILURE}") # Create imported targets and helper functions if(_${_PYTHON_PREFIX}_CMAKE_ROLE STREQUAL "PROJECT") if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND ${_PYTHON_PREFIX}_Interpreter_FOUND AND NOT TARGET ${_PYTHON_PREFIX}::Interpreter) add_executable (${_PYTHON_PREFIX}::Interpreter IMPORTED) set_property (TARGET ${_PYTHON_PREFIX}::Interpreter PROPERTY IMPORTED_LOCATION "${${_PYTHON_PREFIX}_EXECUTABLE}") endif() if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND ${_PYTHON_PREFIX}_Compiler_FOUND AND NOT TARGET ${_PYTHON_PREFIX}::Compiler) add_executable (${_PYTHON_PREFIX}::Compiler IMPORTED) set_property (TARGET ${_PYTHON_PREFIX}::Compiler PROPERTY IMPORTED_LOCATION "${${_PYTHON_PREFIX}_COMPILER}") endif() if (("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND ${_PYTHON_PREFIX}_Development.Module_FOUND) OR ("Development.SABIModule" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND ${_PYTHON_PREFIX}_Development.SABIModule_FOUND) OR ("Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND ${_PYTHON_PREFIX}_Development.Embed_FOUND)) macro (__PYTHON_IMPORT_LIBRARY __name) if (${ARGC} GREATER 1) set (_PREFIX "${ARGV1}_") else() set (_PREFIX "") endif() if (${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_RELEASE MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$" OR ${_PYTHON_PREFIX}_RUNTIME_${_PREFIX}LIBRARY_RELEASE) set (_${_PYTHON_PREFIX}_LIBRARY_TYPE SHARED) else() set (_${_PYTHON_PREFIX}_LIBRARY_TYPE STATIC) endif() if (NOT TARGET ${__name}) add_library (${__name} ${_${_PYTHON_PREFIX}_LIBRARY_TYPE} IMPORTED) endif() set_property (TARGET ${__name} PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_INCLUDE_DIRS}") if (${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_RUNTIME_${_PREFIX}LIBRARY_RELEASE) # System manage shared libraries in two parts: import and runtime if (${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_DEBUG) set_property (TARGET ${__name} PROPERTY IMPORTED_CONFIGURATIONS RELEASE DEBUG) set_target_properties (${__name} PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C" IMPORTED_IMPLIB_RELEASE "${${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_RELEASE}" IMPORTED_LOCATION_RELEASE "${${_PYTHON_PREFIX}_${_PREFIX}RUNTIME_LIBRARY_RELEASE}") set_target_properties (${__name} PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C" IMPORTED_IMPLIB_DEBUG "${${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_DEBUG}" IMPORTED_LOCATION_DEBUG "${${_PYTHON_PREFIX}_RUNTIME_${_PREFIX}LIBRARY_DEBUG}") else() set_target_properties (${__name} PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "C" IMPORTED_IMPLIB "${${_PYTHON_PREFIX}_${_PREFIX}LIBRARIES}" IMPORTED_LOCATION "${${_PYTHON_PREFIX}_RUNTIME_${_PREFIX}LIBRARY_RELEASE}") endif() else() if (${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_DEBUG) set_property (TARGET ${__name} PROPERTY IMPORTED_CONFIGURATIONS RELEASE DEBUG) set_target_properties (${__name} PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C" IMPORTED_LOCATION_RELEASE "${${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_RELEASE}") set_target_properties (${__name} PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C" IMPORTED_LOCATION_DEBUG "${${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_DEBUG}") else() set_target_properties (${__name} PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "C" IMPORTED_LOCATION "${${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_RELEASE}") endif() endif() if (_${_PYTHON_PREFIX}_LIBRARY_TYPE STREQUAL "STATIC") # extend link information with dependent libraries _python_get_config_var (_${_PYTHON_PREFIX}_LINK_LIBRARIES LIBS) if (_${_PYTHON_PREFIX}_LINK_LIBRARIES) set_property (TARGET ${__name} PROPERTY INTERFACE_LINK_LIBRARIES ${_${_PYTHON_PREFIX}_LINK_LIBRARIES}) endif() endif() if (${_PYTHON_PREFIX}_LINK_OPTIONS AND _${_PYTHON_PREFIX}_LIBRARY_TYPE STREQUAL "SHARED") set_property (TARGET ${__name} PROPERTY INTERFACE_LINK_OPTIONS "${${_PYTHON_PREFIX}_LINK_OPTIONS}") endif() endmacro() macro (__PYTHON_IMPORT_MODULE __name) if (NOT TARGET ${__name}) add_library (${__name} INTERFACE IMPORTED) endif() set_property (TARGET ${__name} PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_INCLUDE_DIRS}") # When available, enforce shared library generation with undefined symbols if (APPLE) set_property (TARGET ${__name} PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-undefined,dynamic_lookup") endif() if (CMAKE_SYSTEM_NAME STREQUAL "SunOS") set_property (TARGET ${__name} PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-z,nodefs") endif() if (CMAKE_SYSTEM_NAME STREQUAL "AIX") set_property (TARGET ${__name} PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-b,erok") endif() endmacro() if (${_PYTHON_PREFIX}_Development.Embed_FOUND) __python_import_library (${_PYTHON_PREFIX}::Python) endif() if (${_PYTHON_PREFIX}_Development.Module_FOUND) if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS) # On Windows/CYGWIN/MSYS, Python::Module is the same as Python::Python # but ALIAS cannot be used because the imported library is not GLOBAL. __python_import_library (${_PYTHON_PREFIX}::Module) else() __python_import_module (${_PYTHON_PREFIX}::Module) endif() endif() if (${_PYTHON_PREFIX}_Development.SABIModule_FOUND) if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_SABIMODULE_ARTIFACTS) __python_import_library (${_PYTHON_PREFIX}::SABIModule SABI) else() __python_import_module (${_PYTHON_PREFIX}::SABIModule) endif() endif() # # PYTHON_ADD_LIBRARY ( [STATIC|SHARED|MODULE] src1 src2 ... srcN) # It is used to build modules for python. # function (__${_PYTHON_PREFIX}_ADD_LIBRARY prefix name) cmake_parse_arguments (PARSE_ARGV 2 PYTHON_ADD_LIBRARY "STATIC;SHARED;MODULE;WITH_SOABI" "USE_SABI" "") if (PYTHON_ADD_LIBRARY_STATIC) set (type STATIC) elseif (PYTHON_ADD_LIBRARY_SHARED) set (type SHARED) else() set (type MODULE) endif() if (PYTHON_ADD_LIBRARY_USE_SABI) if (NOT type STREQUAL MODULE) message (SEND_ERROR "${prefix}_ADD_LIBRARY: 'USE_SABI' option is only valid for 'MODULE' type.") return() endif() if (NOT PYTHON_ADD_LIBRARY_USE_SABI MATCHES "^(3)(\\.([0-9]+))?$") message (SEND_ERROR "${prefix}_ADD_LIBRARY: ${PYTHON_ADD_LIBRARY_USE_SABI}: wrong version specified for 'USE_SABI'.") return() endif() # compute value for Py_LIMITED_API macro set (major_version "${CMAKE_MATCH_1}") unset (minor_version) if (CMAKE_MATCH_3) set (minor_version "${CMAKE_MATCH_3}") endif() if (major_version EQUAL "3" AND NOT minor_version) set (Py_LIMITED_API "3") elseif ("${major_version}.${minor_version}" VERSION_LESS "3.2") message (SEND_ERROR "${prefix}_ADD_LIBRARY: ${PYTHON_ADD_LIBRARY_USE_SABI}: invalid version. Version must be '3.2' or upper.") return() else() set (Py_LIMITED_API "0x0${major_version}") if (NOT minor_version) string (APPEND Py_LIMITED_API "00") else() if (minor_version LESS 16) string (APPEND Py_LIMITED_API "0") endif() math (EXPR minor_version "${minor_version}" OUTPUT_FORMAT HEXADECIMAL) string (REGEX REPLACE "^0x(.+)$" "\\1" minor_version "${minor_version}") string (APPEND Py_LIMITED_API "${minor_version}") endif() string (APPEND Py_LIMITED_API "0000") endif() endif() if (type STREQUAL "MODULE") if (PYTHON_ADD_LIBRARY_USE_SABI AND NOT TARGET ${prefix}::SABIModule) message (SEND_ERROR "${prefix}_ADD_LIBRARY: dependent target '${prefix}::SABIModule' is not defined.\n Did you miss to request COMPONENT 'Development.SABIModule'?") return() endif() if (NOT PYTHON_ADD_LIBRARY_USE_SABI AND NOT TARGET ${prefix}::Module) message (SEND_ERROR "${prefix}_ADD_LIBRARY: dependent target '${prefix}::Module' is not defined.\n Did you miss to request COMPONENT 'Development.Module'?") return() endif() endif() if (NOT type STREQUAL "MODULE" AND NOT TARGET ${prefix}::Python) message (SEND_ERROR "${prefix}_ADD_LIBRARY: dependent target '${prefix}::Python' is not defined.\n Did you miss to request COMPONENT 'Development.Embed'?") return() endif() add_library (${name} ${type} ${PYTHON_ADD_LIBRARY_UNPARSED_ARGUMENTS}) get_property (type TARGET ${name} PROPERTY TYPE) if (type STREQUAL "MODULE_LIBRARY") if (PYTHON_ADD_LIBRARY_USE_SABI) target_compile_definitions (${name} PRIVATE Py_LIMITED_API=${Py_LIMITED_API}) target_link_libraries (${name} PRIVATE ${prefix}::SABIModule) else() target_link_libraries (${name} PRIVATE ${prefix}::Module) endif() # customize library name to follow module name rules set_property (TARGET ${name} PROPERTY PREFIX "") if(CMAKE_SYSTEM_NAME STREQUAL "Windows") set_property (TARGET ${name} PROPERTY SUFFIX ".pyd") endif() if (PYTHON_ADD_LIBRARY_WITH_SOABI) if (NOT PYTHON_ADD_LIBRARY_USE_SABI AND ${prefix}_SOABI) get_property (suffix TARGET ${name} PROPERTY SUFFIX) if (NOT suffix) set (suffix "${CMAKE_SHARED_MODULE_SUFFIX}") endif() set_property (TARGET ${name} PROPERTY SUFFIX ".${${prefix}_SOABI}${suffix}") endif() if (PYTHON_ADD_LIBRARY_USE_SABI AND ${prefix}_SOSABI) get_property (suffix TARGET ${name} PROPERTY SUFFIX) if (NOT suffix) set (suffix "${CMAKE_SHARED_MODULE_SUFFIX}") endif() set_property (TARGET ${name} PROPERTY SUFFIX ".${${prefix}_SOSABI}${suffix}") endif() endif() else() if (PYTHON_ADD_LIBRARY_WITH_SOABI OR PYTHON_ADD_LIBRARY_USE_SABI) message (AUTHOR_WARNING "Find${prefix}: Options 'WITH_SOABI' and 'USE_SABI' are only supported for `MODULE` library type.") endif() target_link_libraries (${name} PRIVATE ${prefix}::Python) endif() endfunction() endif() if ("NumPy" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND ${_PYTHON_PREFIX}_NumPy_FOUND AND NOT TARGET ${_PYTHON_PREFIX}::NumPy AND TARGET ${_PYTHON_PREFIX}::Module) add_library (${_PYTHON_PREFIX}::NumPy INTERFACE IMPORTED) set_property (TARGET ${_PYTHON_PREFIX}::NumPy PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_NumPy_INCLUDE_DIRS}") target_link_libraries (${_PYTHON_PREFIX}::NumPy INTERFACE ${_PYTHON_PREFIX}::Module) endif() endif() # final clean-up # Restore CMAKE_FIND_APPBUNDLE if (DEFINED _${_PYTHON_PREFIX}_CMAKE_FIND_APPBUNDLE) set (CMAKE_FIND_APPBUNDLE ${_${_PYTHON_PREFIX}_CMAKE_FIND_APPBUNDLE}) unset (_${_PYTHON_PREFIX}_CMAKE_FIND_APPBUNDLE) else() unset (CMAKE_FIND_APPBUNDLE) endif() # Restore CMAKE_FIND_FRAMEWORK if (DEFINED _${_PYTHON_PREFIX}_CMAKE_FIND_FRAMEWORK) set (CMAKE_FIND_FRAMEWORK ${_${_PYTHON_PREFIX}_CMAKE_FIND_FRAMEWORK}) unset (_${_PYTHON_PREFIX}_CMAKE_FIND_FRAMEWORK) else() unset (CMAKE_FIND_FRAMEWORK) endif() cmake_policy(POP) scikit-build-core-0.11.1/src/scikit_build_core/resources/find_python/FindPython3.cmake000066400000000000000000000451341477275177200311030ustar00rootroot00000000000000# Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. #[=======================================================================[.rst: FindPython3 ----------- .. versionadded:: 3.12 Find Python 3 interpreter, compiler and development environment (include directories and libraries). .. versionadded:: 3.19 When a version is requested, it can be specified as a simple value or as a range. For a detailed description of version range usage and capabilities, refer to the :command:`find_package` command. The following components are supported: * ``Interpreter``: search for Python 3 interpreter * ``Compiler``: search for Python 3 compiler. Only offered by IronPython. * ``Development``: search for development artifacts (include directories and libraries). .. versionadded:: 3.18 This component includes two sub-components which can be specified independently: * ``Development.Module``: search for artifacts for Python 3 module developments. * ``Development.Embed``: search for artifacts for Python 3 embedding developments. .. versionadded:: 3.26 * ``Development.SABIModule``: search for artifacts for Python 3 module developments using the `Stable Application Binary Interface `_. This component is available only for version ``3.2`` and upper. * ``NumPy``: search for NumPy include directories. .. versionadded:: 3.14 Added the ``NumPy`` component. If no ``COMPONENTS`` are specified, ``Interpreter`` is assumed. If component ``Development`` is specified, it implies sub-components ``Development.Module`` and ``Development.Embed``. To ensure consistent versions between components ``Interpreter``, ``Compiler``, ``Development`` (or one of its sub-components) and ``NumPy``, specify all components at the same time:: find_package (Python3 COMPONENTS Interpreter Development) This module looks only for version 3 of Python. This module can be used concurrently with :module:`FindPython2` module to use both Python versions. The :module:`FindPython` module can be used if Python version does not matter for you. .. note:: If components ``Interpreter`` and ``Development`` (or one of its sub-components) are both specified, this module search only for interpreter with same platform architecture as the one defined by CMake configuration. This constraint does not apply if only ``Interpreter`` component is specified. Imported Targets ^^^^^^^^^^^^^^^^ This module defines the following :ref:`Imported Targets `: .. versionchanged:: 3.14 :ref:`Imported Targets ` are only created when :prop_gbl:`CMAKE_ROLE` is ``PROJECT``. ``Python3::Interpreter`` Python 3 interpreter. Target defined if component ``Interpreter`` is found. ``Python3::Compiler`` Python 3 compiler. Target defined if component ``Compiler`` is found. ``Python3::Module`` .. versionadded:: 3.15 Python 3 library for Python module. Target defined if component ``Development.Module`` is found. ``Python3::SABIModule`` .. versionadded:: 3.26 Python 3 library for Python module using the Stable Application Binary Interface. Target defined if component ``Development.SABIModule`` is found. ``Python3::Python`` Python 3 library for Python embedding. Target defined if component ``Development.Embed`` is found. ``Python3::NumPy`` .. versionadded:: 3.14 NumPy library for Python 3. Target defined if component ``NumPy`` is found. Result Variables ^^^^^^^^^^^^^^^^ This module will set the following variables in your project (see :ref:`Standard Variable Names `): ``Python3_FOUND`` System has the Python 3 requested components. ``Python3_Interpreter_FOUND`` System has the Python 3 interpreter. ``Python3_EXECUTABLE`` Path to the Python 3 interpreter. ``Python3_INTERPRETER_ID`` A short string unique to the interpreter. Possible values include: * Python * ActivePython * Anaconda * Canopy * IronPython * PyPy ``Python3_STDLIB`` Standard platform independent installation directory. Information returned by ``distutils.sysconfig.get_python_lib(plat_specific=False,standard_lib=True)`` or else ``sysconfig.get_path('stdlib')``. ``Python3_STDARCH`` Standard platform dependent installation directory. Information returned by ``distutils.sysconfig.get_python_lib(plat_specific=True,standard_lib=True)`` or else ``sysconfig.get_path('platstdlib')``. ``Python3_SITELIB`` Third-party platform independent installation directory. Information returned by ``distutils.sysconfig.get_python_lib(plat_specific=False,standard_lib=False)`` or else ``sysconfig.get_path('purelib')``. ``Python3_SITEARCH`` Third-party platform dependent installation directory. Information returned by ``distutils.sysconfig.get_python_lib(plat_specific=True,standard_lib=False)`` or else ``sysconfig.get_path('platlib')``. ``Python3_SOABI`` .. versionadded:: 3.17 Extension suffix for modules. Information computed from ``distutils.sysconfig.get_config_var('EXT_SUFFIX')`` or ``distutils.sysconfig.get_config_var('SOABI')`` or ``python3-config --extension-suffix``. If package ``distutils.sysconfig`` is not available, ``sysconfig.get_config_var('EXT_SUFFIX')`` or ``sysconfig.get_config_var('SOABI')`` are used. ``Python3_SOSABI`` .. versionadded:: 3.26 Extension suffix for modules using the Stable Application Binary Interface. Information computed from ``importlib.machinery.EXTENSION_SUFFIXES`` if the COMPONENT ``Interpreter`` was specified. Otherwise, the extension is ``abi3`` except for ``Windows``, ``MSYS`` and ``CYGWIN`` for which this is an empty string. ``Python3_Compiler_FOUND`` System has the Python 3 compiler. ``Python3_COMPILER`` Path to the Python 3 compiler. Only offered by IronPython. ``Python3_COMPILER_ID`` A short string unique to the compiler. Possible values include: * IronPython ``Python3_DOTNET_LAUNCHER`` .. versionadded:: 3.18 The ``.Net`` interpreter. Only used by ``IronPython`` implementation. ``Python3_Development_FOUND`` System has the Python 3 development artifacts. ``Python3_Development.Module_FOUND`` .. versionadded:: 3.18 System has the Python 3 development artifacts for Python module. ``Python3_Development.SABIModule_FOUND`` .. versionadded:: 3.26 System has the Python 3 development artifacts for Python module using the Stable Application Binary Interface. ``Python3_Development.Embed_FOUND`` .. versionadded:: 3.18 System has the Python 3 development artifacts for Python embedding. ``Python3_INCLUDE_DIRS`` The Python 3 include directories. ``Python3_LINK_OPTIONS`` .. versionadded:: 3.19 The Python 3 link options. Some configurations require specific link options for a correct build and execution. ``Python3_LIBRARIES`` The Python 3 libraries. ``Python3_LIBRARY_DIRS`` The Python 3 library directories. ``Python3_RUNTIME_LIBRARY_DIRS`` The Python 3 runtime library directories. ``Python3_SABI_LIBRARIES`` .. versionadded:: 3.26 The Python 3 libraries for the Stable Application Binary Interface. ``Python3_SABI_LIBRARY_DIRS`` .. versionadded:: 3.26 The Python 3 ``SABI`` library directories. ``Python3_RUNTIME_SABI_LIBRARY_DIRS`` .. versionadded:: 3.26 The Python 3 runtime ``SABI`` library directories. ``Python3_VERSION`` Python 3 version. ``Python3_VERSION_MAJOR`` Python 3 major version. ``Python3_VERSION_MINOR`` Python 3 minor version. ``Python3_VERSION_PATCH`` Python 3 patch version. ``Python3_PyPy_VERSION`` .. versionadded:: 3.18 Python 3 PyPy version. ``Python3_NumPy_FOUND`` .. versionadded:: 3.14 System has the NumPy. ``Python3_NumPy_INCLUDE_DIRS`` .. versionadded:: 3.14 The NumPy include directories. ``Python3_NumPy_VERSION`` .. versionadded:: 3.14 The NumPy version. Hints ^^^^^ ``Python3_ROOT_DIR`` Define the root directory of a Python 3 installation. ``Python3_USE_STATIC_LIBS`` * If not defined, search for shared libraries and static libraries in that order. * If set to TRUE, search **only** for static libraries. * If set to FALSE, search **only** for shared libraries. .. note:: This hint will be ignored on ``Windows`` because static libraries are not available on this platform. ``Python3_FIND_ABI`` .. versionadded:: 3.16 This variable defines which ABIs, as defined in :pep:`3149`, should be searched. .. note:: If ``Python3_FIND_ABI`` is not defined, any ABI will be searched. The ``Python3_FIND_ABI`` variable is a 3-tuple specifying, in that order, ``pydebug`` (``d``), ``pymalloc`` (``m``) and ``unicode`` (``u``) flags. Each element can be set to one of the following: * ``ON``: Corresponding flag is selected. * ``OFF``: Corresponding flag is not selected. * ``ANY``: The two possibilities (``ON`` and ``OFF``) will be searched. From this 3-tuple, various ABIs will be searched starting from the most specialized to the most general. Moreover, ``debug`` versions will be searched **after** ``non-debug`` ones. For example, if we have:: set (Python3_FIND_ABI "ON" "ANY" "ANY") The following flags combinations will be appended, in that order, to the artifact names: ``dmu``, ``dm``, ``du``, and ``d``. And to search any possible ABIs:: set (Python3_FIND_ABI "ANY" "ANY" "ANY") The following combinations, in that order, will be used: ``mu``, ``m``, ``u``, ````, ``dmu``, ``dm``, ``du`` and ``d``. .. note:: This hint is useful only on ``POSIX`` systems. So, on ``Windows`` systems, when ``Python3_FIND_ABI`` is defined, ``Python`` distributions from `python.org `_ will be found only if value for each flag is ``OFF`` or ``ANY``. ``Python3_FIND_STRATEGY`` .. versionadded:: 3.15 This variable defines how lookup will be done. The ``Python3_FIND_STRATEGY`` variable can be set to one of the following: * ``VERSION``: Try to find the most recent version in all specified locations. This is the default if policy :policy:`CMP0094` is undefined or set to ``OLD``. * ``LOCATION``: Stops lookup as soon as a version satisfying version constraints is founded. This is the default if policy :policy:`CMP0094` is set to ``NEW``. ``Python3_FIND_REGISTRY`` .. versionadded:: 3.13 On Windows the ``Python3_FIND_REGISTRY`` variable determine the order of preference between registry and environment variables. The ``Python3_FIND_REGISTRY`` variable can be set to one of the following: * ``FIRST``: Try to use registry before environment variables. This is the default. * ``LAST``: Try to use registry after environment variables. * ``NEVER``: Never try to use registry. ``Python3_FIND_FRAMEWORK`` .. versionadded:: 3.15 On macOS the ``Python3_FIND_FRAMEWORK`` variable determine the order of preference between Apple-style and unix-style package components. This variable can take same values as :variable:`CMAKE_FIND_FRAMEWORK` variable. .. note:: Value ``ONLY`` is not supported so ``FIRST`` will be used instead. If ``Python3_FIND_FRAMEWORK`` is not defined, :variable:`CMAKE_FIND_FRAMEWORK` variable will be used, if any. ``Python3_FIND_VIRTUALENV`` .. versionadded:: 3.15 This variable defines the handling of virtual environments managed by ``virtualenv`` or ``conda``. It is meaningful only when a virtual environment is active (i.e. the ``activate`` script has been evaluated). In this case, it takes precedence over ``Python3_FIND_REGISTRY`` and ``CMAKE_FIND_FRAMEWORK`` variables. The ``Python3_FIND_VIRTUALENV`` variable can be set to one of the following: * ``FIRST``: The virtual environment is used before any other standard paths to look-up for the interpreter. This is the default. * ``ONLY``: Only the virtual environment is used to look-up for the interpreter. * ``STANDARD``: The virtual environment is not used to look-up for the interpreter but environment variable ``PATH`` is always considered. In this case, variable ``Python3_FIND_REGISTRY`` (Windows) or ``CMAKE_FIND_FRAMEWORK`` (macOS) can be set with value ``LAST`` or ``NEVER`` to select preferably the interpreter from the virtual environment. .. versionadded:: 3.17 Added support for ``conda`` environments. .. note:: If the component ``Development`` is requested, it is **strongly** recommended to also include the component ``Interpreter`` to get expected result. ``Python3_FIND_IMPLEMENTATIONS`` .. versionadded:: 3.18 This variable defines, in an ordered list, the different implementations which will be searched. The ``Python3_FIND_IMPLEMENTATIONS`` variable can hold the following values: * ``CPython``: this is the standard implementation. Various products, like ``Anaconda`` or ``ActivePython``, rely on this implementation. * ``IronPython``: This implementation use the ``CSharp`` language for ``.NET Framework`` on top of the `Dynamic Language Runtime` (``DLR``). See `IronPython `_. * ``PyPy``: This implementation use ``RPython`` language and ``RPython translation toolchain`` to produce the python interpreter. See `PyPy `_. The default value is: * Windows platform: ``CPython``, ``IronPython`` * Other platforms: ``CPython`` .. note:: This hint has the lowest priority of all hints, so even if, for example, you specify ``IronPython`` first and ``CPython`` in second, a python product based on ``CPython`` can be selected because, for example with ``Python3_FIND_STRATEGY=LOCATION``, each location will be search first for ``IronPython`` and second for ``CPython``. .. note:: When ``IronPython`` is specified, on platforms other than ``Windows``, the ``.Net`` interpreter (i.e. ``mono`` command) is expected to be available through the ``PATH`` variable. ``Python3_FIND_UNVERSIONED_NAMES`` .. versionadded:: 3.20 This variable defines how the generic names will be searched. Currently, it only applies to the generic names of the interpreter, namely, ``python3`` and ``python``. The ``Python3_FIND_UNVERSIONED_NAMES`` variable can be set to one of the following values: * ``FIRST``: The generic names are searched before the more specialized ones (such as ``python3.5`` for example). * ``LAST``: The generic names are searched after the more specialized ones. This is the default. * ``NEVER``: The generic name are not searched at all. Artifacts Specification ^^^^^^^^^^^^^^^^^^^^^^^ .. versionadded:: 3.16 To solve special cases, it is possible to specify directly the artifacts by setting the following variables: ``Python3_EXECUTABLE`` The path to the interpreter. ``Python3_COMPILER`` The path to the compiler. ``Python3_DOTNET_LAUNCHER`` .. versionadded:: 3.18 The ``.Net`` interpreter. Only used by ``IronPython`` implementation. ``Python3_LIBRARY`` The path to the library. It will be used to compute the variables ``Python3_LIBRARIES``, ``Python3_LIBRARY_DIRS`` and ``Python3_RUNTIME_LIBRARY_DIRS``. ``Python3_SABI_LIBRARY`` .. versionadded:: 3.26 The path to the library for Stable Application Binary Interface. It will be used to compute the variables ``Python3_SABI_LIBRARIES``, ``Python3_SABI_LIBRARY_DIRS`` and ``Python3_RUNTIME_SABI_LIBRARY_DIRS``. ``Python3_INCLUDE_DIR`` The path to the directory of the ``Python`` headers. It will be used to compute the variable ``Python3_INCLUDE_DIRS``. ``Python3_NumPy_INCLUDE_DIR`` The path to the directory of the ``NumPy`` headers. It will be used to compute the variable ``Python3_NumPy_INCLUDE_DIRS``. .. note:: All paths must be absolute. Any artifact specified with a relative path will be ignored. .. note:: When an artifact is specified, all ``HINTS`` will be ignored and no search will be performed for this artifact. If more than one artifact is specified, it is the user's responsibility to ensure the consistency of the various artifacts. By default, this module supports multiple calls in different directories of a project with different version/component requirements while providing correct and consistent results for each call. To support this behavior, CMake cache is not used in the traditional way which can be problematic for interactive specification. So, to enable also interactive specification, module behavior can be controlled with the following variable: ``Python3_ARTIFACTS_INTERACTIVE`` .. versionadded:: 3.18 Selects the behavior of the module. This is a boolean variable: * If set to ``TRUE``: Create CMake cache entries for the above artifact specification variables so that users can edit them interactively. This disables support for multiple version/component requirements. * If set to ``FALSE`` or undefined: Enable multiple version/component requirements. Commands ^^^^^^^^ This module defines the command ``Python3_add_library`` (when :prop_gbl:`CMAKE_ROLE` is ``PROJECT``), which has the same semantics as :command:`add_library` and adds a dependency to target ``Python3::Python`` or, when library type is ``MODULE``, to target ``Python3::Module`` or ``Python3::SABIModule`` (when ``USE_SABI`` option is specified) and takes care of Python module naming rules:: Python3_add_library ( [STATIC | SHARED | MODULE [USE_SABI ] [WITH_SOABI]] [ ...]) If the library type is not specified, ``MODULE`` is assumed. .. versionadded:: 3.17 For ``MODULE`` library type, if option ``WITH_SOABI`` is specified, the module suffix will include the ``Python3_SOABI`` value, if any. .. versionadded:: 3.26 For ``MODULE`` type, if the option ``USE_SABI`` is specified, the preprocessor definition ``Py_LIMITED_API`` will be specified, as ``PRIVATE``, for the target ```` with the value computed from ```` argument. The expected format for ```` is ``major[.minor]``, where each component is a numeric value. If ``minor`` component is specified, the version should be, at least, ``3.2`` which is the version where the `Stable Application Binary Interface `_ was introduced. Specifying only major version ``3`` is equivalent to ``3.2``. When option ``WITH_SOABI`` is also specified, the module suffix will include the ``Python3_SOSABI`` value, if any. #]=======================================================================] set (_PYTHON_PREFIX Python3) set (_Python3_REQUIRED_VERSION_MAJOR 3) include (${CMAKE_CURRENT_LIST_DIR}/FindPython/Support.cmake) if (COMMAND __Python3_add_library) macro (Python3_add_library) __Python3_add_library (Python3 ${ARGV}) endmacro() endif() unset (_PYTHON_PREFIX) scikit-build-core-0.11.1/src/scikit_build_core/resources/find_python/__init__.py000066400000000000000000000000741477275177200300370ustar00rootroot00000000000000from __future__ import annotations __all__: list[str] = [] scikit-build-core-0.11.1/src/scikit_build_core/resources/known_wheels.toml000066400000000000000000000017241477275177200270100ustar00rootroot00000000000000[tool.scikit-build.cmake] known-wheels = [ "win_arm64", "win_amd64", "win32", "macosx_10_10_universal2", "musllinux_1_1_x86_64", "musllinux_1_1_s390x", "musllinux_1_1_ppc64le", "musllinux_1_1_i686", "musllinux_1_1_aarch64", "musllinux_1_2_armv7l", "manylinux_2_17_s390x", "manylinux_2_17_ppc64le", "manylinux_2_17_aarch64", "manylinux_2_31_armv7l", "manylinux_2_17_x86_64", "manylinux_2_17_i686", "manylinux_2_12_x86_64", "manylinux_2_12_i686", ] [tool.scikit-build.ninja] known-wheels = [ "win_arm64", "win_amd64", "win32", "macosx_10_9_universal2", "musllinux_1_1_x86_64", "musllinux_1_1_s390x", "musllinux_1_1_ppc64le", "musllinux_1_1_i686", "musllinux_1_1_aarch64", "musllinux_1_2_armv7l", "manylinux_2_17_s390x", "manylinux_2_17_ppc64le", "manylinux_2_17_aarch64", "manylinux_2_31_armv7l", "manylinux_2_5_x86_64", "manylinux_2_5_i686", ] scikit-build-core-0.11.1/src/scikit_build_core/resources/scikit-build.schema.json000066400000000000000000000612031477275177200301230ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-07/schema", "$id": "https://github.com/scikit-build/scikit-build-core/blob/main/src/scikit_build_core/resources/scikit-build.schema.json", "description": "Scikit-build-core's settings.", "type": "object", "additionalProperties": false, "properties": { "cmake": { "type": "object", "additionalProperties": false, "properties": { "minimum-version": { "type": "string", "description": "DEPRECATED in 0.8; use version instead.", "deprecated": true }, "version": { "type": "string", "description": "The versions of CMake to allow. If CMake is not present on the system or does not pass this specifier, it will be downloaded via PyPI if possible. An empty string will disable this check. The default on 0.10+ is \"CMakeLists.txt\", which will read it from the project's CMakeLists.txt file, or \">=3.15\" if unreadable or <0.10." }, "args": { "type": "array", "items": { "type": "string" }, "description": "A list of args to pass to CMake when configuring the project. Setting this in config or envvar will override toml. See also ``cmake.define``." }, "define": { "type": "object", "patternProperties": { ".+": { "oneOf": [ { "oneOf": [ { "type": "string" }, { "type": "boolean" }, { "type": "array", "items": { "type": "string" } } ] }, { "type": "object", "additionalProperties": false, "required": [ "env" ], "properties": { "env": { "type": "string", "minLength": 1 }, "default": { "oneOf": [ { "type": "string" }, { "type": "boolean" }, { "type": "array", "items": { "type": "string" } } ] } } } ] } }, "description": "A table of defines to pass to CMake when configuring the project. Additive." }, "verbose": { "type": "boolean", "description": "DEPRECATED in 0.10, use build.verbose instead.", "deprecated": true }, "build-type": { "type": "string", "default": "Release", "description": "The build type to use when building the project. Valid options are: \"Debug\", \"Release\", \"RelWithDebInfo\", \"MinSizeRel\", \"\", etc." }, "source-dir": { "type": "string", "default": ".", "description": "The source directory to use when building the project. Currently only affects the native builder (not the setuptools plugin)." }, "targets": { "type": "array", "items": { "type": "string" }, "description": "DEPRECATED in 0.10; use build.targets instead.", "deprecated": true } } }, "ninja": { "type": "object", "additionalProperties": false, "properties": { "minimum-version": { "type": "string", "description": "DEPRECATED in 0.8; use version instead.", "deprecated": true }, "version": { "type": "string", "default": ">=1.5", "description": "The versions of Ninja to allow. If Ninja is not present on the system or does not pass this specifier, it will be downloaded via PyPI if possible. An empty string will disable this check." }, "make-fallback": { "type": "boolean", "default": true, "description": "If Ninja is not present on the system or is older than required, it will be downloaded via PyPI if this is false." } } }, "logging": { "type": "object", "additionalProperties": false, "properties": { "level": { "enum": [ "NOTSET", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL" ], "default": "WARNING", "description": "The logging level to display, \"DEBUG\", \"INFO\", \"WARNING\", and \"ERROR\" are possible options." } } }, "sdist": { "type": "object", "additionalProperties": false, "properties": { "include": { "type": "array", "items": { "type": "string" }, "description": "Files to include in the SDist even if they are skipped by default. Supports gitignore syntax." }, "exclude": { "type": "array", "items": { "type": "string" }, "description": "Files to exclude from the SDist even if they are included by default. Supports gitignore syntax." }, "reproducible": { "type": "boolean", "default": true, "description": "If set to True, try to build a reproducible distribution (Unix and Python 3.9+ recommended). ``SOURCE_DATE_EPOCH`` will be used for timestamps, or a fixed value if not set." }, "cmake": { "type": "boolean", "default": false, "description": "If set to True, CMake will be run before building the SDist." } } }, "wheel": { "type": "object", "additionalProperties": false, "properties": { "packages": { "oneOf": [ { "type": "array", "items": { "type": "string" } }, { "type": "object", "patternProperties": { ".+": { "type": "string" } } } ], "description": "A list of packages to auto-copy into the wheel. If this is not set, it will default to the first of ``src/``, ``python/``, or ```` if they exist. The prefix(s) will be stripped from the package name inside the wheel. If a dict, provides a mapping of package name to source directory." }, "py-api": { "type": "string", "default": "", "description": "The Python tags. The default (empty string) will use the default Python version. You can also set this to \"cp38\" to enable the CPython 3.8+ Stable ABI / Limited API (only on CPython and if the version is sufficient, otherwise this has no effect). Or you can set it to \"py3\" or \"py2.py3\" to ignore Python ABI compatibility. The ABI tag is inferred from this tag." }, "expand-macos-universal-tags": { "type": "boolean", "default": false, "description": "Fill out extra tags that are not required. This adds \"x86_64\" and \"arm64\" to the list of platforms when \"universal2\" is used, which helps older Pip's (before 21.0.1) find the correct wheel." }, "install-dir": { "type": "string", "default": "", "description": "The install directory for the wheel. This is relative to the platlib root. You might set this to the package name. The original dir is still at SKBUILD_PLATLIB_DIR (also SKBUILD_DATA_DIR, etc. are available). EXPERIMENTAL: An absolute path will be one level higher than the platlib root, giving access to \"/platlib\", \"/data\", \"/headers\", and \"/scripts\"." }, "license-files": { "type": "array", "items": { "type": "string" }, "description": "A list of license files to include in the wheel. Supports glob patterns. The default is ``[\"LICEN[CS]E*\", \"COPYING*\", \"NOTICE*\", \"AUTHORS*\"]``. Must not be set if ``project.license-files`` is set." }, "cmake": { "type": "boolean", "default": true, "description": "If set to True (the default), CMake will be run before building the wheel." }, "platlib": { "type": "boolean", "description": "Target the platlib or the purelib. If not set, the default is to target the platlib if wheel.cmake is true, and the purelib otherwise." }, "exclude": { "type": "array", "items": { "type": "string" }, "description": "A set of patterns to exclude from the wheel. This is additive to the SDist exclude patterns. This applies to the final paths in the wheel, and can exclude files from CMake output as well. Editable installs may not respect this exclusion." }, "build-tag": { "type": "string", "default": "", "description": "The build tag to use for the wheel. If empty, no build tag is used." } } }, "backport": { "type": "object", "additionalProperties": false, "properties": { "find-python": { "type": "string", "default": "3.26.1", "description": "If CMake is less than this value, backport a copy of FindPython. Set to 0 disable this, or the empty string." } } }, "editable": { "type": "object", "additionalProperties": false, "properties": { "mode": { "enum": [ "redirect", "inplace" ], "default": "redirect", "description": "Select the editable mode to use. Can be \"redirect\" (default) or \"inplace\"." }, "verbose": { "type": "boolean", "default": true, "description": "Turn on verbose output for the editable mode rebuilds." }, "rebuild": { "type": "boolean", "default": false, "description": "Rebuild the project when the package is imported. The build-directory must be set." } } }, "build": { "type": "object", "additionalProperties": false, "properties": { "tool-args": { "type": "array", "items": { "type": "string" }, "description": "Extra args to pass directly to the builder in the build step." }, "targets": { "type": "array", "items": { "type": "string" }, "description": "The build targets to use when building the project. Empty builds the default target." }, "verbose": { "type": "boolean", "default": false, "description": "Verbose printout when building." }, "requires": { "type": "array", "items": { "type": "string" }, "description": "Additional ``build-system.requires``. Intended to be used in combination with ``overrides``." } } }, "install": { "type": "object", "additionalProperties": false, "properties": { "components": { "type": "array", "items": { "type": "string" }, "description": "The components to install. If empty, all default components are installed." }, "strip": { "type": "boolean", "description": "Whether to strip the binaries. True for release builds on scikit-build-core 0.5+ (0.5-0.10.5 also incorrectly set this for debug builds)." } } }, "generate": { "type": "array", "items": { "oneOf": [ { "type": "object", "additionalProperties": false, "required": [ "path", "template" ], "properties": { "path": { "type": "string", "description": "The path (relative to platlib) for the file to generate.", "minLength": 1 }, "template": { "type": "string", "description": "The template to use for the file. This includes string.Template style placeholders for all the metadata. If empty, a template-path must be set.", "minLength": 1 }, "location": { "enum": [ "install", "build", "source" ], "default": "install", "description": "The place to put the generated file. The \"build\" directory is useful for CMake files, and the \"install\" directory is useful for Python files, usually. You can also write directly to the \"source\" directory, will overwrite existing files & remember to gitignore the file." } } }, { "type": "object", "additionalProperties": false, "required": [ "path", "template-path" ], "properties": { "path": { "type": "string", "description": "The path (relative to platlib) for the file to generate.", "minLength": 1 }, "template-path": { "type": "string", "description": "The path to the template file. If empty, a template must be set.", "minLength": 1 }, "location": { "enum": [ "install", "build", "source" ], "default": "install", "description": "The place to put the generated file. The \"build\" directory is useful for CMake files, and the \"install\" directory is useful for Python files, usually. You can also write directly to the \"source\" directory, will overwrite existing files & remember to gitignore the file." } } } ] } }, "messages": { "type": "object", "additionalProperties": false, "properties": { "after-failure": { "type": "string", "default": "", "description": "A message to print after a build failure." }, "after-success": { "type": "string", "default": "", "description": "A message to print after a successful build." } } }, "search": { "type": "object", "additionalProperties": false, "properties": { "site-packages": { "type": "boolean", "default": true, "description": "Add the python build environment site_packages folder to the CMake prefix paths." } } }, "metadata": { "type": "object", "description": "List dynamic metadata fields and hook locations in this table.", "additionalProperties": false, "properties": { "version": { "$ref": "#/$defs/metadata" }, "description": { "$ref": "#/$defs/metadata" }, "license": { "$ref": "#/$defs/metadata" }, "readme": { "$ref": "#/$defs/metadata" }, "requires-python": { "$ref": "#/$defs/metadata" }, "dependencies": { "$ref": "#/$defs/metadata" }, "optional-dependencies": { "$ref": "#/$defs/metadata" }, "entrypoints": { "$ref": "#/$defs/metadata" }, "authors": { "$ref": "#/$defs/metadata" }, "maintainers": { "$ref": "#/$defs/metadata" }, "urls": { "$ref": "#/$defs/metadata" }, "classifiers": { "$ref": "#/$defs/metadata" }, "keywords": { "$ref": "#/$defs/metadata" }, "scripts": { "$ref": "#/$defs/metadata" }, "gui-scripts": { "$ref": "#/$defs/metadata" } } }, "strict-config": { "type": "boolean", "default": true, "description": "Strictly check all config options. If False, warnings will be printed for unknown options. If True, an error will be raised." }, "experimental": { "type": "boolean", "default": false, "description": "Enable early previews of features not finalized yet." }, "minimum-version": { "type": "string", "description": "If set, this will provide a method for backward compatibility." }, "build-dir": { "type": "string", "default": "", "description": "The build directory. Defaults to a temporary directory, but can be set." }, "fail": { "type": "boolean", "default": false, "description": "Immediately fail the build. This is only useful in overrides." }, "overrides": { "type": "array", "description": "A list of overrides to apply to the settings, based on the `if` selector.", "items": { "type": "object", "required": [ "if" ], "minProperties": 2, "additionalProperties": false, "properties": { "if": { "anyOf": [ { "$ref": "#/$defs/if_overrides" }, { "type": "object", "properties": { "any": { "$ref": "#/$defs/if_overrides" } }, "required": [ "any" ], "additionalProperties": false } ] }, "inherit": { "type": "object", "properties": { "cmake": { "type": "object", "additionalProperties": false, "properties": { "args": { "$ref": "#/$defs/inherit" }, "define": { "$ref": "#/$defs/inherit" }, "targets": { "$ref": "#/$defs/inherit" } } }, "sdist": { "type": "object", "additionalProperties": false, "properties": { "include": { "$ref": "#/$defs/inherit" }, "exclude": { "$ref": "#/$defs/inherit" } } }, "wheel": { "type": "object", "additionalProperties": false, "properties": { "packages": { "$ref": "#/$defs/inherit" }, "license-files": { "$ref": "#/$defs/inherit" }, "exclude": { "$ref": "#/$defs/inherit" } } }, "build": { "type": "object", "additionalProperties": false, "properties": { "tool-args": { "$ref": "#/$defs/inherit" }, "targets": { "$ref": "#/$defs/inherit" }, "requires": { "$ref": "#/$defs/inherit" } } }, "install": { "type": "object", "additionalProperties": false, "properties": { "components": { "$ref": "#/$defs/inherit" } } } }, "additionalProperties": false }, "cmake": { "$ref": "#/properties/cmake" }, "ninja": { "$ref": "#/properties/ninja" }, "logging": { "$ref": "#/properties/logging" }, "sdist": { "$ref": "#/properties/sdist" }, "wheel": { "$ref": "#/properties/wheel" }, "backport": { "$ref": "#/properties/backport" }, "editable": { "$ref": "#/properties/editable" }, "build": { "$ref": "#/properties/build" }, "install": { "$ref": "#/properties/install" }, "generate": { "$ref": "#/properties/generate" }, "messages": { "$ref": "#/properties/messages" }, "search": { "$ref": "#/properties/search" }, "metadata": { "$ref": "#/properties/metadata" }, "strict-config": { "$ref": "#/properties/strict-config" }, "experimental": { "$ref": "#/properties/experimental" }, "minimum-version": { "$ref": "#/properties/minimum-version" }, "build-dir": { "$ref": "#/properties/build-dir" }, "fail": { "$ref": "#/properties/fail" } } } } }, "$defs": { "metadata": { "type": "object", "properties": { "provider": { "type": "string" }, "provider-path": { "type": "string" } } }, "if_overrides": { "type": "object", "minProperties": 1, "additionalProperties": false, "properties": { "scikit-build-version": { "type": "string", "description": "The version of scikit-build-version. Takes a specifier set." }, "python-version": { "type": "string", "description": "The two-digit Python version. Takes a specifier set." }, "implementation-name": { "type": "string", "description": "The value of `sys.implementation.name`. Takes a regex" }, "implementation-version": { "type": "string", "description": "Derived from `sys.implementation.version`, following PEP 508. Takes a specifier set." }, "platform-system": { "type": "string", "description": "The value of `sys.platform`. Takes a regex." }, "platform-machine": { "type": "string", "description": "The value of `platform.machine()`. Takes a regex." }, "platform-node": { "type": "string", "description": "The value of `platform.node()`. Takes a regex." }, "state": { "type": "string", "description": "The state of the build, one of `sdist`, `wheel`, `editable`, `metadata_wheel`, and `metadata_editable`. Takes a regex." }, "from-sdist": { "type": "boolean", "description": "Whether the build is from an sdist." }, "failed": { "type": "boolean", "description": "Matches if the build fails. A build will be retried if there is at least one matching override with this set to true." }, "system-cmake": { "type": "string", "description": "The version of CMake found on the system. Takes a specifier set." }, "cmake-wheel": { "type": "boolean", "description": "Whether a cmake wheel is known to be provided for this system." }, "abi-flags": { "type": "string", "description": "A sorted string of the abi flags. Takes a regex." }, "env": { "type": "object", "patternProperties": { ".*": { "oneOf": [ { "type": "string" }, { "type": "boolean" } ] } }, "additionalProperties": false, "minProperties": 1, "description": "A table of environment variables mapped to either string regexs, or booleans. Valid 'truthy' environment variables are case insensitive `true`, `on`, `yes`, `y`, `t`, or a number more than 0." } } }, "inherit": { "enum": [ "none", "append", "prepend" ], "default": "none" } } } scikit-build-core-0.11.1/src/scikit_build_core/settings/000077500000000000000000000000001477275177200232325ustar00rootroot00000000000000scikit-build-core-0.11.1/src/scikit_build_core/settings/__init__.py000066400000000000000000000000741477275177200253440ustar00rootroot00000000000000from __future__ import annotations __all__: list[str] = [] scikit-build-core-0.11.1/src/scikit_build_core/settings/_load_provider.py000066400000000000000000000041311477275177200265730ustar00rootroot00000000000000from __future__ import annotations import importlib import sys from pathlib import Path from typing import TYPE_CHECKING, Any, Protocol, Union if TYPE_CHECKING: from collections.abc import Generator, Iterable, Mapping __all__ = ["load_dynamic_metadata", "load_provider"] def __dir__() -> list[str]: return __all__ class DynamicMetadataProtocol(Protocol): def dynamic_metadata( self, fields: Iterable[str], settings: dict[str, Any] ) -> dict[str, Any]: ... class DynamicMetadataRequirementsProtocol(DynamicMetadataProtocol, Protocol): def get_requires_for_dynamic_metadata( self, settings: dict[str, Any] ) -> list[str]: ... class DynamicMetadataWheelProtocol(DynamicMetadataProtocol, Protocol): def dynamic_wheel( self, field: str, settings: Mapping[str, Any] | None = None ) -> bool: ... class DynamicMetadataRequirementsWheelProtocol( DynamicMetadataRequirementsProtocol, DynamicMetadataWheelProtocol, Protocol ): ... DMProtocols = Union[ DynamicMetadataProtocol, DynamicMetadataRequirementsProtocol, DynamicMetadataWheelProtocol, DynamicMetadataRequirementsWheelProtocol, ] def load_provider( provider: str, provider_path: str | None = None, ) -> DMProtocols: if provider_path is None: return importlib.import_module(provider) if not Path(provider_path).is_dir(): msg = "provider-path must be an existing directory" raise AssertionError(msg) try: sys.path.insert(0, provider_path) return importlib.import_module(provider) finally: sys.path.pop(0) def load_dynamic_metadata( metadata: Mapping[str, Mapping[str, str]], ) -> Generator[tuple[str, DMProtocols | None, dict[str, str]], None, None]: for field, orig_config in metadata.items(): if "provider" in orig_config: config = dict(orig_config) provider = config.pop("provider") provider_path = config.pop("provider-path", None) yield field, load_provider(provider, provider_path), config else: yield field, None, dict(orig_config) scikit-build-core-0.11.1/src/scikit_build_core/settings/auto_cmake_version.py000066400000000000000000000012361477275177200274630ustar00rootroot00000000000000from __future__ import annotations from ..ast.ast import parse from ..ast.tokenizer import tokenize __all__ = ["find_min_cmake_version"] def __dir__() -> list[str]: return __all__ def find_min_cmake_version(cmake_content: str) -> str | None: """ Locate the minimum required version. Return None if not found. """ for node in parse(tokenize(cmake_content)): if node.name == "cmake_minimum_required": return ( node.value.replace("VERSION", "") .replace("FATAL_ERROR", "") .split("...")[0] .strip() .strip("\"'[]=") ) return None scikit-build-core-0.11.1/src/scikit_build_core/settings/auto_requires.py000066400000000000000000000026701477275177200265000ustar00rootroot00000000000000from __future__ import annotations from typing import TYPE_CHECKING from packaging.requirements import Requirement from packaging.utils import canonicalize_name from packaging.version import Version if TYPE_CHECKING: from collections.abc import Iterable from packaging.specifiers import Specifier __all__ = ["get_min_requires"] def __dir__() -> list[str]: return __all__ def get_min_requires(package: str, reqlist: Iterable[str]) -> Version | None: """ Read the build requirements from a pyproject.toml file and return the minimum version required for the given package. Returns None if no minimum version is discovered. """ norm_package = canonicalize_name(package) requires = [Requirement(req) for req in reqlist] versions = ( min_from_spec(v) for req in requires if canonicalize_name(req.name) == norm_package and (req.marker is None or req.marker.evaluate()) for v in req.specifier ) return min((v for v in versions if v is not None), default=None) def min_from_spec(spec: Specifier) -> Version | None: """ Return the minimum version from a specifier. Returns None if no minimum version is discovered. ">" technically doesn't include the minimum, but any larger version is acceptable, so it is treated as the minimum. """ if spec.operator in {">=", ">", "==", "~="}: return Version(spec.version) return None scikit-build-core-0.11.1/src/scikit_build_core/settings/documentation.py000066400000000000000000000052501477275177200264570ustar00rootroot00000000000000from __future__ import annotations import ast import dataclasses import inspect import textwrap from pathlib import Path from typing import TYPE_CHECKING from packaging.specifiers import SpecifierSet from packaging.version import Version from .._compat.typing import get_args, get_origin if TYPE_CHECKING: from collections.abc import Generator __all__ = ["pull_docs"] def __dir__() -> list[str]: return __all__ def _get_value(value: ast.expr) -> str: assert isinstance(value, ast.Constant) assert isinstance(value.value, str) return value.value def pull_docs(dc: type[object]) -> dict[str, str]: """ Pulls documentation from a dataclass. """ t = ast.parse(inspect.getsource(dc)) (obody,) = t.body assert isinstance(obody, ast.ClassDef) body = obody.body return { assign.target.id: textwrap.dedent(_get_value(expr.value)) # type: ignore[union-attr] .strip() .replace("\n", " ") for assign, expr in zip(body[:-1], body[1:]) if isinstance(assign, ast.AnnAssign) and isinstance(expr, ast.Expr) } @dataclasses.dataclass class DCDoc: name: str default: str docs: str def __str__(self) -> str: docs = "\n".join(f"# {s}" for s in textwrap.wrap(self.docs, width=78)) return f"{docs}\n{self.name} = {self.default}\n" def mk_docs(dc: type[object], prefix: str = "") -> Generator[DCDoc, None, None]: """ Makes documentation for a dataclass. """ assert dataclasses.is_dataclass(dc) docs = pull_docs(dc) for field in dataclasses.fields(dc): field_type = field.type if isinstance(field_type, type) and dataclasses.is_dataclass(field_type): yield from mk_docs(field_type, prefix=f"{prefix}{field.name}.") continue if get_origin(field.type) is list: field_type = get_args(field.type)[0] if isinstance(field_type, type) and dataclasses.is_dataclass(field_type): yield from mk_docs(field_type, prefix=f"{prefix}{field.name}[].") continue if field.default is not dataclasses.MISSING and field.default is not None: default = repr( str(field.default) if isinstance(field.default, (Path, Version, SpecifierSet)) else field.default ) elif field.default_factory is not dataclasses.MISSING: default = repr(field.default_factory()) else: default = '""' yield DCDoc( f"{prefix}{field.name}".replace("_", "-"), default.replace("'", '"').replace("True", "true").replace("False", "false"), docs[field.name], ) scikit-build-core-0.11.1/src/scikit_build_core/settings/json_schema.py000066400000000000000000000123011477275177200260720ustar00rootroot00000000000000from __future__ import annotations import dataclasses import sys from pathlib import Path from typing import Any, Literal, Union from packaging.specifiers import SpecifierSet from packaging.version import Version from .._compat.builtins import ExceptionGroup from .._compat.typing import Annotated, get_args, get_origin from .documentation import pull_docs __all__ = ["FailedConversionError", "convert_type", "to_json_schema"] def __dir__() -> list[str]: return __all__ class FailedConversionError(TypeError): pass def to_json_schema(dclass: type[Any], *, normalize_keys: bool) -> dict[str, Any]: assert dataclasses.is_dataclass(dclass) props = {} errs = [] required = [] for field in dataclasses.fields(dclass): field_type = field.type if isinstance(field_type, type) and dataclasses.is_dataclass(field.type): props[field.name] = to_json_schema( field_type, normalize_keys=normalize_keys ) continue if get_origin(field.type) is Annotated: if get_args(field.type)[1] == "EnvVar": full = convert_type( get_args(field.type)[0], normalize_keys=normalize_keys ) types = full["patternProperties"][".+"] full["patternProperties"][".+"] = { "oneOf": [ types, { "type": "object", "additionalProperties": False, "required": ["env"], "properties": { "env": {"type": "string", "minLength": 1}, "default": types, }, }, ] } props[field.name] = full continue msg = "Only EnvVar is supported for Annotated" raise FailedConversionError(msg) try: props[field.name] = convert_type(field.type, normalize_keys=normalize_keys) except FailedConversionError as err: if sys.version_info < (3, 11): notes = "__notes__" # set so linter's won't try to be clever setattr(err, notes, [*getattr(err, notes, []), f"Field: {field.name}"]) else: # pylint: disable-next=no-member err.add_note(f"Field: {field.name}") errs.append(err) continue if field.default is not dataclasses.MISSING and field.default is not None: props[field.name]["default"] = ( str(field.default) if isinstance(field.default, (Version, Path, SpecifierSet)) else field.default ) if ( field.default_factory is dataclasses.MISSING and field.default is dataclasses.MISSING ): required.append(field.name) if errs: msg = f"Failed Conversion to JSON Schema on {dclass.__name__}" raise ExceptionGroup(msg, errs) docs = pull_docs(dclass) for k, v in docs.items(): props[k]["description"] = v if "DEPRECATED" in v: props[k]["deprecated"] = True if normalize_keys: props = {k.replace("_", "-"): v for k, v in props.items()} if required: return { "type": "object", "additionalProperties": False, "required": required, "properties": props, } return {"type": "object", "additionalProperties": False, "properties": props} def convert_type(t: Any, *, normalize_keys: bool) -> dict[str, Any]: if isinstance(t, type) and dataclasses.is_dataclass(t): return to_json_schema(t, normalize_keys=normalize_keys) if t is str or t is Path or t is Version or t is SpecifierSet: return {"type": "string"} if t is bool: return {"type": "boolean"} origin = get_origin(t) args = get_args(t) if origin is list: assert len(args) == 1 return { "type": "array", "items": convert_type(args[0], normalize_keys=normalize_keys), } if origin is dict: assert len(args) == 2 assert args[0] is str if args[1] is Any: return {"type": "object"} return { "type": "object", "patternProperties": { ".+": convert_type(args[1], normalize_keys=normalize_keys) }, } if origin is Union: # Ignore optional if len(args) == 2 and any(a is type(None) for a in args): return convert_type( next(iter(a for a in args if a is not type(None))), normalize_keys=normalize_keys, ) return { "oneOf": [ convert_type(a, normalize_keys=normalize_keys) for a in args if a is not type(None) ] } if origin is Literal: return {"enum": list(args)} if hasattr(t, "json_schema"): return convert_type(t.json_schema, normalize_keys=normalize_keys) msg = f"Cannot convert type {t} to JSON Schema" raise FailedConversionError(msg) scikit-build-core-0.11.1/src/scikit_build_core/settings/skbuild_docs.py000066400000000000000000000016641477275177200262600ustar00rootroot00000000000000from __future__ import annotations from .. import __version__ from .documentation import mk_docs from .skbuild_model import ScikitBuildSettings __all__ = ["mk_skbuild_docs"] def __dir__() -> list[str]: return __all__ version = ".".join(__version__.split(".")[:2]) INV = {"cmake.minimum-version", "ninja.minimum-version"} def mk_skbuild_docs() -> str: """ Makes documentation for the skbuild model. """ items = [x for x in mk_docs(ScikitBuildSettings) if x.name not in INV] for item in items: if item.name == "minimum-version": item.default = f'"{version}" # current version' if item.name == "install.strip": item.default = "true" if item.name == "wheel.packages": item.default = '["src/", "python/", ""]' return "\n".join(str(item) for item in items) if __name__ == "__main__": print(mk_skbuild_docs()) # noqa: T201 scikit-build-core-0.11.1/src/scikit_build_core/settings/skbuild_model.py000066400000000000000000000271451477275177200264320ustar00rootroot00000000000000import dataclasses from pathlib import Path from typing import Any, Dict, List, Literal, Optional, Union from packaging.specifiers import SpecifierSet from packaging.version import Version from .._compat.typing import Annotated __all__ = [ "BackportSettings", "BuildSettings", "CMakeSettings", "CMakeSettingsDefine", "EditableSettings", "GenerateSettings", "InstallSettings", "LoggingSettings", "MessagesSettings", "NinjaSettings", "SDistSettings", "ScikitBuildSettings", "SearchSettings", "WheelSettings", ] def __dir__() -> List[str]: return __all__ class CMakeSettingsDefine(str): """ A str subtype for automatically normalizing bool and list values to the CMake representation in the `cmake.define` settings key. """ json_schema = Union[str, bool, List[str]] def __new__(cls, raw: Union[str, bool, List[str]]) -> "CMakeSettingsDefine": def escape_semicolons(item: str) -> str: return item.replace(";", r"\;") if isinstance(raw, bool): value = "TRUE" if raw else "FALSE" elif isinstance(raw, list): value = ";".join(map(escape_semicolons, raw)) else: value = raw return super().__new__(cls, value) @dataclasses.dataclass class CMakeSettings: minimum_version: Optional[Version] = None """ DEPRECATED in 0.8; use version instead. """ version: Optional[SpecifierSet] = None """ The versions of CMake to allow. If CMake is not present on the system or does not pass this specifier, it will be downloaded via PyPI if possible. An empty string will disable this check. The default on 0.10+ is "CMakeLists.txt", which will read it from the project's CMakeLists.txt file, or ">=3.15" if unreadable or <0.10. """ args: List[str] = dataclasses.field(default_factory=list) """ A list of args to pass to CMake when configuring the project. Setting this in config or envvar will override toml. See also ``cmake.define``. """ define: Annotated[Dict[str, CMakeSettingsDefine], "EnvVar"] = dataclasses.field( default_factory=dict ) """ A table of defines to pass to CMake when configuring the project. Additive. """ verbose: Optional[bool] = None """ DEPRECATED in 0.10, use build.verbose instead. """ build_type: str = "Release" """ The build type to use when building the project. Valid options are: "Debug", "Release", "RelWithDebInfo", "MinSizeRel", "", etc. """ source_dir: Path = Path() """ The source directory to use when building the project. Currently only affects the native builder (not the setuptools plugin). """ targets: Optional[List[str]] = None """ DEPRECATED in 0.10; use build.targets instead. """ @dataclasses.dataclass class SearchSettings: site_packages: bool = True """ Add the python build environment site_packages folder to the CMake prefix paths. """ @dataclasses.dataclass class NinjaSettings: minimum_version: Optional[Version] = None """ DEPRECATED in 0.8; use version instead. """ version: SpecifierSet = SpecifierSet(">=1.5") """ The versions of Ninja to allow. If Ninja is not present on the system or does not pass this specifier, it will be downloaded via PyPI if possible. An empty string will disable this check. """ make_fallback: bool = True """ If Ninja is not present on the system or is older than required, it will be downloaded via PyPI if this is false. """ @dataclasses.dataclass class LoggingSettings: level: Literal["NOTSET", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = ( "WARNING" ) """ The logging level to display, "DEBUG", "INFO", "WARNING", and "ERROR" are possible options. """ @dataclasses.dataclass class SDistSettings: include: List[str] = dataclasses.field(default_factory=list) """ Files to include in the SDist even if they are skipped by default. Supports gitignore syntax. """ exclude: List[str] = dataclasses.field(default_factory=list) """ Files to exclude from the SDist even if they are included by default. Supports gitignore syntax. """ reproducible: bool = True """ If set to True, try to build a reproducible distribution (Unix and Python 3.9+ recommended). ``SOURCE_DATE_EPOCH`` will be used for timestamps, or a fixed value if not set. """ cmake: bool = False """ If set to True, CMake will be run before building the SDist. """ @dataclasses.dataclass class WheelSettings: packages: Optional[Union[List[str], Dict[str, str]]] = None """ A list of packages to auto-copy into the wheel. If this is not set, it will default to the first of ``src/``, ``python/``, or ```` if they exist. The prefix(s) will be stripped from the package name inside the wheel. If a dict, provides a mapping of package name to source directory. """ py_api: str = "" """ The Python tags. The default (empty string) will use the default Python version. You can also set this to "cp38" to enable the CPython 3.8+ Stable ABI / Limited API (only on CPython and if the version is sufficient, otherwise this has no effect). Or you can set it to "py3" or "py2.py3" to ignore Python ABI compatibility. The ABI tag is inferred from this tag. """ expand_macos_universal_tags: bool = False """ Fill out extra tags that are not required. This adds "x86_64" and "arm64" to the list of platforms when "universal2" is used, which helps older Pip's (before 21.0.1) find the correct wheel. """ install_dir: str = "" """ The install directory for the wheel. This is relative to the platlib root. You might set this to the package name. The original dir is still at SKBUILD_PLATLIB_DIR (also SKBUILD_DATA_DIR, etc. are available). EXPERIMENTAL: An absolute path will be one level higher than the platlib root, giving access to "/platlib", "/data", "/headers", and "/scripts". """ license_files: Optional[List[str]] = None """ A list of license files to include in the wheel. Supports glob patterns. The default is ``["LICEN[CS]E*", "COPYING*", "NOTICE*", "AUTHORS*"]``. Must not be set if ``project.license-files`` is set. """ cmake: bool = True """ If set to True (the default), CMake will be run before building the wheel. """ platlib: Optional[bool] = None """ Target the platlib or the purelib. If not set, the default is to target the platlib if wheel.cmake is true, and the purelib otherwise. """ exclude: List[str] = dataclasses.field(default_factory=list) """ A set of patterns to exclude from the wheel. This is additive to the SDist exclude patterns. This applies to the final paths in the wheel, and can exclude files from CMake output as well. Editable installs may not respect this exclusion. """ build_tag: str = "" """ The build tag to use for the wheel. If empty, no build tag is used. """ @dataclasses.dataclass class BackportSettings: find_python: Version = Version("3.26.1") """ If CMake is less than this value, backport a copy of FindPython. Set to 0 disable this, or the empty string. """ @dataclasses.dataclass class EditableSettings: mode: Literal["redirect", "inplace"] = "redirect" """ Select the editable mode to use. Can be "redirect" (default) or "inplace". """ verbose: bool = True """ Turn on verbose output for the editable mode rebuilds. """ rebuild: bool = False """ Rebuild the project when the package is imported. The build-directory must be set. """ @dataclasses.dataclass class BuildSettings: tool_args: List[str] = dataclasses.field(default_factory=list) """ Extra args to pass directly to the builder in the build step. """ targets: List[str] = dataclasses.field(default_factory=list) """ The build targets to use when building the project. Empty builds the default target. """ verbose: bool = False """ Verbose printout when building. """ requires: List[str] = dataclasses.field(default_factory=list) """ Additional ``build-system.requires``. Intended to be used in combination with ``overrides``. """ @dataclasses.dataclass class InstallSettings: components: List[str] = dataclasses.field(default_factory=list) """ The components to install. If empty, all default components are installed. """ strip: Optional[bool] = None """ Whether to strip the binaries. True for release builds on scikit-build-core 0.5+ (0.5-0.10.5 also incorrectly set this for debug builds). """ @dataclasses.dataclass class GenerateSettings: path: Path """ The path (relative to platlib) for the file to generate. """ template: str = "" """ The template to use for the file. This includes string.Template style placeholders for all the metadata. If empty, a template-path must be set. """ template_path: Optional[Path] = None """ The path to the template file. If empty, a template must be set. """ location: Literal["install", "build", "source"] = "install" """ The place to put the generated file. The "build" directory is useful for CMake files, and the "install" directory is useful for Python files, usually. You can also write directly to the "source" directory, will overwrite existing files & remember to gitignore the file. """ @dataclasses.dataclass class MessagesSettings: """ Settings for messages. """ after_failure: str = "" """ A message to print after a build failure. """ after_success: str = "" """ A message to print after a successful build. """ @dataclasses.dataclass class ScikitBuildSettings: cmake: CMakeSettings = dataclasses.field(default_factory=CMakeSettings) ninja: NinjaSettings = dataclasses.field(default_factory=NinjaSettings) logging: LoggingSettings = dataclasses.field(default_factory=LoggingSettings) sdist: SDistSettings = dataclasses.field(default_factory=SDistSettings) wheel: WheelSettings = dataclasses.field(default_factory=WheelSettings) backport: BackportSettings = dataclasses.field(default_factory=BackportSettings) editable: EditableSettings = dataclasses.field(default_factory=EditableSettings) build: BuildSettings = dataclasses.field(default_factory=BuildSettings) install: InstallSettings = dataclasses.field(default_factory=InstallSettings) generate: List[GenerateSettings] = dataclasses.field(default_factory=list) messages: MessagesSettings = dataclasses.field(default_factory=MessagesSettings) search: SearchSettings = dataclasses.field(default_factory=SearchSettings) metadata: Dict[str, Dict[str, Any]] = dataclasses.field(default_factory=dict) """ List dynamic metadata fields and hook locations in this table. """ strict_config: bool = True """ Strictly check all config options. If False, warnings will be printed for unknown options. If True, an error will be raised. """ experimental: bool = False """ Enable early previews of features not finalized yet. """ minimum_version: Optional[Version] = None """ If set, this will provide a method for backward compatibility. """ build_dir: str = "" """ The build directory. Defaults to a temporary directory, but can be set. """ fail: bool = False """ Immediately fail the build. This is only useful in overrides. """ scikit-build-core-0.11.1/src/scikit_build_core/settings/skbuild_overrides.py000066400000000000000000000313171477275177200273300ustar00rootroot00000000000000from __future__ import annotations import os import platform import re import sys from pathlib import Path from typing import TYPE_CHECKING, Any, Literal import packaging.tags from packaging.specifiers import SpecifierSet from .. import __version__ from .._compat import tomllib from .._logging import logger from ..builder.sysconfig import get_abi_flags from ..cmake import CMake from ..errors import CMakeNotFoundError from ..resources import resources __all__ = ["process_overides", "regex_match"] def __dir__() -> list[str]: return __all__ if TYPE_CHECKING: from collections.abc import Mapping def strtobool(value: str) -> bool: """ Converts a environment variable string into a boolean value. """ if not value: return False value = value.lower() if value.isdigit(): return bool(int(value)) return value not in {"n", "no", "off", "false", "f"} def version_match(value: str, match: str, name: str) -> str: """ Returns a non-empty string if a version matches a specifier. """ matcher = SpecifierSet(match) did_match = matcher.contains(value) return f"{match!r} matched {name} {value}" if did_match else "" def regex_match(value: str, match: str) -> str: """ Returns a non-empty string if a value matches a regex. """ did_match = re.compile(match).search(value) is not None return f"{match!r} matched {value!r}" if did_match else "" def override_match( *, current_env: Mapping[str, str] | None, current_state: Literal[ "sdist", "wheel", "editable", "metadata_wheel", "metadata_editable" ], has_dist_info: bool, retry: bool, python_version: str | None = None, implementation_name: str | None = None, implementation_version: str | None = None, platform_system: str | None = None, platform_machine: str | None = None, platform_node: str | None = None, env: dict[str, str] | None = None, state: str | None = None, from_sdist: bool | None = None, failed: bool | None = None, system_cmake: str | None = None, cmake_wheel: bool | None = None, abi_flags: str | None = None, scikit_build_version: str | None = None, **unknown: Any, ) -> tuple[dict[str, str], set[str], dict[str, Any]]: """ Check if the current environment matches the overrides. Returns a dict of passed matches, with reasons for values, and a set of non-matches. """ passed_dict = {} failed_set: set[str] = set() if current_env is None: current_env = os.environ if scikit_build_version is not None: current_version = __version__ match_msg = version_match( current_version, scikit_build_version, "scikit-build-core" ) if match_msg: passed_dict["scikit-build-version"] = match_msg else: failed_set.add("scikit-build-version") if python_version is not None: current_python_version = ".".join(str(x) for x in sys.version_info[:2]) match_msg = version_match(current_python_version, python_version, "Python") if match_msg: passed_dict["python-version"] = match_msg else: failed_set.add("python-version") if implementation_name is not None: current_impementation_name = sys.implementation.name match_msg = regex_match(current_impementation_name, implementation_name) if match_msg: passed_dict["implementation-name"] = match_msg else: failed_set.add("implementation-name") if implementation_version is not None: info = sys.implementation.version version = f"{info.major}.{info.minor}.{info.micro}" kind = info.releaselevel if kind != "final": version += f"{kind[0]}{info.serial}" match_msg = version_match( version, implementation_version, "Python implementation" ) if match_msg: passed_dict["implementation-version"] = match_msg else: failed_set.add("implementation-version") if platform_system is not None: current_platform_system = sys.platform match_msg = regex_match(current_platform_system, platform_system) if match_msg: passed_dict["platform-system"] = match_msg else: failed_set.add("platform-system") if platform_machine is not None: current_platform_machine = platform.machine() match_msg = regex_match(current_platform_machine, platform_machine) if match_msg: passed_dict["platform-machine"] = match_msg else: failed_set.add("platform-machine") if platform_node is not None: current_platform_node = platform.node() match_msg = regex_match(current_platform_node, platform_node) if match_msg: passed_dict["platform-node"] = match_msg else: failed_set.add("platform-node") if state is not None: match_msg = regex_match(current_state, state) if match_msg: passed_dict["state"] = match_msg else: failed_set.add("state") if failed is not None: if failed and retry: passed_dict["failed"] = "Previous run failed" elif not failed and not retry: passed_dict["failed"] = "Running on a fresh run" else: failed_set.add("failed") if from_sdist is not None: if has_dist_info: if from_sdist: passed_dict["from-sdist"] = "from sdist due to PKG-INFO" else: failed_set.add("from-sdist") elif not from_sdist: passed_dict["from-sdist"] = "not from sdist, no PKG-INFO" else: failed_set.add("from-sdist") if system_cmake is not None: try: cmake = CMake.default_search( version=SpecifierSet(system_cmake), module=False, env=current_env ) passed_dict["system-cmake"] = ( f"system cmake {cmake.version} found at {cmake.cmake_path} passing {system_cmake}" ) except CMakeNotFoundError: failed_set.add("system-cmake") if cmake_wheel is not None: with resources.joinpath("known_wheels.toml").open("rb") as f: known_wheels_toml = tomllib.load(f) known_cmake_wheels = set( known_wheels_toml["tool"]["scikit-build"]["cmake"]["known-wheels"] ) cmake_plat = known_cmake_wheels.intersection(packaging.tags.sys_tags()) if cmake_plat: passed_dict["cmake-wheel"] = f"cmake wheel available on {cmake_plat}" else: failed_set.add("cmake-wheel") if abi_flags is not None: current_abi_flags = get_abi_flags() match_msg = regex_match(current_abi_flags, abi_flags) if match_msg: passed_dict["abi-flags"] = match_msg else: failed_set.add("abi-flags") if env: for key, value in env.items(): if isinstance(value, bool): if strtobool(current_env.get(key, "")) == value: passed_dict[f"env.{key}"] = f"env {key} is {value}" else: failed_set.add(f"env.{key}") elif key not in current_env: failed_set.add(f"env.{key}") else: current_value = current_env[key] match_msg = regex_match(current_value, value) if match_msg: passed_dict[f"env.{key}"] = f"env {key}: {match_msg}" else: failed_set.add(f"env.{key}") if len(passed_dict) + len(failed_set) + len(unknown) < 1: msg = "At least one override must be provided" raise ValueError(msg) return passed_dict, failed_set, unknown def inherit_join( value: list[str] | dict[str, str] | str | int | bool, previous: list[str] | dict[str, str] | str | int | bool | None, mode: str, ) -> list[str] | dict[str, str] | str | int | bool: if mode not in {"none", "append", "prepend"}: msg = "Only 'none', 'append', and 'prepend' supported for inherit" raise TypeError(msg) if mode == "none" or previous is None: return value if isinstance(previous, list) and isinstance(value, list): return [*previous, *value] if mode == "append" else [*value, *previous] if isinstance(previous, dict) and isinstance(value, dict): return {**previous, **value} if mode == "append" else {**value, **previous} msg = "Append/prepend modes can only be used on lists or dicts" raise TypeError(msg) def process_overides( tool_skb: dict[str, Any], *, state: Literal["sdist", "wheel", "editable", "metadata_wheel", "metadata_editable"], retry: bool, env: Mapping[str, str] | None = None, ) -> set[str]: """ Process overrides into the main dictionary if they match. Modifies the input dictionary. Must be run from the package directory. """ has_dist_info = Path("PKG-INFO").is_file() global_matched: set[str] = set() for override in tool_skb.pop("overrides", []): passed_any: dict[str, str] | None = None passed_all: dict[str, str] | None = None unknown: set[str] = set() failed_any: set[str] = set() failed_all: set[str] = set() if_override = override.pop("if", None) if not if_override: msg = "At least one 'if' override must be provided" raise KeyError(msg) if not isinstance(if_override, dict): msg = "'if' override must be a table" raise TypeError(msg) if "any" in if_override: any_override = if_override.pop("any") select = {k.replace("-", "_"): v for k, v in any_override.items()} passed_any, failed_any, unknown_any = override_match( current_env=env, current_state=state, has_dist_info=has_dist_info, retry=retry, **select, ) unknown |= set(unknown_any) inherit_override = override.pop("inherit", {}) if not isinstance(inherit_override, dict): msg = "'inherit' override must be a table" raise TypeError(msg) select = {k.replace("-", "_"): v for k, v in if_override.items()} if select: passed_all, failed_all, unknown_all = override_match( current_env=env, current_state=state, has_dist_info=has_dist_info, retry=retry, **select, ) unknown |= set(unknown_all) # Verify no unknown options are present unless scikit-build-version is specified passed_or_failed = { *(passed_all or {}), *(passed_any or {}), *failed_all, *failed_any, } if "scikit-build-version" not in passed_or_failed and unknown: msg = f"Unknown overrides: {', '.join(unknown)}" raise TypeError(msg) # If no overrides are passed, do nothing if passed_any is None and passed_all is None: continue # If normal overrides are passed and one or more fails, do nothing if passed_all is not None and failed_all: continue # If any is passed, at least one always needs to pass. if passed_any is not None and not passed_any: continue local_matched = set(passed_any or []) | set(passed_all or []) global_matched |= local_matched if local_matched: if unknown: msg = f"Unknown overrides: {', '.join(unknown)}" raise TypeError(msg) all_str = " and ".join( [ *(passed_all or {}).values(), *([" or ".join(passed_any.values())] if passed_any else []), ] ) logger.info("Overrides {}", all_str) for key, value in override.items(): inherit1 = inherit_override.get(key, {}) if isinstance(value, dict): for key2, value2 in value.items(): inherit2 = inherit1.get(key2, "none") inner = tool_skb.get(key, {}) inner[key2] = inherit_join( value2, inner.get(key2, None), inherit2 ) tool_skb[key] = inner else: inherit_override_tmp = inherit_override or "none" if isinstance(inherit_override_tmp, dict): assert not inherit_override_tmp tool_skb[key] = inherit_join( value, tool_skb.get(key), inherit_override_tmp ) return global_matched scikit-build-core-0.11.1/src/scikit_build_core/settings/skbuild_read_settings.py000066400000000000000000000337021477275177200301610ustar00rootroot00000000000000from __future__ import annotations import copy import dataclasses import difflib import sys from pathlib import Path from typing import TYPE_CHECKING, Any, Literal, TypeVar from packaging.specifiers import SpecifierSet from packaging.version import Version from .. import __version__ from .._compat import tomllib from .._logging import logger, rich_error, rich_print, rich_warning from ..errors import CMakeConfigError from .auto_cmake_version import find_min_cmake_version from .auto_requires import get_min_requires from .skbuild_model import CMakeSettings, NinjaSettings, ScikitBuildSettings from .skbuild_overrides import process_overides from .sources import ConfSource, EnvSource, SourceChain, TOMLSource if TYPE_CHECKING: import os from collections.abc import Generator, Mapping __all__ = ["SettingsReader"] def __dir__() -> list[str]: return __all__ T = TypeVar("T") def _handle_minimum_version( dc: CMakeSettings | NinjaSettings, minimum_version: Version | None, default: str = "", ) -> None: """ Handle the minimum version option. Supports scikit-build-core < 0.8 style minimum_version. Prints an error message and exits. """ name = "cmake" if isinstance(dc, CMakeSettings) else "ninja" version_default = next( iter(f for f in dataclasses.fields(dc) if f.name == "version") ).default if version_default is None: if not default: msg = "Default version must be provided for this function if None is the default" raise AssertionError(msg) version_default = SpecifierSet(f">={default}") # Check for minimum_version < 0.8 and the modern version setting if ( dc.version is not None and dc.version != version_default and minimum_version is not None and minimum_version < Version("0.8") ): rich_error( f"Cannot set {name}.version if minimum-version is set to less than 0.8 (which is where it was introduced)" ) # Backwards compatibility for minimum_version if dc.minimum_version is not None: msg = f"Use {name}.version instead of {name}.minimum-version with scikit-build-core >= 0.8" if minimum_version is None: rich_warning(msg) elif minimum_version >= Version("0.8"): rich_error(msg) if dc.version is not None and dc.version != version_default: rich_error( f"Cannot set both {name}.minimum_version and {name}.version; use version only for scikit-build-core >= 0.8." ) dc.version = SpecifierSet(f">={dc.minimum_version}") if dc.version is None: dc.version = SpecifierSet(f">={default}") def _handle_move( before_name: str, before: T | None, after_name: str, after: T, minimum_version: Version | None, introduced_in: Version, ) -> T: """ Backward_compat for moving names around. The default must be false-like. """ if after and minimum_version is not None and minimum_version < introduced_in: rich_error( f"Cannot set {after_name} if minimum-version is set to less than {introduced_in} (which is where it was introduced)" ) if ( before is not None and minimum_version is not None and minimum_version >= introduced_in ): rich_error( f"Cannot set {before_name} if minimum-version is set to {introduced_in} or higher" ) if before is not None and after: rich_error(f"Cannot set {before_name} and {after_name} at the same time") if before is None: return after if minimum_version is None: rich_warning( f"Use {after_name} instead of {before_name} for scikit-build-core >= {introduced_in}" ) return before class SettingsReader: def __init__( self, pyproject: dict[str, Any], config_settings: Mapping[str, str | list[str]], *, state: Literal[ "sdist", "wheel", "editable", "metadata_wheel", "metadata_editable" ], extra_settings: Mapping[str, Any] | None = None, verify_conf: bool = True, env: Mapping[str, str] | None = None, retry: bool = False, ) -> None: self.state = state # Handle overrides pyproject = copy.deepcopy(pyproject) self.overrides = process_overides( pyproject.get("tool", {}).get("scikit-build", {}), state=state, env=env, retry=retry, ) # Support for minimum-version='build-system.requires' tmp_min_v = ( pyproject.get("tool", {}) .get("scikit-build", {}) .get("minimum-version", None) ) if tmp_min_v == "build-system.requires": reqlist = pyproject["build-system"]["requires"] min_v = get_min_requires("scikit-build-core", reqlist) if min_v is None: rich_error( "scikit-build-core needs a min version in " "build-system.requires to use minimum-version='build-system.requires'" ) pyproject["tool"]["scikit-build"]["minimum-version"] = str(min_v) toml_srcs = [TOMLSource("tool", "scikit-build", settings=pyproject)] # Support for cmake.version='CMakeLists.txt' # We will save the value for now since we need the CMakeLists location force_auto_cmake = ( pyproject.get("tool", {}) .get("scikit-build", {}) .get("cmake", {}) .get("version", None) ) == "CMakeLists.txt" if force_auto_cmake: del pyproject["tool"]["scikit-build"]["cmake"]["version"] if extra_settings is not None: extra_skb = copy.deepcopy(dict(extra_settings)) process_overides(extra_skb, state=state, env=env, retry=retry) toml_srcs.insert(0, TOMLSource(settings=extra_skb)) prefixed = { k: v for k, v in config_settings.items() if k.startswith("skbuild.") } remaining = { k: v for k, v in config_settings.items() if not k.startswith("skbuild.") } self.sources = SourceChain( EnvSource("SKBUILD"), ConfSource("skbuild", settings=prefixed, verify=verify_conf), ConfSource(settings=remaining, verify=verify_conf), *toml_srcs, prefixes=["tool", "scikit-build"], ) self.settings = self.sources.convert_target(ScikitBuildSettings) if self.settings.minimum_version: current_version = Version(__version__) minimum_version = self.settings.minimum_version if current_version < minimum_version: msg = ( f"scikit-build-core version {__version__} is too old. " f"Minimum required version is {self.settings.minimum_version}." ) raise CMakeConfigError(msg) if isinstance(self.settings.wheel.packages, dict): for key, value in self.settings.wheel.packages.items(): if Path(key).name != Path(value).name: rich_error( "wheel.packages table must match in the last component of the paths" ) if self.settings.editable.rebuild: if self.settings.editable.mode == "inplace": rich_error("editable rebuild is incompatible with inplace mode") if not self.settings.build_dir: rich_error("editable mode with rebuild requires build-dir") install_policy = ( self.settings.minimum_version is None or self.settings.minimum_version >= Version("0.5") ) if self.settings.install.strip is None: self.settings.install.strip = ( install_policy and self.settings.cmake.build_type in {"Release", "MinSizeRel"} ) # If we noted earlier that auto-cmake was requested, handle it now if ( self.settings.cmake.version is None and self.settings.cmake.minimum_version is None and ( self.settings.minimum_version is None or self.settings.minimum_version >= Version("0.10") ) ): cmake_path = self.settings.cmake.source_dir / "CMakeLists.txt" try: with cmake_path.open(encoding="utf-8-sig") as f: new_min_cmake = find_min_cmake_version(f.read()) except FileNotFoundError: new_min_cmake = "3.15" rich_warning( "CMakeLists.txt not found when looking for minimum CMake version. " "Report this or (and) set manually to avoid this warning. Using 3.15 as a fall-back." ) if new_min_cmake is None: if force_auto_cmake: rich_error( "Minimum CMake version set as " "'CMakeLists.txt' wasn't able to find minimum version setting. " "If the CMakeLists.txt is valid, this might be a bug in our search algorithm." ) rich_warning( "Minimum CMake version not found in CMakeLists.txt. " "If the CMakeLists.txt is valid, this might be a bug in our search algorithm. Report " "this or (and) set manually to avoid this warning." ) new_min_cmake = "3.15" if Version(new_min_cmake) < Version("3.15"): rich_warning( "Minimum CMake version set as 'CMakeLists.txt' is less than 3.15. " "This is not supported by scikit-build-core; set manually or increase to avoid this warning." ) new_min_cmake = "3.15" self.settings.cmake.version = SpecifierSet(f">={new_min_cmake}") _handle_minimum_version( self.settings.cmake, self.settings.minimum_version, "3.15" ) _handle_minimum_version(self.settings.ninja, self.settings.minimum_version) self.settings.build.verbose = _handle_move( "cmake.verbose", self.settings.cmake.verbose, "build.verbose", self.settings.build.verbose, self.settings.minimum_version, Version("0.10"), ) self.settings.build.targets = _handle_move( "cmake.targets", self.settings.cmake.targets, "build.targets", self.settings.build.targets, self.settings.minimum_version, Version("0.10"), ) def unrecognized_options(self) -> Generator[str, None, None]: return self.sources.unrecognized_options(ScikitBuildSettings) def suggestions(self, index: int) -> dict[str, list[str]]: all_options = list(self.sources[index].all_option_names(ScikitBuildSettings)) result: dict[str, list[str]] = { k: [] for k in self.sources[index].unrecognized_options(ScikitBuildSettings) } for option in result: possibilities = { ".".join(k.split(".")[: option.count(".") + 1]) for k in all_options } result[option] = difflib.get_close_matches(option, possibilities, n=3) return result def print_suggestions(self) -> None: for index in (1, 2, 3): name = {1: "config-settings", 2: "config-settings", 3: "pyproject.toml"}[ index ] suggestions_dict = self.suggestions(index) if suggestions_dict: rich_print( f"{{bold.red}}ERROR:{{normal}} Unrecognized options in {name}:" ) for option, suggestions in suggestions_dict.items(): rich_print(f" {{red}}{option}", end="") if suggestions: sugstr = ", ".join(suggestions) rich_print(f"{{yellow}} -> Did you mean: {sugstr}?", end="") rich_print() def validate_may_exit(self) -> None: unrecognized = list(self.unrecognized_options()) if unrecognized: if self.settings.strict_config: sys.stdout.flush() self.print_suggestions() raise SystemExit(7) logger.warning("Unrecognized options: {}", ", ".join(unrecognized)) for key, value in self.settings.metadata.items(): if "provider" not in value: sys.stdout.flush() rich_error(f"provider= must be provided in {key!r}:") if not self.settings.experimental and ( "provider-path" in value or not value["provider"].startswith("scikit_build_core.") ): sys.stdout.flush() rich_error( "experimental must be enabled currently to use plugins not provided by scikit-build-core" ) for gen in self.settings.generate: if not gen.template and not gen.template_path: sys.stdout.flush() rich_error("template= or template-path= must be provided in generate") @classmethod def from_file( cls, pyproject_path: os.PathLike[str] | str, config_settings: Mapping[str, str | list[str]] | None = None, *, state: Literal[ "sdist", "wheel", "editable", "metadata_wheel", "metadata_editable" ] = "sdist", verify_conf: bool = True, extra_settings: Mapping[str, Any] | None = None, env: Mapping[str, str] | None = None, retry: bool = False, ) -> SettingsReader: with Path(pyproject_path).open("rb") as f: pyproject = tomllib.load(f) return cls( pyproject, config_settings or {}, verify_conf=verify_conf, state=state, extra_settings=extra_settings, env=env, retry=retry, ) scikit-build-core-0.11.1/src/scikit_build_core/settings/skbuild_schema.py000066400000000000000000000171511477275177200265660ustar00rootroot00000000000000from __future__ import annotations import copy import json from typing import Any from ..resources import resources __all__ = ["generate_skbuild_schema", "get_skbuild_schema"] def __dir__() -> list[str]: return __all__ METADATA = [ "version", "description", "license", "readme", "requires-python", "dependencies", "optional-dependencies", "entrypoints", "authors", "maintainers", "urls", "classifiers", "keywords", "scripts", "gui-scripts", ] def generate_skbuild_schema(tool_name: str = "scikit-build") -> dict[str, Any]: "Generate the complete schema for scikit-build settings." assert tool_name == "scikit-build", "Only scikit-build is supported." from .json_schema import to_json_schema from .skbuild_model import ScikitBuildSettings schema = { "$schema": "http://json-schema.org/draft-07/schema", "$id": "https://github.com/scikit-build/scikit-build-core/blob/main/src/scikit_build_core/resources/scikit-build.schema.json", "description": "Scikit-build-core's settings.", **to_json_schema(ScikitBuildSettings, normalize_keys=True), } # Manipulate a bit to get better validation # This is making the generate's template or template-path required generate = schema["properties"]["generate"]["items"] for prop in generate["properties"].values(): if prop.get("type", "") == "string": prop["minLength"] = 1 generate_tmpl = copy.deepcopy(generate) generate_path = copy.deepcopy(generate) generate_tmpl["required"] = ["path", "template"] del generate_tmpl["properties"]["template-path"] del generate_tmpl["properties"]["template"]["default"] generate_path["required"] = ["path", "template-path"] del generate_path["properties"]["template"] schema["properties"]["generate"]["items"] = { "oneOf": [generate_tmpl, generate_path] } schema["$defs"] = { "metadata": { "type": "object", "properties": { "provider": {"type": "string"}, "provider-path": {"type": "string"}, }, } } del schema["properties"]["metadata"]["patternProperties"] schema["properties"]["metadata"]["additionalProperties"] = False schema["properties"]["metadata"]["properties"] = { m: {"$ref": "#/$defs/metadata"} for m in METADATA } props = {k: {"$ref": f"#/properties/{k}"} for k in schema["properties"]} schema["$defs"]["if_overrides"] = { "type": "object", "minProperties": 1, "additionalProperties": False, "properties": { "scikit-build-version": { "type": "string", "description": "The version of scikit-build-version. Takes a specifier set.", }, "python-version": { "type": "string", "description": "The two-digit Python version. Takes a specifier set.", }, "implementation-name": { "type": "string", "description": "The value of `sys.implementation.name`. Takes a regex", }, "implementation-version": { "type": "string", "description": "Derived from `sys.implementation.version`, following PEP 508. Takes a specifier set.", }, "platform-system": { "type": "string", "description": "The value of `sys.platform`. Takes a regex.", }, "platform-machine": { "type": "string", "description": "The value of `platform.machine()`. Takes a regex.", }, "platform-node": { "type": "string", "description": "The value of `platform.node()`. Takes a regex.", }, "state": { "type": "string", "description": "The state of the build, one of `sdist`, `wheel`, `editable`, `metadata_wheel`, and `metadata_editable`. Takes a regex.", }, "from-sdist": { "type": "boolean", "description": "Whether the build is from an sdist.", }, "failed": { "type": "boolean", "description": "Matches if the build fails. A build will be retried if there is at least one matching override with this set to true.", }, "system-cmake": { "type": "string", "description": "The version of CMake found on the system. Takes a specifier set.", }, "cmake-wheel": { "type": "boolean", "description": "Whether a cmake wheel is known to be provided for this system.", }, "abi-flags": { "type": "string", "description": "A sorted string of the abi flags. Takes a regex.", }, "env": { "type": "object", "patternProperties": { ".*": {"oneOf": [{"type": "string"}, {"type": "boolean"}]} }, "additionalProperties": False, "minProperties": 1, "description": "A table of environment variables mapped to either string regexs, or booleans. Valid 'truthy' environment variables are case insensitive `true`, `on`, `yes`, `y`, `t`, or a number more than 0.", }, }, } schema["$defs"]["inherit"] = { "enum": ["none", "append", "prepend"], "default": "none", } inherit_props: dict[str, Any] = { k: { kk: {"$ref": "#/$defs/inherit"} for kk, vv in v["properties"].items() if vv.get("type", "") in {"object", "array"} or any( vvv.get("type", "") in {"object", "array"} for vvv in vv.get("oneOf", {}) ) } for k, v in schema["properties"].items() if v.get("type", "") == "object" } inherit_props = { k: {"type": "object", "additionalProperties": False, "properties": v} for k, v in inherit_props.items() if v } schema["properties"]["overrides"] = { "type": "array", "description": "A list of overrides to apply to the settings, based on the `if` selector.", "items": { "type": "object", "required": ["if"], "minProperties": 2, "additionalProperties": False, "properties": { "if": { "anyOf": [ {"$ref": "#/$defs/if_overrides"}, { "type": "object", "properties": {"any": {"$ref": "#/$defs/if_overrides"}}, "required": ["any"], "additionalProperties": False, }, ] }, "inherit": { "type": "object", "properties": inherit_props, "additionalProperties": False, }, **props, }, }, } return schema def get_skbuild_schema(tool_name: str = "scikit-build") -> dict[str, Any]: "Get the stored complete schema for scikit-build settings." assert tool_name == "scikit-build", "Only scikit-build is supported." with resources.joinpath("scikit-build.schema.json").open(encoding="utf-8") as f: return json.load(f) # type: ignore[no-any-return] if __name__ == "__main__": d = generate_skbuild_schema() print(json.dumps(d, indent=2)) # noqa: T201 scikit-build-core-0.11.1/src/scikit_build_core/settings/sources.py000066400000000000000000000567271477275177200253100ustar00rootroot00000000000000""" This is the configuration tooling for scikit-build-core. This is build around the :class:`Source` Protocol. Sources are created with some input (like a toml file for the :class:`TOMLSource`). Sources also usually have some prefix (like ``tool.scikit-build``) as well. The :class:`SourceChain` holds a collection of Sources, and is the primary way to use them. An end user interacts with :class:`SourceChain` via ``.convert_target``, which takes a Dataclass class and returns an instance with fields populated. Example of usage:: sources = SourceChain(TOMLSource("tool", "mypackage", settings=pyproject_dict), ...) settings = sources.convert_target(SomeSettingsModel) unrecognized_options = list(source.unrecognized_options(SomeSettingsModel) Naming conventions: - ``model`` is the complete Dataclass. - ``target`` is the type to convert a single item to. - ``settings`` is the input data source (unless it already has a name, like ``env``). - ``options`` are the names of the items in the ``model``, formatted in the style of the current Source. - ``fields`` are the tuple of strings describing a nested field in the ``model``. When setting up your dataclasses, these types are handled: - ``str``: A string type, nothing special. - ``bool``: Supports bool in TOML, not handled in envvar/config (so only useful in a Union) - Any callable (`Path`, `Version`): Passed the string input. - ``Optional[T]``: Treated like T. Default should be None, since no input format supports None's. - ``Union[str, ...]``: Supports other input types in TOML form (bool currently). Otherwise a string. - ``List[T]``: A list of items. `;` separated supported in EnvVar/config forms. T can be a dataclass (TOML only). - ``Dict[str, T]``: A table of items. TOML supports a layer of nesting. Any is supported as an item type. - ``Union[list[T], Dict[str, T]]`` (TOML only): A list or dict of items. - ``Literal[...]``: A list of strings, the result must be in the list. - ``Annotated[Dict[...], "EnvVar"]``: A dict of items, where each item can be a string or a dict with "env" and "default" keys. These are supported for JSON schema generation for the TOML, as well. Integers/floats would be easy to add, but haven't been needed yet. """ from __future__ import annotations import dataclasses import os import typing from typing import Any, Literal, Protocol, TypeVar, Union from .._compat.builtins import ExceptionGroup from .._compat.typing import Annotated, get_args, get_origin if typing.TYPE_CHECKING: from collections.abc import Generator, Iterator, Mapping, Sequence T = TypeVar("T") __all__ = ["ConfSource", "EnvSource", "Source", "SourceChain", "TOMLSource"] def __dir__() -> list[str]: return __all__ def _dig_strict(_dict: Mapping[str, Any], /, *names: str) -> Any: for name in names: _dict = _dict[name] return _dict def _process_bool(value: str) -> bool: return value.strip().lower() not in {"0", "false", "off", "no", ""} def _dig_not_strict(_dict: Mapping[str, Any], /, *names: str) -> Any: for name in names: _dict = _dict.get(name, {}) return _dict def _dig_fields(opt: Any, /, *names: str) -> Any: for name in names: fields = dataclasses.fields(opt) types = [x.type for x in fields if x.name == name] if len(types) != 1: msg = f"Could not access {'.'.join(names)}" raise KeyError(msg) (opt,) = types return opt def _process_union(target: type[Any], /) -> Any: """ Filters None out of Unions. If a Union only has one item, return that item. """ origin = get_origin(target) if origin is Union: non_none_args = [a for a in get_args(target) if a is not type(None)] if len(non_none_args) == 1: return non_none_args[0] return Union[tuple(non_none_args)] return target def _process_annotated(target: type[Any], /) -> tuple[Any, tuple[Any, ...]]: """ Splits annotated into raw type and annotations. If not annotated, the annotations will be empty. """ origin = get_origin(target) if origin is Annotated: return get_args(target)[0], get_args(target)[1:] return target, () def _get_target_raw_type(target: type[Any] | Any, /) -> Any: """ Takes a type like ``Optional[str]`` and returns str, or ``Optional[Dict[str, int]]`` and returns dict. Returns Union for a Union with more than one non-none type. Literal is also a valid return. Works through Annotated. """ target, _ = _process_annotated(target) target = _process_union(target) origin = get_origin(target) return origin or target def _get_inner_type(_target: type[Any], /) -> type[Any]: """ Takes a types like ``List[str]`` and returns str, or ``Dict[str, int]`` and returns int. """ raw_target = _get_target_raw_type(_target) target = _process_union(_target) if raw_target is list: return get_args(target)[0] # type: ignore[no-any-return] if raw_target is dict: return get_args(target)[1] # type: ignore[no-any-return] msg = f"Expected a list or dict, got {target!r}" raise AssertionError(msg) def _nested_dataclass_to_names( target: type[Any] | Any, /, *inner: str ) -> Iterator[list[str]]: """ Yields each entry, like ``("a", "b", "c")`` for ``a.b.c``. """ if isinstance(target, type) and dataclasses.is_dataclass(target): for field in dataclasses.fields(target): yield from _nested_dataclass_to_names(field.type, *inner, field.name) else: yield list(inner) def _dict_with_envvar(target: Any, /) -> Any: """ This produces values. Supports "env" and "default" keys. """ if not isinstance(target, dict): return target env = target["env"] default = target.get("default", None) value = os.environ.get(env, default) if isinstance(default, bool) and isinstance(value, str): return _process_bool(value) return value class Source(Protocol): def has_item(self, *fields: str, is_dict: bool) -> bool: """ Check if the source contains a chain of fields. For example, ``fields = [Field(name="a"), Field(name="b")]`` will check if the source contains the key "a.b". ``is_dict`` should be set if it can be nested. """ ... def get_item(self, *fields: str, is_dict: bool) -> Any: """ Select an item from a chain of fields. Raises KeyError if the there is no item. ``is_dict`` should be set if it can be nested. """ ... @classmethod def convert(cls, item: Any, target: type[Any] | Any) -> object: """ Convert an ``item`` from the base representation of the source's source into a ``target`` type. Raises TypeError if the conversion fails. """ ... def unrecognized_options(self, options: object) -> Generator[str, None, None]: """ Given a model, produce an iterator of all unrecognized option names. Empty iterator if this can't be computed for the source (like for environment variables). """ ... def all_option_names(self, target: type[Any]) -> Iterator[str]: """ Given a model, produce a list of all possible names (used for producing suggestions). """ ... class EnvSource: """ This is a source using environment variables. """ def __init__(self, prefix: str, *, env: Mapping[str, str] | None = None) -> None: self.env = env or os.environ self.prefix = prefix def _get_name(self, *fields: str) -> str: names = [field.upper() for field in fields] return "_".join([self.prefix, *names] if self.prefix else names) def has_item(self, *fields: str, is_dict: bool) -> bool: # noqa: ARG002 name = self._get_name(*fields) return bool(self.env.get(name, "")) def get_item( self, *fields: str, is_dict: bool, # noqa: ARG002 ) -> str | dict[str, str]: name = self._get_name(*fields) if name in self.env: return self.env[name] msg = f"{name!r} not found in environment" raise KeyError(msg) @classmethod def convert(cls, item: str, target: type[Any] | Any) -> object: """ Convert an item from the environment (always a string) into a target type. """ target, _ = _process_annotated(target) raw_target = _get_target_raw_type(target) if dataclasses.is_dataclass(raw_target): msg = f"Array of dataclasses are not supported in configuration settings ({raw_target})" raise TypeError(msg) if raw_target is list: return [ cls.convert(i.strip(), _get_inner_type(target)) for i in item.split(";") ] if raw_target is dict: items = (i.strip().split("=") for i in item.split(";")) return {k: cls.convert(v, _get_inner_type(target)) for k, v in items} if raw_target is bool: return _process_bool(item) if raw_target is Union and str in get_args(target): return item if raw_target is Union: args = {_get_target_raw_type(t): t for t in get_args(target)} if str in args: return item if dict in args and "=" in item: items = (i.strip().split("=") for i in item.split(";")) return { k: cls.convert(v, _get_inner_type(args[dict])) for k, v in items } if list in args: return [ cls.convert(i.strip(), _get_inner_type(args[list])) for i in item.split(";") ] msg = f"Can't convert into {target}" raise TypeError(msg) if raw_target is Literal: if item not in get_args(_process_union(target)): msg = f"{item!r} not in {get_args(_process_union(target))!r}" raise TypeError(msg) return item if callable(raw_target): return raw_target(item) msg = f"Can't convert target {target}" raise TypeError(msg) @staticmethod def unrecognized_options( options: object, # noqa: ARG004 ) -> Generator[str, None, None]: yield from () def all_option_names(self, target: type[Any]) -> Iterator[str]: prefix = [self.prefix] if self.prefix else [] for names in _nested_dataclass_to_names(target): yield "_".join(prefix + names).upper() def _unrecognized_dict( settings: Mapping[str, Any], options: Any, above: Sequence[str] ) -> Generator[str, None, None]: for keystr in settings: # We don't have DataclassInstance exposed in typing yet matches = [ x for x in dataclasses.fields(options) if x.name.replace("_", "-") == keystr ] if not matches: yield ".".join((*above, keystr)) continue (inner_option_field,) = matches inner_option = inner_option_field.type if isinstance(inner_option, type) and dataclasses.is_dataclass(inner_option): yield from _unrecognized_dict( settings[keystr], inner_option, (*above, keystr) ) class ConfSource: """ This is a source for the PEP 517 configuration settings. You should initialize it with a dict from PEP 517. a.b will be treated as nested dicts. "verify" is a boolean that determines whether unrecognized options should be checked for. Only set this to false if this might be sharing config options at the same level. While most mechanisms (pip, uv, build) only support text, gpep517 allows an arbitrary json input, so this currently also handles bools. """ def __init__( self, *prefixes: str, settings: Mapping[str, str | list[str] | bool], verify: bool = True, ) -> None: self.prefixes = prefixes self.settings = settings self.verify = verify def _get_name(self, *fields: str) -> list[str]: names = [field.replace("_", "-") for field in fields] return [*self.prefixes, *names] def has_item(self, *fields: str, is_dict: bool) -> bool: names = self._get_name(*fields) name = ".".join(names) if is_dict: return any(k.startswith(f"{name}.") for k in self.settings) return name in self.settings def get_item( self, *fields: str, is_dict: bool ) -> str | list[str] | dict[str, str] | bool: names = self._get_name(*fields) name = ".".join(names) if is_dict: d = { k[len(name) + 1 :]: str(v) for k, v in self.settings.items() if k.startswith(f"{name}.") } if d: return d msg = f"Dict items {name}.* not found in settings" raise KeyError(msg) if name in self.settings: return self.settings[name] msg = f"{name!r} not found in configuration settings" raise KeyError(msg) @classmethod def convert( cls, item: str | list[str] | dict[str, str] | bool, target: type[Any] | Any ) -> object: target, _ = _process_annotated(target) raw_target = _get_target_raw_type(target) if dataclasses.is_dataclass(raw_target): msg = f"Array of dataclasses are not supported in configuration settings ({raw_target})" raise TypeError(msg) if raw_target is list: if isinstance(item, list): return [cls.convert(i, _get_inner_type(target)) for i in item] if isinstance(item, (dict, bool)): msg = f"Expected {target}, got {type(item)}" raise TypeError(msg) return [ cls.convert(i.strip(), _get_inner_type(target)) for i in item.split(";") ] if raw_target is dict: assert not isinstance(item, (str, list, bool)) return {k: cls.convert(v, _get_inner_type(target)) for k, v in item.items()} if raw_target is Union: args = {_get_target_raw_type(t): t for t in get_args(target)} if str in args: return item if dict in args and isinstance(item, dict): return { k: cls.convert(v, _get_inner_type(args[dict])) for k, v in item.items() } if list in args and isinstance(item, list): return [cls.convert(i, _get_inner_type(args[list])) for i in item] msg = f"Can't convert into {target}" raise TypeError(msg) if isinstance(item, (list, dict)): msg = f"Expected {target}, got {type(item).__name__}" raise TypeError(msg) if raw_target is bool: return item if isinstance(item, bool) else _process_bool(item) if raw_target is Literal: if item not in get_args(_process_union(target)): msg = f"{item!r} not in {get_args(_process_union(target))!r}" raise TypeError(msg) return item if callable(raw_target): return raw_target(item) msg = f"Can't convert target {target}" raise TypeError(msg) def unrecognized_options(self, options: object) -> Generator[str, None, None]: if not self.verify: return for keystr in self.settings: keys = keystr.replace("-", "_").split(".")[len(self.prefixes) :] try: outer_option = _dig_fields(options, *keys[:-1]) except KeyError: yield ".".join(keystr.split(".")[:-1]) continue if dataclasses.is_dataclass(outer_option): try: _dig_fields(outer_option, keys[-1]) except KeyError: yield keystr continue if _get_target_raw_type(outer_option) is dict: continue def all_option_names(self, target: type[Any]) -> Iterator[str]: for names in _nested_dataclass_to_names(target): dash_names = [name.replace("_", "-") for name in names] yield ".".join((*self.prefixes, *dash_names)) class TOMLSource: def __init__(self, *prefixes: str, settings: Mapping[str, Any]) -> None: self.prefixes = prefixes self.settings = _dig_not_strict(settings, *prefixes) @staticmethod def _get_name(*fields: str) -> list[str]: return [field.replace("_", "-") for field in fields] def has_item(self, *fields: str, is_dict: bool) -> bool: # noqa: ARG002 names = self._get_name(*fields) try: _dig_strict(self.settings, *names) except KeyError: return False return True def get_item(self, *fields: str, is_dict: bool) -> Any: # noqa: ARG002 names = self._get_name(*fields) try: return _dig_strict(self.settings, *names) except KeyError: msg = f"{names!r} not found in configuration settings" raise KeyError(msg) from None @classmethod def convert(cls, item: Any, target: type[Any] | Any) -> object: """ Convert an ``item`` from TOML into a ``target`` type. """ target, annotations = _process_annotated(target) raw_target = _get_target_raw_type(target) if isinstance(raw_target, type) and dataclasses.is_dataclass(raw_target): fields = dataclasses.fields(raw_target) values = ((k.replace("-", "_"), v) for k, v in item.items()) return raw_target( **{ k: cls.convert(v, *[f.type for f in fields if f.name == k]) for k, v in values } ) if raw_target is list: if not isinstance(item, list): msg = f"Expected {target}, got {type(item).__name__}" raise TypeError(msg) return [cls.convert(it, _get_inner_type(target)) for it in item] if raw_target is dict: if not isinstance(item, dict): msg = f"Expected {target}, got {type(item).__name__}" raise TypeError(msg) if annotations == ("EnvVar",): return { k: cls.convert(_dict_with_envvar(v), _get_inner_type(target)) for k, v in item.items() if _dict_with_envvar(v) is not None } return {k: cls.convert(v, _get_inner_type(target)) for k, v in item.items()} if raw_target is Any: return item if raw_target is Union: args = {_get_target_raw_type(t): t for t in get_args(target)} if type(item) in args: if isinstance(item, dict): return { k: cls.convert(v, _get_inner_type(args[dict])) for k, v in item.items() } if isinstance(item, list): return [cls.convert(i, _get_inner_type(args[list])) for i in item] return item if raw_target is Literal: if item not in get_args(_process_union(target)): msg = f"{item!r} not in {get_args(_process_union(target))!r}" raise TypeError(msg) return item if callable(raw_target): return raw_target(item) msg = f"Can't convert target {target}" raise TypeError(msg) def unrecognized_options(self, options: object) -> Generator[str, None, None]: yield from _unrecognized_dict(self.settings, options, self.prefixes) def all_option_names(self, target: type[Any]) -> Iterator[str]: for names in _nested_dataclass_to_names(target): dash_names = [name.replace("_", "-") for name in names] yield ".".join((*self.prefixes, *dash_names)) class SourceChain: def __init__(self, *sources: Source, prefixes: Sequence[str] = ()) -> None: """ Combine a collection of sources into a single object that can run ``convert_target(dataclass)``. An optional list of prefixes can be given that will be prepended (dot separated) to error messages. """ self.sources = sources self.prefixes = prefixes def __getitem__(self, index: int) -> Source: return self.sources[index] def has_item(self, *fields: str, is_dict: bool) -> bool: return any(source.has_item(*fields, is_dict=is_dict) for source in self.sources) def get_item(self, *fields: str, is_dict: bool) -> Any: for source in self.sources: if source.has_item(*fields, is_dict=is_dict): return source.get_item(*fields, is_dict=is_dict) msg = f"{fields!r} not found in any source" raise KeyError(msg) def convert_target(self, target: type[T], *prefixes: str) -> T: """ Given a dataclass type, create an object of that dataclass filled with the values in the sources. """ errors = [] prep: dict[str, Any] = {} for field in dataclasses.fields(target): # type: ignore[arg-type] if isinstance(field.type, type) and dataclasses.is_dataclass(field.type): try: prep[field.name] = self.convert_target( field.type, *prefixes, field.name ) except Exception as e: name = ".".join([*self.prefixes, *prefixes, field.name]) e.__notes__ = [*getattr(e, "__notes__", []), f"Field: {name}"] # type: ignore[attr-defined] errors.append(e) continue is_dict = _get_target_raw_type(field.type) is dict for source in self.sources: if source.has_item(*prefixes, field.name, is_dict=is_dict): simple = source.get_item(*prefixes, field.name, is_dict=is_dict) try: tmp = source.convert(simple, field.type) except Exception as e: name = ".".join([*self.prefixes, *prefixes, field.name]) e.__notes__ = [*getattr(e, "__notes__", []), f"Field {name}"] # type: ignore[attr-defined] errors.append(e) prep[field.name] = None break if is_dict: assert isinstance(tmp, dict), f"{field.name} must be a dict" prep[field.name] = {**tmp, **prep.get(field.name, {})} continue prep[field.name] = tmp break if field.name in prep: continue if field.default is not dataclasses.MISSING: prep[field.name] = field.default continue if field.default_factory is not dataclasses.MISSING: prep[field.name] = field.default_factory() continue errors.append(ValueError(f"Missing value for {field.name!r}")) if errors: prefix_str = ".".join([*self.prefixes, *prefixes]) msg = f"Failed converting {prefix_str}" raise ExceptionGroup(msg, errors) return target(**prep) def unrecognized_options(self, options: object) -> Generator[str, None, None]: for source in self.sources: yield from source.unrecognized_options(options) if typing.TYPE_CHECKING: _: Source = typing.cast("EnvSource", None) _ = typing.cast("ConfSource", None) _ = typing.cast("TOMLSource", None) scikit-build-core-0.11.1/src/scikit_build_core/setuptools/000077500000000000000000000000001477275177200236135ustar00rootroot00000000000000scikit-build-core-0.11.1/src/scikit_build_core/setuptools/__init__.py000066400000000000000000000000741477275177200257250ustar00rootroot00000000000000from __future__ import annotations __all__: list[str] = [] scikit-build-core-0.11.1/src/scikit_build_core/setuptools/build_cmake.py000066400000000000000000000225241477275177200264310ustar00rootroot00000000000000from __future__ import annotations import shutil import sys from pathlib import Path from typing import TYPE_CHECKING, ClassVar, Literal import setuptools import setuptools.errors from packaging.version import Version from .._compat import tomllib from .._logging import LEVEL_VALUE, raw_logger from ..builder.builder import Builder, get_archs from ..builder.macos import normalize_macos_version from ..cmake import CMake, CMaker from ..settings.skbuild_read_settings import SettingsReader if TYPE_CHECKING: from setuptools.dist import Distribution from ..settings.skbuild_model import ScikitBuildSettings __all__ = [ "BuildCMake", "cmake_args", "cmake_install_target", "cmake_source_dir", "finalize_distribution_options", ] def __dir__() -> list[str]: return __all__ def _validate_settings(settings: ScikitBuildSettings) -> None: assert not settings.wheel.expand_macos_universal_tags, ( "wheel.expand_macos_universal_tags is not supported in setuptools mode" ) assert settings.logging.level == "WARNING", ( "Logging is not adjustable in setuptools mode yet" ) assert not settings.wheel.py_api, ( "wheel.py_api is not supported in setuptools mode, use bdist_wheel options instead" ) def get_source_dir_from_pyproject_toml() -> str | None: try: with Path("pyproject.toml").open("rb") as f: source_dir: str | None = tomllib.load(f)["tool"]["scikit-build"]["cmake"][ "source-dir" ] return source_dir except (FileNotFoundError, KeyError): return None class BuildCMake(setuptools.Command): source_dir: str | None = None cmake_args: list[str] | str | None = None cmake_install_target: str | None = None build_lib: str | None build_temp: str | None debug: bool | None editable_mode: bool parallel: int | None plat_name: str | None user_options: ClassVar[list[tuple[str, str, str]]] = [ ("build-lib=", "b", "directory for compiled extension modules"), ("build-temp=", "t", "directory for temporary files (build by-products)"), ("plat-name=", "p", "platform name to cross-compile for, if supported "), ("debug", "g", "compile/link with debugging information"), ("parallel=", "j", "number of parallel build jobs"), ("source-dir=", "s", "directory with CMakeLists.txt"), ("cmake-args=", "a", "extra arguments for CMake"), ("cmake-install-target=", "", "CMake target to install"), ] def initialize_options(self) -> None: self.build_lib = None self.build_temp = None self.debug = None self.editable_mode = False self.parallel = None self.plat_name = None self.source_dir = get_source_dir_from_pyproject_toml() self.cmake_args = None self.cmake_install_target = None def finalize_options(self) -> None: self.set_undefined_options( "build_ext", ("build_lib", "build_lib"), ("build_temp", "build_temp"), ("debug", "debug"), ("parallel", "parallel"), ("plat_name", "plat_name"), ) if isinstance(self.cmake_args, str): self.cmake_args = [ b.strip() for a in self.cmake_args.split() for b in a.split(";") ] def run(self) -> None: assert self.build_lib is not None assert self.build_temp is not None assert self.plat_name is not None settings = SettingsReader.from_file("pyproject.toml").settings _validate_settings(settings) build_tmp_folder = Path(self.build_temp) build_temp = build_tmp_folder / "_skbuild" dist = self.distribution dist_source_dir = getattr(self.distribution, "cmake_source_dir", None) source_dir = self.source_dir if dist_source_dir is None else dist_source_dir assert source_dir is not None, "This should not be reachable" configure_args = self.cmake_args or [] assert isinstance(configure_args, list) dist_cmake_args = getattr(self.distribution, "cmake_args", None) configure_args.extend(dist_cmake_args or []) bdist_wheel = dist.get_command_obj("bdist_wheel") assert bdist_wheel is not None limited_api = bdist_wheel.py_limited_api # TODO: this is a hack due to moving temporary paths for isolation if build_temp.exists(): shutil.rmtree(build_temp) cmake = CMake.default_search(version=settings.cmake.version) config = CMaker( cmake, source_dir=Path(source_dir), build_dir=build_temp, build_type=settings.cmake.build_type, ) builder = Builder( settings=settings, config=config, ) # Setuptools requires this be specified if there's a mismatch. if sys.platform.startswith("darwin"): arm_only = get_archs(builder.config.env) == ["arm64"] orig_macos_str = self.plat_name.rsplit("-", 1)[0].split("-", 1)[1] orig_macos = normalize_macos_version(orig_macos_str, arm=arm_only) config.env.setdefault("MACOSX_DEPLOYMENT_TARGET", str(orig_macos)) builder.config.build_type = "Debug" if self.debug else settings.cmake.build_type # Setting the install prefix because some libs hardcode CMAKE_INSTALL_PREFIX # Otherwise `cmake --install --prefix` would work by itself install_dir = Path(self.build_lib) defines = {"CMAKE_INSTALL_PREFIX": install_dir} builder.configure( name=dist.get_name(), version=Version(dist.get_version()), defines=defines, limited_api=bool(limited_api), configure_args=configure_args, ) # Set CMAKE_BUILD_PARALLEL_LEVEL to control the parallel build level # across all generators. build_args = [] # self.parallel is a way to set parallel jobs by hand using -j in the # build_ext call, not supported by pip or PyPA-build. if "CMAKE_BUILD_PARALLEL_LEVEL" not in builder.config.env and self.parallel: build_args.append(f"-j{self.parallel}") builder.build(build_args=build_args) builder.install(install_dir=install_dir) # def "get_source_file"(self) -> list[str]: # return ["CMakeLists.txt"] # def get_outputs(self) -> list[str]: # return [] # def get_output_mapping(self) -> dict[str, str]: # return {} def _has_cmake(dist: Distribution) -> bool: build_cmake = dist.get_command_obj("build_cmake") assert isinstance(build_cmake, BuildCMake) return ( build_cmake.source_dir is not None or getattr(dist, "cmake_source_dir", None) is not None ) def finalize_distribution_options(dist: Distribution) -> None: # Prepare new build_cmake command and make sure build calls it build = dist.get_command_class("build") assert build is not None if "build_cmake" not in {x for x, _ in build.sub_commands}: build.sub_commands.append( ("build_cmake", lambda cmd: _has_cmake(cmd.distribution)) ) if get_source_dir_from_pyproject_toml() is not None: _cmake_extension(dist) def _cmake_extension(dist: Distribution) -> None: # Every keyword argument needs to call this # Run this only once if getattr(dist, "_has_cmake_extensions", False): return # pylint: disable-next=protected-access dist._has_cmake_extensions = True # type: ignore[attr-defined] # Setuptools needs to know that it has extensions modules orig_has_ext_modules = dist.has_ext_modules dist.has_ext_modules = lambda: orig_has_ext_modules() or _has_cmake(dist) # type: ignore[method-assign] # Hack for stdlib distutils if not setuptools.distutils.__package__.startswith("setuptools"): # type: ignore[attr-defined] class EvilList(list): # type: ignore[type-arg] def __len__(self) -> int: return super().__len__() or int(_has_cmake(dist)) dist.ext_modules = getattr(dist, "ext_modules", []) or EvilList() # Setup logging settings = SettingsReader.from_file("pyproject.toml").settings level_value = LEVEL_VALUE[settings.logging.level] raw_logger.setLevel(level_value) def cmake_args( dist: Distribution, attr: Literal["cmake_args"], value: list[str] ) -> None: assert attr == "cmake_args" _cmake_extension(dist) if not isinstance(value, list): msg = "cmake_args must be a list" raise setuptools.errors.SetupError(msg) def cmake_source_dir( dist: Distribution, attr: Literal["cmake_source_dir"], value: str ) -> None: assert attr == "cmake_source_dir" if get_source_dir_from_pyproject_toml() is not None: msg = "cmake_source_dir is already defined in pyproject.toml" raise setuptools.errors.SetupError(msg) _cmake_extension(dist) if not Path(value).is_dir(): msg = "cmake_source_dir must be an existing directory" raise setuptools.errors.SetupError(msg) def cmake_install_target( dist: Distribution, attr: Literal["cmake_install_target"], value: str ) -> None: assert attr == "cmake_install_target" assert value is not None _cmake_extension(dist) msg = "cmake_install_target is not supported - please use components and build targets instead" raise setuptools.errors.SetupError(msg) scikit-build-core-0.11.1/src/scikit_build_core/setuptools/build_meta.py000066400000000000000000000042071477275177200262750ustar00rootroot00000000000000# pylint: disable=duplicate-code from __future__ import annotations import setuptools.build_meta from setuptools.build_meta import ( build_sdist, build_wheel, prepare_metadata_for_build_wheel, ) from ..builder.get_requires import GetRequires if hasattr(setuptools.build_meta, "build_editable"): from setuptools.build_meta import build_editable if hasattr(setuptools.build_meta, "prepare_metadata_for_build_editable"): from setuptools.build_meta import ( prepare_metadata_for_build_editable, ) __all__ = [ "build_editable", "build_sdist", "build_wheel", "get_requires_for_build_editable", "get_requires_for_build_sdist", "get_requires_for_build_wheel", "prepare_metadata_for_build_editable", "prepare_metadata_for_build_wheel", ] def __dir__() -> list[str]: return __all__ def get_requires_for_build_sdist( config_settings: dict[str, str | list[str]] | None = None, ) -> list[str]: setuptools_reqs = setuptools.build_meta.get_requires_for_build_sdist( config_settings ) requires = GetRequires.from_config_settings(config_settings) # These are only injected if cmake is required for the SDist step cmake_requires = ( [*requires.cmake(), *requires.ninja()] if requires.settings.sdist.cmake else [] ) return [*setuptools_reqs, *cmake_requires] def get_requires_for_build_wheel( config_settings: dict[str, str | list[str]] | None = None, ) -> list[str]: requires = GetRequires.from_config_settings(config_settings) setuptools_reqs = setuptools.build_meta.get_requires_for_build_wheel( config_settings ) return [*setuptools_reqs, *requires.cmake(), *requires.ninja()] if hasattr(setuptools.build_meta, "get_requires_for_build_editable"): def get_requires_for_build_editable( config_settings: dict[str, str | list[str]] | None = None, ) -> list[str]: requires = GetRequires.from_config_settings(config_settings) setuptools_reqs = setuptools.build_meta.get_requires_for_build_editable( config_settings ) return [*setuptools_reqs, *requires.cmake(), *requires.ninja()] scikit-build-core-0.11.1/src/scikit_build_core/setuptools/wrapper.py000066400000000000000000000025171477275177200256520ustar00rootroot00000000000000from __future__ import annotations import warnings from typing import TYPE_CHECKING, Any import setuptools if TYPE_CHECKING: from collections.abc import Callable, Sequence __all__ = ["setup"] def __dir__() -> list[str]: return __all__ def setup( *, cmake_args: Sequence[str] = (), cmake_install_dir: str = "", cmake_source_dir: str = "", cmake_with_sdist: bool = False, cmake_languages: Sequence[str] | None = None, cmake_minimum_required_version: str | None = None, cmake_process_manifest_hook: Callable[[list[str]], list[str]] | None = None, cmake_install_target: str = "install", **kw: Any, ) -> setuptools.Distribution: assert not cmake_install_dir, "cmake_install_dir not supported yet" assert not cmake_with_sdist, "cmake_with_sdist not supported yet" assert cmake_process_manifest_hook is None, ( "cmake_process_manifest_hook not supported yet" ) assert cmake_install_target == "install", "cmake_install_target not supported yet" if cmake_languages is not None: warnings.warn("cmake_languages no longer has any effect", stacklevel=2) if cmake_minimum_required_version is not None: warnings.warn("Set via pyproject.toml", stacklevel=2) return setuptools.setup( cmake_source_dir=cmake_source_dir, cmake_args=cmake_args, **kw ) scikit-build-core-0.11.1/tests/000077500000000000000000000000001477275177200162705ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/api/000077500000000000000000000000001477275177200170415ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/api/simple_pure/000077500000000000000000000000001477275177200213655ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/api/simple_pure/.cmake/000077500000000000000000000000001477275177200225235ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/api/simple_pure/.cmake/api/000077500000000000000000000000001477275177200232745ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/api/simple_pure/.cmake/api/v1/000077500000000000000000000000001477275177200236225ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/api/simple_pure/.cmake/api/v1/query/000077500000000000000000000000001477275177200247675ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/api/simple_pure/.cmake/api/v1/query/cache-v2000066400000000000000000000000001477275177200262700ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/api/simple_pure/.cmake/api/v1/query/cmakeFiles-v1000066400000000000000000000000001477275177200272670ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/api/simple_pure/.cmake/api/v1/query/codemodel-v2000066400000000000000000000000001477275177200271600ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/api/simple_pure/.cmake/api/v1/query/toolchains-v1000066400000000000000000000000001477275177200273670ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/api/simple_pure/.cmake/api/v1/reply/000077500000000000000000000000001477275177200247555ustar00rootroot00000000000000cache-v2-2dececcab32f1eda138d.json000066400000000000000000000502771477275177200324330ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/api/simple_pure/.cmake/api/v1/reply{ "entries" : [ { "name" : "CMAKE_ADDR2LINE", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Path to a program." } ], "type" : "FILEPATH", "value" : "CMAKE_ADDR2LINE-NOTFOUND" }, { "name" : "CMAKE_AR", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Path to a program." } ], "type" : "FILEPATH", "value" : "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ar" }, { "name" : "CMAKE_BUILD_TYPE", "properties" : [ { "name" : "HELPSTRING", "value" : "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel ..." } ], "type" : "STRING", "value" : "" }, { "name" : "CMAKE_CACHEFILE_DIR", "properties" : [ { "name" : "HELPSTRING", "value" : "This is the directory where this CMakeCache.txt was created" } ], "type" : "INTERNAL", "value" : "/Users/henryschreiner/git/scikit-build/scikit-build-core/tests/simple_pure/build" }, { "name" : "CMAKE_CACHE_MAJOR_VERSION", "properties" : [ { "name" : "HELPSTRING", "value" : "Major version of cmake used to create the current loaded cache" } ], "type" : "INTERNAL", "value" : "3" }, { "name" : "CMAKE_CACHE_MINOR_VERSION", "properties" : [ { "name" : "HELPSTRING", "value" : "Minor version of cmake used to create the current loaded cache" } ], "type" : "INTERNAL", "value" : "24" }, { "name" : "CMAKE_CACHE_PATCH_VERSION", "properties" : [ { "name" : "HELPSTRING", "value" : "Patch version of cmake used to create the current loaded cache" } ], "type" : "INTERNAL", "value" : "1" }, { "name" : "CMAKE_COMMAND", "properties" : [ { "name" : "HELPSTRING", "value" : "Path to CMake executable." } ], "type" : "INTERNAL", "value" : "/usr/local/Cellar/cmake/3.24.1/bin/cmake" }, { "name" : "CMAKE_CPACK_COMMAND", "properties" : [ { "name" : "HELPSTRING", "value" : "Path to cpack program executable." } ], "type" : "INTERNAL", "value" : "/usr/local/Cellar/cmake/3.24.1/bin/cpack" }, { "name" : "CMAKE_CTEST_COMMAND", "properties" : [ { "name" : "HELPSTRING", "value" : "Path to ctest program executable." } ], "type" : "INTERNAL", "value" : "/usr/local/Cellar/cmake/3.24.1/bin/ctest" }, { "name" : "CMAKE_CXX_COMPILER", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "CXX compiler" } ], "type" : "FILEPATH", "value" : "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++" }, { "name" : "CMAKE_CXX_FLAGS", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Flags used by the CXX compiler during all build types." } ], "type" : "STRING", "value" : "" }, { "name" : "CMAKE_CXX_FLAGS_DEBUG", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Flags used by the CXX compiler during DEBUG builds." } ], "type" : "STRING", "value" : "-g" }, { "name" : "CMAKE_CXX_FLAGS_MINSIZEREL", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Flags used by the CXX compiler during MINSIZEREL builds." } ], "type" : "STRING", "value" : "-Os -DNDEBUG" }, { "name" : "CMAKE_CXX_FLAGS_RELEASE", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Flags used by the CXX compiler during RELEASE builds." } ], "type" : "STRING", "value" : "-O3 -DNDEBUG" }, { "name" : "CMAKE_CXX_FLAGS_RELWITHDEBINFO", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Flags used by the CXX compiler during RELWITHDEBINFO builds." } ], "type" : "STRING", "value" : "-O2 -g -DNDEBUG" }, { "name" : "CMAKE_DLLTOOL", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Path to a program." } ], "type" : "FILEPATH", "value" : "CMAKE_DLLTOOL-NOTFOUND" }, { "name" : "CMAKE_EDIT_COMMAND", "properties" : [ { "name" : "HELPSTRING", "value" : "Path to cache edit program executable." } ], "type" : "INTERNAL", "value" : "/usr/local/Cellar/cmake/3.24.1/bin/ccmake" }, { "name" : "CMAKE_EXECUTABLE_FORMAT", "properties" : [ { "name" : "HELPSTRING", "value" : "Executable file format" } ], "type" : "INTERNAL", "value" : "MACHO" }, { "name" : "CMAKE_EXE_LINKER_FLAGS", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Flags used by the linker during all build types." } ], "type" : "STRING", "value" : "" }, { "name" : "CMAKE_EXE_LINKER_FLAGS_DEBUG", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Flags used by the linker during DEBUG builds." } ], "type" : "STRING", "value" : "" }, { "name" : "CMAKE_EXE_LINKER_FLAGS_MINSIZEREL", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Flags used by the linker during MINSIZEREL builds." } ], "type" : "STRING", "value" : "" }, { "name" : "CMAKE_EXE_LINKER_FLAGS_RELEASE", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Flags used by the linker during RELEASE builds." } ], "type" : "STRING", "value" : "" }, { "name" : "CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Flags used by the linker during RELWITHDEBINFO builds." } ], "type" : "STRING", "value" : "" }, { "name" : "CMAKE_EXPORT_COMPILE_COMMANDS", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Enable/Disable output of compile commands during generation." } ], "type" : "BOOL", "value" : "" }, { "name" : "CMAKE_EXTRA_GENERATOR", "properties" : [ { "name" : "HELPSTRING", "value" : "Name of external makefile project generator." } ], "type" : "INTERNAL", "value" : "" }, { "name" : "CMAKE_FIND_PACKAGE_REDIRECTS_DIR", "properties" : [ { "name" : "HELPSTRING", "value" : "Value Computed by CMake." } ], "type" : "STATIC", "value" : "/Users/henryschreiner/git/scikit-build/scikit-build-core/tests/simple_pure/build/CMakeFiles/pkgRedirects" }, { "name" : "CMAKE_GENERATOR", "properties" : [ { "name" : "HELPSTRING", "value" : "Name of generator." } ], "type" : "INTERNAL", "value" : "Ninja" }, { "name" : "CMAKE_GENERATOR_INSTANCE", "properties" : [ { "name" : "HELPSTRING", "value" : "Generator instance identifier." } ], "type" : "INTERNAL", "value" : "" }, { "name" : "CMAKE_GENERATOR_PLATFORM", "properties" : [ { "name" : "HELPSTRING", "value" : "Name of generator platform." } ], "type" : "INTERNAL", "value" : "" }, { "name" : "CMAKE_GENERATOR_TOOLSET", "properties" : [ { "name" : "HELPSTRING", "value" : "Name of generator toolset." } ], "type" : "INTERNAL", "value" : "" }, { "name" : "CMAKE_HOME_DIRECTORY", "properties" : [ { "name" : "HELPSTRING", "value" : "Source directory with the top level CMakeLists.txt file for this project" } ], "type" : "INTERNAL", "value" : "/Users/henryschreiner/git/scikit-build/scikit-build-core/tests/simple_pure" }, { "name" : "CMAKE_INSTALL_NAME_TOOL", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Path to a program." } ], "type" : "FILEPATH", "value" : "/usr/bin/install_name_tool" }, { "name" : "CMAKE_INSTALL_PREFIX", "properties" : [ { "name" : "HELPSTRING", "value" : "Install path prefix, prepended onto install directories." } ], "type" : "PATH", "value" : "/usr/local" }, { "name" : "CMAKE_LINKER", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Path to a program." } ], "type" : "FILEPATH", "value" : "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld" }, { "name" : "CMAKE_MAKE_PROGRAM", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Program used to build from build.ninja files." } ], "type" : "FILEPATH", "value" : "/usr/local/bin/ninja" }, { "name" : "CMAKE_MODULE_LINKER_FLAGS", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Flags used by the linker during the creation of modules during all build types." } ], "type" : "STRING", "value" : "" }, { "name" : "CMAKE_MODULE_LINKER_FLAGS_DEBUG", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Flags used by the linker during the creation of modules during DEBUG builds." } ], "type" : "STRING", "value" : "" }, { "name" : "CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Flags used by the linker during the creation of modules during MINSIZEREL builds." } ], "type" : "STRING", "value" : "" }, { "name" : "CMAKE_MODULE_LINKER_FLAGS_RELEASE", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Flags used by the linker during the creation of modules during RELEASE builds." } ], "type" : "STRING", "value" : "" }, { "name" : "CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Flags used by the linker during the creation of modules during RELWITHDEBINFO builds." } ], "type" : "STRING", "value" : "" }, { "name" : "CMAKE_NM", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Path to a program." } ], "type" : "FILEPATH", "value" : "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/nm" }, { "name" : "CMAKE_NUMBER_OF_MAKEFILES", "properties" : [ { "name" : "HELPSTRING", "value" : "number of local generators" } ], "type" : "INTERNAL", "value" : "1" }, { "name" : "CMAKE_OBJCOPY", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Path to a program." } ], "type" : "FILEPATH", "value" : "CMAKE_OBJCOPY-NOTFOUND" }, { "name" : "CMAKE_OBJDUMP", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Path to a program." } ], "type" : "FILEPATH", "value" : "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/objdump" }, { "name" : "CMAKE_OSX_ARCHITECTURES", "properties" : [ { "name" : "HELPSTRING", "value" : "Build architectures for OSX" } ], "type" : "STRING", "value" : "" }, { "name" : "CMAKE_OSX_DEPLOYMENT_TARGET", "properties" : [ { "name" : "HELPSTRING", "value" : "Minimum OS X version to target for deployment (at runtime); newer APIs weak linked. Set to empty string for default value." } ], "type" : "STRING", "value" : "" }, { "name" : "CMAKE_OSX_SYSROOT", "properties" : [ { "name" : "HELPSTRING", "value" : "The product will be built against the headers and libraries located inside the indicated SDK." } ], "type" : "PATH", "value" : "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk" }, { "name" : "CMAKE_PLATFORM_INFO_INITIALIZED", "properties" : [ { "name" : "HELPSTRING", "value" : "Platform information initialized" } ], "type" : "INTERNAL", "value" : "1" }, { "name" : "CMAKE_PROJECT_DESCRIPTION", "properties" : [ { "name" : "HELPSTRING", "value" : "Value Computed by CMake" } ], "type" : "STATIC", "value" : "" }, { "name" : "CMAKE_PROJECT_HOMEPAGE_URL", "properties" : [ { "name" : "HELPSTRING", "value" : "Value Computed by CMake" } ], "type" : "STATIC", "value" : "" }, { "name" : "CMAKE_PROJECT_NAME", "properties" : [ { "name" : "HELPSTRING", "value" : "Value Computed by CMake" } ], "type" : "STATIC", "value" : "simple_pure" }, { "name" : "CMAKE_RANLIB", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Path to a program." } ], "type" : "FILEPATH", "value" : "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib" }, { "name" : "CMAKE_READELF", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Path to a program." } ], "type" : "FILEPATH", "value" : "CMAKE_READELF-NOTFOUND" }, { "name" : "CMAKE_ROOT", "properties" : [ { "name" : "HELPSTRING", "value" : "Path to CMake installation." } ], "type" : "INTERNAL", "value" : "/usr/local/Cellar/cmake/3.24.1/share/cmake" }, { "name" : "CMAKE_SHARED_LINKER_FLAGS", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Flags used by the linker during the creation of shared libraries during all build types." } ], "type" : "STRING", "value" : "" }, { "name" : "CMAKE_SHARED_LINKER_FLAGS_DEBUG", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Flags used by the linker during the creation of shared libraries during DEBUG builds." } ], "type" : "STRING", "value" : "" }, { "name" : "CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Flags used by the linker during the creation of shared libraries during MINSIZEREL builds." } ], "type" : "STRING", "value" : "" }, { "name" : "CMAKE_SHARED_LINKER_FLAGS_RELEASE", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Flags used by the linker during the creation of shared libraries during RELEASE builds." } ], "type" : "STRING", "value" : "" }, { "name" : "CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Flags used by the linker during the creation of shared libraries during RELWITHDEBINFO builds." } ], "type" : "STRING", "value" : "" }, { "name" : "CMAKE_SKIP_INSTALL_RPATH", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "If set, runtime paths are not added when installing shared libraries, but are added when building." } ], "type" : "BOOL", "value" : "NO" }, { "name" : "CMAKE_SKIP_RPATH", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "If set, runtime paths are not added when using shared libraries." } ], "type" : "BOOL", "value" : "NO" }, { "name" : "CMAKE_STATIC_LINKER_FLAGS", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Flags used by the linker during the creation of static libraries during all build types." } ], "type" : "STRING", "value" : "" }, { "name" : "CMAKE_STATIC_LINKER_FLAGS_DEBUG", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Flags used by the linker during the creation of static libraries during DEBUG builds." } ], "type" : "STRING", "value" : "" }, { "name" : "CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Flags used by the linker during the creation of static libraries during MINSIZEREL builds." } ], "type" : "STRING", "value" : "" }, { "name" : "CMAKE_STATIC_LINKER_FLAGS_RELEASE", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Flags used by the linker during the creation of static libraries during RELEASE builds." } ], "type" : "STRING", "value" : "" }, { "name" : "CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Flags used by the linker during the creation of static libraries during RELWITHDEBINFO builds." } ], "type" : "STRING", "value" : "" }, { "name" : "CMAKE_STRIP", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "Path to a program." } ], "type" : "FILEPATH", "value" : "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/strip" }, { "name" : "CMAKE_UNAME", "properties" : [ { "name" : "HELPSTRING", "value" : "uname command" } ], "type" : "INTERNAL", "value" : "/usr/bin/uname" }, { "name" : "CMAKE_VERBOSE_MAKEFILE", "properties" : [ { "name" : "ADVANCED", "value" : "1" }, { "name" : "HELPSTRING", "value" : "If this value is on, makefiles will be generated without the .SILENT directive, and all commands will be echoed to the console during the make. This is useful for debugging only. With Visual Studio IDE projects all commands are done without /nologo." } ], "type" : "BOOL", "value" : "FALSE" }, { "name" : "simple_pure_BINARY_DIR", "properties" : [ { "name" : "HELPSTRING", "value" : "Value Computed by CMake" } ], "type" : "STATIC", "value" : "/Users/henryschreiner/git/scikit-build/scikit-build-core/tests/simple_pure/build" }, { "name" : "simple_pure_IS_TOP_LEVEL", "properties" : [ { "name" : "HELPSTRING", "value" : "Value Computed by CMake" } ], "type" : "STATIC", "value" : "ON" }, { "name" : "simple_pure_SOURCE_DIR", "properties" : [ { "name" : "HELPSTRING", "value" : "Value Computed by CMake" } ], "type" : "STATIC", "value" : "/Users/henryschreiner/git/scikit-build/scikit-build-core/tests/simple_pure" } ], "kind" : "cache", "version" : { "major" : 2, "minor" : 0 } } cmakeFiles-v1-74870fd87af7f965b597.json000066400000000000000000000062111477275177200327570ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/api/simple_pure/.cmake/api/v1/reply{ "inputs" : [ { "path" : "CMakeLists.txt" }, { "isGenerated" : true, "path" : "build/CMakeFiles/3.24.1/CMakeSystem.cmake" }, { "isCMake" : true, "isExternal" : true, "path" : "/usr/local/Cellar/cmake/3.24.1/share/cmake/Modules/CMakeSystemSpecificInitialize.cmake" }, { "isCMake" : true, "isExternal" : true, "path" : "/usr/local/Cellar/cmake/3.24.1/share/cmake/Modules/Platform/Darwin-Initialize.cmake" }, { "isGenerated" : true, "path" : "build/CMakeFiles/3.24.1/CMakeCXXCompiler.cmake" }, { "isCMake" : true, "isExternal" : true, "path" : "/usr/local/Cellar/cmake/3.24.1/share/cmake/Modules/CMakeSystemSpecificInformation.cmake" }, { "isCMake" : true, "isExternal" : true, "path" : "/usr/local/Cellar/cmake/3.24.1/share/cmake/Modules/CMakeGenericSystem.cmake" }, { "isCMake" : true, "isExternal" : true, "path" : "/usr/local/Cellar/cmake/3.24.1/share/cmake/Modules/CMakeInitializeConfigs.cmake" }, { "isCMake" : true, "isExternal" : true, "path" : "/usr/local/Cellar/cmake/3.24.1/share/cmake/Modules/Platform/Darwin.cmake" }, { "isCMake" : true, "isExternal" : true, "path" : "/usr/local/Cellar/cmake/3.24.1/share/cmake/Modules/Platform/UnixPaths.cmake" }, { "isCMake" : true, "isExternal" : true, "path" : "/usr/local/Cellar/cmake/3.24.1/share/cmake/Modules/CMakeCXXInformation.cmake" }, { "isCMake" : true, "isExternal" : true, "path" : "/usr/local/Cellar/cmake/3.24.1/share/cmake/Modules/CMakeLanguageInformation.cmake" }, { "isCMake" : true, "isExternal" : true, "path" : "/usr/local/Cellar/cmake/3.24.1/share/cmake/Modules/Compiler/AppleClang-CXX.cmake" }, { "isCMake" : true, "isExternal" : true, "path" : "/usr/local/Cellar/cmake/3.24.1/share/cmake/Modules/Compiler/Clang.cmake" }, { "isCMake" : true, "isExternal" : true, "path" : "/usr/local/Cellar/cmake/3.24.1/share/cmake/Modules/Compiler/CMakeCommonCompilerMacros.cmake" }, { "isCMake" : true, "isExternal" : true, "path" : "/usr/local/Cellar/cmake/3.24.1/share/cmake/Modules/Compiler/GNU.cmake" }, { "isCMake" : true, "isExternal" : true, "path" : "/usr/local/Cellar/cmake/3.24.1/share/cmake/Modules/Compiler/CMakeCommonCompilerMacros.cmake" }, { "isCMake" : true, "isExternal" : true, "path" : "/usr/local/Cellar/cmake/3.24.1/share/cmake/Modules/Platform/Apple-AppleClang-CXX.cmake" }, { "isCMake" : true, "isExternal" : true, "path" : "/usr/local/Cellar/cmake/3.24.1/share/cmake/Modules/Platform/Apple-Clang-CXX.cmake" }, { "isCMake" : true, "isExternal" : true, "path" : "/usr/local/Cellar/cmake/3.24.1/share/cmake/Modules/Platform/Apple-Clang.cmake" }, { "isCMake" : true, "isExternal" : true, "path" : "/usr/local/Cellar/cmake/3.24.1/share/cmake/Modules/CMakeCommonLanguageInclude.cmake" } ], "kind" : "cmakeFiles", "paths" : { "build" : "/Users/henryschreiner/git/scikit-build/scikit-build-core/tests/simple_pure/build", "source" : "/Users/henryschreiner/git/scikit-build/scikit-build-core/tests/simple_pure" }, "version" : { "major" : 1, "minor" : 0 } } codemodel-v2-ea39b5a28cb1a3e0a069.json000066400000000000000000000020251477275177200330100ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/api/simple_pure/.cmake/api/v1/reply{ "configurations" : [ { "directories" : [ { "build" : ".", "hasInstallRule" : true, "jsonFile" : "directory-.-5e7a28751b0c9235cbe7.json", "minimumCMakeVersion" : { "string" : "3.15" }, "projectIndex" : 0, "source" : ".", "targetIndexes" : [ 0 ] } ], "name" : "", "projects" : [ { "directoryIndexes" : [ 0 ], "name" : "simple_pure", "targetIndexes" : [ 0 ] } ], "targets" : [ { "directoryIndex" : 0, "id" : "simple_pure::@6890427a1f51a3e7e1df", "jsonFile" : "target-simple_pure-7eb73eb5c359b881e083.json", "name" : "simple_pure", "projectIndex" : 0 } ] } ], "kind" : "codemodel", "paths" : { "build" : "/Users/henryschreiner/git/scikit-build/scikit-build-core/tests/simple_pure/build", "source" : "/Users/henryschreiner/git/scikit-build/scikit-build-core/tests/simple_pure" }, "version" : { "major" : 2, "minor" : 4 } } directory-.-5e7a28751b0c9235cbe7.json000066400000000000000000000010331477275177200325440ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/api/simple_pure/.cmake/api/v1/reply{ "backtraceGraph" : { "commands" : [ "install" ], "files" : [ "CMakeLists.txt" ], "nodes" : [ { "file" : 0 }, { "command" : 0, "file" : 0, "line" : 9, "parent" : 0 } ] }, "installers" : [ { "backtrace" : 1, "component" : "Unspecified", "destination" : "bin", "paths" : [ "simple_pure" ], "targetId" : "simple_pure::@6890427a1f51a3e7e1df", "targetIndex" : 0, "type" : "target" } ], "paths" : { "build" : ".", "source" : "." } } index-2022-09-12T15-23-13-0135.json000066400000000000000000000034031477275177200312540ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/api/simple_pure/.cmake/api/v1/reply{ "cmake" : { "generator" : { "multiConfig" : false, "name" : "Ninja" }, "paths" : { "cmake" : "/usr/local/Cellar/cmake/3.24.1/bin/cmake", "cpack" : "/usr/local/Cellar/cmake/3.24.1/bin/cpack", "ctest" : "/usr/local/Cellar/cmake/3.24.1/bin/ctest", "root" : "/usr/local/Cellar/cmake/3.24.1/share/cmake" }, "version" : { "isDirty" : false, "major" : 3, "minor" : 24, "patch" : 1, "string" : "3.24.1", "suffix" : "" } }, "objects" : [ { "jsonFile" : "codemodel-v2-ea39b5a28cb1a3e0a069.json", "kind" : "codemodel", "version" : { "major" : 2, "minor" : 4 } }, { "jsonFile" : "cache-v2-2dececcab32f1eda138d.json", "kind" : "cache", "version" : { "major" : 2, "minor" : 0 } }, { "jsonFile" : "cmakeFiles-v1-74870fd87af7f965b597.json", "kind" : "cmakeFiles", "version" : { "major" : 1, "minor" : 0 } }, { "jsonFile" : "toolchains-v1-06b92b86597808b5f980.json", "kind" : "toolchains", "version" : { "major" : 1, "minor" : 0 } } ], "reply" : { "cache-v2" : { "jsonFile" : "cache-v2-2dececcab32f1eda138d.json", "kind" : "cache", "version" : { "major" : 2, "minor" : 0 } }, "cmakeFiles-v1" : { "jsonFile" : "cmakeFiles-v1-74870fd87af7f965b597.json", "kind" : "cmakeFiles", "version" : { "major" : 1, "minor" : 0 } }, "codemodel-v2" : { "jsonFile" : "codemodel-v2-ea39b5a28cb1a3e0a069.json", "kind" : "codemodel", "version" : { "major" : 2, "minor" : 4 } }, "toolchains-v1" : { "jsonFile" : "toolchains-v1-06b92b86597808b5f980.json", "kind" : "toolchains", "version" : { "major" : 1, "minor" : 0 } } } } target-simple_pure-7eb73eb5c359b881e083.json000066400000000000000000000031351477275177200342330ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/api/simple_pure/.cmake/api/v1/reply{ "artifacts" : [ { "path" : "simple_pure" } ], "backtrace" : 1, "backtraceGraph" : { "commands" : [ "add_executable", "install", "target_compile_features" ], "files" : [ "CMakeLists.txt" ], "nodes" : [ { "file" : 0 }, { "command" : 0, "file" : 0, "line" : 5, "parent" : 0 }, { "command" : 1, "file" : 0, "line" : 9, "parent" : 0 }, { "command" : 2, "file" : 0, "line" : 7, "parent" : 0 } ] }, "compileGroups" : [ { "compileCommandFragments" : [ { "fragment" : " -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk" }, { "fragment" : "-std=gnu++17" } ], "language" : "CXX", "languageStandard" : { "backtraces" : [ 3 ], "standard" : "17" }, "sourceIndexes" : [ 0 ] } ], "id" : "simple_pure::@6890427a1f51a3e7e1df", "install" : { "destinations" : [ { "backtrace" : 2, "path" : "bin" } ], "prefix" : { "path" : "/usr/local" } }, "link" : { "commandFragments" : [ { "fragment" : "", "role" : "flags" } ], "language" : "CXX" }, "name" : "simple_pure", "nameOnDisk" : "simple_pure", "paths" : { "build" : ".", "source" : "." }, "sourceGroups" : [ { "name" : "Source Files", "sourceIndexes" : [ 0 ] } ], "sources" : [ { "backtrace" : 1, "compileGroupIndex" : 0, "path" : "simple_pure.cpp", "sourceGroupIndex" : 0 } ], "type" : "EXECUTABLE" } toolchains-v1-06b92b86597808b5f980.json000066400000000000000000000026101477275177200327020ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/api/simple_pure/.cmake/api/v1/reply{ "kind" : "toolchains", "toolchains" : [ { "compiler" : { "id" : "AppleClang", "implicit" : { "includeDirectories" : [ "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk/usr/include/c++/v1", "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/13.1.6/include", "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk/usr/include", "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include" ], "linkDirectories" : [ "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk/usr/lib" ], "linkFrameworkDirectories" : [ "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk/System/Library/Frameworks" ], "linkLibraries" : [ "c++" ] }, "path" : "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++", "version" : "13.1.6.13160021" }, "language" : "CXX", "sourceFileExtensions" : [ "C", "M", "c++", "cc", "cpp", "cxx", "m", "mm", "mpp", "CPP", "ixx", "cppm" ] } ], "version" : { "major" : 1, "minor" : 0 } } scikit-build-core-0.11.1/tests/conftest.py000066400000000000000000000264611477275177200205000ustar00rootroot00000000000000from __future__ import annotations import contextlib import dataclasses import importlib.util import os import shutil import subprocess import sys import sysconfig from importlib import metadata from pathlib import Path from typing import Any, Literal, overload import virtualenv as _virtualenv if sys.version_info < (3, 11): import tomli as tomllib else: import tomllib import pytest from packaging.requirements import Requirement DIR = Path(__file__).parent.resolve() BASE = DIR.parent @pytest.fixture(scope="session") def pep518_wheelhouse(tmp_path_factory: pytest.TempPathFactory) -> Path: wheelhouse = tmp_path_factory.mktemp("wheelhouse") subprocess.run( [ sys.executable, "-m", "pip", "wheel", "--wheel-dir", str(wheelhouse), f"{BASE}", ], check=True, ) packages = [ "build", "cython", "hatchling", "pip", "pybind11", "setuptools", "virtualenv", "wheel", ] if importlib.util.find_spec("cmake") is not None: packages.append("cmake") if importlib.util.find_spec("ninja") is not None: packages.append("ninja") subprocess.run( [ sys.executable, "-m", "pip", "download", "-q", "-d", str(wheelhouse), *packages, ], check=True, ) return wheelhouse class VEnv: def __init__(self, env_dir: Path, *, wheelhouse: Path | None = None) -> None: cmd = [str(env_dir), "--no-setuptools", "--no-wheel", "--activators", ""] result = _virtualenv.cli_run(cmd, setup_logging=False) self.wheelhouse = wheelhouse self.executable = Path(result.creator.exe) self.env_dir = env_dir.resolve() self.platlib = Path( self.execute("import sysconfig; print(sysconfig.get_path('platlib'))") ) self.purelib = Path( self.execute("import sysconfig; print(sysconfig.get_path('purelib'))") ) @overload def run(self, *args: str, capture: Literal[True]) -> str: ... @overload def run(self, *args: str, capture: Literal[False] = ...) -> None: ... def run(self, *args: str, capture: bool = False) -> str | None: __tracebackhide__ = True env = os.environ.copy() paths = {str(self.executable.parent)} env["PATH"] = os.pathsep.join([*paths, env["PATH"]]) env["VIRTUAL_ENV"] = str(self.env_dir) env["PIP_DISABLE_PIP_VERSION_CHECK"] = "ON" if self.wheelhouse is not None: env["PIP_NO_INDEX"] = "ON" env["PIP_FIND_LINKS"] = str(self.wheelhouse) str_args = [os.fspath(a) for a in args] # Windows does not make a python shortcut in venv if str_args[0] in {"python", "python3"}: str_args[0] = str(self.executable) if capture: result = subprocess.run( str_args, check=False, capture_output=True, text=True, env=env, ) if result.returncode != 0: print(result.stdout, file=sys.stdout) print(result.stderr, file=sys.stderr) print("FAILED RUN:", *str_args, file=sys.stderr) raise SystemExit(result.returncode) return result.stdout.strip() result_bytes = subprocess.run( str_args, check=False, env=env, ) if result_bytes.returncode != 0: print("FAILED RUN:", *str_args, file=sys.stderr) raise SystemExit(result_bytes.returncode) return None def execute(self, command: str) -> str: return self.run(str(self.executable), "-c", command, capture=True) def module(self, *args: str) -> None: return self.run(str(self.executable), "-m", *args) def install(self, *args: str, isolated: bool = True) -> None: isolated_flags = "" if isolated else ["--no-build-isolation"] self.module("pip", "install", *isolated_flags, *args) @pytest.fixture def isolated(tmp_path: Path, pep518_wheelhouse: Path) -> VEnv: path = tmp_path / "venv" return VEnv(path, wheelhouse=pep518_wheelhouse) @pytest.fixture def virtualenv(tmp_path: Path) -> VEnv: path = tmp_path / "venv" return VEnv(path) @dataclasses.dataclass(frozen=True) class PackageInfo: name: str sdist_hash38: str | None = None sdist_hash39: str | None = None sdist_dated_hash39: str | None = None sdist_dated_hash38: str | None = None @property def sdist_hash(self) -> str | None: return self.sdist_hash38 if sys.version_info < (3, 9) else self.sdist_hash39 @property def sdist_dated_hash(self) -> str | None: return ( self.sdist_dated_hash38 if sys.version_info < (3, 9) else self.sdist_dated_hash39 ) @property def source_date_epoch(self) -> str: return "12345" def process_package( package: PackageInfo, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: package_dir = tmp_path / "pkg" shutil.copytree(DIR / "packages" / package.name, package_dir) monkeypatch.chdir(package_dir) @pytest.fixture def package_simple_pyproject_ext( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> PackageInfo: package = PackageInfo( "simple_pyproject_ext", "71b4e95854ef8d04886758d24d18fe55ebe63648310acf58c7423387cca73508", "ed930179fbf5adc2e71a64a6f9686c61fdcce477c85bc94dd51598641be886a7", "0178462b64b4eb9c41ae70eb413a9cc111c340e431b240af1b218fe81b0c2ecb", "de79895a9d5c2112257715214ab419d3635e841716655e8a55390e5d52445819", ) process_package(package, tmp_path, monkeypatch) return package @pytest.fixture def package_simple_pyproject_script_with_flags( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> PackageInfo: package = PackageInfo( "simple_pyproject_script_with_flags", ) process_package(package, tmp_path, monkeypatch) return package @pytest.fixture def package_simple_pyproject_source_dir( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> PackageInfo: package = PackageInfo( "simple_pyproject_source_dir", ) process_package(package, tmp_path, monkeypatch) return package @pytest.fixture def package_simple_setuptools_ext( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> PackageInfo: package = PackageInfo("simple_setuptools_ext") process_package(package, tmp_path, monkeypatch) return package @pytest.fixture def package_toml_setuptools_ext( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> PackageInfo: package = PackageInfo("toml_setuptools_ext") process_package(package, tmp_path, monkeypatch) return package @pytest.fixture def package_mixed_setuptools( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> PackageInfo: package = PackageInfo("mixed_setuptools") process_package(package, tmp_path, monkeypatch) return package @pytest.fixture def package_filepath_pure( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> PackageInfo: package = PackageInfo("filepath_pure") process_package(package, tmp_path, monkeypatch) return package @pytest.fixture def package_dynamic_metadata( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> PackageInfo: package = PackageInfo("dynamic_metadata") process_package(package, tmp_path, monkeypatch) return package @pytest.fixture def package_hatchling(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> PackageInfo: package = PackageInfo("hatchling") process_package(package, tmp_path, monkeypatch) return package @pytest.fixture def package_simplest_c(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> PackageInfo: package = PackageInfo( "simplest_c", ) process_package(package, tmp_path, monkeypatch) return package @pytest.fixture def navigate_editable(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> PackageInfo: package = PackageInfo( "navigate_editable", ) process_package(package, tmp_path, monkeypatch) return package @pytest.fixture def broken_fallback(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> PackageInfo: package = PackageInfo( "broken_fallback", ) process_package(package, tmp_path, monkeypatch) return package @pytest.fixture def package_sdist_config( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> PackageInfo: package = PackageInfo( "sdist_config", ) process_package(package, tmp_path, monkeypatch) return package @pytest.fixture def package_simple_purelib_package( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> PackageInfo: package = PackageInfo( "simple_purelib_package", ) process_package(package, tmp_path, monkeypatch) return package @pytest.fixture def package_pep639_pure(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> PackageInfo: package = PackageInfo( "pep639_pure", ) process_package(package, tmp_path, monkeypatch) return package def which_mock(name: str) -> str | None: if name in {"ninja", "ninja-build", "cmake3", "samu", "gmake", "make"}: return None if name == "cmake": return "cmake/path" return None @pytest.fixture def protect_get_requires(fp, monkeypatch): """ Protect get_requires from actually calling anything variable during tests. """ # This needs to be passed due to packaging.tags 22 extra checks if macos 10.16 is reported fp.pass_command([sys.executable, fp.any()]) monkeypatch.setattr(shutil, "which", which_mock) monkeypatch.delenv("CMAKE_GENERATOR", raising=False) orig_find_spec = importlib.util.find_spec def find_spec(name: str, package: str | None = None) -> Any: if name in {"cmake", "ninja"}: return None return orig_find_spec(name, package) monkeypatch.setattr(importlib.util, "find_spec", find_spec) def pytest_collection_modifyitems(items: list[pytest.Item]) -> None: for item in items: # Ensure all tests using virtualenv are marked as such if "virtualenv" in getattr(item, "fixturenames", ()): item.add_marker(pytest.mark.virtualenv) if "isolated" in getattr(item, "fixturenames", ()): item.add_marker(pytest.mark.virtualenv) item.add_marker(pytest.mark.isolated) item.add_marker(pytest.mark.network) def pytest_report_header() -> str: with BASE.joinpath("pyproject.toml").open("rb") as f: pyproject = tomllib.load(f) project = pyproject.get("project", {}) pkgs = project.get("dependencies", []) pkgs += [p for ps in project.get("optional-dependencies", {}).values() for p in ps] if "name" in project: pkgs.append(project["name"]) interesting_packages = {Requirement(p).name for p in pkgs} interesting_packages.add("pip") valid = [] for package in sorted(interesting_packages): with contextlib.suppress(ModuleNotFoundError): valid.append(f"{package}=={metadata.version(package)}") reqs = " ".join(valid) lines = [ f"installed packages of interest: {reqs}", f"sysconfig platform: {sysconfig.get_platform()}", ] return "\n".join(lines) scikit-build-core-0.11.1/tests/main.fmf000066400000000000000000000007251477275177200177120ustar00rootroot00000000000000/pytest: summary: Run pytest tests tag: [ pytest ] # TODO: filter only for network marked tests /all: summary: All pytest # TODO: Find a better way to deal with lack of git data path: / test: | # Fake a git archive cat << EOF > .git_archival.txt node: 47431d4eefbac9c3a7c49e62c73e624b932023eb node-date: 2025-02-27T16:18:39-05:00 describe-name: v0.11.0 EOF # Actually run pytest python3 -m pytest scikit-build-core-0.11.1/tests/packages/000077500000000000000000000000001477275177200200465ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/abi3_pyproject_ext/000077500000000000000000000000001477275177200236435ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/abi3_pyproject_ext/CMakeLists.txt000066400000000000000000000013061477275177200264030ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.29) project( ${SKBUILD_PROJECT_NAME} LANGUAGES C VERSION ${SKBUILD_PROJECT_VERSION}) find_package( Python COMPONENTS Interpreter Development.Module ${SKBUILD_SABI_COMPONENT} REQUIRED) if(NOT "${SKBUILD_SABI_VERSION}" STREQUAL "") python_add_library(abi3_example MODULE abi3_example.c WITH_SOABI USE_SABI ${SKBUILD_SABI_VERSION}) if(NOT SKBUILD_SABI_VERSION STREQUAL "3.8") message( FATAL_ERROR "TEST FAILED: SKBUILD_SABI_VERSION (${SKBUILD_SABI_VERSION}) is not 3.8" ) endif() else() python_add_library(abi3_example MODULE abi3_example.c WITH_SOABI) endif() install(TARGETS abi3_example DESTINATION .) scikit-build-core-0.11.1/tests/packages/abi3_pyproject_ext/abi3_example.c000066400000000000000000000013051477275177200263370ustar00rootroot00000000000000#define PY_SSIZE_T_CLEAN #include float square(float x) { return x * x; } static PyObject *square_wrapper(PyObject *self, PyObject *args) { float input, result; if (!PyArg_ParseTuple(args, "f", &input)) { return NULL; } result = square(input); return PyFloat_FromDouble(result); } static PyMethodDef abi3_example_methods[] = { {"square", square_wrapper, METH_VARARGS, "Square function"}, {NULL, NULL, 0, NULL}}; static struct PyModuleDef abi3_example_module = {PyModuleDef_HEAD_INIT, "abi3_example", NULL, -1, abi3_example_methods}; PyMODINIT_FUNC PyInit_abi3_example(void) { return PyModule_Create(&abi3_example_module); } scikit-build-core-0.11.1/tests/packages/abi3_pyproject_ext/pyproject.toml000066400000000000000000000003631477275177200265610ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core"] build-backend = "scikit_build_core.build" [project] name = "abi3-example" version = "0.0.1" [tool.scikit-build] wheel.py-api = "cp38" messages.after-success = "This is a message after success" scikit-build-core-0.11.1/tests/packages/abi3_setuptools_ext/000077500000000000000000000000001477275177200240455ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/abi3_setuptools_ext/CMakeLists.txt000066400000000000000000000005251477275177200266070ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.26) project( ${SKBUILD_PROJECT_NAME} LANGUAGES C VERSION ${SKBUILD_PROJECT_VERSION}) find_package( Python COMPONENTS Interpreter Development.SABIModule REQUIRED) python_add_library(abi3_example MODULE abi3_example.c WITH_SOABI USE_SABI 3.8) install(TARGETS abi3_example DESTINATION .) scikit-build-core-0.11.1/tests/packages/abi3_setuptools_ext/abi3_example.c000066400000000000000000000013051477275177200265410ustar00rootroot00000000000000#define PY_SSIZE_T_CLEAN #include float square(float x) { return x * x; } static PyObject *square_wrapper(PyObject *self, PyObject *args) { float input, result; if (!PyArg_ParseTuple(args, "f", &input)) { return NULL; } result = square(input); return PyFloat_FromDouble(result); } static PyMethodDef abi3_example_methods[] = { {"square", square_wrapper, METH_VARARGS, "Square function"}, {NULL, NULL, 0, NULL}}; static struct PyModuleDef abi3_example_module = {PyModuleDef_HEAD_INIT, "abi3_example", NULL, -1, abi3_example_methods}; PyMODINIT_FUNC PyInit_abi3_example(void) { return PyModule_Create(&abi3_example_module); } scikit-build-core-0.11.1/tests/packages/abi3_setuptools_ext/pyproject.toml000066400000000000000000000001701477275177200267570ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core", "setuptools"] build-backend = "scikit_build_core.setuptools.build_meta" scikit-build-core-0.11.1/tests/packages/abi3_setuptools_ext/setup.cfg000066400000000000000000000002111477275177200256600ustar00rootroot00000000000000[metadata] name = abi3_example version = 0.0.1 [options] zip_safe = False python_requires = >=3.8 [bdist_wheel] py_limited_api = cp38 scikit-build-core-0.11.1/tests/packages/abi3_setuptools_ext/setup.py000066400000000000000000000001011477275177200255470ustar00rootroot00000000000000from setuptools import setup setup( cmake_source_dir=".", ) scikit-build-core-0.11.1/tests/packages/broken_fallback/000077500000000000000000000000001477275177200231455ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/broken_fallback/CMakeLists.txt000066400000000000000000000006511477275177200257070ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.26) project(${SKBUILD_PROJECT_NAME} LANGUAGES C) if(DEFINED BROKEN_CMAKE) message(FATAL_ERROR "Broken CMake") endif() find_package( Python COMPONENTS Interpreter Development.Module REQUIRED) python_add_library(example MODULE main.c WITH_SOABI) if(DEFINED BROKEN_CODE) target_compile_definitions(example PRIVATE BROKEN_CODE) endif() install(TARGETS example DESTINATION .) scikit-build-core-0.11.1/tests/packages/broken_fallback/main.c000066400000000000000000000014261477275177200242400ustar00rootroot00000000000000#define PY_SSIZE_T_CLEAN #include #ifdef BROKEN_CODE #error "Broken code" #endif float square(float x) { return x * x; } static PyObject *square_wrapper(PyObject *self, PyObject *args) { float input, result; if (!PyArg_ParseTuple(args, "f", &input)) { return NULL; } result = square(input); return PyFloat_FromDouble(result); } static PyMethodDef example_methods[] = { {"square", square_wrapper, METH_VARARGS, "Square function"}, {NULL, NULL, 0, NULL}}; static struct PyModuleDef example_module = {PyModuleDef_HEAD_INIT, "example", NULL, -1, example_methods}; /* name here must match extension name, with PyInit_ prefix */ PyMODINIT_FUNC PyInit_example(void) { return PyModule_Create(&example_module); } scikit-build-core-0.11.1/tests/packages/broken_fallback/pyproject.toml000066400000000000000000000005101477275177200260550ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core"] build-backend = "scikit_build_core.build" [project] name = "broken_fallback" version = "0.0.1" [tool.scikit-build] wheel.license-files = [] [[tool.scikit-build.overrides]] if.failed = true wheel.cmake = false [[tool.scikit-build.overrides]] if.env.FAIL_NOW = true fail = true scikit-build-core-0.11.1/tests/packages/cmake_defines/000077500000000000000000000000001477275177200226235ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/cmake_defines/CMakeLists.txt000066400000000000000000000007561477275177200253730ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15) project(cmake_defines LANGUAGES NONE) set(ONE_LEVEL_LIST "" CACHE STRING "") set(NESTED_LIST "" CACHE STRING "") set(out_file "${CMAKE_CURRENT_BINARY_DIR}/log.txt") file(WRITE "${out_file}" "") foreach(list IN ITEMS ONE_LEVEL_LIST NESTED_LIST) list(LENGTH ${list} length) file(APPEND "${out_file}" "${list}.LENGTH = ${length}\n") foreach(item IN LISTS ${list}) file(APPEND "${out_file}" "${item}\n") endforeach() endforeach() scikit-build-core-0.11.1/tests/packages/cmake_defines/pyproject.toml000066400000000000000000000003621477275177200255400ustar00rootroot00000000000000[tool.scikit-build] cmake.version = '>=3.15' [tool.scikit-build.cmake.define] ONE_LEVEL_LIST = [ "Foo", "Bar", "ExceptionallyLargeListEntryThatWouldOverflowTheLine", "Baz", ] NESTED_LIST = [ "Apple", "Lemon;Lime", "Banana" ] scikit-build-core-0.11.1/tests/packages/custom_cmake/000077500000000000000000000000001477275177200225205ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/custom_cmake/CMakeLists.txt000066400000000000000000000005761477275177200252700ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.26) project( ${SKBUILD_PROJECT_NAME} LANGUAGES VERSION 2.3.4) find_package(ExamplePkg REQUIRED) find_package(ExampleRoot REQUIRED) include(ExampleInclude) if(NOT EXAMPLE_INCLUDE_FOUND) message(FATAL_ERROR "ExampleInclude not found") endif() # Testing scripts install(PROGRAMS scripts/script1 DESTINATION "${SKBUILD_SCRIPTS_DIR}") scikit-build-core-0.11.1/tests/packages/custom_cmake/extern/000077500000000000000000000000001477275177200240255ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/custom_cmake/extern/custom_cmake_testing_stuff/000077500000000000000000000000001477275177200314435ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/custom_cmake/extern/custom_cmake_testing_stuff/__init__.py000066400000000000000000000000001477275177200335420ustar00rootroot00000000000000cmake_modules/000077500000000000000000000000001477275177200341745ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/custom_cmake/extern/custom_cmake_testing_stuffExampleInclude.cmake000066400000000000000000000000411477275177200400700ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/custom_cmake/extern/custom_cmake_testing_stuff/cmake_modulesset(EXAMPLE_INCLUDE_FOUND "YES") __init__.py000066400000000000000000000000001477275177200362730ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/custom_cmake/extern/custom_cmake_testing_stuff/cmake_modulesscikit-build-core-0.11.1/tests/packages/custom_cmake/extern/custom_cmake_testing_stuff/cmake_prefix/000077500000000000000000000000001477275177200341005ustar00rootroot00000000000000ExamplePkgConfig.cmake000066400000000000000000000001071477275177200402040ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/custom_cmake/extern/custom_cmake_testing_stuff/cmake_prefixset(ExamplePkg_FOUND TRUE CACHE BOOL "ExamplePkg found" FORCE) __init__.py000066400000000000000000000000001477275177200361200ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/custom_cmake/extern/custom_cmake_testing_stuff/cmake_prefixscikit-build-core-0.11.1/tests/packages/custom_cmake/extern/custom_cmake_testing_stuff/cmake_root/000077500000000000000000000000001477275177200335665ustar00rootroot00000000000000ExampleRootConfig.cmake000066400000000000000000000001111477275177200400670ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/custom_cmake/extern/custom_cmake_testing_stuff/cmake_rootset(ExampleRoot_FOUND TRUE CACHE BOOL "ExampleRoot found" FORCE) __init__.py000066400000000000000000000000001477275177200356060ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/custom_cmake/extern/custom_cmake_testing_stuff/cmake_rootscikit-build-core-0.11.1/tests/packages/custom_cmake/extern/pyproject.toml000066400000000000000000000006251477275177200267440ustar00rootroot00000000000000[build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] name = "custom_cmake_testing_stuff" version = "0.1.0" [project.entry-points."cmake.module"] any = "custom_cmake_testing_stuff.cmake_modules" [project.entry-points."cmake.prefix"] any = "custom_cmake_testing_stuff.cmake_prefix" [project.entry-points."cmake.root"] ExampleRoot = "custom_cmake_testing_stuff.cmake_root" scikit-build-core-0.11.1/tests/packages/custom_cmake/pyproject.toml000066400000000000000000000005661477275177200254430ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core"] build-backend = "scikit_build_core.build" [project] name = "custom_modules" dynamic = ["version"] [tool.scikit-build] wheel.packages = [] wheel.license-files = [] [tool.scikit-build.metadata.version] provider = "scikit_build_core.metadata.regex" input = "CMakeLists.txt" regex = 'project\([^)]+ VERSION (?P[0-9.]+)' scikit-build-core-0.11.1/tests/packages/custom_cmake/scripts/000077500000000000000000000000001477275177200242075ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/custom_cmake/scripts/script1000077500000000000000000000001221477275177200255150ustar00rootroot00000000000000#!/usr/bin/env python3 from pathlib import Path print(Path(__file__).resolve()) scikit-build-core-0.11.1/tests/packages/cython_pxd_editable/000077500000000000000000000000001477275177200240565ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/cython_pxd_editable/pkg1/000077500000000000000000000000001477275177200247205ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/cython_pxd_editable/pkg1/CMakeLists.txt000066400000000000000000000011621477275177200274600ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.29) project(${SKBUILD_PROJECT_NAME} LANGUAGES C) find_package( Python COMPONENTS Interpreter Development.Module REQUIRED) add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/src/pkg1/one.c" MAIN_DEPENDENCY "${CMAKE_CURRENT_SOURCE_DIR}/src/pkg1/one.pyx" VERBATIM COMMAND Python::Interpreter -m cython "${CMAKE_CURRENT_SOURCE_DIR}/src/pkg1/one.pyx" --output-file "${CMAKE_CURRENT_BINARY_DIR}/src/pkg1/one.c") python_add_library(one MODULE "${CMAKE_CURRENT_BINARY_DIR}/src/pkg1/one.c" WITH_SOABI) install(TARGETS one DESTINATION pkg1/) scikit-build-core-0.11.1/tests/packages/cython_pxd_editable/pkg1/pyproject.toml000066400000000000000000000003221477275177200276310ustar00rootroot00000000000000[build-system] build-backend = "scikit_build_core.build" requires = [ "cython>=3.0.0", "scikit-build-core", ] [project] name = "pkg1" version = "1.0.0" [tool.scikit-build] ninja.make-fallback = false scikit-build-core-0.11.1/tests/packages/cython_pxd_editable/pkg1/src/000077500000000000000000000000001477275177200255075ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/cython_pxd_editable/pkg1/src/pkg1/000077500000000000000000000000001477275177200263515ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/cython_pxd_editable/pkg1/src/pkg1/__init__.py000066400000000000000000000000001477275177200304500ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/cython_pxd_editable/pkg1/src/pkg1/one.pxd000066400000000000000000000000171477275177200276450ustar00rootroot00000000000000cdef int one() scikit-build-core-0.11.1/tests/packages/cython_pxd_editable/pkg1/src/pkg1/one.pyx000066400000000000000000000000711477275177200276720ustar00rootroot00000000000000# cython: language_level=3 cdef int one(): return 1 scikit-build-core-0.11.1/tests/packages/cython_pxd_editable/pkg2/000077500000000000000000000000001477275177200247215ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/cython_pxd_editable/pkg2/CMakeLists.txt000066400000000000000000000010661477275177200274640ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.19) project(${SKBUILD_PROJECT_NAME} LANGUAGES C) find_package( Python COMPONENTS Interpreter Development.Module REQUIRED) add_custom_command( OUTPUT src/pkg2/two.c MAIN_DEPENDENCY src/pkg2/two.pyx VERBATIM COMMAND Python::Interpreter -m cython "${CMAKE_CURRENT_SOURCE_DIR}/src/pkg2/two.pyx" --output-file "${CMAKE_CURRENT_BINARY_DIR}/src/pkg2/two.c") python_add_library(two MODULE "${CMAKE_CURRENT_BINARY_DIR}/src/pkg2/two.c" WITH_SOABI) install(TARGETS two DESTINATION pkg2/) scikit-build-core-0.11.1/tests/packages/cython_pxd_editable/pkg2/pyproject.toml000066400000000000000000000003221477275177200276320ustar00rootroot00000000000000[build-system] build-backend = "scikit_build_core.build" requires = [ "cython>=3.0.0", "scikit-build-core", ] [project] name = "pkg2" version = "1.0.0" [tool.scikit-build] ninja.make-fallback = false scikit-build-core-0.11.1/tests/packages/cython_pxd_editable/pkg2/src/000077500000000000000000000000001477275177200255105ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/cython_pxd_editable/pkg2/src/pkg2/000077500000000000000000000000001477275177200263535ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/cython_pxd_editable/pkg2/src/pkg2/__init__.py000066400000000000000000000000001477275177200304520ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/cython_pxd_editable/pkg2/src/pkg2/two.pyx000066400000000000000000000001401477275177200277210ustar00rootroot00000000000000# cython: language_level=3 from pkg1.one cimport one cdef int two(): return one() + one() scikit-build-core-0.11.1/tests/packages/dynamic_metadata/000077500000000000000000000000001477275177200233325ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/dynamic_metadata/CMakeLists.txt000066400000000000000000000005411477275177200260720ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.25) project( ${SKBUILD_PROJECT_NAME} LANGUAGES C VERSION ${SKBUILD_PROJECT_VERSION}) find_package(Python COMPONENTS Interpreter Development.Module) set(Python_SOABI ${SKBUILD_SOABI}) python_add_library(_module MODULE src/module.c WITH_SOABI) install(TARGETS _module DESTINATION ${SKBUILD_PROJECT_NAME}) scikit-build-core-0.11.1/tests/packages/dynamic_metadata/build_requires_project.toml000066400000000000000000000004321477275177200307720ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core"] build-backend = "scikit_build_core.build" [project] name = "more_build_requires" [tool.scikit-build] build.requires = ["foo"] [[tool.scikit-build.overrides]] if.env.LOCAL_FOO = true build.requires = ["foo @ {root:parent:uri}/foo"] scikit-build-core-0.11.1/tests/packages/dynamic_metadata/dual_project.toml000066400000000000000000000004161477275177200267030ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core"] build-backend = "scikit_build_core.build" [project] name = "fancy" dynamic = ["version", "license"] [tool.scikit-build] experimental = true metadata.version.provider = "test_dual" metadata.license.provider = "test_dual" scikit-build-core-0.11.1/tests/packages/dynamic_metadata/faulty_dual_project.toml000066400000000000000000000004771477275177200302760ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core"] build-backend = "scikit_build_core.build" [project] name = "fancy" dynamic = ["version", "readme", "license"] [tool.scikit-build] experimental = true metadata.version.provider = "test_dual" metadata.license.provider = "test_dual" metadata.readme.provider = "test_dual" scikit-build-core-0.11.1/tests/packages/dynamic_metadata/faulty_project.toml000066400000000000000000000003451477275177200272630ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core"] build-backend = "scikit_build_core.build" [project] name = "fancy" version = "0.0.1" [tool.scikit-build.metadata] readme.provider = "scikit_build_core.metadata.fancy_pypi_readme" scikit-build-core-0.11.1/tests/packages/dynamic_metadata/local_pyproject.toml000066400000000000000000000004271477275177200274230ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core"] build-backend = "scikit_build_core.build" [project] name = "dynamic" dynamic = ["version"] [tool.scikit-build] experimental = true [tool.scikit-build.metadata.version] provider = "version.nested" provider-path = "plugins/local" scikit-build-core-0.11.1/tests/packages/dynamic_metadata/plugin_project.toml000066400000000000000000000010641477275177200272540ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core"] build-backend = "scikit_build_core.build" [project] name = "fancy" dynamic = ["readme", "version"] [tool.scikit-build.metadata] version.provider = "scikit_build_core.metadata.setuptools_scm" readme.provider = "scikit_build_core.metadata.fancy_pypi_readme" [tool.setuptools_scm] [tool.hatch.metadata.hooks.fancy-pypi-readme] content-type = "text/x-rst" [[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]] text = "Fragment #1" [[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]] text = "Fragment #2" scikit-build-core-0.11.1/tests/packages/dynamic_metadata/plugins/000077500000000000000000000000001477275177200250135ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/dynamic_metadata/plugins/local/000077500000000000000000000000001477275177200261055ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/dynamic_metadata/plugins/local/version/000077500000000000000000000000001477275177200275725ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/dynamic_metadata/plugins/local/version/__init__.py000066400000000000000000000000001477275177200316710ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/dynamic_metadata/plugins/local/version/nested/000077500000000000000000000000001477275177200310545ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/dynamic_metadata/plugins/local/version/nested/__init__.py000066400000000000000000000006751477275177200331750ustar00rootroot00000000000000from __future__ import annotations __all__ = ["dynamic_metadata"] def __dir__() -> list[str]: return __all__ def dynamic_metadata( field: str, settings: dict[str, object] | None = None, ) -> str: if field != "version": msg = "Only the 'version' field is supported" raise ValueError(msg) if settings: msg = "No inline configuration is supported" raise ValueError(msg) return "3.2.1" scikit-build-core-0.11.1/tests/packages/dynamic_metadata/pyproject.toml000066400000000000000000000005111477275177200262430ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core"] build-backend = "scikit_build_core.build" [project] name = "dynamic" dynamic = ["version", "readme", "license"] [tool.scikit-build] experimental = true metadata.version.provider = "test_version" metadata.readme.provider = "test_readme" metadata.license.provider = "test_license" scikit-build-core-0.11.1/tests/packages/dynamic_metadata/src/000077500000000000000000000000001477275177200241215ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/dynamic_metadata/src/dynamic/000077500000000000000000000000001477275177200255455ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/dynamic_metadata/src/dynamic/__init__.py000066400000000000000000000000621477275177200276540ustar00rootroot00000000000000from ._module import square __all__ = ["square"] scikit-build-core-0.11.1/tests/packages/dynamic_metadata/src/module.c000066400000000000000000000012531477275177200255530ustar00rootroot00000000000000#define PY_SSIZE_T_CLEAN #include float square(float x) { return x * x; } static PyObject *square_wrapper(PyObject *self, PyObject *args) { float input, result; if (!PyArg_ParseTuple(args, "f", &input)) { return NULL; } result = square(input); return PyFloat_FromDouble(result); } static PyMethodDef pysimple_methods[] = { {"square", square_wrapper, METH_VARARGS, "Square function"}, {NULL, NULL, 0, NULL}}; static struct PyModuleDef pysimple_module = {PyModuleDef_HEAD_INIT, "_module", NULL, -1, pysimple_methods}; PyMODINIT_FUNC PyInit__module(void) { return PyModule_Create(&pysimple_module); } scikit-build-core-0.11.1/tests/packages/dynamic_metadata/warn_project.toml000066400000000000000000000003561477275177200267300ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core"] build-backend = "scikit_build_core.build" [project] name = "fancy" version = "0.0.1" dynamic = ["readme"] [tool.scikit-build] experimental = true metadata.readme.provider = "non_existent" scikit-build-core-0.11.1/tests/packages/filepath_pure/000077500000000000000000000000001477275177200226755ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/filepath_pure/CMakeLists.txt000066400000000000000000000030471477275177200254410ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.26) project( "${SKBUILD_PROJECT_NAME}" LANGUAGES C VERSION "${SKBUILD_PROJECT_VERSION}") file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/in_headers.h) file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/in_scripts.py) file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/in_data.txt) file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/main.py) file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/random_file.py) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/random_file.py DESTINATION ${SKBUILD_PLATLIB_DIR}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/in_headers.h DESTINATION ${SKBUILD_HEADERS_DIR}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/in_scripts.py DESTINATION ${SKBUILD_SCRIPTS_DIR}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/in_data.txt DESTINATION ${SKBUILD_DATA_DIR}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/main.py DESTINATION .) if(NOT "${SOME_DEFINE}" STREQUAL "foo") message(FATAL_ERROR "SOME_DEFINE should be foo, is '${SOME_DEFINE}'") endif() if(NOT "${SOME_DEFINE2}" STREQUAL "bar") message(FATAL_ERROR "SOME_DEFINE2 should be bar, is '${SOME_DEFINE2}'") endif() if(NOT "${SOME_DEFINE3}" STREQUAL "baz") message(FATAL_ERROR "SOME_DEFINE3 should be baz, is '${SOME_DEFINE3}'") endif() if(NOT "${SOME_DEFINE4}" STREQUAL "baz") message(FATAL_ERROR "SOME_DEFINE3 should be baz, is '${SOME_DEFINE3}'") endif() if(NOT "${SOME_ARGS1}" STREQUAL "baz") message(FATAL_ERROR "SOME_ARGS1 should be baz, is '${SOME_ARGS1}'") endif() if(DEFINED "${SOME_ARGS2}") message(FATAL_ERROR "args should not be combined, last one wins") endif() scikit-build-core-0.11.1/tests/packages/filepath_pure/pyproject.toml000066400000000000000000000006511477275177200256130ustar00rootroot00000000000000[build-system] requires = ["scikit_build_core"] build-backend = "scikit_build_core.build" [project] name = "cmake_dirs" version = "0.0.1" requires-python = ">=3.8" [project.optional-dependencies] test = ["pytest>=6.0"] [tool.scikit-build] wheel.install-dir = "cmake_dirs" cmake.args = ["-DSOME_ARGS1=foo", "-DSOME_ARGS2=foo"] [tool.scikit-build.cmake.define] SOME_DEFINE = "foo" SOME_DEFINE2 = "foo" SOME_DEFINE3 = "foo" scikit-build-core-0.11.1/tests/packages/fortran_example/000077500000000000000000000000001477275177200232345ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/fortran_example/CMakeLists.txt000066400000000000000000000021011477275177200257660ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.17.2...3.29) project( fibby VERSION 1.0 DESCRIPTION "FIB module" LANGUAGES C Fortran) find_package(Python REQUIRED COMPONENTS Interpreter Development.Module NumPy) # Grab the variables from a local Python installation F2PY headers execute_process( COMMAND "${Python_EXECUTABLE}" -c "import numpy.f2py; print(numpy.f2py.get_include())" OUTPUT_VARIABLE F2PY_INCLUDE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) # Generate the wrapper files add_custom_command( OUTPUT fibbymodule.c fibby-f2pywrappers.f COMMAND Python::Interpreter -m numpy.f2py --lower "${CMAKE_CURRENT_SOURCE_DIR}/fib1.f" -m fibby COMMAND cmake -E touch fibby-f2pywrappers.f VERBATIM DEPENDS fib1.f) python_add_library( fibby MODULE WITH_SOABI "${CMAKE_CURRENT_BINARY_DIR}/fibbymodule.c" "${CMAKE_CURRENT_BINARY_DIR}/fibby-f2pywrappers.f" "${F2PY_INCLUDE_DIR}/fortranobject.c" fib1.f) target_include_directories(fibby PUBLIC ${F2PY_INCLUDE_DIR}) target_link_libraries(fibby PUBLIC Python::NumPy) install(TARGETS fibby DESTINATION .) scikit-build-core-0.11.1/tests/packages/fortran_example/fib1.f000066400000000000000000000005321477275177200242240ustar00rootroot00000000000000C FILE: FIB1.F SUBROUTINE FIB(A,N) C C CALCULATE FIRST N FIBONACCI NUMBERS C INTEGER N REAL*8 A(N) DO I=1,N IF (I.EQ.1) THEN A(I) = 0.0D0 ELSEIF (I.EQ.2) THEN A(I) = 1.0D0 ELSE A(I) = A(I-1) + A(I-2) ENDIF ENDDO END C END FILE FIB1.F scikit-build-core-0.11.1/tests/packages/fortran_example/pyproject.toml000066400000000000000000000005641477275177200261550ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core", "numpy"] build-backend = "scikit_build_core.build" [project] name = "fibby" version = "0.0.1" description = "a minimal example package (fortran version)" requires-python = ">=3.8" classifiers = [ "License :: OSI Approved :: BSD License", ] dependencies = ["numpy"] [tool.scikit-build] cmake.version = "CMakeLists.txt" scikit-build-core-0.11.1/tests/packages/hatchling/000077500000000000000000000000001477275177200220075ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/hatchling/.gitignore000066400000000000000000000047271477275177200240110ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python /build/ /tests/*/build develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ cover/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 db.sqlite3-journal # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder .pybuilder/ target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: # .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # PEP 582; used by e.g. github.com/David-OConnor/pyflow __pypackages__/ # Celery stuff celerybeat-schedule celerybeat.pid # SageMath parsed files *.sage.py # Environments .env* .venv* env*/ venv*/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ # pytype static type analyzer .pytype/ # Cython debug symbols cython_debug/ # setuptools_scm src/*/_version.py # SKBuild cache dir _skbuild/ # Any build dirs in the tests tests/**/build/ /src/scikit_build_core/_version.py # Common editor files *~ *.swp # RPM spec file !/.distro/*.spec /.distro/*.tar.gz *.rpm # ruff .ruff_cache/ # OS specific stuff .DS_Store .DS_Store? ._* .Spotlight-V100 .Trashes ehthumbs.db Thumbs.db .idea/ # tmt setup /.distro/main.fmf /.distro/plans/main.fmf /.distro/tests/main.fmf /docs/**/build .vscode/ scikit-build-core-0.11.1/tests/packages/hatchling/cpp/000077500000000000000000000000001477275177200225715ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/hatchling/cpp/CMakeLists.txt000066400000000000000000000017551477275177200253410ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.29) project(extensionlib_example_cmake LANGUAGES CXX) set(PYBIND11_FINDPYTHON ON) find_package(pybind11 CONFIG REQUIRED) pybind11_add_module(_core MODULE example.cpp) install(TARGETS _core DESTINATION .) if(NOT DEFINED SKBUILD_HATCHLING) message( FATAL_ERROR "This project should be built using scikit-build & hatchling") endif() # Testing metadata file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/metadata_file.txt" "Testing") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/metadata_file.txt" DESTINATION "${SKBUILD_METADATA_DIR}/extra_metadata/") # Testing scripts file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/myscript" "#!/usr/bin/env python\nprint('Hello from myscript.py')") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/myscript" DESTINATION "${SKBUILD_SCRIPTS_DIR}") # Testing data file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/data_file.txt" "Data") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/data_file.txt" DESTINATION "${SKBUILD_DATA_DIR}") scikit-build-core-0.11.1/tests/packages/hatchling/cpp/example.cpp000066400000000000000000000011701477275177200247270ustar00rootroot00000000000000#include int add(int i, int j) { return i + j; } namespace py = pybind11; PYBIND11_MODULE(_core, m) { m.doc() = R"pbdoc( Pybind11 example plugin ----------------------- .. currentmodule:: python_example .. autosummary:: :toctree: _generate add subtract )pbdoc"; m.def("add", &add, R"pbdoc( Add two numbers Some other explanation about the add function. )pbdoc"); m.def( "subtract", [](int i, int j) { return i - j; }, R"pbdoc( Subtract two numbers Some other explanation about the subtract function. )pbdoc"); } scikit-build-core-0.11.1/tests/packages/hatchling/pyproject.toml000066400000000000000000000004551477275177200247270ustar00rootroot00000000000000[build-system] requires = ["hatchling", "pybind11", "scikit-build-core"] build-backend = "hatchling.build" [project] name = "hatchling_example" version = "0.1.0" [tool.hatch.build.targets.wheel.hooks.scikit-build] wheel.install-dir = "hatchling_example" cmake.source-dir = "cpp" experimental = true scikit-build-core-0.11.1/tests/packages/hatchling/src/000077500000000000000000000000001477275177200225765ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/hatchling/src/hatchling_example/000077500000000000000000000000001477275177200262525ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/hatchling/src/hatchling_example/__init__.py000066400000000000000000000001461477275177200303640ustar00rootroot00000000000000__version__ = "0.0.1" from ._core import add, subtract __all__ = ["__version__", "add", "subtract"] scikit-build-core-0.11.1/tests/packages/hatchling/src/hatchling_example/_core.pyi000066400000000000000000000001231477275177200300600ustar00rootroot00000000000000def add(a: int, b: int, /) -> int: ... def subtract(a: int, b: int, /) -> int: ... scikit-build-core-0.11.1/tests/packages/mixed_setuptools/000077500000000000000000000000001477275177200234555ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/mixed_setuptools/CMakeLists.txt000066400000000000000000000013001477275177200262070ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.26) project("${SKBUILD_PROJECT_NAME}" LANGUAGES CXX) if(NOT EXAMPLE_DEFINE1 EQUAL 1) message(FATAL_ERROR "Example define 1 is not set") endif() if(NOT EXAMPLE_DEFINE2 EQUAL 2) message(FATAL_ERROR "Example define 2 is not set") endif() if(NOT EXAMPLE_DEFINE3 EQUAL 3) message(FATAL_ERROR "Example define 3 is not set") endif() if(NOT EXAMPLE_DEFINE4 EQUAL 4) message(FATAL_ERROR "Example define 4 is not set") endif() if(NOT EXAMPLE_DEFINE5 EQUAL 5) message(FATAL_ERROR "Example define 5 is not set") endif() find_package(pybind11 CONFIG REQUIRED) pybind11_add_module(_core src/main.cpp) install(TARGETS _core LIBRARY DESTINATION mixed_setuptools) scikit-build-core-0.11.1/tests/packages/mixed_setuptools/pyproject.toml000066400000000000000000000004051477275177200263700ustar00rootroot00000000000000[build-system] requires = [ "scikit_build_core", "setuptools", "pybind11", ] build-backend = "scikit_build_core.setuptools.build_meta" [tool.scikit-build] cmake.args = ["-DEXAMPLE_DEFINE3=3"] [tool.scikit-build.cmake.define] EXAMPLE_DEFINE4 = "4" scikit-build-core-0.11.1/tests/packages/mixed_setuptools/setup.cfg000066400000000000000000000004171477275177200253000ustar00rootroot00000000000000[metadata] name = mixed-setuptools version = 3.1.4 [options] zip_safe = False python_requires = >=3.8 packages = find: package_dir = =src [options.packages.find] where = src [build_cmake] source_dir = . cmake_args = -DEXAMPLE_DEFINE1=1 -DEXAMPLE_DEFINE2=2 scikit-build-core-0.11.1/tests/packages/mixed_setuptools/setup.py000066400000000000000000000001101477275177200251570ustar00rootroot00000000000000from setuptools import setup setup(cmake_args=["-DEXAMPLE_DEFINE5=5"]) scikit-build-core-0.11.1/tests/packages/mixed_setuptools/src/000077500000000000000000000000001477275177200242445ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/mixed_setuptools/src/main.cpp000066400000000000000000000006551477275177200257020ustar00rootroot00000000000000#include int add(int i, int j) { return i + j; } namespace py = pybind11; PYBIND11_MODULE(_core, m) { m.def("add", &add, R"pbdoc( Add two numbers Some other explanation about the add function. )pbdoc"); m.def("subtract", [](int i, int j) { return i - j; }, R"pbdoc( Subtract two numbers Some other explanation about the subtract function. )pbdoc"); } scikit-build-core-0.11.1/tests/packages/mixed_setuptools/src/mixed_setuptools/000077500000000000000000000000001477275177200276535ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/mixed_setuptools/src/mixed_setuptools/__init__.py000066400000000000000000000000521477275177200317610ustar00rootroot00000000000000from ._core import add __all__ = ["add"] scikit-build-core-0.11.1/tests/packages/mixed_setuptools/src/mixed_setuptools/_core.pyi000066400000000000000000000000471477275177200314660ustar00rootroot00000000000000def add(a: int, b: int, /) -> int: ... scikit-build-core-0.11.1/tests/packages/navigate_editable/000077500000000000000000000000001477275177200234755ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/navigate_editable/CMakeLists.txt000066400000000000000000000013721477275177200262400ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.26) project( ${SKBUILD_PROJECT_NAME} LANGUAGES C VERSION ${SKBUILD_PROJECT_VERSION}) find_package(Python COMPONENTS Interpreter Development.Module) python_add_library(c_module MODULE src/shared_pkg/c_module.c WITH_SOABI) set(CMakeVar "Some_value_C") configure_file(src/shared_pkg/data/generated.txt.in shared_pkg/data/c_generated.txt) configure_file(src/shared_pkg/data/generated.txt.in shared_pkg/data/.hidden) install( TARGETS c_module DESTINATION shared_pkg/ COMPONENT PythonModule) install(FILES ${PROJECT_BINARY_DIR}/shared_pkg/data/c_generated.txt DESTINATION shared_pkg/data/) install(FILES ${PROJECT_BINARY_DIR}/shared_pkg/data/.hidden DESTINATION shared_pkg/data/) scikit-build-core-0.11.1/tests/packages/navigate_editable/pyproject.toml000066400000000000000000000004211477275177200264060ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core"] build-backend = "scikit_build_core.build" [project] name = "navigate_editable" version = "0.0.1" dependencies = [ "importlib-resources; python_version<'3.9'" ] [tool.scikit-build] wheel.packages = ["python/shared_pkg"] scikit-build-core-0.11.1/tests/packages/navigate_editable/python/000077500000000000000000000000001477275177200250165ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/navigate_editable/python/shared_pkg/000077500000000000000000000000001477275177200271255ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/navigate_editable/python/shared_pkg/__init__.py000066400000000000000000000003401477275177200312330ustar00rootroot00000000000000from .c_module import call_py_method from .py_module import call_c_method, read_c_generated_txt, read_py_data_txt __all__ = [ "call_c_method", "call_py_method", "read_c_generated_txt", "read_py_data_txt", ] scikit-build-core-0.11.1/tests/packages/navigate_editable/python/shared_pkg/c_module.pyi000066400000000000000000000000751477275177200314410ustar00rootroot00000000000000def c_method() -> str: ... def call_py_method() -> None: ... scikit-build-core-0.11.1/tests/packages/navigate_editable/python/shared_pkg/data/000077500000000000000000000000001477275177200300365ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/navigate_editable/python/shared_pkg/data/py_data.txt000066400000000000000000000000161477275177200322150ustar00rootroot00000000000000Some_value_Py scikit-build-core-0.11.1/tests/packages/navigate_editable/python/shared_pkg/py_module.py000066400000000000000000000010631477275177200314740ustar00rootroot00000000000000import sys if sys.version_info < (3, 9): from importlib_resources import files else: from importlib.resources import files from .c_module import c_method def call_c_method(): print(c_method()) def py_method(): print("py_method") def read_py_data_txt(): root = files("shared_pkg.data") py_data = root / "py_data.txt" print(py_data.read_text(encoding="utf-8")) def read_c_generated_txt(): root = files("shared_pkg.data") c_generated_txt = root / "c_generated.txt" print(c_generated_txt.read_text(encoding="utf-8")) scikit-build-core-0.11.1/tests/packages/navigate_editable/src/000077500000000000000000000000001477275177200242645ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/navigate_editable/src/shared_pkg/000077500000000000000000000000001477275177200263735ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/navigate_editable/src/shared_pkg/c_module.c000066400000000000000000000030351477275177200303270ustar00rootroot00000000000000#define PY_SSIZE_T_CLEAN #include #include #include const char* c_method() { return "c_method"; } static PyObject *c_method_wrapper(PyObject *self, PyObject *args) { return PyUnicode_FromString(c_method()); } static PyObject *py_method_wrapper(PyObject *self, PyObject *args) { PyObject *py_module = PyImport_ImportModule("shared_pkg.py_module"); if (py_module == NULL) { PyErr_Print(); fprintf(stderr, "Failed to load shared_pkg.py_module\n"); exit(1); } PyObject *py_method = PyObject_GetAttrString(py_module,(char*)"py_method"); if (py_method == NULL) { PyErr_Print(); fprintf(stderr, "Failed to load shared_pkg.py_module.py_method\n"); exit(1); } #if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION > 8 PyObject *res = PyObject_CallNoArgs(py_method); #else PyObject *res = PyObject_CallObject(py_method, NULL); #endif if (res == NULL) { PyErr_Print(); fprintf(stderr, "Failed to execute shared_pkg.py_module.py_method\n"); exit(1); } Py_DECREF(py_module); Py_DECREF(py_method); Py_DECREF(res); Py_RETURN_NONE; } static PyMethodDef c_module_methods[] = { {"c_method", c_method_wrapper, METH_NOARGS, "C native method"}, {"call_py_method", py_method_wrapper, METH_NOARGS, "Call python native method"}, {NULL, NULL, 0, NULL}}; static struct PyModuleDef c_module = {PyModuleDef_HEAD_INIT, "c_module", NULL, -1, c_module_methods}; PyMODINIT_FUNC PyInit_c_module(void) { return PyModule_Create(&c_module); } scikit-build-core-0.11.1/tests/packages/navigate_editable/src/shared_pkg/data/000077500000000000000000000000001477275177200273045ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/navigate_editable/src/shared_pkg/data/generated.txt.in000066400000000000000000000000131477275177200324020ustar00rootroot00000000000000@CMakeVar@ scikit-build-core-0.11.1/tests/packages/pep639_pure/000077500000000000000000000000001477275177200221275ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/pep639_pure/LICENSE1.txt000066400000000000000000000000001477275177200240210ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/pep639_pure/nested/000077500000000000000000000000001477275177200234115ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/pep639_pure/nested/more/000077500000000000000000000000001477275177200243535ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/pep639_pure/nested/more/LICENSE2.txt000066400000000000000000000000001477275177200262460ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/pep639_pure/pyproject.toml000066400000000000000000000004021477275177200250370ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core"] build-backend = "scikit_build_core.build" [project] name = "pep639_pure" version = "0.1.0" license = "MIT" license-files = ["LICENSE1.txt", "nested/more/LICENSE2.txt"] [tool.scikit-build] wheel.cmake = false scikit-build-core-0.11.1/tests/packages/sdist_config/000077500000000000000000000000001477275177200225215ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/sdist_config/.gitignore000066400000000000000000000000321477275177200245040ustar00rootroot00000000000000/pybind11 overwrite.cmake scikit-build-core-0.11.1/tests/packages/sdist_config/CMakeLists.txt000066400000000000000000000020651477275177200252640ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.27) project( sdist_config LANGUAGES CXX VERSION ${SKBUILD_PROJECT_VERSION}) include(FetchContent) set(PYBIND11_FINDPYTHON ON) if(NOT SKBUILD_STATE STREQUAL "sdist" AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/pybind11/CMakeLists.txt") message(STATUS "Using integrated pybind11") add_subdirectory(pybind11) else() FetchContent_Declare( pybind11 GIT_REPOSITORY https://github.com/pybind/pybind11.git GIT_TAG v2.12.0 SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/pybind11) FetchContent_MakeAvailable(pybind11) endif() pybind11_add_module(sdist_config main.cpp) install(TARGETS sdist_config DESTINATION .) # Generation test include("${CMAKE_CURRENT_BINARY_DIR}/output.cmake") if(NOT "${MY_VERSION}" STREQUAL "${PROJECT_VERSION}") message(FATAL_ERROR "Version mismatch: ${MY_VERSION} != ${PROJECT_VERSION}") endif() include("${CMAKE_CURRENT_SOURCE_DIR}/overwrite.cmake") if(NOT "${MY_NAME}" STREQUAL "${SKBUILD_PROJECT_NAME}") message(FATAL_ERROR "Name mismatch: ${MY_NAME} != ${SKBUILD_PROJECT_NAME}") endif() scikit-build-core-0.11.1/tests/packages/sdist_config/input.cmake000066400000000000000000000000351477275177200246600ustar00rootroot00000000000000set(MY_VERSION "${version}") scikit-build-core-0.11.1/tests/packages/sdist_config/main.cpp000066400000000000000000000002071477275177200241500ustar00rootroot00000000000000#include namespace py = pybind11; PYBIND11_MODULE(sdist_config, m) { m.def("life", []() { return 42; }); } scikit-build-core-0.11.1/tests/packages/sdist_config/pyproject.toml000066400000000000000000000012461477275177200254400ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core"] build-backend = "scikit_build_core.build" [project] name = "sdist_config" version = "0.1.0" [tool.scikit-build] sdist.cmake = true sdist.include = [ "pybind11/tools", "pybind11/include", "pybind11/CMakeLists.txt", ] wheel.license-files = [] wheel.packages = [] cmake.define.FETCHCONTENT_QUIET = false [[tool.scikit-build.generate]] path = "output.cmake" location = "build" template-path = "input.cmake" [[tool.scikit-build.generate]] path = "output.py" template = """ version = "${version}" """ [[tool.scikit-build.generate]] path = "overwrite.cmake" location = "source" template = """ set(MY_NAME "${name}") """ scikit-build-core-0.11.1/tests/packages/simple_pure/000077500000000000000000000000001477275177200223725ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/simple_pure/CMakeLists.txt000066400000000000000000000005721477275177200251360ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.26) project(simple_pure LANGUAGES CXX) add_executable(simple_pure simple_pure.cpp) target_compile_features(simple_pure PUBLIC cxx_std_11) install(TARGETS simple_pure) if(DEFINED SKBUILD) message(STATUS "SKBUILD is defined to ${SKBUILD}") endif() if(DEFINED SKBUILD2) message(STATUS "SKBUILD2 is defined to ${SKBUILD2}") endif() scikit-build-core-0.11.1/tests/packages/simple_pure/simple_pure.cpp000066400000000000000000000003601477275177200254210ustar00rootroot00000000000000#include #include #include int main() { std::vector v{"0", "one", "2", "three"}; for (const auto& arg : v) { std::cout << arg << ' '; } std::cout << '\n'; return 0; } scikit-build-core-0.11.1/tests/packages/simple_purelib_package/000077500000000000000000000000001477275177200245345ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/simple_purelib_package/pyproject.toml000066400000000000000000000002711477275177200274500ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core"] build-backend = "scikit_build_core.build" [project] name = "purelib_example" version = "0.0.1" [tool.scikit-build] wheel.cmake = false scikit-build-core-0.11.1/tests/packages/simple_purelib_package/src/000077500000000000000000000000001477275177200253235ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/simple_purelib_package/src/purelib_example/000077500000000000000000000000001477275177200305005ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/simple_purelib_package/src/purelib_example/__init__.py000066400000000000000000000000261477275177200326070ustar00rootroot00000000000000__version__ = "1.2.3" scikit-build-core-0.11.1/tests/packages/simple_pyproject_ext/000077500000000000000000000000001477275177200243165ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/simple_pyproject_ext/CMakeLists.txt000066400000000000000000000007251477275177200270620ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.26) project( "${SKBUILD_PROJECT_NAME}" LANGUAGES CXX VERSION "${SKBUILD_PROJECT_VERSION}") find_package( Python COMPONENTS Interpreter Development.Module REQUIRED) find_package(pybind11 CONFIG REQUIRED) pybind11_add_module(cmake_example src/main.cpp) target_compile_definitions(cmake_example PRIVATE VERSION_INFO=${PROJECT_VERSION}) install(TARGETS cmake_example LIBRARY DESTINATION .) scikit-build-core-0.11.1/tests/packages/simple_pyproject_ext/LICENSE000066400000000000000000000261221477275177200253260ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2022 Henry Schreiner Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. scikit-build-core-0.11.1/tests/packages/simple_pyproject_ext/pyproject.toml000066400000000000000000000006001477275177200272260ustar00rootroot00000000000000[build-system] requires = [ "scikit_build_core", "pybind11", ] build-backend = "scikit_build_core.build" [project] name = "CMake.Example" version = "0.0.1" requires-python = ">=3.8" [project.optional-dependencies] test = ["pytest>=6.0"] [project.scripts] something = "other:callme" [project.gui-scripts] guithing = "a.b:c" [project.entry-points."one.two"] three = "four" scikit-build-core-0.11.1/tests/packages/simple_pyproject_ext/src/000077500000000000000000000000001477275177200251055ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/simple_pyproject_ext/src/main.cpp000066400000000000000000000015371477275177200265430ustar00rootroot00000000000000#include #define STRINGIFY(x) #x #define MACRO_STRINGIFY(x) STRINGIFY(x) int add(int i, int j) { return i + j; } namespace py = pybind11; PYBIND11_MODULE(cmake_example, m) { m.doc() = R"pbdoc( Pybind11 example plugin ----------------------- .. currentmodule:: cmake_example .. autosummary:: :toctree: _generate add subtract )pbdoc"; m.def("add", &add, R"pbdoc( Add two numbers Some other explanation about the add function. )pbdoc"); m.def("subtract", [](int i, int j) { return i - j; }, R"pbdoc( Subtract two numbers Some other explanation about the subtract function. )pbdoc"); #ifdef VERSION_INFO m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO); #else m.attr("__version__") = "dev"; #endif } scikit-build-core-0.11.1/tests/packages/simple_pyproject_script_with_flags/000077500000000000000000000000001477275177200272315ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/simple_pyproject_script_with_flags/CMakeLists.txt000066400000000000000000000005021477275177200317660ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.29) project( "${SKBUILD_PROJECT_NAME}" LANGUAGES C VERSION "${SKBUILD_PROJECT_VERSION}") add_executable(cmake_example src/main.c) set_target_properties(cmake_example PROPERTIES POSITION_INDEPENDENT_CODE ON) install(TARGETS cmake_example DESTINATION "${SKBUILD_SCRIPTS_DIR}") scikit-build-core-0.11.1/tests/packages/simple_pyproject_script_with_flags/LICENSE000066400000000000000000000261221477275177200302410ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2022 Henry Schreiner Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. scikit-build-core-0.11.1/tests/packages/simple_pyproject_script_with_flags/pyproject.toml000066400000000000000000000003311477275177200321420ustar00rootroot00000000000000[build-system] requires = [ "scikit_build_core", ] build-backend = "scikit_build_core.build" [project] name = "CMake.Example" version = "0.0.1" requires-python = ">=3.8" [tool.scikit-build] wheel.py-api = "py3" scikit-build-core-0.11.1/tests/packages/simple_pyproject_script_with_flags/src/000077500000000000000000000000001477275177200300205ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/simple_pyproject_script_with_flags/src/main.c000066400000000000000000000004551477275177200311140ustar00rootroot00000000000000#include #include #include #ifndef FOO static_assert(false, "FOO must be defined"); #elif FOO != 1 static_assert(false, "FOO must be 1"); #endif #ifndef BAR static_assert(false, "BAR must be defined"); #endif int main() { printf("Hello world!"); return 0; } scikit-build-core-0.11.1/tests/packages/simple_pyproject_source_dir/000077500000000000000000000000001477275177200256545ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/simple_pyproject_source_dir/LICENSE000066400000000000000000000261221477275177200266640ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2022 Henry Schreiner Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. scikit-build-core-0.11.1/tests/packages/simple_pyproject_source_dir/pyproject.toml000066400000000000000000000006561477275177200305770ustar00rootroot00000000000000[build-system] requires = [ "scikit_build_core", "pybind11", ] build-backend = "scikit_build_core.build" [tool.scikit-build] cmake.source-dir = "src" [project] name = "CMake.Example" version = "0.0.1" requires-python = ">=3.8" [project.optional-dependencies] test = ["pytest>=6.0"] [project.scripts] something = "other:callme" [project.gui-scripts] guithing = "a.b:c" [project.entry-points."one.two"] three = "four" scikit-build-core-0.11.1/tests/packages/simple_pyproject_source_dir/src/000077500000000000000000000000001477275177200264435ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/simple_pyproject_source_dir/src/CMakeLists.txt000066400000000000000000000007211477275177200312030ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.26) project( "${SKBUILD_PROJECT_NAME}" LANGUAGES CXX VERSION "${SKBUILD_PROJECT_VERSION}") find_package( Python COMPONENTS Interpreter Development.Module REQUIRED) find_package(pybind11 CONFIG REQUIRED) pybind11_add_module(cmake_example main.cpp) target_compile_definitions(cmake_example PRIVATE VERSION_INFO=${PROJECT_VERSION}) install(TARGETS cmake_example LIBRARY DESTINATION .) scikit-build-core-0.11.1/tests/packages/simple_pyproject_source_dir/src/main.cpp000066400000000000000000000015371477275177200301010ustar00rootroot00000000000000#include #define STRINGIFY(x) #x #define MACRO_STRINGIFY(x) STRINGIFY(x) int add(int i, int j) { return i + j; } namespace py = pybind11; PYBIND11_MODULE(cmake_example, m) { m.doc() = R"pbdoc( Pybind11 example plugin ----------------------- .. currentmodule:: cmake_example .. autosummary:: :toctree: _generate add subtract )pbdoc"; m.def("add", &add, R"pbdoc( Add two numbers Some other explanation about the add function. )pbdoc"); m.def("subtract", [](int i, int j) { return i - j; }, R"pbdoc( Subtract two numbers Some other explanation about the subtract function. )pbdoc"); #ifdef VERSION_INFO m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO); #else m.attr("__version__") = "dev"; #endif } scikit-build-core-0.11.1/tests/packages/simple_setuptools_ext/000077500000000000000000000000001477275177200245205ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/simple_setuptools_ext/CMakeLists.txt000066400000000000000000000006051477275177200272610ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.26) project( "${SKBUILD_PROJECT_NAME}" LANGUAGES CXX VERSION "${SKBUILD_PROJECT_VERSION}") find_package(pybind11 CONFIG REQUIRED) pybind11_add_module(cmake_example src/main.cpp) target_compile_definitions(cmake_example PRIVATE VERSION_INFO=${PROJECT_VERSION}) install(TARGETS cmake_example LIBRARY DESTINATION .) scikit-build-core-0.11.1/tests/packages/simple_setuptools_ext/LICENSE000066400000000000000000000261221477275177200255300ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2022 Henry Schreiner Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. scikit-build-core-0.11.1/tests/packages/simple_setuptools_ext/pyproject.toml000066400000000000000000000002231477275177200274310ustar00rootroot00000000000000[build-system] requires = [ "scikit_build_core", "setuptools", "pybind11", ] build-backend = "scikit_build_core.setuptools.build_meta" scikit-build-core-0.11.1/tests/packages/simple_setuptools_ext/setup.py000066400000000000000000000004701477275177200262330ustar00rootroot00000000000000from setuptools import find_packages, setup setup( name="cmake-example", version="0.0.1", cmake_source_dir=".", zip_safe=False, package_dir={"": "src"}, packages=find_packages(), extras_require={"test": ["pytest>=6.0"]}, python_requires=">=3.8", license_files=["LICENSE"], ) scikit-build-core-0.11.1/tests/packages/simple_setuptools_ext/src/000077500000000000000000000000001477275177200253075ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/simple_setuptools_ext/src/main.cpp000066400000000000000000000015371477275177200267450ustar00rootroot00000000000000#include #define STRINGIFY(x) #x #define MACRO_STRINGIFY(x) STRINGIFY(x) int add(int i, int j) { return i + j; } namespace py = pybind11; PYBIND11_MODULE(cmake_example, m) { m.doc() = R"pbdoc( Pybind11 example plugin ----------------------- .. currentmodule:: cmake_example .. autosummary:: :toctree: _generate add subtract )pbdoc"; m.def("add", &add, R"pbdoc( Add two numbers Some other explanation about the add function. )pbdoc"); m.def("subtract", [](int i, int j) { return i - j; }, R"pbdoc( Subtract two numbers Some other explanation about the subtract function. )pbdoc"); #ifdef VERSION_INFO m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO); #else m.attr("__version__") = "dev"; #endif } scikit-build-core-0.11.1/tests/packages/simplest_c/000077500000000000000000000000001477275177200222105ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/simplest_c/.gitignore000066400000000000000000000000121477275177200241710ustar00rootroot00000000000000*ignored* scikit-build-core-0.11.1/tests/packages/simplest_c/CMakeLists.txt000066400000000000000000000032561477275177200247560ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.26) project( ${SKBUILD_PROJECT_NAME} LANGUAGES C VERSION ${SKBUILD_PROJECT_VERSION}) find_package(Python COMPONENTS Interpreter Development.Module) python_add_library(_module MODULE src/module.c WITH_SOABI) install( TARGETS _module DESTINATION ${SKBUILD_PROJECT_NAME} COMPONENT PythonModule) if("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}" AND DEFINED SKBUILD) # Editable in-place builds. THe empty generator expression ensures # multi-config enerators keeps us from having to set # LIBRARY_OUTPUT_DIRECTORY_ too. set_target_properties( _module PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/src/${SKBUILD_PROJECT_NAME}$<0:>") endif() # Testing artifacts file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/generated.txt "Testing") install( FILES ${CMAKE_CURRENT_BINARY_DIR}/generated.txt DESTINATION ${SKBUILD_PROJECT_NAME} COMPONENT Generated) file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/generated_ignored.txt "Testing") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/generated_ignored.txt DESTINATION ${SKBUILD_PROJECT_NAME}) file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/generated_no_wheel.txt "Testing") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/generated_no_wheel.txt DESTINATION ${SKBUILD_PROJECT_NAME}) # Testing metadata file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/metadata_file.txt "Testing") file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/LICENSE.txt "Testing") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/metadata_file.txt DESTINATION "${SKBUILD_METADATA_DIR}") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/LICENSE.txt DESTINATION "${SKBUILD_METADATA_DIR}/licenses") scikit-build-core-0.11.1/tests/packages/simplest_c/pyproject.toml000066400000000000000000000002111477275177200251160ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core"] build-backend = "scikit_build_core.build" [project] name = "simplest" version = "0.0.1" scikit-build-core-0.11.1/tests/packages/simplest_c/src/000077500000000000000000000000001477275177200227775ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/simplest_c/src/module.c000066400000000000000000000012531477275177200244310ustar00rootroot00000000000000#define PY_SSIZE_T_CLEAN #include float square(float x) { return x * x; } static PyObject *square_wrapper(PyObject *self, PyObject *args) { float input, result; if (!PyArg_ParseTuple(args, "f", &input)) { return NULL; } result = square(input); return PyFloat_FromDouble(result); } static PyMethodDef pysimple_methods[] = { {"square", square_wrapper, METH_VARARGS, "Square function"}, {NULL, NULL, 0, NULL}}; static struct PyModuleDef pysimple_module = {PyModuleDef_HEAD_INIT, "_module", NULL, -1, pysimple_methods}; PyMODINIT_FUNC PyInit__module(void) { return PyModule_Create(&pysimple_module); } scikit-build-core-0.11.1/tests/packages/simplest_c/src/not_a_package/000077500000000000000000000000001477275177200255525ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/simplest_c/src/not_a_package/simple.txt000066400000000000000000000000001477275177200275720ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/simplest_c/src/simplest/000077500000000000000000000000001477275177200246375ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/simplest_c/src/simplest/__init__.py000066400000000000000000000000621477275177200267460ustar00rootroot00000000000000from ._module import square __all__ = ["square"] scikit-build-core-0.11.1/tests/packages/simplest_c/src/simplest/_module.pyi000066400000000000000000000000461477275177200270060ustar00rootroot00000000000000def square(x: float, /) -> float: ... scikit-build-core-0.11.1/tests/packages/simplest_c/src/simplest/artifact_ignored.txt000066400000000000000000000000001477275177200306720ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/simplest_c/src/simplest/data.txt000066400000000000000000000000001477275177200262770ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/simplest_c/src/simplest/excluded.txt000066400000000000000000000000001477275177200271630ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/simplest_c/src/simplest/ignored.txt000066400000000000000000000000001477275177200270150ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/simplest_c/src/simplest/ignored_included.txt000066400000000000000000000000001477275177200306640ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/simplest_c/src/simplest/sdist_only.txt000066400000000000000000000000001477275177200275550ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/toml_setuptools_ext/000077500000000000000000000000001477275177200242025ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/toml_setuptools_ext/CMakeLists.txt000066400000000000000000000006051477275177200267430ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.26) project( "${SKBUILD_PROJECT_NAME}" LANGUAGES CXX VERSION "${SKBUILD_PROJECT_VERSION}") find_package(pybind11 CONFIG REQUIRED) pybind11_add_module(cmake_example src/main.cpp) target_compile_definitions(cmake_example PRIVATE VERSION_INFO=${PROJECT_VERSION}) install(TARGETS cmake_example LIBRARY DESTINATION .) scikit-build-core-0.11.1/tests/packages/toml_setuptools_ext/LICENSE000066400000000000000000000261221477275177200252120ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2022 Henry Schreiner Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. scikit-build-core-0.11.1/tests/packages/toml_setuptools_ext/pyproject.toml000066400000000000000000000004151477275177200271160ustar00rootroot00000000000000[build-system] requires = [ "scikit_build_core", "setuptools", "pybind11", ] build-backend = "scikit_build_core.setuptools.build_meta" [project] name = "cmake-example" version = "0.0.1" requires-python = ">=3.8" [tool.scikit-build] cmake.source-dir = "." scikit-build-core-0.11.1/tests/packages/toml_setuptools_ext/src/000077500000000000000000000000001477275177200247715ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/packages/toml_setuptools_ext/src/main.cpp000066400000000000000000000015371477275177200264270ustar00rootroot00000000000000#include #define STRINGIFY(x) #x #define MACRO_STRINGIFY(x) STRINGIFY(x) int add(int i, int j) { return i + j; } namespace py = pybind11; PYBIND11_MODULE(cmake_example, m) { m.doc() = R"pbdoc( Pybind11 example plugin ----------------------- .. currentmodule:: cmake_example .. autosummary:: :toctree: _generate add subtract )pbdoc"; m.def("add", &add, R"pbdoc( Add two numbers Some other explanation about the add function. )pbdoc"); m.def("subtract", [](int i, int j) { return i - j; }, R"pbdoc( Subtract two numbers Some other explanation about the subtract function. )pbdoc"); #ifdef VERSION_INFO m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO); #else m.attr("__version__") = "dev"; #endif } scikit-build-core-0.11.1/tests/plans.fmf000066400000000000000000000014371477275177200201040ustar00rootroot00000000000000/: inherit: false /pytest: summary: Run all pytest prepare: - name: Install test packages how: install package: # Pip install everything - python3-pip # Test everything we got - gcc-c++ - gfortran - cmake - ninja-build # For FindPython - python3-devel # There are tests that use FetchContent git - git - name: Prepare environment how: shell script: | # Fake a git archive cat << EOF > .git_archival.txt node: 47431d4eefbac9c3a7c49e62c73e624b932023eb node-date: 2025-02-27T16:18:39-05:00 describe-name: v0.11.0 EOF pip install --user .[test] discover: how: fmf filter: "tag: pytest" execute: how: tmt scikit-build-core-0.11.1/tests/test_auto.py000066400000000000000000000044471477275177200206620ustar00rootroot00000000000000import textwrap import pytest from packaging.version import Version from scikit_build_core.settings.auto_cmake_version import find_min_cmake_version from scikit_build_core.settings.auto_requires import get_min_requires def test_auto_requires_pkg_no_spec(): reqlist = ["scikit-build-core"] assert get_min_requires("scikit-build-core", reqlist) is None assert get_min_requires("other", reqlist) is None @pytest.mark.parametrize( ("spec", "version"), [ ("==1.0", Version("1.0")), (">=1.0", Version("1.0")), (">1.0", Version("1.0")), ("~=1.0", Version("1.0")), (">=0.3,<0.4", Version("0.3")), ("", None), ], ) def test_auto_requires_pkg_version(spec: str, version: Version): reqlist = [f"scikit_build_core{spec}"] assert get_min_requires("scikit-build-core", reqlist) == version def test_auto_requires_with_marker(): reqlist = [ "scikit_build_core>=0.1; python_version < '3.8'", "scikit_build_core>=0.2; python_version >= '3.8'", ] assert get_min_requires("scikit-build-core", reqlist) == Version("0.2") @pytest.mark.parametrize( ("expr", "answer"), [ ("3.15", "3.15"), ("3.16", "3.16"), ("3.17.2", "3.17.2"), ("3.18...3.29", "3.18"), ("3.19.2...3.29", "3.19.2"), ], ) def test_auto_cmake_version(expr: str, answer: str): txt = f"stuff()\ncmake_minimum_required(VERSION {expr})\nother()" res = find_min_cmake_version(txt) assert res == answer txt = f"stuff()\n# cmake_minimum_version(VERSION 3.1)\ncmake_minimum_required(\nVERSION {expr} FATAL_ERROR)\nother()" res = find_min_cmake_version(txt) assert res == answer @pytest.mark.parametrize( "block", [ "if", "foreach", "while", "macro", "function", "block", ], ) def test_auto_cmake_version_block(block: str): txt = textwrap.dedent(f"""\ # cmake_minimum_version(VERSION 3.1) #[[ cmake_minimum_required(VERSION 3.2) ]] {block}() cmake_minimum_required(VERSION 3.3) end{block}() cmake_MINimum_required(VERSION 3.4) cmake_minimum_required(VERSION 3.5) """) res = find_min_cmake_version(txt) assert res == "3.4" scikit-build-core-0.11.1/tests/test_broken_fallback.py000066400000000000000000000041401477275177200227770ustar00rootroot00000000000000import zipfile from pathlib import Path import pytest from scikit_build_core.build import ( build_wheel, get_requires_for_build_wheel, ) @pytest.mark.compile @pytest.mark.configure @pytest.mark.usefixtures("broken_fallback") @pytest.mark.parametrize("broken_define", ["BROKEN_CMAKE", "BROKEN_CODE"]) def test_broken_code( broken_define: str, capfd: pytest.CaptureFixture[str], tmp_path: Path ): dist = tmp_path / "dist" build_wheel(str(dist), {f"cmake.define.{broken_define}": "1"}) wheel = dist / "broken_fallback-0.0.1-py3-none-any.whl" with zipfile.ZipFile(wheel) as f: file_names = set(f.namelist()) assert file_names == { "broken_fallback-0.0.1.dist-info/RECORD", "broken_fallback-0.0.1.dist-info/WHEEL", "broken_fallback-0.0.1.dist-info/METADATA", } out, err = capfd.readouterr() assert "retrying due to override..." in out if broken_define == "BROKEN_CMAKE": assert "Broken CMake" in err assert "CMake Error at CMakeLists.txt" in err assert "CMake configuration failed" in out else: assert "CMake build failed" in out @pytest.mark.usefixtures("broken_fallback") def test_fail_setting( monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ): monkeypatch.setenv("FAIL_NOW", "1") assert get_requires_for_build_wheel({}) == [] with pytest.raises(SystemExit) as exc: build_wheel("dist") assert exc.value.code == 7 out, _ = capsys.readouterr() assert "fail setting was enabled" in out @pytest.mark.usefixtures("broken_fallback") def test_fail_setting_msg( monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ): monkeypatch.setenv("FAIL_NOW", "1") monkeypatch.setenv( "SKBUILD_MESSAGES_AFTER_FAILURE", "This is a test failure message" ) assert get_requires_for_build_wheel({}) == [] with pytest.raises(SystemExit) as exc: build_wheel("dist") assert exc.value.code == 7 out, _ = capsys.readouterr() assert "This is a test failure message" in out assert "fail setting was enabled" not in out scikit-build-core-0.11.1/tests/test_builder.py000066400000000000000000000216321477275177200213330ustar00rootroot00000000000000import os import platform import pprint import sys import sysconfig import typing import unittest.mock from pathlib import Path from types import SimpleNamespace import pytest from scikit_build_core.builder.builder import Builder, archs_to_tags, get_archs from scikit_build_core.builder.macos import get_macosx_deployment_target from scikit_build_core.builder.sysconfig import ( get_python_include_dir, get_python_library, ) from scikit_build_core.builder.wheel_tag import WheelTag from scikit_build_core.cmake import CMaker from scikit_build_core.settings.skbuild_model import ( BuildSettings, ScikitBuildSettings, WheelSettings, ) # The envvar_higher case shouldn't happen, but the compiler should cause the # correct failure @pytest.mark.parametrize( ("pycom", "envvar", "answer"), [ pytest.param("12.5.2", None, "12.0", id="only_plat_round"), pytest.param("10.12.2", None, "10.12", id="only_plat_classic"), pytest.param("10.12.2", "10.11", "10.11", id="env_var_lower"), pytest.param("10.12.2", "10.13", "10.13", id="env_var_higher"), pytest.param("11.2.12", "11.2", "11.0", id="same_vars_round"), pytest.param("12.1.2", "11", "11.0", id="env_var_no_dot"), pytest.param("11.2.12", "random", "11.0", id="invalid_env_var"), pytest.param("11.2.12", "rand.om", "11.0", id="invalid_env_var_with_dot"), ], ) def test_macos_version(monkeypatch, pycom, envvar, answer): monkeypatch.setattr(platform, "mac_ver", lambda: (pycom, "", "")) if envvar is None: monkeypatch.delenv("MACOSX_DEPLOYMENT_TARGET", raising=False) else: monkeypatch.setenv("MACOSX_DEPLOYMENT_TARGET", envvar) assert str(get_macosx_deployment_target(arm=False)) == answer def test_get_python_include_dir(): assert get_python_include_dir().is_dir() @pytest.mark.xfail( strict=False, reason="Doesn't matter if this fails, usually not used" ) def test_get_python_library(): pprint.pprint( { k: v for k, v in sysconfig.get_config_vars().items() if isinstance(v, str) and 128 > len(v) > 2 and not k.startswith("MODULE") } ) lib = get_python_library({}) if sysconfig.get_platform().startswith("win"): assert lib assert lib.is_file() else: assert lib is None @pytest.mark.skipif(not sysconfig.get_platform().startswith("win"), reason="MSVC only") def test_get_python_library_xcompile(tmp_path): config_path = tmp_path / "tmp.cfg" config_path.write_text( """\ [build_ext] library_dirs=C:\\Python\\libs """ ) env = {"DIST_EXTRA_CONFIG": str(config_path)} lib = get_python_library(env) assert lib assert lib.parent == Path("C:\\Python\\libs") assert lib.parent != Path("C:\\Python\\libs\\python3.lib") lib2 = get_python_library(env, abi3=True) assert lib2 assert lib2 == Path("C:\\Python\\libs\\python3.lib") @pytest.mark.parametrize("archs", [["x86_64"], ["arm64", "universal2"]]) def test_builder_macos_arch(monkeypatch, archs): archflags = " ".join(f"-arch {arch}" for arch in archs) monkeypatch.setattr(sys, "platform", "darwin") assert get_archs({"ARCHFLAGS": archflags}) == archs def test_builder_macos_arch_extra(monkeypatch): archflags = "-arch arm64 -arch x86_64" monkeypatch.setattr(sys, "platform", "darwin") monkeypatch.setenv("ARCHFLAGS", archflags) tmpcfg = typing.cast("CMaker", SimpleNamespace(env=os.environ.copy())) tmpbuilder = Builder( settings=ScikitBuildSettings(wheel=WheelSettings()), config=tmpcfg, ) assert archs_to_tags(get_archs(tmpbuilder.config.env)) == ["universal2"] @pytest.mark.parametrize( ("cmake_args", "answer"), [ ("-DA=1 -DB=2", ["-DA=1", "-DB=2"]), (r"-DA='1 1' -DB=\'2\'", ["-DA=1 1", "-DB='2'"]), (r'-DA="1 1" -DB=\"2\"', ["-DA=1 1", '-DB="2"']), ('"-DA=1 1" -DB=2', ["-DA=1 1", "-DB=2"]), ], ) def test_builder_get_cmake_args(monkeypatch, cmake_args, answer): monkeypatch.setenv("CMAKE_ARGS", cmake_args) tmpcfg = typing.cast("CMaker", SimpleNamespace(env=os.environ.copy())) tmpbuilder = Builder( settings=ScikitBuildSettings(wheel=WheelSettings()), config=tmpcfg, ) assert tmpbuilder.get_cmake_args() == answer def test_build_tool_args(): config = unittest.mock.create_autospec(CMaker) settings = ScikitBuildSettings(build=BuildSettings(tool_args=["b"])) tmpbuilder = Builder( settings=settings, config=typing.cast("CMaker", config), ) tmpbuilder.build(["a"]) config.build.assert_called_once_with( build_args=["a", "--", "b"], targets=[], verbose=settings.build.verbose ) @pytest.mark.parametrize( ("minver", "archs", "answer"), [ pytest.param("10.12", ["x86_64"], "macosx_10_12_x86_64", id="10.12_x86_64"), pytest.param("10.12", ["arm64"], "macosx_11_0_arm64", id="10.12_arm64"), pytest.param( "10.12", ["universal2"], "macosx_10_12_universal2", id="10.12_universal2" ), ], ) def test_wheel_tag(monkeypatch, minver, archs, answer): get_config_var = sysconfig.get_config_var monkeypatch.setattr( sysconfig, "get_config_var", lambda x: None if x == "Py_GIL_DISABLED" else get_config_var(x), ) monkeypatch.setattr(sys, "platform", "darwin") monkeypatch.setenv("MACOSX_DEPLOYMENT_TARGET", minver) monkeypatch.setattr(platform, "mac_ver", lambda: ("10.9.2", "", "")) tags = WheelTag.compute_best(archs) plat = str(tags).split("-")[-1] assert plat == answer @pytest.mark.parametrize("archs", ["x86_64", "arm64", "universal2"]) def test_wheel_build_tag(monkeypatch, archs): get_config_var = sysconfig.get_config_var monkeypatch.setattr( sysconfig, "get_config_var", lambda x: None if x == "Py_GIL_DISABLED" else get_config_var(x), ) monkeypatch.setattr(sys, "platform", "darwin") monkeypatch.setenv("MACOSX_DEPLOYMENT_TARGET", "10.12") monkeypatch.setattr(platform, "mac_ver", lambda: ("10.9.2", "", "")) tags = WheelTag.compute_best(archs, build_tag="1") answer = str(tags).split("-")[0] assert answer == "1" def test_wheel_tag_expand(monkeypatch): get_config_var = sysconfig.get_config_var monkeypatch.setattr( sysconfig, "get_config_var", lambda x: None if x == "Py_GIL_DISABLED" else get_config_var(x), ) monkeypatch.setattr(sys, "platform", "darwin") monkeypatch.setenv("MACOSX_DEPLOYMENT_TARGET", "10.10") monkeypatch.setattr(platform, "mac_ver", lambda: ("10.9.2", "", "")) tags = WheelTag.compute_best(["universal2"]) plat = str(tags).split("-")[-1] assert plat == "macosx_10_10_universal2" tags = WheelTag.compute_best(["universal2"], expand_macos=True) plat = str(tags).split("-")[-1] assert ( plat == "macosx_10_10_universal2.macosx_11_0_universal2.macosx_10_10_x86_64.macosx_11_0_arm64" ) def test_wheel_tag_expand_11(monkeypatch): get_config_var = sysconfig.get_config_var monkeypatch.setattr( sysconfig, "get_config_var", lambda x: None if x == "Py_GIL_DISABLED" else get_config_var(x), ) monkeypatch.setattr(sys, "platform", "darwin") monkeypatch.setenv("MACOSX_DEPLOYMENT_TARGET", "11.2") monkeypatch.setattr(platform, "mac_ver", lambda: ("10.9.2", "", "")) tags = WheelTag.compute_best(["universal2"]) plat = str(tags).split("-")[-1] assert plat == "macosx_11_0_universal2" tags = WheelTag.compute_best(["universal2"], expand_macos=True) plat = str(tags).split("-")[-1] assert plat == "macosx_11_0_universal2.macosx_11_0_x86_64.macosx_11_0_arm64" def test_wheel_tag_with_abi_darwin(monkeypatch): get_config_var = sysconfig.get_config_var monkeypatch.setattr( sysconfig, "get_config_var", lambda x: None if x == "Py_GIL_DISABLED" else get_config_var(x), ) monkeypatch.setattr(sys, "platform", "darwin") monkeypatch.setenv("MACOSX_DEPLOYMENT_TARGET", "10.10") monkeypatch.setattr(platform, "mac_ver", lambda: ("10.9.2", "", "")) tags = WheelTag.compute_best(["x86_64"], py_api="cp39") if sys.version_info < (3, 9) or sys.implementation.name != "cpython": assert "macosx_10_10_x86_64" in str(tags) assert "abi3" not in str(tags) assert "cp39" not in str(tags) else: assert str(tags) == "cp39-abi3-macosx_10_10_x86_64" tags = WheelTag.compute_best(["x86_64"], py_api="cp38") if sys.implementation.name != "cpython": assert "macosx_10_10_x86_64" in str(tags) assert "abi3" not in str(tags) assert "cp38" not in str(tags) else: assert str(tags) == "cp38-abi3-macosx_10_10_x86_64" tags = WheelTag.compute_best(["x86_64"], py_api="py3") assert str(tags) == "py3-none-macosx_10_10_x86_64" tags = WheelTag.compute_best(["x86_64"], py_api="py2.py3") assert str(tags) == "py2.py3-none-macosx_10_10_x86_64" scikit-build-core-0.11.1/tests/test_cmake_ast.py000066400000000000000000000071611477275177200216350ustar00rootroot00000000000000import textwrap from pathlib import Path import pytest from scikit_build_core.ast.ast import parse from scikit_build_core.ast.tokenizer import tokenize DIR = Path(__file__).parent.absolute() FILENAMES = [ *DIR.joinpath("packages").rglob("**/CMakeLists.txt"), *DIR.parent.joinpath("docs/examples").rglob("**/CMakeLists.txt"), ] IDS = [str(p.relative_to(DIR.parent).parent) for p in FILENAMES] @pytest.mark.parametrize("filename", FILENAMES, ids=IDS) def test_cmake_file_parse(filename: Path): for x in parse(tokenize(filename.read_text(encoding="utf-8"))): assert str(x).startswith(f"{x.name}({x.value})") def test_cmake_ast_parse(): txt = textwrap.dedent("""\ # [[[ Not a block comment cmake_minimum_required(VERSION 3.25...3.30) # ]]] Not a block comment #[[ A block comment invalid syntax ]] if(True) block() my_function() endblock() endif() """) ast = list(parse(tokenize(txt))) assert ast[0].name == "cmake_minimum_required" assert ast[1].name == "if" assert ast[1].contents[0].name == "block" # type: ignore[attr-defined] assert ast[1].contents[0].contents[0].name == "my_function" # type: ignore[attr-defined] def test_cmake_ast_parse_long(): txt = textwrap.dedent( """\ # CMakeLists.txt - Example for Tokenization cmake_minimum_required(VERSION 3.10) # Set the project name project(TokenizationExample VERSION 1.0) # Include directories include_directories( ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/external [[ Multiline string ]] ) # Add executable add_executable(TokenizationExample src/main.cpp src/utils.cpp ) # Set C++ standard set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED True) # Link libraries target_link_libraries(TokenizationExample PUBLIC SomeLibrary AnotherLibrary ) #[[ Block comment example This is a block comment. It spans multiple lines. It is used to provide more detailed explanations or to temporarily disable code. ]] # Multiline string example set(MULTILINE_STRING "This is a multiline string \ that spans multiple lines. \ It is often used for setting large chunks of text \ or configurations.") # Multiline function call example add_library(MyLibrary ${PROJECT_SOURCE_DIR}/src/lib.cpp ${PROJECT_SOURCE_DIR}/src/helper.cpp ${PROJECT_SOURCE_DIR}/src/another_helper.cpp ) #[==[ Another block comment example The following block of code adds a custom target and a custom command to run some script. It is useful for adding pre-build or post-build steps. ]=] and ]] and ]===] can't confuse it. function(not real) ]==] if(DEFINED VALUE) add_custom_target(RunScript ALL COMMAND ${CMAKE_COMMAND} -E echo "Running custom script" COMMAND ${CMAKE_COMMAND} -E touch ${PROJECT_BINARY_DIR}/dummy_file ) # trailing comment # Set properties for the custom target set_target_properties(RunScript PROPERTIES COMMENT "This target runs a custom script." ) endif() # End of CMakeLists.txt """ ) for _ in parse(tokenize(txt)): pass scikit-build-core-0.11.1/tests/test_cmake_config.py000066400000000000000000000155311477275177200223130ustar00rootroot00000000000000from __future__ import annotations import os import shutil import sysconfig from pathlib import Path from textwrap import dedent from typing import TYPE_CHECKING import pytest from packaging.specifiers import SpecifierSet from packaging.version import Version from scikit_build_core.builder.builder import Builder from scikit_build_core.cmake import CMake, CMaker from scikit_build_core.errors import CMakeNotFoundError from scikit_build_core.settings.skbuild_read_settings import SettingsReader if TYPE_CHECKING: from collections.abc import Generator DIR = Path(__file__).parent.resolve() def single_config(param: None | str) -> bool: if param is None: return not sysconfig.get_platform().startswith("win") return param in {"Ninja", "Makefiles"} @pytest.fixture( params=[ pytest.param(None, id="default"), pytest.param("Ninja", id="ninja"), pytest.param( "Makefiles", id="makefiles", marks=pytest.mark.skipif( sysconfig.get_platform().startswith("win"), reason="run on Windows only" ), ), pytest.param( "Others", id="others", marks=pytest.mark.skipif( sysconfig.get_platform().startswith("win"), reason="run on Windows only" ), ), ] ) def generator( request: pytest.FixtureRequest, monkeypatch: pytest.MonkeyPatch ) -> str | None: if request.param is None: monkeypatch.delenv("CMAKE_GENERATOR", raising=False) else: monkeypatch.setenv("CMAKE_GENERATOR", request.param) return request.param # type: ignore[no-any-return] def configure_args( config: CMaker, *, init: bool = False, single_config: bool = False ) -> Generator[str, None, None]: yield f"-S{config.source_dir}" yield f"-B{config.build_dir}" if single_config: yield f"-DCMAKE_BUILD_TYPE:STRING={config.build_type}" if init: cmake_init = config.build_dir / "CMakeInit.txt" yield f"-C{cmake_init}" @pytest.mark.configure def test_init_cache( generator: str, tmp_path: Path, fp, ): fp.register( [fp.program("cmake"), "-E", "capabilities"], stdout='{"version":{"string":"3.14.0"}}', ) fp.register( [fp.program("cmake3"), "-E", "capabilities"], stdout='{"version":{"string":"3.14.0"}}', ) config = CMaker( CMake.default_search(), source_dir=DIR / "packages/simple_pure", build_dir=tmp_path / "build", build_type="Release", ) config.init_cache( {"SKBUILD": True, "SKBUILD_VERSION": "1.0.0", "SKBUILD_PATH": config.source_dir} ) cmd = list( configure_args(config, init=True, single_config=single_config(generator)) ) print("Registering: cmake", *cmd) fp.register([fp.program("cmake"), *cmd]) fp.register([fp.program("cmake3"), *cmd]) config.configure() cmake_init = config.build_dir / "CMakeInit.txt" source_dir_str = str(config.source_dir).replace("\\", "/") assert ( cmake_init.read_text(encoding="utf-8") == f"""\ set(SKBUILD ON CACHE BOOL "" FORCE) set(SKBUILD_VERSION [===[1.0.0]===] CACHE STRING "" FORCE) set(SKBUILD_PATH [===[{source_dir_str}]===] CACHE PATH "" FORCE) """ ) @pytest.mark.configure def test_too_old(fp, monkeypatch): monkeypatch.setattr(shutil, "which", lambda _: None) fp.register( [fp.program("cmake"), "-E", "capabilities"], stdout='{"version":{"string":"3.14.0"}}', ) fp.register( [fp.program("cmake3"), "-E", "capabilities"], stdout='{"version":{"string":"3.14.0"}}', ) with pytest.raises(CMakeNotFoundError) as excinfo: CMake.default_search(version=SpecifierSet(">=3.15")) assert "Could not find CMake with version >=3.15" in excinfo.value.args[0] @pytest.mark.configure def test_cmake_args( generator: str, tmp_path: Path, fp, ): fp.register( [fp.program("cmake"), "-E", "capabilities"], stdout='{"version":{"string":"3.15.0"}}', ) fp.register( [fp.program("cmake3"), "-E", "capabilities"], stdout='{"version":{"string":"3.15.0"}}', ) config = CMaker( CMake.default_search(), source_dir=DIR / "packages" / "simple_pure", build_dir=tmp_path / "build", build_type="Release", ) cmd = list(configure_args(config, single_config=single_config(generator))) cmd.append("-DSOMETHING=one") print("Registering: cmake", *cmd) fp.register([fp.program("cmake"), *cmd]) fp.register([fp.program("cmake3"), *cmd]) config.configure(cmake_args=["-DSOMETHING=one"]) # config.configure might mutate config.single_config assert config.single_config == single_config(generator) assert len(fp.calls) == 2 @pytest.mark.configure def test_cmake_paths( generator: str, tmp_path: Path, fp, ): fp.register( [fp.program("cmake"), "-E", "capabilities"], stdout='{"version":{"string":"3.15.0"}}', ) fp.register( [fp.program("cmake3"), "-E", "capabilities"], stdout='{"version":{"string":"3.15.0"}}', ) config = CMaker( CMake.default_search(), source_dir=DIR / "packages/simple_pure", build_dir=tmp_path / "build", build_type="Release", prefix_dirs=[tmp_path / "prefix"], module_dirs=[tmp_path / "module"], ) cmd = list(configure_args(config, single_config=single_config(generator))) print("Registering: cmake", *cmd) fp.register([fp.program("cmake"), *cmd]) fp.register([fp.program("cmake3"), *cmd]) config.configure() assert len(fp.calls) == 2 @pytest.mark.configure def test_cmake_defines( tmp_path: Path, ): source_dir = DIR / "packages" / "cmake_defines" binary_dir = tmp_path / "build" config = CMaker( CMake.default_search(), source_dir=source_dir, build_dir=binary_dir, build_type="Release", ) reader = SettingsReader.from_file(source_dir / "pyproject.toml") builder = Builder(reader.settings, config) builder.configure(defines={}) configure_log = Path.read_text(binary_dir / "log.txt") assert configure_log == dedent( """\ ONE_LEVEL_LIST.LENGTH = 4 Foo Bar ExceptionallyLargeListEntryThatWouldOverflowTheLine Baz NESTED_LIST.LENGTH = 3 Apple Lemon;Lime Banana """ ) def test_get_cmake_via_envvar(monkeypatch: pytest.MonkeyPatch, fp): monkeypatch.setattr("shutil.which", lambda x: x) cmake_path = Path("some-prog") fp.register( [cmake_path, "-E", "capabilities"], stdout='{"version":{"string":"3.20.0"}}' ) monkeypatch.setenv("CMAKE_EXECUTABLE", str(cmake_path)) result = CMake.default_search(env=os.environ) assert result.cmake_path == cmake_path assert result.version == Version("3.20.0") scikit-build-core-0.11.1/tests/test_custom_modules.py000066400000000000000000000016351477275177200227500ustar00rootroot00000000000000from __future__ import annotations import sys from pathlib import Path import pytest DIR = Path(__file__).parent PROJECT_DIR = DIR / "packages" / "custom_cmake" def test_ep(isolated): isolated.install("hatchling", "scikit-build-core") isolated.install(PROJECT_DIR / "extern", isolated=False) isolated.install(PROJECT_DIR, "-v", isolated=False) if sys.platform.startswith("win"): # TODO: maybe figure out how to make this work on windows? pytest.skip("Can't run script on Windows") script = isolated.run("script1", capture=True).strip() pysys = isolated.execute("import sys; print(sys.executable)").strip() contents = Path(script).read_text(encoding="utf-8") assert contents.startswith(f"#!{pysys}") assert ( isolated.execute( "from importlib import metadata; print(metadata.version('custom_modules'), end='')" ) == "2.3.4" ) scikit-build-core-0.11.1/tests/test_dynamic_metadata.py000066400000000000000000000303341477275177200231700ustar00rootroot00000000000000from __future__ import annotations import importlib import shutil import subprocess import textwrap import types import zipfile from pathlib import Path from typing import TYPE_CHECKING, Any import pytest from packaging.version import Version from scikit_build_core._compat import tomllib from scikit_build_core._vendor import pyproject_metadata from scikit_build_core.build import build_wheel from scikit_build_core.build.metadata import get_standard_metadata from scikit_build_core.builder.get_requires import GetRequires from scikit_build_core.metadata import regex from scikit_build_core.settings.skbuild_read_settings import SettingsReader from pathutils import contained if TYPE_CHECKING: from typing import Literal # these are mock plugins returning known results # it turns out to be easier to create EntryPoint objects pointing to real # functions than to mock them. def ep_version( field: str, _settings: dict[str, object] | None = None, ) -> str: assert field == "version" return "0.0.2" def ep_readme( field: str, _settings: dict[str, object] | None = None, ) -> str | dict[str, str | None]: assert field == "readme" return { "content-type": "text/x-rst", "text": "Some text", } def ep_license( field: str, _settings: dict[str, object] | None = None, ) -> dict[str, str | None]: assert field == "license" return {"text": "MIT License"} def ep_dual( _field: str, _settings: dict[str, object] | None = None, ) -> str | dict[str, str | None]: # Fields intentionally not checked to verify backend error thrown if _field == "version": return "0.3" if _field == "license": return {"text": "BSD License"} msg = f"Invalid field {_field}" raise KeyError(msg) original_loader = importlib.import_module def special_loader(name: str, *args: Any, **kwargs: Any) -> Any: if name == "test_version": test_version = types.ModuleType("test_version") test_version.dynamic_metadata = ep_version # type: ignore[attr-defined] return test_version if name == "test_readme": test_readme = types.ModuleType("test_readme") test_readme.dynamic_metadata = ep_readme # type: ignore[attr-defined] return test_readme if name == "test_license": test_license = types.ModuleType("test_license") test_license.dynamic_metadata = ep_license # type: ignore[attr-defined] return test_license if name == "test_dual": test_dual = types.ModuleType("test_dual") test_dual.dynamic_metadata = ep_dual # type: ignore[attr-defined] return test_dual return original_loader(name, *args, **kwargs) @pytest.fixture def mock_entry_points(monkeypatch): monkeypatch.setattr(importlib, "import_module", special_loader) @pytest.mark.usefixtures("mock_entry_points", "package_dynamic_metadata") def test_dynamic_metadata(): with Path("pyproject.toml").open("rb") as ft: pyproject = tomllib.load(ft) settings_reader = SettingsReader(pyproject, {}, state="metadata_wheel") settings = settings_reader.settings settings_reader.validate_may_exit() metadata = get_standard_metadata(pyproject, settings) assert str(metadata.version) == "0.0.2" assert metadata.license == pyproject_metadata.License("MIT License", None) assert metadata.readme == pyproject_metadata.Readme("Some text", None, "text/x-rst") @pytest.mark.usefixtures("package_dynamic_metadata") def test_plugin_metadata(): reason_msg = ( "install hatch-fancy-pypi-readme and setuptools-scm to test the " "dynamic metadata plugins" ) pytest.importorskip("hatch_fancy_pypi_readme", reason=reason_msg) pytest.importorskip("setuptools_scm", reason=reason_msg) if shutil.which("git") is None: pytest.skip("git is not installed") shutil.copy("plugin_project.toml", "pyproject.toml") subprocess.run(["git", "init", "--initial-branch=main"], check=True) subprocess.run(["git", "config", "user.name", "bot"], check=True) subprocess.run(["git", "config", "user.email", "bot@scikit-build.org"], check=True) subprocess.run(["git", "add", "pyproject.toml"], check=True) subprocess.run(["git", "commit", "-m", "initial commit"], check=True) subprocess.run(["git", "tag", "v0.1.0", "-m", "initial commint"], check=True) with Path("pyproject.toml").open("rb") as ft: pyproject = tomllib.load(ft) settings_reader = SettingsReader(pyproject, {}, state="metadata_wheel") settings = settings_reader.settings settings_reader.validate_may_exit() metadata = get_standard_metadata(pyproject, settings) assert str(metadata.version) == "0.1.0" assert metadata.readme == pyproject_metadata.Readme( "Fragment #1Fragment #2", None, "text/x-rst" ) assert set(GetRequires().dynamic_metadata()) == { "hatch-fancy-pypi-readme>=22.3", "setuptools-scm", } @pytest.mark.usefixtures("package_dynamic_metadata") def test_faulty_metadata(): with Path("faulty_project.toml").open("rb") as ft: pyproject = tomllib.load(ft) settings_reader = SettingsReader(pyproject, {}, state="metadata_wheel") settings = settings_reader.settings settings_reader.validate_may_exit() with pytest.raises(KeyError): get_standard_metadata(pyproject, settings) @pytest.mark.usefixtures("package_dynamic_metadata") def test_local_plugin_metadata(): with Path("local_pyproject.toml").open("rb") as ft: pyproject = tomllib.load(ft) settings_reader = SettingsReader(pyproject, {}, state="metadata_wheel") settings = settings_reader.settings settings_reader.validate_may_exit() metadata = get_standard_metadata(pyproject, settings) assert metadata.version == Version("3.2.1") @pytest.mark.usefixtures("package_dynamic_metadata") def test_warn_metadata(): with Path("warn_project.toml").open("rb") as ft: pyproject = tomllib.load(ft) settings_reader = SettingsReader(pyproject, {}, state="metadata_wheel") settings = settings_reader.settings settings_reader.validate_may_exit() with pytest.raises(ModuleNotFoundError): get_standard_metadata(pyproject, settings) @pytest.mark.usefixtures("package_dynamic_metadata") def test_fail_experimental_metadata(): with Path("warn_project.toml").open("rb") as ft: pyproject = tomllib.load(ft) settings_reader = SettingsReader( pyproject, {"experimental": "false"}, state="metadata_wheel" ) with pytest.raises(SystemExit) as exc: settings_reader.validate_may_exit() (value,) = exc.value.args assert value == 7 @pytest.mark.usefixtures("mock_entry_points", "package_dynamic_metadata") def test_dual_metadata(): with Path("dual_project.toml").open("rb") as ft: pyproject = tomllib.load(ft) settings_reader = SettingsReader(pyproject, {}, state="metadata_wheel") settings = settings_reader.settings settings_reader.validate_may_exit() metadata = get_standard_metadata(pyproject, settings) assert str(metadata.version) == "0.3" assert metadata.license == pyproject_metadata.License("BSD License", None) with Path("faulty_dual_project.toml").open("rb") as ft: pyproject = tomllib.load(ft) settings_reader = SettingsReader(pyproject, {}, state="metadata_wheel") settings = settings_reader.settings settings_reader.validate_may_exit() with pytest.raises(KeyError): get_standard_metadata(pyproject, settings) @pytest.mark.compile @pytest.mark.configure @pytest.mark.usefixtures("mock_entry_points", "package_dynamic_metadata") def test_pep517_wheel(virtualenv, tmp_path: Path) -> None: dist = tmp_path / "dist" out = build_wheel(str(dist)) (wheel,) = dist.glob("dynamic-0.0.2-*.whl") assert wheel == dist / out virtualenv.install(wheel) license = virtualenv.execute( "from importlib.metadata import metadata; print(metadata('dynamic')['License'])" ) assert license == "MIT License" with zipfile.ZipFile(wheel) as zf: file_paths = {Path(n) for n in zf.namelist()} dynamic_pkg = {x.name for x in contained(file_paths, "dynamic")} filtered_pkg = {x for x in dynamic_pkg if not x.startswith("_module")} assert len(filtered_pkg) == len(dynamic_pkg) - 1 assert {"dynamic-0.0.2.dist-info", "dynamic"} == {p.parts[0] for p in file_paths} assert {"__init__.py"} == filtered_pkg version = virtualenv.execute("from dynamic import square; print(square(2))") assert version == "4.0" def test_regex(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: d = tmp_path / "test_regex" d.mkdir() monkeypatch.chdir(d) with Path("__init__.py").open("w") as f: f.write("__version__ = '0.1.0'") regex.dynamic_metadata("version", {"input": "__init__.py"}) def test_regex_errors() -> None: with pytest.raises(RuntimeError): regex.dynamic_metadata("version", {}) with pytest.raises(RuntimeError, match="Only string fields supported"): regex.dynamic_metadata("author", {"input": "x", "regex": "x"}) def test_multipart_regex(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: d = tmp_path / "test_multi_regex" d.mkdir() monkeypatch.chdir(d) with Path("version.hpp").open("w") as f: f.write( textwrap.dedent( """\ #define VERSION_MAJOR 1 // Comment #define VERSION_MINOR 2 #define VERSION_PATCH 3dev1 """ ) ) version = regex.dynamic_metadata( "version", { "input": "version.hpp", "regex": r"""(?sx) \#define \s+ VERSION_MAJOR \s+ (?P\d+) .*? \#define \s+ VERSION_MINOR \s+ (?P\d+) .*? \#define \s+ VERSION_PATCH \s+ (?P\w+) """, "result": "{major}.{minor}.{patch}", }, ) assert version == "1.2.3dev1" @pytest.mark.parametrize("dev", [0, 1]) def test_regex_remove( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, dev: int ) -> None: d = tmp_path / "test_multidev_regex" d.mkdir() monkeypatch.chdir(d) with Path("version.hpp").open("w") as f: f.write( textwrap.dedent( f"""\ #define VERSION_MAJOR 1 // Comment #define VERSION_MINOR 2 #define VERSION_PATCH 3 #define VERSION_DEV {dev} """ ) ) version = regex.dynamic_metadata( "version", { "input": "version.hpp", "regex": r"""(?sx) \#define \s+ VERSION_MAJOR \s+ (?P\d+) .*? \#define \s+ VERSION_MINOR \s+ (?P\d+) .*? \#define \s+ VERSION_PATCH \s+ (?P\d+) .*? \#define \s+ VERSION_DEV \s+ (?P\d+) """, "result": "{major}.{minor}.{patch}dev{dev}", "remove": r"dev0", }, ) assert version == ("1.2.3dev1" if dev else "1.2.3") @pytest.mark.usefixtures("package_dynamic_metadata") @pytest.mark.parametrize("override", [None, "env", "sdist"]) def test_build_requires_field(override, monkeypatch) -> None: shutil.copy("build_requires_project.toml", "pyproject.toml") if override == "env": monkeypatch.setenv("LOCAL_FOO", "True") else: monkeypatch.delenv("LOCAL_FOO", raising=False) pyproject_path = Path("pyproject.toml") with pyproject_path.open("rb") as ft: pyproject = tomllib.load(ft) state: Literal["sdist", "metadata_wheel"] = ( "sdist" if override == "sdist" else "metadata_wheel" ) settings_reader = SettingsReader(pyproject, {}, state=state) settings_reader.validate_may_exit() if override is None: assert set(GetRequires().dynamic_metadata()) == { "foo", } elif override == "env": # evaluate ../foo as uri foo_path = pyproject_path.absolute().parent.parent / "foo" foo_path = foo_path.absolute() assert set(GetRequires().dynamic_metadata()) == { f"foo @ {foo_path.as_uri()}", } elif override == "sdist": assert set(GetRequires().dynamic_metadata()) == { # TODO: Check if special handling should be done for sdist "foo", } scikit-build-core-0.11.1/tests/test_editable.py000066400000000000000000000112301477275177200214470ustar00rootroot00000000000000import sys import textwrap from pathlib import Path import pytest from conftest import PackageInfo, process_package @pytest.mark.compile @pytest.mark.configure @pytest.mark.integration @pytest.mark.parametrize("isolate", [True, False], ids=["isolated", "notisolated"]) @pytest.mark.parametrize( "package", [ pytest.param( True, id="package", marks=[pytest.mark.xfail(reason="Only data folders supported currently")], ), pytest.param(False, id="datafolder"), ], ) @pytest.mark.usefixtures("navigate_editable") @pytest.mark.xfail( sys.version_info[:2] == (3, 9), reason="Python 3.9 not supported yet" ) def test_navigate_editable(isolated, isolate, package): isolate_args = ["--no-build-isolation"] if not isolate else [] isolated.install("pip>=23") if not isolate: isolated.install("scikit-build-core") if package: init_py = Path("python/shared_pkg/data/__init__.py") init_py.touch() isolated.install( "-v", "--config-settings=build-dir=build/{wheel_tag}", *isolate_args, "-e", "." ) value = isolated.execute("import shared_pkg; shared_pkg.call_c_method()") assert value == "c_method" value = isolated.execute("import shared_pkg; shared_pkg.call_py_method()") assert value == "py_method" value = isolated.execute("import shared_pkg; shared_pkg.read_py_data_txt()") assert value == "Some_value_Py" value = isolated.execute("import shared_pkg; shared_pkg.read_c_generated_txt()") assert value == "Some_value_C" @pytest.mark.compile @pytest.mark.configure @pytest.mark.integration @pytest.mark.parametrize( ("editable", "editable_mode"), [(False, ""), (True, "redirect"), (True, "inplace")] ) def test_cython_pxd(monkeypatch, tmp_path, editable, editable_mode, isolated): editable_flag = ["-e"] if editable else [] config_mode_flags = [] if editable: config_mode_flags.append(f"--config-settings=editable.mode={editable_mode}") if editable_mode != "inplace": config_mode_flags.append("--config-settings=build-dir=build/{wheel_tag}") package1 = PackageInfo( "cython_pxd_editable/pkg1", ) tmp_path1 = tmp_path / "pkg1" tmp_path1.mkdir() process_package(package1, tmp_path1, monkeypatch) ninja = [ "ninja" for f in isolated.wheelhouse.iterdir() if f.name.startswith("ninja-") ] cmake = [ "cmake" for f in isolated.wheelhouse.iterdir() if f.name.startswith("cmake-") ] isolated.install("pip>23") isolated.install("cython", "scikit-build-core", *ninja, *cmake) isolated.install( "-v", *config_mode_flags, "--no-build-isolation", *editable_flag, ".", ) package2 = PackageInfo( "cython_pxd_editable/pkg2", ) tmp_path2 = tmp_path / "pkg2" tmp_path2.mkdir() process_package(package2, tmp_path2, monkeypatch) isolated.install( "-v", *config_mode_flags, "--no-build-isolation", *editable_flag, ".", ) @pytest.mark.compile @pytest.mark.configure @pytest.mark.integration @pytest.mark.usefixtures("package_simplest_c") def test_install_dir(isolated): isolated.install("pip>=23") isolated.install("scikit-build-core") settings_overrides = { "build-dir": "build/{wheel_tag}", "wheel.install-dir": "other_pkg", "editable.rebuild": "true", } # Create a dummy other_pkg package to satisfy the import other_pkg_src = Path("./src/other_pkg") other_pkg_src.joinpath("simplest").mkdir(parents=True) other_pkg_src.joinpath("__init__.py").write_text( textwrap.dedent( """ from .simplest._module import square """ ) ) other_pkg_src.joinpath("simplest/__init__.py").touch() isolated.install( "-v", *[f"--config-settings={k}={v}" for k, v in settings_overrides.items()], "--no-build-isolation", "-e", ".", ) # Make sure the package is correctly installed in the subdirectory other_pkg_path = isolated.platlib / "other_pkg" c_module_glob = list(other_pkg_path.glob("simplest/_module*")) assert len(c_module_glob) == 1 c_module = c_module_glob[0] assert c_module.exists() # If `install-dir` was not taken into account it would install here failed_c_module = other_pkg_path / "../simplest" / c_module.name assert not failed_c_module.exists() # Run an import in order to re-trigger the rebuild and check paths again out = isolated.execute("import other_pkg.simplest") assert "Running cmake" in out assert c_module.exists() assert not failed_c_module.exists() scikit-build-core-0.11.1/tests/test_editable_redirect.py000066400000000000000000000042661477275177200233430ustar00rootroot00000000000000from __future__ import annotations from pathlib import Path from scikit_build_core.resources._editable_redirect import ScikitBuildRedirectingFinder def process_dict(d: dict[str, str]) -> dict[str, str]: return {k: str(Path(v)) for k, v in d.items()} def process_dict_set(d: dict[str, set[str]]) -> dict[str, set[str]]: return {k: {str(Path(x)) for x in v} for k, v in d.items()} def test_editable_redirect(): known_source_files = process_dict( { "pkg": "/source/pkg/__init__.py", "pkg.module": "/source/pkg/module.py", "pkg.subpkg": "/source/pkg/subpkg/__init__.py", "pkg.subpkg.module": "/source/pkg/subpkg/module.py", "pkg.resources.file": "/source/pkg/resources/file.txt", "pkg.namespace.module": "/source/pkg/namespace/module.py", } ) known_wheel_files = process_dict( { "pkg.subpkg.source": "pkg/subpkg/source.py", "pkg.src_files": "pkg/src_files.py", "pkg.namespace.source": "pkg/namespace/source.py", "pkg.iresources.file": "pkg/iresources/file.txt", "pkg.installed_files": "pkg/installed_files.py", "pkg.source": "pkg/source.py", } ) finder = ScikitBuildRedirectingFinder( known_source_files=known_source_files, known_wheel_files=known_wheel_files, path=None, rebuild=False, verbose=False, build_options=[], install_options=[], dir=str(Path("/sitepackages")), install_dir="", ) assert finder.submodule_search_locations == process_dict_set( { "pkg": { "/sitepackages/pkg", "/source/pkg", }, "pkg.iresources": { "/sitepackages/pkg/iresources", }, "pkg.namespace": { "/sitepackages/pkg/namespace", "/source/pkg/namespace", }, "pkg.resources": {"/source/pkg/resources"}, "pkg.subpkg": { "/sitepackages/pkg/subpkg", "/source/pkg/subpkg", }, } ) assert finder.pkgs == ["pkg", "pkg.subpkg"] scikit-build-core-0.11.1/tests/test_editable_unit.py000066400000000000000000000144341477275177200225170ustar00rootroot00000000000000from __future__ import annotations import sys import textwrap import typing from pathlib import Path from typing import NamedTuple import pytest from scikit_build_core.build._editable import ( editable_redirect, libdir_to_installed, mapping_to_modules, ) from scikit_build_core.build._pathutil import packages_to_file_mapping if typing.TYPE_CHECKING: from conftest import VEnv class EditablePackage(NamedTuple): site_packages: Path pkg_dir: Path src_pkg_dir: Path @pytest.fixture( params=[ pytest.param(False, id="abs"), pytest.param(True, id="rel"), ] ) def editable_package( request: pytest.FixtureRequest, tmp_path: Path, virtualenv: VEnv, monkeypatch: pytest.MonkeyPatch, ): rel = request.param prefix = "" if rel else "pkg" source_dir = tmp_path / "source" source_dir.mkdir() # Functions in build.wheel require running from this dir monkeypatch.chdir(source_dir) site_packages = virtualenv.purelib # Create a fake package pkg_dir = site_packages / "pkg" pkg_dir.mkdir() src_pkg_dir = source_dir / "pkg" src_pkg_dir.mkdir() # Make some fake files src_pkg_dir.joinpath("__init__.py").touch() src_pkg_dir.joinpath("module.py").write_text( textwrap.dedent( f"""\ from {prefix}.subpkg import module from {prefix}.subpkg import source from {prefix}.namespace import module from {prefix}.namespace import source """ ) ) pkg_dir.joinpath("source.py").write_text( textwrap.dedent( f"""\ from {prefix}.subpkg import module from {prefix}.subpkg import source from {prefix}.namespace import module from {prefix}.namespace import source """ ) ) pkg_dir.joinpath("src_files.py").write_text( textwrap.dedent( """\ import sys from importlib.resources import files read_file = files("pkg.resources").joinpath("file.txt").read_text(encoding="utf-8") assert read_file == "hello" """ ) ) resources_dir = src_pkg_dir / "resources" resources_dir.mkdir() resources_dir.joinpath("file.txt").write_text("hello") pkg_dir.joinpath("installed_files.py").write_text( textwrap.dedent( """\ from importlib.resources import files read_file = files("pkg.iresources").joinpath("file.txt").read_text(encoding="utf-8") assert read_file == "hi" """ ) ) iresources_dir = pkg_dir / "iresources" iresources_dir.mkdir() iresources_dir.joinpath("file.txt").write_text("hi") src_sub_package = src_pkg_dir / "subpkg" src_sub_package.mkdir() src_sub_package.joinpath("__init__.py").touch() src_sub_package.joinpath("module.py").touch() sub_package = pkg_dir / "subpkg" sub_package.mkdir() sub_package.joinpath("source.py").touch() src_namespace_pkg = src_pkg_dir / "namespace" src_namespace_pkg.mkdir() src_namespace_pkg.joinpath("module.py").touch() namespace_pkg = pkg_dir / "namespace" namespace_pkg.mkdir() namespace_pkg.joinpath("source.py").touch() return EditablePackage(site_packages, pkg_dir, src_pkg_dir) @pytest.mark.xfail( sys.version_info[:2] == (3, 9), reason="Python 3.9 not supported yet" ) def test_navigate_editable_pkg(editable_package: EditablePackage, virtualenv: VEnv): site_packages, pkg_dir, src_pkg_dir = editable_package # Create a fake editable install packages = {"pkg": "pkg"} mapping = packages_to_file_mapping( packages=packages, platlib_dir=site_packages, include=[], src_exclude=[], target_exclude=[], build_dir="", ) assert mapping == { str(Path("pkg/__init__.py")): str(pkg_dir / "__init__.py"), str(Path("pkg/module.py")): str(pkg_dir / "module.py"), str(Path("pkg/namespace/module.py")): str(pkg_dir / "namespace/module.py"), str(Path("pkg/subpkg/__init__.py")): str(pkg_dir / "subpkg/__init__.py"), str(Path("pkg/subpkg/module.py")): str(pkg_dir / "subpkg/module.py"), str(Path("pkg/resources/file.txt")): str(pkg_dir / "resources/file.txt"), } modules = mapping_to_modules(mapping, libdir=site_packages) assert modules == { "pkg": str(src_pkg_dir / "__init__.py"), "pkg.module": str(src_pkg_dir / "module.py"), "pkg.namespace.module": str(src_pkg_dir / "namespace/module.py"), "pkg.subpkg": str(src_pkg_dir / "subpkg/__init__.py"), "pkg.subpkg.module": str(src_pkg_dir / "subpkg/module.py"), "pkg.resources.file": str(src_pkg_dir / "resources/file.txt"), } installed = libdir_to_installed(site_packages) installed = {k: v for k, v in installed.items() if k.startswith("pkg")} assert installed == { "pkg.subpkg.source": str(Path("pkg/subpkg/source.py")), "pkg.namespace.source": str(Path("pkg/namespace/source.py")), "pkg.source": str(Path("pkg/source.py")), "pkg.installed_files": str(Path("pkg/installed_files.py")), "pkg.iresources.file": str(Path("pkg/iresources/file.txt")), "pkg.src_files": str(Path("pkg/src_files.py")), } editable_txt = editable_redirect( modules=modules, installed=installed, reload_dir=None, rebuild=False, verbose=False, build_options=[], install_options=[], install_dir="", ) site_packages.joinpath("_pkg_editable.py").write_text(editable_txt) site_packages.joinpath("_pkg_editable.pth").write_text("import _pkg_editable\n") # Test the editable install virtualenv.execute("import pkg.subpkg") virtualenv.execute("import pkg.subpkg.module") virtualenv.execute("import pkg.subpkg.source") virtualenv.execute("import pkg.namespace.module") virtualenv.execute("import pkg.namespace.source") # This allows debug print statements in _editable_redirect.py to be seen print(virtualenv.execute("import pkg.module")) print(virtualenv.execute("import pkg.source")) # Load resource files if sys.version_info >= (3, 9): virtualenv.execute("import pkg.src_files") virtualenv.execute("import pkg.installed_files") scikit-build-core-0.11.1/tests/test_file_processor.py000066400000000000000000000047151477275177200227260ustar00rootroot00000000000000from __future__ import annotations import sys from pathlib import Path import pytest from scikit_build_core.build._file_processor import each_unignored_file @pytest.mark.skipif( sys.implementation.name == "pypy" and sys.platform.startswith("win"), reason="PyPy on Windows does not support symlinks", ) def test_on_each_with_symlink(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: """ Test that each_unignored_file() does not follow symlinks. """ monkeypatch.chdir(tmp_path) # Set up a gitignore gitignore = Path(".gitignore") gitignore.write_text("/hidden_dir") # Create a directory with a symlink to a file in the same directory dir = Path("dir") dir.mkdir() file1 = dir / "file" file1.write_text("content") file2 = dir / "link" file2.symlink_to("file") hidden_dir = Path("hidden_dir") hidden_dir.mkdir() hidden_file = hidden_dir / "file2" hidden_file.write_text("content2") exposed_symlink = dir / "exposed_symlink" exposed_symlink.symlink_to("../hidden_dir") local_ignored_file = Path("local_ignored_file.txt") Path(".git/info").mkdir(parents=True) Path(".git/info/exclude").write_text(f"{local_ignored_file}\n") nested_dir = Path("nested_dir") nested_dir.mkdir() nested_dir.joinpath("not_ignored.txt").write_text("content") nested_dir.joinpath("ignored.txt").write_text("content") nested_dir.joinpath(".gitignore").write_text("ignored.txt") nested_dir.joinpath("more").mkdir() nested_dir.joinpath("more/ignored.txt").write_text("content") if ( sys.platform.startswith("win") and not exposed_symlink.joinpath("file2").is_file() ): pytest.skip("Windows symlink support not available") # Test that each_unignored_file() follows the symlink assert set(each_unignored_file(Path())) == { gitignore, exposed_symlink / "file2", file1, file2, nested_dir / "not_ignored.txt", nested_dir / ".gitignore", } def test_dot_git_is_a_file(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: """ Test that each_unignored_file() does not crash when .git is a file (e.g., if the build is being run in a submodule) """ monkeypatch.chdir(tmp_path) # Create a file named .git git = Path(".git") git.write_text("gitdir: ../../.git/modules/foo") # If this throws an exception, the test will fail assert list(each_unignored_file(Path())) == [] scikit-build-core-0.11.1/tests/test_fileapi.py000066400000000000000000000060221477275177200213120ustar00rootroot00000000000000from __future__ import annotations import os import shutil import sysconfig from pathlib import Path import pytest from packaging.specifiers import SpecifierSet from scikit_build_core.cmake import CMake, CMaker from scikit_build_core.file_api._cattrs_converter import ( load_reply_dir as load_reply_dir_cattrs, ) from scikit_build_core.file_api.query import stateless_query from scikit_build_core.file_api.reply import load_reply_dir DIR = Path(__file__).parent.absolute() has_make = shutil.which("make") is not None or shutil.which("gmake") is not None has_ninja = shutil.which("ninja") is not None def prepare_env_or_skip() -> None: if ( "CMAKE_GENERATOR" not in os.environ and not sysconfig.get_platform().startswith("win") and not has_make ): if has_ninja: os.environ["CMAKE_GENERATOR"] = "Ninja" else: pytest.skip("No build system found") @pytest.mark.configure def test_cattrs_comparison(tmp_path): build_dir = tmp_path / "build" cmake = CMake.default_search(version=SpecifierSet(">=3.15")) config = CMaker( cmake, source_dir=DIR / "packages/simple_pure", build_dir=build_dir, build_type="Release", ) reply_dir = stateless_query(config.build_dir) config.configure() cattrs_index = load_reply_dir_cattrs(reply_dir) index = load_reply_dir(reply_dir) assert index == cattrs_index # TODO: Why is this an IndexError? def test_no_index(tmp_path): with pytest.raises(IndexError): load_reply_dir(tmp_path) with pytest.raises(IndexError): load_reply_dir_cattrs(tmp_path) @pytest.mark.configure def test_simple_pure(tmp_path): build_dir = tmp_path / "build" cmake = CMake.default_search(version=SpecifierSet(">=3.15")) config = CMaker( cmake, source_dir=DIR / "packages/simple_pure", build_dir=build_dir, build_type="Release", ) reply_dir = stateless_query(config.build_dir) config.configure() index = load_reply_dir(reply_dir) codemodel = index.reply.codemodel_v2 assert codemodel is not None cache = index.reply.cache_v2 assert cache is not None cmakefiles = index.reply.cmakefiles_v1 assert cmakefiles is not None toolchains = index.reply.toolchains_v1 assert toolchains is not None def test_included_dir(): reply_dir = DIR / "api/simple_pure/.cmake/api/v1/reply" index = load_reply_dir(reply_dir) assert index.cmake.version.string == "3.24.1" assert index.cmake.generator.name == "Ninja" assert len(index.objects) == 4 codemodel = index.reply.codemodel_v2 assert codemodel is not None assert codemodel.kind == "codemodel" assert codemodel.version.major == 2 assert codemodel.version.minor == 4 assert not codemodel.configurations[0].name cache = index.reply.cache_v2 assert cache is not None cmakefiles = index.reply.cmakefiles_v1 assert cmakefiles is not None toolchains = index.reply.toolchains_v1 assert toolchains is not None scikit-build-core-0.11.1/tests/test_fortran.py000066400000000000000000000035651477275177200213650ustar00rootroot00000000000000import shutil import sysconfig import zipfile from pathlib import Path import pytest from packaging.specifiers import SpecifierSet from scikit_build_core.build import build_wheel from scikit_build_core.program_search import ( best_program, get_cmake_programs, get_ninja_programs, ) np = pytest.importorskip("numpy") DIR = Path(__file__).parent.resolve() FORTRAN_EXAMPLE = DIR / "packages/fortran_example" cmake_info = best_program(get_cmake_programs(), version=SpecifierSet(">=3.17.2")) ninja_info = best_program(get_ninja_programs(), version=SpecifierSet(">=1.10")) @pytest.mark.compile @pytest.mark.configure @pytest.mark.fortran @pytest.mark.network @pytest.mark.skipif(shutil.which("gfortran") is None, reason="gfortran not available") @pytest.mark.skipif( sysconfig.get_platform().startswith("win"), reason="No reasonable Fortran compiler for MSVC", ) @pytest.mark.skipif( cmake_info is None, reason="CMake needs to be 3.17.2+ to support Fortran with Ninja" ) @pytest.mark.skipif( ninja_info is None, reason="Ninja needs to be 1.10+ to support Fortran with CMake" ) def test_pep517_wheel(tmp_path, monkeypatch, virtualenv): dist = tmp_path / "dist" monkeypatch.chdir(FORTRAN_EXAMPLE) out = build_wheel(str(dist)) (wheel,) = dist.glob("fibby-0.0.1-*.whl") assert wheel == dist / out with zipfile.ZipFile(wheel) as zf: file_names = {Path(n).parts[0] for n in zf.namelist()} assert len(file_names) == 2 assert "fibby-0.0.1.dist-info" in file_names file_names.remove("fibby-0.0.1.dist-info") (so_file,) = file_names assert so_file.startswith("fibby") print("SOFILE:", so_file) virtualenv.install(wheel) output = virtualenv.execute( "import fibby; import numpy as np; a = np.zeros(9); fibby.fib(a); print(a)", ) assert output == str(np.array([0, 1, 1, 2, 3, 5, 8, 13, 21], dtype=float)) scikit-build-core-0.11.1/tests/test_generator_default.py000066400000000000000000000367031477275177200234040ustar00rootroot00000000000000from scikit_build_core.builder.generator import parse_help_default UNIX_OUTPUT = """\ Usage cmake [options] cmake [options] cmake [options] -S -B Specify a source directory to (re-)generate a build system for it in the current working directory. Specify an existing build directory to re-generate its build system. Options -S = Explicitly specify a source directory. -B = Explicitly specify a build directory. -C = Pre-load a script to populate the cache. -D [:]= = Create or update a cmake cache entry. -U = Remove matching entries from CMake cache. -G = Specify a build system generator. -T = Specify toolset name if supported by generator. -A = Specify platform name if supported by generator. --toolchain = Specify toolchain file [CMAKE_TOOLCHAIN_FILE]. --install-prefix = Specify install directory [CMAKE_INSTALL_PREFIX]. -Wdev = Enable developer warnings. -Wno-dev = Suppress developer warnings. -Werror=dev = Make developer warnings errors. -Wno-error=dev = Make developer warnings not errors. -Wdeprecated = Enable deprecation warnings. -Wno-deprecated = Suppress deprecation warnings. -Werror=deprecated = Make deprecated macro and function warnings errors. -Wno-error=deprecated = Make deprecated macro and function warnings not errors. --preset ,--preset= = Specify a configure preset. --list-presets = List available presets. -E = CMake command mode. -L[A][H] = List non-advanced cached variables. --fresh = Configure a fresh build tree, removing any existing cache file. --build = Build a CMake-generated project binary tree. --install = Install a CMake-generated project binary tree. --open = Open generated project in the associated application. -N = View mode only. -P = Process script mode. --find-package = Legacy pkg-config like mode. Do not use. --graphviz=[file] = Generate graphviz of dependencies, see CMakeGraphVizOptions.cmake for more. --system-information [file] = Dump information about this system. --log-level= = Set the verbosity of messages from CMake files. --loglevel is also accepted for backward compatibility reasons. --log-context = Prepend log messages with context, if given --debug-trycompile = Do not delete the try_compile build tree. Only useful on one try_compile at a time. --debug-output = Put cmake in a debug mode. --debug-find = Put cmake find in a debug mode. --debug-find-pkg=[,...] = Limit cmake debug-find to the comma-separated list of packages --debug-find-var=[,...] = Limit cmake debug-find to the comma-separated list of result variables --trace = Put cmake in trace mode. --trace-expand = Put cmake in trace mode with variable expansion. --trace-format= = Set the output format of the trace. --trace-source= = Trace only this CMake file/module. Multiple options allowed. --trace-redirect= = Redirect trace output to a file instead of stderr. --warn-uninitialized = Warn about uninitialized values. --no-warn-unused-cli = Don't warn about command line options. --check-system-vars = Find problems with variable usage in system files. --compile-no-warning-as-error= Ignore COMPILE_WARNING_AS_ERROR property and CMAKE_COMPILE_WARNING_AS_ERROR variable. --profiling-format= = Output data for profiling CMake scripts. Supported formats: google-trace --profiling-output= = Select an output path for the profiling data enabled through --profiling-format. --help,-help,-usage,-h,-H,/? = Print usage information and exit. --version,-version,/V [] = Print version number and exit. --help-full [] = Print all help manuals and exit. --help-manual [] = Print one help manual and exit. --help-manual-list [] = List help manuals available and exit. --help-command [] = Print help for one command and exit. --help-command-list [] = List commands with help available and exit. --help-commands [] = Print cmake-commands manual and exit. --help-module [] = Print help for one module and exit. --help-module-list [] = List modules with help available and exit. --help-modules [] = Print cmake-modules manual and exit. --help-policy [] = Print help for one policy and exit. --help-policy-list [] = List policies with help available and exit. --help-policies [] = Print cmake-policies manual and exit. --help-property [] = Print help for one property and exit. --help-property-list [] = List properties with help available and exit. --help-properties [] = Print cmake-properties manual and exit. --help-variable var [] = Print help for one variable and exit. --help-variable-list [] = List variables with help available and exit. --help-variables [] = Print cmake-variables manual and exit. Generators The following generators are available on this platform (* marks default): * Unix Makefiles = Generates standard UNIX makefiles. Ninja = Generates build.ninja files. Ninja Multi-Config = Generates build-.ninja files. Watcom WMake = Generates Watcom WMake makefiles. Xcode = Generate Xcode project files. CodeBlocks - Ninja = Generates CodeBlocks project files. CodeBlocks - Unix Makefiles = Generates CodeBlocks project files. CodeLite - Ninja = Generates CodeLite project files. CodeLite - Unix Makefiles = Generates CodeLite project files. Eclipse CDT4 - Ninja = Generates Eclipse CDT 4.0 project files. Eclipse CDT4 - Unix Makefiles= Generates Eclipse CDT 4.0 project files. Kate - Ninja = Generates Kate project files. Kate - Unix Makefiles = Generates Kate project files. Sublime Text 2 - Ninja = Generates Sublime Text 2 project files. Sublime Text 2 - Unix Makefiles = Generates Sublime Text 2 project files. """ WINDOWS_OUTPUT = """\ Generators The following generators are available on this platform (* marks default): Visual Studio 17 2022 = Generates Visual Studio 2022 project files. Use -A option to specify architecture. * Visual Studio 16 2019 = Generates Visual Studio 2019 project files. Use -A option to specify architecture. Visual Studio 15 2017 [arch] = Generates Visual Studio 2017 project files. Optional [arch] can be "Win64" or "ARM". Visual Studio 14 2015 [arch] = Generates Visual Studio 2015 project files. Optional [arch] can be "Win64" or "ARM". Visual Studio 12 2013 [arch] = Generates Visual Studio 2013 project files. Optional [arch] can be "Win64" or "ARM". Visual Studio 11 2012 [arch] = Generates Visual Studio 2012 project files. Optional [arch] can be "Win64" or "ARM". Visual Studio 10 2010 [arch] = Deprecated. Generates Visual Studio 2010 project files. Optional [arch] can be "Win64" or "IA64". Visual Studio 9 2008 [arch] = Generates Visual Studio 2008 project files. Optional [arch] can be "Win64" or "IA64". Borland Makefiles = Generates Borland makefiles. NMake Makefiles = Generates NMake makefiles. NMake Makefiles JOM = Generates JOM makefiles. MSYS Makefiles = Generates MSYS makefiles. MinGW Makefiles = Generates a make file for use with mingw32-make. Green Hills MULTI = Generates Green Hills MULTI files (experimental, work-in-progress). Unix Makefiles = Generates standard UNIX makefiles. Ninja = Generates build.ninja files. Ninja Multi-Config = Generates build-.ninja files. Watcom WMake = Generates Watcom WMake makefiles. CodeBlocks - MinGW Makefiles = Generates CodeBlocks project files. CodeBlocks - NMake Makefiles = Generates CodeBlocks project files. CodeBlocks - NMake Makefiles JOM = Generates CodeBlocks project files. CodeBlocks - Ninja = Generates CodeBlocks project files. CodeBlocks - Unix Makefiles = Generates CodeBlocks project files. CodeLite - MinGW Makefiles = Generates CodeLite project files. CodeLite - NMake Makefiles = Generates CodeLite project files. CodeLite - Ninja = Generates CodeLite project files. CodeLite - Unix Makefiles = Generates CodeLite project files. Eclipse CDT4 - NMake Makefiles = Generates Eclipse CDT 4.0 project files. Eclipse CDT4 - MinGW Makefiles = Generates Eclipse CDT 4.0 project files. Eclipse CDT4 - Ninja = Generates Eclipse CDT 4.0 project files. Eclipse CDT4 - Unix Makefiles= Generates Eclipse CDT 4.0 project files. Kate - MinGW Makefiles = Generates Kate project files. Kate - NMake Makefiles = Generates Kate project files. Kate - Ninja = Generates Kate project files. Kate - Unix Makefiles = Generates Kate project files. Sublime Text 2 - MinGW Makefiles = Generates Sublime Text 2 project files. Sublime Text 2 - NMake Makefiles = Generates Sublime Text 2 project files. Sublime Text 2 - Ninja = Generates Sublime Text 2 project files. Sublime Text 2 - Unix Makefiles = Generates Sublime Text 2 project files. """ OLDER_WINDOWS_OUTPUT = """\ Generators The following generators are available on this platform (* marks default): Visual Studio 17 2022 = Generates Visual Studio 2022 project files. Use -A option to specify architecture. Visual Studio 16 2019 = Generates Visual Studio 2019 project files. Use -A option to specify architecture. * Visual Studio 15 2017 [arch] = Generates Visual Studio 2017 project files. Optional [arch] can be "Win64" or "ARM". Visual Studio 14 2015 [arch] = Generates Visual Studio 2015 project files. Optional [arch] can be "Win64" or "ARM". Visual Studio 12 2013 [arch] = Generates Visual Studio 2013 project files. Optional [arch] can be "Win64" or "ARM". Visual Studio 11 2012 [arch] = Generates Visual Studio 2012 project files. Optional [arch] can be "Win64" or "ARM". Visual Studio 10 2010 [arch] = Deprecated. Generates Visual Studio 2010 project files. Optional [arch] can be "Win64" or "IA64". Visual Studio 9 2008 [arch] = Generates Visual Studio 2008 project files. Optional [arch] can be "Win64" or "IA64". Borland Makefiles = Generates Borland makefiles. NMake Makefiles = Generates NMake makefiles. NMake Makefiles JOM = Generates JOM makefiles. MSYS Makefiles = Generates MSYS makefiles. MinGW Makefiles = Generates a make file for use with mingw32-make. Green Hills MULTI = Generates Green Hills MULTI files (experimental, work-in-progress). Unix Makefiles = Generates standard UNIX makefiles. Ninja = Generates build.ninja files. Ninja Multi-Config = Generates build-.ninja files. Watcom WMake = Generates Watcom WMake makefiles. CodeBlocks - MinGW Makefiles = Generates CodeBlocks project files. CodeBlocks - NMake Makefiles = Generates CodeBlocks project files. CodeBlocks - NMake Makefiles JOM = Generates CodeBlocks project files. CodeBlocks - Ninja = Generates CodeBlocks project files. CodeBlocks - Unix Makefiles = Generates CodeBlocks project files. CodeLite - MinGW Makefiles = Generates CodeLite project files. CodeLite - NMake Makefiles = Generates CodeLite project files. CodeLite - Ninja = Generates CodeLite project files. CodeLite - Unix Makefiles = Generates CodeLite project files. Eclipse CDT4 - NMake Makefiles = Generates Eclipse CDT 4.0 project files. Eclipse CDT4 - MinGW Makefiles = Generates Eclipse CDT 4.0 project files. Eclipse CDT4 - Ninja = Generates Eclipse CDT 4.0 project files. Eclipse CDT4 - Unix Makefiles= Generates Eclipse CDT 4.0 project files. Kate - MinGW Makefiles = Generates Kate project files. Kate - NMake Makefiles = Generates Kate project files. Kate - Ninja = Generates Kate project files. Kate - Unix Makefiles = Generates Kate project files. Sublime Text 2 - MinGW Makefiles = Generates Sublime Text 2 project files. Sublime Text 2 - NMake Makefiles = Generates Sublime Text 2 project files. Sublime Text 2 - Ninja = Generates Sublime Text 2 project files. Sublime Text 2 - Unix Makefiles = Generates Sublime Text 2 project files. """ def test_best_gen_unix(): """Test best_gen() on Unix.""" assert parse_help_default(UNIX_OUTPUT) == "Unix Makefiles" def test_best_gen_windows(): """Test best_gen() on Windows.""" assert parse_help_default(WINDOWS_OUTPUT) == "Visual Studio 16 2019" def test_best_gen_older_windows(): """Test best_gen() on older Windows.""" assert parse_help_default(OLDER_WINDOWS_OUTPUT) == "Visual Studio 15 2017" scikit-build-core-0.11.1/tests/test_get_requires.py000066400000000000000000000103471477275177200224040ustar00rootroot00000000000000from __future__ import annotations import sysconfig from pathlib import Path from typing import TYPE_CHECKING import pytest from scikit_build_core.build import ( get_requires_for_build_editable, get_requires_for_build_sdist, get_requires_for_build_wheel, ) from scikit_build_core.builder.get_requires import GetRequires if TYPE_CHECKING: from pytest_subprocess import FakeProcess ninja = [] if sysconfig.get_platform().startswith("win") else ["ninja>=1.5"] @pytest.fixture(autouse=True) def protect_get_requires_autouse(protect_get_requires: None): """ Autouse this fixture in this test. """ def test_get_requires_parts(fp: FakeProcess): fp.register( [Path("cmake/path"), "-E", "capabilities"], stdout='{"version":{"string":"3.14.0"}}', ) assert set(GetRequires().cmake()) == {"cmake>=3.15"} assert set(GetRequires().ninja()) == {*ninja} def test_get_requires_parts_uneeded(fp: FakeProcess): fp.register( [Path("cmake/path"), "-E", "capabilities"], stdout='{"version":{"string":"3.18.0"}}', ) assert set(GetRequires().cmake()) == set() assert set(GetRequires().ninja()) == {*ninja} def test_get_requires_parts_settings(fp: FakeProcess): fp.register( [Path("cmake/path"), "-E", "capabilities"], stdout='{"version":{"string":"3.18.0"}}', ) config = {"cmake.version": ">=3.20"} assert set(GetRequires.from_config_settings(config).cmake()) == {"cmake>=3.20"} assert set(GetRequires.from_config_settings(config).ninja()) == {*ninja} def test_get_requires_parts_pyproject( fp: FakeProcess, monkeypatch: pytest.MonkeyPatch, tmp_path: Path ): monkeypatch.chdir(tmp_path) tmp_path.joinpath("pyproject.toml").write_text( """ [tool.scikit-build.cmake] version = ">=3.21" """ ) fp.register( [Path("cmake/path"), "-E", "capabilities"], stdout='{"version":{"string":"3.18.0"}}', ) assert set(GetRequires().cmake()) == {"cmake>=3.21"} assert set(GetRequires().ninja()) == {*ninja} def test_get_requires_parts_pyproject_old( fp: FakeProcess, monkeypatch: pytest.MonkeyPatch, tmp_path: Path ): monkeypatch.chdir(tmp_path) tmp_path.joinpath("pyproject.toml").write_text( """ [tool.scikit-build] minimum-version = "0.0" cmake.minimum-version = "3.21" """ ) fp.register( [Path("cmake/path"), "-E", "capabilities"], stdout='{"version":{"string":"3.18.0"}}', ) assert set(GetRequires().cmake()) == {"cmake>=3.21"} assert set(GetRequires().ninja()) == {*ninja} def test_get_requires_for_build_sdist(fp: FakeProcess): fp.register( [Path("cmake/path"), "-E", "capabilities"], stdout='{"version":{"string":"3.14.0"}}', ) assert set(get_requires_for_build_sdist({})) == set() def test_get_requires_for_build_sdist_cmake(fp: FakeProcess): expected = {"cmake>=3.15", *ninja} fp.register( [Path("cmake/path"), "-E", "capabilities"], stdout='{"version":{"string":"3.14.0"}}', ) assert set(get_requires_for_build_sdist({"sdist.cmake": "True"})) == expected def test_get_requires_for_build_wheel(fp: FakeProcess): expected = {"cmake>=3.15", *ninja} fp.register( [Path("cmake/path"), "-E", "capabilities"], stdout='{"version":{"string":"3.14.0"}}', ) assert set(get_requires_for_build_wheel({})) == expected def test_get_requires_for_build_wheel_pure(fp: FakeProcess): fp.register( [Path("cmake/path"), "-E", "capabilities"], stdout='{"version":{"string":"3.14.0"}}', ) assert set(get_requires_for_build_wheel({"wheel.cmake": "False"})) == set() def test_get_requires_for_build_editable(fp: FakeProcess): expected = {"cmake>=3.15", *ninja} fp.register( [Path("cmake/path"), "-E", "capabilities"], stdout='{"version":{"string":"3.14.0"}}', ) assert set(get_requires_for_build_editable({})) == expected def test_get_requires_for_build_editable_pure(fp: FakeProcess): fp.register( [Path("cmake/path"), "-E", "capabilities"], stdout='{"version":{"string":"3.14.0"}}', ) assert set(get_requires_for_build_editable({"wheel.cmake": "False"})) == set() scikit-build-core-0.11.1/tests/test_hatchling.py000066400000000000000000000042661477275177200216520ustar00rootroot00000000000000import sysconfig import tarfile import zipfile from pathlib import Path import pytest pytest.importorskip("hatchling") @pytest.mark.network @pytest.mark.integration @pytest.mark.usefixtures("package_hatchling") def test_hatchling_sdist(isolated, tmp_path: Path) -> None: dist = tmp_path / "dist" isolated.install("build[virtualenv]") isolated.module("build", "--sdist", f"--outdir={dist}") (sdist,) = dist.iterdir() assert sdist.name == "hatchling_example-0.1.0.tar.gz" with tarfile.open(sdist) as f: file_names = set(f.getnames()) assert file_names == { "hatchling_example-0.1.0/.gitignore", "hatchling_example-0.1.0/PKG-INFO", "hatchling_example-0.1.0/cpp/CMakeLists.txt", "hatchling_example-0.1.0/cpp/example.cpp", "hatchling_example-0.1.0/pyproject.toml", "hatchling_example-0.1.0/src/hatchling_example/__init__.py", "hatchling_example-0.1.0/src/hatchling_example/_core.pyi", } @pytest.mark.network @pytest.mark.compile @pytest.mark.configure @pytest.mark.integration @pytest.mark.usefixtures("package_hatchling") @pytest.mark.parametrize( "build_args", [(), ("--wheel",)], ids=["sdist_to_wheel", "wheel_directly"] ) def test_hatchling_wheel(isolated, build_args, tmp_path: Path) -> None: dist = tmp_path / "dist" isolated.install("build[virtualenv]", "scikit-build-core", "hatchling", "pybind11") isolated.module("build", "--no-isolation", f"--outdir={dist}", *build_args) ext_suffix = sysconfig.get_config_var("EXT_SUFFIX") (wheel,) = dist.glob("*.whl") with zipfile.ZipFile(wheel) as f: file_names = set(f.namelist()) assert file_names == { "hatchling_example-0.1.0.data/data/data_file.txt", "hatchling_example-0.1.0.data/scripts/myscript", "hatchling_example-0.1.0.dist-info/METADATA", "hatchling_example-0.1.0.dist-info/RECORD", "hatchling_example-0.1.0.dist-info/WHEEL", "hatchling_example-0.1.0.dist-info/extra_metadata/metadata_file.txt", "hatchling_example/__init__.py", "hatchling_example/_core.pyi", f"hatchling_example/hatchling_example/_core{ext_suffix}", } scikit-build-core-0.11.1/tests/test_json_schema.py000066400000000000000000000034011477275177200221700ustar00rootroot00000000000000from pathlib import Path from typing import Any, Dict, List, Optional, Union import pytest from packaging.version import Version from scikit_build_core.settings.json_schema import FailedConversionError, convert_type def test_convert_str(): assert convert_type(str, normalize_keys=False) == {"type": "string"} def test_convert_str_or_bool(): assert convert_type(Union[str, bool], normalize_keys=False) == { "oneOf": [{"type": "string"}, {"type": "boolean"}] } def test_convert_optional_str(): assert convert_type(Optional[str], normalize_keys=False) == {"type": "string"} def test_convert_path(): assert convert_type(Path, normalize_keys=False) == {"type": "string"} def test_convert_version(): assert convert_type(Version, normalize_keys=False) == {"type": "string"} def test_convert_list(): assert convert_type(List[str], normalize_keys=False) == { "type": "array", "items": {"type": "string"}, } assert convert_type(List[Union[str, bool]], normalize_keys=False) == { "type": "array", "items": {"oneOf": [{"type": "string"}, {"type": "boolean"}]}, } def test_convert_dict(): assert convert_type(Dict[str, str], normalize_keys=False) == { "type": "object", "patternProperties": {".+": {"type": "string"}}, } assert convert_type(Dict[str, Dict[str, str]], normalize_keys=False) == { "type": "object", "patternProperties": { ".+": {"type": "object", "patternProperties": {".+": {"type": "string"}}} }, } assert convert_type(Dict[str, Any], normalize_keys=False) == { "type": "object", } def test_convert_invalid(): with pytest.raises(FailedConversionError): convert_type(object, normalize_keys=False) scikit-build-core-0.11.1/tests/test_logging.py000066400000000000000000000056731477275177200213420ustar00rootroot00000000000000from __future__ import annotations import platform import sys from typing import TYPE_CHECKING if TYPE_CHECKING: from pytest import CaptureFixture, MonkeyPatch import scikit_build_core._logging from scikit_build_core import __version__ from scikit_build_core._logging import Style, rich_print def test_rich_print_nocolor(capsys: CaptureFixture[str], monkeypatch: MonkeyPatch): monkeypatch.setenv("NO_COLOR", "1") monkeypatch.delenv("FORCE_COLOR", raising=False) monkeypatch.setattr(scikit_build_core._logging, "_style", Style()) rich_print("{red}hello{default} world", end="") assert capsys.readouterr().out == "hello world" def test_rich_print_nocolor_forcecolor( capsys: CaptureFixture[str], monkeypatch: MonkeyPatch ): monkeypatch.setenv("NO_COLOR", "1") monkeypatch.setenv("FORCE_COLOR", "1") monkeypatch.setattr(scikit_build_core._logging, "_style", Style()) rich_print("{red}hello{default} world", end="") assert capsys.readouterr().out == "hello world" def test_rich_print_forcecolor(capsys: CaptureFixture[str], monkeypatch: MonkeyPatch): monkeypatch.setenv("FORCE_COLOR", "1") monkeypatch.delenv("NO_COLOR", raising=False) monkeypatch.setattr(scikit_build_core._logging, "_style", Style()) rich_print("{bold.red}hello{normal} world", end="") assert capsys.readouterr().out == "\33[1;31mhello\33[22m world\33[0m" def test_rich_print_fgbg(capsys: CaptureFixture[str], monkeypatch: MonkeyPatch): monkeypatch.setattr(scikit_build_core._logging, "_style", Style(color=True)) rich_print("{bold.fg.red.bg.blue}hello world", end="") assert capsys.readouterr().out == "\33[1;31;44mhello world\33[0m" def test_rich_notrichbrackets(capsys: CaptureFixture[str], monkeypatch: MonkeyPatch): monkeypatch.setattr(scikit_build_core._logging, "_style", Style(color=True)) rich_print("{bold.red}hello{normal} world{default} [notrich]", end="") assert ( capsys.readouterr().out == "\33[1;31mhello\33[22m world\33[39m [notrich]\33[0m" ) def test_rich_print_subs(capsys: CaptureFixture[str], monkeypatch: MonkeyPatch): monkeypatch.setattr(scikit_build_core._logging, "_style", Style(color=True)) rich_print("{platform.platform} {__version__} {sys.version}", end="") assert ( capsys.readouterr().out == f"{platform.platform()} {__version__} {sys.version}" ) def test_rich_print_each_color(capsys: CaptureFixture[str], monkeypatch: MonkeyPatch): monkeypatch.setattr(scikit_build_core._logging, "_style", Style(color=True)) rich_print("hello", "world", color="green", end="") assert capsys.readouterr().out == "\33[32mhello\33[0m \33[32mworld\33[0m" def test_rich_print_internal_color( capsys: CaptureFixture[str], monkeypatch: MonkeyPatch ): monkeypatch.setattr(scikit_build_core._logging, "_style", Style(color=True)) rich_print("hello {color}world", color="green", end="") assert capsys.readouterr().out == "\33[32mhello \33[32mworld\33[0m" scikit-build-core-0.11.1/tests/test_module_dir.py000066400000000000000000000036211477275177200220260ustar00rootroot00000000000000from __future__ import annotations import importlib import importlib.machinery import importlib.util import inspect import pkgutil from pathlib import Path from typing import Generator def on_all_modules( name: str, base_path: Path | None = None, *, pkg: bool ) -> Generator[str, None, None]: if base_path is None: base_module = importlib.import_module(name) base_path = Path(inspect.getfile(base_module)).parent for module_info in pkgutil.iter_modules([str(base_path)]): package_name = f"{name}.{module_info.name}" if module_info.ispkg: if pkg: yield package_name yield from on_all_modules( package_name, base_path / module_info.name, pkg=pkg ) else: yield package_name def test_all_modules_filter_all(): all_modules = on_all_modules("scikit_build_core", pkg=False) all_modules = (n for n in all_modules if not n.split(".")[-1].startswith("__")) for name in all_modules: try: module = importlib.import_module(name) except ModuleNotFoundError: continue try: dir_module = set(dir(module)) except Exception: print(f"dir() failed on {name}") raise items = ["annotations", "os", "sys"] for item in items: assert item not in dir_module, f"{module.__file__} has {item!r}" def test_all_modules_has_all(): all_modules = on_all_modules("scikit_build_core", pkg=True) all_modules = (n for n in all_modules if not n.split(".")[-1].startswith("_")) for name in all_modules: try: module = importlib.import_module(name) except ModuleNotFoundError: continue dir_module = module.__dict__ items = ["__all__"] for item in items: assert item in dir_module, f"{module.__file__} missing {item!r}" scikit-build-core-0.11.1/tests/test_name_main.py000066400000000000000000000035221477275177200216270ustar00rootroot00000000000000import os import subprocess import sys from pathlib import Path DIR = Path(__file__).parent.resolve() def test_cattrs_converter(): env = os.environ.copy() if "FORCE_COLOR" in env: del env["FORCE_COLOR"] out = subprocess.run( [ sys.executable, "-m", "scikit_build_core.file_api._cattrs_converter", str(DIR / "api/simple_pure/.cmake/api/v1/reply"), ], check=True, env=env, stdout=subprocess.PIPE, text=True, ) assert "version=CMakeVersion" in out.stdout assert "toolchains_v1=Toolchains(" in out.stdout def test_query(tmp_path): env = os.environ.copy() if "FORCE_COLOR" in env: del env["FORCE_COLOR"] build_dir = tmp_path / "test_query" build_dir.mkdir() out = subprocess.run( [sys.executable, "-m", "scikit_build_core.file_api.query", str(build_dir)], check=True, env=env, stdout=subprocess.PIPE, text=True, ) api_dir = build_dir / ".cmake/api/v1" query_dir = api_dir / "query" assert query_dir.joinpath("codemodel-v2").is_file() assert query_dir.joinpath("cache-v2").is_file() assert query_dir.joinpath("cmakeFiles-v1").is_file() assert query_dir.joinpath("toolchains-v1").is_file() assert str(api_dir / "reply") == out.stdout.strip() def test_reply(): env = os.environ.copy() if "FORCE_COLOR" in env: del env["FORCE_COLOR"] out = subprocess.run( [ sys.executable, "-m", "scikit_build_core.file_api.reply", str(DIR / "api/simple_pure/.cmake/api/v1/reply"), ], check=True, env=env, stdout=subprocess.PIPE, text=True, ) assert "version=CMakeVersion" in out.stdout assert "toolchains_v1=Toolchains(" in out.stdout scikit-build-core-0.11.1/tests/test_prepare_metadata.py000066400000000000000000000054051477275177200232030ustar00rootroot00000000000000from __future__ import annotations import sys import textwrap from pathlib import Path import pytest from packaging.version import Version from scikit_build_core.build import ( prepare_metadata_for_build_editable, prepare_metadata_for_build_wheel, ) from scikit_build_core.build.metadata import get_standard_metadata from scikit_build_core.settings.skbuild_model import ScikitBuildSettings @pytest.mark.usefixtures("package_simplest_c") @pytest.mark.parametrize("editable", [True, False], ids=["editable", "wheel"]) def test_prepare_metadata_for_build(fp, editable): # Old versions of packaging call mac_ver via subprocess fp.pass_command([sys.executable, fp.any()]) fp.pass_command([fp.program("cmake"), "-E", "capabilities"]) fp.pass_command([fp.program("cmake3"), "-E", "capabilities"]) if editable: assert ( prepare_metadata_for_build_editable("simple") == "simplest-0.0.1.dist-info" ) else: assert prepare_metadata_for_build_wheel("simple") == "simplest-0.0.1.dist-info" with Path("simple/simplest-0.0.1.dist-info").joinpath("METADATA").open() as f: metadata = f.read() assert ( textwrap.dedent( """\ Metadata-Version: 2.2 Name: simplest Version: 0.0.1""" ) == metadata.strip() ) assert len(list(Path("simple/simplest-0.0.1.dist-info").iterdir())) == 2 assert len(list(Path("simple").iterdir())) == 1 def test_multiline_description(): with pytest.raises(ValueError, match="one line summary"): get_standard_metadata( pyproject_dict={ "project": { "name": "hello", "version": "1.1.1", "description": "One\nTwo", } }, settings=ScikitBuildSettings(), ) with pytest.raises(ValueError, match="one line summary"): get_standard_metadata( pyproject_dict={ "project": { "name": "hello", "version": "1.1.1", "description": "One\nTwo", } }, settings=ScikitBuildSettings(minimum_version=Version("0.9")), ) get_standard_metadata( pyproject_dict={ "project": {"name": "hello", "version": "1.1.1", "description": "One\nTwo"} }, settings=ScikitBuildSettings(minimum_version=Version("0.8")), ) def test_license_normalization(): pytest.importorskip("packaging.licenses") metadata = get_standard_metadata( pyproject_dict={ "project": {"name": "hello", "version": "1.1.1", "license": "ApacHE-2.0"} }, settings=ScikitBuildSettings(), ) assert metadata.license == "Apache-2.0" scikit-build-core-0.11.1/tests/test_printouts.py000066400000000000000000000002631477275177200217510ustar00rootroot00000000000000from scikit_build_core.builder.__main__ import main def test_builder_printout(capsys): main() out, err = capsys.readouterr() assert "Detected Python Library" in out scikit-build-core-0.11.1/tests/test_process_scripts.py000066400000000000000000000036511477275177200231330ustar00rootroot00000000000000from pathlib import Path from scikit_build_core.build._scripts import process_script_dir def test_script_dir(tmp_path: Path) -> None: script_dir = tmp_path / "scripts" script_dir.mkdir() script_1 = script_dir / "script1" script_1.write_text("#!/usr/bin/env python3\n\nprint('hello world')") script_1.chmod(0o644) orig_mode_1 = script_1.stat().st_mode script_2 = script_dir / "script2" script_2.write_text("#!/usr/bin/env pypy3\n\nprint('hello world')") script_2.chmod(0o755) orig_mode_2 = script_2.stat().st_mode script_3 = script_dir / "script3" script_3.write_text("#!/usr/bin/env pythonw3.11\n\nprint('hello world')") script_4 = script_dir / "script4" script_4.write_text("#!/usr/bin/python3.11\n\nprint('hello world')") script_5 = script_dir / "script5" script_5.write_text("#!/usr/bin/other\n\nprint('hello world')") script_6 = script_dir / "script6" script_6.write_text("#!/usr/bin/env python3.11 other\n\nprint('hello world')") script_7 = script_dir / "script7" script_7.write_text("#!/usr/bin/env other\n\nprint('hello world')") process_script_dir(script_dir) assert script_1.read_text(encoding="utf-8") == "#!python\n\nprint('hello world')" assert script_1.stat().st_mode == orig_mode_1 assert script_2.read_text(encoding="utf-8") == "#!python\n\nprint('hello world')" assert script_2.stat().st_mode == orig_mode_2 assert script_3.read_text(encoding="utf-8") == "#!python\n\nprint('hello world')" assert script_4.read_text(encoding="utf-8") == "#!python\n\nprint('hello world')" assert ( script_5.read_text(encoding="utf-8") == "#!/usr/bin/other\n\nprint('hello world')" ) assert ( script_6.read_text(encoding="utf-8") == "#!python other\n\nprint('hello world')" ) assert ( script_7.read_text(encoding="utf-8") == "#!/usr/bin/env other\n\nprint('hello world')" ) scikit-build-core-0.11.1/tests/test_program_search.py000066400000000000000000000102231477275177200226730ustar00rootroot00000000000000from __future__ import annotations import logging from pathlib import Path import pytest from packaging.specifiers import SpecifierSet from packaging.version import Version from scikit_build_core.program_search import ( best_program, get_cmake_programs, get_ninja_programs, ) def test_get_cmake_programs_cmake_module(monkeypatch): cmake = pytest.importorskip("cmake") monkeypatch.setattr("shutil.which", lambda _: None) programs = list(get_cmake_programs()) assert len(programs) == 1 assert programs[0].path.name == "cmake" assert programs[0].version == Version(".".join(cmake.__version__.split(".")[:3])) def test_get_ninja_programs_cmake_module(monkeypatch): ninja = pytest.importorskip("ninja") monkeypatch.setattr("shutil.which", lambda _: None) programs = list(get_ninja_programs()) assert len(programs) == 1 assert programs[0].path.name == "ninja" assert programs[0].version == Version(".".join(ninja.__version__.split(".")[:3])) # There's a bug in Path.resolve() on Windows for Python <3.10 that causes it to # return an relative path when the path is non-existent. But we don't care about # that case, so we'll just do the same thing as the search does. def test_get_cmake_programs_all(monkeypatch, fp): monkeypatch.setattr("shutil.which", lambda x: x) cmake_path = Path("cmake") cmake3_path = Path("cmake3") fp.register( [cmake_path, "-E", "capabilities"], stdout='{"version":{"string":"3.20.0"}}', ) fp.register( [cmake3_path, "-E", "capabilities"], stdout='{"version":{"string":"3.19.0"}}', ) programs = list(get_cmake_programs(module=False)) assert len(programs) == 2 assert programs[0].path.name == "cmake3" assert programs[0].version == Version("3.19.0") assert programs[1].path.name == "cmake" assert programs[1].version == Version("3.20.0") best1 = best_program(programs, version=None) assert best1 assert best1.path.name == "cmake3" best2 = best_program(programs, version=SpecifierSet(">=3.20.0")) assert best2 assert best2.path.name == "cmake" def test_get_ninja_programs_all(monkeypatch, fp): monkeypatch.setattr("shutil.which", lambda x: x if "ninja" in x else None) ninja_path = Path("ninja") ninja_build_path = Path("ninja-build") fp.register([ninja_path, "--version"], stdout="1.10.1.git.kitware.jobserver-1") fp.register([ninja_build_path, "--version"], stdout="1.8.2") programs = list(get_ninja_programs(module=False)) assert len(programs) == 2 assert programs[0].path.name == "ninja-build" assert programs[0].version == Version("1.8.2") assert programs[1].path.name == "ninja" assert programs[1].version == Version("1.10.1") best1 = best_program(programs, version=None) assert best1 assert best1.path.name == "ninja-build" best2 = best_program(programs, version=SpecifierSet(">=1.9")) assert best2 assert best2.path.name == "ninja" def test_get_cmake_programs_malformed(monkeypatch, fp, caplog): caplog.set_level(logging.WARNING) monkeypatch.setattr("shutil.which", lambda x: x) cmake_path = Path("cmake") cmake3_path = Path("cmake3") fp.register([cmake_path, "-E", "capabilities"], stdout="scrambled output\n") fp.register([cmake3_path, "-E", "capabilities"], stdout="{}") programs = list(get_cmake_programs(module=False)) assert caplog.records assert "Could not determine CMake version" in str(caplog.records[0].msg) assert "Could not determine CMake version" in str(caplog.records[1].msg) assert len(programs) == 2 fp.register([cmake_path, "-E", "capabilities"], stdout="scrambled output\n") fp.register( [cmake3_path, "-E", "capabilities"], stdout='{"version":{"string":"3.17.3"}}' ) programs = list(get_cmake_programs(module=False)) best_none = best_program(programs, version=None) assert best_none assert best_none.path.name == "cmake3" best_3_15 = best_program(programs, version=SpecifierSet(">=3.15")) assert best_3_15 assert best_3_15.path.name == "cmake3" best_3_20 = best_program(programs, version=SpecifierSet(">=3.20")) assert best_3_20 is None scikit-build-core-0.11.1/tests/test_pyproject_abi3.py000066400000000000000000000037751477275177200226320ustar00rootroot00000000000000import shutil import sys import sysconfig import zipfile from pathlib import Path import pytest from scikit_build_core.build import build_wheel DIR = Path(__file__).parent.resolve() ABI_PKG = DIR / "packages/abi3_pyproject_ext" SYSCONFIGPLAT = sysconfig.get_platform() @pytest.mark.compile @pytest.mark.configure @pytest.mark.skipif( sysconfig.get_platform().startswith(("msys", "mingw")), reason="abi3 FindPython on MSYS/MinGW reports not found", ) def test_abi3_wheel(tmp_path, monkeypatch, virtualenv, capsys): dist = tmp_path / "dist" dist.mkdir() monkeypatch.chdir(ABI_PKG) if Path("build").is_dir(): shutil.rmtree("build") out = build_wheel(str(dist)) stdout, _ = capsys.readouterr() assert "This is a message after success" in stdout (wheel,) = dist.glob("abi3_example-0.0.1-*.whl") assert wheel == dist / out abi3 = sys.implementation.name == "cpython" and not sysconfig.get_config_var( "Py_GIL_DISABLED" ) if abi3: assert "-cp38-abi3-" in out else: assert "-cp38-abi3-" not in out with zipfile.ZipFile(wheel) as zf: file_names = {Path(n).parts[0] for n in zf.namelist()} assert len(file_names) == 2 assert "abi3_example-0.0.1.dist-info" in file_names file_names.remove("abi3_example-0.0.1.dist-info") (so_file,) = file_names if sysconfig.get_platform().startswith("win"): if sys.implementation.name == "cpython": assert so_file == "abi3_example.pyd" else: assert so_file.endswith(".pyd") elif sys.platform.startswith("cygwin"): if abi3: assert so_file == "abi3_example.abi3.dll" else: assert so_file != "abi3_example.abi3.dll" elif abi3: assert so_file == "abi3_example.abi3.so" else: assert so_file != "abi3_example.abi3.so" virtualenv.install(wheel) output = virtualenv.execute( "import abi3_example; print(abi3_example.square(2))", ) assert output.strip() == "4.0" scikit-build-core-0.11.1/tests/test_pyproject_extra_dirs.py000066400000000000000000000041271477275177200241500ustar00rootroot00000000000000import zipfile from pathlib import Path import pytest from scikit_build_core.build import build_wheel from scikit_build_core.errors import CMakeConfigError from pathutils import contained @pytest.mark.compile @pytest.mark.configure @pytest.mark.usefixtures("package_filepath_pure") def test_pep517_wheel_extra_dirs(monkeypatch, tmp_path: Path): monkeypatch.setenv("SKBUILD_CMAKE_DEFINE", "SOME_DEFINE3=baz;SOME_DEFINE4=baz") monkeypatch.setenv("SKBUILD_CMAKE_ARGS", "-DSOME_ARGS1=baz") dist = tmp_path / "dist" out = build_wheel( str(dist), {"cmake.define.SOME_DEFINE2": "bar", "cmake.define.SOME_DEFINE3": "bar"}, ) (wheel,) = dist.glob("cmake_dirs-0.0.1-*.whl") assert wheel == dist / out with zipfile.ZipFile(wheel) as zf: file_paths = {Path(n) for n in zf.namelist()} data_paths = set(contained(file_paths, "cmake_dirs-0.0.1.data")) file_names = {p.parts[0] for p in file_paths} data_dir = {p.parts[0] for p in data_paths} package = {p.name for p in contained(file_paths, "cmake_dirs")} data = {p.name for p in contained(data_paths, "data")} headers = {p.name for p in contained(data_paths, "headers")} scripts = {p.name for p in contained(data_paths, "scripts")} assert { "cmake_dirs-0.0.1.dist-info", "cmake_dirs-0.0.1.data", "cmake_dirs", "random_file.py", } == file_names assert data_dir == {"data", "headers", "scripts"} assert package == {"main.py"} assert data == {"in_data.txt"} assert headers == {"in_headers.h"} assert scripts == {"in_scripts.py"} @pytest.mark.usefixtures("package_filepath_pure") def test_pep517_wheel_too_old_core(monkeypatch): monkeypatch.setenv("SKBUILD_CMAKE_DEFINE", "SOME_DEFINE3=baz;SOME_DEFINE4=baz") monkeypatch.setenv("SKBUILD_CMAKE_ARGS", "-DSOME_ARGS1=baz") with pytest.raises(CMakeConfigError): build_wheel( "dist", { "cmake.define.SOME_DEFINE2": "bar", "cmake.define.SOME_DEFINE3": "bar", "minimum-version": "99", }, ) scikit-build-core-0.11.1/tests/test_pyproject_pep517.py000066400000000000000000000323041477275177200230230ustar00rootroot00000000000000import gzip import hashlib import inspect import shutil import sys import tarfile import time import zipfile from importlib.metadata import PathDistribution from pathlib import Path import build.util import pytest from scikit_build_core.build import ( _file_processor, build_sdist, build_wheel, prepare_metadata_for_build_wheel, ) ENTRYPOINTS = """\ [one.two] three = four [console_scripts] something = other [gui_scripts] guithing = a.b:c """ mark_hashes_different = pytest.mark.xfail( sys.platform.startswith(("win", "cygwin")), reason="hashes differ on Windows", strict=False, ) def compute_uncompressed_hash(inp: Path) -> str: with gzip.open(inp, "rb") as f: return hashlib.sha256(f.read()).hexdigest() @pytest.mark.usefixtures("package_simple_pyproject_ext") def test_pep517_sdist(tmp_path: Path): expected_metadata = ( inspect.cleandoc( """ Metadata-Version: 2.2 Name: CMake.Example Version: 0.0.1 Requires-Python: >=3.8 Provides-Extra: test Requires-Dist: pytest>=6.0; extra == "test" """ ) + "\n\n" ) dist = tmp_path / "dist" out = build_sdist(str(dist)) (sdist,) = dist.iterdir() assert sdist.name == "cmake_example-0.0.1.tar.gz" assert sdist == dist / out with tarfile.open(sdist) as f: file_names = set(f.getnames()) assert file_names == { f"cmake_example-0.0.1/{x}" for x in ( "CMakeLists.txt", "pyproject.toml", "src/main.cpp", "PKG-INFO", "LICENSE", ) } pkg_info = f.extractfile("cmake_example-0.0.1/PKG-INFO") assert pkg_info pkg_info_contents = pkg_info.read().decode() assert pkg_info_contents == expected_metadata @mark_hashes_different def test_pep517_sdist_hash(monkeypatch, package_simple_pyproject_ext, tmp_path: Path): # Unset SOURCE_DATE_EPOCH in order to guarantee the hash match monkeypatch.delenv("SOURCE_DATE_EPOCH", raising=False) dist = tmp_path / "dist" out = build_sdist(str(dist)) sdist = dist / out hash = compute_uncompressed_hash(sdist) assert hash == package_simple_pyproject_ext.sdist_hash mode = sdist.stat().st_mode assert mode == 33188 with gzip.open(sdist, "rb") as f: f.read() assert f.mtime == 1667997441 @pytest.mark.usefixtures("package_simple_pyproject_ext") def test_pep517_sdist_time_hash(tmp_path: Path): dist = tmp_path / "dist" out = build_sdist(str(dist)) sdist = dist / out hash1 = hashlib.sha256(sdist.read_bytes()).hexdigest() time.sleep(2) Path("src/main.cpp").touch() shutil.rmtree(dist) out = build_sdist(str(dist)) sdist = dist / out hash2 = hashlib.sha256(sdist.read_bytes()).hexdigest() assert hash1 == hash2 @pytest.mark.usefixtures("package_simple_pyproject_ext") def test_pep517_sdist_time_hash_nonreproducable(tmp_path: Path): dist = tmp_path / "dist" out = build_sdist(str(dist), {"sdist.reproducible": "false"}) sdist = dist / out hash1 = hashlib.sha256(sdist.read_bytes()).hexdigest() time.sleep(2) shutil.rmtree(dist) out = build_sdist(str(dist)) sdist = dist / out hash2 = hashlib.sha256(sdist.read_bytes()).hexdigest() assert hash1 != hash2 @mark_hashes_different @pytest.mark.parametrize("reverse_order", [False, True]) def test_pep517_sdist_time_hash_set_epoch( monkeypatch, reverse_order, package_simple_pyproject_ext, tmp_path: Path ): dist = tmp_path / "dist" monkeypatch.setenv( "SOURCE_DATE_EPOCH", package_simple_pyproject_ext.source_date_epoch ) _each_unignored_file = _file_processor.each_unignored_file def each_unignored_file_ordered(*args, **kwargs): return sorted(_each_unignored_file(*args, **kwargs), reverse=reverse_order) monkeypatch.setattr( _file_processor, "each_unignored_file", each_unignored_file_ordered ) out = build_sdist(str(dist), {"sdist.reproducible": "true"}) sdist = dist / out hash = compute_uncompressed_hash(sdist) assert hash == package_simple_pyproject_ext.sdist_dated_hash @pytest.mark.compile @pytest.mark.configure @pytest.mark.usefixtures("package_simple_pyproject_script_with_flags") @pytest.mark.parametrize( ("env_var", "setting"), [ ("CMAKE_ARGS", '-DCMAKE_C_FLAGS="-DFOO=1 -DBAR="'), ("SKBUILD_CMAKE_ARGS", "-DCMAKE_C_FLAGS=-DFOO=1 -DBAR="), ], ) def test_passing_cxx_flags(monkeypatch, env_var, setting, tmp_path: Path): # Note: This is sensitive to the types of quotes for SKBUILD_CMAKE_ARGS monkeypatch.setenv(env_var, setting) dist = tmp_path / "dist" build_wheel(str(dist), {"cmake.targets": ["cmake_example"]}) # Could leave empty (wheel,) = dist.glob("cmake_example-0.0.1-py3-none-*.whl") with zipfile.ZipFile(wheel) as f: file_names = set(f.namelist()) ext = ".exe" if sys.platform.startswith(("win", "cygwin")) else "" assert file_names == { "cmake_example-0.0.1.dist-info/RECORD", "cmake_example-0.0.1.dist-info/WHEEL", f"cmake_example-0.0.1.data/scripts/cmake_example{ext}", "cmake_example-0.0.1.dist-info/METADATA", "cmake_example-0.0.1.dist-info/licenses/LICENSE", } @pytest.mark.compile @pytest.mark.configure @pytest.mark.usefixtures("package_simple_pyproject_ext") def test_pep517_wheel(virtualenv, tmp_path: Path): dist = tmp_path / "dist" out = build_wheel( str(dist), {"cmake.targets": ["cmake_example"]} ) # Could leave empty (wheel,) = dist.glob("cmake_example-0.0.1-*.whl") assert wheel == dist / out with zipfile.ZipFile(wheel) as zf: file_paths = {Path(p) for p in zf.namelist()} file_names = {p.parts[0] for p in file_paths} with zf.open("cmake_example-0.0.1.dist-info/METADATA") as f: metadata = f.read().decode("utf-8") with zf.open("cmake_example-0.0.1.dist-info/entry_points.txt") as f: entry_points = f.read().decode("utf-8") assert Path("cmake_example-0.0.1.dist-info/licenses/LICENSE") in file_paths assert len(file_names) == 2 assert "cmake_example-0.0.1.dist-info" in file_names file_names.remove("cmake_example-0.0.1.dist-info") (so_file,) = file_names assert so_file.startswith("cmake_example") print("SOFILE:", so_file) print(entry_points == ENTRYPOINTS) assert 'Requires-Dist: pytest>=6.0; extra == "test"' in metadata assert "Metadata-Version: 2.2" in metadata assert "Name: CMake.Example" in metadata assert "Version: 0.0.1" in metadata assert "Requires-Python: >=3.8" in metadata assert "Provides-Extra: test" in metadata virtualenv.install(wheel) version = virtualenv.execute( "import cmake_example; print(cmake_example.__version__)", ) assert version.strip() == "0.0.1" add = virtualenv.execute( "import cmake_example; print(cmake_example.add(1, 2))", ) assert add.strip() == "3" @pytest.mark.compile @pytest.mark.configure @pytest.mark.usefixtures("package_simple_pyproject_source_dir") def test_pep517_wheel_source_dir(virtualenv, tmp_path: Path): dist = tmp_path / "dist" out = build_wheel(str(dist), config_settings={"skbuild.wheel.build-tag": "1foo"}) (wheel,) = dist.glob("cmake_example-0.0.1-*.whl") assert wheel == dist / out with zipfile.ZipFile(wheel) as zf: file_paths = {Path(p) for p in zf.namelist()} file_names = {p.parts[0] for p in file_paths} with zf.open("cmake_example-0.0.1.dist-info/METADATA") as f: metadata = f.read().decode("utf-8") with zf.open("cmake_example-0.0.1.dist-info/WHEEL") as f: wheel_metadata = f.read().decode("utf-8") with zf.open("cmake_example-0.0.1.dist-info/entry_points.txt") as f: entry_points = f.read().decode("utf-8") assert Path("cmake_example-0.0.1.dist-info/licenses/LICENSE") in file_paths assert len(file_names) == 2 assert "cmake_example-0.0.1.dist-info" in file_names file_names.remove("cmake_example-0.0.1.dist-info") (so_file,) = file_names assert so_file.startswith("cmake_example") print("SOFILE:", so_file) print(entry_points == ENTRYPOINTS) assert 'Requires-Dist: pytest>=6.0; extra == "test"' in metadata assert "Metadata-Version: 2.2" in metadata assert "Name: CMake.Example" in metadata assert "Version: 0.0.1" in metadata assert "Requires-Python: >=3.8" in metadata assert "Provides-Extra: test" in metadata assert "Build: 1foo" in wheel_metadata assert "Wheel-Version: 1.0" in wheel_metadata assert "Generator: scikit-build" in wheel_metadata assert "Root-Is-Purelib: false" in wheel_metadata virtualenv.install(wheel) version = virtualenv.execute( "import cmake_example; print(cmake_example.__version__)", ) assert version.strip() == "0.0.1" add = virtualenv.execute( "import cmake_example; print(cmake_example.add(1, 2))", ) assert add.strip() == "3" @pytest.mark.skip(reason="Doesn't work yet") @pytest.mark.compile @pytest.mark.configure def test_pep517_wheel_time_hash(monkeypatch, tmp_path: Path): monkeypatch.setenv("SOURCE_DATE_EPOCH", "12345") dist = tmp_path / "dist" out = build_wheel(str(dist)) wheel = dist / out hash1 = hashlib.sha256(wheel.read_bytes()).hexdigest() time.sleep(2) Path("src/main.cpp").touch() shutil.rmtree(dist) out = build_wheel(str(dist)) wheel = dist / out hash2 = hashlib.sha256(wheel.read_bytes()).hexdigest() assert hash1 == hash2 @pytest.mark.usefixtures("package_simple_pyproject_ext") def test_prepare_metdata_for_build_wheel(): metadata = build.util.project_wheel_metadata(str(Path.cwd()), isolated=False) answer = { "Metadata-Version": "2.2", "Name": "CMake.Example", "Version": "0.0.1", "Requires-Python": ">=3.8", "Provides-Extra": "test", "Requires-Dist": 'pytest>=6.0; extra == "test"', } for k, b in answer.items(): assert metadata.get(k, None) == b assert len(metadata) == len(answer) @pytest.mark.usefixtures("package_simple_pyproject_ext") def test_prepare_metdata_for_build_wheel_by_hand(tmp_path): mddir = tmp_path / "dist" mddir.mkdir() out = prepare_metadata_for_build_wheel(str(mddir), {}) print("Metadata dir:", (mddir / out).resolve()) metadata = PathDistribution(mddir / out).metadata answer = { "Metadata-Version": "2.2", "Name": "CMake.Example", "Version": "0.0.1", "Requires-Python": ">=3.8", "Provides-Extra": "test", "Requires-Dist": 'pytest>=6.0; extra == "test"', } for k, b in answer.items(): assert metadata.get(k, None) == b assert len(metadata) == len(answer) @pytest.mark.usefixtures("package_pep639_pure") def test_pep639_license_files_metadata(): metadata = build.util.project_wheel_metadata(str(Path.cwd()), isolated=False) answer = { "Metadata-Version": ["2.4"], "Name": ["pep639_pure"], "Version": ["0.1.0"], "License-Expression": ["MIT"], "License-File": ["LICENSE1.txt", "nested/more/LICENSE2.txt"], } for k, b in answer.items(): assert metadata.get_all(k, None) == b assert len(metadata) == sum(len(v) for v in answer.values()) @pytest.mark.usefixtures("package_pep639_pure") def test_pep639_license_files_sdist(tmp_path: Path): expected_metadata = ( inspect.cleandoc( """ Metadata-Version: 2.4 Name: pep639_pure Version: 0.1.0 License-Expression: MIT License-File: LICENSE1.txt License-File: nested/more/LICENSE2.txt """ ) + "\n\n" ) dist = tmp_path / "dist" out = build_sdist(str(dist)) (sdist,) = dist.iterdir() assert sdist.name == "pep639_pure-0.1.0.tar.gz" assert sdist == dist / out with tarfile.open(sdist) as f: file_names = set(f.getnames()) assert file_names == { f"pep639_pure-0.1.0/{x}" for x in ( "pyproject.toml", "PKG-INFO", "LICENSE1.txt", "nested/more/LICENSE2.txt", ) } pkg_info = f.extractfile("pep639_pure-0.1.0/PKG-INFO") assert pkg_info pkg_info_contents = pkg_info.read().decode() assert pkg_info_contents == expected_metadata @pytest.mark.usefixtures("package_pep639_pure") def test_pep639_license_files_wheel(tmp_path: Path): dist = tmp_path / "dist" out = build_wheel(str(dist), {}) (wheel,) = dist.glob("pep639_pure-0.1.0-*.whl") assert wheel == dist / out with zipfile.ZipFile(wheel) as zf: file_paths = {Path(p) for p in zf.namelist()} with zf.open("pep639_pure-0.1.0.dist-info/METADATA") as f: metadata = f.read().decode("utf-8") assert Path("pep639_pure-0.1.0.dist-info/licenses/LICENSE1.txt") in file_paths assert ( Path("pep639_pure-0.1.0.dist-info/licenses/nested/more/LICENSE2.txt") in file_paths ) assert "LICENSE1.txt" in metadata assert "nested/more/LICENSE2.txt" in metadata scikit-build-core-0.11.1/tests/test_pyproject_pep518.py000066400000000000000000000171651477275177200230340ustar00rootroot00000000000000import gzip import hashlib import shutil import sys import tarfile import textwrap import zipfile from pathlib import Path import pytest @pytest.fixture def cleanup_overwrite(): overwrite = Path("overwrite.cmake") yield overwrite if overwrite.exists(): overwrite.unlink() def compute_uncompressed_hash(inp: Path): with gzip.open(inp, "rb") as f: return hashlib.sha256(f.read()).hexdigest() @pytest.mark.network @pytest.mark.integration def test_pep518_sdist(isolated, package_simple_pyproject_ext, tmp_path: Path): correct_metadata = textwrap.dedent( """\ Metadata-Version: 2.2 Name: CMake.Example Version: 0.0.1 Requires-Python: >=3.8 Provides-Extra: test Requires-Dist: pytest>=6.0; extra == "test" """ ) dist = tmp_path / "dist" isolated.install("build[virtualenv]") isolated.module("build", "--sdist", f"--outdir={dist}") (sdist,) = dist.iterdir() assert sdist.name == "cmake_example-0.0.1.tar.gz" if not sys.platform.startswith(("win", "cygwin")): hash = compute_uncompressed_hash(sdist) assert hash == package_simple_pyproject_ext.sdist_hash with tarfile.open(sdist) as f: file_names = set(f.getnames()) assert file_names == { f"cmake_example-0.0.1/{x}" for x in ( "CMakeLists.txt", "pyproject.toml", "src/main.cpp", "PKG-INFO", "LICENSE", ) } pkg_info = f.extractfile("cmake_example-0.0.1/PKG-INFO") assert pkg_info pkg_info_contents = pkg_info.read().decode() assert correct_metadata == pkg_info_contents @pytest.mark.network @pytest.mark.configure @pytest.mark.integration @pytest.mark.usefixtures("package_sdist_config") def test_pep518_sdist_with_cmake_config(isolated, cleanup_overwrite, tmp_path: Path): cleanup_overwrite.write_text("set(MY_VERSION fiddlesticks)") correct_metadata = textwrap.dedent( """\ Metadata-Version: 2.2 Name: sdist_config Version: 0.1.0 """ ) dist = tmp_path / "dist" isolated.install("build[virtualenv]") isolated.module("build", "--sdist", f"--outdir={dist}") (sdist,) = dist.iterdir() assert sdist.name == "sdist_config-0.1.0.tar.gz" with tarfile.open(sdist) as f: file_names = set(f.getnames()) assert file_names > { f"sdist_config-0.1.0/{x}" for x in ( "CMakeLists.txt", "pyproject.toml", "main.cpp", "PKG-INFO", "overwrite.cmake", ".gitignore", ) } assert sum("pybind11" in x for x in file_names) >= 10 pkg_info = f.extractfile("sdist_config-0.1.0/PKG-INFO") assert pkg_info pkg_info_contents = pkg_info.read().decode() assert correct_metadata == pkg_info_contents assert cleanup_overwrite.is_file() @pytest.mark.network @pytest.mark.compile @pytest.mark.configure @pytest.mark.integration @pytest.mark.usefixtures("package_sdist_config") @pytest.mark.parametrize( "build_args", [(), ("--wheel",)], ids=["sdist_to_wheel", "wheel_directly"] ) def test_pep518_wheel_sdist_with_cmake_config( isolated, build_args, capfd, cleanup_overwrite, tmp_path: Path ): dist = tmp_path / "dist" isolated.install("build[virtualenv]") isolated.module( "build", "--config-setting=logging.level=DEBUG", f"--outdir={dist}", *build_args, ) out, err = capfd.readouterr() if not sys.platform.startswith("win32"): assert "Cloning into 'pybind11'..." in err if build_args: assert "Using integrated pybind11" not in out else: assert "Using integrated pybind11" in out (wheel,) = dist.glob("sdist_config-0.1.0-*.whl") with zipfile.ZipFile(wheel) as zf: file_names = {Path(n).parts[0] for n in zf.namelist()} assert len(file_names) == 3 file_names.remove("sdist_config-0.1.0.dist-info") file_names.remove("output.py") (so_file,) = file_names assert so_file.startswith("sdist_config") print("SOFILE:", so_file) isolated.install(wheel) life = isolated.execute("import sdist_config; print(sdist_config.life())") assert life == "42" version = isolated.execute("import output; print(output.version)") assert version == "0.1.0" assert cleanup_overwrite.is_file() @pytest.mark.network @pytest.mark.compile @pytest.mark.configure @pytest.mark.integration @pytest.mark.usefixtures("package_simple_pyproject_ext") @pytest.mark.parametrize( "build_args", [(), ("--wheel",)], ids=["sdist_to_wheel", "wheel_directly"] ) def test_pep518_wheel(isolated, build_args, tmp_path: Path): dist = tmp_path / "dist" isolated.install("build[virtualenv]") isolated.module( "build", "--config-setting=logging.level=DEBUG", f"--outdir={dist}", *build_args, ) (wheel,) = dist.glob("cmake_example-0.0.1-*.whl") with zipfile.ZipFile(wheel) as zf: file_paths = {Path(n) for n in zf.namelist()} file_names = {p.parts[0] for p in file_paths} assert Path("cmake_example-0.0.1.dist-info/licenses/LICENSE") in file_paths assert len(file_names) == 2 assert "cmake_example-0.0.1.dist-info" in file_names file_names.remove("cmake_example-0.0.1.dist-info") (so_file,) = file_names assert so_file.startswith("cmake_example") print("SOFILE:", so_file) isolated.install(wheel) version = isolated.execute("import cmake_example; print(cmake_example.__version__)") assert version == "0.0.1" add = isolated.execute("import cmake_example; print(cmake_example.add(1, 2))") assert add == "3" @pytest.mark.network @pytest.mark.compile @pytest.mark.configure @pytest.mark.integration @pytest.mark.parametrize( "build_args", [(), ("--wheel",)], ids=["sdist_to_wheel", "wheel_directly"] ) @pytest.mark.usefixtures("package_simple_pyproject_ext") def test_pep518_rebuild_build_dir(isolated, tmp_path, build_args): isolated.install("build[virtualenv]") build_dir = tmp_path.joinpath("build") build_dir.mkdir() build_dir = build_dir.resolve() dist = tmp_path / "dist" for _ in range(2): shutil.rmtree(dist, ignore_errors=True) isolated.module( "build", *build_args, f"--outdir={dist}", "--config-setting=logging.level=DEBUG", f"--config-setting=build-dir={build_dir}", ) (wheel,) = dist.glob("cmake_example-0.0.1-*.whl") with zipfile.ZipFile(wheel) as zf: file_paths = {Path(p) for p in zf.namelist()} file_names = {p.parts[0] for p in file_paths} assert Path("cmake_example-0.0.1.dist-info/licenses/LICENSE") in file_paths assert len(file_names) == 2 assert "cmake_example-0.0.1.dist-info" in file_names file_names.remove("cmake_example-0.0.1.dist-info") (so_file,) = file_names assert so_file.startswith("cmake_example") print("SOFILE:", so_file) isolated.install(wheel) version = isolated.execute("import cmake_example; print(cmake_example.__version__)") assert version == "0.0.1" @pytest.mark.network @pytest.mark.compile @pytest.mark.configure @pytest.mark.integration @pytest.mark.usefixtures("package_simple_pyproject_ext") def test_pep518_pip(isolated): isolated.install("-v", ".") version = isolated.execute("import cmake_example; print(cmake_example.__version__)") assert version == "0.0.1" add = isolated.execute("import cmake_example; print(cmake_example.add(1, 2))") assert add == "3" scikit-build-core-0.11.1/tests/test_pyproject_pep660.py000066400000000000000000000076551477275177200230350ustar00rootroot00000000000000import sys import sysconfig import typing import zipfile from pathlib import Path import pytest from scikit_build_core.build import build_editable @pytest.fixture(params=["redirect", "inplace"]) def editable_mode(request: pytest.FixtureRequest) -> str: return typing.cast("str", request.param) # TODO: figure out why gmake is reporting no rule to make simple_pure.cpp @pytest.mark.compile @pytest.mark.configure @pytest.mark.xfail( sys.platform.startswith("cygwin"), strict=False, reason="No idea why this fails on Cygwin", ) @pytest.mark.usefixtures("package_simplest_c") def test_pep660_wheel(editable_mode: str, tmp_path: Path): dist = tmp_path / "dist" out = build_editable(str(dist), {"editable.mode": editable_mode}) (wheel,) = dist.glob("simplest-0.0.1-*.whl") assert wheel == dist / out with zipfile.ZipFile(wheel) as zf: file_names = {Path(p).parts[0] for p in zf.namelist()} with zf.open("simplest-0.0.1.dist-info/METADATA") as f: metadata = f.read().decode("utf-8") assert len(file_names) == 4 if editable_mode == "redirect" else 2 assert "simplest-0.0.1.dist-info" in file_names if editable_mode == "redirect": assert "simplest" in file_names assert "_simplest_editable.py" in file_names else: assert "simplest" not in file_names assert "_simplest_editable.py" not in file_names assert "_simplest_editable.pth" in file_names assert "Metadata-Version: 2.2" in metadata assert "Name: simplest" in metadata assert "Version: 0.0.1" in metadata @pytest.mark.compile @pytest.mark.configure @pytest.mark.integration @pytest.mark.parametrize("isolate", [True, False], ids=["isolated", "not_isolated"]) @pytest.mark.usefixtures("package_simplest_c") def test_pep660_pip_isolated(isolated, isolate, editable_mode: str): isolate_args = ["--no-build-isolation"] if not isolate else [] isolated.install("pip>=23") if not isolate: isolated.install("scikit-build-core") build_dir = "" if editable_mode == "inplace" else "build/{wheel_tag}" isolated.install( "-v", f"--config-settings=build-dir={build_dir}", f"--config-settings=editable.mode={editable_mode}", *isolate_args, "-e", ".", ) value = isolated.execute("import simplest; print(simplest.square(2))") assert value == "4.0" location_str = isolated.execute( "import simplest; print(*simplest.__path__, sep=';')" ) locations = [Path(s).resolve() for s in location_str.split(";")] # First path is from the python source python_source = Path("src/simplest").resolve() assert any(x.samefile(python_source) for x in locations) cmake_install = isolated.platlib.joinpath("simplest").resolve() if editable_mode == "redirect": # Second path is from the CMake install assert any(x.samefile(cmake_install) for x in locations) location = isolated.execute("import simplest; print(simplest.__file__)") # The package file is defined in the python source and __file__ must point to it assert Path("src/simplest/__init__.py").resolve().samefile(Path(location).resolve()) location = isolated.execute( "import simplest._module; print(simplest._module.__file__)" ) if sys.version_info < (3, 8, 7): import distutils.sysconfig # pylint: disable=deprecated-module ext_suffix = distutils.sysconfig.get_config_var("EXT_SUFFIX") else: ext_suffix = sysconfig.get_config_var("EXT_SUFFIX") module_source = python_source if editable_mode == "inplace" else cmake_install module_file = module_source / f"_module{ext_suffix}" # Windows FindPython may produce the wrong extension if ( sys.version_info < (3, 8, 7) and sys.platform.startswith("win") and not module_file.is_file() ): module_file = module_source / "_module.pyd" assert module_file.samefile(Path(location).resolve()) scikit-build-core-0.11.1/tests/test_pyproject_purelib.py000066400000000000000000000011131477275177200234360ustar00rootroot00000000000000from pathlib import Path import pytest from scikit_build_core.build import build_wheel @pytest.mark.compile @pytest.mark.configure @pytest.mark.usefixtures("package_simple_purelib_package") def test_pep517_wheel(virtualenv, tmp_path: Path): dist = tmp_path / "dist" out = build_wheel(str(dist), {}) (wheel,) = dist.glob("purelib_example-0.0.1-*-none-any.whl") assert wheel == dist / out virtualenv.install(wheel) version = virtualenv.execute( "import purelib_example; print(purelib_example.__version__)", ) assert version.strip() == "1.2.3" scikit-build-core-0.11.1/tests/test_schema.py000066400000000000000000000067511477275177200211520ustar00rootroot00000000000000from __future__ import annotations from pathlib import Path from typing import Any import pytest from scikit_build_core._compat import tomllib from scikit_build_core.settings.skbuild_schema import ( generate_skbuild_schema, get_skbuild_schema, ) DIR = Path(__file__).parent.resolve() def test_compare_schemas(): """ Should be the same. If not, run nox -s generate_schema """ assert generate_skbuild_schema() == get_skbuild_schema() SCHEMAS = [ *DIR.parent.joinpath("docs/examples").glob("**/pyproject.toml"), *DIR.joinpath("packages").glob("**/pyproject.toml"), ] @pytest.mark.parametrize("filepath", SCHEMAS) def test_valid_schemas_files(filepath: Path) -> None: api = pytest.importorskip("validate_pyproject.api") with filepath.open("rb") as f: example = tomllib.load(f) validator = api.Validator() assert validator(example) is not None @pytest.mark.parametrize( "addition", [ {"minimum-version": 0.3}, {"random": "not valid"}, {"logging": {"level": "POODLE"}}, {"generate": [{"path": "CMakeLists.txt"}]}, {"generate": [{"path": "me.py", "template": "hi", "template-path": "hello"}]}, {"generate": [{"path": "me.py", "template": ""}]}, {"overrides": [{"cmake": {"args": ["-DFOO=BAR"]}}]}, {"overrides": [{"select": {"python-version": ">=3.10"}}]}, { "overrides": [ { "if": {"python-version": ">=3.10"}, "cmake.nonexist": ["-DFOO=BAR"], } ] }, {"metadata": {"version": {"provider-path": True}}}, {"metadata": {"version": {"provider": 2}}}, {"metadata": {"invalid": {"provider": "correct"}}}, {"cmake": {"define": {"FOO": {"env": ""}}}}, {"cmake": {"define": {"FOO": {"default": False}}}}, ], ) def test_invalid_schemas(addition: dict[str, Any]) -> None: fastjsonschema = pytest.importorskip("fastjsonschema") api = pytest.importorskip("validate_pyproject.api") example_toml = """\ [project] name = "myproj" version = "0" [tool.scikit-build] minimum-version = "0.3" """ example = tomllib.loads(example_toml) example["tool"]["scikit-build"].update(**addition) validator = api.Validator() print(example) with pytest.raises(fastjsonschema.JsonSchemaValueException): validator(example) @pytest.mark.parametrize( "addition", [ {"generate": [{"path": "CMakeLists.txt", "template": "hi"}]}, {"generate": [{"path": "me.py", "template-path": "hello"}]}, { "overrides": [ { "if": {"python-version": ">=3.10"}, "cmake": {"args": ["-DFOO=BAR"]}, } ] }, {"metadata": {"version": {"provider-path": "string"}}}, {"metadata": {"description": {"anything": True}}}, {"cmake": {"define": {"FOO": "BAR"}}}, {"cmake": {"define": {"FOO": {"env": "FOO"}}}}, {"cmake": {"define": {"FOO": {"env": "FOO", "default": False}}}}, ], ) def test_valid_schemas(addition: dict[str, Any]) -> None: api = pytest.importorskip("validate_pyproject.api") example_toml = """\ [project] name = "myproj" version = "0" [tool.scikit-build] minimum-version = "0.3" """ example = tomllib.loads(example_toml) example["tool"]["scikit-build"].update(**addition) validator = api.Validator() validator(example) scikit-build-core-0.11.1/tests/test_settings.py000066400000000000000000000501771477275177200215530ustar00rootroot00000000000000import dataclasses import re from pathlib import Path from typing import Any, Dict, List, Literal, Optional, Union import pytest from packaging.version import Version from scikit_build_core._compat.builtins import ExceptionGroup from scikit_build_core.settings.sources import ( ConfSource, EnvSource, SourceChain, TOMLSource, ) @dataclasses.dataclass class SettingChecker: zero: Path one: str two: int three: List[str] four: List[int] = dataclasses.field(default_factory=list) five: str = "empty" six: Path = Path("empty") seven: Union[int, None] = None eight: Dict[str, Union[str, bool]] = dataclasses.field(default_factory=dict) nine: Dict[str, int] = dataclasses.field(default_factory=dict) literal: Literal["one", "two", "three"] = "one" # TOML only ten: Dict[str, Any] = dataclasses.field(default_factory=dict) eleven: Optional[Union[List[str], Dict[str, str]]] = None def test_empty(monkeypatch): monkeypatch.setenv("SKB_ZERO", "zero") monkeypatch.setenv("SKB_ONE", "one") monkeypatch.setenv("SKB_TWO", "2") monkeypatch.setenv("SKB_THREE", "three") sources = SourceChain( EnvSource("SKB"), ConfSource(settings={}), TOMLSource(settings={}), ) settings = sources.convert_target(SettingChecker) assert settings.zero == Path("zero") assert settings.one == "one" assert settings.two == 2 assert settings.three == ["three"] assert settings.four == [] assert settings.five == "empty" assert settings.six == Path("empty") assert settings.seven is None assert settings.eight == {} assert settings.nine == {} assert settings.literal == "one" def test_env(monkeypatch): monkeypatch.setenv("SKBUILD_ZERO", "zero") monkeypatch.setenv("SKBUILD_ONE", "one") monkeypatch.setenv("SKBUILD_TWO", "2") monkeypatch.setenv("SKBUILD_THREE", "three") monkeypatch.setenv("SKBUILD_FOUR", "4") monkeypatch.setenv("SKBUILD_FIVE", "five") monkeypatch.setenv("SKBUILD_SIX", "six") monkeypatch.setenv("SKBUILD_SEVEN", "7") monkeypatch.setenv("SKBUILD_EIGHT", "thing=8;thought=9") monkeypatch.setenv("SKBUILD_NINE", "thing=8") monkeypatch.setenv("SKBUILD_ELEVEN", "one;two") monkeypatch.setenv("SKBUILD_LITERAL", "two") sources = SourceChain( EnvSource("SKBUILD"), ConfSource(settings={}), TOMLSource(settings={}), ) settings = sources.convert_target(SettingChecker) assert settings.zero == Path("zero") assert settings.one == "one" assert settings.two == 2 assert settings.three == ["three"] assert settings.four == [4] assert settings.five == "five" assert settings.six == Path("six") assert settings.seven == 7 assert settings.eight == {"thing": "8", "thought": "9"} assert settings.nine == {"thing": 8} assert settings.eleven == ["one", "two"] assert settings.literal == "two" def test_env_union(monkeypatch): monkeypatch.setenv("SKBUILD_ZERO", "zero") monkeypatch.setenv("SKBUILD_ONE", "one") monkeypatch.setenv("SKBUILD_TWO", "2") monkeypatch.setenv("SKBUILD_THREE", "three") monkeypatch.setenv("SKBUILD_ELEVEN", "a=one;b=two") sources = SourceChain( EnvSource("SKBUILD"), ConfSource(settings={}), TOMLSource(settings={}), ) settings = sources.convert_target(SettingChecker) assert settings.eleven == {"a": "one", "b": "two"} def test_conf(): config_settings: Dict[str, Union[str, List[str]]] = { "zero": "zero", "one": "one", "two": "2", "three": ["three"], "four": ["4"], "five": "five", "six": "six", "seven": "7", "eight.foo": "one", "eight.bar": "two", "nine.thing": "8", "eleven": ["one", "two"], "literal": "three", } sources = SourceChain( EnvSource("SKBUILD"), ConfSource(settings=config_settings), TOMLSource(settings={}), ) settings = sources.convert_target(SettingChecker) assert settings.zero == Path("zero") assert settings.one == "one" assert settings.two == 2 assert settings.three == ["three"] assert settings.four == [4] assert settings.five == "five" assert settings.six == Path("six") assert settings.seven == 7 assert settings.eight == {"foo": "one", "bar": "two"} assert settings.nine == {"thing": 8} assert settings.literal == "three" def test_toml(): toml_settings = { "zero": "zero", "one": "one", "two": 2, "three": ["three"], "four": [4], "five": "five", "six": "six", "seven": 7, "eight": {"one": "one", "two": "two", "bool": False}, "nine": {"thing": 8}, "ten": {"a": {"b": 3}}, "eleven": ["one", "two"], "literal": "three", } sources = SourceChain( EnvSource("SKBUILD"), ConfSource(settings={}), TOMLSource(settings=toml_settings), ) settings = sources.convert_target(SettingChecker) assert settings.zero == Path("zero") assert settings.one == "one" assert settings.two == 2 assert settings.three == ["three"] assert settings.four == [4] assert settings.five == "five" assert settings.six == Path("six") assert settings.seven == 7 assert settings.eight == {"one": "one", "two": "two", "bool": False} assert settings.nine == {"thing": 8} assert settings.ten == {"a": {"b": 3}} assert settings.eleven == ["one", "two"] assert settings.literal == "three" def test_toml_union(): toml_settings = { "zero": "zero", "one": "one", "two": 2, "three": ["three"], "eleven": {"a": "one", "b": "two"}, } sources = SourceChain( EnvSource("SKBUILD"), ConfSource(settings={}), TOMLSource(settings=toml_settings), ) settings = sources.convert_target(SettingChecker) assert settings.eleven == {"a": "one", "b": "two"} def test_all_names(): keys = [x.name for x in dataclasses.fields(SettingChecker)] envame = [f"SKBUILD_{x.upper()}" for x in keys] assert list(EnvSource("SKBUILD").all_option_names(SettingChecker)) == envame assert list(ConfSource(settings={}).all_option_names(SettingChecker)) == keys skkeys = [f"skbuild.{x}" for x in keys] assert ( list(ConfSource("skbuild", settings={}).all_option_names(SettingChecker)) == skkeys ) assert list(TOMLSource(settings={}).all_option_names(SettingChecker)) == keys assert ( list(TOMLSource("skbuild", settings={}).all_option_names(SettingChecker)) == skkeys ) @dataclasses.dataclass class NestedSettingChecker: zero: Path one: str two: SettingChecker three: int = 3 def test_env_nested(monkeypatch): monkeypatch.setenv("SKBUILD_ZERO", "zero") monkeypatch.setenv("SKBUILD_ONE", "one") monkeypatch.setenv("SKBUILD_TWO_ZERO", "zero") monkeypatch.setenv("SKBUILD_TWO_ONE", "one") monkeypatch.setenv("SKBUILD_TWO_TWO", "2") monkeypatch.setenv("SKBUILD_TWO_THREE", "three") monkeypatch.setenv("SKBUILD_TWO_FOUR", "4") sources = SourceChain( EnvSource("SKBUILD"), ConfSource(settings={}), TOMLSource(settings={}), ) settings = sources.convert_target(NestedSettingChecker) assert settings.zero == Path("zero") assert settings.one == "one" assert settings.two.zero == Path("zero") assert settings.two.one == "one" assert settings.two.two == 2 assert settings.two.three == ["three"] assert settings.two.four == [4] assert settings.two.five == "empty" assert settings.two.six == Path("empty") assert settings.three == 3 def test_conf_nested(): config_settings: Dict[str, Union[str, List[str]]] = { "zero": "zero", "one": "one", "two.zero": "zero", "two.one": "one", "two.two": "2", "two.three": ["three"], "two.four": ["4"], } sources = SourceChain( EnvSource("SKBUILD"), ConfSource(settings=config_settings), TOMLSource(settings={}), ) settings = sources.convert_target(NestedSettingChecker) assert settings.zero == Path("zero") assert settings.one == "one" assert settings.two.zero == Path("zero") assert settings.two.one == "one" assert settings.two.two == 2 assert settings.two.three == ["three"] assert settings.two.four == [4] assert settings.two.five == "empty" assert settings.two.six == Path("empty") assert settings.three == 3 def test_toml_nested(): toml_settings = { "zero": "zero", "one": "one", "two": { "zero": "zero", "one": "one", "two": 2, "three": ["three"], "four": [4], }, } sources = SourceChain( EnvSource("SKBUILD"), ConfSource(settings={}), TOMLSource(settings=toml_settings), ) settings = sources.convert_target(NestedSettingChecker) assert settings.zero == Path("zero") assert settings.one == "one" assert settings.two.zero == Path("zero") assert settings.two.one == "one" assert settings.two.two == 2 assert settings.two.three == ["three"] assert settings.two.four == [4] assert settings.two.five == "empty" assert settings.two.six == Path("empty") assert settings.three == 3 def test_all_names_nested(): keys_two = [x.name for x in dataclasses.fields(SettingChecker)] ikeys = [["zero"], ["one"], *[["two", k] for k in keys_two], ["three"]] envame = [f"SKBUILD_{'_'.join(x).upper()}" for x in ikeys] assert list(EnvSource("SKBUILD").all_option_names(NestedSettingChecker)) == envame keys = [".".join(x) for x in ikeys] assert list(ConfSource(settings={}).all_option_names(NestedSettingChecker)) == keys skkeys = [f"skbuild.{x}" for x in keys] assert ( list(ConfSource("skbuild", settings={}).all_option_names(NestedSettingChecker)) == skkeys ) assert list(TOMLSource(settings={}).all_option_names(NestedSettingChecker)) == keys assert ( list(TOMLSource("skbuild", settings={}).all_option_names(NestedSettingChecker)) == skkeys ) @dataclasses.dataclass class SettingBools: false: bool = False true: bool = True def test_env_var_bools_empty(monkeypatch): monkeypatch.setenv("SKBUILD_FALSE", "") monkeypatch.setenv("SKBUILD_TRUE", "") sources = SourceChain( EnvSource("SKBUILD"), ConfSource(settings={}), TOMLSource(settings={}), ) settings = sources.convert_target(SettingBools) assert not settings.false assert settings.true @pytest.mark.parametrize( ("truthy", "falsey"), [("1", "0"), ("true", "false"), ("yes", "no"), ("on", "off")] ) def test_env_var_bools(monkeypatch, truthy, falsey): sources = SourceChain( EnvSource("SKBUILD"), ConfSource(settings={}), TOMLSource(settings={}), ) monkeypatch.setenv("SKBUILD_FALSE", truthy) monkeypatch.setenv("SKBUILD_TRUE", falsey) settings = sources.convert_target(SettingBools) assert settings.false assert not settings.true def test_conf_settings_bools(): sources = SourceChain( ConfSource(settings={"false": True, "true": False}), ) settings = sources.convert_target(SettingBools) assert settings.false assert not settings.true @dataclasses.dataclass class SettingLists: list0: List[str] = dataclasses.field(default_factory=list) list1: List[str] = dataclasses.field(default_factory=list) list2: List[str] = dataclasses.field(default_factory=list) list3: List[str] = dataclasses.field(default_factory=list) list4: List[str] = dataclasses.field(default_factory=list) def test_lists(monkeypatch): monkeypatch.setenv("SKBUILD_LIST1", "one;two;three") sources = SourceChain( EnvSource("SKBUILD"), ConfSource( settings={"list2": ["one", "two", "three"], "list3": "one;two;three"} ), TOMLSource(settings={"list4": ["one", "two", "three"]}), ) settings = sources.convert_target(SettingLists) assert settings.list0 == [] assert settings.list1 == ["one", "two", "three"] assert settings.list2 == ["one", "two", "three"] assert settings.list3 == ["one", "two", "three"] assert settings.list4 == ["one", "two", "three"] @dataclasses.dataclass class SettingListsOptional: list0: Optional[List[str]] = None list1: Optional[List[str]] = None list2: Optional[List[str]] = None list3: Optional[List[str]] = None list4: Optional[List[str]] = None def test_lists_optional(monkeypatch): monkeypatch.setenv("SKBUILD_LIST1", "one;two;three") sources = SourceChain( EnvSource("SKBUILD"), ConfSource( settings={"list2": ["one", "two", "three"], "list3": "one;two;three"} ), TOMLSource(settings={"list4": ["one", "two", "three"]}), ) settings = sources.convert_target(SettingListsOptional) assert settings.list0 is None assert settings.list1 == ["one", "two", "three"] assert settings.list2 == ["one", "two", "three"] assert settings.list3 == ["one", "two", "three"] assert settings.list4 == ["one", "two", "three"] @pytest.mark.parametrize( "prefixes", [(), ("x",), ("ab", "cd"), ("x", "other")], ids=".".join ) def test_missing_opts_conf(prefixes): settings = { "one": "one", "missing": "missing", "two.one": "one", "two.missing": "missing", "other.missing": "missing", } settings = {".".join([*prefixes, k]): v for k, v in settings.items()} print(settings) sources = SourceChain( EnvSource("SKBUILD"), ConfSource(*prefixes, settings=settings), TOMLSource(settings={}), ) answer = ["missing", "two.missing", "other"] answer = [".".join([*prefixes, k]) for k in answer] assert list(sources.unrecognized_options(NestedSettingChecker)) == answer def test_ignore_conf(): settings = { "one": "one", "missing": "missing", "two.one": "one", "two.missing": "missing", "other.missing": "missing", } sources = SourceChain( EnvSource("SKBUILD"), ConfSource(settings=settings, verify=False), TOMLSource(settings={}), ) assert list(sources.unrecognized_options(NestedSettingChecker)) == [] def test_missing_opts_toml(): sources = SourceChain( EnvSource("SKBUILD"), ConfSource(settings={}), TOMLSource( "tool", settings={ "tool": { "one": "one", "missing": "missing", "two": {"one": "one", "missing": "missing"}, "other": {"missing": "missing"}, }, "things": {"thing": "x"}, }, ), ) assert list(sources.unrecognized_options(NestedSettingChecker)) == [ "tool.missing", "tool.two.missing", "tool.other", ] @dataclasses.dataclass class SettingsOverride: dict0: Dict[str, str] = dataclasses.field(default_factory=dict) dict1: Optional[Dict[str, int]] = None dict2: Optional[Dict[str, str]] = None def test_override(): sources = SourceChain( EnvSource("SKBUILD"), ConfSource( settings={"dict0.one": "one", "dict0.two": "two", "dict1.other": "2"} ), TOMLSource(settings={"dict0": {"two": "TWO", "three": "THREE"}, "dict2": {}}), ) settings = sources.convert_target(SettingsOverride) assert settings.dict0 == {"one": "one", "two": "two", "three": "THREE"} assert settings.dict1 == {"other": 2} assert settings.dict2 == {} @dataclasses.dataclass class SettingVersion: first_req: Version = Version("1.2") first_opt: Optional[Version] = None second_req: Version = Version("1.2") second_opt: Optional[Version] = None third_req: Version = Version("1.2") third_opt: Optional[Version] = None fourth_req: Version = Version("1.2") fourth_opt: Optional[Version] = None def test_versions(monkeypatch): monkeypatch.setenv("SKBUILD_SECOND_REQ", "2.3") monkeypatch.setenv("SKBUILD_SECOND_OPT", "2.4") sources = SourceChain( EnvSource("SKBUILD"), ConfSource(settings={"third-req": "3.4", "third-opt": "3.5"}), TOMLSource(settings={"fourth-req": "4.5", "fourth-opt": "4.6"}), ) settings = sources.convert_target(SettingVersion) assert settings.first_req == Version("1.2") assert settings.first_opt is None assert settings.second_req == Version("2.3") assert settings.second_opt == Version("2.4") assert settings.third_req == Version("3.4") assert settings.third_opt == Version("3.5") assert settings.fourth_req == Version("4.5") assert settings.fourth_opt == Version("4.6") @dataclasses.dataclass class SettingLiteral: literal: Literal["one", "two", "three"] optional: Optional[Literal["foo", "bar"]] = None def test_literal_failure_empty(): sources = SourceChain( EnvSource("SKBUILD"), ConfSource(settings={}), TOMLSource(settings={}), ) with pytest.raises(ValueError, match="Missing value for 'literal'"): # noqa: PT012 try: sources.convert_target(SettingLiteral) except ExceptionGroup as e: assert len(e.exceptions) == 1 # noqa: PT017 raise e.exceptions[0] from None def test_literal_failure_not_contained(): sources = SourceChain( EnvSource("SKBUILD"), ConfSource(settings={"literal": "four"}), TOMLSource(settings={}), ) with pytest.raises( # noqa: PT012 TypeError, match=re.escape("'four' not in ('one', 'two', 'three')") ): try: sources.convert_target(SettingLiteral) except ExceptionGroup as e: assert len(e.exceptions) == 1 # noqa: PT017 raise e.exceptions[0] from None def test_literal_failure_not_contained_optional(): sources = SourceChain( EnvSource("SKBUILD"), ConfSource(settings={"literal": "three"}), TOMLSource(settings={"optional": "five"}), ) with pytest.raises( # noqa: PT012 TypeError, match=re.escape("'five' not in ('foo', 'bar')") ): try: sources.convert_target(SettingLiteral) except ExceptionGroup as e: assert len(e.exceptions) == 1 # noqa: PT017 raise e.exceptions[0] from None @dataclasses.dataclass class ArraySetting: required: str optional: Optional[str] = None @dataclasses.dataclass class ArraySettings: array: List[ArraySetting] = dataclasses.field(default_factory=list) def test_empty_array(): sources = SourceChain( EnvSource("SKBUILD"), ConfSource(settings={}), TOMLSource(settings={}), ) settings = sources.convert_target(ArraySettings) assert settings.array == [] def test_toml_required(): sources = SourceChain( EnvSource("SKBUILD"), ConfSource(settings={}), TOMLSource(settings={"array": [{"optional": "2"}]}), ) with pytest.raises(ExceptionGroup): sources.convert_target(ArraySettings) def test_toml_array(): sources = SourceChain( EnvSource("SKBUILD"), ConfSource(settings={}), TOMLSource( settings={ "array": [{"required": "one"}, {"required": "two", "optional": "2"}] } ), ) settings = sources.convert_target(ArraySettings) assert settings.array == [ArraySetting("one"), ArraySetting("two", "2")] def test_env_array_error(monkeypatch: pytest.MonkeyPatch): monkeypatch.setenv("SKBUILD_ARRAY", "one") sources = SourceChain( EnvSource("SKBUILD"), ConfSource(settings={}), TOMLSource(settings={}), ) with pytest.raises(ExceptionGroup): sources.convert_target(ArraySettings) def test_config_array_error(): sources = SourceChain( EnvSource("SKBUILD"), ConfSource( settings={ "array": [{"required": "one"}, {"required": "two", "optional": "2"}] # type: ignore[list-item] } ), TOMLSource(settings={}), ) with pytest.raises(ExceptionGroup): sources.convert_target(ArraySettings) scikit-build-core-0.11.1/tests/test_settings_overrides.py000066400000000000000000000645571477275177200236440ustar00rootroot00000000000000from __future__ import annotations import sysconfig import typing from pathlib import Path from textwrap import dedent import pytest import scikit_build_core.settings.skbuild_overrides from scikit_build_core.settings.skbuild_overrides import regex_match from scikit_build_core.settings.skbuild_read_settings import SettingsReader if typing.TYPE_CHECKING: from pytest_subprocess import FakeProcess class VersionInfo(typing.NamedTuple): major: int minor: int micro: int releaselevel: str = "final" @pytest.mark.parametrize("python_version", ["3.9", "3.10"]) def test_skbuild_overrides_pyver( python_version: str, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ): monkeypatch.setattr("sys.version_info", (*map(int, python_version.split(".")), 0)) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( """\ [[tool.scikit-build.overrides]] if = {python-version = ">=3.10"} cmake.args = ["-DFOO=BAR"] experimental = true cmake.define.SPAM = "EGGS" sdist.cmake = true """ ), encoding="utf-8", ) settings_reader = SettingsReader.from_file(pyproject_toml) settings = settings_reader.settings if python_version == "3.10": assert settings.cmake.args == ["-DFOO=BAR"] assert settings.cmake.define == {"SPAM": "EGGS"} assert settings.experimental assert settings.sdist.cmake else: assert not settings.cmake.args assert not settings.cmake.define assert not settings.experimental @pytest.mark.parametrize("implementation_version", ["7.3.14", "7.3.15"]) def test_skbuild_overrides_implver( implementation_version: str, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ): monkeypatch.setattr("sys.implementation.name", "pypy") monkeypatch.setattr( "sys.implementation.version", VersionInfo(*(int(x) for x in implementation_version.split("."))), # type: ignore[arg-type] ) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( """\ [[tool.scikit-build.overrides]] if.implementation-name = "pypy" if.implementation-version = ">=7.3.15" experimental = true """ ), encoding="utf-8", ) settings_reader = SettingsReader.from_file(pyproject_toml) settings = settings_reader.settings if implementation_version == "7.3.15": assert settings.experimental else: assert not settings.experimental @pytest.mark.parametrize("implementation_name", ["cpython", "pypy"]) @pytest.mark.parametrize("platform_system", ["darwin", "linux"]) def test_skbuild_overrides_dual( implementation_name: str, platform_system: str, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ): monkeypatch.setattr( "sys.implementation", type("Mock", (), {"name": implementation_name}) ) monkeypatch.setattr("sys.platform", platform_system) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( """\ [[tool.scikit-build.overrides]] if = {implementation-name = "pypy", platform-system = "darwin"} editable.verbose = false install.components = ["headers"] [[tool.scikit-build.overrides]] if.implementation-name = "cpython" if.platform-system = "darwin" install.components = ["bindings"] """ ), encoding="utf-8", ) settings_reader = SettingsReader.from_file(pyproject_toml) settings = settings_reader.settings print(settings_reader.overrides) if implementation_name == "pypy" and platform_system == "darwin": assert not settings.editable.verbose assert settings.install.components == ["headers"] elif implementation_name == "cpython" and platform_system == "darwin": assert settings.editable.verbose assert settings.install.components == ["bindings"] else: assert settings.editable.verbose assert not settings.install.components @pytest.mark.parametrize("implementation_name", ["cpython", "pypy"]) @pytest.mark.parametrize("platform_system", ["darwin", "linux"]) def test_skbuild_overrides_any( implementation_name: str, platform_system: str, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ): monkeypatch.setattr( "sys.implementation", type("Mock", (), {"name": implementation_name}) ) monkeypatch.setattr("sys.platform", platform_system) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( """\ [[tool.scikit-build.overrides]] if.any = {implementation-name = "pypy", platform-system = "darwin"} editable.verbose = false install.components = ["headers"] [[tool.scikit-build.overrides]] if.any.implementation-name = "cpython" if.any.platform-system = "darwin" install.components = ["bindings"] """ ), encoding="utf-8", ) settings_reader = SettingsReader.from_file(pyproject_toml) settings = settings_reader.settings if implementation_name == "cpython" or platform_system == "darwin": assert settings.editable.verbose == ( platform_system == "linux" and implementation_name == "cpython" ) assert settings.install.components == ["bindings"] elif implementation_name == "pypy" or platform_system == "darwin": assert not settings.editable.verbose assert settings.install.components == ["headers"] else: assert settings.editable.verbose assert not settings.install.components @pytest.mark.parametrize("python_version", ["3.9", "3.10"]) @pytest.mark.parametrize("implementation_name", ["cpython", "pypy"]) @pytest.mark.parametrize("platform_system", ["darwin", "linux"]) def test_skbuild_overrides_any_mixed( implementation_name: str, platform_system: str, python_version: str, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ): monkeypatch.setattr( "sys.implementation", type("Mock", (), {"name": implementation_name}) ) monkeypatch.setattr("sys.platform", platform_system) monkeypatch.setattr("sys.version_info", (*map(int, python_version.split(".")), 0)) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( """\ [[tool.scikit-build.overrides]] if.any = {implementation-name = "pypy", platform-system = "darwin"} if.python-version = ">=3.10" editable.verbose = false install.components = ["headers"] """ ), encoding="utf-8", ) settings_reader = SettingsReader.from_file(pyproject_toml) settings = settings_reader.settings if python_version == "3.10" and ( implementation_name == "pypy" or platform_system == "darwin" ): assert not settings.editable.verbose assert settings.install.components == ["headers"] else: assert settings.editable.verbose assert not settings.install.components @pytest.mark.parametrize("platform_node", ["thismatch", "matchthat"]) def test_skbuild_overrides_platnode( platform_node: str, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ): monkeypatch.setattr("platform.node", lambda: platform_node) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( """\ [[tool.scikit-build.overrides]] if.platform-node = "^match" experimental = true """ ), encoding="utf-8", ) settings_reader = SettingsReader.from_file(pyproject_toml) settings = settings_reader.settings if platform_node == "matchthat": assert settings.experimental else: assert not settings.experimental @pytest.mark.parametrize("platform_machine", ["x86_64", "x86_32", "other"]) def test_skbuild_overrides_regex( platform_machine: str, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ): monkeypatch.setattr("platform.machine", lambda: platform_machine) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( """\ [tool.scikit-build] install.components = ["default"] [[tool.scikit-build.overrides]] if = {platform_machine = "x86_.*"} install.components = ["headers"] [[tool.scikit-build.overrides]] if = {platform_machine = "x86_32"} install.components = ["headers_32"] """ ), encoding="utf-8", ) settings_reader = SettingsReader.from_file(pyproject_toml) settings = settings_reader.settings if platform_machine == "x86_64": assert settings.install.components == ["headers"] elif platform_machine == "x86_32": assert settings.install.components == ["headers_32"] else: assert settings.install.components == ["default"] def test_skbuild_overrides_no_if( tmp_path: Path, ): pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( """\ [[tool.scikit-build.overrides]] minimum-version="0.1" """ ), encoding="utf-8", ) with pytest.raises(KeyError, match="At least one 'if' override must be provided"): SettingsReader.from_file(pyproject_toml) def test_skbuild_overrides_empty_if( tmp_path: Path, ): pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( """\ [[tool.scikit-build.overrides]] if = {} minimum-version="0.1" """ ), encoding="utf-8", ) with pytest.raises(KeyError, match="At least one 'if' override must be provided"): SettingsReader.from_file(pyproject_toml) def test_skbuild_overrides_invalid_key( tmp_path: Path, ): pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( """\ [[tool.scikit-build.overrides]] if = {python_version = ">=3"} invalid-key = "Hi" """ ), encoding="utf-8", ) settings = SettingsReader.from_file(pyproject_toml) with pytest.raises(SystemExit): settings.validate_may_exit() @pytest.mark.parametrize("regex", ["is", "this", "^this", "string$"]) def test_regex_match(regex: str): assert regex_match("this_is_a_string", regex) @pytest.mark.parametrize("regex", ["^string", "this$", "other"]) def test_not_regex_match(regex: str): assert not regex_match("this_is_a_string", regex) @pytest.mark.parametrize("envvar", ["BAR", "", None]) def test_skbuild_env( envvar: str | None, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ): if envvar is None: monkeypatch.delenv("FOO", raising=False) else: monkeypatch.setenv("FOO", envvar) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( """\ [[tool.scikit-build.overrides]] if.env.FOO = "BAR" sdist.cmake = true """ ), encoding="utf-8", ) settings_reader = SettingsReader.from_file(pyproject_toml) settings = settings_reader.settings if envvar == "BAR": assert settings.sdist.cmake else: assert not settings.sdist.cmake @pytest.mark.parametrize("envvar", ["tRUE", "3", "0", "", None]) def test_skbuild_env_bool( envvar: str | None, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ): if envvar is None: monkeypatch.delenv("FOO", raising=False) else: monkeypatch.setenv("FOO", envvar) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( """\ [[tool.scikit-build.overrides]] if.env.FOO = true sdist.cmake = true """ ), encoding="utf-8", ) settings_reader = SettingsReader.from_file(pyproject_toml) settings = settings_reader.settings if envvar in {"tRUE", "3"}: assert settings.sdist.cmake else: assert not settings.sdist.cmake @pytest.mark.parametrize("envvar", ["random", "FalSE", "", "0", None]) def test_skbuild_env_negative_bool( envvar: str | None, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ): if envvar is None: monkeypatch.delenv("FOO", raising=False) else: monkeypatch.setenv("FOO", envvar) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( """\ [[tool.scikit-build.overrides]] if.env.FOO = false sdist.cmake = true """ ), encoding="utf-8", ) settings_reader = SettingsReader.from_file(pyproject_toml) settings = settings_reader.settings if envvar in {"random"}: assert not settings.sdist.cmake else: assert settings.sdist.cmake @pytest.mark.parametrize("foo", ["true", "false"]) @pytest.mark.parametrize("bar", ["true", "false"]) @pytest.mark.parametrize("any", [True, False]) def test_skbuild_env_bool_all_any( foo: str, bar: str, any: bool, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ): monkeypatch.setenv("FOO", foo) monkeypatch.setenv("BAR", bar) any_str = ".any" if any else "" pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( f"""\ [[tool.scikit-build.overrides]] if{any_str}.env.FOO = true if{any_str}.env.BAR = true sdist.cmake = true """ ), encoding="utf-8", ) settings_reader = SettingsReader.from_file(pyproject_toml) settings = settings_reader.settings if (foo == "true" and bar == "true") or (any and (foo == "true" or bar == "true")): assert settings.sdist.cmake else: assert not settings.sdist.cmake @pytest.mark.parametrize("state", ["wheel", "sdist"]) def test_skbuild_overrides_state(state: str, tmp_path: Path): pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( f"""\ [[tool.scikit-build.overrides]] if.state = "{state}" experimental = true """ ), encoding="utf-8", ) settings_reader = SettingsReader.from_file(pyproject_toml, state="wheel") settings = settings_reader.settings if state == "wheel": assert settings.experimental else: assert not settings.experimental @pytest.mark.parametrize("inherit", ["none", "append", "prepend"]) def test_skbuild_overrides_inherit(inherit: str, tmp_path: Path): pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( f"""\ [tool.scikit-build] cmake.args = ["a", "b"] cmake.targets = ["a", "b"] wheel.packages = ["a", "b"] wheel.license-files = ["a.txt", "b.txt"] wheel.exclude = ["x", "y"] install.components = ["a", "b"] cmake.define = {{a="A", b="B"}} [[tool.scikit-build.overrides]] if.state = "wheel" inherit.cmake.args = "{inherit}" inherit.cmake.targets = "{inherit}" inherit.wheel.packages = "{inherit}" inherit.wheel.license-files = "{inherit}" inherit.wheel.exclude = "{inherit}" inherit.install.components = "{inherit}" inherit.cmake.define = "{inherit}" cmake.args = ["c", "d"] cmake.targets = ["c", "d"] wheel.packages = ["c", "d"] wheel.license-files = ["c.txt", "d.txt"] wheel.exclude = ["xx", "yy"] install.components = ["c", "d"] cmake.define = {{b="X", c="C"}} """ ), encoding="utf-8", ) settings_reader = SettingsReader.from_file(pyproject_toml, state="wheel") settings = settings_reader.settings if inherit == "none": assert settings.cmake.args == ["c", "d"] assert settings.cmake.targets == ["c", "d"] assert settings.wheel.packages == ["c", "d"] assert settings.wheel.license_files == ["c.txt", "d.txt"] assert settings.wheel.exclude == ["xx", "yy"] assert settings.install.components == ["c", "d"] assert settings.cmake.define == {"b": "X", "c": "C"} elif inherit == "append": assert settings.cmake.args == ["a", "b", "c", "d"] assert settings.cmake.targets == ["a", "b", "c", "d"] assert settings.wheel.packages == ["a", "b", "c", "d"] assert settings.wheel.license_files == ["a.txt", "b.txt", "c.txt", "d.txt"] assert settings.wheel.exclude == ["x", "y", "xx", "yy"] assert settings.install.components == ["a", "b", "c", "d"] assert settings.cmake.define == {"a": "A", "b": "X", "c": "C"} elif inherit == "prepend": assert settings.cmake.args == ["c", "d", "a", "b"] assert settings.cmake.targets == ["c", "d", "a", "b"] assert settings.wheel.packages == ["c", "d", "a", "b"] assert settings.wheel.license_files == ["c.txt", "d.txt", "a.txt", "b.txt"] assert settings.wheel.exclude == ["xx", "yy", "x", "y"] assert settings.install.components == ["c", "d", "a", "b"] assert settings.cmake.define == {"a": "A", "b": "B", "c": "C"} @pytest.mark.parametrize("from_sdist", [True, False]) def test_skbuild_overrides_from_sdist( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, from_sdist: bool ): pyproject_toml = ( tmp_path / ("from_sdist" if from_sdist else "not_from_sdist") / "pyproject.toml" ) pyproject_toml.parent.mkdir(exist_ok=True) pyproject_toml.write_text( dedent( """\ [tool.scikit-build] cmake.version = ">=3.15" wheel.cmake = false sdist.cmake = false [[tool.scikit-build.overrides]] if.from-sdist = false wheel.cmake = true [[tool.scikit-build.overrides]] if.from-sdist = true sdist.cmake = true """ ), encoding="utf-8", ) if from_sdist: pyproject_toml.parent.joinpath("PKG-INFO").touch(exist_ok=True) monkeypatch.chdir(pyproject_toml.parent) settings_reader = SettingsReader.from_file(pyproject_toml, state="wheel") settings = settings_reader.settings assert settings.wheel.cmake != from_sdist assert settings.sdist.cmake == from_sdist def test_failed_retry(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( """\ [tool.scikit-build] wheel.cmake = false sdist.cmake = false [[tool.scikit-build.overrides]] if.failed = true wheel.cmake = true [[tool.scikit-build.overrides]] if.failed = false sdist.cmake = true """ ), encoding="utf-8", ) monkeypatch.chdir(tmp_path) settings_reader = SettingsReader.from_file(pyproject_toml, retry=False) settings = settings_reader.settings assert not settings.wheel.cmake assert settings.sdist.cmake settings_reader = SettingsReader.from_file(pyproject_toml, retry=True) settings = settings_reader.settings assert settings.wheel.cmake assert not settings.sdist.cmake @pytest.mark.parametrize("sys_tag", ["win32", "linux"]) def test_wheel_platform(tmp_path: Path, monkeypatch: pytest.MonkeyPatch, sys_tag: str): pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( """\ [tool.scikit-build] wheel.cmake = false [[tool.scikit-build.overrides]] if.cmake-wheel = true wheel.cmake = true """ ), encoding="utf-8", ) monkeypatch.chdir(tmp_path) monkeypatch.setattr("packaging.tags.sys_tags", lambda: [sys_tag]) settings_reader = SettingsReader.from_file(pyproject_toml, retry=False) settings = settings_reader.settings assert settings.wheel.cmake == (sys_tag == "win32") @pytest.mark.parametrize("cmake_version", ["3.21", "3.27"]) @pytest.mark.usefixtures("protect_get_requires") def test_system_cmake( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, cmake_version: str | None, fp: FakeProcess, ) -> None: if cmake_version: fp.register( [Path("cmake/path"), "-E", "capabilities"], stdout=f'{{"version":{{"string": "{cmake_version}"}}}}', ) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( """\ [tool.scikit-build] wheel.cmake = false [[tool.scikit-build.overrides]] if.system-cmake = ">=3.24" wheel.cmake = true """ ), encoding="utf-8", ) monkeypatch.chdir(tmp_path) settings_reader = SettingsReader.from_file(pyproject_toml, retry=False) settings = settings_reader.settings assert settings.wheel.cmake == (cmake_version == "3.27") def test_free_threaded_override(tmp_path: Path): pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( """\ [tool.scikit-build] wheel.cmake = false [[tool.scikit-build.overrides]] if.abi-flags = "t" wheel.cmake = true """ ) ) settings_reader = SettingsReader.from_file(pyproject_toml, state="wheel") settings = settings_reader.settings assert settings.wheel.cmake == bool(sysconfig.get_config_var("Py_GIL_DISABLED")) @pytest.mark.parametrize("version", ["0.9", "0.10"]) def test_skbuild_overrides_version( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, version: str ): monkeypatch.setattr( scikit_build_core.settings.skbuild_overrides, "__version__", version ) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( """\ [tool.scikit-build] wheel.cmake = false [[tool.scikit-build.overrides]] if.scikit-build-version = ">=0.10" wheel.cmake = true """ ) ) settings_reader = SettingsReader.from_file(pyproject_toml, state="wheel") settings = settings_reader.settings if version == "0.10": assert settings.wheel.cmake else: assert not settings.wheel.cmake def test_skbuild_overrides_unmatched_version( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ): monkeypatch.setattr( scikit_build_core.settings.skbuild_overrides, "__version__", "0.10" ) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( """\ [[tool.scikit-build.overrides]] if.scikit-build-version = "<0.10" if.is-not-real = true also-not-real = true """ ) ) settings = SettingsReader.from_file(pyproject_toml) settings.validate_may_exit() def test_skbuild_overrides_matched_version_if( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ): monkeypatch.setattr( scikit_build_core.settings.skbuild_overrides, "__version__", "0.10" ) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( """\ [[tool.scikit-build.overrides]] if.scikit-build-version = ">=0.10" if.is-not-real = true """ ) ) with pytest.raises(TypeError, match="is_not_real"): SettingsReader.from_file(pyproject_toml) def test_skbuild_overrides_matched_version_extra( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ): monkeypatch.setattr( scikit_build_core.settings.skbuild_overrides, "__version__", "0.10" ) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( """\ [[tool.scikit-build.overrides]] if.scikit-build-version = ">=0.10" not-real = true """ ) ) settings = SettingsReader.from_file(pyproject_toml) with pytest.raises(SystemExit): settings.validate_may_exit() assert "not-real" in capsys.readouterr().out def test_skbuild_overrides_matched_version_if_any( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ): monkeypatch.setattr( scikit_build_core.settings.skbuild_overrides, "__version__", "0.9" ) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( """\ [[tool.scikit-build.overrides]] if.any.scikit-build-version = ">=0.10" if.any.not-real = true also-not-real = true """ ) ) settings = SettingsReader.from_file(pyproject_toml) settings.validate_may_exit() def test_skbuild_overrides_matched_version_if_any_dual( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ): monkeypatch.setattr( scikit_build_core.settings.skbuild_overrides, "__version__", "0.9" ) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( """\ [[tool.scikit-build.overrides]] if.scikit-build-version = ">=0.10" if.any.not-real = true if.any.python-version = ">=3.8" also-not-real = true """ ) ) settings = SettingsReader.from_file(pyproject_toml) settings.validate_may_exit() def test_skbuild_overrides_matched_version_if_any_match( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ): monkeypatch.setattr( scikit_build_core.settings.skbuild_overrides, "__version__", "0.10" ) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( dedent( """\ [[tool.scikit-build.overrides]] if.any.scikit-build-version = ">=0.10" if.any.not-real = true if.python-version = ">=3.8" experimental = true """ ) ) with pytest.raises(TypeError, match="not_real"): SettingsReader.from_file(pyproject_toml) scikit-build-core-0.11.1/tests/test_setuptools_abi3.py000066400000000000000000000033451477275177200230250ustar00rootroot00000000000000import shutil import sys import sysconfig import zipfile from pathlib import Path import pytest from scikit_build_core.setuptools.build_meta import build_wheel pytestmark = pytest.mark.setuptools DIR = Path(__file__).parent.resolve() ABI_PKG = DIR / "packages/abi3_setuptools_ext" SYSCONFIGPLAT = sysconfig.get_platform() @pytest.mark.compile @pytest.mark.configure @pytest.mark.skipif( sys.implementation.name == "pypy", reason="pypy does not support abi3" ) @pytest.mark.skipif( sysconfig.get_config_var("Py_GIL_DISABLED"), reason="Free-threaded Python does not support abi3", ) @pytest.mark.skipif( SYSCONFIGPLAT.startswith(("msys", "mingw")), reason="abi3 FindPython on MSYS/MinGW reports not found", ) def test_abi3_wheel(tmp_path, monkeypatch, virtualenv): dist = tmp_path / "dist" dist.mkdir() # create a temporary copy of the package source so we don't contaminate the # main source tree with build artefacts src = tmp_path / "src" shutil.copytree(ABI_PKG, src) monkeypatch.chdir(src) out = build_wheel(str(dist)) (wheel,) = dist.glob("abi3_example-0.0.1-*.whl") assert wheel == dist / out assert "-cp38-abi3-" in out assert virtualenv.execute("print('hello')") == "hello" with zipfile.ZipFile(wheel) as zf: file_names = {Path(n).parts[0] for n in zf.namelist()} if sysconfig.get_platform().startswith("win"): assert "abi3_example.pyd" in file_names elif sys.platform.startswith("cygwin"): assert "abi3_example.abi3.dll" in file_names else: assert "abi3_example.abi3.so" in file_names virtualenv.install(wheel) output = virtualenv.execute("import abi3_example; print(abi3_example.square(2))") assert output == "4.0" scikit-build-core-0.11.1/tests/test_setuptools_pep517.py000066400000000000000000000152551477275177200232330ustar00rootroot00000000000000import importlib.metadata import tarfile import textwrap import zipfile from pathlib import Path import pytest from packaging.version import Version from scikit_build_core.setuptools.build_meta import build_sdist, build_wheel pytestmark = pytest.mark.setuptools setuptools_version = Version(importlib.metadata.version("setuptools")) @pytest.mark.usefixtures("package_simple_setuptools_ext") def test_pep517_sdist(tmp_path: Path): correct_metadata = textwrap.dedent( """\ Name: cmake-example Version: 0.0.1 Requires-Python: >=3.8 Provides-Extra: test """ # TODO: why is this missing? # Requires-Dist: pytest>=6.0; extra == "test" # This was removed in https://github.com/pypa/setuptools/pull/4698 as part of 2.2 support: # Metadata-Version: 2.1 ) metadata_set = set(correct_metadata.strip().splitlines()) dist = tmp_path / "dist" out = build_sdist(str(dist)) (sdist,) = dist.iterdir() assert sdist.name in {"cmake-example-0.0.1.tar.gz", "cmake_example-0.0.1.tar.gz"} assert sdist == dist / out cmake_example = sdist.name[:13] with tarfile.open(sdist) as f: file_names = set(f.getnames()) assert file_names == { f"{cmake_example}-0.0.1/{x}" for x in ( # TODO: "CMakeLists.txt", "PKG-INFO", "src", "src/cmake_example.egg-info", "src/cmake_example.egg-info/PKG-INFO", "src/cmake_example.egg-info/SOURCES.txt", "src/cmake_example.egg-info/dependency_links.txt", "src/cmake_example.egg-info/not-zip-safe", "src/cmake_example.egg-info/requires.txt", "src/cmake_example.egg-info/top_level.txt", "pyproject.toml", "setup.cfg", "setup.py", "LICENSE", # TODO: "src/main.cpp", ) } | {f"{cmake_example}-0.0.1"} pkg_info = f.extractfile(f"{cmake_example}-0.0.1/PKG-INFO") assert pkg_info pkg_info_contents = set(pkg_info.read().decode().strip().splitlines()) assert metadata_set <= pkg_info_contents @pytest.mark.compile @pytest.mark.configure @pytest.mark.broken_on_urct @pytest.mark.usefixtures("package_simple_setuptools_ext") def test_pep517_wheel(virtualenv, tmp_path: Path): dist = tmp_path / "dist" out = build_wheel(str(dist)) (wheel,) = dist.glob("cmake_example-0.0.1-*.whl") assert wheel == dist / out with zipfile.ZipFile(wheel) as zf: file_names = {Path(n).parts[0] for n in zf.namelist()} assert len(file_names) == 2 assert "cmake_example-0.0.1.dist-info" in file_names file_names.remove("cmake_example-0.0.1.dist-info") (so_file,) = file_names assert so_file.startswith("cmake_example") print("SOFILE:", so_file) virtualenv.install(wheel) version = virtualenv.execute( "import cmake_example; print(cmake_example.__version__)" ) assert version.strip() == "0.0.1" add = virtualenv.execute("import cmake_example; print(cmake_example.add(1, 2))") assert add.strip() == "3" @pytest.mark.usefixtures("package_toml_setuptools_ext") @pytest.mark.skipif( setuptools_version < Version("61.0"), reason="Requires setuptools 61+" ) def test_toml_sdist(tmp_path: Path): correct_metadata = textwrap.dedent( """\ Name: cmake-example Version: 0.0.1 Requires-Python: >=3.8 """ # This was removed in https://github.com/pypa/setuptools/pull/4698 as part of 2.2 support: # Metadata-Version: 2.1 ) metadata_set = set(correct_metadata.strip().splitlines()) dist = tmp_path / "dist" out = build_sdist(str(dist)) (sdist,) = dist.iterdir() assert sdist.name in {"cmake-example-0.0.1.tar.gz", "cmake_example-0.0.1.tar.gz"} assert sdist == dist / out cmake_example = sdist.name[:13] with tarfile.open(sdist) as f: file_names = set(f.getnames()) assert file_names == { f"{cmake_example}-0.0.1/{x}" for x in ( # TODO: "CMakeLists.txt", "PKG-INFO", "src", "src/cmake_example.egg-info", "src/cmake_example.egg-info/PKG-INFO", "src/cmake_example.egg-info/SOURCES.txt", "src/cmake_example.egg-info/dependency_links.txt", "src/cmake_example.egg-info/top_level.txt", "pyproject.toml", "setup.cfg", "LICENSE", # TODO: "src/main.cpp", ) } | {f"{cmake_example}-0.0.1"} pkg_info = f.extractfile(f"{cmake_example}-0.0.1/PKG-INFO") assert pkg_info pkg_info_contents = set(pkg_info.read().decode().strip().splitlines()) assert metadata_set <= pkg_info_contents @pytest.mark.compile @pytest.mark.configure @pytest.mark.usefixtures("package_toml_setuptools_ext") @pytest.mark.skipif( setuptools_version < Version("61.0"), reason="Requires setuptools 61+" ) def test_toml_wheel(virtualenv, tmp_path: Path): dist = tmp_path / "dist" out = build_wheel(str(dist)) (wheel,) = dist.glob("cmake_example-0.0.1-*.whl") assert wheel == dist / out with zipfile.ZipFile(wheel) as zf: file_names = {Path(n).parts[0] for n in zf.namelist()} assert len(file_names) == 2 assert "cmake_example-0.0.1.dist-info" in file_names file_names.remove("cmake_example-0.0.1.dist-info") (so_file,) = file_names assert so_file.startswith("cmake_example") print("SOFILE:", so_file) virtualenv.install(wheel) version = virtualenv.execute( "import cmake_example; print(cmake_example.__version__)" ) assert version.strip() == "0.0.1" add = virtualenv.execute("import cmake_example; print(cmake_example.add(1, 2))") assert add.strip() == "3" @pytest.mark.compile @pytest.mark.configure @pytest.mark.usefixtures("package_mixed_setuptools") def test_mixed_wheel(virtualenv, tmp_path: Path): dist = tmp_path / "dist" out = build_wheel(str(dist)) (wheel,) = dist.glob("mixed_setuptools-3.1.4-*.whl") assert wheel == dist / out with zipfile.ZipFile(wheel) as zf: file_names = {Path(n).parts[0] for n in zf.namelist()} assert len(file_names) == 2 assert "mixed_setuptools-3.1.4.dist-info" in file_names file_names.remove("mixed_setuptools-3.1.4.dist-info") (so_file,) = file_names assert so_file.startswith("mixed_setuptools") print("SOFILE:", so_file) virtualenv.install(wheel) add = virtualenv.execute( "import mixed_setuptools; print(mixed_setuptools.add(1, 2))" ) assert add.strip() == "3" scikit-build-core-0.11.1/tests/test_setuptools_pep518.py000066400000000000000000000036121477275177200232260ustar00rootroot00000000000000import sys import zipfile from pathlib import Path import pytest pytestmark = pytest.mark.setuptools # TODO: work out why this fails on Cygwin @pytest.mark.compile @pytest.mark.configure @pytest.mark.integration @pytest.mark.broken_on_urct @pytest.mark.xfail( sys.platform.startswith("cygwin"), reason="Cygwin fails here with ld errors", strict=False, ) @pytest.mark.usefixtures("package_simple_setuptools_ext") def test_pep518_wheel(isolated, tmp_path: Path): dist = tmp_path / "dist" isolated.install("build[virtualenv]") isolated.module("build", "--wheel", f"--outdir={dist}") (wheel,) = dist.iterdir() assert "cmake_example-0.0.1" in wheel.name assert wheel.suffix == ".whl" with zipfile.ZipFile(wheel) as zf: file_names = {Path(n).parts[0] for n in zf.namelist()} assert len(file_names) == 2 assert "cmake_example-0.0.1.dist-info" in file_names file_names.remove("cmake_example-0.0.1.dist-info") (so_file,) = file_names assert so_file.startswith("cmake_example") print("SOFILE:", so_file) isolated.install(wheel) version = isolated.execute("import cmake_example; print(cmake_example.__version__)") assert version == "0.0.1" add = isolated.execute("import cmake_example; print(cmake_example.add(1, 2))") assert add == "3" @pytest.mark.compile @pytest.mark.configure @pytest.mark.integration @pytest.mark.broken_on_urct @pytest.mark.xfail( sys.platform.startswith("cygwin"), reason="Cygwin fails here with ld errors", strict=False, ) @pytest.mark.usefixtures("package_simple_setuptools_ext") def test_pep518_pip(isolated): isolated.install("-v", ".") version = isolated.execute( "import cmake_example; print(cmake_example.__version__)", ) assert version == "0.0.1" add = isolated.execute( "import cmake_example; print(cmake_example.add(1, 2))", ) assert add == "3" scikit-build-core-0.11.1/tests/test_shutil.py000066400000000000000000000022331477275177200212110ustar00rootroot00000000000000from __future__ import annotations import shutil import stat import sys from typing import TYPE_CHECKING import pytest from scikit_build_core._shutil import _fix_all_permissions if TYPE_CHECKING: from pathlib import Path def _make_dir_with_ro(tmp_path: Path) -> Path: base = tmp_path / "fix_all_perm" base.mkdir() base.joinpath("normal_file.txt").touch() ro = base / "read_only.txt" ro.touch() ro.chmod(stat.S_IREAD) nested = base / "nested" nested.mkdir() ro2 = nested / "read_only_2.txt" ro2.touch() ro2.chmod(stat.S_IREAD) # Validity check assert not stat.S_IMODE(ro2.stat().st_mode) & stat.S_IWRITE return base @pytest.fixture def make_dir_with_ro(tmp_path: Path) -> Path: return _make_dir_with_ro(tmp_path) def test_broken_all_permissions(make_dir_with_ro: Path) -> None: if sys.platform.startswith("win"): with pytest.raises(PermissionError): shutil.rmtree(make_dir_with_ro) else: shutil.rmtree(make_dir_with_ro) def test_fix_all_permissions(make_dir_with_ro: Path) -> None: _fix_all_permissions(str(make_dir_with_ro)) shutil.rmtree(make_dir_with_ro) scikit-build-core-0.11.1/tests/test_simple_pure.py000066400000000000000000000057671477275177200222440ustar00rootroot00000000000000from __future__ import annotations import os import shutil import subprocess import sys import sysconfig from pathlib import Path import pytest from packaging.specifiers import SpecifierSet from scikit_build_core.cmake import CMake, CMaker DIR = Path(__file__).parent.absolute() has_make = shutil.which("make") is not None or shutil.which("gmake") is not None has_ninja = shutil.which("ninja") is not None def prepare_env_or_skip() -> None: if ( "CMAKE_GENERATOR" not in os.environ and not sysconfig.get_platform().startswith("win") and not has_make ): if has_ninja: os.environ["CMAKE_GENERATOR"] = "Ninja" else: pytest.skip("No build system found") @pytest.fixture(scope="session") def config(tmp_path_factory): prepare_env_or_skip() tmp_path = tmp_path_factory.mktemp("build") build_dir = tmp_path / "build" cmake = CMake.default_search(version=SpecifierSet(">=3.15")) config = CMaker( cmake, source_dir=DIR / "packages/simple_pure", build_dir=build_dir, build_type="Release", ) config.configure() config.build() return config # TODO: figure out why gmake is reporting no rule to make simple_pure.cpp @pytest.mark.compile @pytest.mark.configure @pytest.mark.xfail( sys.platform.startswith("cygwin"), strict=False, reason="No idea why this fails on Cygwin", ) def test_bin_in_config(config): # TODO: this should use config.single_config, but that's not always correct currently pkg = config.build_dir / ( "Release/simple_pure" if config.build_dir.joinpath("Release").is_dir() else "simple_pure" ) result = subprocess.run( [str(pkg)], capture_output=True, text=True, check=False, ) assert result.returncode == 0 assert result.stdout == "0 one 2 three \n" # TODO: figure out why gmake is reporting no rule to make simple_pure.cpp @pytest.mark.compile @pytest.mark.configure @pytest.mark.xfail( sys.platform.startswith("cygwin"), strict=False, reason="No idea why this fails on Cygwin", ) def test_install(config): install_dir = config.build_dir.parent / "install" config.install(install_dir) result = subprocess.run( [str(install_dir / "bin/simple_pure")], capture_output=True, text=True, check=False, ) assert result.returncode == 0 assert result.stdout == "0 one 2 three \n" @pytest.mark.configure def test_variable_defined(tmp_path, capfd): prepare_env_or_skip() build_dir = tmp_path / "build" cmake = CMake.default_search(version=SpecifierSet(">=3.15")) config = CMaker( cmake, source_dir=DIR / "packages/simple_pure", build_dir=build_dir, build_type="Release", ) config.init_cache({"SKBUILD": True}) config.configure(defines={"SKBUILD2": True}) out = capfd.readouterr().out assert "SKBUILD is defined to ON" in out assert "SKBUILD2 is defined to ON" in out scikit-build-core-0.11.1/tests/test_simplest_c.py000066400000000000000000000110171477275177200220430ustar00rootroot00000000000000import tarfile import zipfile from pathlib import Path import pytest from scikit_build_core.build import build_sdist, build_wheel from pathutils import contained DIR = Path(__file__).parent.resolve() SIMPLEST = DIR / "packages/simplest_c" def test_pep517_sdist(tmp_path, monkeypatch): dist = tmp_path.resolve() / "dist" monkeypatch.chdir(SIMPLEST) out = build_sdist(str(dist)) (sdist,) = dist.iterdir() assert sdist.name == "simplest-0.0.1.tar.gz" assert sdist == dist / out with tarfile.open(sdist) as f: file_names = set(f.getnames()) assert file_names == { f"simplest-0.0.1/{x}" for x in ( "CMakeLists.txt", "pyproject.toml", ".gitignore", "src/module.c", "src/simplest/__init__.py", "src/simplest/_module.pyi", "src/simplest/data.txt", "src/simplest/sdist_only.txt", "src/not_a_package/simple.txt", "src/simplest/excluded.txt", "PKG-INFO", ) } @pytest.mark.compile @pytest.mark.configure @pytest.mark.parametrize( "component", [[], ["PythonModule"], ["PythonModule", "Generated"]] ) def test_pep517_wheel(tmp_path, monkeypatch, virtualenv, component): dist = tmp_path / "dist" dist.mkdir() monkeypatch.chdir(SIMPLEST) out = build_wheel(str(dist), config_settings={"install.components": component}) (wheel,) = dist.glob("simplest-0.0.1-*.whl") assert wheel == dist / out virtualenv.install(wheel) with zipfile.ZipFile(wheel) as zf: file_paths = {Path(n) for n in zf.namelist()} file_names = {p.parts[0] for p in file_paths} simplest_pkg = {p.name for p in contained(file_paths, "simplest")} filtered_pkg = {x for x in simplest_pkg if not x.startswith("_module")} if not component or "PythonModule" in component: assert filtered_pkg != simplest_pkg else: assert filtered_pkg == simplest_pkg expected_wheel_files = { "__init__.py", "data.txt", "excluded.txt", "sdist_only.txt", } if not component: expected_wheel_files.add("generated_ignored.txt") expected_wheel_files.add("generated_no_wheel.txt") if not component or "Generated" in component: expected_wheel_files.add("generated.txt") assert len(filtered_pkg) == len(simplest_pkg) - 2 assert {"simplest-0.0.1.dist-info", "simplest"} == file_names assert expected_wheel_files == filtered_pkg # Note that generated_ignored.txt is here because all CMake installed files are # present, CMake has the final say. version = virtualenv.execute("from simplest import square; print(square(2))") assert version == "4.0" @pytest.mark.compile @pytest.mark.configure def test_pep517_wheel_incexl(tmp_path, monkeypatch, virtualenv): dist = tmp_path / "dist" dist.mkdir() monkeypatch.chdir(SIMPLEST) out = build_wheel( str(dist), { "sdist.include": "src/simplest/*included*.txt", "sdist.exclude": "src/simplest/*excluded*.txt", "wheel.exclude": [ "simplest/sdist_only.txt", "simplest/generated_no_wheel.txt", ], "wheel.packages": ["src/simplest", "src/not_a_package"], }, ) (wheel,) = dist.glob("simplest-0.0.1-*.whl") assert wheel == dist / out virtualenv.install(wheel) with zipfile.ZipFile(wheel) as zf: file_paths = {Path(n) for n in zf.namelist()} file_names = {p.parts[0] for p in file_paths} simplest_pkg = {x.name for x in contained(file_paths, "simplest")} not_a_pkg = {x.name for x in contained(file_paths, "not_a_package")} metadata_items = set(contained(file_paths, "simplest-0.0.1.dist-info")) assert { Path("licenses/LICENSE.txt"), Path("metadata_file.txt"), Path("RECORD"), Path("METADATA"), Path("WHEEL"), } == metadata_items filtered_pkg = {x for x in simplest_pkg if not x.startswith("_module")} assert len(filtered_pkg) == len(simplest_pkg) - 2 assert {"simplest-0.0.1.dist-info", "simplest", "not_a_package"} == file_names assert { "__init__.py", "data.txt", "ignored_included.txt", "generated.txt", "generated_ignored.txt", } == filtered_pkg assert {"simple.txt"} == not_a_pkg version = virtualenv.execute( "from simplest import square; print(square(2))", ) assert version == "4.0" scikit-build-core-0.11.1/tests/test_skbuild_settings.py000066400000000000000000000667041477275177200232730ustar00rootroot00000000000000# Comparison with empty strings okay for symmetry # ruff: noqa: PLC1901 from __future__ import annotations import re import textwrap from pathlib import Path import pytest from packaging.specifiers import SpecifierSet from packaging.version import Version import scikit_build_core._logging import scikit_build_core.settings.skbuild_read_settings from scikit_build_core.settings.skbuild_model import GenerateSettings from scikit_build_core.settings.skbuild_read_settings import SettingsReader def test_skbuild_settings_default(tmp_path: Path): pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text("", encoding="utf-8") config_settings: dict[str, list[str] | str] = {} settings_reader = SettingsReader.from_file(pyproject_toml, config_settings) settings = settings_reader.settings assert list(settings_reader.unrecognized_options()) == [] assert settings.ninja.minimum_version is None assert settings.ninja.version == SpecifierSet(">=1.5") assert settings.ninja.make_fallback assert settings.cmake.minimum_version is None assert settings.cmake.version == SpecifierSet(">=3.15") assert settings.cmake.args == [] assert settings.cmake.define == {} assert not settings.build.verbose assert settings.cmake.build_type == "Release" assert settings.cmake.source_dir == Path() assert settings.build.targets == [] assert settings.logging.level == "WARNING" assert settings.sdist.include == [] assert settings.sdist.exclude == [] assert settings.sdist.reproducible assert not settings.sdist.cmake assert settings.wheel.packages is None assert settings.wheel.py_api == "" assert not settings.wheel.expand_macos_universal_tags assert settings.wheel.license_files is None assert settings.wheel.exclude == [] assert settings.wheel.build_tag == "" assert settings.backport.find_python == Version("3.26.1") assert settings.strict_config assert not settings.experimental assert settings.minimum_version is None assert settings.build_dir == "" assert settings.metadata == {} assert settings.editable.mode == "redirect" assert not settings.editable.rebuild assert settings.editable.verbose assert settings.build.tool_args == [] assert settings.install.components == [] assert settings.install.strip assert settings.generate == [] assert not settings.fail assert settings.messages.after_failure == "" assert settings.messages.after_success == "" def test_skbuild_settings_envvar(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): monkeypatch.setattr( scikit_build_core.settings.skbuild_read_settings, "__version__", "0.10.0" ) monkeypatch.setenv("SKBUILD_NINJA_VERSION", ">=1.1") monkeypatch.setenv("SKBUILD_NINJA_MAKE_FALLBACK", "0") monkeypatch.setenv("SKBUILD_CMAKE_VERSION", ">=3.16") monkeypatch.setenv("SKBUILD_CMAKE_ARGS", "-DFOO=BAR;-DBAR=FOO") monkeypatch.setenv("SKBUILD_CMAKE_DEFINE", "a=1;b=2") monkeypatch.setenv("SKBUILD_CMAKE_BUILD_TYPE", "Debug") monkeypatch.setenv("SKBUILD_CMAKE_SOURCE_DIR", "a/b/c") monkeypatch.setenv("SKBUILD_LOGGING_LEVEL", "DEBUG") monkeypatch.setenv("SKBUILD_SDIST_INCLUDE", "a;b; c") monkeypatch.setenv("SKBUILD_SDIST_EXCLUDE", "d;e;f") monkeypatch.setenv("SKBUILD_SDIST_REPRODUCIBLE", "OFF") monkeypatch.setenv("SKBUILD_SDIST_CMAKE", "ON") monkeypatch.setenv("SKBUILD_WHEEL_PACKAGES", "j; k; l") monkeypatch.setenv("SKBUILD_WHEEL_PY_API", "cp39") monkeypatch.setenv("SKBUILD_WHEEL_EXPAND_MACOS_UNIVERSAL_TAGS", "True") monkeypatch.setenv("SKBUILD_WHEEL_LICENSE_FILES", "a;b;c") monkeypatch.setenv("SKBUILD_WHEEL_EXCLUDE", "b;y;e") monkeypatch.setenv("SKBUILD_WHEEL_BUILD_TAG", "1") monkeypatch.setenv("SKBUILD_BACKPORT_FIND_PYTHON", "0") monkeypatch.setenv("SKBUILD_STRICT_CONFIG", "0") monkeypatch.setenv("SKBUILD_EXPERIMENTAL", "1") monkeypatch.setenv("SKBUILD_MINIMUM_VERSION", "0.10") monkeypatch.setenv("SKBUILD_BUILD_DIR", "a/b/c") monkeypatch.setenv("SKBUILD_EDITABLE_REBUILD", "True") monkeypatch.setenv("SKBUILD_EDITABLE_VERBOSE", "False") monkeypatch.setenv("SKBUILD_BUILD_VERBOSE", "TRUE") monkeypatch.setenv("SKBUILD_BUILD_TARGETS", "a;b;c") monkeypatch.setenv("SKBUILD_BUILD_TOOL_ARGS", "a;b") monkeypatch.setenv("SKBUILD_INSTALL_COMPONENTS", "a;b;c") monkeypatch.setenv("SKBUILD_INSTALL_STRIP", "False") monkeypatch.setenv("SKBUILD_FAIL", "1") monkeypatch.setenv( "SKBUILD_MESSAGES_AFTER_FAILURE", "This is a test failure message" ) monkeypatch.setenv( "SKBUILD_MESSAGES_AFTER_SUCCESS", "This is a test success message" ) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text("", encoding="utf-8") config_settings: dict[str, list[str] | str] = {} settings_reader = SettingsReader.from_file(pyproject_toml, config_settings) settings = settings_reader.settings assert list(settings_reader.unrecognized_options()) == [] assert settings.ninja.version == SpecifierSet(">=1.1") assert settings.cmake.version == SpecifierSet(">=3.16") assert settings.cmake.args == ["-DFOO=BAR", "-DBAR=FOO"] assert settings.cmake.define == {"a": "1", "b": "2"} assert settings.cmake.build_type == "Debug" assert settings.cmake.source_dir == Path("a/b/c") assert not settings.ninja.make_fallback assert settings.logging.level == "DEBUG" assert settings.sdist.include == ["a", "b", "c"] assert settings.sdist.exclude == ["d", "e", "f"] assert not settings.sdist.reproducible assert settings.sdist.cmake assert settings.wheel.packages == ["j", "k", "l"] assert settings.wheel.py_api == "cp39" assert settings.wheel.expand_macos_universal_tags assert settings.wheel.license_files == ["a", "b", "c"] assert settings.wheel.exclude == ["b", "y", "e"] assert settings.wheel.build_tag == "1" assert settings.backport.find_python == Version("0") assert not settings.strict_config assert settings.experimental assert settings.minimum_version == Version("0.10") assert settings.build_dir == "a/b/c" assert settings.metadata == {} assert settings.editable.mode == "redirect" assert settings.editable.rebuild assert not settings.editable.verbose assert settings.build.verbose assert settings.build.targets == ["a", "b", "c"] assert settings.build.tool_args == ["a", "b"] assert settings.install.components == ["a", "b", "c"] assert not settings.install.strip assert settings.fail assert settings.messages.after_failure == "This is a test failure message" assert settings.messages.after_success == "This is a test success message" @pytest.mark.parametrize("prefix", [True, False], ids=["skbuild", "noprefix"]) def test_skbuild_settings_config_settings( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, prefix: bool ): monkeypatch.setattr( scikit_build_core.settings.skbuild_read_settings, "__version__", "0.10.0" ) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text("", encoding="utf-8") config_settings: dict[str, str | list[str]] = { "ninja.version": ">=1.2", "ninja.make-fallback": "False", "cmake.version": ">=3.17", "cmake.args": ["-DFOO=BAR", "-DBAR=FOO"], "cmake.define.a": "1", "cmake.define.b": "2", "cmake.build-type": "Debug", "cmake.source-dir": "a/b/c", "logging.level": "INFO", "sdist.include": ["a", "b", "c"], "sdist.exclude": "d;e;f", "sdist.reproducible": "false", "sdist.cmake": "true", "wheel.packages": ["j", "k", "l"], "wheel.py-api": "cp39", "wheel.expand-macos-universal-tags": "True", "wheel.license-files": ["a", "b", "c"], "wheel.exclude": ["b", "y", "e"], "wheel.build-tag": "1foo", "backport.find-python": "0", "strict-config": "false", "experimental": "1", "minimum-version": "0.10", "build-dir": "a/b/c", "editable.mode": "redirect", "editable.rebuild": "True", "editable.verbose": "False", "build.verbose": "true", "build.targets": ["a", "b", "c"], "build.tool-args": ["a", "b"], "install.components": ["a", "b", "c"], "install.strip": "True", "fail": "1", "messages.after-failure": "This is a test failure message", "messages.after-success": "This is a test success message", } if prefix: config_settings = {f"skbuild.{k}": v for k, v in config_settings.items()} settings_reader = SettingsReader.from_file(pyproject_toml, config_settings) settings = settings_reader.settings assert list(settings_reader.unrecognized_options()) == [] assert settings.ninja.version == SpecifierSet(">=1.2") assert not settings.ninja.make_fallback assert settings.cmake.version == SpecifierSet(">=3.17") assert settings.cmake.args == ["-DFOO=BAR", "-DBAR=FOO"] assert settings.cmake.define == {"a": "1", "b": "2"} assert settings.build.verbose assert settings.cmake.build_type == "Debug" assert settings.cmake.source_dir == Path("a/b/c") assert settings.logging.level == "INFO" assert settings.sdist.include == ["a", "b", "c"] assert settings.sdist.exclude == ["d", "e", "f"] assert not settings.sdist.reproducible assert settings.sdist.cmake assert settings.wheel.packages == ["j", "k", "l"] assert settings.wheel.py_api == "cp39" assert settings.wheel.expand_macos_universal_tags assert settings.wheel.license_files == ["a", "b", "c"] assert settings.wheel.exclude == ["b", "y", "e"] assert settings.wheel.build_tag == "1foo" assert settings.backport.find_python == Version("0") assert not settings.strict_config assert settings.experimental assert settings.minimum_version == Version("0.10") assert settings.build_dir == "a/b/c" assert settings.metadata == {} assert settings.editable.mode == "redirect" assert settings.editable.rebuild assert not settings.editable.verbose assert settings.build.targets == ["a", "b", "c"] assert settings.build.tool_args == ["a", "b"] assert settings.install.components == ["a", "b", "c"] assert settings.install.strip assert settings.fail assert settings.messages.after_failure == "This is a test failure message" assert settings.messages.after_success == "This is a test success message" def test_skbuild_settings_pyproject_toml( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ): monkeypatch.setattr( scikit_build_core.settings.skbuild_read_settings, "__version__", "0.10.0" ) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( textwrap.dedent( """\ [tool.scikit-build] ninja.version = ">=1.3" ninja.make-fallback = false cmake.version = ">=3.18" cmake.args = ["-DFOO=BAR", "-DBAR=FOO"] cmake.define = {a = "1", b = "2"} cmake.build-type = "Debug" cmake.source-dir = "a/b/c" logging.level = "ERROR" sdist.include = ["a", "b", "c"] sdist.exclude = ["d", "e", "f"] sdist.reproducible = false sdist.cmake = true wheel.packages = ["j", "k", "l"] wheel.py-api = "cp39" wheel.expand-macos-universal-tags = true wheel.license-files = ["a", "b", "c"] wheel.exclude = ["b", "y", "e"] wheel.build-tag = "1_bar" backport.find-python = "3.18" strict-config = false experimental = true minimum-version = "0.10" build-dir = "a/b/c" metadata.version.provider = "a" editable.mode = "redirect" editable.rebuild = true editable.verbose = false build.verbose = true build.targets = ["a", "b", "c"] build.tool-args = ["a", "b"] install.components = ["a", "b", "c"] install.strip = true fail = true messages.after-failure = "This is a test failure message" messages.after-success = "This is a test success message" [[tool.scikit-build.generate]] path = "a/b/c" template = "hello" [[tool.scikit-build.generate]] path = "d/e/f" template-path = "g/h/i" location = "build" """ ), encoding="utf-8", ) config_settings: dict[str, list[str] | str] = {} settings_reader = SettingsReader.from_file(pyproject_toml, config_settings) settings = settings_reader.settings assert list(settings_reader.unrecognized_options()) == [] assert settings.ninja.version == SpecifierSet(">=1.3") assert not settings.ninja.make_fallback assert settings.cmake.version == SpecifierSet(">=3.18") assert settings.cmake.args == ["-DFOO=BAR", "-DBAR=FOO"] assert settings.cmake.define == {"a": "1", "b": "2"} assert settings.cmake.build_type == "Debug" assert settings.cmake.source_dir == Path("a/b/c") assert settings.logging.level == "ERROR" assert settings.sdist.include == ["a", "b", "c"] assert settings.sdist.exclude == ["d", "e", "f"] assert not settings.sdist.reproducible assert settings.sdist.cmake assert settings.wheel.packages == ["j", "k", "l"] assert settings.wheel.py_api == "cp39" assert settings.wheel.expand_macos_universal_tags assert settings.wheel.license_files == ["a", "b", "c"] assert settings.wheel.exclude == ["b", "y", "e"] assert settings.wheel.build_tag == "1_bar" assert settings.backport.find_python == Version("3.18") assert not settings.strict_config assert settings.experimental assert settings.minimum_version == Version("0.10") assert settings.build_dir == "a/b/c" assert settings.metadata == {"version": {"provider": "a"}} assert settings.editable.mode == "redirect" assert settings.editable.rebuild assert not settings.editable.verbose assert settings.build.verbose assert settings.build.targets == ["a", "b", "c"] assert settings.build.tool_args == ["a", "b"] assert settings.install.components == ["a", "b", "c"] assert settings.install.strip assert settings.generate == [ GenerateSettings(path=Path("a/b/c"), template="hello", location="install"), GenerateSettings( path=Path("d/e/f"), template_path=Path("g/h/i"), location="build" ), ] assert settings.fail assert settings.messages.after_failure == "This is a test failure message" assert settings.messages.after_success == "This is a test success message" def test_skbuild_settings_pyproject_toml_broken( tmp_path: Path, capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch ): monkeypatch.setattr( scikit_build_core.settings.skbuild_read_settings, "__version__", "0.9.0" ) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( textwrap.dedent( """\ [tool.scikit-build] minimum-version = "0.9" cmake.verison = ">=3.18" ninja.version = ">=1.3" ninja.make-fallback = false logger.level = "ERROR" """ ), encoding="utf-8", ) config_settings: dict[str, list[str] | str] = {} settings_reader = SettingsReader.from_file(pyproject_toml, config_settings) assert list(settings_reader.unrecognized_options()) == [ "tool.scikit-build.cmake.verison", "tool.scikit-build.logger", ] with pytest.raises(SystemExit): settings_reader.validate_may_exit() ex = capsys.readouterr().out ex = re.sub(r"\x1b(\[.*?[@-~]|\].*?(\x07|\x1b\\))", "", ex) assert ex.split() == [ "ERROR:", "Unrecognized", "options", "in", "pyproject.toml:", "tool.scikit-build.cmake.verison", "->", "Did", "you", "mean:", "tool.scikit-build.cmake.version,", "tool.scikit-build.cmake.verbose,", "tool.scikit-build.cmake.define?", "tool.scikit-build.logger", "->", "Did", "you", "mean:", "tool.scikit-build.logging,", "tool.scikit-build.generate,", "tool.scikit-build.search?", ] def test_skbuild_settings_pyproject_conf_broken( tmp_path: Path, capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch ): monkeypatch.setattr( scikit_build_core.settings.skbuild_read_settings, "__version__", "0.9.0" ) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( "tool.scikit-build.minimum-version = '0.9'", encoding="utf-8" ) config_settings: dict[str, str | list[str]] = { "cmake.verison": ">=3.17", "ninja.version": ">=1.2", "ninja.make-fallback": "False", "logger.level": "INFO", } settings_reader = SettingsReader.from_file(pyproject_toml, config_settings) assert list(settings_reader.unrecognized_options()) == [ "cmake.verison", "logger", ] with pytest.raises(SystemExit): settings_reader.validate_may_exit() ex = capsys.readouterr().out # Filter terminal color codes ex = re.sub(r"\x1b(\[.*?[@-~]|\].*?(\x07|\x1b\\))", "", ex) assert ex.split() == [ "ERROR:", "Unrecognized", "options", "in", "config-settings:", "cmake.verison", "->", "Did", "you", "mean:", "cmake.version,", "cmake.verbose,", "cmake.define?", "logger", "->", "Did", "you", "mean:", "logging?", ] def test_skbuild_settings_min_version_defaults_strip( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ): monkeypatch.setattr( scikit_build_core.settings.skbuild_read_settings, "__version__", "0.5.0" ) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text("", encoding="utf-8") settings_reader = SettingsReader.from_file( pyproject_toml, {"minimum-version": "0.4"} ) settings = settings_reader.settings assert not settings.install.strip settings_reader = SettingsReader.from_file( pyproject_toml, {"minimum-version": "0.5"} ) settings = settings_reader.settings assert settings.install.strip def test_skbuild_settings_min_version_versions( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ): monkeypatch.setattr( scikit_build_core.settings.skbuild_read_settings, "__version__", "0.10.0" ) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( "tool.scikit-build.cmake.minimum-version = '3.21'", encoding="utf-8" ) cmakelists = tmp_path / "CMakeLists.txt" cmakelists.write_text("cmake_minimum_required(VERSION 3.20)", encoding="utf-8") settings_reader = SettingsReader.from_file(pyproject_toml, {}) settings = settings_reader.settings assert settings.cmake.version == SpecifierSet(">=3.21") settings_reader = SettingsReader.from_file( pyproject_toml, {"minimum-version": "0.7"} ) settings = settings_reader.settings assert settings.cmake.version == SpecifierSet(">=3.21") with pytest.raises(SystemExit): SettingsReader.from_file(pyproject_toml, {"minimum-version": "0.8"}) def test_skbuild_settings_version_too_old( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ): monkeypatch.setattr( scikit_build_core.settings.skbuild_read_settings, "__version__", "0.8.0" ) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( "tool.scikit-build.cmake.version = '>=3.21'", encoding="utf-8" ) SettingsReader.from_file(pyproject_toml) with pytest.raises(SystemExit): SettingsReader.from_file(pyproject_toml, {"minimum-version": "0.7"}) def test_skbuild_settings_pyproject_toml_envvar_defines( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ): pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( textwrap.dedent( """\ [tool.scikit-build.cmake.define] a = "1" b = {env = "SIMPLE"} c = {env = "DEFAULT", default="empty"} d = false e = {env = "BOOL", default = false} f = {env = "NOTSET"} """ ), encoding="utf-8", ) config_settings: dict[str, list[str] | str] = {} monkeypatch.setenv("SIMPLE", "2") settings_reader = SettingsReader.from_file(pyproject_toml, config_settings) assert settings_reader.settings.cmake.define == { "a": "1", "b": "2", "c": "empty", "d": "FALSE", "e": "FALSE", } monkeypatch.setenv("DEFAULT", "3") monkeypatch.setenv("BOOL", "ON") settings_reader = SettingsReader.from_file(pyproject_toml, config_settings) assert settings_reader.settings.cmake.define == { "a": "1", "b": "2", "c": "3", "d": "FALSE", "e": "TRUE", } def test_backcompat_cmake_build(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): monkeypatch.setattr( scikit_build_core.settings.skbuild_read_settings, "__version__", "0.10.0" ) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( textwrap.dedent( """\ [tool.scikit-build] minimum-version = "0.9" cmake.verbose = true cmake.targets = ["a", "b"] """ ), encoding="utf-8", ) settings_reader = SettingsReader.from_file(pyproject_toml, {}) assert settings_reader.settings.build.verbose assert settings_reader.settings.build.targets == ["a", "b"] def test_backcompat_cmake_build_env(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): monkeypatch.setattr( scikit_build_core.settings.skbuild_read_settings, "__version__", "0.9.0" ) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( textwrap.dedent( """\ [tool.scikit-build] minimum-version = "0.9" """ ), encoding="utf-8", ) monkeypatch.setenv("SKBUILD_CMAKE_VERBOSE", "ON") monkeypatch.setenv("SKBUILD_CMAKE_TARGETS", "a;b") settings_reader = SettingsReader.from_file(pyproject_toml, {}) assert settings_reader.settings.build.verbose assert settings_reader.settings.build.targets == ["a", "b"] def test_backcompat_cmake_build_both_specified( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ): monkeypatch.setattr( scikit_build_core.settings.skbuild_read_settings, "__version__", "0.10.0" ) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( textwrap.dedent( """\ [tool.scikit-build] cmake.verbose = true """ ), encoding="utf-8", ) with pytest.raises(SystemExit): SettingsReader.from_file(pyproject_toml, {"build.verbose": "1"}) def test_auto_minimum_version(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): monkeypatch.setattr( scikit_build_core.settings.skbuild_read_settings, "__version__", "0.10.0" ) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( textwrap.dedent( """\ [build-system] requires = ["scikit-build-core>=0.8"] [tool.scikit-build] minimum-version = "build-system.requires" """ ), encoding="utf-8", ) reader = SettingsReader.from_file(pyproject_toml, {}) assert reader.settings.minimum_version == Version("0.8") def test_auto_cmake_version(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( textwrap.dedent( """\ [build-system] requires = ["scikit-build-core>=0.8"] [tool.scikit-build] cmake.version = "CMakeLists.txt" """ ), encoding="utf-8", ) cmakelists_txt = tmp_path / "CMakeLists.txt" cmakelists_txt.write_text( textwrap.dedent( """\ cmake_minimum_required(VERSION 3.21) """ ), encoding="utf-8", ) monkeypatch.chdir(tmp_path) reader = SettingsReader.from_file(pyproject_toml, {}) assert reader.settings.cmake.version == SpecifierSet(">=3.21") @pytest.mark.parametrize("version", ["0.9", "0.10"]) def test_default_auto_cmake_version( version: str, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ): monkeypatch.setattr( scikit_build_core.settings.skbuild_read_settings, "__version__", "0.10.0" ) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( textwrap.dedent( f"""\ [build-system] requires = ["scikit-build-core>={version}"] [tool.scikit-build] minimum-version = "build-system.requires" """ ), encoding="utf-8", ) cmakelists_txt = tmp_path / "CMakeLists.txt" cmakelists_txt.write_text( textwrap.dedent( """\ cmake_minimum_required(VERSION 3.21) """ ), encoding="utf-8", ) monkeypatch.chdir(tmp_path) reader = SettingsReader.from_file(pyproject_toml, {}) assert reader.settings.cmake.version == SpecifierSet( ">=3.21" if version == "0.10" else ">=3.15" ) def test_skbuild_settings_auto_cmake_warning( tmp_path: Path, capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch ): monkeypatch.setattr( scikit_build_core.settings.skbuild_read_settings, "__version__", "0.10.0" ) scikit_build_core._logging.rich_warning.cache_clear() pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( textwrap.dedent( """\ [tool.scikit-build] minimum-version = "0.10" """ ), encoding="utf-8", ) cmakelists_txt = tmp_path / "CMakeLists.txt" cmakelists_txt.write_text( textwrap.dedent( """\ cmake_minimum_required(VERSION 3.14) """ ), encoding="utf-8", ) config_settings: dict[str, list[str] | str] = {} settings_reader = SettingsReader.from_file(pyproject_toml, config_settings) assert settings_reader.settings.cmake.version == SpecifierSet(">=3.15") ex = capsys.readouterr().out ex = re.sub(r"\x1b(\[.*?[@-~]|\].*?(\x07|\x1b\\))", "", ex) print(ex) assert ex.split() == [ "WARNING:", "CMakeLists.txt", "not", "found", "when", "looking", "for", "minimum", "CMake", "version.", "Report", "this", "or", "(and)", "set", "manually", "to", "avoid", "this", "warning.", "Using", "3.15", "as", "a", "fall-back.", ] def test_skbuild_settings_cmake_define_list(): pyproject_toml = ( Path(__file__).parent / "packages" / "cmake_defines" / "pyproject.toml" ) config_settings: dict[str, list[str] | str] = {} settings_reader = SettingsReader.from_file(pyproject_toml, config_settings) settings = settings_reader.settings assert settings.cmake.define == { "NESTED_LIST": r"Apple;Lemon\;Lime;Banana", "ONE_LEVEL_LIST": "Foo;Bar;ExceptionallyLargeListEntryThatWouldOverflowTheLine;Baz", } scikit-build-core-0.11.1/tests/test_wheelfile_utils.py000066400000000000000000000045301477275177200230670ustar00rootroot00000000000000import stat import zipfile from packaging.tags import Tag import scikit_build_core.build._wheelfile from scikit_build_core._vendor.pyproject_metadata import StandardMetadata def test_wheel_metadata() -> None: metadata = scikit_build_core.build._wheelfile.WheelMetadata( generator="scikit-build-core 1.2.3" ) assert ( metadata.as_bytes() == b"Wheel-Version: 1.0\nGenerator: scikit-build-core 1.2.3\nRoot-Is-Purelib: false\n\n" ) def test_wheel_writer_simple(tmp_path, monkeypatch): metadata = StandardMetadata.from_pyproject( { "project": { "name": "something", "version": "1.2.3", }, }, metadata_version="2.3", ) out_dir = tmp_path / "out" wheel = scikit_build_core.build._wheelfile.WheelWriter( metadata, out_dir, {Tag("py3", "none", "any")}, scikit_build_core.build._wheelfile.WheelMetadata(), None, ) wheel.wheel_metadata.generator = "scikit-build-core 1.2.3" monkeypatch.setenv("SOURCE_DATE_EPOCH", "315532800") assert wheel.timestamp() == (1980, 1, 1, 0, 0, 0) assert wheel.name_ver == "something-1.2.3" assert wheel.wheelpath.name == "something-1.2.3-py3-none-any.whl" assert wheel.basename == "something-1.2.3-py3-none-any" dist_info = wheel.dist_info_contents() assert dist_info == { "METADATA": b"Metadata-Version: 2.3\nName: something\nVersion: 1.2.3\n\n", "WHEEL": b"Wheel-Version: 1.0\nGenerator: scikit-build-core 1.2.3\nRoot-Is-Purelib: false\nTag: py3-none-any\n\n", } platlib = tmp_path / "platlib" with wheel: wheel.build({"platlib": platlib}) assert (out_dir / "something-1.2.3-py3-none-any.whl").exists() with zipfile.ZipFile(out_dir / "something-1.2.3-py3-none-any.whl") as zf: assert zf.namelist() == [ "something-1.2.3.dist-info/METADATA", "something-1.2.3.dist-info/WHEEL", "something-1.2.3.dist-info/RECORD", ] assert zf.read("something-1.2.3.dist-info/METADATA") == dist_info["METADATA"] assert zf.read("something-1.2.3.dist-info/WHEEL") == dist_info["WHEEL"] for info in zf.infolist(): assert info.external_attr == (0o664 | stat.S_IFREG) << 16 assert info.compress_type == zipfile.ZIP_DEFLATED scikit-build-core-0.11.1/tests/utils/000077500000000000000000000000001477275177200174305ustar00rootroot00000000000000scikit-build-core-0.11.1/tests/utils/pathutils.py000066400000000000000000000005471477275177200220250ustar00rootroot00000000000000from __future__ import annotations import contextlib import typing if typing.TYPE_CHECKING: from collections.abc import Generator, Iterable from pathlib import Path def contained(paths: Iterable[Path], rel: str) -> Generator[Path, None, None]: for p in paths: with contextlib.suppress(ValueError): yield p.relative_to(rel)