pax_global_header00006660000000000000000000000064146636643550014534gustar00rootroot0000000000000052 comment=9e20ec612893541d253674e688269e2495be0a12 scikit-build-0.18.1/000077500000000000000000000000001466366435500142065ustar00rootroot00000000000000scikit-build-0.18.1/.distro/000077500000000000000000000000001466366435500155705ustar00rootroot00000000000000scikit-build-0.18.1/.distro/.fmf/000077500000000000000000000000001466366435500164165ustar00rootroot00000000000000scikit-build-0.18.1/.distro/.fmf/version000066400000000000000000000000021466366435500200160ustar00rootroot000000000000001 scikit-build-0.18.1/.distro/plans/000077500000000000000000000000001466366435500167055ustar00rootroot00000000000000scikit-build-0.18.1/.distro/plans/main.fmf.dist-git000066400000000000000000000000551466366435500220460ustar00rootroot00000000000000discover: how: fmf dist-git-source: true scikit-build-0.18.1/.distro/plans/rpminspect.fmf000066400000000000000000000005751466366435500215720ustar00rootroot00000000000000plan: import: url: https://github.com/packit/tmt-plans ref: main name: /plans/rpminspect environment: # upstream is excluded here because it triggers "Unexpected changed source archive content" # This happens when the released version already contains the package version: # https://github.com/packit/tmt-plans/issues/13 RPMINSPECT_EXCLUDE: metadata,upstream scikit-build-0.18.1/.distro/plans/rpmlint.fmf000066400000000000000000000002761466366435500210710ustar00rootroot00000000000000prepare: - how: shell script: cp ./*.rpmlintrc $TMT_PLAN_DATA/ discover: how: fmf filter: "tag: rpmlint" url: https://github.com/packit/tmt-plans ref: main execute: how: tmt scikit-build-0.18.1/.distro/plans/smoke.fmf000066400000000000000000000001401466366435500205100ustar00rootroot00000000000000summary: Basic smoke tests discover+: how: fmf filter: "tag: smoke" execute: how: tmt scikit-build-0.18.1/.distro/python-scikit-build.rpmlintrc000066400000000000000000000000001466366435500234140ustar00rootroot00000000000000scikit-build-0.18.1/.distro/python-scikit-build.spec000066400000000000000000000045301466366435500223500ustar00rootroot00000000000000%global debug_package %{nil} Name: python-scikit-build Version: 0.0.0 Release: %autorelease Summary: Improved build system generator for Python C/C++/Fortran/Cython extensions # This project is mainly MIT but LICENSE also mentions some code # that is BSD-2-Clause-Views licensed. # All bundled(cmake()) files listed are Apache-2.0 licensed. License: MIT AND BSD-2-Clause-Views AND Apache-2.0 URL: https://github.com/scikit-build/scikit-build Source: %{pypi_source scikit_build} BuildRequires: python3-devel # For tests: BuildRequires: cmake BuildRequires: gcc BuildRequires: gcc-c++ BuildRequires: gcc-gfortran BuildRequires: git-core BuildRequires: ninja-build %global _description %{expand: Improved build system generator for CPython C/C++/Fortran/Cython extensions. Better support is available for additional compilers, build systems, cross compilation, and locating dependencies and determining their build requirements. The scikit-build package is fundamentally just glue between the setup-tools Python module and CMake.} %description %_description %package -n python3-scikit-build Summary: %{summary} Requires: cmake Requires: ninja-build BuildArch: noarch # Files listed below are located in skbuild/resources/cmake. # Since they contain "Copyright 2011 Kitware, Inc." in them we list them as bundled, # their versions are unknown. # There is no such copyright in the remaining files so we assume # they are original part of the project. Provides: bundled(cmake(FindCython)) Provides: bundled(cmake(FindPythonExtensions)) Provides: bundled(cmake(UseCython)) Provides: bundled(cmake(UseF2PY)) Provides: bundled(cmake(UsePythonExtensions)) %description -n python3-scikit-build %_description %prep %autosetup -p1 -n scikit_build-%{version} %generate_buildrequires %pyproject_buildrequires -x test %build %pyproject_wheel %install %pyproject_install %pyproject_save_files skbuild %check # Some tests have assumptions that don't work if the RPM build flags are set, # so we clean them. export CFLAGS=' ' export CXXFLAGS=' ' # isolated tests are disabled because they require internet %pytest -v -m "not isolated and not deprecated and not nosetuptoolsscm" %files -n python3-scikit-build -f %{pyproject_files} %doc README.* %changelog %autochangelog scikit-build-0.18.1/.distro/tests/000077500000000000000000000000001466366435500167325ustar00rootroot00000000000000scikit-build-0.18.1/.distro/tests/smoke.fmf000066400000000000000000000002211466366435500205350ustar00rootroot00000000000000# Common test variables tag: - smoke tier: 0 path: / # Define tests /version: test: python3 -c "import skbuild; print(skbuild.__version__)" scikit-build-0.18.1/.git-blame-ignore-revs000066400000000000000000000001771466366435500203130ustar00rootroot00000000000000# style: black double quotes e02c9b391d54dedd7cf1a5c4177bc15436ab4a28 # style: black 321a615d3f5dcd5e423f366ae748348eb3ea216c scikit-build-0.18.1/.git_archival.txt000066400000000000000000000002171466366435500174610ustar00rootroot00000000000000node: 9e20ec612893541d253674e688269e2495be0a12 node-date: 2024-08-28T14:14:05-04:00 describe-name: 0.18.1 ref-names: HEAD -> main, tag: 0.18.1 scikit-build-0.18.1/.gitattributes000066400000000000000000000010021466366435500170720ustar00rootroot00000000000000# Auto detect text files and perform LF normalization * text=auto # Custom for Visual Studio *.cs diff=csharp *.sln merge=union *.csproj merge=union *.vbproj merge=union *.fsproj merge=union *.dbproj merge=union # Standard to msysgit *.doc diff=astextplain *.DOC diff=astextplain *.docx diff=astextplain *.DOCX diff=astextplain *.dot diff=astextplain *.DOT diff=astextplain *.pdf diff=astextplain *.PDF diff=astextplain *.rtf diff=astextplain *.RTF diff=astextplain .git_archival.txt export-subst scikit-build-0.18.1/.github/000077500000000000000000000000001466366435500155465ustar00rootroot00000000000000scikit-build-0.18.1/.github/codecov.yml000066400000000000000000000003561466366435500177170ustar00rootroot00000000000000codecov: notify: after_n_builds: 8 coverage: status: project: default: # Don't allow overall project coverage to be dropped more than 2% threshold: 2 patch: default: informational: true scikit-build-0.18.1/.github/dependabot.yml000066400000000000000000000003401466366435500203730ustar00rootroot00000000000000version: 2 updates: # Maintain dependencies for GitHub Actions - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" groups: actions: patterns: - "*" scikit-build-0.18.1/.github/release.yml000066400000000000000000000001141466366435500177050ustar00rootroot00000000000000changelog: exclude: authors: - dependabot - pre-commit-ci scikit-build-0.18.1/.github/workflows/000077500000000000000000000000001466366435500176035ustar00rootroot00000000000000scikit-build-0.18.1/.github/workflows/cd.yml000066400000000000000000000013551466366435500207200ustar00rootroot00000000000000name: CD on: workflow_dispatch: release: types: - published jobs: dist: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Build SDist and wheel run: pipx run build - uses: actions/upload-artifact@v4 with: path: dist/* - name: Check metadata run: pipx run twine check dist/* publish: needs: [dist] environment: pypi permissions: id-token: write runs-on: ubuntu-latest if: github.event_name == 'release' && github.event.action == 'published' steps: - uses: actions/download-artifact@v4 with: name: artifact path: dist - uses: pypa/gh-action-pypi-publish@release/v1 scikit-build-0.18.1/.github/workflows/ci.yml000066400000000000000000000126101466366435500207210ustar00rootroot00000000000000name: CI on: pull_request: push: branches: - main concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true env: FORCE_COLOR: "3" jobs: lint: name: Lint runs-on: ubuntu-latest timeout-minutes: 5 steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 - uses: pre-commit/action@v3.0.1 tests: runs-on: ${{ matrix.runs-on }} needs: [lint] timeout-minutes: 60 strategy: fail-fast: false matrix: runs-on: [ubuntu-22.04, macos-13, windows-2019, windows-2022] name: Tests on ${{ matrix.runs-on }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: yezz123/setup-uv@v4 - name: Set environment for available GHA MSVCs if: matrix.runs-on == 'windows-2019' shell: bash run: | echo "SKBUILD_TEST_FIND_VS2017_INSTALLATION_EXPECTED=0" >> $GITHUB_ENV echo "SKBUILD_TEST_FIND_VS2019_INSTALLATION_EXPECTED=1" >> $GITHUB_ENV echo "SKBUILD_TEST_FIND_VS2022_INSTALLATION_EXPECTED=0" >> $GITHUB_ENV - name: Set environment for available GHA MSVCs if: matrix.runs-on == 'windows-2022' shell: bash run: | echo "SKBUILD_TEST_FIND_VS2017_INSTALLATION_EXPECTED=0" >> $GITHUB_ENV echo "SKBUILD_TEST_FIND_VS2019_INSTALLATION_EXPECTED=0" >> $GITHUB_ENV echo "SKBUILD_TEST_FIND_VS2022_INSTALLATION_EXPECTED=1" >> $GITHUB_ENV - name: Set environment for Fortran compiler on MacOS if: runner.os == 'macOS' run: | brew reinstall gfortran echo "FC=gfortran" >> $GITHUB_ENV - name: Setup nox uses: wntrblm/nox@2024.04.15 with: python-versions: "3.7,3.8,3.9,3.10,3.11,3.12,3.13" # We check all Python's on Linux, because it's fast. # We check minimum Python and maximum Python on all OS's. - name: Test on 🐍 3.7 run: > nox --default-venv-backend=${{ startsWith(matrix.runs-on, 'windows') && 'virtualenv' || 'uv'}} -s tests-3.7 -- --cov --cov-report=xml --cov-report=term --durations=20 - name: Test on 🐍 3.8 if: runner.os == 'Linux' run: nox -s tests-3.8 -- --cov --cov-report=xml --cov-report=term --cov-append --durations=20 - name: Test on 🐍 3.9 if: runner.os == 'Linux' run: nox -s tests-3.9 -- --cov --cov-report=xml --cov-report=term --cov-append --durations=20 - name: Test on 🐍 3.10 if: runner.os == 'Linux' run: nox -s tests-3.10 -- --cov --cov-report=xml --cov-report=term --cov-append --durations=20 - name: Test on 🐍 3.11 run: nox -s tests-3.11 -- --cov --cov-report=xml --cov-report=term --cov-append --durations=20 - name: Test on 🐍 3.12 run: nox -s tests-3.12 -- --cov --cov-report=xml --cov-report=term --cov-append --durations=20 if: runner.os == 'Linux' - name: Test on 🐍 3.13 run: nox -s tests-3.13 -- --cov --cov-report=xml --cov-report=term --cov-append --durations=20 - name: Upload coverage report uses: codecov/codecov-action@v4 with: name: ${{ matrix.runs-on }}-any verbose: true cygwin: name: Tests on 🐍 3.9 • cygwin runs-on: windows-latest timeout-minutes: 120 needs: [lint] steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: cygwin/cygwin-install-action@v4 with: platform: x86_64 packages: cmake ninja git make gcc-g++ gcc-fortran python39 python39-devel python39-pip - name: Install nox run: python3.9 -m pip install nox - name: Run tests env: TMP: /tmp TEMP: /tmp run: python3.9 -m nox -s tests-3.9 -- -m "not deprecated" --durations=20 tests-pypy: name: Tests on 🐍 PyPy ${{ matrix.pypy-version }} • ${{ matrix.runs-on }} runs-on: ${{ matrix.runs-on }} timeout-minutes: 40 needs: [lint] strategy: fail-fast: false matrix: runs-on: [ubuntu-latest, macos-13, windows-latest] pypy-version: ["3.9"] include: - runs-on: ubuntu-latest pypy-version: "3.7" steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: yezz123/setup-uv@v4 - name: Setup nox uses: wntrblm/nox@2024.04.15 - uses: actions/setup-python@v5 with: python-version: pypy-${{ matrix.pypy-version }} - name: Test on 🐍 PyPy ${{ matrix.pypy-version }} run: > nox -s tests-pypy${{ matrix.pypy-version }} -- --cov --cov-report=xml --cov-report=term --durations=20 ${{ matrix.runs-on == 'windows-latest' && '-k "not pep518"' || '' }} - name: Upload coverage report uses: codecov/codecov-action@v4 with: name: ${{ runner.os }}-${{ matrix.pypy-version }} verbose: true dist: name: Distribution timeout-minutes: 5 runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Build SDist and wheel run: pipx run build - uses: actions/upload-artifact@v4 with: path: dist/* - name: Check metadata run: pipx run twine check dist/* pass: if: always() timeout-minutes: 1 needs: [tests, tests-pypy, dist] runs-on: ubuntu-latest steps: - name: Decide whether the needed jobs succeeded or failed uses: re-actors/alls-green@release/v1 with: jobs: ${{ toJSON(needs) }} scikit-build-0.18.1/.gitignore000066400000000000000000000012241466366435500161750ustar00rootroot00000000000000*.py[cod] # C extensions *.so # Packages *.egg *.eggs *.egg-info dist build _skbuild eggs parts bin var sdist develop-eggs .installed.cfg lib lib64 # Installer logs pip-log.txt # Unit test / coverage reports .cache .*_cache/ .coverage .tox coverage.xml htmlcov # Translations *.mo # Mr Developer .mr.developer.cfg .project .pydevproject # Complexity output/*.html output/*/index.html # Sphinx docs/_build # IDE junk .idea/* *.swp .vscode/* *~ # build output (testing) skbuild/_cmake_test_compile/* .coverage* skbuild/_version.py /*env*/ # Fedora packaging /.distro/main.fmf /.distro/plans/main.fmf /.distro/tests/main.fmf # pyenv .python-version scikit-build-0.18.1/.packit.yaml000066400000000000000000000030671466366435500164310ustar00rootroot00000000000000# See the documentation for more information: # https://packit.dev/docs/configuration/ specfile_path: .distro/python-scikit-build.spec files_to_sync: - src: .distro/ dest: ./ filters: - "- plans/main.fmf.dist-git" - "- plans/rpminspect.fmf" - "- plans/rpmlint.fmf" - .packit.yaml - src: .distro/plans/main.fmf.dist-git dest: plans/main.fmf upstream_package_name: scikit-build downstream_package_name: python-scikit-build update_release: false jobs: - &copr_build job: copr_build trigger: release owner: "@scikit-build" project: release targets: &targets - fedora-all-x86_64 - fedora-all-aarch64 - &tests job: tests trigger: release targets: *targets fmf_path: .distro - <<: *copr_build trigger: commit branch: main project: nightly - <<: *tests trigger: commit branch: main - <<: *copr_build trigger: pull_request project: scikit-build update_release: true release_suffix: "{PACKIT_RPMSPEC_RELEASE}" - <<: *tests trigger: pull_request - job: propose_downstream trigger: release dist_git_branches: # TODO: Switch to fedora-development and fedora-latest # There is an issue that the commits diverge on different PRs. In the meantime will create PRs on branched fedora # manually # https://github.com/packit/packit/issues/1724 - fedora-rawhide - job: koji_build trigger: commit dist_git_branches: - fedora-all - job: bodhi_update trigger: commit dist_git_branches: - fedora-branched scikit-build-0.18.1/.pre-commit-config.yaml000066400000000000000000000032431466366435500204710ustar00rootroot00000000000000repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: "v4.6.0" hooks: - id: check-added-large-files - id: check-case-conflict - id: check-merge-conflict # - id: check-docstring-first - id: check-symlinks - id: check-toml - id: check-yaml - id: debug-statements - id: end-of-file-fixer - id: mixed-line-ending - id: requirements-txt-fixer - id: trailing-whitespace - repo: https://github.com/sirosen/texthooks rev: "0.6.6" hooks: - id: fix-ligatures - id: fix-smartquotes - repo: https://github.com/adamchainz/blacken-docs rev: 1.18.0 hooks: - id: "blacken-docs" additional_dependencies: - black==24.* - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.5.0 hooks: - id: ruff args: ["--fix", "--show-fixes"] - id: ruff-format exclude: ^docs/conf\.py$ - repo: https://github.com/codespell-project/codespell rev: "v2.3.0" hooks: - id: codespell exclude: "(.png|.svg|^_version.py)$" args: ["-L", "ba,endwhile,unparseable"] - 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/henryiii/check-sdist rev: "v1.0.0rc2" hooks: - id: check-sdist args: [--inject-junk] additional_dependencies: - hatch-fancy-pypi-readme - hatch-vcs - hatchling - repo: https://github.com/pre-commit/mirrors-mypy rev: "v1.10.1" hooks: - id: mypy files: ^(skbuild|tests) exclude: ^tests/samples additional_dependencies: - cmake - ninja - packaging - pytest<8 - tomli - types-requests - types-setuptools>=70 scikit-build-0.18.1/.readthedocs.yml000066400000000000000000000007041466366435500172750ustar00rootroot00000000000000# .readthedocs.yml # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Include PDF and ePub formats: all # Set the version of Python build: os: ubuntu-22.04 tools: python: "3.11" # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/conf.py python: install: - method: pip path: . extra_requirements: - docs scikit-build-0.18.1/AUTHORS.rst000066400000000000000000000002001466366435500160550ustar00rootroot00000000000000======= Credits ======= Please see the GitHub project page at https://github.com/scikit-build/scikit-build/graphs/contributors scikit-build-0.18.1/CHANGES.rst000066400000000000000000001420401466366435500160110ustar00rootroot00000000000000============= Release Notes ============= This is the list of changes to scikit-build between each release. For full details, see the commit logs at https://github.com/scikit-build/scikit-build Next Release ============ We are hard at work on the next generation of scikit-build `scikit-build-core `_, which will eventually replace the backend here. We are also continuing to fix bugs, make improvements, and backport changes here. .. START-BRIEF-CHANGELOG Scikit-build 0.18.1 =================== This release fixes issues with setuptools 74, and avoids a warning from recent versions of wheel. Android and iOS are now included in known platforms. Bug fixes --------- * Support for setuptools 74 in :pr:`1116` * iOS and Android support by :user:`FeodorFitsner` in :pr:`1101` Testing ------- * Fix for distutils change in :pr:`1103` * Remove test directives by :user:`s-t-e-v-e-n-k` in :pr:`1108` Scikit-build 0.18.0 =================== This release bumps the minimum required CMake to 3.5 and supports CPython 3.13. Bug fixes --------- * Support MSVC 17.10 in :pr:`1081` * CMake 3.5+ requirement in :pr:`1095` * Support CPython 3.13 with windows lib finding fix in :pr:`1094` * Don't die on PermissionError during chmod by :user:`mweinelt` in :pr:`1073` * Remove usage of deprecated distutils in cmake files by :user:`hmaarrfk` in :pr:`1032` * Use first available option for vswhere output by :user:`ZzEeKkAa` in :pr:`1030` Testing ------- * Support setuptools 69.3.0 changes in two tests by :user:`s-t-e-v-e-n-k` in :pr:`1087` * Use uv in a few places in :pr:`1092` Fedora CI --------- * Fedora maintenance by :user:`LecrisUT` in :pr:`1078` * Fedora: Fix rsync filter rule by :user:`LecrisUT` in :pr:`1003` * Fix Fedora tests by :user:`LecrisUT` in :pr:`1050` * Fedora downstream CI by :user:`LecrisUT` in :pr:`993` Miscellaneous ------------- * Clean up pylint in :pr:`1017` * Fix mypy type ignores for new setuptools types in :pr:`1082` * Move to Ruff-format in :pr:`1035` * Remove pkg_resources and test command in :pr:`1014` * Ruff moved to astral-sh in :pr:`1007` * Target-version no longer needed by Black or Ruff in :pr:`1008` * Update ruff and fix warnings in :pr:`1060` * Use 2x faster black mirror in :pr:`1021` * Group dependabot updates in :pr:`1054` * macos-latest is changing to macos-14 ARM runners in :pr:`1083` * Skip win PyPy PEP 518 in :pr:`1091` Scikit-build 0.17.6 =================== A small fix release with some new platforms and better testing, including CPython 3.12.0b1. Bug fixes --------- * Support added for SunOS by :user:`mtelka` in :pr:`983`. * Support added for AIX (with recent CMake) by :user:`bhuntsman` in :pr:`988`. Testing ------- * Tests now pass on CPython 3.12.0b1 in :pr:`879`. * Tests no longer use ``pytest-virtualenv`` in :pr:`879`. * ``isolated`` marker now includes ``test_distribution`` tests in :pr:`879`. * Tests avoid incorrect ``get_map`` match by :user:`keszybz` in :pr:`990`. * Fedora testing fix by :user:`LecrisUT` in :pr:`986` and :pr:`938`. Miscellaneous ------------- * Docs improvements in :pr:`979`. Scikit-build 0.17.5 =================== A small fix release fixing the passing on of generator specific arguments. This fixes some cases where the Ninja generator was found but then was unable to build. NetBSD was reported to work, so was added to the BSD's supported. Bug fixes --------- * Generator args were missing for actual compile in :pr:`975`. * Add support for netbsd & pyodide (future) in :pr:`977`. Scikit-build 0.17.4 =================== A followup fix to the issue 0.17.3 tried to fix. We now have a method to manually test downstream packages, too. Bug fixes --------- * Make sure include dir is found even if the lib is not present in :pr:`974`. Scikit-build 0.17.3 =================== A small release related to ``PYTHON_LIBRARY`` handling changes in 0.17.2; scikit-build 0.17.3 returns an empty string from ``get_python_library`` if no Python library is present (like on manylinux), where 0.17.2 returned None, and previous versions returned a non-existent path. Note that adding ``REQUIRED`` to ``find_package(PythonLibs`` will fail, but it is incorrect (you must not link to ``libPython.so``) and was really just injecting a non-existent path before. Bug fixes --------- * Keep ``get_python_library`` return type string if python lib non-existing for now in :pr:`959`. * Avoid 'not found' warning if libs are not found by FindPythonExtensions in :pr:`960`. * FindNumPy should not call FindPythonLibs in :pr:`958`. Scikit-build 0.17.2 =================== Another small release with fixes for non-MSVC Windows platforms. Bug fixes --------- * RPM spec fix by :user:`LecrisUT` in :pr:`937`. * Validate value before returning library path by :user:`dlech` in :pr:`942`. * Only add ``Python_LIBRARY`` on Windows MSVC in :pr:`943` and :pr:`944`. * Slightly nicer traceback for failed compiler in :pr:`947`. Testing ------- * Hide a few warnings that are expected in :pr:`948`. Scikit-build 0.17.1 =================== This is a small release fixing a few bugs; the primary one being a change that was triggering a bug in older FindPython. The unused variable messages have been deactivated to simplify output, as well. Bug fixes --------- * Older (<3.24) CMake breaks when lib specified in :pr:`932`. * An error output was missing formatting in :pr:`931`. * Make empty ``CMAKE_OSX_DEPLOYMENT_TARGET`` a warning (bug in conda-forge's clang activation fixed upstream) in :pr:`934`. * Remove unused variable warnings by in :pr:`930`. Testing ------- * Add Fedora packaging with packit automation by :user:`LecrisUT` in :pr:`928`. * Fix codecov ci by :user:`LecrisUT` in :pr:`929`. * Update some coverage settings in :pr:`933`. Scikit-build 0.17.0 =================== A lot of bug fixes are present in this release, focusing on Windows, PyPy, and cross compiling. We've also improved the compatibility with default setuptools behaviors a little, and enabled some things that were previously unavailable, like overriding the build type via the cmake argument environment variables. We've expanded our CI matrix to include Windows and macOS PyPy and some Fortran tests on Linux. This release requires Python 3.7+. Bug fixes --------- * Match setuptools behavior for ``include_package_data`` default. by :user:`vyasr` in :pr:`873`. * Misc. fixes for F2PY and PythonExtensions modules by :user:`benbovy` in :pr:`495`. * Provide more useful error if user provides ``CMAKE_INSTALL_PREFIX`` by :user:`vyasr` in :pr:`872`. * Stop assuming that ``.pyx`` files are in the same directory as ``CMakeLists.txt`` by :user:`vyasr` in :pr:`871`. * Allow build type overriding in :pr:`902`. * Detect PyPy library correctly on Windows by user:`gershnik` in :pr:`904`. * Include library for FindPython for better Windows cross-compiles in :pr:`913`. Thanks to user:`maxbachmann` for testing. * Fix logic for default generator when cross-compiling for ARM on Windows in :pr:`917` by :user:`dlech`. * Use f2py's ``get_include`` if present in :pr:`877`. * Fix support for cross-compilation exception using ``targetLinkLibrariesWithDynamicLookup`` by :user:`erykoff` in :pr:`901`. * Treat empty ``MACOSX_DEPLOYMENT_TARGET`` as if it was unset in :pr:`918`. Testing ------- * Add hello fortran sample package + tests by :user:`benbovy` in :pr:`493`. * Add sdist check & fix in :pr:`906`. * Fix some setuptools types in :pr:`888`. * Add PyPy Win & macOS to the CI in :pr:`907`. * Add tests for Python 3.12 Linux alphas in :pr:`922`. Miscellaneous ------------- * Drop Python 3.6 in :pr:`862`. * Move building backend to hatchling in :pr:`870`. * Avoid mutating function input parameters in :pr:`899`. * Use _compat/typing name in :pr:`869`. .. END-BRIEF-CHANGELOG Scikit-build 0.16.7 =================== This is expected to be the final release series supporting Python 3.6. 0.17 will require Python 3.7+ and start removing deprecated functionality. * Added ``SKBUILD_GNU_SKIP_LOCAL_SYMBOL_EXPORT_OVERRIDE`` to disable script in :pr:`848`, thanks to :user:`aaron-bray` and :user:`vyasr`. * Address a new warning from setuptools in our test suite in :pr:`859`. * Move to using Ruff, update to Black 23, and use Flynt to move more code to f-strings. Scikit-build 0.16.6 =================== * Fix a discovery regression in 0.16.5 when a ``cmake`` folder or ``cmake.py`` was present in :pr:`848`. * Correct an issue in the tests where a generator wasn't expanded into a list in :pr:`850`. Scikit-build 0.16.5 =================== * Use cmake module if installed over system installs in :pr:`839`. * Support setting of ``-DCMAKE_SYSTEM_PROCESSOR`` if passed for selecting an arch, useful for cross compiling on conda-forge in :pr:`843`. * Fixed a rare encoded error output string on Windows in :pr:`842`. * Better granularity in extras in :pr:`838`. * Add test markers for nosetuptoolsscm and isolated (helpful for package distributions building scikit-build itself like conda) in :pr:`837`. Scikit-build 0.16.4 =================== This releases backports additions for Windows ARM cross-compiling via cibuildwheel from scikit-build-core 0.1.4. * Initial experimental support for Windows ARM cross-compile in :pr:`824` and :pr:`818` * Replace mailing list with GitHub Discussions board in :pr:`823` * Some CI updates in :pr:`811` and :pr:`812` Scikit-build 0.16.3 =================== This release fixes logging issues using setuptools 65.6+ affecting our tests. Pytest 7.2+ is now supported. ``setup.py `` and ``setup_requires`` are deprecated, and tests are marked as such. * Fix typo in usage.rst in :pr:`795`, thanks to :user:`chohner`. * Support pytest 7.2+ in :pr:`801`. * Change warning filtering in :pr:`802`. * Handle logging changes in setuptools 65.6+ in :pr:`807`. * Add deprecated markers to some tests in :pr:`807`. * Allow known warnings to show up in the tests :pr:`807`. Scikit-build 0.16.2 =================== This addresses one more small regression with the FindPython change from 0.16.0 that was affecting conda. :pr:`793`. Scikit-build 0.16.1 =================== This was a quick patch release that fixed a missing Python requires setting and some missing files :pr:`790`, and addressed a warning from setuptools in the tests. * Ignored distutils warning :pr:`785`. thanks to :user:`bnavigator`. Scikit-build 0.16.0 =================== This release adds support for Python 3.11 and removes support for Python 2.7 and 3.5 (:pr:`688`). Testing and static checking improved, including being fully statically typed internally (though setuptools is not fully typed, so it is of limited use). All deprecated setuptools/distutils features are also deprecated in scikit-build, like the ``test`` command, ``easy_install``, etc. Editable mode is still unsupported. Python 3.6 support is deprecated. Older versions of CMake (<3.15) are not recommended; a future version will remove support for older CMake's (along with providing a better mechanism for ensuring a proper CMake is available). If you need any of these features, please open or find an issue explaining what and why you need something. New Features ------------ * Cython module now supports FindPython mode. :pr:`743` * PyPy is discovered without extra settings in FindPython mode :pr:`744` Bug fixes --------- * FindPython mode uses a new path specification, should help make it usable. :pr:`774` * Better flushing and output streams for more consistent output ordering. :pr:`781` Documentation ------------- * scikit-build mailing list transitioned to the `scikit-build GitHub Discussions board `_. See :issue:`800`. * Transitioning away from the mailing list and adopting the GitHub Discussions will provide a more integrated platform enabling us to more effectively engage with the community. * After sending a `last message `_ describing the transition, the mailing list was updated to be read-only and the welcome message was updated to redirect visitor toward the Discussions board. Scikit-build 0.15.0 =================== This release is the final (again) release for Python < 3.6 and MSVC<2017. Support for FindPython from CMake 3.12+ was added, including FindPython2. Support for Cygwin added. New Features ------------ * Add support for FindPython (including 2 and 3). Thanks :user:`hameerabbasi` for the contribution. See :pr:`712`. * Add support for Cygwin. Thanks :user:`ax3l` and :user:`DWesl` and :user:`poikilos` for the help! See :pr:`485`. Bug fixes --------- * Fixed issue with distutils usage in Python 3.10. Thanks to :user:`SuperSandro2000` for the contribution in :pr:`700`. Scikit-build 0.14.1 =================== This release fixes a regression, and reverts a fix in 0.14.0. Some changes made to CI to fix recent removals. Bug fixes --------- * Fix issue with ``SKBUILD_CONFIGURE_OPTIONS`` not being read. * Reverted manifest install changes. Scikit-build 0.14.0 =================== This is the final release for Python < 3.6 and MSVC<2017. New Features ------------ * Add support for ``--install-target`` scikit-build command line option. And ``cmake_install_target`` in ``setup.py``. Allows providing an install target different than the default ``install``. Thanks :user:`phcerdan` for the contribution. See :pr:`477`. Bug fixes --------- * The manifest install location computation was fixed. Thanks :user:`kratsg` for the contribution in :pr:`682`. (Reverted in 0.14.1) * Byte-compilation was skipped due to a missing return. Thanks :user:`pekkarr` in :pr:`678`. * Packages can now be computed from the same shared collections, before this could confuse Scikit-build. Thanks :user:`vyasr` in :pr:`675`. * Fixed library detection for PyPy 3.9. Thanks :user:`rkaminsk` in :pr:`673`. Internal -------- * Scikit-build now uses ``pyproject.toml`` and ``setuptools_scm`` to build. If you are packaging scikit-build itself, you might need to update your requirements. See :pr:`634`. * The codebase is now formatted with Black. :pr:`665` Scikit-build 0.13.1 =================== This release fixes two bugs affecting Windows. Users should use ``"ninja; platform_system!='Windows"``, at least for now, since MSVC ships with Ninja, and that Ninja is better at finding the matching MSVC than the Python package is. Including it may slow down the search and force the IDE generator instead, but will at least no longer discover GCC instead. Bug fixes --------- * On Windows, don't let Ninja find something other than what it's supposed to look for. Ensure the Ninja package is used for the search, just like normal runs, if installed. :pr:`652`. * Do not throw an error when printing info and a logger is disconnected. :pr:`652` Scikit-build 0.13.0 =================== This is likely one of the final releases to support Python 2.7 and 3.5; future releases will likely target at least Python 3.6+ and MSCV 2017+. If you are using scikit-build via ``pyproject.toml``, please remember to include ``setuptools`` and ``wheel``. A future version of scikit-build may remove the setuptools install-time hard requirement. New Features ------------ * CMake module :doc:`/cmake-modules/Cython` now uses Cython default arguments. This no longer adds ``--no-docstrings`` in Release and MinSizeRel builds, so Cython docstrings are now retained by default. Additionally, ``--embed-positions`` is no longer added to Debug and RelWithDebInfo builds. Users can enable these and other Cython arguments via the option ``CYTHON_FLAGS``. See :issue:`518` and :pr:`519`, thanks to :user:`bdice` for the improvement. * Experimental support for ARM64 on Windows. Thanks to :user:`gaborkertesz-linaro` in :pr:`612`. * Support for MSVC 2022. Thanks to :user:`tttapa` for the contribution in :pr:`627`. * Support the modern form of ``target_link_libraries``, via ``SKBUILD_LINK_LIBRARIES_KEYWORD`` (somewhat experimental). Thanks to :user:`maxbachmann` in :pr:`611`. Bug fixes --------- * Update the Ninja path if using the ``ninja`` package. This fixes repeated isolated builds. Further path inspection and updates for isolated builds may be considered in the future. :pr:`631`, thanks to :user:`RUrlus` and :user:`segevfiner` for help in tracking this down. * Allow OpenBSD to pass the platform check (untested). See :pr:`586`. * Avoid forcing the min macOS version. Behaviour is now inline with setuptools. Users should set ``MACOSX_DEPLOYMENT_TARGET`` when building (automatic with cibuildwheel), otherwise you will get the same value Python was compiled with. Note: This may seem like a regression for PyPy until the next release (7.3.8), since it was compiled with 10.7, which is too old to build with on modern macOS - manually set ``MACOSX_DEPLOYMENT_TARGET`` (including setting it if unset in your ``setup.py``) for PyPy until 7.3.8. :pr:`607` * Fix logging issue when using Setuptools 60.2+. :pr:`623` * MacOS cross compiling support fix (for conda-forge) for built-in modules. Thanks to :user:`isuruf` for the contribution in :pr:`622`. * Better detection of the library path, fixes some issues with PyPy. Thanks to :user:`rkaminsk` for the contribution in :pr:`620` and :pr:`630`. PyPy is now part of our testing matrix as of :pr:`624`. Also :user:`robtaylor` in :pr:`632`. * Fixed issue when cross-compiling on conda-forge (probably upstream bug, but easy to avoid). :pr:`646`. Scikit-build 0.12.0 =================== The scikit-build GitHub organization welcomes :user:`henryiii` and :user:`mayeut` as core contributors and maintainers. Both are also maintainers of `cibuildwheel `_. :user:`henryiii` is a `pybind11 `_ and `pypa/build `_ maintainer, has been instrumental in adding Apple Silicon support, adding support for Visual Studio 2019, updating the Continuous Integration infrastructure, as well as helping review & integrate contributions, and addressing miscellaneous issues. Additionally, :user:`henryiii` has worked on an `example project `_ to build with ``pybind11`` and ``scikit-build``. :user:`mayeut` is a `manylinux `_ maintainer and focused his effort on updating the ``cmake-python-distributions`` and ``ninja-python-distributions`` so that the corresponding wheels are available on all supported platforms including Apple Silicon and all flavors of manylinux. New Features ------------ * Support Apple Silicon, including producing Universal2 wheels (:pr:`530`) and respecting standard setuptools cross-compile variables (:pr:`555`). Thanks to :user:`YannickJadoul` for the contributions. * Support MSVC 2019 without having to run it with the MSVC activation variables, just like 2017 and earlier versions. Thanks to :user:`YannickJadoul` for the contribution in :pr:`526`. Bug fixes --------- * Support ``-A`` and ``-T`` internally when setting up MSVC generators. Architecture now always passed through ``-A`` to MSVC generators. Thanks :user:`YannickJadoul` for the contribution. See :pr:`557` and :pr:`536`. * Fixed a regression that caused setuptools to complain about unknown setup option (`cmake_process_manifest_hook`). Thanks :user:`Jmennius` for the contribution. See :pr:`498`. * If it applies, ensure generator toolset is used to configure the project. Thanks :user:`YannickJadoul` for the contributions. See :pr:`526`. * Read ``CYTHON_FLAGS`` where needed, instead of once, allowing the user to define multiple modules with different flags. Thanks :user:`oiffrig` for the contributions in :pr:`536`. * Avoid an IndexError if prefix was empty. Thanks :user:`dfaure` for the contributions in :pr:`522`. Documentation ------------- * Update ``Conda: Step-by-step`` release guide available in :doc:`/make_a_release` section. * Update links to CMake documentation pages in :doc:`/generators`. Thanks :user:`Eothred` for the contributions in :pr:`508`. Tests ----- * Improve and simplify Continuous Integration infrastructure. * Support ``nox`` for running the tests locally. See :pr:`540`. * Use GitHub Actions for Continuous Integration and remove use of scikit-ci, tox, TravisCI, AppVeyor and CircleCI. See :pr:`549`, :pr:`551` and :pr:`552`. * Add support for testing against Python 3.10. See :pr:`565`. * Style checking handled by pre-commit. See :pr:`541`. * Check for misspellings adding GitHub Actions workflow using codespell. See :pr:`541`. * Fix linting error `F522 `_ reported with flake8 >= 3.8.x. Thanks :user:`benbovy` for the contributions. See :issue:`494`. * Fix regex in tests to support Python 3.10. Thanks :user:`mgorny` for the contributions in :pr:`544`. Scikit-build 0.11.1 =================== Bug fixes --------- * Support using scikit-build with conan where ``distro<1.2.0`` is required. Thanks :user:`AntoinePrv` and :user:`Chrismarsh` for reporting issues :issue:`472` and :issue:`488`. Documentation ------------- * Fix link in ``Conda: Step-by-step`` release guide available in :doc:`/make_a_release` section. Scikit-build 0.11.0 =================== New Features ------------ * Add a hook to process the cmake install manifest building the wheel. The hook function can be specified as an argument to the ``setup()`` function. This can be used e.g. to prevent installing cmake configuration files, headers, or static libraries with the wheel. Thanks :user:`SylvainCorlay` for the contribution. See :issue:`473`. * Add support for passing :ref:`CMake configure options ` like ``-DFOO:STRING:bar`` as global ``setuptools`` or ``pip`` options. * Add support for building project using PyPy or PyPy3. See https://pypy.org See :issue:`407`. * Add support for OS/400 (now known as IBM i). Thanks :user:`jwoehr` for the contribution. See :issue:`444`. * Display CMake command used to configure the project. Thanks :user:`native-api` for the contribution. See :issue:`443`. * CMake modules: * Improve CMake module :doc:`/cmake-modules/F2PY` adding ``add_f2py_target()`` CMake function allowing to generate ``*-f2pywrappers.f`` and `*module.c` files from ``*.pyf`` files. Thanks :user:`xoviat` for the contribution. * Update CMake module :doc:`/cmake-modules/PythonExtensions` adding ``add_python_library()`` and ``add_python_extension()``. Thanks :user:`xoviat` for the contribution. Bug fixes --------- * Fix python 2.7 installation ensuring setuptools < 45 is required. See :issue:`478`. * Fix unclosed file resource in :meth:`skbuild.cmaker.CMaker.check_for_bad_installs`. Thanks :user:`Nic30` for the suggestion. See :issue:`429`. * Update CMake module :doc:`/cmake-modules/PythonExtensions`: * Ensure correct suffix is used for compiled python module on windows. See :issue:`383`. * Fix warning using ``EXT_SUFFIX`` config variable instead of deprecated ``SO`` variable. See :issue:`381`. * Honor the ``MACOSX_DEPLOYMENT_TARGET`` environment variable if it is defined on macOS. Thanks :user:`certik` for the contribution. See :issue:`441`. * Fix CMake module :doc:`/cmake-modules/F2PY` to ensure the ``f2py`` executable specific to the python version being used is found. See :issue:`449`. Thanks :user:`bnavigator` for the contribution. * Replace ``platform.linux_distribution()`` which was removed in Python 3.8 by a call to ``distro.id()``. This adds the ``distro`` package as dependency. See :issue:`458`. Thanks :user:`bnavigator` for the contribution. Documentation ------------- * Add notes section to the ``For maintainers`` top-level category that includes a comparison between ``sysconfig`` and ``distutils.sysconfig`` modules. * Remove obsolete comment in ``cmaker.py``. See :issue:`439`. Thanks :user:`isuruf` Tests ----- * Update ``initialize_git_repo_and_commit`` to prevent signing message on system with commit signing enabled globally. Scikit-build 0.10.0 =================== New Features ------------ * Improve message displayed when discovering a working environment for building projects. For example, instead of displaying ``-- Trying "Ninja" generator``, it now displays a message like ``-- Trying "Ninja (Visual Studio 15 2017 Win64 v140)" generator``. Bug fixes --------- * Checking generator candidates can now handle handle paths and binaries with spaces, so that ``setup.py --cmake-executable "C:/Program Files (x86)/cmake/cmake.exe"`` works as expected. Contributed by :user:`jokva`. See :issue:`400`. * Fix sdist command to ensure symlinks in original source tree are maintained. Contributed by :user:`anibali`. See :issue:`401`. * Ensure use of ``bdist_egg`` or ``bdist_rpm`` commands trigger build using cmake. * Fix default value returned by :func:`skbuild.constants.skbuild_plat_name()` on macOS. See :issue:`417`. Internal API ------------ * Add :func:`skbuild.platform_specifics.windows.find_visual_studio`. Documentation ------------- * Fix typo in example associated with :doc:`/cmake-modules/PythonExtensions`. Thanks :user:`eirrgang` for the contribution. * Update :doc:`/make_a_release` section to include ``Conda: Step-by-step`` release guide. Tests ----- * Introduce ``check_sdist_content()`` and fix tests that are checking content of sdist to account for changes introduced in Python 3.8 and backported to python 2.7, 3.6 and 3.7. The changes introduced in `python/cpython#9419 `_ adds directory entries to ZIP files created by distutils. Thanks :user:`anibali` for the contribution. See :issue:`404`. * Fix ``check_wheel_content()`` to consider changes in ``0.33.1 < wheel.__version__ < 0.33.4`` where directory entries are included when building wheel. See _`pypa/wheel#294 `. * Fix reporting of ``AssertionError`` raised in ``check_wheel_content()`` function by relocating the source code into a dedicated module ``tests.pytest_helpers`` and by adding a ``conftest.py`` configuration file registering it for pytest assertion rewriting. See https://docs.pytest.org/en/latest/writing_plugins.html#assertion-rewriting and :issue:`403`. * Fix ``test_generator_selection`` when building with "Visual C++ for Python 2.7" installed for all users. This addresses failure associated with ``win_c_compilervs2008cxx_compilervs2008python2.7`` when running test in `scikit-build-feedstock `_ where "Visual C++ for Python 2.7" is installed using (`vcpython27 `_ chocolatey package. * Continuous Integration * Add support for Azure Pipelines for Python 3.7 32-bit and 64-bit * AppVeyor: Disable test for Python 3.7 32-bit and 64-bit. * CircleCI: Update version of docker images from jessie to stretch. This addresses issue `circleci/circleci-images#370 `_. * TravisCI: Remove obsolete Python 3.4 testing. It reached `end-of-life on March 18 2019 `_. Scikit-build 0.9.0 ================== New Features ------------ * Add support for building distutils based extensions associated with ``ext_modules`` setup keyword along side skbuild based extensions. This means using ``build_ext`` command (and associated ``--inplace`` argument) is supported. Thanks :user:`Erotemic` for the contribution. See :issue:`284`. Bug fixes --------- * Fix build of wheels if path includes spaces. See issue :issue:`375`. Thanks :user:`padraic-padraic` for the contribution. * Ensure wheel platform name is correctly set when providing custom ``CMAKE_OSX_DEPLOYMENT_TARGET`` and ``CMAKE_OSX_ARCHITECTURES`` values are provided. Thanks :user:`nonhermitian` for the contribution. See :issue:`377`. * Fix testing with recent version of pytest by updating the pytest-runner requirements expression in ``setup.py``. Thanks :user:`mackelab` for the contribution. Scikit-build 0.8.1 ================== Bug fixes --------- * Fix ``bdist_wheel`` command to support ``wheel >= 0.32.0``. Thanks :user:`fbudin69500` for reporting issue :issue:`360`. Tests ----- * Fix ``test_distribution.py`` updating use of ``Path.files()`` and requiring ``path.py>=11.5.0``. Scikit-build 0.8.0 ================== New Features ------------ * Introduced :const:`skbuild.constants.CMAKE_DEFAULT_EXECUTABLE` to facilitate distribution of scikit-build in package manager like `Nixpkgs `_ where all paths to dependencies are hardcoded. Suggested by :user:`FRidh`. * Setup keywords: * If not already set, ``zip_safe`` option is set to ``False``. Suggested by :user:`blowekamp`. * Add support for ``--skip-generator-test`` when a generator is explicitly selected using ``--generator``. This allows to speed up overall build when the build environment is known. Bug fixes --------- * Fix support for building project with CMake source directory outside of the ``setup.py`` directory. See :issue:`335` fixed by :user:`massich`. * Fix reading of ``.cmake`` files having any character not available in `CP-1252 `_ (the default code page on windows). See :issue:`334` fixed by :user:`bgermann`. * Fix parsing of macOS specific arguments like ``--plat-name macosx-X.Y-x86_64`` and ``-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=X.Y`` and ensure that the ones specified as command line arguments override the default values or the one hard-coded in the ``cmake_args`` setup keyword. Thanks :user:`yonip` for the help addressing :issue:`342`. * Support case where relative directory set in ``package_dir`` has an ending slash. For example, specifying ``package_dir={'awesome': 'src/awesome/'},`` is now properly handled. * Fix support for isolated build environment ensuring the CMake project is reconfigured when ``pip install -e .`` is called multiple times. See :issue:`352`. Documentation ------------- * README: Update overall download count. * Add logo and update sphinx configuration. Thanks :user:`SteveJordanKW` for the design work. * Update :ref:`CMake installation ` section. Thanks :user:`thewtex`. * Add :ref:`support_isolated_build` section. * Add :ref:`optimized_incremental_build` section. * Update :ref:`usage documentation ` to specify that ``--universal`` and ``--python-tags`` have no effect. Thanks :user:`bgermann` for the suggestion. See :issue:`353`. * Simplify documentation merging ``Extension Build System`` section with the ``Advanced Usage`` section. Thanks :user:`thewtex` for the suggestion. Tests ----- * Add ``check_wheel_content`` utility function. * Skip ``test_setup_requires_keyword_include_cmake`` if running in conda test environment or if https://pypi.org is not reachable. Suggested by :user:`Luthaf`. * Continuous Integration * TravisCI: * Remove testing of linux now covered by CircleCI, add testing for Python 3.5, 3.6 and 3.7 on macOS. * Ensure system python uses latest version of pip * AppVeyor, CircleCI: Add testing for Python 3.7 * Remove uses of unneeded ``$`` command wrapper. scikit-build should already take care of setting up the expected environment. * Always install up-to-date `scikit-ci`_ and `scikit-ci-addons`_. * Simplify release process managing ``versioning`` with `python-versioneer `_ and update :ref:`making_a_release` documentation. Scikit-build 0.7.1 ================== Documentation ------------- * Fix description and classifier list in setup.py. * Fix link in README. Scikit-build 0.7.0 ================== New Features ------------ * Faster incremental build by re-configuring the project only if needed. This was achieved by (1) adding support to retrieve the environment mapping associated with the generator set in the ``CMakeCache.txt`` file, (2) introducing a :func:`CMake spec file ` storing the CMake version as well as the the CMake arguments and (3) re-configuring only if either the generator or the CMake specs change. Thanks :user:`xoviat` for the contribution. See :issue:`301`. * CMake modules: * CMake module :doc:`/cmake-modules/PythonExtensions`: Set symbol visibility to export only the module init function. This applies to GNU and MSVC compilers. Thanks :user:`xoviat`. See :issue:`299`. * Add CMake module :doc:`/cmake-modules/F2PY` useful to find the ``f2py`` executable for building Python extensions with Fortran. Thanks to :user:`xoviat` for moving forward with the integration. Concept for the module comes from the work of :user:`scopatz` done in `PyNE `_ project. See :issue:`273`. * Update CMake module :doc:`/cmake-modules/NumPy` setting variables ``NumPy_CONV_TEMPLATE_EXECUTABLE`` and ``NumPy_FROM_TEMPLATE_EXECUTABLE``. Thanks :user:`xoviat` for the contribution. See :issue:`278`. * Setup keywords: * Add support for :ref:`cmake_languages ` setup keyword. * Add support for ``include_package_data`` and ``exclude_package_data`` setup keywords as well as parsing of ``MANIFEST.in``. See :issue:`315`. Thanks :user:`reiver-dev` for reporting the issue. * Add support for ``cmake_minimum_required_version`` setup keyword. See :issue:`312`. Suggested by :user:`henryiii`. * Install cmake if found in ``setup_requires`` list. See :issue:`313`. Suggested by :user:`henryiii`. * Add support for ``--cmake-executable`` scikit-build command line option. Thanks :user:`henryborchers` for the suggestion. See :issue:`317`. * Use ``_skbuild/platform-X.Y`` instead of ``_skbuild`` to build package. This allows to have a different build directory for each python version. Thanks :user:`isuruf` for the suggestion and :user:`xoviat` for contributing the feature. See :issue:`283`. * Run cmake and ``develop`` command when command ``test`` is executed. Bug fixes --------- * Fix support of ``--hide-listing`` when building wheel. * CMake module :doc:`/cmake-modules/Cython`: Fix escaping of spaces associated with ``CYTHON_FLAGS`` when provided as command line arguments to the cython executable through CMake cache entries. See :issue:`265` fixed by :user:`neok-m4700`. * Ensure package data files specified in the ``setup()`` function using ``package_data`` keyword are packaged and installed. * Support specifying a default directory for all packages not already associated with one using syntax like ``package_dir={'':'src'}`` in ``setup.py``. Thanks :user:`benjaminjack` for reporting the issue. See :issue:`274`. * Improve ``--skip-cmake`` command line option support so that it can re-generate a source distribution or a python wheel without having to run cmake executable to re-configure and build. Thanks to :user:`jonwoodring` for reporting the issue on the `mailing list `_. * Set ``skbuild `` as wheel generator. See `PEP-0427 `_ and :issue:`191`. * Ensure ``MANIFEST.in`` is considered when generating source distribution. Thanks :user:`seanlis` for reporting the problem and providing an initial patch, and thanks :user:`henryiii` for implementing the corresponding test. See :issue:`260`. * Support generation of source distribution for git repository having submodules. This works only for version of git >= 2.11 supporting the ``--recurse-submodules`` option with ``ls-files`` command. Internal API ------------ * Add :meth:`skbuild.cmaker.get_cmake_version`. Python Support -------------- * Tests using Python 3.3.x were removed and support for this version of python is not guaranteed anymore. Support was removed following the deprecation warnings reported by version 0.31.0 of wheel package, these were causing the tests ``test_source_distribution`` and ``test_wheel`` to fail. Tests ----- * Speedup execution of tests that do not require any CMake language enabled. This is achieved by (1) introducing the test project ``hello-no-language``, (2) updating test utility functions ``execute_setup_py`` and ``project_setup_py_test`` to accept the optional parameter ``disable_languages_test`` allowing to skip unneeded compiler detection in test project used to verify that the selected CMake generator works as expected, and (3) updating relevant tests to use the new test project and parameters. Overall testing time on all continuous integration services was reduced: * AppVeyor: * from **~16 to ~7** minutes for 64 and 32-bit Python 2.7 tests done using Visual Studio Express 2008 * from more than **2 hours to ~50 minutes** for 64 and 32-bit Python 3.5 tests done using Visual Studio 2015. Improvement specific to Python 3.x were obtained by caching the results of slow calls to ``distutils.msvc9compiler.query_vcvarsall`` (for Python 3.3 and 3.4) and ``distutils._msvccompiler._get_vc_env`` (for Python 3.5 and above). These functions were called multiple times to create the list of :class:`skbuild.platform_specifics.windows.CMakeVisualStudioCommandLineGenerator` used in :class:`skbuild.platform_specifics.windows.WindowsPlatform`. * CircleCI: from **~7 to ~5** minutes. * TravisCI: from **~21 to ~10** minutes. * Update maximum line length specified in flake8 settings from 80 to 120 characters. * Add ``prepend_sys_path`` utility function. * Ensure that the project directory is prepended to ``sys.path`` when executing test building sample project with the help of ``execute_setup_py`` function. * Add codecov config file for better defaults and prevent associated Pull Request checks from reporting failure when coverage only slightly changes. Documentation ------------- * Improve internal API documentation: * :mod:`skbuild.platform_specifics.windows` * :mod:`skbuild.command` * :mod:`skbuild.command.generate_source_manifest` * :mod:`skbuild.utils` * Split usage documentation into a ``Basic Usage`` and ``Advanced Usage`` sections. Cleanups -------- * Fix miscellaneous pylint warnings. Scikit-build 0.6.1 ================== Bug fixes --------- * Ensure CMake arguments passed to scikit-build and starting with ``-DCMAKE_*`` are passed to the test project allowing to determine which generator to use. For example, this ensures that arguments like ``-DCMAKE_MAKE_PROGRAM:FILEPATH=/path/to/program`` are passed. See :issue:`256`. Documentation ------------- * Update :doc:`/make_a_release` section including instructions to update ``README.rst`` with up-to-date pypi download statistics based on Google big table. Scikit-build 0.6.0 ================== New features ------------ * Improve ``py_modules`` support: Python modules generated by CMake are now properly included in binary distribution. * Improve developer mode support for ``py_modules`` generated by CMake. Bug fixes --------- * Do not implicitly install python modules when the beginning of their name match a package explicitly listed. For example, if a project has a package ``foo/__init__.py`` and a module ``fooConfig.py``, and only package ``foo`` was listed in ``setup.py``, ``fooConfig.py`` is not installed anymore. * CMake module :doc:`/cmake-modules/targetLinkLibrariesWithDynamicLookup`: Fix the caching of *dynamic lookup* variables. See :issue:`240` fixed by :user:`blowekamp`. Requirements ------------ * wheel: As suggested by :user:`thewtex`, unpinning version of the package by requiring ``>=0.29.0`` instead of ``==0.29.0`` will avoid uninstalling a newer version of wheel package on up-to-date system. Documentation ------------- * Add a command line :ref:`CMake Options ` section to :doc:`Usage <\usage>`. * Fix :ref:`table ` listing *Visual Studio IDE* version and corresponding with *CPython version* in :doc:`/generators`. * Improve :doc:`/make_a_release` section. Tests ----- * Extend ``test_hello``, ``test_setup``, and ``test_sdist_hide_listing`` to (1) check if python modules are packaged into source and wheel distributions and (2) check if python modules are copied into the source tree when developer mode is enabled. Internal API ------------ * Fix :meth:`skbuild.setuptools_wrap.strip_package` to handle empty package. * Teach :meth:`skbuild.command.build_py.build_py.find_modules` function to look for ``py_module`` file in ``CMAKE_INSTALL_DIR``. * Teach :class:`skbuild.utils.PythonModuleFinder` to search for ``python module`` in the CMake install tree. * Update :func:`skbuild.setuptools_wrap._consolidate` to copy file into the CMake tree only if it exists. * Update :meth:`skbuild.setuptools_wrap._copy_file` to create directory only if there is one associated with the destination file. Scikit-build 0.5.1 ================== Bug fixes --------- * Ensure file copied in "develop" mode have "mode bits" maintained. Scikit-build 0.5.0 ================== New features ------------ * Improve user experience by running CMake only if needed. See :issue:`207` * Add support for :ref:`cmake_with_sdist ` setup keyword argument. * Add support for ``--force-cmake`` and ``--skip-cmake`` global :ref:`setup command-line options `. * scikit-build conda-forge recipe added by :user:`isuruf`. See `conda-forge/staged-recipes#1989 `_ * Add support for `development mode `_. (:issue:`187`). * Improved :doc:`/generators` selection: * If available, uses :ref:`Ninja` build system generator on all platforms. An advantages is that ninja automatically parallelizes the build based on the number of CPUs. * Automatically set the expected ``Visual Studio`` environment when ``Ninja`` or ``NMake Makefiles`` generators are used. * Support `Microsoft Visual C++ Compiler for Python 2.7 `_. See :issue:`216`. * Prompt for user to install the required compiler if it is not available. See :issue:`27`. * Improve :doc:`/cmake-modules/targetLinkLibrariesWithDynamicLookup` CMake Module extending the API of ``check_dynamic_lookup`` function: * Update long signature: ```` is now optional * Add support for short signature: ``check_dynamic_lookup()``. See `SimpleITK/SimpleITK#80 `_. Bug fixes --------- * Fix scikit-build source distribution and add test. See :issue:`214` Thanks :user:`isuruf` for reporting the issue. * Support building extension within a virtualenv on windows. See :issue:`119`. Documentation ------------- * add :doc:`/generators` section * add :doc:`/history` section * allow github issues and users to easily be referenced using ``:issue:`XY``` and ``:user:`username``` markups. This functionality is enabled by the `sphinx-issue `_ sphinx extension * make_a_release: Ensure uploaded distributions are signed * usage: * Add empty cross-compilation / wheels building sections * Add :ref:`Why should I use scikit-build ? ` * Add :ref:`Setup options ` section * hacking: * Add :ref:`internal_api` section generated using ``sphinx-apidoc``. * Add :ref:`internal_cmake_modules` to document :doc:`/cmake-modules/targetLinkLibrariesWithDynamicLookup` CMake module. Requirements ------------ * setuptools: As suggested by :user:`mivade` in :issue:`212`, remove the hard requirement for ``==28.8.0`` and require version ``>= 28.0.0``. This allows to "play" nicely with conda where it is problematic to update the version of setuptools. See `pypa/pip#2751 `_ and `ContinuumIO/anaconda-issues#542 `_. Tests ----- * Improve "push_dir" tests to not rely on build directory name. Thanks :user:`isuruf` for reporting the issue. * travis/install_pyenv: Improve MacOSX build time updating `scikit-ci-addons`_ * Add ``get_cmakecache_variables`` utility function. .. _scikit-ci: http://scikit-ci.readthedocs.io .. _scikit-ci-addons: http://scikit-ci-addons.readthedocs.io Internal API ------------ * :meth:`skbuild.cmaker.CMaker.configure`: Change parameter name from ``generator_id`` to ``generator_name``. This is consistent with how generator are identified in `CMake documentation `_. This change breaks backward compatibility. * :meth:`skbuild.platform_specifics.abstract.CMakePlatform.get_best_generator`: Change parameter name from ``generator`` to ``generator_name``. Note that this function is also directly importable from :mod:`skbuild.platform_specifics`. This change breaks backward compatibility. * :class:`skbuild.platform_specifics.abstract.CMakeGenerator`: This class allows to handle generators as sophisticated object instead of simple string. This is done anticipating the support for `CMAKE_GENERATOR_PLATFORM `_ and `CMAKE_GENERATOR_TOOLSET `_. Note also that the class is directly importable from :mod:`skbuild.platform_specifics` and is now returned by :meth:`skbuild.platform_specifics.get_best_generator`. This change breaks backward compatibility. Cleanups -------- * appveyor.yml: * Remove unused "on_failure: event logging" and "notifications: GitHubPullRequest" * Remove unused SKIP env variable Scikit-build 0.4.0 ================== New features ------------ * Add support for ``--hide-listing`` option * allow to build distributions without displaying files being included * useful when building large project on Continuous Integration service limiting the amount of log produced by the build * CMake module: ``skbuild/resources/cmake/FindPythonExtensions.cmake`` * Function ``python_extension_module``: add support for `module suffix `_ Bug fixes --------- * Do not package python modules under "purelib" dir in non-pure wheel * CMake module: ``skbuild/resources/cmake/targetLinkLibrariesWithDynamicLookup.cmake``: * Fix the logic checking for cross-compilation (the regression was introduced by :issue:`51` and :issue:`47` * It configure the text project setting `CMAKE_ENABLE_EXPORTS `_ to ON. Doing so ensure the executable compiled in the test exports symbols (if supported by the underlying platform) Docs ---- * Add `short note `_ explaining how to include scikit-build CMake module * Move "Controlling CMake using scikit-build" into a "hacking" section * Add initial version of `"extension_build_system" documentation `_ Tests ----- * tests/samples: Simplify project removing unneeded install rules and file copy * Simplify continuous integration * use `scikit-ci`_ and `scikit-ci-addons`_ * speed up build setting up caching * Makefile: * Fix ``coverage`` target * Add ``docs-only`` target allowing to regenerate the Sphinx documentation without opening a new page in the browser. Scikit-build 0.3.0 ================== New features ------------ * Improve support for "pure", "CMake" and "hybrid" python package * a "pure" package is a python package that have all files living in the project source tree * an "hybrid" package is a python package that have some files living in the project source tree and some files installed by CMake * a "CMake" package is a python package that is fully generated and installed by CMake without any of his files existing in the source tree * Add support for source distribution. See :issue:`84` * Add support for setup arguments specific to scikit-build: * ``cmake_args``: additional option passed to CMake * ``cmake_install_dir``: relative directory where the CMake project being built should be installed * ``cmake_source_dir``: location of the CMake project * Add CMake module ``FindNumPy.cmake`` * Automatically set ``package_dir`` to reasonable defaults * Support building project without CMakeLists.txt Bug fixes --------- * Fix dispatch of arguments to setuptools, CMake and build tool. See :issue:`118` * Force binary wheel generation. See :issue:`106` * Fix support for ``py_modules`` (`6716723 `_) * Do not raise error if calling "clean" command twice Documentation ------------- * Improvement of documentation published on http://scikit-build.readthedocs.io/en/latest/ * Add docstrings for most of the modules, classes and functions Tests ----- * Ensure each test run in a dedicated temporary directory * Add tests to raise coverage from 70% to 91% * Refactor CI testing infrastructure introducing CI drivers written in python for AppVeyor, CircleCI and TravisCI * Switch from ``nose`` to ``py.test`` * Relocate sample projects into a dedicated home: https://github.com/scikit-build/scikit-build-sample-projects Cleanups -------- * Refactor commands introducing ``set_build_base_mixin`` and ``new_style`` * Remove unused code scikit-build-0.18.1/CONTRIBUTING.rst000066400000000000000000000071451466366435500166560ustar00rootroot00000000000000============ Contributing ============ Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given. Types of Contributions ---------------------- You can contribute in many ways: Report Bugs ~~~~~~~~~~~ Report bugs at https://github.com/scikit-build/scikit-build/issues. If you are reporting a bug, please include: * Your operating system name and version. * Any details about your local setup that might be helpful in troubleshooting. * Detailed steps to reproduce the bug. Fix Bugs ~~~~~~~~ Look through the GitHub issues for bugs. Anything tagged with "bug" is open to whoever wants to implement it. Implement Features ~~~~~~~~~~~~~~~~~~ Look through the GitHub issues for features. Anything tagged with "feature" is open to whoever wants to implement it. Write Documentation ~~~~~~~~~~~~~~~~~~~ The scikit-build project could always use more documentation. We welcome help with the official scikit-build docs, in docstrings, or even on blog posts and articles for the web. Submit Feedback ~~~~~~~~~~~~~~~ The best way to send feedback is to file an issue at https://github.com/scikit-build/scikit-build/issues. If you are proposing a new feature: * Explain in detail how it would work. * Keep the scope as narrow as possible, to make it easier to implement. * Remember that this is a volunteer-driven project, and that contributions are welcome :) Get Started ----------- Ready to contribute? Here's how to set up ``scikit-build`` for local development. 1. Fork the ``scikit-build`` repo on GitHub. 2. Clone your fork locally:: $ git clone git@github.com:your_name_here/scikit-build.git You can use the ``gh`` command line application to do these last two steps, as well. 3. Make sure you have ``nox`` installed using ``pipx install nox``. If you don't have pipx, you can install it with ``pip install pipx``. (You can install ``nox`` with ``pip`` instead, but nox is an application, not a library, and applications should always use pipx.) You can install both of these packages from brew on macOS/linux. You can also use ``pipx run nox`` instead. 4. Create a branch for local development:: $ git checkout -b name-of-your-bugfix-or-feature Now you can make your changes locally. 5. When you're done making changes, check that your changes pass our linters and the tests: $ nox If you would like to check all Python versions and you don't happen to have them all installed locally, you can use the manylinux docker image instead: $ docker run --rm -itv $PWD:/src -w /src quay.io/pypa/manylinux_2_24_x86_64:latest pipx run nox 6. Commit your changes and push your branch to GitHub:: $ git add -u . $ git commit -m "Your detailed description of your changes." $ git push origin name-of-your-bugfix-or-feature 7. Submit a pull request through the GitHub website or the ``gh`` command line application. Pull Request Guidelines ----------------------- Before you submit a pull request, check that it meets these guidelines: 1. The pull request should include tests. 2. If the pull request adds functionality, the docs should be updated. Put your new functionality into a function with a docstring, and add the feature to the list in ``README.rst``. 3. The pull request should work for Python 3.7+ and PyPy. Make sure that the tests pass for all supported Python versions in CI on your PR. Tips ---- To run a subset of tests:: $ nox -s tests -- tests/test_skbuild.py You can build and serve the docs:: $ nox -s docs -- serve You can build an SDist and a wheel in the ``dist`` folder:: $ nox -s build scikit-build-0.18.1/LICENSE000066400000000000000000000053141466366435500152160ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2014 Mike Sarahan 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 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. This project borrows a great deal from the setup tools of the PyNE project. Here is its license: Copyright 2011-2014, the PyNE 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. THIS SOFTWARE IS PROVIDED BY THE PYNE DEVELOPMENT TEAM ``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 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 views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the stakeholders of the PyNE project or the employers of PyNE developers. scikit-build-0.18.1/README.rst000066400000000000000000000137031466366435500157010ustar00rootroot00000000000000=============================== scikit-build =============================== .. image:: https://github.com/scikit-build/scikit-build/actions/workflows/ci.yml/badge.svg :target: https://github.com/scikit-build/scikit-build/actions/workflows/ci.yml .. image:: https://dev.azure.com/scikit-build/scikit-build/_apis/build/status/scikit-build.scikit-build?branchName=main :target: https://dev.azure.com/scikit-build/scikit-build/_build/latest?definitionId=1&branchName=main .. image:: https://codecov.io/gh/scikit-build/scikit-build/branch/main/graph/badge.svg :target: https://codecov.io/gh/scikit-build/scikit-build :alt: Code coverage status .. image:: https://img.shields.io/static/v1?label=Discussions&message=Ask&color=blue&logo=github :target: https://github.com/orgs/scikit-build/discussions :alt: GitHub Discussion .. START-INTRO **scikit-build** is a Python build system for CPython C/C++/Fortran/Cython extensions using CMake. The scikit-build package is fundamentally just glue between the ``setuptools`` Python module and `CMake`_. The next generation of scikit-build, `scikit-build-core`_, is currently under development. This provides a simple, reliable build backend for CMake that does not use setuptools and provides a lot of new features. Scikit-build-core can also power a setuptools-based extension system, which will eventually become the backend for scikit-build (classic). If you do not require extensive customization of the build process, you should consider trying scikit-build-core instead of scikit-build. To get started, see `this example `_. For more examples, see `scikit-build-sample-projects `_. .. END-INTRO Latest Release -------------- .. table:: +-----------------------------------------------------------------------------+-------------------------------------------------------------------------------+ | Versions | Downloads | +=============================================================================+===============================================================================+ | .. image:: https://img.shields.io/pypi/v/scikit-build.svg | .. image:: https://img.shields.io/pypi/dm/scikit-build | | :target: https://pypi.python.org/pypi/scikit-build | :target: https://pypi.python.org/pypi/scikit-build | +-----------------------------------------------------------------------------+-------------------------------------------------------------------------------+ | .. image:: https://anaconda.org/conda-forge/scikit-build/badges/version.svg | .. image:: https://anaconda.org/conda-forge/scikit-build/badges/downloads.svg | | :target: https://anaconda.org/conda-forge/scikit-build | :target: https://anaconda.org/conda-forge/scikit-build | +-----------------------------------------------------------------------------+-------------------------------------------------------------------------------+ .. INJECT-CHANGELOG Publications ------------ Please use the first citation when referencing scikit-build in scientific publications. * Jean-Christophe Fillion-Robin, Matt McCormick, Omar Padron, Max Smolens, Michael Grauer, & Michael Sarahan. (2018, July 13). jcfr/scipy_2018_scikit-build_talk: SciPy 2018 Talk | scikit-build: A Build System Generator for CPython C/C++/Fortran/Cython Extensions. Zenodo. https://doi.org/10.5281/zenodo.2565368 * Schreiner, Henry, Rickerby, Joe, Grosse-Kunstleve, Ralf, Jakob, Wenzel, Darbois, Matthieu, Gokaslan, Aaron, Fillion-Robin, Jean-Christophe, & McCormick, Matt. (2022, August 1). Building Binary Extensions with pybind11, scikit-build, and cibuildwheel. https://doi.org/10.25080/majora-212e5952-033 History ------- PyCMake was created at SciPy 2014 in response to general difficulties building C++ and Fortran based Python extensions across platforms. It was renamed to "scikit-build" in 2016. Scikit-build-core was started in 2022. Known Issues ------------ These issues are likely to be addressed in upcoming releases, and are already addressed in `scikit-build-core`_. * Editable installs do not work with the latest versions of Setuptools (and had issues with older versions, too). * Configuration scikit-build cares about _must_ be specified in ``setup()`` currently. * The cache directory (``_skbuild``) may need to be deleted between builds in some cases (like rebuilding with a different Python interpreter). * AIX requires a newer version of CMake than the IBM-supplied CMake 3.22.0 from the AIX Toolbox for Open Source Software. We currently recommend building CMake from source on AIX. We are also working on improving scikit-build, so there are some upcoming changes and deprecations: * All deprecated setuptools/distutils features are also deprecated in scikit-build, like the ``test`` command, ``easy_install``, etc. * Older versions of CMake (<3.15) are not recommended; a future version will remove support for older CMake's (along with providing a better mechanism for ensuring a proper CMake is available). If you need any of these features, please open or find an issue explaining what and why you need something. Miscellaneous ------------- * Free software: MIT license * Documentation: http://scikit-build.readthedocs.org * Source code: https://github.com/scikit-build/scikit-build * Discussions: https://github.com/orgs/scikit-build/discussions * Scikit-build-core: https://github.com/scikit-build/scikit-build-core Support for this work was provided by NSF grant `OAC-2209877 `_. .. _scikit-build-core: https://scikit-build-core.readthedocs.io .. _cmake: https://cmake.org scikit-build-0.18.1/azure-pipelines.yml000066400000000000000000000024671466366435500200560ustar00rootroot00000000000000trigger: - main jobs: - job: Windows pool: vmImage: 'windows-2019' strategy: matrix: Python37: python.arch: 'x86' python.version: '3.7' Python37-x64: python.arch: 'x64' python.version: '3.7' maxParallel: 2 steps: - powershell: | $environmentVars = get-childitem -path env:* foreach($var in $environmentVars) { $keyname = $var.Key $keyvalue = $var.Value Write-Output "${keyname}: $keyvalue" } displayName: Display env. variables - task: UsePythonVersion@0 inputs: versionSpec: '$(python.version)' addToPath: true architecture: '$(python.arch)' - script: | python -m pip install nox displayName: Prerequisites - script: python -m nox -s tests-$(python.version) displayName: Test env: # Used in 'test_platform_windows_find_visual_studio' # See https://github.com/Microsoft/azure-pipelines-image-generation/blob/master/images/win/Vs2017-Server2016-Readme.md SKBUILD_TEST_FIND_VS2017_INSTALLATION_EXPECTED: 0 SKBUILD_TEST_FIND_VS2019_INSTALLATION_EXPECTED: 1 SKBUILD_TEST_FIND_VS2022_INSTALLATION_EXPECTED: 0 scikit-build-0.18.1/docs/000077500000000000000000000000001466366435500151365ustar00rootroot00000000000000scikit-build-0.18.1/docs/Makefile000066400000000000000000000151721466366435500166040ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/complexity.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/complexity.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/complexity" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/complexity" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." scikit-build-0.18.1/docs/authors.rst000066400000000000000000000000341466366435500173520ustar00rootroot00000000000000.. include:: ../AUTHORS.rst scikit-build-0.18.1/docs/cmake-modules.rst000066400000000000000000000011431466366435500204150ustar00rootroot00000000000000============= CMake modules ============= To facilitate the writing of ``CMakeLists.txt`` used to build CPython C/C++/Cython extensions, **scikit-build** provides the following CMake modules: .. toctree:: :maxdepth: 1 cmake-modules/Cython cmake-modules/NumPy cmake-modules/PythonExtensions cmake-modules/F2PY They can be included using ``find_package``: .. code-block:: cmake find_package(Cython REQUIRED) find_package(NumPy REQUIRED) find_package(PythonExtensions REQUIRED) find_package(F2PY REQUIRED) For more details, see the respective documentation of each modules. scikit-build-0.18.1/docs/cmake-modules/000077500000000000000000000000001466366435500176645ustar00rootroot00000000000000scikit-build-0.18.1/docs/cmake-modules/Cython.rst000066400000000000000000000002211466366435500216550ustar00rootroot00000000000000Cython ------ .. cmake-module:: ../../skbuild/resources/cmake/FindCython.cmake .. cmake-module:: ../../skbuild/resources/cmake/UseCython.cmake scikit-build-0.18.1/docs/cmake-modules/F2PY.rst000066400000000000000000000002111466366435500211300ustar00rootroot00000000000000F2PY ---- .. cmake-module:: ../../skbuild/resources/cmake/FindF2PY.cmake .. cmake-module:: ../../skbuild/resources/cmake/UseF2PY.cmake scikit-build-0.18.1/docs/cmake-modules/NumPy.rst000066400000000000000000000001151466366435500214630ustar00rootroot00000000000000NumPy ----- .. cmake-module:: ../../skbuild/resources/cmake/FindNumPy.cmake scikit-build-0.18.1/docs/cmake-modules/PythonExtensions.rst000066400000000000000000000002711466366435500237570ustar00rootroot00000000000000PythonExtensions ---------------- .. cmake-module:: ../../skbuild/resources/cmake/FindPythonExtensions.cmake .. cmake-module:: ../../skbuild/resources/cmake/UsePythonExtensions.cmake scikit-build-0.18.1/docs/cmake-modules/targetLinkLibrariesWithDynamicLookup.rst000066400000000000000000000002461466366435500277140ustar00rootroot00000000000000targetLinkLibrariesWithDynamicLookup ------------------------------------ .. cmake-module:: ../../skbuild/resources/cmake/targetLinkLibrariesWithDynamicLookup.cmake scikit-build-0.18.1/docs/conf.py000066400000000000000000000214531466366435500164420ustar00rootroot00000000000000# complexity documentation build configuration file, created by # sphinx-quickstart on Tue Jul 9 22:26:36 2013. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. from __future__ import annotations import os import sys # If extensions (or modules to document with autodoc) are in another # directory, add these directories to sys.path here. If the directory is # relative to the documentation root, use os.path.abspath to make it # absolute, like shown here. sys.path.insert(0, os.path.abspath('.')) # Get the project root dir, which is the parent dir of this cwd = os.getcwd() project_root = os.path.dirname(cwd) # Insert the project root dir as the first element in the PYTHONPATH. # This lets us ensure that the source package is imported, and that its # version is used. sys.path.insert(0, project_root) import skbuild # noqa: E402 # -- General configuration --------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinxcontrib.moderncmakedomain', 'sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx_issues', ] issues_github_path = 'scikit-build/scikit-build' # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'scikit-build' copyright = '2016, the scikit-build team' # The version info for the project you're documenting, acts as replacement # for |version| and |release|, also used in various other places throughout # the built documents. # # The short X.Y version. version = skbuild.__version__ # The full version, including alpha/beta/rc tags. release = skbuild.__version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to # some non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built # documents. #keep_warnings = False # -- 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 = 'default' # Theme options are theme-specific and customize the look and feel of a # theme further. For a list of options available for each theme, see the # documentation. html_theme_options = { 'logo_only': True, } # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as # html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the # top of the sidebar. html_logo = "logo/scikit_build_logo.svg" # The name of an image file (within the static path) to use as favicon # of the docs. This file should be a Windows icon file (.ico) being # 16x16 or 32x32 pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) # here, relative to this directory. They are copied after the builtin # static files, so a file named "default.css" will overwrite the builtin # "default.css". #html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page # bottom, using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names # to template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. # Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. # Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages # will contain a tag referring to it. The value of this option # must be the base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'scikit-build-doc' # -- Options for LaTeX output ------------------------------------------ latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass # [howto/manual]). latex_documents = [ ('index', 'scikit-build.tex', 'scikit-build Documentation', 'scikit-build team', 'manual'), ] # The name of an image file (relative to this directory) to place at # the top of the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings # are parts, not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output ------------------------------------ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'scikit-build', 'scikit-build Documentation', ['scikit-build team'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ---------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'scikit-build', 'scikit-build Documentation', 'scikit-build team', 'scikit-build', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False # -- Read The Docs ----------------------------------------------------- # on_rtd is whether we are on readthedocs.io on_rtd = os.environ.get('READTHEDOCS', None) == 'True' if not on_rtd: # only import and set the theme if we're building docs locally import sphinx_rtd_theme html_theme = 'sphinx_rtd_theme' html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] scikit-build-0.18.1/docs/contributing.rst000066400000000000000000000000411466366435500203720ustar00rootroot00000000000000.. include:: ../CONTRIBUTING.rst scikit-build-0.18.1/docs/generators.rst000066400000000000000000000400151466366435500200410ustar00rootroot00000000000000============================================== C Runtime, Compiler and Build System Generator ============================================== scikit-build uses sensible defaults allowing to select the C runtime matching the `official CPython `_ recommendations. It also ensures developers remain productive by selecting an alternative environment if recommended one is not available. The table below lists the different C runtime implementations, compilers and their usual distribution mechanisms for each operating systems. .. table:: +------------------+---------------------------+-------------------------+-----------------------------------+ | | Linux | MacOSX | Windows | +==================+===========================+=========================+===================================+ | **C runtime** | `GNU C Library (glibc)`_ | `libSystem library`_ | `Microsoft C run-time library`_ | +------------------+---------------------------+-------------------------+-----------------------------------+ | **Compiler** | `GNU compiler (gcc)`_ | `clang`_ | Microsoft C/C++ Compiler (cl.exe) | +------------------+---------------------------+-------------------------+-----------------------------------+ | **Provenance** | `Package manager`_ | OSX SDK within `XCode`_ | - `Microsoft Visual Studio`_ | | | | | - `Microsoft Windows SDK`_ | +------------------+---------------------------+-------------------------+-----------------------------------+ .. _GNU C Library (glibc): https://en.wikipedia.org/wiki/GNU_C_Library .. _Package manager: https://en.wikipedia.org/wiki/Package_manager .. _Microsoft C run-time library: https://en.wikipedia.org/wiki/Microsoft_Windows_library_files#Runtime_libraries .. _libSystem library: https://www.safaribooksonline.com/library/view/mac-os-x/0596003560/ch05s02.html .. _XCode: https://en.wikipedia.org/wiki/Xcode#Version_comparison_table .. _Microsoft Windows SDK: https://en.wikipedia.org/wiki/Microsoft_Windows_SDK .. _Microsoft Visual Studio: https://en.wikipedia.org/wiki/Microsoft_Visual_Studio .. _GNU compiler (gcc): https://en.wikipedia.org/wiki/GNU_Compiler_Collection .. _clang: https://en.wikipedia.org/wiki/Clang Build system generator ---------------------- Since scikit-build simply provides glue between ``setuptools`` and ``CMake``, it needs to choose a `CMake generator`_ to configure the build system allowing to build of CPython C extensions. .. _CMake generator: https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html The table below lists the generator supported by scikit-build: .. table:: +----------------------+---------+------------+--------------------------------------------------+ | **Operating System** | Linux | MacOSX | Windows | +======================+=========+============+==================================================+ | **CMake Generator** | 1. `Ninja`_ | 1. `Ninja`_ | | | 2. `Unix Makefiles`_ | 2. `Visual Studio`_ | | | | 3. `NMake Makefiles`_ | | | | 4. :ref:`NMake Makefiles JOM ` | +----------------------+----------------------+--------------------------------------------------+ When building a project, scikit-build iteratively tries each generator (in the order listed in the table) until it finds a working one. For more details about CMake generators, see `CMake documentation `_. .. _Ninja: Ninja ^^^^^ - Supported platform(s): Linux, MacOSX and Windows - If `ninja executable `_ is in the ``PATH``, the associated generator is used to setup the project build system based on ``ninja`` files. - In a given python environment, installing the `ninja python package `_ with ``pip install ninja`` will ensure that ninja is in the ``PATH``. .. note:: **Automatic parallelism** An advantage of ninja is that it automatically parallelizes the build based on the number of CPUs. See :ref:`usage_enabling_parallel_build`. .. note:: **Ninja on Windows** When `Ninja` generator is used on Windows, scikit-build will make sure the project is configured and built with the appropriate [#automaticvsenv]_ environment (equivalent of calling ``vcvarsall.bat x86`` or ``vcvarsall.bat amd64``). When Visual Studio >= 2017 is used, ninja is available by default thanks to the Microsoft CMake extension: :: C:/Program Files (x86)/Microsoft Visual Studio/2017/Professional/Common7/IDE/CommonExtensions/Microsoft/CMake/Ninja/ninja.exe .. _Unix Makefiles: Unix Makefiles ^^^^^^^^^^^^^^ - Supported platform(s): Linux, MacOSX - scikit-build uses this generator to generate a traditional ``Makefile`` based build system. .. _Visual Studio IDE: Visual Studio IDE ^^^^^^^^^^^^^^^^^ - Supported platform(s): Windows - scikit-build uses the generator corresponding to selected version of Visual Studio and generate a ``solution file`` based build system. .. table:: +-------------------+------------------------------------------------------+ | | Architecture | +-------------------+------------------------+-----------------------------+ | CPython Version | x86 (32-bit) | x64 (64-bit) | +===================+========================+=============================+ | **3.7 and above** | Visual Studio 17 2022 | Visual Studio 17 2022 Win64 | | | Visual Studio 16 2019 | Visual Studio 16 2019 Win64 | | | Visual Studio 15 2017 | Visual Studio 15 2017 Win64 | +-------------------+------------------------+-----------------------------+ .. note:: The Visual Studio generators can not be used when only :ref:`alternative environments ` are installed, in that case :ref:`Ninja` or :ref:`NMake Makefiles` are used. .. _NMake Makefiles: NMake Makefiles ^^^^^^^^^^^^^^^ - Supported platform(s): Windows - scikit-build will make sure the project is configured and built with the appropriate [#automaticvsenv]_ environment (equivalent of calling ``vcvarsall.bat x86`` or ``vcvarsall.bat amd64``). .. note:: **NMake Makefiles JOM** The `NMake Makefiles JOM` generator is supported **but** it is not automatically used by scikit-build (even if `jom executable `_ is in the ``PATH``), it always needs to be explicitly specified. For example:: python setup.py build -G "NMake Makefiles JOM" For more details, see :ref:`usage_scikit-build_options`. Linux ----- scikit-build uses the toolchain set using ``CC`` (and ``CXX``) environment variables. If no environment variable is set, it defaults to ``gcc``. To build compliant Linux wheels, scikit-build also supports the ``manylinux`` platform described in `PEP-0513 `_. We recommend the use of `dockcross/manylinux-x64 `_ and `dockcross/manylinux-x86 `_. These images are optimized for building Linux wheels using scikit-build. MacOSX ------ scikit-build uses the toolchain set using ``CC`` (and ``CXX``) environment variables. If no environment variable is set, it defaults to the `Apple compiler`_ installed with XCode. .. _Apple compiler: https://en.wikipedia.org/wiki/Xcode#Toolchain_versions Default Deployment Target and Architecture ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. versionadded:: 0.7.0 The default deployment target and architecture selected by scikit-build are hard-coded for MacOSX and are respectively ``10.9`` and ``x86_64``. This means that the platform name associated with the ``bdist_wheel`` command is:: macosx-10.9-x86_64 and is equivalent to building the wheel using:: python setup.py bdist_wheel --plat-name macosx-10.9-x86_64 Respectively, the values associated with the corresponding `CMAKE_OSX_DEPLOYMENT_TARGET`_ and `CMAKE_OSX_ARCHITECTURES`_ CMake options that are automatically used to configure the project are the following:: CMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.9 CMAKE_OSX_ARCHITECTURES:STRING=x86_64 .. _CMAKE_OSX_DEPLOYMENT_TARGET: https://cmake.org/cmake/help/latest/variable/CMAKE_OSX_DEPLOYMENT_TARGET.html .. _CMAKE_OSX_ARCHITECTURES: https://cmake.org/cmake/help/latest/variable/CMAKE_OSX_ARCHITECTURES.html As illustrated in the table below, choosing ``10.9`` as deployment target to build MacOSX wheels will allow them to work on ``System CPython``, the ``Official CPython``, ``Macports`` and also ``Homebrew`` installations of CPython. .. table:: List of platform names for each CPython distributions, CPython and OSX versions. +----------------------+-------------------------+--------------+--------------------------------+ | CPython Distribution | CPython Version | OSX Version | ``get_platform()`` [#getplat]_ | +======================+=========================+==============+================================+ | Official CPython | 3.9, 3.10 | 10.9 | macosx-10.9-universal2 | | +-------------------------+--------------+--------------------------------+ | | 3.8 | 11 | macosx-11.0-universal2 | | +-------------------------+--------------+--------------------------------+ | | 3.7, 3.8, 3.9, 3.10 | 10.9 | macosx-10.9-x86_64 | +----------------------+-------------------------+--------------+--------------------------------+ | Macports CPython | 3.x | Current | Depends on current macOS | +----------------------+-------------------------+--------------+ version. | | Homebrew CPython | 3.x | Current | | +----------------------+-------------------------+--------------+--------------------------------+ The information above have been adapted from the excellent `Spinning wheels`_ article written by Matthew Brett. .. _Spinning wheels: https://github.com/MacPython/wiki/wiki/Spinning-wheels Default SDK and customization ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. versionadded:: 0.7.0 By default, scikit-build lets CMake discover the most recent SDK available on the system during the configuration of the project. CMake internally uses the logic implemented in the `Platform/Darwin-Initialize.cmake`_ CMake module. .. _Platform/Darwin-Initialize.cmake: https://github.com/Kitware/CMake/blob/master/Modules/Platform/Darwin-Initialize.cmake Customizing SDK ^^^^^^^^^^^^^^^ .. versionadded:: 0.7.0 If needed, this can be overridden by explicitly passing the CMake option `CMAKE_OSX_SYSROOT`_. For example:: python setup.py bdist_wheel -- -DCMAKE_OSX_SYSROOT:PATH=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk .. _CMAKE_OSX_SYSROOT: https://cmake.org/cmake/help/latest/variable/CMAKE_OSX_SYSROOT.html Customizing Deployment Target and Architecture ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. versionadded:: 0.11.0 Deployment target can be customized by setting the ``MACOSX_DEPLOYMENT_TARGET`` environment variable. .. versionadded:: 0.7.0 Deployment target and architecture can be customized by associating the ``--plat-name macosx--`` option with the ``bdist_wheel`` command. For example:: python setup.py bdist_wheel --plat-name macosx-10.9-x86_64 scikit-build also sets the value of `CMAKE_OSX_DEPLOYMENT_TARGET`_ and `CMAKE_OSX_ARCHITECTURES`_ option based on the provided platform name. Based on the example above, the options used to configure the associated CMake project are:: -DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.9 -DCMAKE_OSX_ARCHITECTURES:STRING=x86_64 libstdc++ vs libc++ ^^^^^^^^^^^^^^^^^^^ Before OSX 10.9, the default was ``libstdc++``. With OSX 10.9 and above, the default is ``libc++``. Forcing the use of ``libstdc++`` on newer version of OSX is still possible using the flag ``-stdlib=libstdc++``. That said, doing so will report the following warning:: clang: warning: libstdc++ is deprecated; move to libc++ * `libstdc++ `_: This is the GNU Standard C++ Library v3 aiming to implement the ISO 14882 Standard C++ library. * `libc++ `_: This is a new implementation of the C++ standard library, targeting C++11. Windows ------- Microsoft C run-time and Visual Studio version ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ On windows, scikit-build looks for the version of Visual Studio matching the version of CPython being used. The selected Visual Studio version also defines which Microsoft C run-time and compiler are used: .. table:: +---------------------------+-----------------+ | Python version | 3.7 and above | +===========================+=================+ | **Microsoft C run-time** | `ucrtbase.dll`_ | +---------------------------+-----------------+ | **Compiler version** | MSVC++ 14.0 | +---------------------------+-----------------+ | **Visual Studio version** | 2017 | +---------------------------+-----------------+ .. _ucrtbase.dll: https://msdn.microsoft.com/en-us/library/abx4dbyh(v=vs.140).aspx Installing compiler and Microsoft C run-time ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ As outlined above, installing a given version of Visual Studio will automatically install the corresponding compiler along with the Microsoft C run-time libraries. This means that if you already have the corresponding version of Visual Studio installed, your environment is ready. Nevertheless, since older version of Visual Studio are not available anymore, this next table references links for installing alternative environments: .. _table-vs_download_links: .. table:: Download links for Windows SDK and Visual Studio. +-------------------+-------------------------------------------------+ | CPython version | Download links for Windows SDK or Visual Studio | +===================+=================================================+ | **3.7 and above** | - `Visual C++ Build Tools`_ | | | | | | or | | | | | | - `Visual Studio`_ (2017 or newer) | +-------------------+-------------------------------------------------+ These links have been copied from the great article [#alternativevs]_ of Steve Dower, engineer at Microsoft. .. _Visual C++ Build Tools: https://visualstudio.microsoft.com/downloads/ .. _Visual Studio: https://visualstudio.microsoft.com/downloads/ .. _Windows SDK for Windows 7 and .NET 4.0: https://www.microsoft.com/download/details.aspx?id=8279 .. rubric:: Footnotes .. [#getplat] ``from distutils.util import get_platform; print(get_platform())`` .. [#alternativevs] `How to deal with the pain of "unable to find vcvarsall.bat" `_ .. [#automaticvsenv] Implementation details: This is made possible by internally using the function ``query_vcvarsall`` from ``distutils._msvccompiler``. To ensure, the environment associated with the latest compiler is properly detected, the ``distutils`` modules are systematically patched using ``setuptools.monkey.patch_for_msvc_specialized_compiler()``. scikit-build-0.18.1/docs/hacking.rst000066400000000000000000000021401466366435500172710ustar00rootroot00000000000000======= Hacking ======= Controlling CMake using scikit-build ------------------------------------ You can drive CMake directly using scikit-build:: """ Use scikit-build's `cmaker` to control CMake configuration and build. 1. Use `cmaker` to define an object that provides convenient access to CMake's configure and build functionality. 2. Use defined object, `maker`, to call `configure()` to read the `CMakeLists.txt` file in the current directory and generate a Makefile, Visual Studio solution, or whatever is appropriate for your platform. 3. Call `make()` on the object to execute the build with the appropriate build tool and perform installation to the local directory. """ from skbuild import cmaker maker = cmaker.CMaker() maker.configure() maker.make() See :obj:`skbuild.cmaker.CMaker` for more details. .. _internal_api: Internal API ------------ .. include:: modules.rst .. _internal_cmake_modules: Internal CMake Modules ---------------------- .. toctree:: :maxdepth: 1 cmake-modules/targetLinkLibrariesWithDynamicLookup scikit-build-0.18.1/docs/history.rst000066400000000000000000000003421466366435500173700ustar00rootroot00000000000000.. include:: ../CHANGES.rst History ------- PyCMake was created at SciPy 2014 in response to general difficulties building C++ and Fortran based Python extensions across platforms. It was renamed to "scikit-build" in 2016. scikit-build-0.18.1/docs/index.rst000066400000000000000000000010251466366435500167750ustar00rootroot00000000000000Welcome to scikit-build ======================= .. include:: ../README.rst :start-after: .. START-INTRO :end-before: .. END-INTRO .. toctree:: :maxdepth: 1 :caption: User guide installation usage generators cmake-modules contributing hacking authors history .. toctree:: :maxdepth: 1 :caption: For maintainers make_a_release Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` .. include:: ../README.rst :start-after: .. INJECT-CHANGELOG scikit-build-0.18.1/docs/installation.rst000066400000000000000000000036761466366435500204050ustar00rootroot00000000000000============ Installation ============ Install package with pip ------------------------ To install with pip:: $ pip install scikit-build Install from source ------------------- To install scikit-build from the latest source, first obtain the source code:: $ git clone https://github.com/scikit-build/scikit-build $ cd scikit-build then install with:: $ pip install . or:: $ pip install -e . for development. Dependencies ------------ Python Packages ^^^^^^^^^^^^^^^ The project has a few common Python package dependencies. These can be seen in ``setup.py`` and ``pyproject.toml``. Compiler Toolchain ^^^^^^^^^^^^^^^^^^ The same compiler toolchain used to build the CPython interpreter should also be available. Refer to the `CPython Developer's Guide `_ for details about the compiler toolchain for your operating system. For example, on *Ubuntu Linux*, install with:: $ sudo apt-get install build-essential On *Mac OSX*, install `XCode `_ to build packages for the system Python. On Windows, install `the version of Visual Studio used to create the target version of CPython `_ .. _installation_cmake: CMake ^^^^^ The easiest way to get `CMake `_ is :ref:`to add it to the pyproject.toml file `. With pip 10 or later, this will cause the CMake Python package to be downloaded and installed when your project is built. To manually install the *cmake* package from PyPI:: $ pip install cmake To install the *cmake* package in conda:: $ conda install -c conda-forge cmake You can also `download the standard CMake binaries `_ for your platform. Alternatively, `build CMake from source `_ with a C++ compiler if binaries are not available for your operating system. scikit-build-0.18.1/docs/logo/000077500000000000000000000000001466366435500160765ustar00rootroot00000000000000scikit-build-0.18.1/docs/logo/scikit_build_logo.png000066400000000000000000001244371466366435500223040ustar00rootroot00000000000000PNG  IHDRdIsBIT|d IDATxw\U)ْ-%! $P +RDYitٿ~ *_A;B$;c2swg39cBs߳;{`7s|'ؤY2e. 8"S}7PdxËVAZ~ܙqqqʦ`(-uw&ȜEVR$qÄ^@Znx1𺔒fqqq[5d"1v ^J|'KuF@>7C遇c͎ZAgu_d&Y}=+cB)U^X888NeLITОq+fXfgy1hϣPpԶoRsɢAۣ8݇z"Hy/K_>Ɍx= ݿ81ApX{kfS;8882]?} 6Id=nkAeS@p,JEǛ&O:Ժ^P(AyRH\nz!9~6$ F,.>|9F)0D 1qqqT QK( X~5RS!Tc9!5rUVJS_ 5@w[ AڿQeX=O6m OG"5J)^VЎ888N؆L}ᑸyl3$)ѝSK|=ۂ 7lƎ;s`bD ^/׭?l[Gl,}vqqq*XE6d;n}LwI52:{u/E?HEktbu\F(Gk z]_8">#_ VxՄ&E_JIE(-Ϟ?BqqqqʭqŽS?) C,F*қDAX{usR*mFv=.@2񿂠+,"jܫFm{1{p;888kR_$ }Q["7#9c;sWc!?K)qk|`Ta+G> ƿODFt]D$|ߣ#uvqqqvҫxW/LJS͟MLنTF!& S&zTs aRqH't9v Qa`Wn`g~Mf, ay/n/y888{̢/d|-q"1B110Q6I$M>pR5-Eȁ-6*'INHMvx+;WGp->$x 888.*!KyyEh wG$Jz牑5Rr_AcA_xO!0>=bv~Xkoa9XBQ嬞HSߠ.$5:rjqqqg7R u Eǯ1[PCҚB1e Kپ'j/85"01_iE./B7~#*&3Rb`/睌 ޞ;7888do,i*mIӋ渮bvULm$Bq6֕?5ƀP z/'9;uj K+4:jԷK888v(Ba=a+fNDv`IZ %_ lv Ag#r_zjO2~Th3\T;888]w~R)Ĵ'#-eDلloeɓ)f/cH"􏦀J)u^?Ac%yID "\s'.$k=BmfxqA3CXSd 539.`gR\A981Zl\|~m: -wCROtwɆLCƿ@[#q=[E%豒=,CRj/d}?M+ry%`#O{ a?%l]Kl,~qqqǩ\CvGa K&`^ Q6R*\!(}5!~!IWzǮHI`Q>t!AE!>…S888Nإޕ/h`'EaApSP1[\'@0Eo"F" (n.TJY]UBaw}+,$jpGFm{`ڪ qqq].Ր($I؂ܵ^l%gzy >qC,fgG5AjXĦυRJ\f 3`0 #g`CzuUu6!qqq].u?yN?'22:fVEP0 WL<铥\_$N! A s 3]|61+@7Pi&&"<¥<888%2::q"1^TF3&1-$B$ m*y{`ԁC--YUraaS$ H#5Ooae6 ;z_lo_O³1 eeD=}jvP93`.bQ \gw:qߓZ hlEBcʔ%?tW "R23!L%9GX9b?0{~࿽3SLrgst8jhT-:$-g)>c׌STSlvDS 8Y']ό(ES8hXOYZ)IӋ渮bvULm$Bq6֕51=ÿ@xiD.$'| }j˃ kʝlЀO`GEH*w,qSHDaQqZpF_{4B-g" TI8'Q5ZW8ېVh!E!ma; A2^LU2IrAu;::`gC, 8bc0Wr;j,?~T(:rKʝqt2en+T8sцM͇DqݡXcPEI9M҇8Ny>muq?'T@&sW6l,["YVaOԩm+f 2džQIiPOZB{H i- Ɣ93x=?=uԶI;8o5›IQoG?;~;mRAd+I兒(ăO禛L|WLClegrtqvZז5֬.mySl8M%u°!GA&1=㶸b1G~&^TqmcJ+t7(t`G( b{/`O@ $FZQZS9{pg&IWc9e[. ,Z3/GP e8ro8I~'l0ILF1;X]Cf~.w'B*jB1`yJZk|Ժ\" BׂnI!0?6afc,Tge#ކ>qH)UgWrr'7rcKT;u:vHؖ𥢇&Z}cqv;7r|Ņ#O[)Gs02UBaEcx$q=VQ :Rjڗ 2UƉAmAL7e`'|sQ`bDReމ`r'{]?%l]Cl,Ӟ뺮ܹIGl J. R2Ȗ[(\(KhXkiOR8[_E~Oq^ְjȴwv(LI 2:=d?HEE릴M:ԺBaZxfXZ> q>#_ VhW 0HZԥO-Xr'stHS!$S4*q?_8Gh))Fr,W#9얶$ɗJZ-~^;-Xæ!Kq]$h E lEp%l%gyy q#K^dIEl^ӹP_J,z{01-dz&_dEDFt_D$c:r]T[|/*M3_n2\n>$  5(o8}qgrQR OS8/1,zKszWIXn'BQ|lӼm44B)S&}y90 @ߵ?@c>10UI)Z&zɏkQL0y#LLEʒʝq-VxgCA'_{f27۲ )Fr-xXa\&se_cIUtIPE /DHl67Z7S $rE;P I^PA߂JIiZ?IDh )cǮg?jg⨨&U*t9p#gpJIvH ?|-oN?8#J-j7qLFoqACF qi=^iG ]Vj\GǭcfT3+&`G͊@7;Q*HWR;p:SMz[9SkyY'-r1z?S _[I9Sb{~KVAQ?M'_LkuqÛO;k(?-t&hЂuSSڐuvk$ҋ,磵пtjۤKtS0aJb;~@Gf̝l`W"E#D6 .<)Plf[b?TE Q-ir7.Rnrd))lea,*y$}Ԣ` E.$9X󎳱UuX!z7Ue3u䏼G%ƣu59KDQk8.n(7[^^ 8yț˝q]y2BǾ!ZnYn5  Ny ޷˥r<>J,rc 5RH< -lMk Mu;y%a˸éYu z^ ƒyz?J\Ո:ӑ^2888k(KC&7eJ)!%IˬB,<|{<%$bׂA}飒p.aM 2 ^yS&_T|888E9::~lc=nk+S@pgJEE[&Sj]U}3 ÁJ] 4WF3 gIs1Q>KoB ^9 H6vdaUCl&ξF&1LceN888 !muѢ7j? WQc4c rCDkMD(n.[XE,ts '01 +$kYv9Vc4,8^oaIUDeF־BI0ڣeOix>R\grtqqqߐ6dj֢1<UT8+~ 9tJٱt S}HD=}ӊ\^qvY#~杄 $`BF!ƟPŒL: BRcT-.'غX|ߧ=u]:888k2w? dm\CuEpe)S&\j]KV{ *aglv-;?qQ ?ׇu{h偐˵ʓH"y˜y‘H#>h@$] RL]ԢE;8883xduE܇;! C@pcH0CqA֛H!^xGuseJizVUx}s-`gYu+nkm-GOP;x]Ac_zV6Y7|dcjԀFm{߰32'wl>j+`!$20Ol`Mh].l75_zs"# Sad O5]w[H;T 5}Dh!ku@ ZLAam 0m>t8B$2BOD jbB4,ؼ-š~]iMi9e *! $IlE1~wݑGUJ͎kL>0Bl[ӹP_J,z; S Q6 ƿ꧙hZϾfdZ;dS &{d rWY@^ҀDj/bexzLG)m>X軝5$!!丌mJW/"!?L&ULti֘5`W$[~v_ juJM JV-v505*Ď?T{EO -`:k LŖkyl(_3{j2Iq7)N_M9na̱d>Wl닗5XQŷg΄WjIIAo!{㌕qҚFx{{B2Z6x k?;1zǵ: IDATTTwEuQ`W$rE/%?{ܢwhR BqQ)GKƙ{~DMb{'Q_~R,M̱dΎ*QbxguLoՙw!AHWF a<2X;EB=$q_{޳ Úv˛oezi/1 3QJSF*ct6[iVi M> )!HBTヘ%ywLUUxͩX"9w ~Ĭ= fLf#M[LF&1+ (eFOY4w>w/l|)hWRQBQZW۰&cv SxeOL!'~MXOB!TZ)>Z̜؋II?d0#WR:k ؛=),]pa`̱UWe>P}+Z֚?ߐ K8Cg_!Oׂ7!D 85?kxRd!,Ė؆;FoRJC!z_hQ h1M2g JܗsSBP%.rDl vXӋO@̎׏jׇ/x i7a__wmao1?|/J%414}JqzD T$&kXHvBJ#_wd+lߛM8=oB }OU-_$M|wT: jCQ15c d~H ?/Ȍ ~%b`D4cDZG6 a`N1=iy4=PvW}lőլ=nBـz"O]E>|_5tvۇ0zERe>kY%Dx-#RM k̭i)Cuȟ_shmNjF) 6<,޿}?ܚ$qsy(,zfdK Ns+Ɨlw^wQ'%a8}头9g5#[ |5%68B{_ZOtDMV{HB8Y{jw jܥ>ټgv_g|Ӑ`uvki OjYl| hlApB5 JkNmtI)5wNAi;e 0bɑ@ao$;=Hxd+~Q>2XBP y|=Dm̡e_`_ehzddGKs~zOe/FBl)r$qxX-`ӥ{۞+w=A=fT*̋_"zA朞K'n9 3G_d.HMKAl+bEx9[kuІ)p^{\uШ5Z)=%Tk{_:g,a)=3G_;)d+_l)ޘ;gP{P2Kr]WA0%czQQW!͘"@ #$q:Ժj IQZ^ $_h/ k*gDu`"ԑ4SSqZ]PB#O((PZ2SY{\ԳW6h$\Z߶҉6?kߔ3S~֘NGIᇂ9dONظ7Z}E&SuA#nN_њB4QbArp^ zl23$,"ϝaW+w.q^^~eAUW~a%gzE!Jh Y2ay?OۍhxJ-;&^TDQmcK̷0kf*f!|=A`OқȤsJ4<Fj XBFɘH!lLCbb$XH#c~J|%ḻUIX7uz#c`g^T^)e[ mQ2pA o3lIu^]~z뗽zFR3b#a1's`-xJz=Y8s̷L.`[V;a}8,?{|{x#/ U W\r`#̣s{J<,/,bTc9DD)E'/n.׹Xk/R!\x:)$z#lmVHjN~yA+^0_2y=s]<0^Ոo)5bnmYJ@'Qbў~džV,g1'| 9?wbL;qg3 X|Wn/̦jj *_cEcx8gT81UA`|0 smWJ͎gg >K+ryya8Lhw"&k&0^ #*Crl8{yi7U5ԾMWҀ|`bc 2R]TUݵeR Lfg>*|ԈOb{ X{fgUk&R[kˎgkەh.g" W#N%jP[7;NnKCi(œj%O ('13V;/q>Bl grgq^^~z9^ՈoҌD}[kl (U_X%kߥ+ƋZ9.wǩdY{jZ%[&i9*?kbIe]<}}$h "*yz,}}׽MuR3׾AjB!C\J,Z%̄)ǨpG&3~gkdZuiՃrLYx ܊A@dV_ ]+N}!ex\mS&>qW3{K1L-OB!6<51Oa^VB"J9F1* tҍ{S ?r^ֆc*iJBx@kgK^I U)<۷~n[]6ز!?k/VU< c,(Zɉ>ؐ;F s-< Kɉ=}Uܣ9g%M᡺ sͿl^<kEj>c&EKjt~ʯt! A)ԝ_ iE./@rGvLUU_ hF$1kr%cx3*ӻ:f 2P4>Am@` cwF?yԩhJ}LkH1$%-W5]M1u3G_Ge61]g-4\bZ~~|0L!@)߱0=`ځb-B"јB ܜ"R1qQUuҲᢦ蚆{ZPzӥdx'QlA\Cf R1jLjRc!NUƚ': k,~%?}䗅AIV3lw\/kqBl޻7No|u8Ie꿓H⽥>c&1+m(^{WZQ2hnRg-@#;ΗD.!8L&8*,H,jB119Do! $mAZ=`|*dtHNe୿ y5VOYl{8}MkZtĵ/>',6zcYǐIQ{<|%F%d%7ObnߐxZTc̖¦ 4]O~yù##fO';n6_q`)^Wt>KF%IϷ=7du2pIBSmB3UKIxJ+0[(3Xz.n:KV7,fL%Ws^5>O 3~qvCӯb\(9((;;Ͽdu h9"JgL!J5d'%9+)x2Ral&?޴7z^aRA7xx魟V#})DTD8sS O0p{8Us,I牑O_2#8A[wUH3&|;m.v>8&ضb1=iy=<{bI>oz6=CVc I?m}HPVOkGECm';Ƅμ/)'<0znN_ѐ^l} uƯ[+@vc3~Ox NEwR/] ԿMZMm_{ S..l:KV77\\!7K7G(Y߳#Ye?3} Z)om-xJg{i3oN6^JB@Qk*qSDU9!.yF{gwڗ𷤖ǯuc 8Co5tjۤKxO􃪓(~)wZM$N8)h}xkl-:! .IڣQP(.-gہsm%غck@`/睌z io|O*rRq*)Hb- g<^TX6Z e j ߚv3F Nyu?-39fijٙNN_QW%cүkxkL+_~M獳V4y}=4_zh¦{3VF35]3qBj+jgNCX_iM'ZqvC¯1zJoK3VKﲳV,;}yu!nU;6J;ݐY}Yu&Ӗ1(~QW!͘2 #$IM:R-7$IG=sց҈\fVo. 0ʣNԖ-o25CݳXH|}$5aqև.ЀO`GG(.Ï3fy=L5khܹ2;Xk髎cښƫnJ`][oxGv3GZedvvʽ|Z\kZfg`sv6W4ܖZ3F ѝ VLfLFf>fvsg/Y.ĬuZںoZqv3g6dC -k.]tN_aoc2;ݐQB\X(n Ta+f#<(NJrzDF_n=d=lv,A x>"LqӿɳEV ;`-F{4wmޠiX2LQJUutv/ kFZd}3O74f/o $q`X34/Dq|_vQM|_z_[ӥ6N_1]+?=RF3׶+QT1@OٙoIً-iԡ>~vƊ Q)H)rKF~6?+ͩ>bto7)#?{gWUsιNdJ@ʢWůWA6M(7Q}ki.Ifg{cZXJ{Izs>̽|γ|{ma$ Z+dȬZZ)$ХxUc.| eYp]ϭ kŊg>+(8}OCt(poు&~zI@`);݄ K0ɛ` h;"ұB:tϻm7[۸xGSӟL]j)#kcxQ3|M>_J^5Ɋ_7LG2- 't$޼,x秧pWa>9$ONc쩽dqÉEDD63ގo57qݻb!n\!=;"bt6N0hmu3M'Љ7n8͸)ۆ̪Z>=~ T) qb5b,$|ﮯ;(pTe.`ِ*ds &] #-TsK9](qF1HoZ ?$8x ֨z0##|`G_BHrE:Drv0 IN`7>vΆae87&6طRvW#3fR( όEWƣZ  Ѹ܌F0bFɡ؃ |8J'aOV]qG(A_#H2e IDAT NuրZw@j-DVox/‚Bge.& )R3!Up~Xc1Mǜopb:QK0}AO{;s+t\V:eaXDyEDDK8{hxgdX}| PAYc("":U]:>[tIu? 'bpp]5=evː)(<ᗣ!YV  ˶o:H̖+킢`>⧀χy`|ƙke`*Lxl XJE灭)nY/$PE1e@].&|pgߕ-s,*i`a/KXc +&6_R7""ob .t`3eUR)TœzD{`9̉ p)bݛf[5Kvڃ6=6{Q!dX[n۱?XըukKaU^P5:ZmM t6E{o}h5zyhf ޚX&x!J~*:vń{֞0 H9Q xmE*4 )!1*_8]2Jޡ?{q >'{V2;6wvglYX mԽϟb3CRcٗ@x\&ʆMF߿ |N.9 0Zـ# BlSmX溑; kAl{Ҫd؁ t}fPRLL5׼1@ʳEA%?UqC2OT!1R|cRGۮOTP%okN=6!Mmc4]kK|O,%? ;QUk,:SGt N*Շ8y d^ 1?ݙҊy jBRcRc|FHT?y# 1鹇bը\uAƠ!6`7k1e2*݅ 8SQ@ƃy_xù5p*7 !^_ GWna\? 7n8"Q{ !16ܝX.R IB7I+-  5 TYKGrkD:ϐBSX^qgd[‰>H! ztM(SH5Aa4  X0x]! \*2.0BC|DȐlBkB;^|aXk͎!u*K)K~1^0<)UՃBҐJ!μ0oN5Ab.3,C!PwXsIC?D~ƪƸ4Z9CۑW]蛵pzPҀ24T>q7aα+V\k]#Ei|7S3Db/)By?߹x1:HLtV\/U{̘0"02ov-""bW6eZP1$n2Ŵ0DD+$ռ0|mުk [ìZY+VP34,3ybƸ .}IZ:8h\Z@ِȼ XcЮa`p=G`Be!{[3r-.X@[Q:HMK_z&@cW81. (RlT?{;}%<σ e`*aGWg [l [GK߼4C,7LA0?I#rKf H"hmʷ{_ 'bx:"2PK\Pu@E$o 1*y"*=Ydem>ED 1%蓶 E(dsY7Cƒʌ"_(D%c 8JeYH^4nkkߔey!6(ʓ,+co'FA: &62EvMGgb0* ( -A== (S?=E[ղfŜ1Ize'k#v3z[&PNZ/|{Vw|=GF !k(N.AyOyFW֌  ö%?q}ꏡh‹t&k)R۟kA\kَ rc5BCu1@K*$Ԏ0pQ@[/ӌěG/1$@Ơol5 ^F(x/ ZZWZ_7;OҺ͌ےXi?7= g'5 KY'YUU'obɦסA *AD00kCO0J5׾9fv\,-M.1ʟ2)|ǭw`cش>+8\j!=Ry]Oʦcx$@A~T*xl ±+o̘ HέeX>u)VU5<1ѐ*;f"Ʌ?_ؠu &j!|^9kQb V?tĜ>ZV8~r-lMo^\:mfgohc;lIbx'J~~:@s*,=f@VR4 0Xn,1f-l*@a Ԑɚ<"1Ha7eUK- n0z}zr 1o!5p$]L h,4 @GA-d&1ϧ a@1߆Hoε E;eb.H4L{(fyiM$ubW}szy_nɘ&D#Q[SRSU#"3BYfǞkm<; EKDDA$+` 'pH^o; g~:=rwq8|cbժs3ƛ{׶,g s#vQs3ZߡSdI|P3?|$x>̩?eʐ/[p2߆ %N.aN`jlYXLuK2UH 3rjuBk %Է ېCZW'P!O`@/pDz,AK6DzڏFD([4?qfciT?l<^CǬPO/b|od:UԾBjٯ}`uMoձhJ-ShV.޶۱/L-=YQW& ^gG?o"'hW Ȼ>XC͂A=A$q+\PypՌgl ʗa"Bȋ0}ު0BwB\ͼ*D9yEx8xRxU'~p`ߥm}E%Y8S LWACx06& -SAl]? f bըZuAƠf |Km_5lsj4\ⵖ"T!2>CHH.pUИҶ B1"hplOqʍ[=2H\K >,Ǚk-E>GbPvJ9=k?_kh#{0|˱h?ʑ}8}ʖՁJ5>YJ҂>7@aI-xrzԍ-Vh_p!H _*cv*(zy) -B!r}$9tJ әgg/͵\h0'3s1fX4fSX|}gem"Jێd<[N8BOR\Xrc<à2xD ӳ+,;OB8|a'ۓ0P0<hIиsԝynRS_yb83AԾD@:z|?CbGDz#{$TH^kPY A}WQ8jAz==C:,׺rID Rü%frfg|28ÍC2_H(m;"t1EDɦ-!X=+6vzv-8CBG١҈at߆ r>a1ax%qѝ7y0=\*4γѝ' ,-j]J){U˚AfB }@!(̀Uk/`g~֢o2 VσwCH(B{h;K0샅C>=(%Y1ײ -&\^%c'kf3Ŝ TqſC ta8V]aR^7(xw~~HF_N'da AB (DC7DtDrr *h(_. |ӆ8TpĔ)n}3e7<>ga[!)1WA>y/,1z5 )CX&6io3Pd `N Y?: 2oaK)мZX'[ve2?a6cjl3헖) ȨijDhXeW*X7`+JqqUXہUTl}'Gn,\s$D%H~hL}Y7$0e_V\Tm7)V/f&1K0~%pK~O87~3[Z[Қj*u_!~͠-Dׂ!ܧ`3uI ( U-~FYg:*<\cg!(pX֎CZ-'̄2>c,)Қ ElĸC5& D:ueRu`e $D~̞E ɿ 0F&1iyLg, BP#DDdq!'8mX7l)yraǵU !\Da@UZGLAŹְ+گ(;VIζ5)W Rّy^7^b'A}L"0~6X-e͏.>c91=P2L?}="ؒ3t)3d8N% &\kÅ5?`]ι=udD4q}Đ1 kA$jBӓDu9a6(m)%|oP 3veӭ<ׅ+V$朹s/v%0'|%9GM|;k!b$"!vCxh;BkF8?II@ׂ3u譿PxaN(;&Dm_hpd|ڢt_,?3cQ#7(N6Nh DQAp$ˉ wM/x00Jgb ekÆ,1#h0S P1!`kCR.)х,vvt]W|N+OJ$Nx/1~4n}}Zs1igoʓǏ{@lг< ^cǐYÏIVvX XU~,>lg&P ]tt `A}$7IkW)AۧȄoMڼ/.E K|߬7?pW$;N2p=p,Ϋ %`Da\[/)cIQsy IDATF  .UÃ(Aqy{rcW_6޷S~$2~n NWȓҥ0b3FC)UҺA]d{'_x[΃"̀ ku鷐|>f2>FcMX@u=#-$ڏ"9Æ!h;j `e0z…/XC|YsK}|f!{< yd({J|l7^;澗}Ͻϖ2TTPEDalل/Vʠ1SO )**8":vFgŖ"'cLX{kiRw)8KRTJv SH!M9-`@^,JĞ_D C~`笐d\Ͱ,_,;>(iGĘA3Culu ED1G00s=6.)ܑF+F*0Z2!5~!%JYV/ztq5SbTqK'3۶ x.{?P|-" 6 A^*)pnG}{_IuٿtOԆP bU+Q6`mG-04`zv!˲`nEBikh2DbޣAKaHw~bbDcx Ѱ.>'K" ˔ц!w,b0\Kf$a.d|ìÓO J헖 +LD"/&dpOCqէ086[R,r!%ͩ;hhzq[CO91ʐ}ضsʖW9o,a|^հ$Qt<l=-٨o .]0e0V0!(G3 [\B_ H.9tJ ٹs뗎"zG3!20&]DAaz^ 4&=Ѹ὞0NLv:٤䒩? -02"B3 R\:O\Xbz&6@Esے7qQ^:f:J|ضayv z~UɦiGC> ?tv,a; woxA4n]]ɞ&fLE'3/7HBϜ@;";`BAue#qBiH{,Ac? ` =tLϨ o-f-`E- e/q [ B?h;2@#=| B{,#`D(ӎF1 QXvk@Dڪ;Nq@}=SoXF淾v0މ)NjG<Ͱ-5UZ;l7%Eq$!dǂG8>FlRc iڃ(9fàXGkϠY،o/t+!/.+- o a}u4$a…'L w%epnU뚯9Kc[>6$a Pl=r`گ t|ן{~rXXM|ey9J6"y!FlMوQ,d;#8K'rvu#noAж yf9aX:jr>*q,*=\F+ nL1؉|áp,Qlr{n@K$B1p5 JdE;u\X$f d'ÈU߇e\x!uzeDŽq$&-Iad"E,Z} ?m%lzGFU5I=aeY!+ Qȍ@z H0\ׅ G;g޼f' n>6-_ܢJDf,/[@( A/hg:*<i1X9|ZO sg/!TjJw]Z2&D}kL2fkڐx0zxa%S<ȐcTTkmJʽKkF݃W\K(cj'0'p / EPĘMֲ#+#;!#o#̐‰64:+6:]\"-BE]u_ϰSc0#/4Yđ#x~Hݬoe<ʲՅ92(X9] BTTT4n}}Tڃ>=T( ѵQL:dEې)s-W/[#\_HQ-0Re'O-4X00;iX =!Ig>#-K!BSYTq7s%O"!,N~ogT@ك#AaRYT~Qv#ids\kG4buM/xf}_[! 6&4aظhðjy1Z uEٱ(ߏ- nI.ڠShPɥ5#Csv_ّ{% Y47 *,KG0MwluH*m'cΜٳ^ NTHIR6Xlş͎mZC lxhbL!=l]x9`0bTL7SQabv XV38P0AQȄU17~MIؖZ`ޟBZ`\Ced>03Xչֵ#Do{+ikCg"3%{ I6MSPƎ<Ͱ2Zf~XS|ð-y@qWB 7fkfPG'b,ids\h*~&WUDX%AgT,~~k}dafri?Ë$/VX1 !r6C W?N5cS3#ܩ9BJ8^P3ZCF2A<ׅ'VX聣~2 pItGU^kBBE<^HT ߉#Ah m9~  kMUN /Gׂ/Co0Eo:7%+)5BJ6R^#,礠%KLOdL{ DZ/uOΫI5׶ۖ:!_ȔON"%̀}9`;}҂LO3ɥKJߴKPf ؒw"f:R_1ؾ]_*=1Y./9siFǶ?NԈxARK΁5A0YCI*cѤC : K?7f !st=yBW. InZwh'L;iڃ gk9.w8tοl_Rʩ}sm>M a|PJH5nEW:u\]!QPt :F ")}h+XƩ>ATw !ɥKߚh~+ԡ?lIRil'TveˋϹ Y yRyapqMnVIиkATʆ|Ԅ0$^qpqP(z)PYHEB"σqr]xlOS~bD_ w+vx`K ffIC øxc.2'W}5@hXr(Vc[&HKXvOKk^RԣO 'lW0mJ)ZNޣً/^gkgt-a6%!f~n0$HL"ioۮݙe-C뼶Xq#U#({v}Z?龟Vܐpbu4LNBȹRl+g-[W>^I5T8S~1JqxB8PaRVqK'fy}%~z.ZcĦA>BQlzn`T=t/ϜԄ/? bT[8H{h;S~SwC5`zueZò,dѐ>^ĭý0_RCNyC ؼ[;o'8H礘%4#M3K-g$<NK9O9r9;pC22s?>`u.YmىY۳oӌBG֤&]_LU7xXk*/czaE*mfDd@=#JRGԿxmA5qK.LbYw+.x0m ;H8 {RߘN0gdA *'BclT$ SQ_nJ~0\cvHk1T|m6-B9fE'y Wf3J!8{~YY+%|Vֱo8-ut⧤{lz7@M Țbd)b3T%$B c 0"P S7/DBQ=xQ:KÑ~91~j' 8_Xz9u_ñ>GV<ˎi}ڽw A(:\ޝGU˽k$tWw'aIe_Ft@(SDpAF:ADPAE( *!,FtVHzߪ}  ]}*p9~Ss1Ö4@˅p4t aĞNV#GK! 'Ce(1C5 yaKlBBLUk)zt<)ьy';HgԖr.p)1'ݭs{ p. Ƨc1ɫM<%6'vz>%+12ss1rvWv/j|U%a(<fLf[J.׍ q` 3 s9`W)bZjDEpkGrntUU10=w:gs7|ɐ?ėmvǏ>Bm7ś7ϸlWj`Co= WCI90 UMYg2#]{8+Й_#WkQG v 1j4nN(y^\VJg!VZd IDAT:ۺzr`.A?EgwPͯDueYO_p W^y\܆?LHσ^/z\A^ {̶n"Q7΂x Ps"`S9HcFG#vms =P*.p]"^)qxBៅ\$M92E:U5oZRb,a8ujKm' 7.ӗ T^7Y-Bwlvq}wM\|CzfN) ϕA]. fl]swuu|}XӗC0]&7YK/.XK@|݇n5՘ac#+OFl}Ӱ\  oc&{B3'3绪7xa{W;$gp)ޞq"Ws]/M_q/rІƘ/'g@dpνxy}ܔjʄځ{ϴ5zQzǷCz?OUɻ^qv*!QP1c_MmxE韉_CQΧQ Ƙ^kn:N9ZĦEM;|SFzjIی1?X`D}ݬOAq),GEɼ'J.6[XWy4ۺFbɗM"9 w7BdRi81Lo@zg7(0j|d^pLTk70[Czžd2>&UtZ6!T჌1xyҘaoK\w8{A㲱ʸ~ERx@3#ef+*f3U |?WZ?M͆9N ='bg # $ѹN0y1QnPY8o_LXe* 6$j/:[l2J^e]I?HR9%58py|I۾{.fڦk_/ G;cL2k_O ^ojpk_V ZQݮY㸃Fjx:CJ+> `|:N? g>YAש2!$NXf!޲nr9Dl pi6ڿ;|dw[l_}w7Az@%Wtk*`qXg75ejk-fsMNR5zJNCe)޼Xj\z %+;#kڬQ+*[]8޹O7w-;\ľ lrFe;upn3vj]8qw:5ARFw` #o+Z״bK <E4y8Y^5[[Ww2.u`A,[\Y%+cur͗⛮\a4D슨s+OeY2ƸSuHvo:csjTC9s`nߧ\X\li/Rec0M9Qg${˓$[}`眅s1$z:_aqzGZd9&]/Q#QpjzD 1`MD0:6eӟVC8(A$@ Lݥ+^uq lJ8 5t/|ؾȃjN0&_pz>1ԨsB!B_P].ǩ^7`,iқ 1&C2 (ԍjxڏ|σ޹s:!B!hECBmCXك8(}8yX@!'{ΦfCÜ˂tz;r8br _.`rt-x<uQa anJ$fu.B!B!ɛ̼ƺB| ^dGf?I֢,Vl&F+Ō=l!ӛy GFf~l:c'Yg@c z{EB!BH! ua ),2dL]*c [m)` WZUV0'~0WS{v8 ط_'.\u}a <[-:8V)&B!2jyՐATk-48Rcof vw3I`JXlj76^B%= =W%&[  $бn`Kv s@r0 ]u.B!B!˻̉c0`8X1,W *Y( =l&Z$svYj{҃X_;sr0m=uNc 1F%jgEB!BH~Ȼ 4^4Ζ,K>>^5BYuֵ:y"3u$E1l6gÖ$:{0ǎ_74TB!BG^6dPc6#!0)!g"Rh+W~- s R{Rƒk-^ t?h?fǭy U@ 0 khH,:!B! 0-<{`hjx"p!ž?EA7vKp}AgBM`*efR\sz0Q7{~Թ!B!䗼n4nB%0X,_$S<*] ,8bU뚵 ssԿ s8TŀFS'؁e8~ϓ0]u,B!B!'2P_7kz2EGA4e*p'aA,;l57fSsQ =`0g|pwtğf v \u .` :'|xR" Gk:!B! }YJ S^pQʑ W!a!g[0HoUO=& ]$S^&u`AsĝASހW!9P8(:uGB!BH*D폂0|Bz,zf <'A:Vٜm]d3 d`)M q(?3[F0|0#<. [RsM82 C!B!DC'*fNi)Su9:] h?cժ5dSsinrF>`iWԞ1[k$3axڏ yPZ;gNwB!BH) J;^afR+ғXL44\kp5'H-G#&k^/\zZxzӸ` annHԞ7!!B!jޘMp8@񔮂Bd[75Z)f4Eӛy GA9HE03NǣkF|N!B!TCj U ),GEҔ)t)RxWrDz.l¥ZAt)`q oRB_'N@0kyuߛ;1>0!B!"Pp LgXkǑ0b\,R`BII/ʧh =t,|Vm}?naZ0 8<6nͶn"8J0ɜݯnz#@O|9 M>5鱙ls{"aՉٳ`B!BRA6d1Q{Q:s!0i-0X$S|8dS;]]8GÑ]̄B!Bv;ߐG(c,8')S {u%5gSsGl3 `I q(?g:=``qiؒzt4nBps&4!B!E4dv)R3D '4REҔ)C.g4|ߟu}\pg_:=caN =y @` n3Q7{r!B!9u41qGClLsZ'u;C`/90"ld:gMVgEB!BHxK=1 IENDB`scikit-build-0.18.1/docs/logo/scikit_build_logo.svg000066400000000000000000000143031466366435500223050ustar00rootroot00000000000000 scikit-build-0.18.1/docs/logo/scikit_build_mark.png000066400000000000000000002333531466366435500222740ustar00rootroot00000000000000PNG  IHDRMKiTXtXML:com.adobe.xmp ` IDATx{\e߳]I0h%" $n{#ގ##3*8!^kB8 "ƄiZ!H:?2{̨ Okտ+gݏR@yI_Mo" KJZurT~H.itQIHvV}hyD~%-t{$^U';L<^7Y.I|Vb]S |tpLwK;LaFOpK |t:a k0=>!?I:iCwYIAX A <$t$͞3It;(@2 @iD03@{~u}Z`9NwЁ_snɋ99h]V3KUxt7t?}sđQIj{3}7Wҥt`03yK&)0ttn~ĭ;kxpE/zmHiހ)M^%i)z;9Fw鷯߮8[1R6o_%2?תϽh%=y L'?b3eVQV?$/)}E'κ<ةh-Oҵ~Cp! <̓ގ܈no*`}Q{gJ7j?@*}ٺaz7;LS [V]ޗ)>5'Ir%i:0Ah#?8Yne]RM޸g}vcAX_حԛw")'RM)~E>}J/=fU[޸Tm_Nt`WJ^lVQ,t^hIgkD+T;ضVD@:ퟲ.ύ}E{$"`@3wlG"OwNrqz~zLYZu<_qz i].#y~IqdW?Xv3ї5waC$Y?ft`A[3&ڎ=5t|y$-KjRƒS=2_"YW|HϷi.#;-w5q/@^.fZc_qqAȌL~iaDl~e#b;eNoTf&\UU) g2KlVDϸ]+>Akl_[*iЈ̕y[oG^9٣w HqFN)?= /QhKfk;^Wf[̊Vԯk5y6I?At[1%/ގӃGہdm$}^8A=& D@*bI){7x~ϑ'տ|Ld}(/:?Y|` AQ[o|n(+b;20d4;JkՇ\~֕ /yYIe~|s,O$-H0 tjDyOG&ڡH@*4U&:nJ~صTٜ֤}Id@ NGj]}IuPaIZBWy.tY>[oG~]`2 EE:-W>jҔtfVMI.v~_ۖU?D@:dscflV^0>b.7i} Ѿ}cЁyFԶ~,tϝ6ޗQKtj?@ZPc'd%.#`HsXu#?E}IZIZ(pB%Y[oGo=+fU3IZ~+BGq:}j5nY]h&d]$bSWb]^f)7薇Ah HhКJ^QV8L%H*g02z]1+6r6ͫ\&!_Pt˽/Pҵ#nb)j۶$]3:-z;D$֬͏01%z^z[Wy:)|Ǻas2u*@O>[j]XMZm~ $II8@j15 4"󓼉#&<2OM%Pƴ0 ߪ\nEԖwbi23@w:Gۺ1aW^3wmH5?OrH#tК3K^Ay=h;0\{ #IIgkq:wLϬ#mOLCO?g]V ^;߲I²X@0 <̓Ҷ6"kkծU[z}t-0R|Ҁu˔0Q}N}kOr9:0?xݜ5v'+ڭX@*7㚟MACQaJ^n̽JQ44ů޹3wcn[o_!e$ k]ۦ; .".7i}c?::<c;'x܈*^Ϋת]jB>o[o>!w{`j(^pT>0U)bF- K2ObGq€7yʘz;e]@7qO[I81 Sqq-Z)st*Ͷi6wy5WgZGS_p@׿b2㻒\ᡮ]c&m('.#qz~T=ɎSUuS Sαe0J97MR߲ 2 GnbW_e :A:f-Ll3Fw7G۽1i&i^^*OdL0gHm]_Y78L=z<͉H9xLJ|`yAM*AԉyͽE2T8c[=tLcD@:rZ׫uʪ(;bAh " :,(z euO53е<#^QJ5ILV*S=|zfӶl[zze$ L.xṒZ -@=)d_X]{%]q@0Aגn1FV6bOw4jLiAfT{׼^*O(e ߷n#׸ HO*k]^ u@Jrf,tFdΛz;dI/6k %;e:1 cK+E[,M$zN./4Beޔ~*01kz⌒[oG鑨e$ ˆGےΪתcS IDAT&4džizfɶ o^\2S`]5wAdZo2f%]aa,`Fa@GWXueElGܨfy$-߮ת) <+Vo( WR,W*vGa&U,I|I;8 ܔ3Ds HMSo4,mGq:E5v-]M.mɀBN[DEAs nw_P^V|̇c]_ٴTDJٻUz$-]ftx}Eۑ׵KZ]FR_-MҲSg*STۖ{^%tH9+>=O\0lTƎ$-Ax߫bퟯת/)叞9֛*`J#e햻]L: ;W.O2}ϣo@::Rb32z&ݎnjpI1L&i^v`~f ;dLZ>&>ºg*0*z#I{$(0Б:\\bȫ[%=H@*I=hfJ~%Ε1^& 7)> ŽP7; WoGʍ<|?e::ReКJ^|m([mQ {?PKtV.U~mY{U./(:Ĉ0fLIQ۶(z?m::Rdy"noV=yaVUN|e ߱nc,l'Fj]XK*UߓLr;L &F#~7vVY[śT橩dIZH^ҘV~\Dmy/&=t#NP|{ˋ/>w9 Oߵ!Iy~*p1#֜Y2moHTrq[-3GB'%}^&$=Ɔczfe[nOLCO?g]aH.iY$]aa,a@G0z;r#2}2ˏ߳~PQUj?ߡb֛dV$: t'#ŧ~A* X\ɽCa+>5'i9J(tt4?xݜ3ގdYu Hf\>Hw~j|yz2.Dwii_63wG+KUܖA!Wy+z OH@*wFJdVMq{*Bžm˽]/LCOuynt*k}5$i׸ X~o[?{sǓ$^ntS+Xsܶ޼RZsH@:xF/)Y?x2\p*ܩdo"::[1v8~Ӭ<03 L$iYZUrSA8'Z7X7bq%30?o]_y8LIZ>cZAG^dyM qe$ froYGq:_BzyJitD@:؏Yw?Z0LmIZ.b@GYoGdH@*xfIZuLbgwλm뽍Li}Oܠf0]zTyIZH83L-(\QbȫZ=zNyT m$i$o~zr%_4K'(~ˋWԯt ]Do|º< +|ۑszv`*EzaDY]60VGdžSϬ֛p|2 XR|^0fL< e"^yIA""t tt?DqS"#SzwvLVk]~coZ7zVf;Rq:)7a"O$-H8~ǀЈ󞬷#?:Yv\FR Z%}QaA]KY5QKER+!@W*OZ9Qαe0w}Iٽn)cR,7[}Hr : h q ,4coiVߟQl뉲vvoLgIZnתK#?Q_k%e'ہ)o=C:hUܵa"t-ws7D?(s  fFd_0vUƸ2 iR'7JIRmlMqǶ{r.pQj]^^_8 NUYeG_L=?t(,(z euO53е<#^QJ5I:^pS=G=O7~-=hiЕL.x$/cU@Xߕmb)tv߀€JKY=Ԋ=,ͷ10S3:KeZvWy:G]`ݰEGX\؈N⾹}*0:]igDZ($ʦ*m_0EٻUH?\€i1y,zvETWKFtvVe*c?FvLÜ Uuݦ%4v$i\^:8?ߚ7vQVwNX*z^>*O?zzfж<]F!QorWӞP Y';W.O2}ϣortP~3/vltwWTNʌdkծÌ\i0SD/c#+~M[^z\Oߑ4I:A 8ۑWJzFT8Xz{.Ѧ͒qcAh46|s~nOWm7xEt?X .ύnQſa N"yD8 Wyt8FۑGcOwNX`^DFWy:r;m +5wg-Q[)nX7w߷~St8:??wkVۑXMߎ44/3eiVM# GZ7q%30?o]_y8L$}y$-Nы#i!.,M\%}Q2P@iR2 A$1ܮTY{@+Էreƶ%iY9.ƀwLwQVu HH̏`-+;^nuS|lymy>q HlV񻾤س[jZ@^: <֨W^"~2Oa =,1FVۑ[]ͲZ|S:93C3IZ~TU) |d/7#[=OV>! ޺T0l[[,GqХ4"se#j9]FRa&t|+6I8BOcK+Y1\x<ѿ5Н?Fժ<TQ~o7aUtt+|ۑszv`*EzaDY푤OkսcuxlLcV^0HʛH>AzO/X ;l/7*j?@Z@Y7[ʬH@:x9R~Xyc[^eEK0kDfIDۑ,kJY$-JMaE-<dCEb!0S寥YTa"`f*٤ʺD[K8HtFwaRg9 (TQiQu[&dƸ$j̢1f$;\3o23,{Kbt7sdGmOwթ_6NuUϹoWxUFb'G8 0 /HC-[-CZj1!OK}{0c-{L'Gu<Ag&ӑwT}hUFbq?Ak1T2vWގ`qr(D.shד=Wa xb嬓cEš7#u#礎+QsK|YUDJ&jBFZ/uAtVe$A@cw@68 +@|ɑ T7/mm.$鮺0r &0Y/:9;iiV|Z/KYUK@1KN8v:>9{+LXc {89raZW7/m'&OGT b䡔 &*: d\ϣGr\ؠZu%7kL;ܹ0 :3i #j}R5!j`S1L+P&II |(H~]al0!a;]h*ZUFaQoN5ZEq22x IDAT\`ԉ@X264a)͸ I;90L,Q1Ȕ`n}PL1evJ&G:H2LK B,5;o7ZWwCnC%B^03LlTՑt]psXZ7B{ܜ36]ExsZ ] x0-pf~ܠ3R1T2HiiOg#1H̓9 Q(}#8pPrȌ1e`C4ô<2߷^?ndN##=ƑXÈn| -N0O7M˺*(G:c UW GK])3uH`yM?1ko[Q%X,3L+0kܠ7?^s0J&֍tf'}ws0>Z:u#~xN?a &e(mucLHiiPH?Mݍ)#<v:9-ô.S?nЛT j#36rJ&^]+w>F:ccg ;F:c #ֶZiɑ *oܠ7!ôzx:2cT2uC4c]^99c4iW>~c0|^č9=:wkRK1LSfOK ^bͫTO gysNJ&c(ôZe1]QҁRK{'c6>oƘUMG{!?a#צ_F3L9X(O9tׂ调*#1 P#=(Nc|R[i9=tؠIϐ[8Ё̩_&՗[ {Ws6[w7JNG-4zdaZ*sWܛaZg=j}H&K}YOnH%Tej޹ b*kTFb<nhňj-m|_f/8#MXSTsɑ(G ➭_v0L+T !XS/"" l=c#TiiM.j7C2F!Ϻ hHmx v7 uw#=t&e>@d N@5>D5IY a|*#1 QlgWwUMa|/4;!׌5 Iiѭv#ֽ|;X3 |м5L+*/ܠ7˃Z5`tظԟ~pM*(d>Z/ݦH!Ͻ\o[ G@u?'7 fDG> *Mt3LUSR`e936=y֪NG*xSUFaVRD>>&b=7@FzbG%v76^GƚR+Qn9Haut+ 䟖Tcl0E IgW8 0-? /#v%@!5%yِ''ׇȮ>{KZ7k6Q#s r5n]&/Ę+`B_ _5):Lj @Dr' zvØ[DZ @. =u >M:{5BVqg#;"r=c͢s G4L+6DXݥ ӚA8m +9cyj5'G~J&j\GH6Boa Ny7-%;o$E%HfX3uT 5 X}݅ x^6yrJe%"N:ԤiiEQ+> M#zĪ% NN4q3E@^︃\_E?Vٕ1+ &]<䵆i}a(@V?Aw It,P1Wy^'N4kd.W|h+\bXj9r7 POͥ1I cUo3⏾0*`6Awô>!ӑ3+sK|94GwR4um_DKzZ`b>o!ॽ.*틡ە#rѮeNIGs~0YܠaZ^Є$M(c:S/ьS2UyaZ %P޸df}9K)覧t_OC|B?BǚSvE(Ir}h*?pi}q0V׸3s-~6y:Y b$?X >j>OK_BuD>2cp)&z7!ɣD? iWeєI^K y7K0#Z7.}n@H^ۃתRhԗR O%UejF;7!4j*Ď*#1^/uMZxm040"ɟCatrH6c@[Q#VDv&k#i6V?AwôUY!hRRKjؠs;z^T2RUFa|2 <0c!?5r}{9=C5HC4g/@U灴9d|>u=h\?pr/\`|B#WH(c'fWa#߷^t(;zƟ4qK펦S{!W㑙O0+EOBfߒ};5{{9N ӚOܠ7 _j<y_͋kasl|ڟ~T2AHɽd':Z/_{7Ę;ð!?>tUVȎG,%w)eyiAڌ'aWo[ ]Viv-!7=5L48naZ$԰ӑԅFi0-LAUFaL-@v\X3|5[Uw,^#ק&*D̴kPW F6> ֏툾gi~?ܠ7- ' ^u`'G8 0 +!@ bVS'Oy\r}#{DxiGZ@a"FFu:2?KFtSE6?AI[ ӺV_AoPm]75y.~g͇ZPe$\!3 8t*D.sh_'{ _@.-8Ld']0cë "=;FkJ0V 񵋠{}0'X5YӑsRrTe$\boahߧBH@cC:pyԢuy q ʡc&bl MTq| Ø.'Xmm.$m:2KaLJ+dOɑܡ(N0LzQAZ H`!O'G<`z>VD})yw #=g!lU5ܱB}kyi] ǯ hk? h4j{ 6J`cO%ezO}z vpcn G9Jro`V `_KOGv&bLibK;n^pNôȏ z1LkORӶr5K}5GMdb<ݫs9^\I*CH[4u93(OQ1uf-?_/=O%λCϣ' z0L+P MHYM R"j;c1A89X*h`ޕ{!tcMK[`4}]StlSFhi[D,5/snEMc.dO c;_AhGuw a z(b_+*F5Ę+Cs<p8 0-g/D~F{NS1w84nD6@(@trd_.cC:Ӯ!{{5g&=NaZ Y7 &ovƥ |^ M%"5B{E-ߩ2cÞp]S"Һ,=] ?vD sn%mq;<5GChKo[5iU z3L ![k+9`ND gx NlEqaZ{^?m]X8zƟ$' 9Br}`߻t,>D!r}-47*L)OAozU'189ô>*ܠױ.ʠ&?C]bMyfl0^YEtAZpU*i*SE,~y`)PS5źUc'Cn|;2?40Q-e"h o[Q%zXfVDY(vԸASYhkXY*LĘ{\#jS:UyaZ@ =3%{$_C|eBuwB/$קS70ceע=\8ٝ ~OO! z2LKlW<JixTbNE;6neVt.JV18|$GvwZJ-",T1gcg!;|.DZa?<:-ô.SnP j<y[-Ę+Qy T8jh[S1w<:r'>Xű3W 1FSy^/go[ fXh%Gj4LkmDv0w7aZ_S~ū#E[i@PkAl~sQl_ɑ7(Xr(yXs?W1Wc&3 {"1uIo#b"ח[ {P)G#:^/D|ݝ =#K :QU7u0&OG=xW&K}Yae725m\pzuV:%ƚy-mҦv]pl|)(MƆ1r}˺d&TsheKô/Q x^FWU˥ؠs8V89P*xNUFaD{?EChkW1אg]L$ZDK6kAntg/@M[lxd'ű3/"x`D#'GQeJqW '1jS1W2f{Tw!_G *K;W1 ӚAm +9cyj5'G~J&TiFgZ/lXU 9y7뚪YOP U5_G%2\^nDC˗ߋX2'G&X&  70-`ta0.G0ڮTƚYB+Tqm!ܦ(N0L+jYhiգ V-`lprŐ$ׇD@DI︝\_E?{!@P*sr.&C [;zm0/>7ìhk?jXqs5K&I2bb'N84里;Vjx! }@e"ܡe)7)LtYAYzs+ldd'^±s]MfX{BGo_n7UaaR&OG6AlkZή-EqK) CgT&bƝ krw"T[nOB&bnSC&y-^@nuYl99 ô Q}tvufj}oMkU^`4\G__O%UejF;7!4\j*Ď*#1^/[kj_ <OE/{73'I[HYiKЫ5euO eɯ;aE{acGt ӊTgJ /_ c:GayͻSoUiiM棯OXDW!uM[4\4HC4g/@US9+Thdv!y'Gߊ?]b[<<TFbG34 ôt[K=(;zƟ4qi˃:M-o6^,jx<2gb'!3o 4¢Mowr'iR B]$y:oasl|ڟ~T2A(ƽd'"4j^t=ݨ2c>&rVJ#־Ta wx}kr/@O)Ly?{!6ICUFfWҶJ~`aZ|UE!n1LkM\Vy:2cЛ(K%PQ5S DH>лW1F!Ͻ5i8;B/g|_\a"ֈ2ӮF%~>I*LT툾g~(7lW 7 1B@_᧯kk]pvhw@(U5̌oO[w#|NMp4͍!d^ȿ/dl0)=E'G^p8 0Ԋ{zz) Ǝ~Ȗ\ixjEۗw70s¦me.;Sȝp>>o-;^TE89ôPYq>f#oQDGx3N>Lp]~ bl0r񐳮"v"`eh^mϑKOGv(LU߂kbY!q;4L7C0>!ӑӶ˼R1|Y5Gdb<+jعb*s] kZuM)ڽ/?Ea"Vf-@-@:^xiAKNaZ|Uep> l&$iKWJT_mgl0s7@fi9 N&G7> 6fWN,5/ҳ&O t) !ׇw޷&j,@xKN\;4nЇ@K$_X_ b'*#1 cQܤ(N0LK w ޼(TbOL|\F5 vD7>A/&"3ZuX(@zOv/ 5X893ôNSp~:/Ic{J^Xa2>gRצ~EG!][Ϣ^U =uMmj]pmy\P4sn%mh!MAo[ 5 iU Q0L V؂%n4 !?a>mD>hmG-~ɑ/UUy܈_M] bĘ+Bp8%w 'P5~s9f0 r+(1}BYnio6HN _wD%n0Uqn :/쿣V F,Ѱo25m\p+y)ضb+} cM<H6UTE6<_frFqYs-QxYtH/ ޓI0L+TB4*^.EPo-cp^qrT2W Ӛ(t<1 y@Dr}lgw*LĎġ͋軛5 PcN,cg_DZS@;~|uMAlq_!Oe$\a<ʘuRm[դiii>o[xNȢ5ciB2Yry -) Ď? Jx L^a"vJ㑞F ovbf́|WôܠFGWeAM_^v%,<6 G=H~3{Ke$\A:YהA5սȶv7&]¸3&bGg~ xYAݮ ֶrôap1 jXYӌ |O53L8Fi%/OCD<8r}uM"ֶ7%d${WPn9\4)L ތ{H ?+ ܠ- I~u~F*#1 'uQQav7 ʃɋj~C*#1 ظП&WMI%|?- [+J:١.cnC\/D55U^M/w6'e0j 4jEФ 1(hk[94՝>p?5Q Vs(.fjN0L4Fg?6Bt6{'9;@ɺ;fbhwsfu*L(_Jd>ڽ nG ֽɑIIظAii[< <`u9v2̦iE!9diiEQ-< M#zĪ%LhSW󈯻\_E?{!@۴(sr.!?\&b[;z0ϫӨAE[APŊX6IfC>qT2rB J-> ( ĘK=r7 .^BCAW3)_P}7&k4bmK&bJi'G3LҺpGi%|B#gl /W"*#1rizdbi7)LĆ.+-+zB?8\/BعЮ; }maZTp `MlV&ɏ3ִZQ\gWh>%}!z=@痱5d_}y5?~E'#sU LZr?x]#[/sQT9 Q#wJ^ņ^- ޾ɯa Ӣ@XS7iEBsVQԚ9Zt?'LVUFad}kR1*r<>ڽ5*9 `U|17A"[W ө0;]n{ɑAQԝf$O>s (cѻ[8 0-3ہgkJ]JuMMߋE\ Go(LԼJz ĆDZoz#d,UyA6]$!}5/޲*#1 ظ؟~T2Aýd'F]{7Ę;BD.׊iڗ* IdJMc?0Q5g/f< Р?]V'cGKU-?LXnV^Ui06Ғ԰ӑFi~`[*xGUFa -!@v'&b=9B-;B/g|_\a撙v5*эVPg#,8u)- ?_S ZL%$<'G8 0 /HC{MZ j1 'MZkj:r͵(g-PyF'9r`7"Y}lz# Ӻ@Uzt z[WAM#^ Ę+a|_4rPpu*p4IΕKl@-wLĘ;DFrOw+ Yh[v7ǟK&r@z@$Z%xbhkFD|"heGw6LEUzT aZ5<9/u,GUFb5.f"T2P r.(LĘKyuME;'\?*ʡ [fƷP ӿ1V_|9w<;դ_MӠmmGHtaTJ+dOɑWܮ(N0LSOD9whjO~uMMS+:l{HϹ6mc/c? "Ep {W ӢwiЋf#on၌Q9ތ#}K%ez_zA sPa A>rzND_*LIDtsӑ= O$>;x[;B/8yaZG]S4i #j}rW1Fq/׾)LVQtu{%c>C;@lZ2coN5-uMD;ݜ9oP0 ̾{-&bSɠټQ4L)A7L+P W[ R"j;cqzɑS4 ô>7ypc$shL.n|5Uw7K͋셨 3KQ8f>}[a"64(SW\ߠl/l v;cl,*8hn7)0 |VBgh7 9sSa'@.?)X#lGt#=rl"2ӯSCb q ޓ݋&b#)=N0Tn;Ic{R1Wa2>g _K%"5BV[Ϣ^_2cÞt]"^+Y{Lr}vg3CaeC;4PK|Զq;<5GChYk%ۖvz$`aZߍȵ aZ-UiNV[ؐcy%dw4 ôjGTFb5(}]Sz~;H/`wszͨy);(kzoFt3Nũ F R[<}Ɔĉ( Oɑ AQaGV k@V=Xs4rʅwuM[QkqHϸQ]TT{ozUՓK;Cô>*Hsee}%(lt=5/ޮ; +kȟ.WBN%|?-Ehj0oQ1W(O}\h] 17:y ><&SqԄ9BjDlެ,4i#6*Erô\vu xZ_5,E&b=>#jORD<0@fu{1֜ Ϲ:XykbG$qAr}: T&j ע=\8~؆ǜ9*3\ՠ% x+@f%~W̩kTQaVtDJKy` cMKN=HWG?twa`- տؙN&_j -Vճɑou<#U z5I~a[ՏvPe$\!@ ૩d{C-ˁ*1'+<ٽ+ ĚAp3W կ'̛z9EoVhj%JN<`V<#5 aZzӑ|1K}Y^{d6ֵ_pUzzIe$A%nұ5!znrQ OP> 6jh׃{&bW:xie^EqF+tôP@ cw:>9z-X3k%黛mO3 [n܄;6"46VG]GhN|03\JQ h6y:rw-Mү2c0 |ʗsri,@4J-odU&bW[y]r my\_ninY? zpuw*LQl:90UN ߠu_ߧVqD~Ɔ&]m ɱo25m\pEz}5ַUFb<BjR9kb*E6<_?i̔/841r}˺d&bS͢.'Gb~iV /`VditUU Rڌ Z'GJ%Ϫ( Ӛ(t[Ęk3Z&c?Sa"̜n P{3;(Aok&b,xmqrd>*3lS-&ӑWC>s(cnUq!Ao_;!1֜&$!%z@.ĻPT"c yD#>i乨fw!f:ƚP|.'G0 m;/ jjjۋ5`tظğRT2h'|M@tR1W 7rq^ĆIdv7Nqg*L4l!}w!=R/^s45!ݮ vQ%ei5Ul jXYӌ |O53LVQ?rsy q-v捨zsD^r >i6*L$ތ_99rVGl x+$y*a2cpJHz]68 0- K(*UK*bl0买''Ȯ&b/JkVgoԳ1pʗ͈lo!e " aZʣR5]5IǻHB6.g U2SOj'+ ĘKsA. Ca >^x*v/;.PHGz@#? ZD|mФ 1AX"hUIG Ӣ ՠ5Q /H +9cyi5'G~J&j\Zn&8 1n5U C1vxNw7g_JpDj Lr}{9 17 f=$ԤQatôD&?hRkfӴ"N*P9:y IDATaA,44ROT V-``ŐfW&6<8jg/hJᘳt >:DP5"ɑ <*4L^5I%i$kfY|O284>@Z.%>2cr 9א˽]u&VBCd ҘLDCꍢƷZi% fZ)}iWsh J$OG^DTFb5.I%;Ui]ھp+y*`-UsMX`xThZ{J[DxZB Ķɒ|[Q!Qق- I>GĘ+L@^GO8u#LyG>m@<rО59Hw)Eo[RCf&r4;[o`%?H$S9ì&7m(K-^AkX^l /h*]Vw#}GQź']c5`rF*Kt39ݜ; ǟ0yߵK$ ⫖#1vGVѼr DÉd7djM~X)`swdu7Q7LCSUzH:`;} z~0c!O 8״^RӪ)ӳo@W&v%*G Ea"Z w}6EqKm x'@:|E%>H FTQ-8Y ?)S7T'V,rک?|5+}vtBWh1r!>зwJw@7dD2u<6=/i}ˏUVHe$\!g3"+MCwIΕ}j@-z?T1w>V1"^G䃗qstD "ݵh;| K| L5t(jfHh;r^zL92cq/hѷLCo ԉkOGz] =0c.![ qMqM%VUȳm_EiD~u>{%۟U1?!-'K.O$S_Rǩؠm{A&wGX b]M)qTq/d~טλܾя~׸qc$_Zˣ{ 16r4UKɳC{1,nzQv9(Kx]+}ɒ|]Qݠlq{@TCx51x_ɒ(S7ɔ@ h f1[\]ػQ؆&r(Rǀ~ޛۍ;?SK _#'Kw"*ňm׮[ZPȯQ^R Ę+x`@>h5T7 ňO-vQ1w`w8iRB'ؗ$g!`CC{lmy2x-GMhvx2KBJ$Sꎪ bD6d*^dHR _g1Zst_v_P^$Z/>*#1k}\S5"Zp09uXޡ*; ("} m%QQAȎ BOT61(adFVn$)mA ?ciR73+qM{M+ci̠w"!ymƆKl#4N""JþAI. #ˇ7,>`qf h{ir fs>&r=kb,yv 'Cn¼zMKh<'5aZBsRh6OV`E6+ Q%Qa6D2uZ_5o%ѝv0[c ;|}/c5'%7%sT$öA/bQHs[cTFb°p? jE9Wλ\=ŏU`15\B,q<Ցo;P3ma={Y4Z dM+B+ 8Yr_"jU z"Y[s>9,"dߙEUzX\DZ֋BS1wgnWO_v'>vJ cuWKgKɒqQ(ߠ')oYJ?1AkX#0Sr)ЇLJ$SҪX~ǁ_Pck=kbX%Wg7 ݋a?&GAsGo zA@xN\HV?SA/=Z ]P1W N,zEqK"kk+*1 r1@_U\c*9:ھ"5iG"ڝEGkHHR\ܷ-S1>D|=*H%:u埕"n#E/!h1VßɒN$S(٠^ܐ&}Xnvき#$h B@pHy5{51F%&ع[hH] eX2VITQ!9k/4 }Uyź5=!z)^|l|Ae$A`_k8c'|s≟WDɒ> z"&$-^(PT3~1WpQ'LC<"L!^G23F \ N8&bqy =K C:p9"1Hdyd=SN`=ZU*Θ Ntvc7*S_}K8B-)ϕea 9 rwS)ނ&G/O~/kkHw~ oc'K~Hk}$wGv'K~Hk9ޠZ|HȻ-^AkX^l /h#]Vw!-;T/84HZQ)/+clHE#;1k'o G߁پ,4_Az.ƒړ9g&7Jq5oy%5?hZߦچ/J,Re;Yrg"?XA7djG|)ڰ4#b[55:aWYt4}1vҝ aG _z>3(5M=h5o%,iAﳽ8yEƘ2h[w30ƀG`དXN:qs/"s9W-HkC{,S&ɒK+8$IN^1wF:@-H.5 GtV@t#Qǀ~5ޛۍ;?!Wj:kXÀMnrk$΅B-NlW5A_K!I!R U :g)Us4VЗS/kמjH? #H\8NkChZk<אr) 4m,v;yc+0v٣D-N4@ItY~_; ޷MZX Ձ>D5rN7*Uo3[֋H<2cmuя QRc^\O58c!?}\طc^pq=ڳo|\Ϙc#` A'no`Wx54TAҿ x8LμYe! ;ruDC[˫!Pa|3)gѼj)s}6"m c |ʆ@zb's_6 :4yiE"G?їC e!m!'K4|7sЋ5/*{"Q']>m5?q>|WÖX+<\[w?|_i˒q#v%JU\v(NnN8ڠG@Z6gUtb5nI$S'S/LC_c3?$/h %s@~﴿zT0܄O? }۱emmoŰcQMFfϐ|8>[c:"g吼&m{@Ti_I͋t4>cڎ̔ ,Y%WRZpǯ06[O(ӏhUuMig@N2cP@[jUe+hZG@~GD uH30V,o鮅F~?4t'8wΐ&$)t8/XC+*7p8%r(O7aB֞ ![*cv`d7H\`!j<n{גsFa,?f60TG8Y򷦡|ڠ^s'8ßA36+;'U"B͐.xcط\toٙ<\ߴ7ߨ,OSRhr}b06rOAӜ,y43Se)Gi6eekϕ#ێ.K$SǨS/t^ y\`*#1 Rj"'(iDԇxz+Ԋ&bldTHwd>CrcH.5-gbyЅ5x񊳣M~HgZ&q 1l 3jWiO쇉d]UzavtrLȎϫĘ;H@rPtrWfTN1Jst6>w-[0_Vs\oHw/7ta>mW9Y}/jo`=%xc)B (l5?ɔ.si iI04Ċ˭P+:*klU_ s=K&tὫ$8c!7_64 t/ ӗ_1 `渚4j F21sm Y 9Y2?)S7LC/#|e=y!?s+Xנּxr}ȓ*kh97 {+)Ltpg> 2JцUTvPjdɿNo@4t{@||1aڎ6L$SS/LCOr < smŽ=yUe ){(y>rDv+,i_Ios!7/4df|ɒgKtzlo{`Gc y>:cRUIx0L5)UDxmCm?cīN*k8dگ%{{_KnT M&WSY4A,\i:AF14yCVyLDYU\c78:> 5ii6-R 2 tߚƴ3 ] bw-7Ijin5}b(L0E9v%5 }<6覡@JFyY,P?ߑzŽ`sD2[LC!*K}TFb5Gz4~"+IU9 6=KU 4;oAhL,cî0 )s?R :vIjFUq36[ϔ(я2Ջ'܇\ z9eTAV vT.Aw/⻪JI$f`bP0д!r}5:z;)ߠ^ j^4!I*gC^;Yr^"AUzazL^lwˁ_PbGEWu*ئ_#ޢ0hZ Z)MOϾ\a"ƜM'8ti)ߠi蛪ZlQ-4~Kk4ao6"4C¾,\_cQ#'#wũWExh#4伅>GkLVBl~\^wr|Va Hw.ESn*!ٙ_B{ղau9qG~ḧlMCRZpN ΄gWD2E?RoB@/`pHXq?qpW #&blhe'⸹/!ufWܳZ"K͇t"XgYqT2^d*SQAXRKU1G Ob(;{@"jV~\/q6pF#5r +˜U^wUY](c@?v#`LY=d!Wr1ר ؟Y <JG"ira;9^S:Y2 %"r@(2c{DwRK|W8E}F|2x-L݈n~>;|FCn@WQdɷLCߨ*ňnMC/4y-鑺&$ dᓴk5VYH9L}IUzaN@M < &b=Dϯޭ/4Ea"OvQjEm=B}IF%5_ +LY㑛䨧 Plć,qYaoX]zEvtDD25AUzyܣ{Z/9r|s˂xvZ&者Ť/}mXa/A, ErW~טN쒦Έo /zjLo316JɒQK$S|32J2'\ZbD߇!חA^Ɔ%|Hw-|zQ-yRhvUqߒ|o9DGL⽪QghAĦ$_y 6X4u]UVS2נt4>1]|Q Cn64G,y4Tq&6`V XD 3*#1Uڎdj olD,,益* N9\ؗDӖ*LT{ Mq tݪ0kX 85iMl-^)SP1W(@K$VMCdvv- Q'yCLT1wA{>ry54blp7t'E%FZ }"۟#A;鹋aM24taPStiҊyZ<2Vɒn(N0 P®8{Yx6kX '~yFPcr},| նu?/>v%JUQv(Nn ۠G@Z6gOB%nI$S.ikbk*- _ũ[:mڄw 'Y0ָv 0Q.@Mw[f([闠<]a"&Y*Tⓝ,iU9\5A7 =.U+l|6k5XݔHU^vy1yAl*LĘ{HG]U6LSGar}d}DG6=K /,E{,ͯ8wvd\d )3$jvm jѪ8K/Xò%t92;}7>ɒ3 ~b4r@RthB A>ؠVZ!ݑ8L]*O0 } z3&X8ľzTμ i-#Ds;TP` 7d:ɒLCGUTt0 =Y{f,_Ve$\ryѯedUyE'%OGf:z#a16K]|W ⨙L\ߌ_*LR/≟P+ҝ_W՛J,zEq\]l ٷl~]d|jX{_$LZ2ikXs=+1ʆ ݵRE%$ޤ|v.(y!7;`s!@ɒLC߭*Pئ!M^`I|g?&TUbRvVw 3/mMH_rgXGkv+Lŀq*щ}0;5 ^r}ƼhX=N>#4ߪʣBl46fNe$\JdT\ܻѭTȽג>acHw/7ti\x [`4+jDO s5`|) >zD2˜+EZ~ ̹ r$uysl?Ċ˭P+:/, k4,UB{V!Ir}qt;2CFdCC}a +ӟֈ۠@HUPO0sm Y 9Y2?)S7LC/!|eDy!?s+odV@ >|WaZĽ&j g> ̼hm8_@e%brUyT i2/$ d'c/UmHNU^y< smŽ=zʜu: GD\N#^?FF*ć Q8HalSsd9)3lz]QZg;c%L9;ܛHP^>>z_˩!>2ְUor ]UJIqޟu BxdL~FUo 26  :5Z)`k rɒUDZ$ y@Ye"\Ay\dk^w/& xx$OR-i>!9q'*LFc,Yh:}m s4jPϷ(B2IR9c mɒT 񺳻 &b#=~SG|2Uwb[@/!;RPtbH/*г cWn ɫRe1<:ީ::> m Ы"<;FCZ5UT"G:Y-7S+\A7 G!qf ]ei9#}y"<4RZnESnR1=U ) Zfc[P_Ra"v4HW.VKw}70;#Gn^ /CQ :,qg|ԝ1g1lGGDL$STf #)j@"kICO4t(RMi :5E[[O-`#N & /^8:D2?#D)Co|WX@br$dSf^Aj^ZBdlDw<ЮkEX!Ggn6 |e54t+[R |&C|ԝ$ޫ:u~+S7LC'H+ 6Xu0zDldگEhr}lc ЯE_ BzT{e>'8Yi菩Sfop?2c\%t+' E~N!?2c %+ 9t-|WGw ;]:X+<\K>_aDl849|+Q4|av1J%f8qQ[.\eC-NZ5 SUy܈&[R#P:)C3hkd);jij,p8azٰmҴ @.XhםzOC|Y~X=JT-o#g+Q w(Lmg8gFU1 %X̫ĘgG9i~"R-QO6!G͆e)!(YU7(3϶ (LĆ;--?MH_\%:N9@+@x9:^Zj_4X=%hX[æ^ x(L9eΓb#nFq(َHNT-LCOJ;IG_0cޡm"gU/88r}tzv0U4o¦%a MZf ;'- YxPGiUcAÚr5IpV&\ADnEayڄ\7TFbdOΪUU@Jr_Ga"Vv4ιHa"2+ #(+.5 |!w#zM^[nj} Ę'| 69>@(4 F}1E<k$I./u|VU)KZ^ 5~Y>_AhߛQj&rNZezBUzB\$%H#BKy'+\H۰iHF-IGžHyx`r&j\٣F>#o>65 67JYawj>G!7,'-PQO:i诔#j}Vé~zX=T(ӷ}djLn1؟#jr|Z ;U]gUYi 2W!M[WyF1x\_EּFa"cOUŁ[ &/Љš͒mw!xcG/VbNZ:g"yYlą%O*1 bk"Yek~ Z 'y»6OCa Kg)H'-LC_Q:i走f/(B_ eյَZIYdgWWLfȓn;#;mQjTqd\j4r}ӛPml^&ק{C5آ0;ǟxGu~cx/0 -qF02cd ysD25]U0{G~/r\d*#1 Xw'DH|[gUS@vоm/ 1 в^rnCFJhUNZvVQC!ahW|%;c ,+ I1b80zѪޓb#A1M6!/ZǨLĘ7wC ry->9>zj0zo4SGۡnEvb؇ϒKcF~r>/jvri{T*^;dhܪDsB .amae9v2 HbF y `ڶg!>|\㳪,;cͩ,|0kͯ a>m\JM_n'-?3 qUyw h {khOUHێnCn"Q-LC yڄ{Hy@qܒL 08'Jڈk&bW+e*İ1Wn ]HϹINQ!Yx_}ę5w,IH" +Ѷo~MI<K awDR =~LW@;&b&w+r}+L4|BC%5pi|!arX=;5G[8azMDZ&M\'cBl_KoN{ _ m(⩇HGξfξ֣4 4^yh@ CY2Z_b+Uq зV"˗-!Le$L@MXNj/b2hOkdZIw)KDyڄ}@82c6ԿU  t ؁F˦U[5Iڛ"2{ȝ x'-MUF A`z)EG>!qZ(emi9:#}q"PU0 }} sĥ 1b>gUg5YU'r3/@yD'Gd_Ra" *;}7{!wqOqtZi8@$VAŚJ-w')G};ay\_p2 ;{KMC'e}n5{%r!Cy['aGNѨܠ(k>_Ae"Ƽ!x h^0dH75*9lkeC+e\_Ջܴ&|6ā{2.1 }LD4y^U| @mుճ]qۊ⸆i%DZvm\2ְ[!vl &~q'*L4Š"7%G[a"蚷F O~ƍL%(sWmnƹ(wNZVVUF tELC-]J gs'cuưvR1i٥ ~bc${ek\}VՉP@<+?D9hٸ iF}+a`Un,G8 @WkcE[Z?Wq>96U .Q'Z>%B[j3PQ0 Bm%"M`$fq}'}M^9p u=W\6<5!ggRLOT[:iiTa]1г~)iC9`Ƙ=oŰۢo%iMi +&b;HGgKQShdr}'DMhy5 d[K *N\qDӾ%?P^]ϗ)]?~9-58*:LP-̾c> 9T`<Y@wvV5+Ϊ:QG|hs܇h/զ t]0s5_pi6vX`hvP DYe$|8Uu_IVB˫?&y sGq#7argz :ii蟪>1=LC#\lIFmuv5i4tE4!ŽkLmx?:T+aٵv75~- Mr}v(uzNZe:3"&r.{(wt6+LFۡՊj #{v~8ha0aeZߦpjB3V-'*q[D25JU&jM W1o YՕ5G#$)7MAf6 )y@ 3hN=H"g^3(!h D妡aHXY"f>&X=jIKՊKlą#O*1 bGΪ^.1пO{'*Z6ShXc»6sOCa{3=tҲ4A a4ZXϲ(B_ eյɊZIYdlWh~p3I7 ;VĖؽ\ꬪ92\ &{`SZ6&ק{C-|ȿ/?dƟ7?pȿ) a47kPcHyƓ&䥣-'8{&UFbl N*jpϪ:Qj7~6>ooofkhr/ aw!Ji*'-\{H) a(m ~S#qgs$:F@ djxJע&OcT&bһ!^\^ YU'j0zo4WGZ@ۡ.?bE/9rgW9z~i{"/Ї!eDV%JԞ9v cl -8|CQ0 =HbF y yڶg >|\>XgUȚWKoN=`sa>m\Jt4>7m1J#Di;i`GsLC |5I, qb=U"m;z)D2գ*[2JSe TFb$ )P YU' cC~iȮFa"W+e]MQbbHMХt $wNؑ aK(5ӭ* *#1 UqX|Éd*0;Dny>03V_!UQaҏJh޼Ja")7w\_nDnrBC%pi|0 a."K*Rax c@~(k^ѶEI& ,s?גKc9N{ _2}'c$z쯢ק&dg_JQN"4 Nؑ a4?@#P>բcL$SoCD6![CsHy )>m\J~VՉ3P3\Y>,yO2^jW]6:|I@wgXMjXcAÚr[D2ժ({DZ~>VrB` lkL2w@ ybD i }>c*}Mo=J4OAhBH-4}:%l].e[֛"&tFZ;i٣LC#* pTe,Ƽm[#SΪ:aC ݷv FlѲ.-c -cMsG}Ŏ?gB5>ɯ4mNK^ ir-]OH! iDkd(vZH_H.T-LC)EmJ]e"ƼaGI.4OA8 -GFQ$jen];X#4"L/;VϥȲ.cE5)H NougQAQ0 }zC.Xv1vPb'zVՉ7E(C߆K-WWگG4_L-^i j}LX8#U1xG^:h_djय़kHϽ\ooUrhx4VCdˈxz0Еg"<*An~ 6C.CxÊ8imEq\4"g FB.Xs\kXu;6 ¸I`EGvS~K!'?>UĆ/]4ڥqf0}2Vj mG/!L*[]Jn1 '1F"^(_ ̽/ʏ? ?3?1Fhٸ ިm1Hvyh_S8G?XA8jDD2hwPO6!1 yh/\nZ߫v&<_qZ6?c̙7oy|"">j}1Ue} gMQ0 Bm%%M` )NƘ|'}MeV떟_KgS ~PZkÎ<^{iY?pZP>ت1z[1h3D2PU0 J7@οBa"ƼC{A4}qFظ?eψ"^!3&mDyi[aC|I vSkXTзD25BU&0 &S)*#1 ZXO?j#chJ; 5_Y8miux @&o.Km1MUFb *1'-cܣ(Di}u&]oC$\^n+a*l -VgƎlQq77z/=4rHgYRkB`aX]Q|h9:#}~""OY*wACO`. ^JLrO; #?mON?†=^{iɪߡ7iN TFb3P}lJ$STqǐaѐAa6KGn?>86P1Z{ӶO8nb/=(ٷm:~] Փq'--H$SY yh0c1 ƇNJZ -o&ycGBwpDltK0H&YRvR/%P=ǿ5 GFb˖>l]e#ĉjMFrSb:'-~Rgl]HufiFwƆ„Q;Z00AadrrҲma 4RP`V'QCb?v5{5:c&ؚo0Y5 dqvX:;š}K5(jOU4HUp؈r=6d%aRiu:/B)Xc\+:IrW S*)%D25ے"FG9:P7*:BmijPc遜HyƚJg_djbTBHW;ia *ljt FXÇ1'-1%)>q-J RuDyCz7+˫Ț( Xcjvri蟩Æ7^7(eXϭJ='rhAMe,\* *#1 UqX|{ÉdvݲE۾) ߦK|`tBڋ˭pzoT1ﲅ ?6LC狟/"¾"EZa*#1 ;BVN*WEmeԠ` 'c{bZryi1M@ySnPi%O5[MCߠ*s^3>Χ5`a( lu(~'-7%STq з*:&b;Ć_O%DF+LĘ sW|OQ2@gεE[wh5cAÚruߟHZErpQGJ^9E<5Jv t aaY}|M %TD4ycSHynj5e"g2 ]"~&Xy' >F./Б22VOJT㝴bTa t?LC/4y-]OH# iDkd(vZH_H*[S5@h; )LĘw#gg]rTs裑rgR/ٟ0 KZNVw(TP}4L9z Ef߱#w ^N9rI*#1 m@VH[ K8WP 4a<^lfقWcNZN$S|36"i1WvAl~\^i22ҽ7pri;Ua tLCš&ER/pr(x;c$޵:?⸆i&D(yP[>JN]b\sܤP;I˯LCgUy}.߱pF 2cd9t,Uy^¾S.Ȯ3UFb.JViBF*QK| ~I/P+Պ*#1 E3&H߀yڄ}DyCv/jHϽVa @ ݿVѤMC߯*s?^hy82VvVI<V5LC/!ҺH5@rrGGXoر\_Ɲ0c[fƹ(wNZVVU @ge.Rj}HXAcյ~KH$SGoKY 7:H ˽נjS<Y:iEq5籢-֏UqxVq$Éd~hwPؿZoc?c^\nZ]0cÏ1з\b:euD4">j}1Ę'| gMQ0 B,TKi4 A)A$4|!#kKPm{oRy /iY?p  C9`Ƙ=oŰ8iYHZfrCW(LĘwh/=!Lj~ʱq 16< d}IKDzX:#\_vSkX֔зD25BU&0 }irƩ~Y@-R lj< #ݷ 94t/ЙCM\j/b(Ę'A/UbNZGQw/*@ M*1 ކH+6|!#lQq򷦡oWy/Й#C^dIAڻ S9y;cujGd+b-nuu?Uyc4t;ً-gȡH5 c{-d+դq3G΁mX@{Gx >?LG)Lؑю.}4t. /!3 zj}TqF02ct5&)CU0ף4@6!Ǵh\cIJw,ezv16Tc]rEqX:;,}]xZ?W<_Qe$< k+MBN$SA!~3i*ddċD.",U1ulhH OX.7 =0k@g- +6ȏԏ ЁHy»2d-e.)i]Z4>Bj~ȓW~hxE֓ O@n_'7g;i4u tvLC߫ 5j}PH, 'c֢g;Z8}":AU0 }&: rE 1 ">u\喣u(k0@g-~IDGg *Qk d~hۭ(KP륾K/MCH\ ݿ>* c t6h"ln@1lTw(ۈ/FH'^voXch+%:";/dd_k *M|4t`:4C\,%H#BK ehE~W&Ūi;U#OQ]2c!^y\sqG Ǝ䦝e=a lPrYj?ַi5cK`M eVw/LR-Ri W1oՠ[aHFzM5`9WǐIKnm7MlP fR"f>X=dž*y p8YΑM&lȩĘg4!/m#LMS-̞"rbgJ?!8+`|d1,_eFy[';bxΎ&o,cj\QV1O؍^ƝLJQ0 ]"~& KpTe,Ƽ?@l}\^#7/dddD5>I-AU>/ez)Ew}>!qZ(emrtFD2Uy4ҮM 6&b;G}볳rTX*>)g8iy ^#4)ȗTh5|)[x҄dj a C.cǁOS8T5ܤPwGMCgUylȘ%q3>&,, dUFb3,ǐsD25KU0עz9ք:Se$ƼAJhitߍ|!#;tHd%NZ>?fClH3;Wm~2cP><[ix(Lh(MȞ 'LĘ7d/\^Fz 1o[ +hJ/ِhyQ2VvVI<R5LC/!ҺH5@rrGXoر\_̹Qa8iYeTalșK@#6s'cu=[aѪiRӀXxg@r`5&b^Sn:iy-0/ٰ5E[CɆ^a#+*RHyGL)[-BD6M`$)dq}Ķ'M Y}KPm{oR1xΆ 3~)A:6:cފapҲ4L*[6je 4D] FdPS]@vڗ~#lX\_Z 1ְl )Qou'29U(=^8 ү2c u"2n__H-4>.3 K#?ذe"2cT9i ӢJ4>:HDyî%f"7/dd)ubciUaP ;C^dKAڻ B9y;cujGdRUy4]rrCĥO`.c:);Zg*Lܦ0x' ?S t6,"wM95 Udj+bn]+Z?U_ذebKjYj'rhX=)ZIT5LC9Mz"Qȓugz -ԿAqTDlMYҘyNZ4 ! t6ÆZl̫Ęg<]#ldJW-u( /ϓ@UFbXPΒ[w#yD%:);,SA t6uu.Yj$_|E44^ x$Lrh(|@dd ˭Hdz* Ć#+`ȓI$MC(ؠ:s&RA~~l Ę'+CH"NZ8azֳPGH`9Ƙ{֓ O@~bMn(qCש`:sjB\D 39>(Y>'-H$S'oE5䆶ɐc54>#קD5ܡ0.-G!;'-[|KQ/Йkvuh_RG8AcAS&$OD2?#GayڄCkc%KEh/MCH/W  ݿ>bR c16x\%k+R %5檣Sܡ(kn#ھ ŽkLmxR\dYa 6Բ]KPiۦ*c*iiđ_H|)c51|jJd'7 }i2> UFb3+c̹帣s䦝y)Ø2@gceZߦpj3V-5&[D2wavk͞ٳL  0k$LAJ@x@@ɬWmB1ѫTX!3lڋ'L>k]_ {JwdFbIEa:R#K+Of*ZH@*<k咴>(NLj  ,=_ͺm{o&:NSߕ䷬k1qK唷du0UPc@GjDa0`[Ke5`NZgg)0حVw%seWR9mjp/Wmɚ>7<"#21>vg5ʶ~ K݁kg}XMcϤ{!_(.qCܨ֧Zϓ9skqߐJw(B힬J#%]!* HoUe$ ^PJrQ kVo0g\#Þ'=@5ȸMC͡#| '\ :R' Z7[cYΨmi'#}iPUNOMܺʺapT挫&{kޤ!aq&ixb@G*EaHxևT-[zMKV-NBq<"pڭg}ۄ92_2]&V:LVSw%:Wz55䔤+0tttVA->Sɞ Tk=$-#nlַMħSqH}{o˛S%@ƅZsIZYWyƀԊ [[k{Rw`.9MrkQLy~&̖+y\UG#01gIZ\tZ?j϶~k뵽eԸ1J7 5thbNm9"?e$ fyzWijgrnsj斫^exЋ.\{l4ίB?7ew$ݖ/90rSַMuȌ2}~a*}@IZ~ 郎 :SR?}}l@k!I :75n8H:$tVi8X; I6lX2ڒ( {o[?[j3cDI(NLj [լY6!Weԗ};yo.6RyR5Q<*1Da0.2FV>3^s(v xiꅸ7I˵B\Wy:EOUmBǘ~@@x~Q޴y.W}`Dݫ:$-H:wjƳ^6tV|9kFYR~$ݒ/G]eWެ֛gKMC!oRܶ*39MOPO䃪ӯ҆mo:#( V)_toW7ӶaUw HՃ$-+%oMOMW]&ź>r*'s TxZ'i( ~*1+EaPzxVk}׳wzhBRWy:Efݐ["%n}+^ybv{I3G$}Q`c@G׊~ٽT$dP/Wy:E41K)̊Sd \FyT/[LM\V&ъ8BeIZQ06Rlk2 51구bXa`}NĶLYfٱ.P5j%vŽ7zIo`a,c0!76bY?R?Q5]FR&b+eBGQԕ[rZM#22[#e2)ۺ|Wi3JI>rJ?`<@a@C|ϻĶ3:¶O·ZI|UNjuce&."*?/CDDO8I8@Gb@c_ޗlW4u~t`.mFcX-|I E6~|Lo02+~?0'SʿE޸azUڰ]jˢ0 ʆ^_;#e}<еO4-u?^'Q@͚ID@wz6/_n]^[V.tsǯPc1IZ>c9QyBcduQ3:'[QFvDnvo"Ֆ9LEGmN( >* ~ta͚DW}"_(rSD6|]3{ֶ^{H@:ͪ]ߣ6{ns-PJQ.t8@j0sj=d[BȯŒzFC/M4Jզo'/2} *'RS4]qvQwEa`<ХЁ9Da`ry]OgXv쉸_[IZ6ImGq:F/Pq +޲]ĝ~.ʱQSZx*|"Ig0b|S0i,c,+Rw7*ʼn^4_(&eFQ<ڴ=Ȭ{D@JɻSRuʺej5j; pإIZ~$ÀX{;mW4tjϬH@*4QRlO2t[PL=~̾h}ۄY{tgKUZa!|6PK< ZЁrim+2-We$ VVrIZIwcDaV =7oz{'R{.rLdf5NrC:@S=l|lY=\iWk@{LBLWy:E՜mBK92E*?g]?M͟ғ5'$}_GRHhb|jYois\>ݱgҁ۽/K!rKnmیy8IDAT5sopYo힬JIy!uIo€e@B K͵c[6Sձ C͡$-GKQȅ^j}ۄ9e, |DNuFwnoIQ Ё( Gƻ~kXYR-NBq<"pڭwudVa" %mnZv+M&;z5$-%IWDa`Y À ~m[ROUk<@ךzuOs0Iˈ[b6~o1mF\&a3T :@Һk΍&i& g]:2Da-l=O:+;cqN?ne+)ѫ4`[/e\_{ U:9LN_Wu+|9 /t teɶl^Wq HC$[~cP\*O&&~&4~H@:#3v҆:Ț*=II:tt \{mk_u Hףn fI-_(vσa`ZUݶ<^wHST'A_j N:Ԇj%Q( Jtt 7F֧mRq=(05YK;ۧ~QAM%{qFoVf{{o{~٣g8 tLxƓ|2 ;\ :pDa'mY/k* ^pP*OV 7:LwgKW]0W>Nߜ>(ЕЁh)_?}}l@k!X>0VG~X/Z6I2Պ{>m]g~@/Og45C&c}8gKҥQg8Ё,盷7gkCWe$ Qk%H?t( XU͚m}śt<"o&U9~aX>j.>>IG0xU[1Yjg'Gh@N{P/ĽIZ)0xR!s)=Ey{.S}`DUGBOx]%(&vՌgmo鬌r@׊>RwI%_()Io0ϖt HfCޮmUhzr3ӯ҆mogFeQحP7'aUw HՃ$-+%}Q201k40>jܰD@:j}ɶ~EOSgGҒo4ղz͗|xh6~.d%mMxʮ̭>+O[E|AzUڰ]Jˢ03AÀ̃olu:Rև]kH3RU>(NLj V5kh6_'e[ټ'Ѻ|B.<W$-c5+ <`3#cSV6Qvޙ/)0xڴ֛Ҧwm3ӧ\gM$-wKc@IԍK~-Rw`.v6UpKP\*SX;y4xii.#jueUn2*m|bTogTZ}Ea`0ߏ?TV{l_J|$;lpM^ؖǯJ\2 Ϟ;T>\_4q=n੗^t`Eak{tV_Y9szO}IZ.tSDa3Ǵ/|mG&ۺ|U[:>k-=jsEaat`Eae;C~sz_n]Ú5^DP ͍{ȶLM_s Hۍ$-CnT\v% ˥%LCyuyspǓ-)MnW]]Q_t`|󺖱-kq~OҲIo;10(m&ۯxv0'sywjU[YUI|6 'iߢOce9>6T)NR#B1/4Jfݰ|̺;L?%U[&VwWإIH4pX@&nN= 32 MGw6$ݖ/zOo0k/V2KvnD~=_;glŒ.€€,09\4^ɶ-We$ V.IK ct( jX6!GmRod@ AyOe]^=TU9oVc$~C%i:Da0#3:/[VWsv&I{♮t( v9k}ۄ%sewT~޺~:BՖ?$>,@cwVc.e~K3,uҖa5=|][rf?l[nN>O.Ш˿qЁ:eݓUi6ɷ>g.QXЁ*k뱷Ƕ~mcUw HԫCIZd900kVo0g\+Þ'f]^ TYs@w9tTO`O$ Pa@( jYlg3:;;>s==D{/֏Q?5q *d_t3{_?e]_^&WM1ߒtSl_y1kߵľ<{=/i< -i;;58j?dy2}q;0 g_B2eЁ._(b'w]yQtdP\bjٛtK0|\:ـ. vom5Y\Io- )/|` @g`@:@O79H>0hbm#:!6Fc7Wc@' ťbAVթ~+0?!a@:H7o0F|t 喜/-8( 1$ scikit-build-0.18.1/docs/make.bat000066400000000000000000000145031466366435500165460ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\complexity.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\complexity.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end scikit-build-0.18.1/docs/make_a_release.rst000066400000000000000000000162051466366435500206110ustar00rootroot00000000000000.. _making_a_release: ================ Making a release ================ A core developer should use the following steps to create a release ``X.Y.Z`` of **scikit-build** on `PyPI`_ and `Conda`_. ------------- Prerequisites ------------- * All CI tests are passing on `GitHub Actions`_ and `Azure Pipelines`_. * You have a `GPG signing key `_. ------------------------- Documentation conventions ------------------------- The commands reported below should be evaluated in the same terminal session. Commands to evaluate starts with a dollar sign. For example:: $ echo "Hello" Hello means that ``echo "Hello"`` should be copied and evaluated in the terminal. ---------------------- Setting up environment ---------------------- 1. First, `register for an account on PyPI `_. 2. If not already the case, ask to be added as a ``Package Index Maintainer``. 3. Create a ``~/.pypirc`` file with your login credentials:: [distutils] index-servers = pypi pypitest [pypi] username= password= [pypitest] repository=https://test.pypi.org/legacy/ username= password= where ```` and ```` correspond to your PyPI account. --------------------- `PyPI`_: Step-by-step --------------------- 1. Make sure that all CI tests are passing on `GitHub Actions`_ and `Azure Pipelines`_. 2. Download the latest sources (or use an existing git checkout) .. code:: $ cd /tmp && \ git clone git@github.com:scikit-build/scikit-build && \ cd scikit-build 3. List all tags sorted by creation date .. code:: $ git tag -l --sort creatordate 4. Choose the next release version number .. code:: $ release=X.Y.Z .. warning:: To ensure the packages are uploaded on `PyPI`_, tags must match this regular expression: ``^[0-9]+(\.[0-9]+)*(\.post[0-9]+)?$``. 5. In ``CHANGES.rst`` replace ``Next Release`` section header with ``Scikit-build X.Y.Z`` and commit the changes. .. code:: $ git add CHANGES.rst && \ git commit -m "Scikit-build $release" 6. Tag the release .. code:: $ git tag --sign -m "Scikit-build $release" $release main .. warning:: We recommend using a `GPG signing key `_ to sign the tag. 7. Publish both the release tag and the main branch .. code:: $ git push origin $release && \ git push origin main 8. Make a `GitHub release `_. Paste the converted release notes as markdown; convert using .. code:: cat CHANGES.rst | pandoc -f rst -t gfm and then edit the result (it will not be perfect) to prepare the body of the release. You can also try `clipboardtomarkdown `_ or copying to a draft `discord `_ post. PRs should be converted to simple ``#`` form. Be sure to use the tag you just pushed as the tag version, and ``Scikit-build X.Y.Z`` should be the name. .. note:: For examples of releases, see https://github.com/scikit-build/scikit-build/releases 9. Add a ``Next Release`` section back in ``CHANGES.rst``, commit and push local changes. .. code:: $ git add CHANGES.rst && \ git commit -m "CHANGES.rst: Add \"Next Release\" section [ci skip]" && \ git push origin main 10. Add an entry to the ``Announcements`` category of the `scikit-build discussions board`_. .. note:: For examples of announcements, see https://github.com/orgs/scikit-build/discussions/categories/announcements .. _virtualenvwrapper: https://virtualenvwrapper.readthedocs.io/ .. _virtualenv: http://virtualenv.readthedocs.io .. _venv: https://docs.python.org/3/library/venv.html .. _Azure Pipelines: https://dev.azure.com/scikit-build/scikit-build/_build .. _GitHub Actions: https://github.com/scikit-build/scikit-build/actions .. _PyPI: https://pypi.org/project/scikit-build .. _TestPyPI: https://test.pypi.org/project/scikit-build .. _scikit-build discussions board: https://github.com/orgs/scikit-build/discussions/categories/announcements ----------------------- `Conda`_: Step-by-step ----------------------- .. warning:: Publishing on conda requires to have corresponding the corresponding Github release. After a GitHub release is created in the `scikit-build `_ project and after the conda-forge `Autoticking Bot `_ creates a pull request on the `scikit-build-feedstock`_ , follow these steps to finalize the conda package release: 1. Review the pull-request 2. Merge pull-request In case the bot failed (e.g because of GH rate limitation) and in order to explicitly release a new version on conda-forge, follow the steps below: 1. Choose the next release version number (that matches with the PyPI version last published) .. code:: $ release=X.Y.Z 2. Fork scikit-build-feedstock First step is to fork `scikit-build-feedstock`_ repository. This is the recommended `best practice `_ by conda. 3. Clone forked feedstock Fill the YOURGITHUBUSER part. .. code:: $ YOURGITHUBUSER=user $ cd /tmp && git clone https://github.com/$YOURGITHUBUSER/scikit-build-feedstock.git 4. Download corresponding source for the release version .. code:: $ cd /tmp && \ wget https://github.com/scikit-build/scikit-build/archive/$release.tar.gz 5. Create a new branch .. code:: $ cd scikit-build-feedstock && \ git checkout -b $release 6. Modify ``meta.yaml`` Update the `version string `_ and `sha256 `_. We have to modify the sha and the version string in the ``meta.yaml`` file. For linux flavors: .. code:: $ sed -i "1s/.*/{% set version = \"$release\" %}/" recipe/meta.yaml && \ sha=$(openssl sha256 /tmp/$release.tar.gz | awk '{print $2}') && \ sed -i "2s/.*/{% set sha256 = \"$sha\" %}/" recipe/meta.yaml For macOS: .. code:: $ sed -i -- "1s/.*/{% set version = \"$release\" %}/" recipe/meta.yaml && \ sha=$(openssl sha256 /tmp/$release.tar.gz | awk '{print $2}') && \ sed -i -- "2s/.*/{% set sha256 = \"$sha\" %}/" recipe/meta.yaml Commit local changes. .. code:: $ git add recipe/meta.yaml && \ git commit -m "scikit-build v$release version" 7. Push the changes .. code:: $ git push origin $release 8. Create a Pull Request Create a pull request against the `main repository `_. If the tests are passed a new release will be published on Anaconda cloud. .. _Conda: https://anaconda.org/conda-forge/scikit-build .. _scikit-build-feedstock: https://github.com/conda-forge/scikit-build-feedstock scikit-build-0.18.1/docs/modules.rst000066400000000000000000000000721466366435500173370ustar00rootroot00000000000000skbuild ======= .. toctree:: :maxdepth: 4 skbuild scikit-build-0.18.1/docs/skbuild.command.rst000066400000000000000000000044021466366435500207420ustar00rootroot00000000000000skbuild.command package ======================= .. automodule:: skbuild.command :members: :undoc-members: :show-inheritance: Submodules ---------- skbuild.command.bdist module ---------------------------- .. automodule:: skbuild.command.bdist :members: :undoc-members: :show-inheritance: skbuild.command.bdist\_wheel module ----------------------------------- .. automodule:: skbuild.command.bdist_wheel :members: :undoc-members: :show-inheritance: skbuild.command.build module ---------------------------- .. automodule:: skbuild.command.build :members: :undoc-members: :show-inheritance: skbuild.command.build\_ext module --------------------------------- .. automodule:: skbuild.command.build_ext :members: :undoc-members: :show-inheritance: skbuild.command.build\_py module -------------------------------- .. automodule:: skbuild.command.build_py :members: :undoc-members: :show-inheritance: skbuild.command.clean module ---------------------------- .. automodule:: skbuild.command.clean :members: :undoc-members: :show-inheritance: skbuild.command.egg\_info module -------------------------------- .. automodule:: skbuild.command.egg_info :members: :undoc-members: :show-inheritance: skbuild.command.generate\_source\_manifest module ------------------------------------------------- .. automodule:: skbuild.command.generate_source_manifest :members: :undoc-members: :show-inheritance: skbuild.command.install module ------------------------------ .. automodule:: skbuild.command.install :members: :undoc-members: :show-inheritance: skbuild.command.install\_lib module ----------------------------------- .. automodule:: skbuild.command.install_lib :members: :undoc-members: :show-inheritance: skbuild.command.install\_scripts module --------------------------------------- .. automodule:: skbuild.command.install_scripts :members: :undoc-members: :show-inheritance: skbuild.command.sdist module ---------------------------- .. automodule:: skbuild.command.sdist :members: :undoc-members: :show-inheritance: skbuild.command.test module --------------------------- .. automodule:: skbuild.command.test :members: :undoc-members: :show-inheritance: scikit-build-0.18.1/docs/skbuild.platform_specifics.rst000066400000000000000000000035661466366435500232120ustar00rootroot00000000000000skbuild.platform\_specifics package =================================== .. automodule:: skbuild.platform_specifics :members: :undoc-members: :show-inheritance: Submodules ---------- skbuild.platform\_specifics.abstract module ------------------------------------------- .. automodule:: skbuild.platform_specifics.abstract :members: :undoc-members: :show-inheritance: skbuild.platform\_specifics.bsd module -------------------------------------- .. automodule:: skbuild.platform_specifics.bsd :members: :undoc-members: :show-inheritance: skbuild.platform\_specifics.cygwin module ----------------------------------------- .. automodule:: skbuild.platform_specifics.cygwin :members: :undoc-members: :show-inheritance: skbuild.platform\_specifics.linux module ---------------------------------------- .. automodule:: skbuild.platform_specifics.linux :members: :undoc-members: :show-inheritance: skbuild.platform\_specifics.osx module -------------------------------------- .. automodule:: skbuild.platform_specifics.osx :members: :undoc-members: :show-inheritance: skbuild.platform\_specifics.aix module ---------------------------------------- .. automodule:: skbuild.platform_specifics.aix :members: :undoc-members: :show-inheritance: skbuild.platform\_specifics.platform\_factory module ---------------------------------------------------- .. automodule:: skbuild.platform_specifics.platform_factory :members: :undoc-members: :show-inheritance: skbuild.platform\_specifics.unix module --------------------------------------- .. automodule:: skbuild.platform_specifics.unix :members: :undoc-members: :show-inheritance: skbuild.platform\_specifics.windows module ------------------------------------------ .. automodule:: skbuild.platform_specifics.windows :members: :undoc-members: :show-inheritance: scikit-build-0.18.1/docs/skbuild.rst000066400000000000000000000014761466366435500173350ustar00rootroot00000000000000skbuild package =============== .. automodule:: skbuild :members: :undoc-members: :show-inheritance: Subpackages ----------- .. toctree:: :maxdepth: 4 skbuild.command skbuild.platform_specifics skbuild.utils Submodules ---------- skbuild.cmaker module --------------------- .. automodule:: skbuild.cmaker :members: :undoc-members: :show-inheritance: skbuild.constants module ------------------------ .. automodule:: skbuild.constants :members: :undoc-members: :show-inheritance: skbuild.exceptions module ------------------------- .. automodule:: skbuild.exceptions :members: :undoc-members: :show-inheritance: skbuild.setuptools\_wrap module ------------------------------- .. automodule:: skbuild.setuptools_wrap :members: :undoc-members: :show-inheritance: scikit-build-0.18.1/docs/skbuild.utils.rst000066400000000000000000000002011466366435500204550ustar00rootroot00000000000000skbuild.utils package ===================== .. automodule:: skbuild.utils :members: :undoc-members: :show-inheritance: scikit-build-0.18.1/docs/usage.rst000066400000000000000000000536111466366435500170020ustar00rootroot00000000000000 .. _why: =============================== Why should I use scikit-build ? =============================== Scikit-build is a replacement for `distutils.core.Extension `_ with the following advantages: - provide better support for :doc:`additional compilers and build systems ` - first-class :ref:`cross-compilation ` support - location of dependencies and their associated build requirements =========== Basic Usage =========== .. _basic_usage_example: Example of setup.py, CMakeLists.txt and pyproject.toml ------------------------------------------------------ The full example code is `Here `_ Make a fold name my_project as your project root folder, place the following in your project's ``setup.py`` file:: from skbuild import setup # This line replaces 'from setuptools import setup' setup( name="hello-cpp", version="1.2.3", description="a minimal example package (cpp version)", author='The scikit-build team', license="MIT", packages=['hello'], python_requires=">=3.7", ) Your project now uses scikit-build instead of setuptools. Next, add a ``CMakeLists.txt`` to describe how to build your extension. In the following example, a C++ extension named ``_hello`` is built:: cmake_minimum_required(VERSION 3.18...3.22) project(hello) find_package(PythonExtensions REQUIRED) add_library(_hello MODULE hello/_hello.cxx) python_extension_module(_hello) install(TARGETS _hello LIBRARY DESTINATION hello) Then, add a ``pyproject.toml`` to list the build system requirements:: [build-system] requires = [ "setuptools>=42", "scikit-build>=0.13", "cmake>=3.18", "ninja", ] build-backend = "setuptools.build_meta" Make a hello folder inside my_project folder and place `_hello.cxx `_ and `__init__.py `_ inside hello folder. Now every thing is ready, go to my_project's parent folder and type following command to install your extension:: pip install my_project/. If you want to see the detail of installation:: pip install my_project/. -v Try your new extension:: $ python Python 3.10.4 (main, Jun 29 2022, 12:14:53) [GCC 11.2.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import hello >>> hello.hello("scikit-build") Hello, scikit-build! >>> You can add lower limits to ``cmake`` or ``scikit-build`` as needed. Ninja should be limited to non-Windows systems, as MSVC 2017+ ships with Ninja already, and there are fall-backs if Ninja is missing, and the Python Ninja seems to be less likely to find MSVC than the built-in one currently. .. note:: By default, scikit-build looks in the project top-level directory for a file named ``CMakeLists.txt``. It will then invoke ``cmake`` executable specifying a :doc:`generator ` matching the python being used. .. _usage-setup_options: Setup options ------------- setuptools options ^^^^^^^^^^^^^^^^^^ The section below documents some of the options accepted by the ``setup()`` function. These currently must be passed in your ``setup.py``, not in ``setup.cfg``, as scikit-build intercepts them and inspects them. This restriction may be relaxed in the future. Setuptools options not listed here can be placed in ``setup.cfg`` as normal. - ``packages``: Explicitly list of all packages to include in the distribution. Setuptools will not recursively scan the source tree looking for any directory with an ``__init__.py`` file. To automatically generate the list of packages, see `Using find_package()`_. - ``package_dir``: A mapping of package to directory names - ``include_package_data``: If set to ``True``, this tells setuptools to automatically include any data files it finds inside your package directories that are specified by your ``MANIFEST.in`` file. For more information, see the setuptools documentation section on `Including Data Files`_. scikit-build matches `the setuptools behavior `__ of defaulting this parameter to ``True`` if a pyproject.toml file exists and contains either the ``project`` or ``tool.setuptools`` table. - ``package_data``: A dictionary mapping package names to lists of glob patterns. For a complete description and examples, see the setuptools documentation section on `Including Data Files`_. You do not need to use this option if you are using include_package_data, unless you need to add e.g. files that are generated by your setup script and build process. (And are therefore not in source control or are files that you don't want to include in your source distribution.) - ``exclude_package_data``: Dictionary mapping package names to lists of glob patterns that should be excluded from the package directories. You can use this to trim back any excess files included by include_package_data. For a complete description and examples, see the setuptools documentation section on `Including Data Files`_. - ``py_modules``: List all modules rather than listing packages. More details in the `Listing individual modules`_ section of the distutils documentation. - ``data_files``: Sequence of ``(directory, files)`` pairs. Each ``(directory, files)`` pair in the sequence specifies the installation directory and the files to install there. More details in the `Installing Additional Files`_ section of the setuptools documentation. - ``entry_points``: A dictionary mapping entry point group names to strings or lists of strings defining the entry points. Entry points are used to support dynamic discovery of services or plugins provided by a project. See `Dynamic Discovery of Services and Plugins`_ for details and examples of the format of this argument. In addition, this keyword is used to support `Automatic Script Creation`_. Note that if using ``pyproject.toml`` for configuration, the requirement to put ``entry_points`` in ``setup.py`` also requires that the ``project`` section include ``entry_points`` in the ``dynamic`` section. - ``scripts``: List of python script relative paths. If the first line of the script starts with ``#!`` and contains the word ``python``, the Distutils will adjust the first line to refer to the current interpreter location. More details in the `Installing Scripts `_ section of the distutils documentation. .. versionadded:: 0.8.0 - ``zip_safe``: A boolean indicating if the Python packages may be run directly from a zip file. If not already set, scikit-build sets this option to ``False``. See `Setting the zip_safe flag`_ section of the setuptools documentation. .. note:: As specified in the `Wheel documentation`_, the ``universal`` and ``python-tag`` options have no effect. .. _Using find_package(): https://setuptools.readthedocs.io/en/latest/setuptools.html#using-find-packages .. _Including Data Files: https://setuptools.readthedocs.io/en/latest/setuptools.html#including-data-files .. _Installing Additional Files: https://docs.python.org/3/distutils/setupscript.html#installing-additional-files .. _Listing individual modules: https://docs.python.org/3/distutils/setupscript.html#listing-individual-modules .. _Dynamic Discovery of Services and Plugins: https://setuptools.readthedocs.io/en/latest/setuptools.html#dynamic-discovery-of-services-and-plugins .. _Automatic Script Creation: https://setuptools.readthedocs.io/en/latest/setuptools.html#automatic-script-creation .. _Setting the zip_safe flag: https://setuptools.readthedocs.io/en/latest/setuptools.html#setting-the-zip-safe-flag .. _Wheel documentation: https://wheel.readthedocs.io/en/stable/ scikit-build options ^^^^^^^^^^^^^^^^^^^^ Scikit-build augments the ``setup()`` function with the following options: - ``cmake_args``: List of `CMake options `_. For example:: setup( [...] cmake_args=['-DSOME_FEATURE:BOOL=OFF'] [...] ) - ``cmake_install_dir``: relative directory where the CMake artifacts are installed. By default, it is set to an empty string. - ``cmake_source_dir``: Relative directory containing the project ``CMakeLists.txt``. By default, it is set to the top-level directory where ``setup.py`` is found. - ``cmake_process_manifest_hook``: Python function consuming the list of files to be installed produced by cmake. For example, ``cmake_process_manifest_hook`` can be used to exclude static libraries from the built wheel. For example:: def exclude_static_libraries(cmake_manifest): return list(filter(lambda name: not (name.endswith('.a')), cmake_manifest)) setup( [...] cmake_process_manifest_hook=exclude_static_libraries [...] ) .. _usage-cmake_with_sdist: .. versionadded:: 0.5.0 - ``cmake_with_sdist``: Boolean indicating if CMake should be executed when running ``sdist`` command. Setting this option to ``True`` is useful when part of the sources specified in ``MANIFEST.in`` are downloaded by CMake. By default, this option is ``False``. .. _usage-cmake_languages: .. versionadded:: 0.7.0 - ``cmake_languages``: Tuple of languages that the project use, by default ``('C', 'CXX',)``. This option ensures that a generator is chosen that supports all languages for the project. - ``cmake_minimum_required_version``: String identifying the minimum version of CMake required to configure the project. - ``cmake_install_target``: Name of the target to "build" for installing the artifacts into the wheel. By default, this option is set to ``install``, which is always provided by CMake. This can be used to only install certain components. For example:: install(TARGETS foo COMPONENT runtime) add_custom_target(foo-install-runtime ${CMAKE_COMMAND} -DCMAKE_INSTALL_COMPONENT=runtime -P "${PROJECT_BINARY_DIR}/cmake_install.cmake" DEPENDS foo ) Scikit-build changes the following options: .. versionadded:: 0.7.0 - ``setup_requires``: If ``cmake`` is found in the list, it is explicitly installed first by scikit-build. Command line options -------------------- Warning: Passing options to ``setup.py`` is deprecated and may be removed in a future release. Environment variables can be used instead for most options. :: usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] [skbuild_opts] [cmake_configure_opts] [-- [cmake_opts] [-- [build_tool_opts]]] or: setup.py --help [cmd1 cmd2 ...] or: setup.py --help-commands or: setup.py cmd --help There are few types of options: - :ref:`setuptools options `: - ``[global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]`` - ``--help [cmd1 cmd2 ...]`` - ``cmd --help`` - :ref:`scikit-build options `: ``[skbuild_opts]`` - :ref:`CMake configure options `: ``[cmake_configure_opts]`` - :ref:`CMake options `: ``[cmake_opts]`` - :ref:`build tool options`:``[build_tool_opts]`` setuptools, scikit-build and CMake configure options can be passed normally, the cmake and build_tool set of options needs to be separated by ``--``:: Arguments following a "--" are passed directly to CMake (e.g. -DSOME_FEATURE:BOOL=ON). Arguments following a second "--" are passed directly to the build tool. .. _usage-setuptools_options: setuptools options ^^^^^^^^^^^^^^^^^^ For more details, see the `official documentation `_. scikit-build extends the global set of setuptools options with: .. versionadded:: 0.4.0 :: Global options: [...] --hide-listing do not display list of files being included in the distribution .. versionadded:: 0.5.0 :: Global options: [...] --force-cmake always run CMake --skip-cmake do not run CMake .. note:: As specified in the `Wheel documentation`_, the ``--universal`` and ``--python-tag`` options have no effect. .. _usage_scikit-build_options: scikit-build options ^^^^^^^^^^^^^^^^^^^^ :: scikit-build options: --build-type specify the CMake build type (e.g. Debug or Release) -G , --generator specify the CMake build system generator -j N allow N build jobs at once [...] .. versionadded:: 0.7.0 :: scikit-build options: [...] --cmake-executable specify the path to the cmake executable .. versionadded:: 0.8.0 :: scikit-build options: [...] --skip-generator-test skip generator test when a generator is explicitly selected using --generator .. _usage_cmake_configure_options: CMake Configure options ^^^^^^^^^^^^^^^^^^^^^^^ .. versionadded:: 0.10.1 These options are relevant when configuring a project and can be passed as global options using ``setup.py`` or ``pip install``. The CMake options accepted as global options are any of the following: :: -C = Pre-load a script to populate the cache. -D[:]= = Create or update a cmake cache entry. .. warning:: The CMake configure option should be passed without spaces. For example, use `-DSOME_FEATURE:BOOL=ON` instead of `-D SOME_FEATURE:BOOL=ON`. .. _usage_cmake_options: CMake options ^^^^^^^^^^^^^ These are any specific to CMake. See list of `CMake options `_. For example:: -DSOME_FEATURE:BOOL=OFF .. _usage_build_tool_options: build tool options ^^^^^^^^^^^^^^^^^^ These are specific to the underlying build tool (e.g msbuild.exe, make, ninja). ============== Advanced Usage ============== How to test if scikit-build is driving the compilation ? -------------------------------------------------------- To support the case of code base being built as both a standalone project and a python wheel, it is possible to test for the variable ``SKBUILD``: .. code-block:: cmake if(SKBUILD) message(STATUS "The project is built using scikit-build") endif() Adding cmake as building requirement only if not installed or too low a version ------------------------------------------------------------------------------- If systematically installing cmake wheel is not desired, it is possible to set it using an ``in-tree backend``. For this purpose place the following configuration in your ``pyproject.toml``:: [build-system] requires = [ "setuptools>=42", "packaging", "scikit-build", "ninja; platform_system!='Windows'" ] build-backend = "backend" backend-path = ["_custom_build"] then you can implement a thin wrapper around ``build_meta`` in the ``_custom_build/backend.py`` file:: from setuptools import build_meta as _orig prepare_metadata_for_build_wheel = _orig.prepare_metadata_for_build_wheel build_wheel = _orig.build_wheel build_sdist = _orig.build_sdist get_requires_for_build_sdist = _orig.get_requires_for_build_sdist def get_requires_for_build_wheel(config_settings=None): from packaging import version from skbuild.exceptions import SKBuildError from skbuild.cmaker import get_cmake_version packages = [] try: if version.parse(get_cmake_version()) < version.parse("3.4"): packages.append('cmake') except SKBuildError: packages.append('cmake') return _orig.get_requires_for_build_wheel(config_settings) + packages Also see `scikit-build-core `_ where this is a built-in feature. .. _usage_enabling_parallel_build: Enabling parallel build ----------------------- Ninja ^^^^^ If :ref:`Ninja` generator is used, the associated build tool (called ``ninja``) will automatically parallelize the build based on the number of available CPUs. To limit the number of parallel jobs, the build tool option ``-j N`` can be passed to ``ninja``. For example, to limit the number of parallel jobs to ``3``, the following could be done:: python setup.py bdist_wheel -- -- -j3 For complex projects where more granularity is required, it is also possible to limit the number of simultaneous link jobs, or compile jobs, or both. Indeed, starting with CMake 3.11, it is possible to configure the project with these options: * `CMAKE_JOB_POOL_COMPILE `_ * `CMAKE_JOB_POOL_LINK `_ * `CMAKE_JOB_POOLS `_ For example, to have at most ``5`` compile jobs and ``2`` link jobs, the following could be done:: python setup.py bdist_wheel -- \ -DCMAKE_JOB_POOL_COMPILE:STRING=compile \ -DCMAKE_JOB_POOL_LINK:STRING=link \ '-DCMAKE_JOB_POOLS:STRING=compile=5;link=2' Unix Makefiles ^^^^^^^^^^^^^^ If :ref:`Unix Makefiles` generator is used, the associated build tool (called ``make``) will **NOT** automatically parallelize the build, the user has to explicitly pass option like ``-j N``. For example, to limit the number of parallel jobs to ``3``, the following could be done:: python setup.py bdist_wheel -- -- -j3 Visual Studio IDE ^^^^^^^^^^^^^^^^^ If :ref:`Visual Studio IDE` generator is used, there are two types of parallelism: * target level parallelism * object level parallelism .. warning:: Since finding the right combination of parallelism can be challenging, whenever possible we recommend to use the `Ninja`_ generator. To adjust the object level parallelism, the compiler flag ``/MP[processMax]`` could be specified. To learn more, read `/MP (Build with Multiple Processes) `_. For example:: set CXXFLAGS=/MP4 python setup.py bdist_wheel The target level parallelism can be set from command line using ``/maxcpucount:N``. This defines the number of simultaneous ``MSBuild.exe`` processes. To learn more, read `Building Multiple Projects in Parallel with MSBuild `_. For example:: python setup.py bdist_wheel -- -- /maxcpucount:4 .. _support_isolated_build: Support for isolated build -------------------------- .. versionadded:: 0.8.0 As specified in `PEP 518`_, dependencies required at install time can be specified using a ``pyproject.toml`` file. Starting with pip 10.0, pip reads the ``pyproject.toml`` file and installs the associated dependencies in an isolated environment. See the `pip build system interface`_ documentation. An isolated environment will be created when using pip to install packages directly from source or to create an editable installation. scikit-build supports these use cases as well as the case where the isolated environment support is explicitly disabled using the pip option ``--no-build-isolation`` available with the ``install``, ``download`` and ``wheel`` commands. .. _PEP 518: https://www.python.org/dev/peps/pep-0518/ .. _pip build system interface: https://pip.pypa.io/en/stable/reference/pip/#build-system-interface .. _optimized_incremental_build: Optimized incremental build --------------------------- To optimize the developer workflow, scikit-build reconfigures the CMake project only when needed. It caches the environment associated with the generator as well as the CMake execution properties. The CMake properties are saved in a :func:`CMake spec file ` responsible to store the CMake executable path, the CMake configuration arguments, the CMake version as well as the environment variables ``PYTHONNOUSERSITE`` and ``PYTHONPATH``. If there are no ``CMakeCache.txt`` file or if any of the CMake properties changes, scikit-build will explicitly reconfigure the project calling :meth:`skbuild.cmaker.CMaker.configure`. If a file is added to the CMake build system by updating one of the ``CMakeLists.txt`` file, scikit-build will not explicitly reconfigure the project. Instead, the generated build-system will automatically detect the change and reconfigure the project after :meth:`skbuild.cmaker.CMaker.make` is called. Environment variable configuration ---------------------------------- Scikit-build support environment variables to configure some options. These are: ``SKBUILD_CONFIGURE_OPTIONS``/``CMAKE_ARGS`` This will add configuration options when configuring CMake. ``SKBUILD_CONFIGURE_OPTIONS`` will be used instead of ``CMAKE_ARGS`` if both are defined. ``SKBUILD_BUILD_OPTIONS`` Pass options to the build. .. _cross_compilation: Cross-compilation ----------------- See `CMake Toolchains `_. Introduction to dockross ^^^^^^^^^^^^^^^^^^^^^^^^ .. note:: *To be documented.* See :issue:`227`. Using dockcross-manylinux to generate Linux wheels ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. note:: *To be documented.* See :issue:`227`. Using dockcross-mingwpy to generate Windows wheels ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. note:: *To be documented.* See :issue:`227`. Examples for scikit-build developers ------------------------------------ .. note:: *To be documented.* See :issue:`227`. Provide small, self-contained setup function calls for (at least) two use cases: - when a `CMakeLists.txt` file already exists - when a user wants scikit-build to create a `CMakeLists.txt` file based on the user specifying some input files. scikit-build-0.18.1/noxfile.py000066400000000000000000000111401466366435500162210ustar00rootroot00000000000000from __future__ import annotations import argparse import os import shutil import sys from pathlib import Path import nox nox.needs_version = ">=2024.3.2" nox.options.default_venv_backend = "uv|virtualenv" nox.options.sessions = ["lint", "tests"] PYTHON_ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "pypy3.7", "pypy3.8", "pypy3.9", "pypy3.10"] MSVC_ALL_VERSIONS = {"2017", "2019", "2022"} @nox.session def lint(session): """ Run the linters. """ session.install("pre-commit") session.run("pre-commit", "run", "-a") @nox.session(python=PYTHON_ALL_VERSIONS) def tests(session: nox.Session) -> None: """ Run the tests. """ posargs = list(session.posargs) env = os.environ.copy() # This should be handled via markers or some other pytest mechanism, but for now, this is usable. # nox -s tests-3.9 -- 2017 2019 if sys.platform.startswith("win") and MSVC_ALL_VERSIONS & set(posargs): known_MSVC = {arg for arg in posargs if arg in MSVC_ALL_VERSIONS} posargs = [arg for arg in posargs if arg not in MSVC_ALL_VERSIONS] for version in MSVC_ALL_VERSIONS: contained = "1" if version in known_MSVC else "0" env[f"SKBUILD_TEST_FIND_VS{version}_INSTALLATION_EXPECTED"] = contained numpy = [] if "pypy" in session.python or "3.13" in session.python else ["numpy"] install_spec = "-e.[test,cov,doctest]" if "--cov" in posargs else ".[test,doctest]" if "--cov" in posargs: posargs.append("--cov-config=pyproject.toml") # Latest versions may break things, so grab them for testing! session.install("-U", "setuptools", "wheel", "virtualenv") session.install(install_spec, *numpy) session.run("pytest", *posargs, env=env) @nox.session def pylint(session): """ Run PyLint. """ session.install(".", "pylint", "cmake", "distro") session.run("pylint", "skbuild", *session.posargs) @nox.session def docs(session): """ Build the docs. Use "-R" to rebuild faster. Check options with "-- -h". """ parser = argparse.ArgumentParser(prog=f"{Path(sys.argv[0]).name} -s docs") parser.add_argument("--serve", action="store_true", help="Serve the docs") args = parser.parse_args(session.posargs) session.install("-e.[docs]") session.chdir("docs") shutil.rmtree("_build") session.run("sphinx-build", "-M", "html", ".", "_build", "-W") if args.serve: print("Launching docs at http://localhost:8000/ - use Ctrl-C to quit") session.run("python", "-m", "http.server", "8000", "-d", "_build/html") @nox.session def build(session): """ Make an SDist and a wheel. """ session.install("build") session.run("python", "-m", "build") @nox.session(reuse_venv=True) def build_api_docs(session: nox.Session) -> None: """ Build (regenerate) API docs. """ session.install("sphinx") session.chdir("docs") session.run( "sphinx-apidoc", "-o", ".", "--no-toc", "--force", "--module-first", "../skbuild", ) @nox.session(reuse_venv=True) 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 --system-site-packages nox -s downstream -- https://github.com/... # (requires tomli, so allowing access to system-site-packages) parser = argparse.ArgumentParser(prog=f"{Path(sys.argv[0]).name} -s downstream") parser.add_argument("path", help="Location to git clone from") args, remaining = parser.parse_known_args(session.posargs) if sys.version_info < (3, 11): import tomli as tomllib else: import tomllib tmp_dir = Path(session.create_tmp()) proj_dir = tmp_dir / "git" session.install("build", "hatch-fancy-pypi-readme", "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.path, *remaining, proj_dir, "--recurse-submodules", external=True) session.chdir(proj_dir) # Read and strip requirements pyproject_toml = Path("pyproject.toml") with pyproject_toml.open("rb") as f: pyproject = tomllib.load(f) requires = (x for x in pyproject["build-system"]["requires"] if "scikit-build" not in x.replace("_", "-")) session.install(*requires) session.run("python", "-m", "build", "--no-isolation", "--skip-dependency-check", "--wheel", ".") scikit-build-0.18.1/pyproject.toml000066400000000000000000000155141466366435500171300ustar00rootroot00000000000000[build-system] requires = ["hatchling", "hatch-fancy-pypi-readme", "hatch-vcs"] build-backend = "hatchling.build" [project] name = "scikit-build" dynamic = ["version", "readme"] description = "Improved build system generator for Python C/C++/Fortran/Cython extensions" requires-python = ">=3.7" authors = [ { name = "The scikit-build team" }, ] keywords = [ "scikit-build", ] classifiers = [ "Development Status :: 2 - Pre-Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Natural Language :: English", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.7", "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", "Typing :: Typed", ] dependencies = [ 'distro', 'packaging', 'setuptools>=42.0.0', 'tomli; python_version<"3.11"', 'typing-extensions>=3.7; python_version<"3.8"', 'wheel>=0.32.0', ] [project.optional-dependencies] cov = [ "coverage[toml]>=4.2", "pytest-cov>=2.7.1", ] docs = [ "pygments", "sphinx-issues", "sphinx-rtd-theme>=1.0", "sphinx>=4", "sphinxcontrib-moderncmakedomain>=3.19", ] doctest = [ "ubelt>=0.8.2", "xdoctest>=0.10.0", ] test = [ 'build>=0.7', 'cython>=0.25.1', 'importlib-metadata;python_version<"3.8"', 'pip', 'pytest-mock>=1.10.4', 'pytest>=6.0.0', 'requests', 'virtualenv', ] [project.urls] "Bug Tracker" = "https://github.com/scikit-build/scikit-build/issues" Changelog = "https://scikit-build.readthedocs.io/en/latest/history.html" Discussions = "https://github.com/orgs/scikit-build/discussions" Documentation = "https://scikit-build.readthedocs.io/" Examples = "https://github.com/scikit-build/scikit-build-sample-projects" Homepage = "https://github.com/scikit-build/scikit-build" [tool.hatch] build.targets.wheel.packages = ["skbuild"] version.source = "vcs" build.hooks.vcs.version-file = "skbuild/_version.py" [tool.hatch.metadata.hooks.fancy-pypi-readme] content-type = "text/x-rst" [[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]] path = "README.rst" start-after = ".. START-INTRO" end-before = ".. END-INTRO" [[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]] path = "CHANGES.rst" start-after = ".. START-BRIEF-CHANGELOG" end-before = ".. END-BRIEF-CHANGELOG" [[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]] path = "README.rst" start-after = ".. INJECT-CHANGELOG" [[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]] pattern = ':pr:`(\d+)`' replacement = '`#\1 `_' [[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]] pattern = ':issue:`(\d+)`' replacement = '`#\1 `_' [[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]] pattern = ':user:`(\S+)`' replacement = '`@\1 `_' [tool.mypy] files = ["skbuild", "tests"] exclude = ["tests/samples"] python_version = "3.8" warn_unused_configs = true enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] strict = true disallow_untyped_defs = false disallow_untyped_calls = false [[tool.mypy.overrides]] module = ["skbuild.*"] disallow_untyped_defs = true disallow_untyped_calls = true [[tool.mypy.overrides]] module = [ "wheel.*", "setuptools.logging", "setuptools.command.*", "numpy", "distro", ] ignore_missing_imports = true [tool.pylint] py-version = "3.7" jobs = "0" reports.output-format = "colorized" similarities.ignore-imports = "yes" messages_control.enable = [ "useless-suppression", ] messages_control.disable = [ "deprecated-module", # distutils usage "fixme", "invalid-name", "line-too-long", "missing-module-docstring", "no-member", "protected-access", "too-few-public-methods", "too-many-arguments", "too-many-branches", "too-many-locals", "too-many-statements", "too-many-lines", "too-many-return-statements", "too-many-locals", "ungrouped-imports", "wrong-import-order", "wrong-import-position", "global-statement", # Covered by Ruff "unused-import", # Covered by Ruff ] [tool.pytest.ini_options] minversion = "6.0" testpaths = ["tests"] addopts = ["-ra", "--strict-markers", "--strict-config", "--showlocals"] norecursedirs = ["_skbuild"] xfail_strict = true filterwarnings = [ 'error', 'ignore:setup.py install is deprecated:Warning', 'ignore:easy_install command is deprecated:Warning', 'ignore:setuptools.installer and fetch_build_eggs are deprecated:Warning', 'ignore:.*ends with a trailing slash, which is not supported by setuptools:FutureWarning', "ignore:Config variable 'Py_DEBUG' is unset:RuntimeWarning", "ignore:Config variable 'WITH_PYMALLOC' is unset, Python ABI tag may be incorrect:RuntimeWarning", 'default:shell/Perl-style subs.* are deprecated:DeprecationWarning', 'default:subprocess .* is still running:ResourceWarning', 'ignore:pkg_resources is deprecated as an API:DeprecationWarning', 'ignore:onerror argument is deprecated, use onexc instead:DeprecationWarning', # Caused by wheel and Python 3.12 'ignore:(ast.Str|Attribute s|ast.NameConstant|ast.Num) is deprecated:DeprecationWarning:_pytest', ] log_cli_level = "info" markers = [ "fortran: Fortran testing", "deprecated: These tests deprecated setuptools features", "nosetuptoolsscm: Requires setuptools_scm to not be installed", "isolated: Downloads dependencies and sets up isolated environments", ] [tool.ruff] line-length = 120 exclude = [] [tool.ruff.format] exclude = ["docs/conf.py"] [tool.ruff.lint] extend-select = [ "B", # flake8-bugbear "I", # isort "ARG", # flake8-unused-arguments "C4", # flake8-comprehensions "ICN", # flake8-import-conventions "G", # flake8-logging-format "EM", # flake8-errmsg "PGH", # pygrep-hooks "PIE", # flake8-pie "PL", # pylint "PT", # flake8-pytest-style "RET", # flake8-return "RUF", # Ruff-specific "SIM", # flake8-simplify "UP", # pyupgrade "YTT", # flake8-2020 "EXE", # flake8-executable "NPY", # NumPy specific rules ] typing-modules = ["skbuild._compat.typing"] ignore = ["PLR2004", "PLR09", "SIM117"] flake8-unused-arguments.ignore-variadic-names = true isort.known-third-party = ["distutils"] isort.force-to-top = ["setuptools"] isort.required-imports = ["from __future__ import annotations"] [tool.ruff.lint.per-file-ignores] "tests/**" = ["T20"] "noxfile.py" = ["T20"] "*.pyx" = ["I"] [tool.check-sdist] sdist-only = ["skbuild/_version.py"] [tool.coverage.run] branch = true source = ["skbuild"] omit = ["skbuild/_version.py"] scikit-build-0.18.1/skbuild/000077500000000000000000000000001466366435500156435ustar00rootroot00000000000000scikit-build-0.18.1/skbuild/__init__.py000066400000000000000000000007601466366435500177570ustar00rootroot00000000000000""" scikit-build is an improved build system generator for CPython C extensions. This module provides the *glue* between the setuptools Python module and CMake. """ from __future__ import annotations from ._version import version as __version__ from .setuptools_wrap import setup __author__ = "The scikit-build team" __email__ = "scikit-build@googlegroups.com" __all__ = ["setup", "__version__"] # Cleaner Python 3.7 command line completion def __dir__() -> list[str]: return __all__ scikit-build-0.18.1/skbuild/_compat/000077500000000000000000000000001466366435500172655ustar00rootroot00000000000000scikit-build-0.18.1/skbuild/_compat/__init__.py000066400000000000000000000000001466366435500213640ustar00rootroot00000000000000scikit-build-0.18.1/skbuild/_compat/tomllib.py000066400000000000000000000002421466366435500212770ustar00rootroot00000000000000from __future__ import annotations import sys if sys.version_info >= (3, 11): from tomllib import load else: from tomli import load __all__ = ["load"] scikit-build-0.18.1/skbuild/_compat/typing.py000066400000000000000000000004171466366435500211530ustar00rootroot00000000000000from __future__ import annotations import sys if sys.version_info >= (3, 8): from typing import Final, Literal, Protocol, TypedDict else: from typing_extensions import Final, Literal, Protocol, TypedDict __all__ = ["Protocol", "TypedDict", "Final", "Literal"] scikit-build-0.18.1/skbuild/_version.pyi000066400000000000000000000003201466366435500202050ustar00rootroot00000000000000from __future__ import annotations __version__: str version: str __version_tuple__: tuple[int, int, int, str, str] | tuple[int, int, int] version_tuple: tuple[int, int, int, str, str] | tuple[int, int, int] scikit-build-0.18.1/skbuild/cmaker.py000066400000000000000000000752741466366435500174760ustar00rootroot00000000000000""" This module provides an interface for invoking CMake executable. """ from __future__ import annotations import argparse import configparser import contextlib import glob import itertools import os import os.path import platform import re import shlex import subprocess import sys import sysconfig import textwrap from pathlib import Path from shlex import quote from typing import Mapping, Sequence, overload import distutils.sysconfig as du_sysconfig from .constants import ( CMAKE_BUILD_DIR, CMAKE_DEFAULT_EXECUTABLE, CMAKE_INSTALL_DIR, SETUPTOOLS_INSTALL_DIR, ) from .exceptions import SKBuildError from .platform_specifics import get_platform RE_FILE_INSTALL = re.compile(r"""[ \t]*file\(INSTALL DESTINATION "([^"]+)".*"([^"]+)"\).*""") @overload def pop_arg(arg: str, args: Sequence[str], default: None = None) -> tuple[list[str], str | None]: ... @overload def pop_arg(arg: str, args: Sequence[str], default: str) -> tuple[list[str], str]: ... def pop_arg(arg: str, args: Sequence[str], default: str | None = None) -> tuple[list[str], str | None]: """Pops an argument ``arg`` from an argument list ``args`` and returns the new list and the value of the argument if present and a default otherwise. """ parser = argparse.ArgumentParser(add_help=False) parser.add_argument(arg) namespace_names, args = parser.parse_known_args(args) namespace = tuple(vars(namespace_names).items()) val = namespace[0][1] if namespace and namespace[0][1] is not None else default return args, val def _remove_cwd_prefix(path: str) -> str: cwd = os.getcwd() result = path.replace("/", os.sep) if result.startswith(cwd): result = os.path.relpath(result, cwd) if platform.system() == "Windows": result = result.replace("\\\\", os.sep) return result.replace("\n", "") def has_cmake_cache_arg(cmake_args: list[str], arg_name: str, arg_value: str | None = None) -> bool: """Return True if ``-D:TYPE=`` is found in ``cmake_args``. If ``arg_value`` is None, return True only if ``-D:`` is found in the list.""" for arg in reversed(cmake_args): if arg.startswith(f"-D{arg_name}:"): if arg_value is None: return True if "=" in arg: return arg.split("=")[1] == arg_value return False def get_cmake_version(cmake_executable: str = CMAKE_DEFAULT_EXECUTABLE) -> str: """ Runs CMake and extracts associated version information. Raises :class:`skbuild.exceptions.SKBuildError` if it failed to execute CMake. Example: >>> # xdoc: IGNORE_WANT >>> from skbuild.cmaker import get_cmake_version >>> print(get_cmake_version()) 3.14.4 """ try: version_string_bytes = subprocess.run( [cmake_executable, "--version"], check=True, stdout=subprocess.PIPE ).stdout except (OSError, subprocess.CalledProcessError) as err: msg = f"Problem with the CMake installation, aborting build. CMake executable is {cmake_executable}" raise SKBuildError(msg) from err version_string = version_string_bytes.decode() return version_string.splitlines()[0].split(" ")[-1] class CMaker: r"""Interface to CMake executable. Example: >>> # Setup dummy repo >>> from skbuild.cmaker import CMaker >>> import ubelt as ub >>> from os.path import join >>> repo_dpath = ub.ensure_app_cache_dir('skbuild', 'test_cmaker') >>> ub.delete(repo_dpath) >>> src_dpath = ub.ensuredir(join(repo_dpath, 'SRC')) >>> cmake_fpath = join(src_dpath, 'CMakeLists.txt') >>> open(cmake_fpath, 'w').write(ub.codeblock( ''' cmake_minimum_required(VERSION 3.5.0) project(foobar NONE) file(WRITE "${CMAKE_BINARY_DIR}/foo.txt" "# foo") install(FILES "${CMAKE_BINARY_DIR}/foo.txt" DESTINATION ".") install(CODE "message(STATUS \\"Project has been installed\\")") message(STATUS "CMAKE_SOURCE_DIR:${CMAKE_SOURCE_DIR}") message(STATUS "CMAKE_BINARY_DIR:${CMAKE_BINARY_DIR}") ''' >>> )) >>> # create a cmaker instance in the dummy repo, configure, and make. >>> from skbuild.utils import push_dir >>> with push_dir(repo_dpath): >>> cmkr = CMaker() >>> config_kwargs = {'cmake_source_dir': str(src_dpath)} >>> print('--- test cmaker configure ---') >>> env = cmkr.configure(**config_kwargs) >>> print('--- test cmaker make ---') >>> cmkr.make(env=env) """ def __init__(self, cmake_executable: str = CMAKE_DEFAULT_EXECUTABLE) -> None: self.cmake_executable = cmake_executable self.cmake_version = get_cmake_version(self.cmake_executable) self.platform = get_platform() @staticmethod def get_cached(variable_name: str) -> str | None: """If set, returns the variable cached value from the :func:`skbuild.constants.CMAKE_BUILD_DIR()`, otherwise returns None""" variable_name = f"{variable_name}:" cmake_cache = Path(CMAKE_BUILD_DIR()) / "CMakeCache.txt" with contextlib.suppress(OSError): for line in cmake_cache.read_text("utf8").splitlines(): if line.startswith(variable_name): return line.split("=", 1)[-1].strip() return None @classmethod def get_cached_generator_name(cls) -> str | None: """Reads and returns the cached generator from the :func:`skbuild.constants.CMAKE_BUILD_DIR()`:. Returns None if not found. """ return cls.get_cached("CMAKE_GENERATOR") def get_cached_generator_env(self) -> dict[str, str] | None: """If any, return a mapping of environment associated with the cached generator.""" generator_name = self.get_cached_generator_name() if generator_name is not None: return self.platform.get_generator(generator_name).env return None def configure( self, clargs: Sequence[str] = (), generator_name: str | None = None, skip_generator_test: bool = False, cmake_source_dir: str = ".", cmake_install_dir: str = "", languages: Sequence[str] = ("C", "CXX"), cleanup: bool = True, ) -> dict[str, str]: """Calls cmake to generate the Makefile/VS Solution/XCode project. clargs: tuple List of command line arguments to pass to cmake executable. generator_name: string The string representing the CMake generator to use. If None, uses defaults for your platform. skip_generator_test: bool If set to True and if a generator name is specified (either as a keyword argument or as `clargs` using `-G `), the generator test is skipped. cmake_source_dir: string Path to source tree containing a ``CMakeLists.txt`` cmake_install_dir: string Relative directory to append to :func:`skbuild.constants.CMAKE_INSTALL_DIR()`. languages: tuple List of languages required to configure the project and expected to be supported by the compiler. The language identifier that can be specified in the list corresponds to the one recognized by CMake. cleanup: bool If True, cleans up temporary folder used to test generators. Set to False for debugging to see CMake's output files. Return a mapping of the environment associated with the selected :class:`skbuild.platform_specifics.abstract.CMakeGenerator`. Mapping of the environment can also be later retrieved using :meth:`.get_cached_generator_env`. """ # if no provided default generator_name, check environment if generator_name is None: generator_name = os.environ.get("CMAKE_GENERATOR") # if generator_name is provided on command line, use it clargs, cli_generator_name = pop_arg("-G", clargs) if cli_generator_name is not None: generator_name = cli_generator_name # if arch is provided on command line, use it clargs, cli_arch = pop_arg("-A", clargs) generator = self.platform.get_best_generator( generator_name, skip_generator_test=skip_generator_test, cmake_executable=self.cmake_executable, cmake_args=clargs, languages=languages, cleanup=cleanup, architecture=cli_arch, ) ninja_executable_path = None if generator.name == "Ninja": with contextlib.suppress(ImportError): import ninja # pylint: disable=import-outside-toplevel ninja_executable_path = os.path.join(ninja.BIN_DIR, "ninja") if not os.path.exists(CMAKE_BUILD_DIR()): os.makedirs(CMAKE_BUILD_DIR()) if not os.path.exists(CMAKE_INSTALL_DIR()): os.makedirs(CMAKE_INSTALL_DIR()) if not os.path.exists(SETUPTOOLS_INSTALL_DIR()): os.makedirs(SETUPTOOLS_INSTALL_DIR()) python_version = CMaker.get_python_version() python_include_dir = CMaker.get_python_include_dir(python_version) python_library = CMaker.get_python_library(python_version) cmake_source_dir = os.path.abspath(cmake_source_dir) cmake_resource_dir = os.path.join(os.path.dirname(__file__), "resources", "cmake") cmake_install_prefix = os.path.abspath(os.path.join(CMAKE_INSTALL_DIR(), cmake_install_dir)) python_version_string = sys.version.split(" ", maxsplit=1)[0] cmd = [ self.cmake_executable, cmake_source_dir, "-G", generator.name, *generator.args, "--no-warn-unused-cli", f"-DCMAKE_INSTALL_PREFIX:PATH={cmake_install_prefix}", f"-DPYTHON_VERSION_STRING:STRING={python_version_string}", "-DSKBUILD:INTERNAL=TRUE", f"-DCMAKE_MODULE_PATH:PATH={cmake_resource_dir}", f"-DPYTHON_EXECUTABLE:PATH={sys.executable}", ] if python_include_dir: cmd.append(f"-DPYTHON_INCLUDE_DIR:PATH={python_include_dir}") if python_library: cmd.append(f"-DPYTHON_LIBRARY:PATH={python_library}") for prefix in ["-DPython", "-DPython3"]: cmd.extend( [ f"{prefix}_EXECUTABLE:PATH={sys.executable}", f"{prefix}_ROOT_DIR:PATH={sys.prefix}", f"{prefix}_FIND_REGISTRY:STRING=NEVER", ] ) if python_include_dir: cmd.append(f"{prefix}_INCLUDE_DIR:PATH={python_include_dir}") if python_library and sysconfig.get_platform().startswith("win"): cmd.append(f"{prefix}_LIBRARY:PATH={python_library}") if sys.implementation.name == "pypy": cmd.append(f"{prefix}_FIND_IMPLEMENTATIONS:STRING=PyPy") with contextlib.suppress(ImportError): import numpy as np # pylint: disable=import-outside-toplevel cmd.append(f"{prefix}_NumPy_INCLUDE_DIRS:PATH=" + np.get_include()) if generator.toolset: cmd.extend(["-T", generator.toolset]) if generator.architecture and "Visual Studio" in generator.name: cmd.extend(["-A", generator.architecture]) if ninja_executable_path is not None: cmd.append(f"-DCMAKE_MAKE_PROGRAM:FILEPATH={ninja_executable_path}") cmd.extend(clargs) # Parse CMAKE_ARGS only if SKBUILD_CONFIGURE_OPTIONS is not present if "SKBUILD_CONFIGURE_OPTIONS" in os.environ: env_cmake_args = list(filter(None, shlex.split(os.environ["SKBUILD_CONFIGURE_OPTIONS"]))) if any("CMAKE_INSTALL_PREFIX" in arg for arg in env_cmake_args): msg = "CMAKE_INSTALL_PREFIX may not be passed via SKBUILD_CONFIGURE_OPTIONS." raise ValueError(msg) else: env_cmake_args_filtered = filter(None, shlex.split(os.environ.get("CMAKE_ARGS", ""))) env_cmake_args = [s for s in env_cmake_args_filtered if "CMAKE_INSTALL_PREFIX" not in s] cmd.extend(env_cmake_args) # changes dir to cmake_build and calls cmake's configure step # to generate makefile print( "Configuring Project\n" " Working directory:\n" f" {os.path.abspath(CMAKE_BUILD_DIR())}\n" " Command:\n" f" {self._formatArgsForDisplay(cmd)}\n", flush=True, ) rtn = subprocess.run(cmd, cwd=CMAKE_BUILD_DIR(), env=generator.env, check=False).returncode if rtn != 0: msg = textwrap.dedent( f"""\ An error occurred while configuring with CMake. Command: {self._formatArgsForDisplay(cmd)} Source directory: {os.path.abspath(cmake_source_dir)} Working directory: {os.path.abspath(CMAKE_BUILD_DIR())} Please see CMake's output for more information. """ ) raise SKBuildError(msg) CMaker.check_for_bad_installs() return generator.env @staticmethod def get_python_version() -> str: """Get version associated with the current python interpreter. Returns: str: python version string Example: >>> # xdoc: +IGNORE_WANT >>> from skbuild.cmaker import CMaker >>> python_version = CMaker.get_python_version() >>> print('python_version = {!r}'.format(python_version)) python_version = '3.7' """ python_version = sysconfig.get_config_var("VERSION") if not python_version: python_version = sysconfig.get_config_var("py_version_short") if not python_version: python_version = ".".join(map(str, sys.version_info[:2])) assert isinstance(python_version, str) return python_version # NOTE(opadron): The try-excepts raise the cyclomatic complexity, but we # need them for this function. @staticmethod def get_python_include_dir(python_version: str) -> str | None: """Get include directory associated with the current python interpreter. Args: python_version (str): python version, may be partial. Returns: PathLike: python include dir Example: >>> # xdoc: +IGNORE_WANT >>> from skbuild.cmaker import CMaker >>> python_version = CMaker.get_python_version() >>> python_include_dir = CMaker.get_python_include_dir(python_version) >>> print('python_include_dir = {!r}'.format(python_include_dir)) python_include_dir = '.../conda/envs/py37/include/python3.7m' """ # determine python include dir python_include_dir: str | None = sysconfig.get_config_var("INCLUDEPY") # if Python.h not found (or python_include_dir is None), try to find a # suitable include dir found_python_h = python_include_dir is not None and os.path.exists(os.path.join(python_include_dir, "Python.h")) if not found_python_h: # NOTE(opadron): these possible prefixes must be guarded against # AttributeErrors and KeyErrors because they each can throw on # different platforms or even different builds on the same platform. include_py: str | None = sysconfig.get_config_var("INCLUDEPY") include_dir: str | None = sysconfig.get_config_var("INCLUDEDIR") include: str | None = None plat_include: str | None = None python_inc: str | None = None python_inc2: str | None = None with contextlib.suppress(AttributeError, KeyError): include = sysconfig.get_path("include") with contextlib.suppress(AttributeError, KeyError): plat_include = sysconfig.get_path("platinclude") with contextlib.suppress(AttributeError): python_inc = sysconfig.get_python_inc() # type: ignore[attr-defined] if include_py is not None: include_py = os.path.dirname(include_py) if include is not None: include = os.path.dirname(include) if plat_include is not None: plat_include = os.path.dirname(plat_include) if python_inc is not None: python_inc2 = os.path.join(python_inc, ".".join(map(str, sys.version_info[:2]))) all_candidate_prefixes = [include_py, include_dir, include, plat_include, python_inc, python_inc2] candidate_prefixes: list[str] = [pre for pre in all_candidate_prefixes if pre] candidate_versions: tuple[str, ...] = (python_version,) if python_version: candidate_versions += ("",) pymalloc = None with contextlib.suppress(AttributeError): pymalloc = bool(sysconfig.get_config_var("WITH_PYMALLOC")) if pymalloc: candidate_versions += (python_version + "m",) candidates = ( os.path.join(prefix, "".join(("python", ver))) for (prefix, ver) in itertools.product(candidate_prefixes, candidate_versions) ) for candidate in candidates: if os.path.exists(os.path.join(candidate, "Python.h")): # we found an include directory python_include_dir = candidate break # TODO(opadron): what happens if we don't find an include directory? # Throw SKBuildError? return python_include_dir @staticmethod def get_python_library(python_version: str) -> str | None: """Get path to the python library associated with the current python interpreter. Args: python_version (str): python version, may be partial. Returns: PathLike: python_library : python shared library Example: >>> # xdoc: +IGNORE_WANT >>> from skbuild.cmaker import CMaker >>> python_version = CMaker.get_python_version() >>> python_library = CMaker.get_python_include_dir(python_version) >>> print('python_library = {!r}'.format(python_library)) python_library = '.../conda/envs/py37/include/python3.7m' """ # On Windows, support cross-compiling in the same way as setuptools # When cross-compiling, check DIST_EXTRA_CONFIG first config_file = os.environ.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: minor = sys.version_info[1] return str(Path(result) / f"python3{minor}.lib") # This seems to be the simplest way to detect the library path with # modern python versions that avoids the complicated construct below. # It avoids guessing the library name. Tested with cpython 3.8 and # pypy 3.8 on Ubuntu. libdir: str | None = sysconfig.get_config_var("LIBDIR") ldlibrary: str | None = sysconfig.get_config_var("LDLIBRARY") if libdir and ldlibrary and os.path.exists(libdir): if sysconfig.get_config_var("MULTIARCH"): masd = sysconfig.get_config_var("multiarchsubdir") if masd: if masd.startswith(os.sep): masd = masd[len(os.sep) :] libdir_masd = os.path.join(libdir, masd) if os.path.exists(libdir_masd): libdir = libdir_masd libpath = Path(libdir) / ldlibrary if sys.platform.startswith("win") and libpath.suffix == ".dll": libpath = libpath.with_suffix(".lib") if libpath.is_file(): return str(libpath) return CMaker._guess_python_library(python_version) @staticmethod def _guess_python_library(python_version: str) -> str | None: # determine direct path to libpython python_library: str | None = sysconfig.get_config_var("LIBRARY") # if static (or nonexistent), try to find a suitable dynamic libpython if not python_library or os.path.splitext(python_library)[1][-2:] == ".a": candidate_lib_prefixes = ["", "lib"] candidate_suffixes = [""] candidate_implementations = ["python"] if sys.implementation.name == "pypy": candidate_implementations[:0] = ["pypy-c", "pypy3-c", "pypy"] candidate_suffixes.append("-c") candidate_extensions = [".lib", ".so", ".a"] # On pypy + MacOS, the variable WITH_DYLD is not set. It would # actually be possible to determine the python library there using # LDLIBRARY + LIBDIR. As a simple fix, we check if the LDLIBRARY # ends with .dylib and add it to the candidate matrix in this case. with_ld = sysconfig.get_config_var("WITH_DYLD") ld_lib = sysconfig.get_config_var("LDLIBRARY") if with_ld or (ld_lib and ld_lib.endswith(".dylib")): candidate_extensions.insert(0, ".dylib") candidate_versions = [python_version] if python_version: candidate_versions.append("") candidate_versions.insert(0, "".join(python_version.split(".")[:2])) abiflags = getattr(sys, "abiflags", "") candidate_abiflags = [abiflags] if abiflags: candidate_abiflags.append("") # Ensure the value injected by virtualenv is # returned on windows. # Because calling `sysconfig.get_config_var('multiarchsubdir')` # returns an empty string on Linux, `du_sysconfig` is only used to # get the value of `LIBDIR`. candidate_libdirs = [] libdir_a = du_sysconfig.get_config_var("LIBDIR") assert not isinstance(libdir_a, int) if libdir_a is None: libdest = sysconfig.get_config_var("LIBDEST") candidate_libdirs.append(os.path.abspath(os.path.join(libdest, "..", "libs") if libdest else "libs")) libdir_b = sysconfig.get_config_var("LIBDIR") for libdir in (libdir_a, libdir_b): if libdir is None: continue if sysconfig.get_config_var("MULTIARCH"): masd = sysconfig.get_config_var("multiarchsubdir") if masd: if masd.startswith(os.sep): masd = masd[len(os.sep) :] candidate_libdirs.append(os.path.join(libdir, masd)) candidate_libdirs.append(libdir) candidates = ( os.path.join(libdir, "".join((pre, impl, ver, abi, suf, ext))) for (libdir, pre, impl, ext, ver, abi, suf) in itertools.product( candidate_libdirs, candidate_lib_prefixes, candidate_implementations, candidate_extensions, candidate_versions, candidate_abiflags, candidate_suffixes, ) ) for candidate in candidates: if os.path.exists(candidate): # we found a (likely alternate) libpython python_library = candidate break # Temporary workaround for some libraries (opencv) processing the # string output. Will return None instead of empty string in future # versions if the library does not exist. if python_library is None: return None return python_library if python_library and os.path.exists(python_library) else "" @staticmethod def check_for_bad_installs() -> None: """This function tries to catch files that are meant to be installed outside the project root before they are actually installed. Indeed, we can not wait for the manifest, so we try to extract the information (install destination) from the CMake build files ``*.cmake`` found in :func:`skbuild.constants.CMAKE_BUILD_DIR()`. It raises :class:`skbuild.exceptions.SKBuildError` if it found install destination outside of :func:`skbuild.constants.CMAKE_INSTALL_DIR()`. """ bad_installs = [] install_dir = os.path.join(os.getcwd(), CMAKE_INSTALL_DIR()) for root, _, file_list in os.walk(CMAKE_BUILD_DIR()): for filename in file_list: if os.path.splitext(filename)[1] != ".cmake": continue with open(os.path.join(root, filename), encoding="utf-8") as fp: lines = fp.readlines() for line in lines: match = RE_FILE_INSTALL.match(line) if match is None: continue destination = os.path.normpath(match.group(1).replace("${CMAKE_INSTALL_PREFIX}", install_dir)) if not destination.startswith(install_dir): bad_installs.append(os.path.join(destination, os.path.basename(match.group(2)))) if bad_installs: msg = "\n".join( ( " CMake-installed files must be within the project root.", " Project Root:", f" {install_dir}", " Violating Files:", "\n".join(f" {_install}" for _install in bad_installs), ) ) raise SKBuildError(msg) def make( self, clargs: Sequence[str] = (), config: str = "Release", source_dir: str = ".", install_target: str = "install", env: Mapping[str, str] | None = None, ) -> None: """Calls the system-specific make program to compile code. install_target: string Name of the target responsible to install the project. Default is "install". .. note:: To workaround CMake issue #8438. See https://gitlab.kitware.com/cmake/cmake/-/issues/8438 Due to a limitation of CMake preventing from adding a dependency on the "build-all" built-in target, we explicitly build the project first when the install target is different from the default on. """ clargs, config = pop_arg("--config", clargs, config) clargs, install_target = pop_arg("--install-target", clargs, install_target) if not os.path.exists(CMAKE_BUILD_DIR()): msg = ( f"CMake build folder ({CMAKE_BUILD_DIR()}) does not exist. " "Did you forget to run configure before make?" ) raise SKBuildError(msg) # Workaround CMake issue #8438 # See https://gitlab.kitware.com/cmake/cmake/-/issues/8438 # Due to a limitation of CMake preventing from adding a dependency # on the "build-all" built-in target, we explicitly build # the project first when # the install target is different from the default on. if install_target != "install": self.make_impl(clargs=clargs, config=config, source_dir=source_dir, install_target=None, env=env) self.make_impl(clargs=clargs, config=config, source_dir=source_dir, install_target=install_target, env=env) def make_impl( self, clargs: list[str], config: str, source_dir: str, install_target: str | None, env: Mapping[str, str] | None = None, ) -> None: """ Precondition: clargs does not have --config nor --install-target options. These command line arguments are extracted in the caller function `make` with `clargs, config = pop_arg('--config', clargs, config)` This is a refactor effort for calling the function `make` twice in case the install_target is different than the default `install`. """ if not install_target: cmd = [self.cmake_executable, "--build", source_dir, "--config", config, "--"] else: cmd = [self.cmake_executable, "--build", source_dir, "--target", install_target, "--config", config, "--"] cmd.extend(clargs) cmd.extend(filter(bool, shlex.split(os.environ.get("SKBUILD_BUILD_OPTIONS", "")))) rtn = subprocess.run(cmd, cwd=CMAKE_BUILD_DIR(), env=env, check=False).returncode # For reporting errors (if any) if not install_target: install_target = "internal build step [valid]" if rtn != 0: msg = textwrap.dedent( f"""\ An error occurred while building with CMake. Command: {self._formatArgsForDisplay(cmd)} Install target: {install_target} Source directory: {os.path.abspath(source_dir)} Working directory: {os.path.abspath(CMAKE_BUILD_DIR())} Please check the install target is valid and see CMake's output for more information. """ ) raise SKBuildError(msg) def install(self) -> list[str]: """Returns a list of file paths to install via setuptools that is compatible with the data_files keyword argument. """ return self._parse_manifests() def _parse_manifests(self) -> list[str]: paths = glob.glob(os.path.join(CMAKE_BUILD_DIR(), "install_manifest*.txt")) try: return next(self._parse_manifest(path) for path in paths) except StopIteration: return [] @staticmethod def _parse_manifest(install_manifest_path: str) -> list[str]: with open(install_manifest_path, encoding="utf-8") as manifest: return [_remove_cwd_prefix(path) for path in manifest] @staticmethod def _formatArgsForDisplay(args: Sequence[str]) -> str: """Format a list of arguments appropriately for display. When formatting a command and its arguments, the user should be able to execute the command by copying and pasting the output directly into a shell. Currently, the only formatting is naively surrounding each argument with quotation marks. """ return " ".join(quote(arg) for arg in args) scikit-build-0.18.1/skbuild/command/000077500000000000000000000000001466366435500172615ustar00rootroot00000000000000scikit-build-0.18.1/skbuild/command/__init__.py000066400000000000000000000021421466366435500213710ustar00rootroot00000000000000"""Collection of objects allowing to customize behavior of standard distutils and setuptools commands. """ from __future__ import annotations from .._compat.typing import Protocol from ..constants import SETUPTOOLS_INSTALL_DIR from ..utils import Distribution class CommandMixinProtocol(Protocol): """Protocol for commands that use CMake.""" build_base: str distribution: Distribution outfiles: list[str] install_lib: str | None install_platlib: str # pylint: disable-next=missing-function-docstring def finalize_options(self, *args: object, **kwargs: object) -> None: ... class set_build_base_mixin: """Mixin allowing to override distutils and setuptools commands.""" def finalize_options(self: CommandMixinProtocol, *args: object, **kwargs: object) -> None: """Override built-in function and set a new `build_base`.""" build_base = getattr(self, "build_base", "EMPTY") if not build_base or build_base == "build": self.build_base = SETUPTOOLS_INSTALL_DIR() super().finalize_options(*args, **kwargs) # type: ignore[safe-super] scikit-build-0.18.1/skbuild/command/bdist.py000066400000000000000000000005341466366435500207420ustar00rootroot00000000000000"""This module defines custom implementation of ``bdist`` setuptools command.""" from __future__ import annotations import setuptools # noqa: F401 from distutils.command.bdist import bdist as _bdist from . import set_build_base_mixin class bdist(set_build_base_mixin, _bdist): """Custom implementation of ``bdist`` setuptools command.""" scikit-build-0.18.1/skbuild/command/bdist_wheel.py000066400000000000000000000040311466366435500221220ustar00rootroot00000000000000"""This module defines custom implementation of ``bdist_wheel`` setuptools command.""" from __future__ import annotations from typing import TYPE_CHECKING if TYPE_CHECKING: from wheel.bdist_wheel import bdist_wheel as _bdist_wheel from wheel.wheelfile import WheelFile else: try: from setuptools.command.bdist_wheel import WheelFile # type: ignore[attr-defined] from setuptools.command.bdist_wheel import bdist_wheel as _bdist_wheel except ImportError: from wheel.bdist_wheel import bdist_wheel as _bdist_wheel from wheel.wheelfile import WheelFile from .._version import version as skbuild_version from ..utils import distribution_hide_listing from . import set_build_base_mixin class bdist_wheel(set_build_base_mixin, _bdist_wheel): # type: ignore[misc] """Custom implementation of ``bdist_wheel`` setuptools command.""" def run(self, *args: object, **kwargs: object) -> None: """Handle --hide-listing option.""" old_write_files = WheelFile.write_files def update_write_files(wheelfile_self: bdist_wheel, base_dir: str) -> None: with distribution_hide_listing(self.distribution) as hide_listing: if hide_listing: zip_filename = wheelfile_self.filename print(f"creating {zip_filename!r} and adding {base_dir!r} to it", flush=True) old_write_files(wheelfile_self, base_dir) WheelFile.distribution = self.distribution WheelFile.write_files = update_write_files try: super().run(*args, **kwargs) finally: WheelFile.write_files = old_write_files del WheelFile.distribution def write_wheelfile(self, wheelfile_base: str, _: None = None) -> None: """Write ``skbuild `` as a wheel generator. See `PEP-0427 `_ for more details. """ generator = f"skbuild {skbuild_version}" super().write_wheelfile(wheelfile_base, generator) scikit-build-0.18.1/skbuild/command/build.py000066400000000000000000000005651466366435500207400ustar00rootroot00000000000000"""This module defines custom implementation of ``build`` setuptools command.""" from __future__ import annotations import setuptools # noqa: F401 from distutils.command.build import build as _build from . import set_build_base_mixin # TODO: setuptools stubs class build(set_build_base_mixin, _build): """Custom implementation of ``build`` setuptools command.""" scikit-build-0.18.1/skbuild/command/build_ext.py000066400000000000000000000034741466366435500216220ustar00rootroot00000000000000"""This module defines custom implementation of ``build_ext`` setuptools command.""" from __future__ import annotations import os from distutils.file_util import copy_file from setuptools.command.build_ext import build_ext as _build_ext from ..constants import CMAKE_INSTALL_DIR from . import set_build_base_mixin class build_ext(set_build_base_mixin, _build_ext): """Custom implementation of ``build_ext`` setuptools command.""" def copy_extensions_to_source(self) -> None: """This function is only-called when doing inplace build. It is customized to ensure the extensions compiled using distutils are copied back to the source tree instead of the :func:`skbuild.constants.CMAKE_INSTALL_DIR()`. """ build_py = self.get_finalized_command("build_py") for ext in self.extensions: fullname: str = self.get_ext_fullname(ext.name) filename: str = self.get_ext_filename(fullname) # type: ignore[no-untyped-call] modpath = fullname.split(".") package = ".".join(modpath[:-1]) package_dir = build_py.get_package_dir(package) # type: ignore[attr-defined] # skbuild: strip install dir for inplace build package_dir = package_dir[len(CMAKE_INSTALL_DIR()) + 1 :] dest_filename = os.path.join(package_dir, os.path.basename(filename)) src_filename = os.path.join(self.build_lib, filename) # Always copy, even if source is older than destination, to ensure # that the right extensions for the current Python/platform are # used. copy_file(src_filename, dest_filename, verbose=self.verbose, dry_run=self.dry_run) # type: ignore[attr-defined] if ext._needs_stub: self.write_stub(package_dir or os.curdir, ext, True) scikit-build-0.18.1/skbuild/command/build_py.py000066400000000000000000000077151466366435500214540ustar00rootroot00000000000000"""This module defines custom implementation of ``build_py`` setuptools command.""" from __future__ import annotations import os from setuptools.command.build_py import build_py as _build_py from ..constants import CMAKE_INSTALL_DIR from ..utils import distribution_hide_listing, logger from . import set_build_base_mixin class build_py(set_build_base_mixin, _build_py): """Custom implementation of ``build_py`` setuptools command.""" def initialize_options(self) -> None: """Handle --hide-listing option. Initializes ``outfiles_count``. """ super().initialize_options() self.outfiles_count = 0 def build_module(self, module: str | list[str] | tuple[str, ...], module_file: str, package: str) -> None: """Handle --hide-listing option. Increments ``outfiles_count``. """ super().build_module(module, module_file, package) # type: ignore[no-untyped-call] self.outfiles_count += 1 def run(self, *args: object, **kwargs: object) -> None: """Handle --hide-listing option. Display number of copied files. It corresponds to the value of ``outfiles_count``. """ with distribution_hide_listing(self.distribution): super().run(*args, **kwargs) logger.info("copied %d files", self.outfiles_count) def find_modules(self) -> list[tuple[str, str, str]]: """Finds individually-specified Python modules, ie. those listed by module name in 'self.py_modules'. Returns a list of tuples (package, module_base, filename): 'package' is a tuple of the path through package-space to the module; 'module_base' is the bare (no packages, no dots) module name, and 'filename' is the path to the ".py" file (relative to the distribution root) that implements the module. """ # Map package names to tuples of useful info about the package: # (package_dir, checked) # package_dir - the directory where we'll find source files for # this package # checked - true if we have checked that the package directory # is valid (exists, contains __init__.py, ... ?) packages: dict[str, tuple[str, bool]] = {} # List of (package, module, filename) tuples to return modules: list[tuple[str, str, str]] = [] # We treat modules-in-packages almost the same as toplevel modules, # just the "package" for a toplevel is empty (either an empty # string or empty list, depending on context). Differences: # - don't check for __init__.py in directory for empty package for module in self.py_modules: path = module.split(".") package = ".".join(path[0:-1]) module_base = path[-1] try: (package_dir, checked) = packages[package] except KeyError: package_dir = self.get_package_dir(package) # type: ignore[no-untyped-call] checked = False if not checked: init_py = self.check_package(package, package_dir) # type: ignore[no-untyped-call] packages[package] = (package_dir, True) if init_py: modules.append((package, "__init__", init_py)) # XXX perhaps we should also check for just .pyc files # (so greedy closed-source bastards can distribute Python # modules too) module_file = os.path.join(package_dir, module_base + ".py") # skbuild: prepend CMAKE_INSTALL_DIR if file exists in the # CMake install tree. if os.path.exists(os.path.join(CMAKE_INSTALL_DIR(), module_file)): module_file = os.path.join(CMAKE_INSTALL_DIR(), module_file) if not self.check_module(module, module_file): # type: ignore[no-untyped-call] continue modules.append((package, module_base, module_file)) return modules scikit-build-0.18.1/skbuild/command/clean.py000066400000000000000000000017731466366435500207250ustar00rootroot00000000000000"""This module defines custom implementation of ``clean`` setuptools command.""" from __future__ import annotations import os from shutil import rmtree import setuptools # noqa: F401 from distutils.command.clean import clean as _clean from ..constants import CMAKE_BUILD_DIR, CMAKE_INSTALL_DIR, SKBUILD_DIR from ..utils import logger from . import set_build_base_mixin class clean(set_build_base_mixin, _clean): """Custom implementation of ``clean`` setuptools command.""" def run(self) -> None: """After calling the super class implementation, this function removes the directories specific to scikit-build.""" super().run() for dir_ in (CMAKE_INSTALL_DIR(), CMAKE_BUILD_DIR(), SKBUILD_DIR()): if os.path.exists(dir_): logger.info("removing '%s'", dir_) # This seems to be there but isn't typed in the stubs TODO if not self.dry_run and os.path.exists(dir_): # type: ignore[attr-defined] rmtree(dir_) scikit-build-0.18.1/skbuild/command/egg_info.py000066400000000000000000000036201466366435500214110ustar00rootroot00000000000000"""This module defines custom implementation of ``egg_info`` setuptools command.""" from __future__ import annotations import os import os.path from typing import Any from setuptools.command.egg_info import egg_info as _egg_info from ..constants import CMAKE_INSTALL_DIR from ..utils import to_unix_path from . import set_build_base_mixin class egg_info(set_build_base_mixin, _egg_info): """Custom implementation of ``egg_info`` setuptools command.""" def finalize_options(self, *args: Any, **kwargs: Any) -> None: if self.egg_base is None: if self.distribution.package_dir is not None and len(self.distribution.package_dir) == 1: # Recover directory specified in setup() function # using `package_dir={'':}` # This is required to successfully update the python path when # running the test command. package_name = next(iter(self.distribution.package_dir.keys())) egg_base = to_unix_path(next(iter(self.distribution.package_dir.values()))) cmake_install_dir = to_unix_path(CMAKE_INSTALL_DIR()) if egg_base.startswith(cmake_install_dir): egg_base = egg_base[len(cmake_install_dir) + 1 :] if package_name and egg_base.endswith(package_name): egg_base = egg_base[: -len(package_name) - 1] if not egg_base: egg_base = "." # pylint:disable=attribute-defined-outside-init self.egg_base = egg_base else: script_path = os.path.abspath(self.distribution.script_name or "") script_dir = os.path.dirname(script_path) # pylint:disable=attribute-defined-outside-init self.egg_base = os.path.join(script_dir, self.egg_base) super().finalize_options(*args, **kwargs) # type: ignore[misc] scikit-build-0.18.1/skbuild/command/generate_source_manifest.py000066400000000000000000000065561466366435500247070ustar00rootroot00000000000000"""This module defines custom ``generate_source_manifest`` setuptools command.""" from __future__ import annotations import os import subprocess import sys from distutils.cmd import Command from ..constants import SKBUILD_DIR, SKBUILD_MARKER_FILE from . import set_build_base_mixin class generate_source_manifest(set_build_base_mixin, Command): """Custom setuptools command generating a `MANIFEST` file if not already provided.""" description = "generate source MANIFEST" def initialize_options(self) -> None: """Set default values for all the options that this command supports.""" def run(self) -> None: """ If neither a `MANIFEST`, nor a `MANIFEST.in` file is provided, and we are in a git repo, try to create a `MANIFEST.in` file from the output of `git ls-tree --name-only -r HEAD`. We need a reliable way to tell if an existing `MANIFEST` file is one we've generated. distutils already uses a first-line comment to tell if the `MANIFEST` file was generated from `MANIFEST.in`, so we use a dummy file, `_skbuild_MANIFEST`, to avoid confusing distutils. """ do_generate = ( # If there's a MANIFEST.in file, we assume that we had nothing to do # with the project's manifest. not os.path.exists("MANIFEST.in") # otherwise, we check to see that there is no MANIFEST, ... if not os.path.exists("MANIFEST") # ... (if there is one,) that we created it else os.path.exists(SKBUILD_MARKER_FILE()) ) if do_generate: try: with open("MANIFEST.in", "wb") as manifest_in_file: # Since Git < 2.11 does not support --recurse-submodules option, fallback to # regular listing. try: cmd_out = subprocess.run( ["git", "ls-files", "--recurse-submodules"], stdout=subprocess.PIPE, check=True ).stdout except subprocess.CalledProcessError: cmd_out = subprocess.run(["git", "ls-files"], stdout=subprocess.PIPE, check=True).stdout git_files = [git_file.strip() for git_file in cmd_out.split(b"\n")] manifest_text = b"\n".join([b"include %s" % git_file.strip() for git_file in git_files if git_file]) manifest_text += b"\nexclude MANIFEST.in" manifest_in_file.write(manifest_text) except subprocess.CalledProcessError: sys.stderr.write( "\n\n" "Since scikit-build could not find MANIFEST.in or " "MANIFEST, it tried to generate a MANIFEST.in file " "automatically, but could not because it could not " "determine which source files to include.\n\n" 'The command used was "git ls-files"\n' "\n\n" ) raise if not os.path.exists(SKBUILD_DIR()): os.makedirs(SKBUILD_DIR()) with open(SKBUILD_MARKER_FILE(), "w", encoding="utf-8"): # touch pass def finalize_options(self, *args: object, **kwargs: object) -> None: """Set final values for all the options that this command supports.""" scikit-build-0.18.1/skbuild/command/install.py000066400000000000000000000020031466366435500212740ustar00rootroot00000000000000"""This module defines custom implementation of ``install`` setuptools command.""" from __future__ import annotations from typing import Any from setuptools.command.install import install as _install from . import CommandMixinProtocol, set_build_base_mixin class install(set_build_base_mixin, _install): """Custom implementation of ``install`` setuptools command.""" def finalize_options(self: CommandMixinProtocol, *args: Any, **kwargs: Any) -> None: """Ensure that if the distribution is non-pure, all modules are installed in ``self.install_platlib``. .. note:: `setuptools.dist.Distribution.has_ext_modules()` is overridden in :func:`..setuptools_wrap.setup()`. """ if self.install_lib is None and self.distribution.has_ext_modules(): # type: ignore[attr-defined] # pylint:disable=attribute-defined-outside-init self.install_lib = self.install_platlib super().finalize_options(*args, **kwargs) # type: ignore[safe-super] scikit-build-0.18.1/skbuild/command/install_lib.py000066400000000000000000000014221466366435500221260ustar00rootroot00000000000000"""This module defines custom implementation of ``install_lib`` setuptools command.""" from __future__ import annotations from setuptools.command.install_lib import install_lib as _install_lib from ..utils import distribution_hide_listing, logger from . import CommandMixinProtocol, set_build_base_mixin class install_lib(set_build_base_mixin, _install_lib): """Custom implementation of ``install_lib`` setuptools command.""" def install(self: CommandMixinProtocol) -> list[str]: """Handle --hide-listing option.""" with distribution_hide_listing(self.distribution): outfiles: list[str] = super().install() # type: ignore[misc] if outfiles is not None: logger.info("copied %d files", len(outfiles)) return outfiles scikit-build-0.18.1/skbuild/command/install_scripts.py000066400000000000000000000014251466366435500230520ustar00rootroot00000000000000"""This module defines custom implementation of ``install_scripts`` setuptools command.""" from __future__ import annotations from typing import Any from setuptools.command.install_scripts import install_scripts as _install_scripts from ..utils import distribution_hide_listing, logger from . import CommandMixinProtocol, set_build_base_mixin class install_scripts(set_build_base_mixin, _install_scripts): """Custom implementation of ``install_scripts`` setuptools command.""" def run(self: CommandMixinProtocol, *args: Any, **kwargs: Any) -> None: """Handle --hide-listing option.""" with distribution_hide_listing(self.distribution): super().run(*args, **kwargs) # type: ignore[misc] logger.info("copied %d files", len(self.outfiles)) scikit-build-0.18.1/skbuild/command/sdist.py000066400000000000000000000027701466366435500207670ustar00rootroot00000000000000"""This module defines custom implementation of ``sdist`` setuptools command.""" from __future__ import annotations from typing import Sequence from setuptools.command.sdist import sdist as _sdist from ..utils import distribution_hide_listing, logger from . import CommandMixinProtocol, set_build_base_mixin class sdist(set_build_base_mixin, _sdist): """Custom implementation of ``sdist`` setuptools command.""" def make_release_tree(self: CommandMixinProtocol, base_dir: str, files: Sequence[str]) -> None: """Handle --hide-listing option.""" with distribution_hide_listing(self.distribution): super().make_release_tree(base_dir, files) # type: ignore[misc] logger.info("copied %d files", len(files)) def make_archive( # type: ignore[override] self, base_name: str, format: str, root_dir: str | None = None, base_dir: str | None = None, owner: str | None = None, group: str | None = None, ) -> str: """Handle --hide-listing option.""" logger.info("creating '%s' %s archive and adding '%s' to it", base_name, format, base_dir) with distribution_hide_listing(self.distribution): return super().make_archive(base_name, format, root_dir, base_dir, owner, group) def run(self, *args: object, **kwargs: object) -> None: """Force :class:`.egg_info.egg_info` command to run.""" self.run_command("generate_source_manifest") super().run(*args, **kwargs) scikit-build-0.18.1/skbuild/constants.py000066400000000000000000000137331466366435500202400ustar00rootroot00000000000000""" This module defines constants commonly used in scikit-build. """ from __future__ import annotations import contextlib import functools import json import os import platform import shutil import subprocess import sys from pathlib import Path from distutils.util import get_platform from packaging.version import InvalidVersion, Version @functools.lru_cache(maxsize=None) def get_cmake_version(cmake_path: os.PathLike[str] | str) -> Version: try: result = subprocess.run([str(cmake_path), "-E", "capabilities"], capture_output=True, text=True, check=False) with contextlib.suppress(json.decoder.JSONDecodeError, KeyError, InvalidVersion): return Version(json.loads(result.stdout)["version"]["string"]) except subprocess.CalledProcessError: # In some cases (like Pyodide<0.26's cmake wrapper), `-E` isn't handled # correctly, so let's try `--version`, which is more common so more # likely to be wrapped correctly with contextlib.suppress(subprocess.CalledProcessError): result = subprocess.run([str(cmake_path), "--version"], capture_output=True, text=True, check=False) with contextlib.suppress(IndexError, InvalidVersion): return Version(result.stdout.splitlines()[0].split()[-1].split("-")[0]) except PermissionError: pass return Version("0.0") def _get_cmake_executable() -> str: with contextlib.suppress(ImportError): from cmake import CMAKE_BIN_DIR # pylint: disable=import-outside-toplevel path = f"{CMAKE_BIN_DIR}/cmake" if Path(f"{path}.exe").is_file(): return f"{path}.exe" return path for name in ("cmake", "cmake3"): prog = shutil.which(name) if prog and get_cmake_version(prog) >= Version("3.5"): return prog # Just guess otherwise return "cmake" CMAKE_DEFAULT_EXECUTABLE = _get_cmake_executable() """Default path to CMake executable.""" def _default_skbuild_plat_name() -> str: """Get default platform name. On linux and windows, it corresponds to :func:`distutils.util.get_platform()`. On macOS, it corresponds to the version and machine associated with :func:`platform.mac_ver()`. """ if not sys.platform.startswith("darwin"): return get_platform() supported_macos_architectures = {"x86_64", "arm64"} macos_universal2_architectures = {"x86_64", "arm64"} # See https://github.com/scikit-build/scikit-build/issues/643 for a weird cross # compiling bug that forces us to avoid getting machine from platform.mac_ver()[2] release = platform.mac_ver()[0] machine = platform.machine() # If the MACOSX_DEPLOYMENT_TARGET environment variable is defined, use # it, as it will be the most accurate. Otherwise use the value returned by # platform.mac_ver() provided by the platform module available in the # Python standard library. # # Note that on macOS, distutils.util.get_platform() is not used because # it returns the macOS version on which Python was built which may be # significantly older than the user's current machine. release = os.environ.get("MACOSX_DEPLOYMENT_TARGET", "") or release major_macos, minor_macos = release.split(".")[:2] # On macOS 11+, only the major version matters. if int(major_macos) >= 11: minor_macos = "0" # Use CMAKE_OSX_ARCHITECTURES if that is set, otherwise use ARCHFLAGS, # which is the variable used by Setuptools. Fall back to the machine arch # if neither of those is given. Not that -D flags like CMAKE_SYSTEM_PROCESSOR # will override this by setting it later. archflags = os.environ.get("ARCHFLAGS") if archflags is not None: machine = ";".join(set(archflags.split()) & supported_macos_architectures) machine = os.environ.get("CMAKE_OSX_ARCHITECTURES", machine) # Handle universal2 wheels, if those two architectures are requested. if set(machine.split(";")) == macos_universal2_architectures: machine = "universal2" return f"macosx-{major_macos}.{minor_macos}-{machine}" _SKBUILD_PLAT_NAME = _default_skbuild_plat_name() def set_skbuild_plat_name(plat_name: str) -> None: """Set platform name associated with scikit-build functions returning a path: * :func:`SKBUILD_DIR()` * :func:`SKBUILD_MARKER_FILE()` * :func:`CMAKE_BUILD_DIR()` * :func:`CMAKE_INSTALL_DIR()` * :func:`CMAKE_SPEC_FILE()` * :func:`SETUPTOOLS_INSTALL_DIR()` """ global _SKBUILD_PLAT_NAME # noqa: PLW0603 _SKBUILD_PLAT_NAME = plat_name def skbuild_plat_name() -> str: """Get platform name formatted as `[-]-`. Default value corresponds to :func:`_default_skbuild_plat_name()` and can be overridden with :func:`set_skbuild_plat_name()`. Examples of values are `macosx-10.9-x86_64`, `linux-x86_64`, `linux-i686` or `win-am64`. """ return _SKBUILD_PLAT_NAME def SKBUILD_DIR() -> str: """Top-level directory where setuptools and CMake directories are generated.""" version_str = ".".join(map(str, sys.version_info[:2])) return os.path.join("_skbuild", f"{_SKBUILD_PLAT_NAME}-{version_str}") def SKBUILD_MARKER_FILE() -> str: """Marker file used by :func:`skbuild.command.generate_source_manifest.generate_source_manifest.run()`.""" return os.path.join(SKBUILD_DIR(), "_skbuild_MANIFEST") def CMAKE_BUILD_DIR() -> str: """CMake build directory.""" return os.path.join(SKBUILD_DIR(), "cmake-build") def CMAKE_INSTALL_DIR() -> str: """CMake install directory.""" return os.path.join(SKBUILD_DIR(), "cmake-install") def CMAKE_SPEC_FILE() -> str: """CMake specification file storing CMake version, CMake configuration arguments and environment variables ``PYTHONNOUSERSITE`` and ``PYTHONPATH``. """ return os.path.join(CMAKE_BUILD_DIR(), "CMakeSpec.json") def SETUPTOOLS_INSTALL_DIR() -> str: """Setuptools install directory.""" return os.path.join(SKBUILD_DIR(), "setuptools") scikit-build-0.18.1/skbuild/exceptions.py000066400000000000000000000010261466366435500203750ustar00rootroot00000000000000""" This module defines exceptions commonly used in scikit-build. """ from __future__ import annotations class SKBuildError(RuntimeError): """Exception raised when an error occurs while configuring or building a project. """ class SKBuildInvalidFileInstallationError(SKBuildError): """Exception raised when a file is being installed into an invalid location.""" class SKBuildGeneratorNotFoundError(SKBuildError): """Exception raised when no suitable generator is found for the current platform. """ scikit-build-0.18.1/skbuild/platform_specifics/000077500000000000000000000000001466366435500215175ustar00rootroot00000000000000scikit-build-0.18.1/skbuild/platform_specifics/__init__.py000066400000000000000000000007261466366435500236350ustar00rootroot00000000000000"""This package provides :func:`get_platform()` allowing to get an instance of :class:`.abstract.CMakePlatform` matching the current platform. This folder contains files the define CMake's defaults for given platforms. Any of them can be overridden by either command line or by environment variables. """ from __future__ import annotations from .abstract import CMakeGenerator from .platform_factory import get_platform __all__ = ["CMakeGenerator", "get_platform"] scikit-build-0.18.1/skbuild/platform_specifics/abstract.py000066400000000000000000000304731466366435500237030ustar00rootroot00000000000000"""This module defines objects useful to discover which CMake generator is supported on the current platform.""" from __future__ import annotations import os import shutil import subprocess import textwrap from typing import Iterable, Mapping from ..constants import CMAKE_DEFAULT_EXECUTABLE from ..exceptions import SKBuildGeneratorNotFoundError from ..utils import push_dir test_folder = "_cmake_test_compile" class CMakePlatform: """This class encapsulates the logic allowing to get the identifier of a working CMake generator. Derived class should at least set :attr:`default_generators`. """ def __init__(self) -> None: # default_generators is a property for mocking in tests self._default_generators: list[CMakeGenerator] = [] self.architecture: str | None = None @property def default_generators(self) -> list[CMakeGenerator]: """List of generators considered by :func:`get_best_generator()`.""" return self._default_generators @default_generators.setter def default_generators(self, generators: list[CMakeGenerator]) -> None: self._default_generators = generators @property def generator_installation_help(self) -> str: """Return message guiding the user for installing a valid toolchain.""" raise NotImplementedError() # pragma: no cover @staticmethod def write_test_cmakelist(languages: Iterable[str]) -> None: """Write a minimal ``CMakeLists.txt`` useful to check if the requested ``languages`` are supported.""" if not os.path.exists(test_folder): os.makedirs(test_folder) with open(f"{test_folder}/CMakeLists.txt", "w", encoding="utf-8") as f: f.write("cmake_minimum_required(VERSION 3.5)\n") f.write("PROJECT(compiler_test NONE)\n") for language in languages: f.write(f"ENABLE_LANGUAGE({language:s})\n") f.write( 'if("${_SKBUILD_FORCE_MSVC}")\n' ' if("${_SKBUILD_FORCE_MSVC}" STREQUAL "1930")\n' ' math(EXPR FORCE_MAX "${_SKBUILD_FORCE_MSVC}+19")\n' " else()\n" ' math(EXPR FORCE_MAX "${_SKBUILD_FORCE_MSVC}+9")\n' " endif()\n" ' math(EXPR FORCE_MIN "${_SKBUILD_FORCE_MSVC}")\n' " if(NOT MSVC)\n" ' message(FATAL_ERROR "MSVC is required to pass this check.")\n' " elseif(MSVC_VERSION LESS FORCE_MIN OR MSVC_VERSION GREATER FORCE_MAX)\n" ' message(FATAL_ERROR "MSVC ${MSVC_VERSION} does pass this check.")\n' " endif()\n" "endif()\n" ) @staticmethod def cleanup_test() -> None: """Delete test project directory.""" if os.path.exists(test_folder): shutil.rmtree(test_folder) def get_generator(self, generator_name: str) -> CMakeGenerator: """Loop over generators and return the first that matches the given name. """ for default_generator in self.default_generators: if default_generator.name == generator_name: return default_generator return CMakeGenerator(generator_name) def get_generators(self, generator_name: str) -> list[CMakeGenerator]: """Loop over generators and return all that match the given name.""" return [ default_generator for default_generator in self.default_generators if default_generator.name == generator_name ] # TODO: this method name is not great. Does anyone have a better idea for # renaming it? def get_best_generator( self, generator_name: str | None = None, skip_generator_test: bool = False, languages: Iterable[str] = ("CXX", "C"), cleanup: bool = True, cmake_executable: str = CMAKE_DEFAULT_EXECUTABLE, cmake_args: Iterable[str] = (), architecture: str | None = None, ) -> CMakeGenerator: """Loop over generators to find one that works by configuring and compiling a test project. :param generator_name: If provided, uses only provided generator, \ instead of trying :attr:`default_generators`. :type generator_name: str | None :param skip_generator_test: If set to True and if a generator name is \ specified, the generator test is skipped. If no generator_name is specified \ and the option is set to True, the first available generator is used. :type skip_generator_test: bool :param languages: The languages you'll need for your project, in terms \ that CMake recognizes. :type languages: tuple :param cleanup: If True, cleans up temporary folder used to test \ generators. Set to False for debugging to see CMake's output files. :type cleanup: bool :param cmake_executable: Path to CMake executable used to configure \ and build the test project used to evaluate if a generator is working. :type cmake_executable: str :param cmake_args: List of CMake arguments to use when configuring \ the test project. Only arguments starting with ``-DCMAKE_`` are \ used. :type cmake_args: tuple :return: CMake Generator object :rtype: :class:`CMakeGenerator` or None :raises skbuild.exceptions.SKBuildGeneratorNotFoundError: """ candidate_generators: list[CMakeGenerator] = [] if generator_name is None: candidate_generators = self.default_generators else: # Lookup CMakeGenerator by name. Doing this allow to get a # generator object with its ``env`` property appropriately # initialized. # MSVC should be used in "-A arch" form if architecture is not None: self.architecture = architecture # Support classic names for generators generator_name, self.architecture = _parse_legacy_generator_name(generator_name, self.architecture) candidate_generators = [] for default_generator in self.default_generators: if default_generator.name == generator_name: candidate_generators.append(default_generator) if not candidate_generators: candidate_generators = [CMakeGenerator(generator_name)] self.write_test_cmakelist(languages) working_generator: CMakeGenerator | None if skip_generator_test: working_generator = candidate_generators[0] else: working_generator = self.compile_test_cmakelist(cmake_executable, candidate_generators, cmake_args) if working_generator is None: line = "*" * 80 installation_help = self.generator_installation_help msg = textwrap.dedent( f"""\ {line} scikit-build could not get a working generator for your system. Aborting build. {installation_help} {line}""" ) raise SKBuildGeneratorNotFoundError(msg) if cleanup: CMakePlatform.cleanup_test() return working_generator @staticmethod @push_dir(directory=test_folder) def compile_test_cmakelist( cmake_exe_path: str, candidate_generators: Iterable[CMakeGenerator], cmake_args: Iterable[str] = () ) -> CMakeGenerator | None: """Attempt to configure the test project with each :class:`CMakeGenerator` from ``candidate_generators``. Only cmake arguments starting with ``-DCMAKE_`` are used to configure the test project. The function returns the first generator allowing to successfully configure the test project using ``cmake_exe_path``.""" # working generator is the first generator we find that works. working_generator = None # Include only -DCMAKE_* arguments cmake_args = [arg for arg in cmake_args if arg.startswith("-DCMAKE_")] # Do not complain about unused CMake arguments cmake_args.insert(0, "--no-warn-unused-cli") def _generator_discovery_status_msg(_generator: CMakeGenerator, suffix: str = "") -> None: outer = "-" * 80 inner = ["-" * ((idx * 5) - 3) for idx in range(1, 8)] print("\n".join(inner) if suffix else outer) print(f"-- Trying {_generator.description!r} generator{suffix}") print(outer if suffix else "\n".join(inner[::-1]), flush=True) for generator in candidate_generators: print("\n", flush=True) _generator_discovery_status_msg(generator) # clear the cache for each attempted generator type if os.path.isdir("build"): shutil.rmtree("build") with push_dir("build", make_directory=True): # call cmake to see if the compiler specified by this # generator works for the specified languages cmd = [cmake_exe_path, "../", "-G", generator.name] if generator.toolset: cmd.extend(["-T", generator.toolset]) if generator.architecture and "Visual Studio" in generator.name: cmd.extend(["-A", generator.architecture]) cmd.extend(cmake_args) cmd.extend(generator.args) status = subprocess.run(cmd, env=generator.env, check=False).returncode msg = "success" if status == 0 else "failure" _generator_discovery_status_msg(generator, f" - {msg}") print(flush=True) # cmake succeeded, this generator should work if status == 0: # we have a working generator, don't bother looking for more working_generator = generator break return working_generator class CMakeGenerator: """Represents a CMake generator. .. automethod:: __init__ """ def __init__( self, name: str, env: Mapping[str, str] | None = None, toolset: str | None = None, arch: str | None = None, args: Iterable[str] | None = None, ) -> None: """Instantiate a generator object with the given ``name``. By default, ``os.environ`` is associated with the generator. Dictionary passed as ``env`` parameter will be merged with ``os.environ``. If an environment variable is set in both ``os.environ`` and ``env``, the variable in ``env`` is used. Some CMake generators support a ``toolset`` specification to tell the native build system how to choose a compiler. You can also include CMake arguments. """ self._generator_name = name self.args = list(args or []) self.env = dict(list(os.environ.items()) + list(env.items() if env else [])) self._generator_toolset = toolset self._generator_architecture = arch description_arch = name if arch is None else f"{name} {arch}" if toolset is None: self._description = description_arch else: self._description = f"{description_arch} {toolset}" @property def name(self) -> str: """Name of CMake generator.""" return self._generator_name @property def toolset(self) -> str | None: """Toolset specification associated with the CMake generator.""" return self._generator_toolset @property def architecture(self) -> str | None: """Architecture associated with the CMake generator.""" return self._generator_architecture @property def description(self) -> str: """Name of CMake generator with properties describing the environment (e.g toolset)""" return self._description def _parse_legacy_generator_name(generator_name: str, arch: str | None) -> tuple[str, str | None]: """ Support classic names for MSVC generators. Architecture is stripped from the name and "arch" is replaced with the arch string if a legacy name is given. """ if generator_name.startswith("Visual Studio"): if generator_name.endswith(" Win64"): arch = "x64" generator_name = generator_name[:-6] elif generator_name.endswith(" ARM"): arch = "ARM" generator_name = generator_name[:-4] return generator_name, arch scikit-build-0.18.1/skbuild/platform_specifics/aix.py000066400000000000000000000013721466366435500226550ustar00rootroot00000000000000"""This module defines object specific to AIX platform.""" from __future__ import annotations import sys import textwrap from . import unix class AIXPlatform(unix.UnixPlatform): """AIX implementation of :class:`.abstract.CMakePlatform`.""" @property def generator_installation_help(self) -> str: """Return message guiding the user for installing a valid toolchain.""" return ( textwrap.dedent( """ Building AIX wheels for Python {pyver} requires IBM XL C/C++. Get it here: https://www.ibm.com/products/xl-c-aix-compiler-power """ ) .format(pyver=".".join(str(v) for v in sys.version_info[:2])) .strip() ) scikit-build-0.18.1/skbuild/platform_specifics/bsd.py000066400000000000000000000003761466366435500226470ustar00rootroot00000000000000"""This module defines object specific to BSD platform.""" from __future__ import annotations from . import unix # pylint:disable=abstract-method class BSDPlatform(unix.UnixPlatform): """BSD implementation of :class:`.abstract.CMakePlatform`.""" scikit-build-0.18.1/skbuild/platform_specifics/cygwin.py000066400000000000000000000017111466366435500233710ustar00rootroot00000000000000"""This module defines object specific to Cygwin platform.""" from __future__ import annotations import sys import textwrap from . import abstract from .abstract import CMakeGenerator class CygwinPlatform(abstract.CMakePlatform): """Cygwin implementation of :class:`.abstract.CMakePlatform`.""" def __init__(self) -> None: super().__init__() self.default_generators = [CMakeGenerator("Ninja"), CMakeGenerator("Unix Makefiles")] @property def generator_installation_help(self) -> str: """Return message guiding the user for installing a valid toolchain.""" pyver = ".".join(str(v) for v in sys.version_info[:2]) return textwrap.dedent( f"""\ Building Cygwin wheels for Python {pyver} requires Cygwin packages ninja or make and compilers from e.g. gcc-core and gcc-g++. Get them here: https://cygwin.com/packages/package_list.html""" ) scikit-build-0.18.1/skbuild/platform_specifics/linux.py000066400000000000000000000053731466366435500232400ustar00rootroot00000000000000"""This module defines object specific to Linux platform.""" from __future__ import annotations import platform import sys import textwrap import distro from . import unix class LinuxPlatform(unix.UnixPlatform): """Linux implementation of :class:`.abstract.CMakePlatform`""" @staticmethod def build_essential_install_cmd() -> tuple[str, str]: """Return a tuple of the form ``(distribution_name, cmd)``. ``cmd`` is the command allowing to install the build tools in the current Linux distribution. It set to an empty string if the command is not known. ``distribution_name`` is the name of the current distribution. It is set to an empty string if the distribution could not be determined. """ # gentoo, slackware: Compiler is available by default. distribution_name = distro.id() cmd = "" if distribution_name in {"debian", "Ubuntu", "mandrake", "mandriva"}: cmd = "sudo apt-get install build-essential" elif distribution_name in {"centos", "fedora", "redhat", "turbolinux", "yellowdog", "rocks"}: # http://unix.stackexchange.com/questions/16422/cant-install-build-essential-on-centos#32439 cmd = "sudo yum groupinstall 'Development Tools'" elif distribution_name in {"SuSE"}: # http://serverfault.com/questions/437680/equivalent-development-build-tools-for-suse-professional-11#437681 cmd = "zypper install -t pattern devel_C_C++" return distribution_name, cmd @property def generator_installation_help(self) -> str: """Return message guiding the user for installing a valid toolchain.""" distribution_name, cmd = self.build_essential_install_cmd() install_help = "" if distribution_name: install_help = f"But scikit-build does *NOT* know how to install it on {distribution_name}\n" if distribution_name and cmd: install_help = f"It can be installed using {distribution_name} package manager:\n\n {cmd}\n" arch = "x64" if platform.architecture()[0] == "64bit" else "x86" version_str = ".".join(str(v) for v in sys.version_info[:2]) return textwrap.dedent( f""" Building Linux wheels for Python {version_str} requires a compiler (e.g gcc). {install_help} To build compliant wheels, consider using the manylinux system described in PEP-513. Get it with "dockcross/manylinux-{arch}" docker image: https://github.com/dockcross/dockcross#readme For more details, please refer to scikit-build documentation: http://scikit-build.readthedocs.io/en/latest/generators.html#linux """ ).strip() scikit-build-0.18.1/skbuild/platform_specifics/osx.py000066400000000000000000000013441466366435500227040ustar00rootroot00000000000000"""This module defines object specific to OSX platform.""" from __future__ import annotations import sys import textwrap from . import unix class OSXPlatform(unix.UnixPlatform): """OSX implementation of :class:`.abstract.CMakePlatform`.""" @property def generator_installation_help(self) -> str: """Return message guiding the user for installing a valid toolchain.""" return ( textwrap.dedent( """ Building MacOSX wheels for Python {pyver} requires XCode. Get it here: https://developer.apple.com/xcode/ """ ) .format(pyver=".".join(str(v) for v in sys.version_info[:2])) .strip() ) scikit-build-0.18.1/skbuild/platform_specifics/platform_factory.py000066400000000000000000000026221466366435500254460ustar00rootroot00000000000000"""This modules implements the logic allowing to instantiate the expected :class:`.abstract.CMakePlatform`.""" from __future__ import annotations # pylint: disable=import-outside-toplevel import platform from . import abstract def get_platform() -> abstract.CMakePlatform: """Return an instance of :class:`.abstract.CMakePlatform` corresponding to the current platform.""" this_platform = platform.system().lower() if this_platform == "windows": from . import windows return windows.WindowsPlatform() # Some flexibility based on what emcripten distros decide to call themselves if this_platform.startswith(("linux", "emscripten", "pyodide", "android")): from . import linux return linux.LinuxPlatform() if this_platform.startswith("cygwin"): from . import cygwin return cygwin.CygwinPlatform() if this_platform in ["darwin", "ios"]: from . import osx return osx.OSXPlatform() if this_platform in {"freebsd", "netbsd", "os400", "openbsd"}: from . import bsd return bsd.BSDPlatform() if this_platform == "sunos": from . import sunos return sunos.SunOSPlatform() if this_platform == "aix": from . import aix return aix.AIXPlatform() msg = f"Unsupported platform: {this_platform:s}. Please contact the scikit-build team." raise RuntimeError(msg) scikit-build-0.18.1/skbuild/platform_specifics/sunos.py000066400000000000000000000013721466366435500232430ustar00rootroot00000000000000"""This module defines object specific to SunOS platform.""" from __future__ import annotations import sys import textwrap from . import unix class SunOSPlatform(unix.UnixPlatform): """SunOS implementation of :class:`.abstract.CMakePlatform`.""" @property def generator_installation_help(self) -> str: """Return message guiding the user for installing a valid toolchain.""" return ( textwrap.dedent( """ Building SunOS wheels for Python {pyver} requires build toolchain. It can be installed using: pkg install build-essential """ ) .format(pyver=".".join(str(v) for v in sys.version_info[:2])) .strip() ) scikit-build-0.18.1/skbuild/platform_specifics/unix.py000066400000000000000000000014151466366435500230550ustar00rootroot00000000000000"""This module defines object specific to Unix platform.""" from __future__ import annotations import os from . import abstract from .abstract import CMakeGenerator # pylint:disable=abstract-method class UnixPlatform(abstract.CMakePlatform): """Unix implementation of :class:`.abstract.CMakePlatform`.""" def __init__(self) -> None: super().__init__() try: import ninja # pylint: disable=import-outside-toplevel ninja_executable_path = os.path.join(ninja.BIN_DIR, "ninja") ninja_args = [f"-DCMAKE_MAKE_PROGRAM:FILEPATH={ninja_executable_path}"] except ImportError: ninja_args = [] self.default_generators = [CMakeGenerator("Ninja", args=ninja_args), CMakeGenerator("Unix Makefiles")] scikit-build-0.18.1/skbuild/platform_specifics/windows.py000066400000000000000000000227201466366435500235660ustar00rootroot00000000000000"""This module defines object specific to Windows platform.""" from __future__ import annotations import os import platform import re import subprocess import sys import textwrap from typing import Iterable from setuptools import monkey from .._compat.typing import TypedDict from . import abstract from .abstract import CMakeGenerator VS_YEAR_TO_VERSION = { "2017": 15, "2019": 16, "2022": 17, } """Describes the version of `Visual Studio` supported by :class:`CMakeVisualStudioIDEGenerator` and :class:`CMakeVisualStudioCommandLineGenerator`. The different version are identified by their year. """ VS_YEAR_TO_MSC_VER = { "2017": "1910", # VS 2017 - can be +9 "2019": "1920", # VS 2019 - can be +9 "2022": "1930", # VS 2022 - can be +19 } ARCH_TO_MSVC_ARCH = { "Win32": "x86", "ARM64": "x86_arm64", "x64": "x86_amd64", } class CachedEnv(TypedDict): """Stored environment.""" PATH: str INCLUDE: str LIB: str class WindowsPlatform(abstract.CMakePlatform): """Windows implementation of :class:`.abstract.CMakePlatform`.""" def __init__(self) -> None: super().__init__() self._vs_help = textwrap.dedent( """ Building windows wheels requires Microsoft Visual Studio. Get it from: n https://visualstudio.microsoft.com/vs/ """ ).strip() # For Python 3.7 and above: VS2022, VS2019, VS2017 supported_vs_years = [("2022", "v144"), ("2022", "v143"), ("2019", "v142"), ("2017", "v141")] try: import ninja # pylint: disable=import-outside-toplevel ninja_executable_path = os.path.join(ninja.BIN_DIR, "ninja") ninja_args = [f"-DCMAKE_MAKE_PROGRAM:FILEPATH={ninja_executable_path}"] except ImportError: ninja_args = [] extra = [] for vs_year, vs_toolset in supported_vs_years: vs_version = VS_YEAR_TO_MSC_VER[vs_year] args = [f"-D_SKBUILD_FORCE_MSVC={vs_version}"] self.default_generators.extend( [ CMakeVisualStudioCommandLineGenerator("Ninja", vs_year, vs_toolset, args=ninja_args + args), CMakeVisualStudioIDEGenerator(vs_year, vs_toolset), ] ) extra.append(CMakeVisualStudioCommandLineGenerator("NMake Makefiles", vs_year, vs_toolset, args=args)) self.default_generators.extend(extra) @property def generator_installation_help(self) -> str: """Return message guiding the user for installing a valid toolchain.""" return self._vs_help def _compute_arch() -> str: """Currently only supports Intel -> ARM cross-compilation.""" if platform.machine() == "ARM64" or "arm64" in os.environ.get("SETUPTOOLS_EXT_SUFFIX", "").lower(): return "ARM64" if platform.architecture()[0] == "64bit": return "x64" return "Win32" class CMakeVisualStudioIDEGenerator(CMakeGenerator): """ Represents a Visual Studio CMake generator. .. automethod:: __init__ """ def __init__(self, year: str, toolset: str | None = None) -> None: """Instantiate a generator object with its name set to the `Visual Studio` generator associated with the given ``year`` (see :data:`VS_YEAR_TO_VERSION`), the current platform (32-bit or 64-bit) and the selected ``toolset`` (if applicable). """ vs_version = VS_YEAR_TO_VERSION[year] vs_base = f"Visual Studio {vs_version} {year}" vs_arch = _compute_arch() super().__init__(vs_base, toolset=toolset, arch=vs_arch) def _find_visual_studio_2017_or_newer(vs_version: int) -> str: """Adapted from https://github.com/python/cpython/blob/3.7/Lib/distutils/_msvccompiler.py The ``vs_version`` corresponds to the `Visual Studio` version to lookup. See :data:`VS_YEAR_TO_VERSION`. Returns `path` based on the result of invoking ``vswhere.exe``. If no install is found, returns an empty string. ..note: If ``vswhere.exe`` is not available, by definition, VS 2017 or newer is not installed. """ root = os.environ.get("PROGRAMFILES(X86)") or os.environ.get("PROGRAMFILES") if not root: return "" try: # vswhere.exe may return multiple locations separated by new line. For # example for 2022 Build Tools and for 2022 Community Edition. Ideally # we want user to have the option to choose, but as a quick fix we just # return first available option. path = ( subprocess.run( [ os.path.join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"), "-version", f"[{vs_version:.1f}, {vs_version + 1:.1f})", "-prerelease", "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", "-property", "installationPath", "-products", "*", ], encoding="utf-8" if sys.platform.startswith("cygwin") else "mbcs", check=True, stdout=subprocess.PIPE, errors="strict", ) .stdout.strip() .split("\n")[0] .strip() ) except (subprocess.CalledProcessError, OSError, UnicodeDecodeError): return "" path = os.path.join(path, "VC", "Auxiliary", "Build") if os.path.isdir(path): return path return "" def find_visual_studio(vs_version: int) -> str: """Return Visual Studio installation path associated with ``vs_version`` or an empty string if any. The ``vs_version`` corresponds to the `Visual Studio` version to lookup. See :data:`VS_YEAR_TO_VERSION`. .. note:: - Returns `path` based on the result of invoking ``vswhere.exe``. """ return _find_visual_studio_2017_or_newer(vs_version) # To avoid multiple slow calls to ``subprocess.run()`` (either directly or # indirectly through ``query_vcvarsall``), results of previous calls are cached. __get_msvc_compiler_env_cache: dict[str, CachedEnv] = {} def _get_msvc_compiler_env(vs_version: int, vs_toolset: str | None = None) -> CachedEnv | dict[str, str]: """ Return a dictionary of environment variables corresponding to ``vs_version`` that can be used with :class:`CMakeVisualStudioCommandLineGenerator`. The ``vs_toolset`` is used only for Visual Studio 2017 or newer (``vs_version >= 15``). If specified, ``vs_toolset`` is used to set the `-vcvars_ver=XX.Y` argument passed to ``vcvarsall.bat`` script. """ # Set architecture vc_arch = ARCH_TO_MSVC_ARCH[_compute_arch()] # If any, return cached version cache_key = ",".join([str(vs_version), vc_arch, str(vs_toolset)]) if cache_key in __get_msvc_compiler_env_cache: return __get_msvc_compiler_env_cache[cache_key] if hasattr(monkey, "patch_for_msvc_specialized_compiler"): monkey.patch_for_msvc_specialized_compiler() # type: ignore[no-untyped-call] vc_dir = find_visual_studio(vs_version) vcvarsall = os.path.join(vc_dir, "vcvarsall.bat") if not os.path.exists(vcvarsall): return {} # Set vcvars_ver argument based on toolset vcvars_ver = "" if vs_toolset is not None: match = re.findall(r"^v(\d\d)(\d+)$", vs_toolset)[0] if match: match_str = ".".join(match) vcvars_ver = f"-vcvars_ver={match_str}" try: out_bytes = subprocess.run( f'cmd /u /c "{vcvarsall}" {vc_arch} {vcvars_ver} && set', stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=sys.platform.startswith("cygwin"), check=True, ).stdout out = out_bytes.decode("utf-16le", errors="replace") vc_env = { key.lower(): value for key, _, value in (line.partition("=") for line in out.splitlines()) if key and value } cached_env: CachedEnv = { "PATH": vc_env.get("path", ""), "INCLUDE": vc_env.get("include", ""), "LIB": vc_env.get("lib", ""), } __get_msvc_compiler_env_cache[cache_key] = cached_env return cached_env except subprocess.CalledProcessError as exc: print(exc.output.decode("utf-16le", errors="replace"), file=sys.stderr, flush=True) return {} class CMakeVisualStudioCommandLineGenerator(CMakeGenerator): """ Represents a command-line CMake generator initialized with a specific `Visual Studio` environment. .. automethod:: __init__ """ def __init__(self, name: str, year: str, toolset: str | None = None, args: Iterable[str] | None = None): """Instantiate CMake command-line generator. The generator ``name`` can be values like `Ninja`, `NMake Makefiles` or `NMake Makefiles JOM`. The ``year`` defines the `Visual Studio` environment associated with the generator. See :data:`VS_YEAR_TO_VERSION`. If set, the ``toolset`` defines the `Visual Studio Toolset` to select. The platform (32-bit or 64-bit or ARM) is automatically selected. """ arch = _compute_arch() vc_env = _get_msvc_compiler_env(VS_YEAR_TO_VERSION[year], toolset) env = {str(key.upper()): str(value) for key, value in vc_env.items()} super().__init__(name, env, arch=arch, args=args) self._description = f"{self.name} ({CMakeVisualStudioIDEGenerator(year, toolset).description})" scikit-build-0.18.1/skbuild/py.typed000066400000000000000000000000001466366435500173300ustar00rootroot00000000000000scikit-build-0.18.1/skbuild/resources/000077500000000000000000000000001466366435500176555ustar00rootroot00000000000000scikit-build-0.18.1/skbuild/resources/cmake/000077500000000000000000000000001466366435500207355ustar00rootroot00000000000000scikit-build-0.18.1/skbuild/resources/cmake/FindCython.cmake000066400000000000000000000057271466366435500240170ustar00rootroot00000000000000#.rst: # # Find ``cython`` executable. # # This module will set the following variables in your project: # # ``CYTHON_EXECUTABLE`` # path to the ``cython`` program # # ``CYTHON_VERSION`` # version of ``cython`` # # ``CYTHON_FOUND`` # true if the program was found # # For more information on the Cython project, see https://cython.org/. # # *Cython is a language that makes writing C extensions for the Python language # as easy as Python itself.* # #============================================================================= # Copyright 2011 Kitware, Inc. # # 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. #============================================================================= # Use the Cython executable that lives next to the Python executable # if it is a local installation. if(Python_EXECUTABLE) get_filename_component(_python_path ${Python_EXECUTABLE} PATH) elseif(Python3_EXECUTABLE) get_filename_component(_python_path ${Python3_EXECUTABLE} PATH) elseif(DEFINED PYTHON_EXECUTABLE) get_filename_component(_python_path ${PYTHON_EXECUTABLE} PATH) endif() if(DEFINED _python_path) find_program(CYTHON_EXECUTABLE NAMES cython cython.bat cython3 HINTS ${_python_path} DOC "path to the cython executable") else() find_program(CYTHON_EXECUTABLE NAMES cython cython.bat cython3 DOC "path to the cython executable") endif() if(CYTHON_EXECUTABLE) set(CYTHON_version_command ${CYTHON_EXECUTABLE} --version) execute_process(COMMAND ${CYTHON_version_command} OUTPUT_VARIABLE CYTHON_version_output ERROR_VARIABLE CYTHON_version_error RESULT_VARIABLE CYTHON_version_result OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE) if(NOT ${CYTHON_version_result} EQUAL 0) set(_error_msg "Command \"${CYTHON_version_command}\" failed with") set(_error_msg "${_error_msg} output:\n${CYTHON_version_error}") message(SEND_ERROR "${_error_msg}") else() if("${CYTHON_version_output}" MATCHES "^[Cc]ython version ([^,]+)") set(CYTHON_VERSION "${CMAKE_MATCH_1}") else() if("${CYTHON_version_error}" MATCHES "^[Cc]ython version ([^,]+)") set(CYTHON_VERSION "${CMAKE_MATCH_1}") endif() endif() endif() endif() include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(Cython REQUIRED_VARS CYTHON_EXECUTABLE) mark_as_advanced(CYTHON_EXECUTABLE) include(UseCython) scikit-build-0.18.1/skbuild/resources/cmake/FindF2PY.cmake000066400000000000000000000103751466366435500232660ustar00rootroot00000000000000#.rst: # # The purpose of the F2PY –Fortran to Python interface generator– project is to provide a # connection between Python and Fortran languages. # # F2PY is a Python package (with a command line tool f2py and a module f2py2e) that facilitates # creating/building Python C/API extension modules that make it possible to call Fortran 77/90/95 # external subroutines and Fortran 90/95 module subroutines as well as C functions; to access Fortran # 77 COMMON blocks and Fortran 90/95 module data, including allocatable arrays from Python. # # For more information on the F2PY project, see http://www.f2py.com/. # # The following variables are defined: # # :: # # F2PY_EXECUTABLE - absolute path to the F2PY executable # # :: # # F2PY_VERSION_STRING - the version of F2PY found # F2PY_VERSION_MAJOR - the F2PY major version # F2PY_VERSION_MINOR - the F2PY minor version # F2PY_VERSION_PATCH - the F2PY patch version # # # .. note:: # # By default, the module finds the F2PY program associated with the installed NumPy package. # # Example usage # ^^^^^^^^^^^^^ # # Assuming that a package named ``method`` is declared in ``setup.py`` and that the corresponding directory # containing ``__init__.py`` also exists, the following CMake code can be added to ``method/CMakeLists.txt`` # to ensure the C sources associated with ``cylinder_methods.f90`` are generated and the corresponding module # is compiled: # # .. code-block:: cmake # # find_package(F2PY REQUIRED) # # set(f2py_module_name "_cylinder_methods") # set(fortran_src_file "${CMAKE_CURRENT_SOURCE_DIR}/cylinder_methods.f90") # # set(generated_module_file ${CMAKE_CURRENT_BINARY_DIR}/${f2py_module_name}${PYTHON_EXTENSION_MODULE_SUFFIX}) # # add_custom_target(${f2py_module_name} ALL # DEPENDS ${generated_module_file} # ) # # add_custom_command( # OUTPUT ${generated_module_file} # COMMAND ${F2PY_EXECUTABLE} # -m ${f2py_module_name} # -c # ${fortran_src_file} # WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} # ) # # install(FILES ${generated_module_file} DESTINATION methods) # # .. warning:: # # Using ``f2py`` with ``-c`` argument means that f2py is also responsible to build the module. In that # case, CMake is not used to find the compiler and configure the associated build system. # find_program(F2PY_EXECUTABLE NAMES f2py${PYTHON_VERSION_MAJOR} f2py) # XXX This is required to support NumPy < v0.15.0. See note in module documentation above. if(NOT F2PY_EXECUTABLE) find_package(NumPy) set(F2PY_EXECUTABLE "${PYTHON_EXECUTABLE}" "-m" "numpy.f2py") endif() if(NOT F2PY_INCLUDE_DIR) execute_process( COMMAND "${PYTHON_EXECUTABLE}" -c "import os; from numpy import f2py; print(f2py.get_include() if hasattr(f2py, 'get_include') else os.path.join(os.path.dirname(f2py.__file__), 'src'))" OUTPUT_VARIABLE _f2py_directory OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET ) string(REPLACE "\\" "/" _f2py_directory ${_f2py_directory}) set(F2PY_INCLUDE_DIR "${_f2py_directory}" CACHE STRING "F2PY source directory location" FORCE) endif() # Set-up the F2PY libraries and include directories file(GLOB _f2py_sources "${F2PY_INCLUDE_DIR}/*.c") add_library(_f2py_runtime_library STATIC ${_f2py_sources}) target_include_directories( _f2py_runtime_library PRIVATE ${PYTHON_INCLUDE_DIRS} ${NumPy_INCLUDE_DIRS} ) set_target_properties(_f2py_runtime_library PROPERTIES POSITION_INDEPENDENT_CODE ON) set(F2PY_LIBRARIES _f2py_runtime_library) set(F2PY_INCLUDE_DIRS "${F2PY_INCLUDE_DIR}" "${NumPy_INCLUDE_DIRS}") if(F2PY_EXECUTABLE) # extract the version string execute_process(COMMAND "${F2PY_EXECUTABLE}" -v OUTPUT_VARIABLE F2PY_VERSION_STRING OUTPUT_STRIP_TRAILING_WHITESPACE) if("${F2PY_VERSION_STRING}" MATCHES "^([0-9]+)(.([0-9+]))?(.([0-9+]))?$") set(F2PY_VERSION_MAJOR ${CMAKE_MATCH_1}) set(F2PY_VERSION_MINOR "${CMAKE_MATCH_3}") set(F2PY_VERSION_PATCH "${CMAKE_MATCH_5}") endif() endif() # handle the QUIETLY and REQUIRED arguments and set F2PY_FOUND to TRUE if # all listed variables are TRUE include(FindPackageHandleStandardArgs) find_package_handle_standard_args(F2PY REQUIRED_VARS F2PY_EXECUTABLE VERSION_VAR F2PY_VERSION_STRING ) mark_as_advanced(F2PY_EXECUTABLE) include(UseF2PY) scikit-build-0.18.1/skbuild/resources/cmake/FindNumPy.cmake000066400000000000000000000076131466366435500236170ustar00rootroot00000000000000#.rst: # # Find the include directory for ``numpy/arrayobject.h`` as well as other NumPy tools like ``conv-template`` and # ``from-template``. # # This module sets the following variables: # # ``NumPy_FOUND`` # True if NumPy was found. # ``NumPy_INCLUDE_DIRS`` # The include directories needed to use NumpPy. # ``NumPy_VERSION`` # The version of NumPy found. # ``NumPy_CONV_TEMPLATE_EXECUTABLE`` # Path to conv-template executable. # ``NumPy_FROM_TEMPLATE_EXECUTABLE`` # Path to from-template executable. # # The module will also explicitly define one cache variable: # # ``NumPy_INCLUDE_DIR`` # # .. note:: # # To support NumPy < v0.15.0 where ``from-template`` and ``conv-template`` are not declared as entry points, # the module emulates the behavior of standalone executables by setting the corresponding variables with the # path the the python interpreter and the path to the associated script. For example: # :: # # set(NumPy_CONV_TEMPLATE_EXECUTABLE /path/to/python /path/to/site-packages/numpy/distutils/conv_template.py CACHE STRING "Command executing conv-template program" FORCE) # # set(NumPy_FROM_TEMPLATE_EXECUTABLE /path/to/python /path/to/site-packages/numpy/distutils/from_template.py CACHE STRING "Command executing from-template program" FORCE) # if(NOT NumPy_FOUND) set(_find_extra_args) if(NumPy_FIND_REQUIRED) list(APPEND _find_extra_args REQUIRED) endif() if(NumPy_FIND_QUIET) list(APPEND _find_extra_args QUIET) endif() find_program(NumPy_CONV_TEMPLATE_EXECUTABLE NAMES conv-template) find_program(NumPy_FROM_TEMPLATE_EXECUTABLE NAMES from-template) if(PYTHON_EXECUTABLE) execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c "import numpy; print(numpy.get_include())" OUTPUT_VARIABLE _numpy_include_dir OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET ) execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c "import numpy; print(numpy.__version__)" OUTPUT_VARIABLE NumPy_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET ) # XXX This is required to support NumPy < v0.15.0. See note in module documentation above. if(NOT NumPy_CONV_TEMPLATE_EXECUTABLE) execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c "from numpy.distutils import conv_template; print(conv_template.__file__)" OUTPUT_VARIABLE _numpy_conv_template_file OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET ) set(NumPy_CONV_TEMPLATE_EXECUTABLE "${PYTHON_EXECUTABLE}" "${_numpy_conv_template_file}" CACHE STRING "Command executing conv-template program" FORCE) endif() # XXX This is required to support NumPy < v0.15.0. See note in module documentation above. if(NOT NumPy_FROM_TEMPLATE_EXECUTABLE) execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c "from numpy.distutils import from_template; print(from_template.__file__)" OUTPUT_VARIABLE _numpy_from_template_file OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET ) set(NumPy_FROM_TEMPLATE_EXECUTABLE "${PYTHON_EXECUTABLE}" "${_numpy_from_template_file}" CACHE STRING "Command executing from-template program" FORCE) endif() endif() endif() find_path(NumPy_INCLUDE_DIR numpy/arrayobject.h PATHS "${_numpy_include_dir}" "${PYTHON_INCLUDE_DIR}" PATH_SUFFIXES numpy/core/include ) set(NumPy_INCLUDE_DIRS ${NumPy_INCLUDE_DIR}) # handle the QUIETLY and REQUIRED arguments and set NumPy_FOUND to TRUE if # all listed variables are TRUE include(FindPackageHandleStandardArgs) find_package_handle_standard_args(NumPy REQUIRED_VARS NumPy_INCLUDE_DIR NumPy_CONV_TEMPLATE_EXECUTABLE NumPy_FROM_TEMPLATE_EXECUTABLE VERSION_VAR NumPy_VERSION ) mark_as_advanced(NumPy_INCLUDE_DIR) scikit-build-0.18.1/skbuild/resources/cmake/FindPythonExtensions.cmake000066400000000000000000000541351466366435500261110ustar00rootroot00000000000000#.rst: # # This module defines CMake functions to build Python extension modules and # stand-alone executables. # # The following variables are defined: # :: # # PYTHON_PREFIX - absolute path to the current Python # distribution's prefix # PYTHON_SITE_PACKAGES_DIR - absolute path to the current Python # distribution's site-packages directory # PYTHON_RELATIVE_SITE_PACKAGES_DIR - path to the current Python # distribution's site-packages directory # relative to its prefix # PYTHON_SEPARATOR - separator string for file path # components. Equivalent to ``os.sep`` in # Python. # PYTHON_PATH_SEPARATOR - separator string for PATH-style # environment variables. Equivalent to # ``os.pathsep`` in Python. # PYTHON_EXTENSION_MODULE_SUFFIX - suffix of the compiled module. For example, on # Linux, based on environment, it could be ``.cpython-35m-x86_64-linux-gnu.so``. # # # # The following functions are defined: # # .. cmake:command:: python_extension_module # # For libraries meant to be used as Python extension modules, either dynamically # loaded or directly linked. Amend the configuration of the library target # (created using ``add_library``) with additional options needed to build and # use the referenced library as a Python extension module. # # python_extension_module( # [LINKED_MODULES_VAR ] # [FORWARD_DECL_MODULES_VAR ] # [MODULE_SUFFIX ]) # # Only extension modules that are configured to be built as MODULE libraries can # be runtime-loaded through the standard Python import mechanism. All other # modules can only be included in standalone applications that are written to # expect their presence. In addition to being linked against the libraries for # these modules, such applications must forward declare their entry points and # initialize them prior to use. To generate these forward declarations and # initializations, see ``python_modules_header``. # # If ```` does not refer to a target, then it is assumed to refer to an # extension module that is not linked at all, but compiled along with other # source files directly into an executable. Adding these modules does not cause # any library configuration modifications, and they are not added to the list of # linked modules. They still must be forward declared and initialized, however, # and so are added to the forward declared modules list. # # If the associated target is of type ``MODULE_LIBRARY``, the LINK_FLAGS target # property is used to set symbol visibility and export only the module init function. # This applies to GNU and MSVC compilers. # # Options: # # ``LINKED_MODULES_VAR `` # Name of the variable referencing a list of extension modules whose libraries # must be linked into the executables of any stand-alone applications that use # them. By default, the global property ``PY_LINKED_MODULES_LIST`` is used. # # ``FORWARD_DECL_MODULES_VAR `` # Name of the variable referencing a list of extension modules whose entry # points must be forward declared and called by any stand-alone applications # that use them. By default, the global property # ``PY_FORWARD_DECL_MODULES_LIST`` is used. # # ``MODULE_SUFFIX `` # Suffix appended to the python extension module file. # The default suffix is retrieved using ``sysconfig.get_config_var("SO")"``, # if not available, the default is then ``.so`` on unix and ``.pyd`` on # windows. # Setting the variable ``PYTHON_EXTENSION_MODULE_SUFFIX`` in the caller # scope defines the value used for all extensions not having a suffix # explicitly specified using ``MODULE_SUFFIX`` parameter. # # # .. cmake:command:: python_standalone_executable # # python_standalone_executable() # # For standalone executables that initialize their own Python runtime # (such as when building source files that include one generated by Cython with # the --embed option). Amend the configuration of the executable target # (created using ``add_executable``) with additional options needed to properly # build the referenced executable. # # # .. cmake:command:: python_modules_header # # Generate a header file that contains the forward declarations and # initialization routines for the given list of Python extension modules. # ```` is the logical name for the header file (no file extensions). # ```` is the actual destination filename for the header file # (e.g.: decl_modules.h). # # python_modules_header( [HeaderFilename] # [FORWARD_DECL_MODULES_LIST ] # [HEADER_OUTPUT_VAR ] # [INCLUDE_DIR_OUTPUT_VAR ]) # # without the extension is used as the logical name. If only ```` is # # If only ```` is provided, and it ends in the ".h" extension, then it # is assumed to be the ````. The filename of the header file # provided, and it does not end in the ".h" extension, then the # ```` is assumed to ``.h``. # # The exact contents of the generated header file depend on the logical # ````. It should be set to a value that corresponds to the target # application, or for the case of multiple applications, some identifier that # conveyes its purpose. It is featured in the generated multiple inclusion # guard as well as the names of the generated initialization routines. # # The generated header file includes forward declarations for all listed # modules, as well as implementations for the following class of routines: # # ``int _(void)`` # Initializes the python extension module, ````. Returns an integer # handle to the module. # # ``void _LoadAllPythonModules(void)`` # Initializes all listed python extension modules. # # ``void CMakeLoadAllPythonModules(void);`` # Alias for ``_LoadAllPythonModules`` whose name does not depend on # ````. This function is excluded during preprocessing if the # preprocessing macro ``EXCLUDE_LOAD_ALL_FUNCTION`` is defined. # # ``void Py_Initialize_Wrapper();`` # Wrapper arpund ``Py_Initialize()`` that initializes all listed python # extension modules. This function is excluded during preprocessing if the # preprocessing macro ``EXCLUDE_PY_INIT_WRAPPER`` is defined. If this # function is generated, then ``Py_Initialize()`` is redefined to a macro # that calls this function. # # Options: # # ``FORWARD_DECL_MODULES_LIST `` # List of extension modules for which to generate forward declarations of # their entry points and their initializations. By default, the global # property ``PY_FORWARD_DECL_MODULES_LIST`` is used. # # ``HEADER_OUTPUT_VAR `` # Name of the variable to set to the path to the generated header file. By # default, ```` is used. # # ``INCLUDE_DIR_OUTPUT_VAR `` # Name of the variable to set to the path to the directory containing the # generated header file. By default, ``_INCLUDE_DIRS`` is used. # # Defined variables: # # ```` # The path to the generated header file # # ```` # Directory containing the generated header file # # # Example usage # ^^^^^^^^^^^^^ # # .. code-block:: cmake # # find_package(PythonExtensions) # find_package(Cython) # find_package(Boost COMPONENTS python) # # # Simple Cython Module -- no executables # add_cython_target(_module.pyx) # add_library(_module MODULE ${_module}) # python_extension_module(_module) # # # Mix of Cython-generated code and C++ code using Boost Python # # Stand-alone executable -- no modules # include_directories(${Boost_INCLUDE_DIRS}) # add_cython_target(main.pyx CXX EMBED_MAIN) # add_executable(main boost_python_module.cxx ${main}) # target_link_libraries(main ${Boost_LIBRARIES}) # python_standalone_executable(main) # # # stand-alone executable with three extension modules: # # one statically linked, one dynamically linked, and one loaded at runtime # # # # Freely mixes Cython-generated code, code using Boost-Python, and # # hand-written code using the CPython API. # # # module1 -- statically linked # add_cython_target(module1.pyx) # add_library(module1 STATIC ${module1}) # python_extension_module(module1 # LINKED_MODULES_VAR linked_module_list # FORWARD_DECL_MODULES_VAR fdecl_module_list) # # # module2 -- dynamically linked # include_directories(${Boost_INCLUDE_DIRS}) # add_library(module2 SHARED boost_module2.cxx) # target_link_libraries(module2 ${Boost_LIBRARIES}) # python_extension_module(module2 # LINKED_MODULES_VAR linked_module_list # FORWARD_DECL_MODULES_VAR fdecl_module_list) # # # module3 -- loaded at runtime # add_cython_target(module3a.pyx) # add_library(module3 MODULE ${module3a} module3b.cxx) # target_link_libraries(module3 ${Boost_LIBRARIES}) # python_extension_module(module3 # LINKED_MODULES_VAR linked_module_list # FORWARD_DECL_MODULES_VAR fdecl_module_list) # # # application executable -- generated header file + other source files # python_modules_header(modules # FORWARD_DECL_MODULES_LIST ${fdecl_module_list}) # include_directories(${modules_INCLUDE_DIRS}) # # add_cython_target(mainA) # add_cython_target(mainC) # add_executable(main ${mainA} mainB.cxx ${mainC} mainD.c) # # target_link_libraries(main ${linked_module_list} ${Boost_LIBRARIES}) # python_standalone_executable(main) # #============================================================================= # Copyright 2011 Kitware, Inc. # # 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. #============================================================================= find_package(PythonInterp REQUIRED) if(SKBUILD AND NOT PYTHON_LIBRARY) set(PYTHON_LIBRARY "no-library-required") find_package(PythonLibs) unset(PYTHON_LIBRARY) unset(PYTHON_LIBRARIES) else() find_package(PythonLibs) endif() include(targetLinkLibrariesWithDynamicLookup) set(_command " import sys if sys.version_info >= (3, 10): import sysconfig else: import distutils.sysconfig import itertools import os import os.path import site result = None rel_result = None candidate_lists = [] try: if sys.version_info >= (3, 10): candidate_lists.append((sysconfig.get_paths()['purelib'],)) else: candidate_lists.append((distutils.sysconfig.get_python_lib(),)) except AttributeError: pass try: candidate_lists.append(site.getsitepackages()) except AttributeError: pass try: candidate_lists.append((site.getusersitepackages(),)) except AttributeError: pass candidates = itertools.chain.from_iterable(candidate_lists) for candidate in candidates: rel_candidate = os.path.relpath( candidate, sys.prefix) if not rel_candidate.startswith(\"..\"): result = candidate rel_result = rel_candidate break if sys.version_info >= (3, 10): ext_suffix = sysconfig.get_config_var('EXT_SUFFIX') else: ext_suffix = distutils.sysconfig.get_config_var('EXT_SUFFIX') sys.stdout.write(\";\".join(( os.sep, os.pathsep, sys.prefix, result, rel_result, ext_suffix, ))) ") execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c "${_command}" OUTPUT_VARIABLE _list RESULT_VARIABLE _result) list(GET _list 0 _item) set(PYTHON_SEPARATOR "${_item}") mark_as_advanced(PYTHON_SEPARATOR) list(GET _list 1 _item) set(PYTHON_PATH_SEPARATOR "${_item}") mark_as_advanced(PYTHON_PATH_SEPARATOR) list(GET _list 2 _item) set(PYTHON_PREFIX "${_item}") mark_as_advanced(PYTHON_PREFIX) list(GET _list 3 _item) set(PYTHON_SITE_PACKAGES_DIR "${_item}") mark_as_advanced(PYTHON_SITE_PACKAGES_DIR) list(GET _list 4 _item) set(PYTHON_RELATIVE_SITE_PACKAGES_DIR "${_item}") mark_as_advanced(PYTHON_RELATIVE_SITE_PACKAGES_DIR) if(NOT DEFINED PYTHON_EXTENSION_MODULE_SUFFIX) list(GET _list 5 _item) set(PYTHON_EXTENSION_MODULE_SUFFIX "${_item}") endif() function(_set_python_extension_symbol_visibility _target) if(PYTHON_VERSION_MAJOR VERSION_GREATER 2) set(_modinit_prefix "PyInit_") else() set(_modinit_prefix "init") endif() message("_modinit_prefix:${_modinit_prefix}") if("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC") set_target_properties(${_target} PROPERTIES LINK_FLAGS "/EXPORT:${_modinit_prefix}${_target}" ) elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") # Option to not run version script. See https://github.com/scikit-build/scikit-build/issues/668 if(NOT DEFINED SKBUILD_GNU_SKIP_LOCAL_SYMBOL_EXPORT_OVERRIDE) set(SKBUILD_GNU_SKIP_LOCAL_SYMBOL_EXPORT_OVERRIDE FALSE) endif() set(_script_path ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${_target}-version-script.map ) # Export all symbols. See https://github.com/scikit-build/scikit-build/issues/668 if(SKBUILD_GNU_SKIP_LOCAL_SYMBOL_EXPORT_OVERRIDE) file(WRITE ${_script_path} "{global: ${_modinit_prefix}${_target};};" ) else() file(WRITE ${_script_path} "{global: ${_modinit_prefix}${_target}; local: *;};" ) endif() if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "SunOS") set_property(TARGET ${_target} APPEND_STRING PROPERTY LINK_FLAGS " -Wl,--version-script=\"${_script_path}\"" ) else() set_property(TARGET ${_target} APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-M \"${_script_path}\"" ) endif() endif() endfunction() function(python_extension_module _target) set(one_ops LINKED_MODULES_VAR FORWARD_DECL_MODULES_VAR MODULE_SUFFIX) cmake_parse_arguments(_args "" "${one_ops}" "" ${ARGN}) set(_lib_type "NA") if(TARGET ${_target}) get_property(_lib_type TARGET ${_target} PROPERTY TYPE) endif() set(_is_non_lib TRUE) set(_is_static_lib FALSE) if(_lib_type STREQUAL "STATIC_LIBRARY") set(_is_static_lib TRUE) set(_is_non_lib FALSE) endif() set(_is_shared_lib FALSE) if(_lib_type STREQUAL "SHARED_LIBRARY") set(_is_shared_lib TRUE) set(_is_non_lib FALSE) endif() set(_is_module_lib FALSE) if(_lib_type STREQUAL "MODULE_LIBRARY") set(_is_module_lib TRUE) set(_is_non_lib FALSE) endif() if(_is_static_lib OR _is_shared_lib OR _is_non_lib) if(_is_static_lib OR _is_shared_lib) if(_args_LINKED_MODULES_VAR) set(${_args_LINKED_MODULES_VAR} ${${_args_LINKED_MODULES_VAR}} ${_target} PARENT_SCOPE) else() set_property(GLOBAL APPEND PROPERTY PY_LINKED_MODULES_LIST ${_target}) endif() endif() if(_args_FORWARD_DECL_MODULES_VAR) set(${_args_FORWARD_DECL_MODULES_VAR} ${${_args_FORWARD_DECL_MODULES_VAR}} ${_target} PARENT_SCOPE) else() set_property(GLOBAL APPEND PROPERTY PY_FORWARD_DECL_MODULES_LIST ${_target}) endif() endif() if(NOT _is_non_lib) include_directories("${PYTHON_INCLUDE_DIRS}") endif() if(_is_module_lib) set_target_properties(${_target} PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}") endif() if(_is_module_lib OR _is_shared_lib) if(_is_module_lib) if(NOT _args_MODULE_SUFFIX) set(_args_MODULE_SUFFIX "${PYTHON_EXTENSION_MODULE_SUFFIX}") endif() if(_args_MODULE_SUFFIX STREQUAL "" AND WIN32 AND NOT CYGWIN) set(_args_MODULE_SUFFIX ".pyd") endif() if(NOT _args_MODULE_SUFFIX STREQUAL "") set_target_properties(${_target} PROPERTIES SUFFIX ${_args_MODULE_SUFFIX}) endif() endif() target_link_libraries_with_dynamic_lookup(${_target} ${PYTHON_LIBRARIES}) if(_is_module_lib) _set_python_extension_symbol_visibility(${_target}) endif() endif() endfunction() function(python_standalone_executable _target) include_directories(${PYTHON_INCLUDE_DIRS}) target_link_libraries(${_target} ${SKBUILD_LINK_LIBRARIES_KEYWORD} ${PYTHON_LIBRARIES}) endfunction() function(python_modules_header _name) set(one_ops FORWARD_DECL_MODULES_LIST HEADER_OUTPUT_VAR INCLUDE_DIR_OUTPUT_VAR) cmake_parse_arguments(_args "" "${one_ops}" "" ${ARGN}) list(GET _args_UNPARSED_ARGUMENTS 0 _arg0) # if present, use arg0 as the input file path if(_arg0) set(_source_file ${_arg0}) # otherwise, must determine source file from name, or vice versa else() get_filename_component(_name_ext "${_name}" EXT) # if extension provided, _name is the source file if(_name_ext) set(_source_file ${_name}) get_filename_component(_name "${_source_file}" NAME_WE) # otherwise, assume the source file is ${_name}.h else() set(_source_file ${_name}.h) endif() endif() if(_args_FORWARD_DECL_MODULES_LIST) set(static_mod_list ${_args_FORWARD_DECL_MODULES_LIST}) else() get_property(static_mod_list GLOBAL PROPERTY PY_FORWARD_DECL_MODULES_LIST) endif() string(REPLACE "." "_" _header_name "${_name}") string(TOUPPER ${_header_name} _header_name_upper) set(_header_name_upper "_${_header_name_upper}_H") set(generated_file ${CMAKE_CURRENT_BINARY_DIR}/${_source_file}) set(generated_file_tmp "${generated_file}.in") file(WRITE ${generated_file_tmp} "/* Created by CMake. DO NOT EDIT; changes will be lost. */\n") set(_chunk "") set(_chunk "${_chunk}#ifndef ${_header_name_upper}\n") set(_chunk "${_chunk}#define ${_header_name_upper}\n") set(_chunk "${_chunk}\n") set(_chunk "${_chunk}#include \n") set(_chunk "${_chunk}\n") set(_chunk "${_chunk}#ifdef __cplusplus\n") set(_chunk "${_chunk}extern \"C\" {\n") set(_chunk "${_chunk}#endif /* __cplusplus */\n") set(_chunk "${_chunk}\n") set(_chunk "${_chunk}#if PY_MAJOR_VERSION < 3\n") file(APPEND ${generated_file_tmp} "${_chunk}") foreach(_module ${static_mod_list}) file(APPEND ${generated_file_tmp} "PyMODINIT_FUNC init${PYTHON_MODULE_PREFIX}${_module}(void);\n") endforeach() file(APPEND ${generated_file_tmp} "#else /* PY_MAJOR_VERSION >= 3*/\n") foreach(_module ${static_mod_list}) file(APPEND ${generated_file_tmp} "PyMODINIT_FUNC PyInit_${PYTHON_MODULE_PREFIX}${_module}(void);\n") endforeach() set(_chunk "") set(_chunk "${_chunk}#endif /* PY_MAJOR_VERSION >= 3*/\n\n") set(_chunk "${_chunk}#ifdef __cplusplus\n") set(_chunk "${_chunk}}\n") set(_chunk "${_chunk}#endif /* __cplusplus */\n") set(_chunk "${_chunk}\n") file(APPEND ${generated_file_tmp} "${_chunk}") foreach(_module ${static_mod_list}) set(_import_function "${_header_name}_${_module}") set(_prefixed_module "${PYTHON_MODULE_PREFIX}${_module}") set(_chunk "") set(_chunk "${_chunk}int ${_import_function}(void)\n") set(_chunk "${_chunk}{\n") set(_chunk "${_chunk} static char name[] = \"${_prefixed_module}\";\n") set(_chunk "${_chunk} #if PY_MAJOR_VERSION < 3\n") set(_chunk "${_chunk} return PyImport_AppendInittab(") set(_chunk "${_chunk}name, init${_prefixed_module});\n") set(_chunk "${_chunk} #else /* PY_MAJOR_VERSION >= 3 */\n") set(_chunk "${_chunk} return PyImport_AppendInittab(") set(_chunk "${_chunk}name, PyInit_${_prefixed_module});\n") set(_chunk "${_chunk} #endif /* PY_MAJOR_VERSION >= 3 */\n") set(_chunk "${_chunk}}\n\n") file(APPEND ${generated_file_tmp} "${_chunk}") endforeach() file(APPEND ${generated_file_tmp} "void ${_header_name}_LoadAllPythonModules(void)\n{\n") foreach(_module ${static_mod_list}) file(APPEND ${generated_file_tmp} " ${_header_name}_${_module}();\n") endforeach() file(APPEND ${generated_file_tmp} "}\n\n") set(_chunk "") set(_chunk "${_chunk}#ifndef EXCLUDE_LOAD_ALL_FUNCTION\n") set(_chunk "${_chunk}void CMakeLoadAllPythonModules(void)\n") set(_chunk "${_chunk}{\n") set(_chunk "${_chunk} ${_header_name}_LoadAllPythonModules();\n") set(_chunk "${_chunk}}\n") set(_chunk "${_chunk}#endif /* !EXCLUDE_LOAD_ALL_FUNCTION */\n\n") set(_chunk "${_chunk}#ifndef EXCLUDE_PY_INIT_WRAPPER\n") set(_chunk "${_chunk}static void Py_Initialize_Wrapper()\n") set(_chunk "${_chunk}{\n") set(_chunk "${_chunk} ${_header_name}_LoadAllPythonModules();\n") set(_chunk "${_chunk} Py_Initialize();\n") set(_chunk "${_chunk}}\n") set(_chunk "${_chunk}#define Py_Initialize Py_Initialize_Wrapper\n") set(_chunk "${_chunk}#endif /* !EXCLUDE_PY_INIT_WRAPPER */\n\n") set(_chunk "${_chunk}#endif /* !${_header_name_upper} */\n") file(APPEND ${generated_file_tmp} "${_chunk}") # with configure_file() cmake complains that you may not use a file created # using file(WRITE) as input file for configure_file() execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${generated_file_tmp}" "${generated_file}" OUTPUT_QUIET ERROR_QUIET) set(_header_output_var ${_name}) if(_args_HEADER_OUTPUT_VAR) set(_header_output_var ${_args_HEADER_OUTPUT_VAR}) endif() set(${_header_output_var} ${generated_file} PARENT_SCOPE) set(_include_dir_var ${_name}_INCLUDE_DIRS) if(_args_INCLUDE_DIR_OUTPUT_VAR) set(_include_dir_var ${_args_INCLUDE_DIR_OUTPUT_VAR}) endif() set(${_include_dirs_var} ${CMAKE_CURRENT_BINARY_DIR} PARENT_SCOPE) endfunction() include(UsePythonExtensions) scikit-build-0.18.1/skbuild/resources/cmake/UseCython.cmake000066400000000000000000000320531466366435500236630ustar00rootroot00000000000000#.rst: # # The following functions are defined: # # .. cmake:command:: add_cython_target # # Create a custom rule to generate the source code for a Python extension module # using cython. # # add_cython_target( [] # [EMBED_MAIN] # [C | CXX] # [PY2 | PY3] # [OUTPUT_VAR ]) # # ```` is the name of the new target, and ```` # is the path to a cython source file. Note that, despite the name, no new # targets are created by this function. Instead, see ``OUTPUT_VAR`` for # retrieving the path to the generated source for subsequent targets. # # If only ```` is provided, and it ends in the ".pyx" extension, then it # is assumed to be the ````. The name of the input without the # extension is used as the target name. If only ```` is provided, and it # does not end in the ".pyx" extension, then the ```` is assumed to # be ``.pyx``. # # The Cython include search path is amended with any entries found in the # ``INCLUDE_DIRECTORIES`` property of the directory containing the # ```` file. Use ``include_directories`` to add to the Cython # include search path. # # Options: # # ``EMBED_MAIN`` # Embed a main() function in the generated output (for stand-alone # applications that initialize their own Python runtime). # # ``C | CXX`` # Force the generation of either a C or C++ file. By default, a C file is # generated, unless the C language is not enabled for the project; in this # case, a C++ file is generated by default. # # ``PY2 | PY3`` # Force compilation using either Python-2 or Python-3 syntax and code # semantics. By default, Python-2 syntax and semantics are used if the major # version of Python found is 2. Otherwise, Python-3 syntax and semantics are # used. # # ``OUTPUT_VAR `` # Set the variable ```` in the parent scope to the path to the # generated source file. By default, ```` is used as the output # variable name. # # Defined variables: # # ```` # The path of the generated source file. # # Cache variables that affect the behavior include: # # ``CYTHON_ANNOTATE`` # Whether to create an annotated .html file when compiling. # # ``CYTHON_FLAGS`` # Additional flags to pass to the Cython compiler. # # Example usage # ^^^^^^^^^^^^^ # # .. code-block:: cmake # # find_package(Cython) # # # Note: In this case, either one of these arguments may be omitted; their # # value would have been inferred from that of the other. # add_cython_target(cy_code cy_code.pyx) # # add_library(cy_code MODULE ${cy_code}) # target_link_libraries(cy_code ...) # #============================================================================= # Copyright 2011 Kitware, Inc. # # 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. #============================================================================= # Configuration options. set(CYTHON_ANNOTATE OFF CACHE BOOL "Create an annotated .html file when compiling *.pyx.") set(CYTHON_FLAGS "" CACHE STRING "Extra flags to the cython compiler.") mark_as_advanced(CYTHON_ANNOTATE CYTHON_FLAGS) set(CYTHON_CXX_EXTENSION "cxx") set(CYTHON_C_EXTENSION "c") get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES) function(add_cython_target _name) set(options EMBED_MAIN C CXX PY2 PY3) set(options1 OUTPUT_VAR) cmake_parse_arguments(_args "${options}" "${options1}" "" ${ARGN}) list(GET _args_UNPARSED_ARGUMENTS 0 _arg0) # if provided, use _arg0 as the input file path if(_arg0) set(_source_file ${_arg0}) # otherwise, must determine source file from name, or vice versa else() get_filename_component(_name_ext "${_name}" EXT) # if extension provided, _name is the source file if(_name_ext) set(_source_file ${_name}) get_filename_component(_name "${_source_file}" NAME_WE) # otherwise, assume the source file is ${_name}.pyx else() set(_source_file ${_name}.pyx) endif() endif() set(_embed_main FALSE) if("C" IN_LIST languages) set(_output_syntax "C") elseif("CXX" IN_LIST languages) set(_output_syntax "CXX") else() message(FATAL_ERROR "Either C or CXX must be enabled to use Cython") endif() if(_args_EMBED_MAIN) set(_embed_main TRUE) endif() if(_args_C) set(_output_syntax "C") endif() if(_args_CXX) set(_output_syntax "CXX") endif() # Doesn't select an input syntax - Cython # defaults to 2 for Cython 2 and 3 for Cython 3 set(_input_syntax "default") if(_args_PY2) set(_input_syntax "PY2") endif() if(_args_PY3) set(_input_syntax "PY3") endif() set(embed_arg "") if(_embed_main) set(embed_arg "--embed") endif() set(cxx_arg "") set(extension "c") if(_output_syntax STREQUAL "CXX") set(cxx_arg "--cplus") set(extension "cxx") endif() set(py_version_arg "") if(_input_syntax STREQUAL "PY2") set(py_version_arg "-2") elseif(_input_syntax STREQUAL "PY3") set(py_version_arg "-3") endif() set(generated_file "${CMAKE_CURRENT_BINARY_DIR}/${_name}.${extension}") set_source_files_properties(${generated_file} PROPERTIES GENERATED TRUE) set(_output_var ${_name}) if(_args_OUTPUT_VAR) set(_output_var ${_args_OUTPUT_VAR}) endif() set(${_output_var} ${generated_file} PARENT_SCOPE) file(RELATIVE_PATH generated_file_relative ${CMAKE_BINARY_DIR} ${generated_file}) set(comment "Generating ${_output_syntax} source ${generated_file_relative}") set(cython_include_directories "") set(pxd_dependencies "") set(c_header_dependencies "") # Get the include directories. get_directory_property(cmake_include_directories DIRECTORY ${CMAKE_CURRENT_LIST_DIR} INCLUDE_DIRECTORIES) list(APPEND cython_include_directories ${cmake_include_directories}) # Determine dependencies. # Add the pxd file with the same basename as the given pyx file. get_source_file_property(pyx_location ${_source_file} LOCATION) get_filename_component(pyx_path ${pyx_location} PATH) get_filename_component(pyx_file_basename ${_source_file} NAME_WE) unset(corresponding_pxd_file CACHE) find_file(corresponding_pxd_file ${pyx_file_basename}.pxd PATHS "${pyx_path}" ${cmake_include_directories} NO_DEFAULT_PATH) if(corresponding_pxd_file) list(APPEND pxd_dependencies "${corresponding_pxd_file}") endif() # pxd files to check for additional dependencies set(pxds_to_check "${_source_file}" "${pxd_dependencies}") set(pxds_checked "") set(number_pxds_to_check 1) while(number_pxds_to_check GREATER 0) foreach(pxd ${pxds_to_check}) list(APPEND pxds_checked "${pxd}") list(REMOVE_ITEM pxds_to_check "${pxd}") # look for C headers file(STRINGS "${pxd}" extern_from_statements REGEX "cdef[ ]+extern[ ]+from.*$") foreach(statement ${extern_from_statements}) # Had trouble getting the quote in the regex string(REGEX REPLACE "cdef[ ]+extern[ ]+from[ ]+[\"]([^\"]+)[\"].*" "\\1" header "${statement}") unset(header_location CACHE) find_file(header_location ${header} PATHS ${cmake_include_directories}) if(header_location) list(FIND c_header_dependencies "${header_location}" header_idx) if(${header_idx} LESS 0) list(APPEND c_header_dependencies "${header_location}") endif() endif() endforeach() # check for pxd dependencies # Look for cimport statements. set(module_dependencies "") file(STRINGS "${pxd}" cimport_statements REGEX cimport) foreach(statement ${cimport_statements}) if(${statement} MATCHES from) string(REGEX REPLACE "from[ ]+([^ ]+).*" "\\1" module "${statement}") else() string(REGEX REPLACE "cimport[ ]+([^ ]+).*" "\\1" module "${statement}") endif() list(APPEND module_dependencies ${module}) endforeach() # check for pxi dependencies # Look for include statements. set(include_dependencies "") file(STRINGS "${pxd}" include_statements REGEX include) foreach(statement ${include_statements}) string(REGEX REPLACE "include[ ]+[\"]([^\"]+)[\"].*" "\\1" module "${statement}") list(APPEND include_dependencies ${module}) endforeach() list(REMOVE_DUPLICATES module_dependencies) list(REMOVE_DUPLICATES include_dependencies) # Add modules to the files to check, if appropriate. foreach(module ${module_dependencies}) unset(pxd_location CACHE) find_file(pxd_location ${module}.pxd PATHS "${pyx_path}" ${cmake_include_directories} NO_DEFAULT_PATH) if(pxd_location) list(FIND pxds_checked ${pxd_location} pxd_idx) if(${pxd_idx} LESS 0) list(FIND pxds_to_check ${pxd_location} pxd_idx) if(${pxd_idx} LESS 0) list(APPEND pxds_to_check ${pxd_location}) list(APPEND pxd_dependencies ${pxd_location}) endif() # if it is not already going to be checked endif() # if it has not already been checked endif() # if pxd file can be found endforeach() # for each module dependency discovered # Add includes to the files to check, if appropriate. foreach(_include ${include_dependencies}) unset(pxi_location CACHE) find_file(pxi_location ${_include} PATHS "${pyx_path}" ${cmake_include_directories} NO_DEFAULT_PATH) if(pxi_location) list(FIND pxds_checked ${pxi_location} pxd_idx) if(${pxd_idx} LESS 0) list(FIND pxds_to_check ${pxi_location} pxd_idx) if(${pxd_idx} LESS 0) list(APPEND pxds_to_check ${pxi_location}) list(APPEND pxd_dependencies ${pxi_location}) endif() # if it is not already going to be checked endif() # if it has not already been checked endif() # if include file can be found endforeach() # for each include dependency discovered endforeach() # for each include file to check list(LENGTH pxds_to_check number_pxds_to_check) endwhile() # Set additional flags. set(annotate_arg "") if(CYTHON_ANNOTATE) set(annotate_arg "--annotate") endif() set(cython_debug_arg "") set(line_directives_arg "") if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") set(cython_debug_arg "--gdb") set(line_directives_arg "--line-directives") endif() # Include directory arguments. list(REMOVE_DUPLICATES cython_include_directories) set(include_directory_arg "") foreach(_include_dir ${cython_include_directories}) set(include_directory_arg ${include_directory_arg} "--include-dir" "${_include_dir}") endforeach() list(REMOVE_DUPLICATES pxd_dependencies) list(REMOVE_DUPLICATES c_header_dependencies) string(REGEX REPLACE " " ";" CYTHON_FLAGS_LIST "${CYTHON_FLAGS}") # Add the command to run the compiler. add_custom_command(OUTPUT ${generated_file} COMMAND ${CYTHON_EXECUTABLE} ARGS ${cxx_arg} ${include_directory_arg} ${py_version_arg} ${embed_arg} ${annotate_arg} ${cython_debug_arg} ${line_directives_arg} ${CYTHON_FLAGS_LIST} ${pyx_location} --output-file ${generated_file} DEPENDS ${_source_file} ${pxd_dependencies} IMPLICIT_DEPENDS ${_output_syntax} ${c_header_dependencies} COMMENT ${comment}) # NOTE(opadron): I thought about making a proper target, but after trying it # out, I decided that it would be far too convenient to use the same name as # the target for the extension module (e.g.: for single-file modules): # # ... # add_cython_target(_module.pyx) # add_library(_module ${_module}) # ... # # The above example would not be possible since the "_module" target name # would already be taken by the cython target. Since I can't think of a # reason why someone would need the custom target instead of just using the # generated file directly, I decided to leave this commented out. # # add_custom_target(${_name} DEPENDS ${generated_file}) # Remove their visibility to the user. set(corresponding_pxd_file "" CACHE INTERNAL "") set(header_location "" CACHE INTERNAL "") set(pxd_location "" CACHE INTERNAL "") endfunction() scikit-build-0.18.1/skbuild/resources/cmake/UseF2PY.cmake000066400000000000000000000113721466366435500231400ustar00rootroot00000000000000#.rst: # # The following functions are defined: # # .. cmake:command:: add_f2py_target # # Create a custom rule to generate the source code for a Python extension module # using f2py. # # add_f2py_target( [] # [OUTPUT_VAR ]) # # ```` is the name of the new target, and ```` # is the path to a pyf source file. Note that, despite the name, no new # targets are created by this function. Instead, see ``OUTPUT_VAR`` for # retrieving the path to the generated source for subsequent targets. # # If only ```` is provided, and it ends in the ".pyf" extension, then it # is assumed to be the ````. The name of the input without the # extension is used as the target name. If only ```` is provided, and it # does not end in the ".pyf" extension, then the ```` is assumed to # be ``.pyf``. # # # Options: # # ``OUTPUT_VAR `` # Set the variable ```` in the parent scope to the path to the # generated source file. By default, ```` is used as the output # variable name. # # ``DEPENDS [source [source2...]]`` # Sources that must be generated before the F2PY command is run. # # Defined variables: # # ```` # The path of the generated source file. # # Example usage # ^^^^^^^^^^^^^ # # .. code-block:: cmake # # find_package(F2PY) # # # Note: In this case, either one of these arguments may be omitted; their # # value would have been inferred from that of the other. # add_f2py_target(f2py_code f2py_code.pyf) # # add_library(f2py_code MODULE ${f2py_code}) # target_link_libraries(f2py_code ...) # #============================================================================= # Copyright 2011 Kitware, Inc. # # 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. #============================================================================= get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES) function(add_f2py_target _name) set(options ) set(oneValueArgs OUTPUT_VAR) set(multiValueArgs DEPENDS) cmake_parse_arguments(_args "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) list(GET _args_UNPARSED_ARGUMENTS 0 _arg0) # if provided, use _arg0 as the input file path if(_arg0) set(_source_file ${_arg0}) # otherwise, must determine source file from name, or vice versa else() get_filename_component(_name_ext "${_name}" EXT) # if extension provided, _name is the source file if(_name_ext) set(_source_file ${_name}) string(REGEX REPLACE "\\.[^.]*$" "" _name ${_source}) # otherwise, assume the source file is ${_name}.pyf else() set(_source_file ${_name}.pyf) endif() endif() set(_embed_main FALSE) if("C" IN_LIST languages) set(_output_syntax "C") else() message(FATAL_ERROR "C must be enabled to use F2PY") endif() set(extension "c") set(generated_file "${CMAKE_CURRENT_BINARY_DIR}/${_name}module.${extension}") set(generated_wrappers "${CMAKE_CURRENT_BINARY_DIR}/${_name}-f2pywrappers.f" "${CMAKE_CURRENT_BINARY_DIR}/${_name}-f2pywrappers2.f90" ) get_filename_component(generated_file_dir ${generated_file} DIRECTORY) set_source_files_properties(${generated_file} PROPERTIES GENERATED TRUE) set_source_files_properties(${generated_wrappers} PROPERTIES GENERATED TRUE) set(_output_var ${_name}) if(_args_OUTPUT_VAR) set(_output_var ${_args_OUTPUT_VAR}) endif() set(${_output_var} ${generated_file} ${generated_wrappers} PARENT_SCOPE) file(RELATIVE_PATH generated_file_relative ${CMAKE_BINARY_DIR} ${generated_file}) set(comment "Generating ${_output_syntax} source ${generated_file_relative}") # Get the include directories. get_source_file_property(pyf_location ${_source_file} LOCATION) get_filename_component(pyf_path ${pyf_location} PATH) # Create the directory so that the command can cd to it file(MAKE_DIRECTORY ${generated_file_dir}) # Add the command to run the compiler. add_custom_command(OUTPUT ${generated_file} ${generated_wrappers} COMMAND ${F2PY_EXECUTABLE} ${pyf_location} DEPENDS ${_source_file} ${_args_DEPENDS} WORKING_DIRECTORY ${generated_file_dir} COMMENT ${source_comment}) endfunction() scikit-build-0.18.1/skbuild/resources/cmake/UsePythonExtensions.cmake000066400000000000000000000241551466366435500257640ustar00rootroot00000000000000#.rst: # # The following functions are defined: # # .. cmake:command:: add_python_library # # Add a library that contains a mix of C, C++, Fortran, Cython, F2PY, Template, # and Tempita sources. The required targets are automatically generated to # "lower" source files from their high-level representation to a file that the # compiler can accept. # # # add_python_library( # SOURCES [source1 [source2 ...]] # [INCLUDE_DIRECTORIES [dir1 [dir2 ...]] # [LINK_LIBRARIES [lib1 [lib2 ...]] # [DEPENDS [source1 [source2 ...]]]) # # # Example usage # ^^^^^^^^^^^^^ # # .. code-block:: cmake # # find_package(PythonExtensions) # # file(GLOB arpack_sources ARPACK/SRC/*.f ARPACK/UTIL/*.f) # # add_python_library(arpack_scipy # SOURCES ${arpack_sources} # ${g77_wrapper_sources} # INCLUDE_DIRECTORIES ARPACK/SRC # ) # # .. cmake:command:: add_python_extension # # Add a extension that contains a mix of C, C++, Fortran, Cython, F2PY, Template, # and Tempita sources. The required targets are automatically generated to # "lower" source files from their high-level representation to a file that the # compiler can accept. # # # add_python_extension( # SOURCES [source1 [source2 ...]] # [INCLUDE_DIRECTORIES [dir1 [dir2 ...]] # [LINK_LIBRARIES [lib1 [lib2 ...]] # [DEPENDS [source1 [source2 ...]]]) # # # Example usage # ^^^^^^^^^^^^^ # # .. code-block:: cmake # # find_package(PythonExtensions) # # file(GLOB arpack_sources ARPACK/SRC/*.f ARPACK/UTIL/*.f) # # add_python_extension(arpack_scipy # SOURCES ${arpack_sources} # ${g77_wrapper_sources} # INCLUDE_DIRECTORIES ARPACK/SRC # ) # # #============================================================================= # Copyright 2011 Kitware, Inc. # # 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. #============================================================================= macro(_remove_whitespace _output) string(REGEX REPLACE "[ \r\n\t]+" " " ${_output} "${${_output}}") string(STRIP "${${_output}}" ${_output}) endmacro() function(add_python_library _name) set(options STATIC SHARED MODULE) set(multiValueArgs SOURCES INCLUDE_DIRECTORIES LINK_LIBRARIES COMPILE_DEFINITIONS DEPENDS) cmake_parse_arguments(_args "${options}" "" "${multiValueArgs}" ${ARGN} ) # Validate arguments to allow simpler debugging if(NOT _args_SOURCES) message( FATAL_ERROR "You have called add_python_library for library ${_name} without " "any source files. This typically indicates a problem with " "your CMakeLists.txt file" ) endif() # Initialize the list of sources set(_sources ${_args_SOURCES}) # Generate targets for all *.src files set(_processed ) foreach(_source IN LISTS _sources) if(${_source} MATCHES ".pyf.src$" OR ${_source} MATCHES "\\.f\\.src$") if(NOT NumPy_FOUND) message( FATAL_ERROR "NumPy is required to process *.src Template files" ) endif() string(REGEX REPLACE "\\.[^.]*$" "" _source_we ${_source}) add_custom_command( OUTPUT ${_source_we} COMMAND ${NumPy_FROM_TEMPLATE_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/${_source} ${CMAKE_CURRENT_BINARY_DIR}/${_source_we} DEPENDS ${_source} ${_args_DEPENDS} COMMENT "Generating ${_source_we} from template ${_source}" ) list(APPEND _processed ${_source_we}) elseif(${_source} MATCHES "\\.c\\.src$") if(NOT NumPy_FOUND) message( FATAL_ERROR "NumPy is required to process *.src Template files" ) endif() string(REGEX REPLACE "\\.[^.]*$" "" _source_we ${_source}) add_custom_command( OUTPUT ${_source_we} COMMAND ${NumPy_CONV_TEMPLATE_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/${_source} ${CMAKE_CURRENT_BINARY_DIR}/${_source_we} DEPENDS ${_source} ${_args_DEPENDS} COMMENT "Generating ${_source_we} from template ${_source}" ) list(APPEND _processed ${_source_we}) elseif(${_source} MATCHES "\\.pyx\\.in$") if(NOT Cython_FOUND) message( FATAL_ERROR "Cython is required to process *.in Tempita files" ) endif() string(REGEX REPLACE "\\.[^.]*$" "" _source_we ${_source}) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/${_source} ${CMAKE_CURRENT_BINARY_DIR}/${_source} COPYONLY ) set(_tempita_command " import os; import sys; from Cython.Tempita import Template; cwd = os.getcwd(); open(os.path.join(cwd, '${_source_we}'), 'w+') .write( Template.from_filename(os.path.join(cwd, '${_source}'), encoding=sys.getdefaultencoding()).substitute() ) " ) _remove_whitespace(_tempita_command) add_custom_command( OUTPUT ${_source_we} COMMAND ${PYTHON_EXECUTABLE} -c "${_tempita_command}" DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${_source}" ${_args_DEPENDS} ) list(APPEND _processed ${_source_we}) else() list(APPEND _processed ${_source}) endif() endforeach() set(_sources ${_processed}) # If we're building a Python extension and we're given only Fortran sources, # We can conclude that we need to generate a Fortran interface file list(FILTER _processed EXCLUDE REGEX "(\\.f|\\.f90)$") if(NOT _processed AND _args_MODULE) if(NOT NumPy_FOUND) message( FATAL_ERROR "NumPy is required to process *.pyf F2PY files" ) endif() set(_sources_abs ) foreach(_source IN LISTS _sources) if(NOT IS_ABSOLUTE ${_source}) set(_source ${CMAKE_CURRENT_SOURCE_DIR}/${_source}) endif() list(APPEND _sources_abs ${_source}) endforeach() add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_name}.pyf WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMAND ${F2PY_EXECUTABLE} ARGS -h ${_name}.pyf -m ${_name} --overwrite-signature ${_sources_abs} DEPENDS ${_sources} ${_args_DEPENDS} COMMENT "Generating ${_name} Fortran interface file" ) list(APPEND _sources ${_name}.pyf) endif() # Are there F2PY targets? set(_has_f2py_targets OFF) set(_has_cython_targets OFF) # Generate targets for all *.pyx and *.pyf files set(_processed ) foreach(_source IN LISTS _sources) if(${_source} MATCHES \\.pyx$) if(NOT Cython_FOUND) message( FATAL_ERROR "Cython is required to process *.pyx Cython files" ) endif() string(REGEX REPLACE "\\.[^.]*$" "" _pyx_target_name ${_source}) set(_has_cython_targets ON) add_cython_target(${_pyx_target_name} ${_source} OUTPUT_VAR _pyx_target_output DEPENDS ${_args_DEPENDS} ) list(APPEND _processed ${_pyx_target_output}) elseif(${_source} MATCHES \\.pyf$) if(NOT NumPy_FOUND) message( FATAL_ERROR "NumPy is required to process *.pyf F2PY files" ) endif() string(REGEX REPLACE "\\.[^.]*$" "" _pyf_target_name ${_source}) set(_has_f2py_targets ON) add_f2py_target(${_pyf_target_name} ${_source} OUTPUT_VAR _pyf_target_output DEPENDS ${_args_DEPENDS} ) list(APPEND _processed ${_pyf_target_output}) else() list(APPEND _processed ${_source}) endif() endforeach() set(_sources ${_processed}) if(_args_SHARED) add_library(${_name} SHARED ${_sources}) elseif(_args_MODULE) add_library(${_name} MODULE ${_sources}) else() # Assume static add_library(${_name} STATIC ${_sources}) endif() target_include_directories(${_name} PRIVATE ${_args_INCLUDE_DIRECTORIES}) target_link_libraries(${_name} ${SKBUILD_LINK_LIBRARIES_KEYWORD} ${_args_LINK_LIBRARIES}) if(_has_f2py_targets) target_include_directories(${_name} PRIVATE ${F2PY_INCLUDE_DIRS}) target_link_libraries(${_name} ${SKBUILD_LINK_LIBRARIES_KEYWORD} ${F2PY_LIBRARIES}) endif() if(_args_COMPILE_DEFINITIONS) target_compile_definitions(${_name} PRIVATE ${_args_COMPILE_DEFINITIONS}) endif() if(_args_DEPENDS) add_custom_target( "${_name}_depends" DEPENDS ${_args_DEPENDS} ) add_dependencies(${_name} "${_name}_depends") endif() endfunction() function(add_python_extension _name) # FIXME: make sure that extensions with the same name can happen # in multiple directories set(multiValueArgs SOURCES INCLUDE_DIRECTORIES LINK_LIBRARIES COMPILE_DEFINITIONS DEPENDS) cmake_parse_arguments(_args "" "" "${multiValueArgs}" ${ARGN} ) # Validate arguments to allow simpler debugging if(NOT _args_SOURCES) message( FATAL_ERROR "You have called add_python_extension for library ${_name} without " "any source files. This typically indicates a problem with " "your CMakeLists.txt file" ) endif() add_python_library(${_name} MODULE SOURCES ${_args_SOURCES} INCLUDE_DIRECTORIES ${_args_INCLUDE_DIRECTORIES} LINK_LIBRARIES ${_args_LINK_LIBRARIES} COMPILE_DEFINITIONS ${_args_COMPILE_DEFINITIONS} DEPENDS ${_args_DEPENDS} ) python_extension_module(${_name}) file(RELATIVE_PATH _relative "${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}") if(_relative STREQUAL "") set(_relative ".") endif() install( TARGETS ${_name} LIBRARY DESTINATION "${_relative}" RUNTIME DESTINATION "${_relative}" ) endfunction() scikit-build-0.18.1/skbuild/resources/cmake/targetLinkLibrariesWithDynamicLookup.cmake000066400000000000000000000421101466366435500312310ustar00rootroot00000000000000#.rst: # # Public Functions # ^^^^^^^^^^^^^^^^ # # The following functions are defined: # # .. cmake:command:: target_link_libraries_with_dynamic_lookup # # :: # # target_link_libraries_with_dynamic_lookup( []) # # # Useful to "weakly" link a loadable module. For example, it should be used # when compiling a loadable module when the symbols should be resolve from # the run-time environment where the module is loaded, and not a specific # system library. # # Like proper linking, except that the given ```` are not necessarily # linked. Instead, the ```` is produced in a manner that allows for # symbols unresolved within it to be resolved at runtime, presumably by the # given ````. If such a target can be produced, the provided # ```` are not actually linked. # # It links a library to a target such that the symbols are resolved at # run-time not link-time. # # The linker is checked to see if it supports undefined # symbols when linking a shared library. If it does then the library # is not linked when specified with this function. # # On platforms that do not support weak-linking, this function works just # like ``target_link_libraries``. # # .. note:: # # For OSX it uses ``undefined dynamic_lookup``. This is similar to using # ``-shared`` on Linux where undefined symbols are ignored. # # For more details, see `blog `_ # from Tim D. Smith. # # # .. cmake:command:: check_dynamic_lookup # # Check if the linker requires a command line flag to allow leaving symbols # unresolved when producing a target of type ```` that is # weakly-linked against a dependency of type ````. # # ```` # can be one of "STATIC", "SHARED", "MODULE", or "EXE". # # ```` # can be one of "STATIC", "SHARED", or "MODULE". # # Long signature: # # :: # # check_dynamic_lookup( # # # []) # # # Short signature: # # :: # # check_dynamic_lookup() # set to "MODULE" # # set to "SHARED" # # # The result is cached between invocations and recomputed only when the value # of CMake's linker flag list changes; ``CMAKE_STATIC_LINKER_FLAGS`` if # ```` is "STATIC", and ``CMAKE_SHARED_LINKER_FLAGS`` otherwise. # # # Defined variables: # # ```` # Whether the current C toolchain supports weak-linking for target binaries of # type ```` that are weakly-linked against a dependency target of # type ````. # # ```` # List of flags to add to the linker command to produce a working target # binary of type ```` that is weakly-linked against a dependency # target of type ````. # # ``HAS_DYNAMIC_LOOKUP__`` # Cached, global alias for ```` # # ``DYNAMIC_LOOKUP_FLAGS__`` # Cached, global alias for ```` # # # Private Functions # ^^^^^^^^^^^^^^^^^ # # The following private functions are defined: # # .. warning:: These functions are not part of the scikit-build API. They # exist purely as an implementation detail and may change from version # to version without notice, or even be removed. # # We mean it. # # # .. cmake:command:: _get_target_type # # :: # # _get_target_type( ) # # # Shorthand for querying an abbreviated version of the target type # of the given ````. # # ```` is set to: # # - "STATIC" for a STATIC_LIBRARY, # - "SHARED" for a SHARED_LIBRARY, # - "MODULE" for a MODULE_LIBRARY, # - and "EXE" for an EXECUTABLE. # # Defined variables: # # ```` # The abbreviated version of the ````'s type. # # # .. cmake:command:: _test_weak_link_project # # :: # # _test_weak_link_project( # # # ) # # # Attempt to compile and run a test project where a target of type # ```` is weakly-linked against a dependency of type ````: # # - ```` can be one of "STATIC", "SHARED", "MODULE", or "EXE". # - ```` can be one of "STATIC", "SHARED", or "MODULE". # # Defined variables: # # ```` # Whether the current C toolchain can produce a working target binary of type # ```` that is weakly-linked against a dependency target of type # ````. # # ```` # List of flags to add to the linker command to produce a working target # binary of type ```` that is weakly-linked against a dependency # target of type ````. # function(_get_target_type result_var target) set(target_type "SHARED_LIBRARY") if(TARGET ${target}) get_property(target_type TARGET ${target} PROPERTY TYPE) endif() set(result "STATIC") if(target_type STREQUAL "STATIC_LIBRARY") set(result "STATIC") endif() if(target_type STREQUAL "SHARED_LIBRARY") set(result "SHARED") endif() if(target_type STREQUAL "MODULE_LIBRARY") set(result "MODULE") endif() if(target_type STREQUAL "EXECUTABLE") set(result "EXE") endif() set(${result_var} ${result} PARENT_SCOPE) endfunction() function(_test_weak_link_project target_type lib_type can_weak_link_var project_name) set(gnu_ld_ignore "-Wl,--unresolved-symbols=ignore-all") set(osx_dynamic_lookup "-undefined dynamic_lookup") set(no_flag "") if(CMAKE_CROSSCOMPILING) set(link_flag_spec "no_flag") set(link_flag "${${link_flag_spec}}") set(test_skipping_reason "") set(test_pass FALSE) if(APPLE AND NOT CMAKE_CROSSCOMPILING_EMULATOR) set(link_flag_spec "osx_dynamic_lookup") set(link_flag "${${link_flag_spec}}") set(test_skipping_reason " (Cross compiling without emulator on macOS)") set(test_pass TRUE) endif() if(test_pass) set(test_description "Weak Link ${target_type} -> ${lib_type} (${link_flag_spec})") message(STATUS "Performing Test ${test_description} - Assuming Success${test_skipping_reason}") set(${can_weak_link_var} ${test_pass} PARENT_SCOPE) set(${project_name} ${link_flag} PARENT_SCOPE) return() endif() endif() foreach(link_flag_spec gnu_ld_ignore osx_dynamic_lookup no_flag) set(link_flag "${${link_flag_spec}}") set(test_project_dir "${PROJECT_BINARY_DIR}/CMakeTmp") set(test_project_dir "${test_project_dir}/${project_name}") set(test_project_dir "${test_project_dir}/${link_flag_spec}") set(test_project_dir "${test_project_dir}/${target_type}") set(test_project_dir "${test_project_dir}/${lib_type}") set(test_project_src_dir "${test_project_dir}/src") set(test_project_bin_dir "${test_project_dir}/build") file(MAKE_DIRECTORY ${test_project_src_dir}) file(MAKE_DIRECTORY ${test_project_bin_dir}) set(mod_type "STATIC") set(link_mod_lib TRUE) set(link_exe_lib TRUE) set(link_exe_mod FALSE) if("${target_type}" STREQUAL "EXE") set(link_exe_lib FALSE) set(link_exe_mod TRUE) else() set(mod_type "${target_type}") endif() if("${mod_type}" STREQUAL "MODULE") set(link_mod_lib FALSE) endif() file(WRITE "${test_project_src_dir}/CMakeLists.txt" " cmake_minimum_required(VERSION ${CMAKE_VERSION}) project(${project_name} C) include_directories(${test_project_src_dir}) add_library(number ${lib_type} number.c) add_library(counter ${mod_type} counter.c) ") if("${mod_type}" STREQUAL "MODULE") file(APPEND "${test_project_src_dir}/CMakeLists.txt" " set_target_properties(counter PROPERTIES PREFIX \"\") ") endif() if(link_mod_lib) file(APPEND "${test_project_src_dir}/CMakeLists.txt" " target_link_libraries(counter ${SKBUILD_LINK_LIBRARIES_KEYWORD} number) ") elseif(NOT link_flag STREQUAL "") file(APPEND "${test_project_src_dir}/CMakeLists.txt" " set_target_properties(counter PROPERTIES LINK_FLAGS \"${link_flag}\") ") endif() file(APPEND "${test_project_src_dir}/CMakeLists.txt" " add_executable(main main.c) ") if(link_exe_lib) file(APPEND "${test_project_src_dir}/CMakeLists.txt" " target_link_libraries(main ${SKBUILD_LINK_LIBRARIES_KEYWORD} number) ") elseif(NOT link_flag STREQUAL "") file(APPEND "${test_project_src_dir}/CMakeLists.txt" " target_link_libraries(main ${SKBUILD_LINK_LIBRARIES_KEYWORD} \"${link_flag}\") ") endif() if(link_exe_mod) file(APPEND "${test_project_src_dir}/CMakeLists.txt" " target_link_libraries(main ${SKBUILD_LINK_LIBRARIES_KEYWORD} counter) ") else() file(APPEND "${test_project_src_dir}/CMakeLists.txt" " target_link_libraries(main ${SKBUILD_LINK_LIBRARIES_KEYWORD} \"${CMAKE_DL_LIBS}\") ") endif() file(WRITE "${test_project_src_dir}/number.c" " #include static int _number; void set_number(int number) { _number = number; } int get_number() { return _number; } ") file(WRITE "${test_project_src_dir}/number.h" " #ifndef _NUMBER_H #define _NUMBER_H extern void set_number(int); extern int get_number(void); #endif ") file(WRITE "${test_project_src_dir}/counter.c" " #include int count() { int result = get_number(); set_number(result + 1); return result; } ") file(WRITE "${test_project_src_dir}/counter.h" " #ifndef _COUNTER_H #define _COUNTER_H extern int count(void); #endif ") file(WRITE "${test_project_src_dir}/main.c" " #include #include #include ") if(NOT link_exe_mod) file(APPEND "${test_project_src_dir}/main.c" " #include ") endif() file(APPEND "${test_project_src_dir}/main.c" " int my_count() { int result = get_number(); set_number(result + 1); return result; } int main(int argc, char **argv) { int result; ") if(NOT link_exe_mod) file(APPEND "${test_project_src_dir}/main.c" " void *counter_module; int (*count)(void); counter_module = dlopen(\"./counter.so\", RTLD_LAZY | RTLD_GLOBAL); if(!counter_module) goto error; count = dlsym(counter_module, \"count\"); if(!count) goto error; ") endif() file(APPEND "${test_project_src_dir}/main.c" " result = count() != 0 ? EXIT_FAILURE : my_count() != 1 ? EXIT_FAILURE : my_count() != 2 ? EXIT_FAILURE : count() != 3 ? EXIT_FAILURE : count() != 4 ? EXIT_FAILURE : count() != 5 ? EXIT_FAILURE : my_count() != 6 ? EXIT_FAILURE : EXIT_SUCCESS; ") if(NOT link_exe_mod) file(APPEND "${test_project_src_dir}/main.c" " goto done; error: fprintf(stderr, \"Error occurred:\\n %s\\n\", dlerror()); result = 1; done: if(counter_module) dlclose(counter_module); ") endif() file(APPEND "${test_project_src_dir}/main.c" " return result; } ") set(_rpath_arg) if(APPLE) set(_rpath_arg "-DCMAKE_MACOSX_RPATH='${CMAKE_MACOSX_RPATH}'") endif() try_compile(project_compiles "${test_project_bin_dir}" "${test_project_src_dir}" "${project_name}" CMAKE_FLAGS "-DCMAKE_SHARED_LINKER_FLAGS='${CMAKE_SHARED_LINKER_FLAGS}'" "-DCMAKE_ENABLE_EXPORTS=ON" ${_rpath_arg} OUTPUT_VARIABLE compile_output) set(project_works 1) set(run_output) if(project_compiles) execute_process(COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} "${test_project_bin_dir}/main" WORKING_DIRECTORY "${test_project_bin_dir}" RESULT_VARIABLE project_works OUTPUT_VARIABLE run_output ERROR_VARIABLE run_output) endif() set(test_description "Weak Link ${target_type} -> ${lib_type} (${link_flag_spec})") if(project_works EQUAL 0) set(project_works TRUE) message(STATUS "Performing Test ${test_description} - Success") else() set(project_works FALSE) message(STATUS "Performing Test ${test_description} - Failed") file(APPEND ${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/CMakeError.log "Performing Test ${test_description} failed with the " "following output:\n" "BUILD\n-----\n${compile_output}\nRUN\n---\n${run_output}\n") endif() set(${can_weak_link_var} ${project_works} PARENT_SCOPE) if(project_works) set(${project_name} ${link_flag} PARENT_SCOPE) break() endif() endforeach() endfunction() function(check_dynamic_lookup) # Two signatures are supported: if(ARGC EQUAL "1") # # check_dynamic_lookup() # set(target_type "MODULE") set(lib_type "SHARED") set(has_dynamic_lookup_var "${ARGV0}") set(link_flags_var "unused") elseif(ARGC GREATER "2") # # check_dynamic_lookup( # # # []) # set(target_type "${ARGV0}") set(lib_type "${ARGV1}") set(has_dynamic_lookup_var "${ARGV2}") if(ARGC EQUAL "3") set(link_flags_var "unused") else() set(link_flags_var "${ARGV3}") endif() else() message(FATAL_ERROR "missing arguments") endif() _check_dynamic_lookup( ${target_type} ${lib_type} ${has_dynamic_lookup_var} ${link_flags_var} ) set(${has_dynamic_lookup_var} ${${has_dynamic_lookup_var}} PARENT_SCOPE) if(NOT "x${link_flags_var}x" STREQUAL "xunusedx") set(${link_flags_var} ${${link_flags_var}} PARENT_SCOPE) endif() endfunction() function(_check_dynamic_lookup target_type lib_type has_dynamic_lookup_var link_flags_var ) # hash the CMAKE_FLAGS passed and check cache to know if we need to rerun if("${target_type}" STREQUAL "STATIC") string(MD5 cmake_flags_hash "${CMAKE_STATIC_LINKER_FLAGS}") else() string(MD5 cmake_flags_hash "${CMAKE_SHARED_LINKER_FLAGS}") endif() set(cache_var "HAS_DYNAMIC_LOOKUP_${target_type}_${lib_type}") set(cache_hash_var "HAS_DYNAMIC_LOOKUP_${target_type}_${lib_type}_hash") set(result_var "DYNAMIC_LOOKUP_FLAGS_${target_type}_${lib_type}") if( NOT DEFINED ${cache_hash_var} OR NOT "${${cache_hash_var}}" STREQUAL "${cmake_flags_hash}") unset(${cache_var} CACHE) endif() if(NOT DEFINED ${cache_var}) if(CMAKE_CROSSCOMPILING AND NOT CMAKE_CROSSCOMPILING_EMULATOR) set(skip_test TRUE) endif() _test_weak_link_project(${target_type} ${lib_type} has_dynamic_lookup link_flags) set(caveat " (when linking ${target_type} against ${lib_type})") set(${cache_var} "${has_dynamic_lookup}" CACHE BOOL "linker supports dynamic lookup for undefined symbols${caveat}") mark_as_advanced(${cache_var}) set(${result_var} "${link_flags}" CACHE STRING "linker flags for dynamic lookup${caveat}") mark_as_advanced(${result_var}) set(${cache_hash_var} "${cmake_flags_hash}" CACHE INTERNAL "hashed flags for ${cache_var} check") endif() set(${has_dynamic_lookup_var} "${${cache_var}}" PARENT_SCOPE) set(${link_flags_var} "${${result_var}}" PARENT_SCOPE) endfunction() function(target_link_libraries_with_dynamic_lookup target) _get_target_type(target_type ${target}) set(link_props) set(link_items) set(link_libs) foreach(lib ${ARGN}) _get_target_type(lib_type ${lib}) check_dynamic_lookup(${target_type} ${lib_type} has_dynamic_lookup dynamic_lookup_flags) if(has_dynamic_lookup) if(dynamic_lookup_flags) if("${target_type}" STREQUAL "EXE") list(APPEND link_items "${dynamic_lookup_flags}") else() list(APPEND link_props "${dynamic_lookup_flags}") endif() endif() elseif(${lib} MATCHES "(debug|optimized|general)") # See gh-255 else() list(APPEND link_libs "${lib}") endif() endforeach() if(link_props) list(REMOVE_DUPLICATES link_props) endif() if(link_items) list(REMOVE_DUPLICATES link_items) endif() if(link_libs) list(REMOVE_DUPLICATES link_libs) endif() if(link_props) set_target_properties(${target} PROPERTIES LINK_FLAGS "${link_props}") endif() set(links "${link_items}" "${link_libs}") if(links) target_link_libraries(${target} ${SKBUILD_LINK_LIBRARIES_KEYWORD} "${links}") endif() endfunction() scikit-build-0.18.1/skbuild/setuptools_wrap.py000066400000000000000000001222571466366435500215000ustar00rootroot00000000000000"""This module provides functionality for wrapping key infrastructure components from distutils and setuptools. """ from __future__ import annotations import argparse import contextlib import copy import glob import io import json import os import os.path import platform import shutil import stat import sys import warnings from typing import Any, Callable, Generator, Mapping, Sequence import setuptools from distutils.errors import DistutilsArgError, DistutilsError, DistutilsGetoptError from packaging.requirements import Requirement from packaging.version import parse as parse_version from setuptools.dist import Distribution as upstream_Distribution from . import cmaker from ._compat import tomllib from .command import ( bdist, bdist_wheel, build, build_ext, build_py, clean, egg_info, generate_source_manifest, install, install_lib, install_scripts, sdist, ) from .constants import ( CMAKE_DEFAULT_EXECUTABLE, CMAKE_INSTALL_DIR, CMAKE_SPEC_FILE, set_skbuild_plat_name, skbuild_plat_name, ) from .exceptions import ( SKBuildError, SKBuildGeneratorNotFoundError, SKBuildInvalidFileInstallationError, ) from .utils import ( PythonModuleFinder, mkdir_p, parse_manifestin, to_platform_path, to_unix_path, ) def create_skbuild_argparser() -> argparse.ArgumentParser: """Create and return a scikit-build argument parser.""" parser = argparse.ArgumentParser(add_help=False) parser.add_argument( "--build-type", default=None, metavar="", help="specify the CMake build type (e.g. Debug or Release)" ) parser.add_argument("-G", "--generator", metavar="", help="specify the CMake build system generator") parser.add_argument("-j", metavar="N", type=int, dest="jobs", help="allow N build jobs at once") parser.add_argument("--cmake-executable", default=None, metavar="", help="specify the path to the cmake executable") parser.add_argument( "--install-target", default=None, metavar="", help="specify the CMake target performing the install. If not provided, uses the target ``install``", ) parser.add_argument( "--skip-generator-test", action="store_true", help="skip generator test when a generator is explicitly selected using --generator", ) return parser def _is_cmake_configure_argument(arg: str) -> bool: """Return True if ``arg`` is a relevant argument to pass to cmake when configuring a project.""" return any(arg.startswith(cmake_arg) for cmake_arg in ("-C", "-D")) def parse_skbuild_args( args: Sequence[str], cmake_args: Sequence[str], build_tool_args: Sequence[str] ) -> tuple[list[str], str | None, bool, list[str], list[str]]: """ Parse arguments in the scikit-build argument set. Convert specified arguments to proper format and append to cmake_args and build_tool_args. Returns the tuple ``(remaining arguments, cmake executable, skip_generator_test)``. """ parser = create_skbuild_argparser() # Consider CMake arguments passed as global setuptools options _cmake_args = [*cmake_args, *(arg for arg in args if _is_cmake_configure_argument(arg))] # ... and remove them from the list _args = [arg for arg in args if not _is_cmake_configure_argument(arg)] _build_tool_args = list(build_tool_args) namespace, remaining_args = parser.parse_known_args(_args) # Construct CMake argument list if namespace.build_type is not None: _cmake_args.append("-DCMAKE_BUILD_TYPE:STRING=" + namespace.build_type) if namespace.generator is not None: _cmake_args.extend(["-G", namespace.generator]) # Construct build tool argument list if namespace.jobs is not None: _build_tool_args.extend(["-j", str(namespace.jobs)]) if namespace.install_target is not None: _build_tool_args.extend(["--install-target", namespace.install_target]) if namespace.generator is None and namespace.skip_generator_test is True: sys.exit("ERROR: Specifying --skip-generator-test requires --generator to also be specified.") return remaining_args, namespace.cmake_executable, namespace.skip_generator_test, _cmake_args, _build_tool_args def parse_args() -> tuple[list[str], str | None, bool, list[str], list[str]]: """This function parses the command-line arguments ``sys.argv`` and returns the tuple ``(setuptools_args, cmake_executable, skip_generator_test, cmake_args, build_tool_args)`` where each ``*_args`` element corresponds to a set of arguments separated by ``--``.""" dutils: list[str] = [] cmake: list[str] = [] make: list[str] = [] argsets = [dutils, cmake, make] i = 0 separator = "--" for arg in sys.argv: if arg == separator: i += 1 if i >= len(argsets): sys.exit(f"ERROR: Too many {separator!r} separators provided (expected at most {len(argsets) - 1}).") else: argsets[i].append(arg) dutils, cmake_executable, skip_generator_test, cmake, make = parse_skbuild_args(dutils, cmake, make) return dutils, cmake_executable, skip_generator_test, cmake, make @contextlib.contextmanager def _capture_output() -> Generator[list[io.StringIO | str], None, None]: out: list[io.StringIO | str] with contextlib.redirect_stdout(io.StringIO()) as stdout, contextlib.redirect_stderr(io.StringIO()) as stderr: out = [stdout, stderr] yield out assert isinstance(out[0], io.StringIO) assert isinstance(out[1], io.StringIO) out[0] = out[0].getvalue() out[1] = out[1].getvalue() def _parse_setuptools_arguments( setup_attrs: Mapping[str, Any], ) -> tuple[bool, bool, list[str], bool, bool, bool, str, bool]: """This function instantiates a Distribution object and parses the command line arguments. It returns the tuple ``(display_only, help_commands, commands, hide_listing, force_cmake, skip_cmake, plat_name)`` where - display_only is a boolean indicating if an argument like '--help', '--help-commands' or '--author' was passed. - help_commands is a boolean indicating if argument '--help-commands' was passed. - commands contains the list of commands that were passed. - hide_listing is a boolean indicating if the list of files being included in the distribution is displayed or not. - force_cmake a boolean indicating that CMake should always be executed. - skip_cmake is a boolean indicating if the execution of CMake should explicitly be skipped. - plat_name is a string identifying the platform name to embed in generated filenames. It defaults to :func:`skbuild.constants.skbuild_plat_name()`. - build_ext_inplace is a boolean indicating if ``build_ext`` command was specified along with the --inplace argument. Otherwise it raises DistutilsArgError exception if there are any error on the command-line, and it raises DistutilsGetoptError if there any error in the command 'options' attribute. The code has been adapted from the setup() function available in distutils/core.py. """ setup_attrs = dict(setup_attrs) setup_attrs["script_name"] = os.path.basename(sys.argv[0]) dist = upstream_Distribution(setup_attrs) # Update class attribute to also ensure the argument is processed # when ``setuptools.setup`` is called. upstream_Distribution.global_options.extend( [ ("hide-listing", None, "do not display list of files being included in the distribution"), ("force-cmake", None, "always run CMake"), ("skip-cmake", None, "do not run CMake"), ] ) # Find and parse the config file(s): they will override options from # the setup script, but be overridden by the command line. dist.parse_config_files() # Parse the command line and override config files; any # command-line errors are the end user's fault, so turn them into # SystemExit to suppress tracebacks. with _capture_output(): result = dist.parse_command_line() # type: ignore[no-untyped-call] display_only = not result if not hasattr(dist, "hide_listing"): dist.hide_listing = False # type: ignore[attr-defined] if not hasattr(dist, "force_cmake"): dist.force_cmake = False # type: ignore[attr-defined] if not hasattr(dist, "skip_cmake"): dist.skip_cmake = False # type: ignore[attr-defined] plat_names = set() for cmd in [dist.get_command_obj(command) for command in dist.commands]: plat_name = getattr(cmd, "plat_name", None) if plat_name is not None: plat_names.add(plat_name) if not plat_names: plat_names.add(None) elif len(plat_names) > 1: names = ", ".join(plat_names) msg = f"--plat-name is ambiguous: {names}" raise SKBuildError(msg) plat_name = next(iter(plat_names)) build_ext_cmd = dist.get_command_obj("build_ext") build_ext_inplace: bool = getattr(build_ext_cmd, "inplace", False) return ( display_only, dist.help_commands, # type: ignore[attr-defined] dist.commands, dist.hide_listing, # type: ignore[attr-defined] dist.force_cmake, # type: ignore[attr-defined] dist.skip_cmake, # type: ignore[attr-defined] plat_name, build_ext_inplace, ) def _check_skbuild_parameters(cmake_install_dir: str, cmake_source_dir: str) -> None: if os.path.isabs(cmake_install_dir): msg = ( "\n setup parameter 'cmake_install_dir' is set to " "an absolute path. A relative path is expected.\n" f" Project Root : {os.getcwd()}\n" f" CMake Install Directory: {cmake_install_dir}\n" ) raise SKBuildError(msg) if not os.path.exists(os.path.abspath(cmake_source_dir)): msg = ( "\n setup parameter 'cmake_source_dir' set to " "a nonexistent directory.\n" f" Project Root : {os.getcwd()}\n" f" CMake Source Directory: {cmake_source_dir}\n" ) raise SKBuildError(msg) def strip_package(package_parts: Sequence[str], module_file: str) -> str: """Given ``package_parts`` (e.g. ``['foo', 'bar']``) and a ``module_file`` (e.g. ``foo/bar/jaz/rock/roll.py``), starting from the left, this function will strip the parts of the path matching the package parts and return a new string (e.g ``jaz/rock/roll.py``). The function will work as expected for either Windows or Unix-style ``module_file`` and this independently of the platform. """ if not package_parts or os.path.isabs(module_file): return module_file package = "/".join(package_parts) module_dir = os.path.dirname(module_file.replace("\\", "/")) module_dir = module_dir[: len(package)] return module_file[len(package) + 1 :] if package and module_dir.startswith(package) else module_file def _package_data_contain_module(module: tuple[str, str, str], package_data: Mapping[str, list[str]]) -> bool: """Return True if the ``module`` is contained in the ``package_data``. ``module`` is a tuple of the form ``(package, modulename, module_file)``. """ (package, _, module_file) = module if package not in package_data: return False # We need to strip the package because a module entry # usually looks like this: # # ('foo.bar', 'module', 'foo/bar/module.py') # # and the entry in package_data would look like this: # # {'foo.bar' : ['module.py']} return strip_package(package.split("."), module_file) in package_data[package] def _should_run_cmake(commands: Sequence[str], cmake_with_sdist: bool) -> bool: """Return True if at least one command requiring ``cmake`` to run is found in ``commands``.""" given_commands = set(commands) expected_commands = { "build", "build_ext", "develop", "install", "install_lib", "bdist", "bdist_dumb", "bdist_egg", "bdist_rpm", "bdist_wininst", "bdist_wheel", } if expected_commands & given_commands: return True return bool("sdist" in given_commands and cmake_with_sdist) def _save_cmake_spec(args: Mapping[str, Any]) -> None: """Save the CMake spec to disk""" # We use JSON here because readability is more important than performance with contextlib.suppress(OSError): os.makedirs(os.path.dirname(CMAKE_SPEC_FILE())) with open(CMAKE_SPEC_FILE(), "w+", encoding="utf-8") as fp: json.dump(args, fp) def _load_cmake_spec() -> Any: """Load and return the CMake spec from disk""" with contextlib.suppress(OSError, ValueError), open(CMAKE_SPEC_FILE(), encoding="utf-8") as fp: return json.load(fp) return None def get_default_include_package_data() -> bool: """ Include package data if pyproject.toml contains the project or tool.setuptools table. """ # https://setuptools.pypa.io/en/latest/history.html#id255 # https://github.com/pypa/setuptools/pull/3067 pyproject_file = os.path.join(os.getcwd(), "pyproject.toml") try: with open(pyproject_file, "rb") as f: pyproject = tomllib.load(f) return "project" in pyproject or "setuptools" in pyproject.get("tool", {}) except FileNotFoundError: return False def setup( *, cmake_args: Sequence[str] = (), cmake_install_dir: str = "", cmake_source_dir: str = "", cmake_with_sdist: bool = False, cmake_languages: Sequence[str] = ("C", "CXX"), 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, ) -> upstream_Distribution: """This function wraps setup() so that we can run cmake, make, CMake build, then proceed as usual with setuptools, appending the CMake-generated output as necessary. The CMake project is re-configured only if needed. This is achieved by (1) retrieving the environment mapping associated with the generator set in the ``CMakeCache.txt`` file, (2) saving the CMake configure arguments and version in :func:`skbuild.constants.CMAKE_SPEC_FILE()`: and (3) re-configuring only if either the generator or the CMake specs change. """ # If any, strip ending slash from each package directory # Regular setuptools does not support this # TODO: will become an error in the future if "package_dir" in kw: for package, prefix in kw["package_dir"].items(): if prefix.endswith("/"): msg = f"package_dir={{{package!r}: {prefix!r}}} ends with a trailing slash, which is not supported by setuptools." warnings.warn(msg, FutureWarning, stacklevel=2) kw["package_dir"][package] = prefix[:-1] sys.argv, cmake_executable, skip_generator_test, cmake_args_from_args, make_args = parse_args() if any("CMAKE_INSTALL_PREFIX" in arg for arg in cmake_args_from_args): msg = "CMAKE_INSTALL_PREFIX may not be passed to the scikit-build CLI." raise ValueError(msg) if any("CMAKE_INSTALL_PREFIX" in arg for arg in cmake_args): msg = "CMAKE_INSTALL_PREFIX may not be passed via cmake_args to setup." raise ValueError(msg) # work around https://bugs.python.org/issue1011113 # (patches provided, but no updates since 2014) cmdclass = kw.get("cmdclass", {}) cmdclass["build"] = cmdclass.get("build", build.build) cmdclass["build_py"] = cmdclass.get("build_py", build_py.build_py) cmdclass["build_ext"] = cmdclass.get("build_ext", build_ext.build_ext) cmdclass["install"] = cmdclass.get("install", install.install) cmdclass["install_lib"] = cmdclass.get("install_lib", install_lib.install_lib) cmdclass["install_scripts"] = cmdclass.get("install_scripts", install_scripts.install_scripts) cmdclass["clean"] = cmdclass.get("clean", clean.clean) cmdclass["sdist"] = cmdclass.get("sdist", sdist.sdist) cmdclass["bdist"] = cmdclass.get("bdist", bdist.bdist) cmdclass["bdist_wheel"] = cmdclass.get("bdist_wheel", bdist_wheel.bdist_wheel) cmdclass["egg_info"] = cmdclass.get("egg_info", egg_info.egg_info) cmdclass["generate_source_manifest"] = cmdclass.get( "generate_source_manifest", generate_source_manifest.generate_source_manifest ) kw["cmdclass"] = cmdclass # Extract setup keywords specific to scikit-build and remove them from kw. # Removing the keyword from kw need to be done here otherwise, the # following call to _parse_setuptools_arguments would complain about # unknown setup options. # ... and validate them try: _check_skbuild_parameters(cmake_install_dir, cmake_source_dir) except SKBuildError as ex: import traceback # pylint: disable=import-outside-toplevel print("Traceback (most recent call last):", file=sys.stderr) traceback.print_tb(sys.exc_info()[2]) print(file=sys.stderr, flush=True) sys.exit(ex) # type: ignore[arg-type] # Convert source dir to a path relative to the root # of the project if cmake_source_dir == ".": cmake_source_dir = "" if os.path.isabs(cmake_source_dir): cmake_source_dir = os.path.relpath(cmake_source_dir) # Skip running CMake in the following cases: # * flag "--skip-cmake" is provided # * "display only" argument is provided (e.g '--help', '--author', ...) # * no command-line arguments or invalid ones are provided # * no command requiring cmake is provided # * no CMakeLists.txt if found display_only = has_invalid_arguments = help_commands = False force_cmake = skip_cmake = False commands: list[str] = [] try: ( display_only, help_commands, commands, hide_listing, force_cmake, skip_cmake, plat_name, build_ext_inplace, ) = _parse_setuptools_arguments(kw) except (DistutilsArgError, DistutilsGetoptError): has_invalid_arguments = True plat_name = None hide_listing = False has_cmakelists = os.path.exists(os.path.join(cmake_source_dir, "CMakeLists.txt")) if not has_cmakelists: print("skipping skbuild (no CMakeLists.txt found)", flush=True) skip_skbuild = ( display_only or has_invalid_arguments or not _should_run_cmake(commands, cmake_with_sdist) or not has_cmakelists ) if skip_skbuild and not force_cmake: if help_commands: # Prepend scikit-build help. Generate option descriptions using # argparse. skbuild_parser = create_skbuild_argparser() arg_descriptions = [line for line in skbuild_parser.format_help().split("\n") if line.startswith(" ")] print("scikit-build options:") print("\n".join(arg_descriptions)) print() print('Arguments following a "--" are passed directly to CMake (e.g. -DMY_VAR:BOOL=TRUE).') print('Arguments following a second "--" are passed directly to the build tool.') print(flush=True) return setuptools.setup(**kw) developer_mode = "develop" in commands or build_ext_inplace packages = kw.get("packages", []) package_dir = kw.get("package_dir", {}) package_data = {k: copy.copy(v) for k, v in kw.get("package_data", {}).items()} py_modules = kw.get("py_modules", []) new_py_modules = {py_module: False for py_module in py_modules} scripts = kw.get("scripts", []) new_scripts = {script: False for script in scripts} data_files = {(parent_dir or "."): set(file_list) for parent_dir, file_list in kw.get("data_files", [])} # Handle cmake_install_target # get the target (next item after '--install-target') or return '' if no --install-target cmake_install_target_from_command = next( (make_args[index + 1] for index, item in enumerate(make_args) if item == "--install-target"), "" ) cmake_install_target_from_setup = cmake_install_target # Setting target from command takes precedence # cmake_install_target_from_setup has the default 'install', # so cmake_install_target would never be empty. if cmake_install_target_from_command: cmake_install_target = cmake_install_target_from_command else: cmake_install_target = cmake_install_target_from_setup # Parse CMAKE_ARGS env_cmake_args = os.environ["CMAKE_ARGS"].split() if "CMAKE_ARGS" in os.environ else [] env_cmake_args = [s for s in env_cmake_args if "CMAKE_INSTALL_PREFIX" not in s] # Since CMake arguments provided through the command line have more weight # and when CMake is given multiple times a argument, only the last one is # considered, let's prepend the one provided in the setup call. # # Using the environment variable CMAKE_ARGS has lower precedence than # manual options. # # The command line arguments to setup.py are deprecated, but they have highest precedence. cmake_args = [*env_cmake_args, *cmake_args, *cmake_args_from_args] if sys.platform == "darwin": # If no ``--plat-name`` argument was passed, set default value. if plat_name is None: plat_name = skbuild_plat_name() (_, version, machine) = plat_name.split("-") # The loop here allows for CMAKE_OSX_* command line arguments to overload # values passed with either the ``--plat-name`` command-line argument # or the ``cmake_args`` setup option. for cmake_arg in cmake_args: if "CMAKE_OSX_DEPLOYMENT_TARGET" in cmake_arg: version_arg = cmake_arg.split("=")[1] if version_arg: version = version_arg else: msg = "CMAKE_OSX_DEPLOYMENT_TARGET should not be empty if specified, ignoring" warnings.warn(msg, stacklevel=1) if "CMAKE_OSX_ARCHITECTURES" in cmake_arg: machine = cmake_arg.split("=")[1] if set(machine.split(";")) == {"x86_64", "arm64"}: machine = "universal2" elif "CMAKE_SYSTEM_PROCESSOR" in cmake_arg: machine = cmake_arg.split("=")[1] assert machine in {"x86_64", "arm64", "universal2"}, f"macOS arch {machine} not understood" set_skbuild_plat_name(f"macosx-{version}-{machine}") # Set platform env. variable so that commands (e.g. bdist_wheel) # uses this information. The _PYTHON_HOST_PLATFORM env. variable is # used in distutils.util.get_platform() function. os.environ.setdefault("_PYTHON_HOST_PLATFORM", skbuild_plat_name()) # Set CMAKE_OSX_DEPLOYMENT_TARGET and CMAKE_OSX_ARCHITECTURES if not already # specified (_, version, machine) = skbuild_plat_name().split("-") if not cmaker.has_cmake_cache_arg(cmake_args, "CMAKE_OSX_DEPLOYMENT_TARGET"): cmake_args.append(f"-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING={version}") if not cmaker.has_cmake_cache_arg(cmake_args, "CMAKE_OSX_ARCHITECTURES"): machine_archs = "x86_64;arm64" if machine == "universal2" else machine cmake_args.append(f"-DCMAKE_OSX_ARCHITECTURES:STRING={machine_archs}") # Select correct --config using final CMAKE_BUILD_TYPE for item in cmake_args[::-1]: if item.startswith("-DCMAKE_BUILD_TYPE"): _, config_type = item.split("=") break else: config_type = "Release" cmake_args.append("-DCMAKE_BUILD_TYPE:STRING=Release") make_args.extend(["--config", config_type]) # Install cmake if listed in `setup_requires` for package in kw.get("setup_requires", []): if Requirement(package).name == "cmake": setup_requires = [package] dist = upstream_Distribution({"setup_requires": setup_requires}) dist.fetch_build_eggs(setup_requires) with contextlib.suppress(ImportError): # Considering packages associated with "setup_requires" keyword are # installed in .eggs subdirectory without honoring setuptools "console_scripts" # entry_points and without settings the expected executable permissions, we are # taking care of it below. # A local "cmake" folder can be imported by mistake, keep going if it is # pylint: disable-next=import-outside-toplevel from cmake import CMAKE_BIN_DIR for executable_name in ("cmake", "cpack", "ctest"): executable = os.path.join(CMAKE_BIN_DIR, executable_name) if platform.system().lower() == "windows": executable += ".exe" st = os.stat(executable) permissions = st.st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH with contextlib.suppress(PermissionError): os.chmod(executable, permissions) cmake_executable = os.path.join(CMAKE_BIN_DIR, "cmake") break # Languages are used to determine a working generator try: if cmake_executable is None: cmake_executable = CMAKE_DEFAULT_EXECUTABLE cmkr = cmaker.CMaker(cmake_executable) if not skip_cmake: if cmake_minimum_required_version is not None and parse_version(cmkr.cmake_version) < parse_version( cmake_minimum_required_version ): msg = f"CMake version {cmake_minimum_required_version} or higher is required. CMake version {cmkr.cmake_version} is being used" raise SKBuildError(msg) # Used to confirm that the cmake executable is the same, and that the environment # didn't change cmake_spec = { "args": [shutil.which(CMAKE_DEFAULT_EXECUTABLE), *cmake_args], "version": cmkr.cmake_version, "environment": { "PYTHONNOUSERSITE": os.environ.get("PYTHONNOUSERSITE"), "PYTHONPATH": os.environ.get("PYTHONPATH"), }, } # skip the configure step for a cached build env = cmkr.get_cached_generator_env() if env is None or cmake_spec != _load_cmake_spec(): env = cmkr.configure( cmake_args, skip_generator_test=skip_generator_test, cmake_source_dir=cmake_source_dir, cmake_install_dir=cmake_install_dir, languages=cmake_languages, ) _save_cmake_spec(cmake_spec) cmkr.make(make_args, install_target=cmake_install_target, env=env) except SKBuildGeneratorNotFoundError as ex: sys.exit(ex) # type: ignore[arg-type] except SKBuildError as ex: import traceback # pylint: disable=import-outside-toplevel print("Traceback (most recent call last):", file=sys.stderr) traceback.print_tb(sys.exc_info()[2]) print(file=sys.stderr, flush=True) sys.exit(ex) # type: ignore[arg-type] # If needed, set reasonable defaults for package_dir for package in packages: if package not in package_dir: package_dir[package] = package.replace(".", "/") if "" in package_dir: package_dir[package] = to_unix_path(os.path.join(package_dir[""], package_dir[package])) kw["package_dir"] = package_dir package_prefixes = _collect_package_prefixes(package_dir, packages) # This hook enables custom processing of the cmake manifest cmake_manifest = cmkr.install() process_manifest = cmake_process_manifest_hook if process_manifest is not None: if callable(process_manifest): cmake_manifest = process_manifest(cmake_manifest) else: msg = "The cmake_process_manifest_hook argument should be callable." raise SKBuildError(msg) _classify_installed_files( cmake_manifest, package_data, package_prefixes, py_modules, new_py_modules, scripts, new_scripts, data_files, cmake_source_dir, cmake_install_dir, ) original_manifestin_data_files = [] if kw.get("include_package_data", get_default_include_package_data()): original_manifestin_data_files = parse_manifestin(os.path.join(os.getcwd(), "MANIFEST.in")) for path in original_manifestin_data_files: _classify_file( path, package_data, package_prefixes, py_modules, new_py_modules, scripts, new_scripts, data_files ) if developer_mode: # Copy packages for package, package_file_list in package_data.items(): for package_file in package_file_list: package_path = os.path.join(package_dir[package], package_file) cmake_file = os.path.join(CMAKE_INSTALL_DIR(), package_path) if os.path.exists(cmake_file): _copy_file(cmake_file, package_path, hide_listing) # Copy modules for py_module in py_modules: package_file = py_module + ".py" cmake_file = os.path.join(CMAKE_INSTALL_DIR(), package_file) if os.path.exists(cmake_file): _copy_file(cmake_file, package_file, hide_listing) else: _consolidate_package_modules(cmake_source_dir, packages, package_dir, py_modules, package_data, hide_listing) original_package_data = kw.get("package_data", {}).copy() _consolidate_package_data_files(original_package_data, package_prefixes, hide_listing) for data_file in original_manifestin_data_files: dest_data_file = os.path.join(CMAKE_INSTALL_DIR(), data_file) _copy_file(data_file, dest_data_file, hide_listing) kw["package_data"] = package_data kw["package_dir"] = { package: ( os.path.join(CMAKE_INSTALL_DIR(), prefix) if os.path.exists(os.path.join(CMAKE_INSTALL_DIR(), prefix)) else prefix ) for prefix, package in package_prefixes } kw["scripts"] = [ os.path.join(CMAKE_INSTALL_DIR(), script) if mask else script for script, mask in new_scripts.items() ] kw["data_files"] = [(parent_dir, list(file_set)) for parent_dir, file_set in data_files.items()] if "zip_safe" not in kw: kw["zip_safe"] = False # Adapted from espdev/ITKPythonInstaller/setup.py.in class BinaryDistribution(upstream_Distribution): # pylint: disable=missing-class-docstring def has_ext_modules(self) -> bool: # pylint: disable=missing-function-docstring return has_cmakelists kw["distclass"] = BinaryDistribution print(flush=True) return setuptools.setup(**kw) def _collect_package_prefixes(package_dir: dict[str, str], packages: list[Any | str]) -> list[Any | tuple[str, str]]: """ Collect the list of prefixes for all packages The list is used to match paths in the install manifest to packages specified in the setup.py script. The list is sorted in decreasing order of prefix length so that paths are matched with their immediate parent package, instead of any of that package's ancestors. For example, consider the project structure below. Assume that the setup call was made with a package list featuring "top" and "top.bar", but not "top.not_a_subpackage". :: top/ -> top/ __init__.py -> top/__init__.py (parent: top) foo.py -> top/foo.py (parent: top) bar/ -> top/bar/ (parent: top) __init__.py -> top/bar/__init__.py (parent: top.bar) not_a_subpackage/ -> top/not_a_subpackage/ (parent: top) data_0.txt -> top/not_a_subpackage/data_0.txt (parent: top) data_1.txt -> top/not_a_subpackage/data_1.txt (parent: top) The paths in the generated install manifest are matched to packages according to the parents indicated on the right. Only packages that are specified in the setup() call are considered. Because of the sort order, the data files on the bottom would have been mapped to "top.not_a_subpackage" instead of "top", proper -- had such a package been specified. """ return sorted( ((package_dir[package].replace(".", "/"), package) for package in packages), key=lambda tup: len(tup[0]), reverse=True, ) def _classify_installed_files( install_paths: Sequence[str], package_data: dict[str, list[str]], package_prefixes: Sequence[tuple[str, str]], py_modules: Sequence[str], new_py_modules: dict[str, bool], scripts: Sequence[str], new_scripts: dict[str, bool], data_files: dict[Any, Any], cmake_source_dir: str, _cmake_install_dir: str, ) -> None: assert not os.path.isabs(cmake_source_dir) assert cmake_source_dir != "." install_root = os.path.join(os.getcwd(), CMAKE_INSTALL_DIR()) for path in install_paths: # if this installed file is not within the project root, complain and # exit if not to_platform_path(path).startswith(CMAKE_INSTALL_DIR()): msg = ( "\n CMake-installed files must be within the project root.\n" f" Project Root : {install_root}\n" f" Violating File: {to_platform_path(path)}\n" ) raise SKBuildInvalidFileInstallationError(msg) # peel off the 'skbuild' prefix unix_path = to_unix_path(os.path.relpath(path, CMAKE_INSTALL_DIR())) _classify_file( unix_path, package_data, package_prefixes, py_modules, new_py_modules, scripts, new_scripts, data_files ) def _classify_file( path: str, package_data: dict[str, list[str]], package_prefixes: Sequence[tuple[str, str]], py_modules: Sequence[str], new_py_modules: dict[str, bool], scripts: Sequence[str], new_scripts: dict[str, bool], data_files: dict[str, set[str]], ) -> None: found_package = False found_module = False found_script = False path = to_unix_path(path) # check to see if path is part of a package for prefix, package in package_prefixes: if path.startswith(prefix + "/"): # peel off the package prefix path = to_unix_path(os.path.relpath(path, prefix)) package_file_list = list(package_data.get(package, [])) package_file_list.append(path) package_data[package] = package_file_list found_package = True break if found_package: return # If control reaches this point, then this installed file is not part of # a package. # check if path is a module for module in py_modules: if path.replace("/", ".") == ".".join((module, "py")): new_py_modules[module] = True found_module = True break if found_module: return # If control reaches this point, then this installed file is not a # module # if the file is a script, mark the corresponding script for script in scripts: if path == script: new_scripts[script] = True found_script = True break if found_script: return # If control reaches this point, then this installed file is not a # script # If control reaches this point, then we have installed files that are # not part of a package, not a module, nor a script. Without any other # information, we can only treat it as a generic data file. parent_dir = os.path.dirname(path) file_set = data_files.get(parent_dir) if file_set is None: file_set = set() data_files[parent_dir] = file_set file_set.add(os.path.join(CMAKE_INSTALL_DIR(), path)) def _copy_file(src_file: str, dest_file: str, hide_listing: bool | int = True) -> None: """Copy ``src_file`` to ``dest_file`` ensuring parent directory exists. By default, message like `creating directory /path/to/package` and `copying directory /src/path/to/package -> path/to/package` are displayed on standard output. Setting ``hide_listing`` to False avoids message from being displayed. """ # Create directory if needed dest_dir = os.path.dirname(dest_file) if dest_dir and not os.path.exists(dest_dir): if not hide_listing: print(f"creating directory {dest_dir}", flush=True) mkdir_p(dest_dir) # Copy file if not hide_listing: print(f"copying {src_file} -> {dest_file}", flush=True) shutil.copyfile(src_file, dest_file) shutil.copymode(src_file, dest_file) def _consolidate_package_modules( cmake_source_dir: str, packages: list[Any | str], package_dir: dict[str, str], py_modules: list[Any | str], package_data: dict[str, list[str]], hide_listing: bool | int, ) -> None: """This function consolidates packages having modules located in both the source tree and the CMake install tree into one location. The one location is the CMake install tree (see :func:`.constants.CMAKE_INSTALL_DIR()`). Why ? This is a necessary evil because ``Setuptools`` keeps track of packages and modules files to install using a dictionary of lists where the key are package names (e.g ``foo.bar``) and the values are lists of module files (e.g ``['__init__.py', 'baz.py']``. Since this doesn't allow to "split" files associated with a given module in multiple location, one location is selected, and files are copied over. How? It currently searches for modules across both locations using the :class:`.utils.PythonModuleFinder`. then with the help of :func:`_package_data_contain_module`, it identifies which one are either already included or missing from the distribution. Once a module has been identified as ``missing``, it is both copied into the :func:`.constants.CMAKE_INSTALL_DIR()` and added to the ``package_data`` dictionary so that it can be considered by the upstream setup function. """ try: # Search for python modules in both the current directory # and cmake install tree. modules = PythonModuleFinder( packages, package_dir, py_modules, alternative_build_base=CMAKE_INSTALL_DIR() ).find_all_modules() except DistutilsError as err: msg = f"error: {err}" raise SystemExit(msg) from None print(flush=True) for entry in modules: # Check if module file should be copied into the CMake install tree. if _package_data_contain_module(entry, package_data): continue (package, _, src_module_file) = entry # Copy missing module file if os.path.exists(src_module_file): dest_module_file = os.path.join(CMAKE_INSTALL_DIR(), src_module_file) _copy_file(src_module_file, dest_module_file, hide_listing) # Since the mapping in package_data expects the package to be associated # with a list of files relative to the directory containing the package, # the following section makes sure to strip the redundant part of the # module file path. # The redundant part should be stripped for both cmake_source_dir and # the package. package_parts = [] if cmake_source_dir: package_parts = cmake_source_dir.split(os.path.sep) package_parts += package.split(".") stripped_module_file = strip_package(package_parts, src_module_file) # Update list of files associated with the corresponding package try: package_data[package].append(stripped_module_file) except KeyError: package_data[package] = [stripped_module_file] def _consolidate_package_data_files( original_package_data: dict[str, list[str]], package_prefixes: list[Any | tuple[str, str]], hide_listing: bool | int, ) -> None: """This function copies package data files specified using the ``package_data`` keyword into :func:`.constants.CMAKE_INSTALL_DIR()`. :: setup(..., packages=['mypkg'], package_dir={'mypkg': 'src/mypkg'}, package_data={'mypkg': ['data/*.dat']}, ) Considering that (1) the packages associated with modules located in both the source tree and the CMake install tree are consolidated into the CMake install tree, and (2) the consolidated package path set in the ``package_dir`` dictionary and later used by setuptools to package (or install) modules and data files is :func:`.constants.CMAKE_INSTALL_DIR()`, copying the data files is required to ensure setuptools can find them when it uses the package directory. """ project_root = os.getcwd() for prefix, package in package_prefixes: if package not in original_package_data: continue raw_patterns = original_package_data[package] for pattern in raw_patterns: expanded_package_dir = os.path.join(project_root, prefix, pattern) for src_data_file in glob.glob(expanded_package_dir): full_prefix_length = len(os.path.join(project_root, prefix)) + 1 data_file = src_data_file[full_prefix_length:] dest_data_file = os.path.join(CMAKE_INSTALL_DIR(), prefix, data_file) _copy_file(src_data_file, dest_data_file, hide_listing) scikit-build-0.18.1/skbuild/utils/000077500000000000000000000000001466366435500170035ustar00rootroot00000000000000scikit-build-0.18.1/skbuild/utils/__init__.py000066400000000000000000000225361466366435500211240ustar00rootroot00000000000000"""This module defines functions generally useful in scikit-build.""" from __future__ import annotations import contextlib import logging import os import typing from contextlib import contextmanager from typing import Any, Iterable, Iterator, Mapping, NamedTuple, Sequence, TypeVar from distutils.command.build_py import build_py as distutils_build_py from distutils.errors import DistutilsTemplateError from distutils.filelist import FileList from distutils.text_file import TextFile from .._compat.typing import Protocol if typing.TYPE_CHECKING: import setuptools._distutils.dist class CommonLog(Protocol): """Protocol for loggers with an info method.""" # pylint: disable-next=missing-function-docstring def info(self, __msg: str, *args: object) -> None: ... logger: CommonLog try: import setuptools.logging skb_log = logging.getLogger("skbuild") skb_log.setLevel(logging.INFO) logging_module = True logger = skb_log except ImportError: from distutils import log as distutils_log logger = distutils_log logging_module = False class Distribution(NamedTuple): """Distribution stand-in.""" script_name: str def _log_warning(msg: str, *args: object) -> None: try: if logging_module: skb_log.warning(msg, *args) else: distutils_log.warn(msg, *args) except ValueError: # Setuptools might disconnect the logger. That shouldn't be an error for a warning. print(msg % args, flush=True) def mkdir_p(path: str) -> None: """Ensure directory ``path`` exists. If needed, parent directories are created. """ return os.makedirs(path, exist_ok=True) Self = TypeVar("Self", bound="push_dir") class push_dir(contextlib.ContextDecorator): """Context manager to change current directory.""" def __init__(self, directory: str | None = None, make_directory: bool = False) -> None: """ :param directory: Path to set as current working directory. If ``None`` is passed, ``os.getcwd()`` is used instead. :param make_directory: If True, ``directory`` is created. """ super().__init__() self.directory = directory self.make_directory = make_directory self.old_cwd: str | None = None def __enter__(self: Self) -> Self: self.old_cwd = os.getcwd() if self.directory: if self.make_directory: os.makedirs(self.directory, exist_ok=True) os.chdir(self.directory) return self def __exit__(self, typ: None, val: None, traceback: None) -> None: assert self.old_cwd is not None os.chdir(self.old_cwd) class PythonModuleFinder(distutils_build_py): """Convenience class to search for python modules. This class is based on ``distutils.command.build_py.build_by`` and provides a specialized version of ``find_all_modules()``. """ distribution: Distribution # type: ignore[assignment] # pylint: disable-next=super-init-not-called def __init__( self, packages: Sequence[str], package_dir: Mapping[str, str], py_modules: Sequence[str], alternative_build_base: str | None = None, ) -> None: """ :param packages: List of packages to search. :param package_dir: Dictionary mapping ``package`` with ``directory``. :param py_modules: List of python modules. :param alternative_build_base: Additional directory to search in. """ self.packages = packages self.package_dir = package_dir self.py_modules = py_modules self.alternative_build_base = alternative_build_base self.distribution = Distribution("setup.py") def find_all_modules(self, project_dir: str | None = None) -> list[Any | tuple[str, str, str]]: """Compute the list of all modules that would be built by project located in current directory, whether they are specified one-module-at-a-time ``py_modules`` or by whole packages ``packages``. By default, the function will search for modules in the current directory. Specifying ``project_dir`` parameter allow to change this. Return a list of tuples ``(package, module, module_file)``. """ with push_dir(project_dir): # TODO: typestubs for distutils return super().find_all_modules() # type: ignore[no-any-return, no-untyped-call] def find_package_modules(self, package: str, package_dir: str) -> Iterable[tuple[str, str, str]]: """Temporally prepend the ``alternative_build_base`` to ``module_file``. Doing so will ensure modules can also be found in other location (e.g ``skbuild.constants.CMAKE_INSTALL_DIR``). """ if package_dir and not os.path.exists(package_dir) and self.alternative_build_base is not None: package_dir = os.path.join(self.alternative_build_base, package_dir) modules: Iterable[tuple[str, str, str]] = super().find_package_modules(package, package_dir) # type: ignore[no-untyped-call] # Strip the alternative base from module_file def _strip_directory(entry: tuple[str, str, str]) -> tuple[str, str, str]: module_file = entry[2] if self.alternative_build_base is not None and module_file.startswith(self.alternative_build_base): module_file = module_file[len(self.alternative_build_base) + 1 :] return entry[0], entry[1], module_file return map(_strip_directory, modules) def check_module(self, module: str, module_file: str) -> bool: """Return True if ``module_file`` belongs to ``module``.""" if self.alternative_build_base is not None: updated_module_file = os.path.join(self.alternative_build_base, module_file) if os.path.exists(updated_module_file): module_file = updated_module_file if not os.path.isfile(module_file): _log_warning("file %s (for module %s) not found", module_file, module) return False return True OptStr = TypeVar("OptStr", str, None) def to_platform_path(path: OptStr) -> OptStr: """Return a version of ``path`` where all separator are :attr:`os.sep`""" if path is None: return path return path.replace("/", os.sep).replace("\\", os.sep) def to_unix_path(path: OptStr) -> OptStr: """Return a version of ``path`` where all separator are ``/``""" if path is None: return path return path.replace("\\", "/") @contextmanager def distribution_hide_listing( distribution: setuptools._distutils.dist.Distribution | Distribution, ) -> Iterator[bool | int]: """Given a ``distribution``, this context manager temporarily sets distutils threshold to WARN if ``--hide-listing`` argument was provided. It yields True if ``--hide-listing`` argument was provided. """ hide_listing = getattr(distribution, "hide_listing", False) wheel_log = logging.getLogger("wheel") root_log = logging.getLogger() # setuptools 65.6+ needs this hidden too if logging_module: # Setuptools 60.2+, will always be on Python 3.7+ old_wheel_level = wheel_log.getEffectiveLevel() old_root_level = root_log.getEffectiveLevel() try: if hide_listing: wheel_log.setLevel(logging.WARNING) root_log.setLevel(logging.WARNING) # The classic logger doesn't respond to set_threshold anymore, # but it does log info and above to stdout, so let's hide that with open(os.devnull, "w", encoding="utf-8") as f, contextlib.redirect_stdout(f): yield hide_listing else: yield hide_listing finally: if hide_listing: wheel_log.setLevel(old_wheel_level) root_log.setLevel(old_root_level) else: old_threshold = distutils_log._global_log.threshold # type: ignore[attr-defined] if hide_listing: distutils_log.set_threshold(distutils_log.WARN) try: yield hide_listing finally: distutils_log.set_threshold(old_threshold) def parse_manifestin(template: str) -> list[str]: """This function parses template file (usually MANIFEST.in)""" if not os.path.exists(template): return [] template_file = TextFile( template, strip_comments=True, skip_blanks=True, join_lines=True, lstrip_ws=True, rstrip_ws=True, collapse_join=True, ) file_list = FileList() try: while True: line = template_file.readline() if line is None: # end of file break try: file_list.process_template_line(line) # the call above can raise a DistutilsTemplateError for # malformed lines, or a ValueError from the lower-level # convert_path function except (DistutilsTemplateError, ValueError) as msg: filename = template_file.filename if hasattr(template_file, "filename") else "Unknown" current_line = template_file.current_line if hasattr(template_file, "current_line") else "Unknown" print(f"{filename}, line {current_line}: {msg}", flush=True) return file_list.files finally: template_file.close() scikit-build-0.18.1/tests/000077500000000000000000000000001466366435500153505ustar00rootroot00000000000000scikit-build-0.18.1/tests/__init__.py000066400000000000000000000240051466366435500174620ustar00rootroot00000000000000from __future__ import annotations import setuptools # noqa: F401 try: import distutils.dir_util import distutils.sysconfig except ImportError: import distutils # Python < 3.10 import functools import os import os.path import pathlib import re import subprocess import sys from contextlib import contextmanager from unittest.mock import patch import _pytest.tmpdir import py.path import requests from skbuild.platform_specifics import get_platform from skbuild.utils import push_dir SAMPLES_DIR = os.path.join( os.path.dirname(os.path.realpath(__file__)), "samples", ) __all__ = [ "SAMPLES_DIR", "execute_setup_py", "get_cmakecache_variables", "initialize_git_repo_and_commit", "list_ancestors", "prepare_project", "project_setup_py_test", "push_dir", "push_env", ] @contextmanager def push_argv(argv): old_argv = sys.argv sys.argv = argv yield sys.argv = old_argv @contextmanager def push_env(**kwargs): """This context manager allow to set/unset environment variables.""" saved_env = dict(os.environ) for var, value in kwargs.items(): if value is not None: os.environ[var] = value elif var in os.environ: del os.environ[var] yield os.environ.clear() for saved_var, saved_value in saved_env.items(): os.environ[saved_var] = saved_value @contextmanager def prepend_sys_path(paths): """This context manager allows to prepend paths to ``sys.path`` and restore the original list. """ saved_paths = list(sys.path) sys.path = paths + saved_paths yield sys.path = saved_paths def _tmpdir(basename: str) -> py.path.local: """This function returns a temporary directory similar to the one returned by the ``tmpdir`` pytest fixture. The difference is that the `basetemp` is not configurable using the pytest settings.""" # Adapted from _pytest.tmpdir.tmpdir() basename = re.sub(r"[\W]", "_", basename) max_val = 30 if len(basename) > max_val: basename = basename[:max_val] # Adapted from _pytest.tmpdir.TempdirFactory.getbasetemp() try: basetemp = _tmpdir._basetemp # type: ignore[attr-defined] except AttributeError: temproot = py.path.local.get_temproot() user = _pytest.tmpdir.get_user() # use a sub-directory in the temproot to speed-up # make_numbered_dir() call rootdir = temproot.join(f"pytest-of-{user}") if user else temproot rootdir.ensure(dir=1) basetemp = py.path.local.make_numbered_dir(prefix="pytest-", rootdir=rootdir) # Adapted from _pytest.tmpdir.TempdirFactory.mktemp return py.path.local.make_numbered_dir(prefix=basename, keep=0, rootdir=basetemp, lock_timeout=None) def _copy(src, target): """ Copies a single entry (file, dir) named 'src' to 'target'. Softlinks are processed properly as well. Copied from pytest-datafiles/pytest_datafiles.py (MIT License) """ if not src.exists(): msg = f"'{src}' does not exist!" raise ValueError(msg) if src.isdir(): src.copy(target / src.basename) elif src.islink(): (target / src.basename).mksymlinkto(src.realpath()) else: # file src.copy(target) def _copy_dir(target_dir, src_dir, on_duplicate="exception", keep_top_dir=False): """ Copies all entries (files, dirs) from 'src_dir' to 'target_dir' taking into account the 'on_duplicate' option (which defines what should happen if an entry already exists: raise an exception, overwrite it or ignore it). Adapted from pytest-datafiles/pytest_datafiles.py (MIT License) """ src_files = [] if isinstance(src_dir, str): src_dir = py.path.local(src_dir) if keep_top_dir: src_files = src_dir elif src_dir.isdir(): src_files.extend(src_dir.listdir()) else: src_files.append(src_dir) for entry in src_files: target_entry = target_dir / entry.basename if not target_entry.exists() or on_duplicate == "overwrite": _copy(entry, target_dir) elif on_duplicate == "exception": msg = f"'{target_entry}' already exists (src {entry})" raise ValueError(msg) def initialize_git_repo_and_commit(project_dir, verbose=True): """Convenience function creating a git repository in ``project_dir``. If ``project_dir`` does NOT contain a ``.git`` directory, a new git repository with one commit containing all the directories and files is created. """ if isinstance(project_dir, str): project_dir = py.path.local(project_dir) if project_dir.join(".git").exists(): return # If any, exclude virtualenv files project_dir.join(".gitignore").write(".env") with push_dir(str(project_dir)): for cmd in [ ["git", "init"], ["git", "config", "user.name", "scikit-build"], ["git", "config", "user.email", "test@test"], ["git", "config", "commit.gpgsign", "false"], ["git", "add", "-A"], ["git", "reset", ".gitignore"], ["git", "commit", "-m", "Initial commit"], ]: subprocess.run(cmd, stdout=None if verbose else subprocess.PIPE, check=True) def prepare_project(project, tmp_project_dir, force=False): """Convenience function setting up the build directory ``tmp_project_dir`` for the selected sample ``project``. If ``tmp_project_dir`` does not exist, it is created. If ``tmp_project_dir`` is empty, the sample ``project`` is copied into it. Specifying ``force=True`` will copy the files even if ``tmp_project_dir`` is not empty. """ if isinstance(tmp_project_dir, str): tmp_project_dir = py.path.local(tmp_project_dir) # Create project directory if it does not exist if not tmp_project_dir.exists(): tmp_project_dir = _tmpdir(project) # If empty or if force is True, copy project files and initialize git if not tmp_project_dir.listdir() or force: _copy_dir(tmp_project_dir, os.path.join(SAMPLES_DIR, project)) @contextmanager def execute_setup_py(project_dir, setup_args, disable_languages_test=False): """Context manager executing ``setup.py`` with the given arguments. It yields after changing the current working directory to ``project_dir``. """ # See https://stackoverflow.com/questions/9160227/dir-util-copy-tree-fails-after-shutil-rmtree distutils.dir_util._path_created.clear() # type: ignore[attr-defined] # Clear _PYTHON_HOST_PLATFORM to ensure value sets in skbuild.setuptools_wrap.setup() does not # influence other tests. if "_PYTHON_HOST_PLATFORM" in os.environ: del os.environ["_PYTHON_HOST_PLATFORM"] with push_dir(str(project_dir)), push_argv(["setup.py", *setup_args]), prepend_sys_path([str(project_dir)]): with open("setup.py") as fp: setup_code = compile(fp.read(), "setup.py", mode="exec") if setup_code is not None: if disable_languages_test: platform = get_platform() original_write_test_cmakelist = platform.write_test_cmakelist def write_test_cmakelist_no_languages(_self, _languages): original_write_test_cmakelist([]) with patch.object(type(platform), "write_test_cmakelist", new=write_test_cmakelist_no_languages): exec(setup_code) else: exec(setup_code) yield def project_setup_py_test(project, setup_args, tmp_dir=None, verbose_git=True, disable_languages_test=False, ret=False): def dec(fun): @functools.wraps(fun) def wrapped(*iargs, **ikwargs): if wrapped.tmp_dir is None: # type: ignore[attr-defined] wrapped.tmp_dir = _tmpdir(fun.__name__) # type: ignore[attr-defined] prepare_project(wrapped.project, wrapped.tmp_dir) # type: ignore[attr-defined] initialize_git_repo_and_commit(wrapped.tmp_dir, verbose=wrapped.verbose_git) # type: ignore[attr-defined] with execute_setup_py(wrapped.tmp_dir, wrapped.setup_args, disable_languages_test=disable_languages_test): # type: ignore[attr-defined] result2 = fun(*iargs, **ikwargs) if ret: return wrapped.tmp_dir, result2 # type: ignore[attr-defined] return None wrapped.project = project # type: ignore[attr-defined] wrapped.setup_args = setup_args # type: ignore[attr-defined] wrapped.tmp_dir = tmp_dir # type: ignore[attr-defined] wrapped.verbose_git = verbose_git # type: ignore[attr-defined] return wrapped return dec def get_cmakecache_variables(cmakecache): """Returns a dictionary of all variables found in given CMakeCache.txt. Dictionary entries are tuple of the form ``(variable_type, variable_value)``. Possible `variable_type` are documented `here `_. """ results = {} cache_entry_pattern = re.compile(r"^([\w\d_-]+):([\w]+)=") with open(cmakecache) as content: for full_line in content.readlines(): line = full_line.strip() result = cache_entry_pattern.match(line) if result: variable_name = result.group(1) variable_type = result.group(2) variable_value = line.split("=")[1] results[variable_name] = (variable_type, variable_value) return results def is_site_reachable(url): """Return True if the given website can be accessed""" try: request = requests.get(url) return request.status_code == 200 except requests.exceptions.ConnectionError: return False def list_ancestors(path): """Return logical ancestors of the path.""" return [str(parent) for parent in pathlib.PurePosixPath(path).parents if str(parent) != "."] def get_ext_suffix(): """Return python extension suffix.""" return distutils.sysconfig.get_config_var("EXT_SUFFIX") scikit-build-0.18.1/tests/conftest.py000066400000000000000000000142231466366435500175510ustar00rootroot00000000000000from __future__ import annotations import importlib.util import os import shutil import subprocess import sys from collections.abc import Generator from pathlib import Path import pytest import virtualenv as _virtualenv if sys.version_info < (3, 8): import importlib_metadata as metadata from typing_extensions import Literal, overload else: from importlib import metadata from typing import Literal, overload HAS_SETUPTOOLS_SCM = importlib.util.find_spec("setuptools_scm") is not None DIR = Path(__file__).parent.resolve() BASE = DIR.parent pytest.register_assert_rewrite("tests.pytest_helpers") @pytest.fixture(scope="session") def pep518_wheelhouse(tmp_path_factory: pytest.TempPathFactory) -> Path: numpy = ["numpy"] if sys.version_info < (3, 13) else [] wheelhouse = tmp_path_factory.mktemp("wheelhouse") subprocess.run( [ sys.executable, "-m", "pip", "wheel", "--wheel-dir", str(wheelhouse), f"{BASE}", ], check=True, ) # Hatch-* packages only required for test_distribution packages = [ "build", "setuptools", "virtualenv", "wheel", "ninja", "cmake", "hatch-fancy-pypi-readme", "hatch-vcs", "hatchling", ] subprocess.run( [ sys.executable, "-m", "pip", "download", "-q", "-d", str(wheelhouse), *numpy, *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.dest = env_dir.resolve() @overload def run(self, *args: str | os.PathLike[str], capture: Literal[True], cwd: Path | None = ...) -> str: ... @overload def run(self, *args: str | os.PathLike[str], capture: Literal[False] = ..., cwd: Path | None = ...) -> None: ... def run(self, *args: str | os.PathLike[str], capture: bool = False, cwd: Path | None = None) -> str | None: __tracebackhide__ = True env = os.environ.copy() env["PATH"] = f"{self.executable.parent}{os.pathsep}{env['PATH']}" env["VIRTUAL_ENV"] = str(self.dest) 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, cwd=cwd, ) 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, cwd=cwd, ) 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, cwd: Path | None = None) -> str: return self.run(str(self.executable), "-c", command, capture=True, cwd=cwd) @overload def module(self, *args: str | os.PathLike[str], capture: Literal[False] = ..., cwd: Path | None = ...) -> None: ... @overload def module(self, *args: str | os.PathLike[str], capture: Literal[True], cwd: Path | None = ...) -> str: ... def module(self, *args: str | os.PathLike[str], capture: bool = False, cwd: Path | None = None) -> None | str: return self.run(str(self.executable), "-m", *args, capture=capture, cwd=cwd) # type: ignore[no-any-return,call-overload] def install(self, *args: str | os.PathLike[str]) -> None: self.module("pip", "install", *args) @pytest.fixture() def pep518(pep518_wheelhouse, monkeypatch): monkeypatch.setenv("PIP_FIND_LINKS", str(pep518_wheelhouse)) monkeypatch.setenv("PIP_NO_INDEX", "true") return pep518_wheelhouse @pytest.fixture() def isolated(tmp_path: Path, pep518_wheelhouse: Path) -> Generator[VEnv, None, None]: path = tmp_path / "venv" try: yield VEnv(path, wheelhouse=pep518_wheelhouse) finally: shutil.rmtree(path, ignore_errors=True) def _get_program(name: str) -> str: res = shutil.which(name) if res is None: return f"No {name} executable found on PATH" result = subprocess.run([res, "--version"], check=True, text=True, capture_output=True) version = result.stdout.splitlines()[0] return f"{res}: {version}" def pytest_report_header() -> str: interesting_packages = { "build", "cmake", "distro", "ninja", "packaging", "pip", "scikit-build", "setuptools", "setuptools_scm", "virtualenv", "wheel", } valid = [] for package in interesting_packages: try: version = metadata.version(package) except ModuleNotFoundError: continue valid.append(f"{package}=={version}") reqs = " ".join(sorted(valid)) pkg_line = f"installed packages of interest: {reqs}" prog_lines = [_get_program(n) for n in ("cmake3", "cmake", "ninja")] return "\n".join([pkg_line, *prog_lines]) def pytest_runtest_setup(item: pytest.Item) -> None: if HAS_SETUPTOOLS_SCM and tuple(item.iter_markers(name="nosetuptoolsscm")): pytest.exit("Setuptools_scm installed and nosetuptoolsscm tests not skipped.") scikit-build-0.18.1/tests/pytest_helpers.py000066400000000000000000000101271466366435500207750ustar00rootroot00000000000000from __future__ import annotations import sys import tarfile from zipfile import ZipFile from packaging.version import Version from skbuild import __version__ as skbuild_version from . import list_ancestors if sys.version_info < (3, 8): import importlib_metadata as metadata else: from importlib import metadata def check_sdist_content(sdist_archive, expected_distribution_name, expected_content, package_dir=""): """This function raises an AssertionError if the given sdist_archive does not have the expected content. """ sdist_zip = sdist_archive.endswith(".zip") sdist_tar = sdist_archive.endswith(".tar.gz") assert sdist_zip or sdist_tar expected_content = set(expected_content) expected_name = "_".join(expected_distribution_name.split("-")[:-1]) if not package_dir: egg_info_dir = f"{expected_distribution_name}/{expected_name}.egg-info" else: egg_info_dir = f"{expected_distribution_name}/{package_dir}/{expected_name}.egg-info" expected_content |= { f"{expected_distribution_name}/PKG-INFO", f"{expected_distribution_name}/setup.cfg", f"{egg_info_dir}/dependency_links.txt", f"{egg_info_dir}/top_level.txt", f"{egg_info_dir}/PKG-INFO", f"{egg_info_dir}/SOURCES.txt", } if sdist_zip and sys.version_info > (3, 7, 1): # Add directory entries in ZIP files created by distutils. # See https://github.com/python/cpython/pull/9419 for entry in expected_content: expected_content |= {ent + "/" for ent in list_ancestors(entry)} if sdist_zip: with ZipFile(sdist_archive) as zp: member_list = set(zp.namelist()) else: with tarfile.open(sdist_archive) as tf: member_list = {member.name for member in tf.getmembers() if not member.isdir()} assert member_list == expected_content def check_wheel_content(wheel_archive, expected_distribution_name, expected_content, pure=False): """This function raises an AssertionError if the given wheel_archive does not have the expected content. Note that this function already takes care of appending the ``.dist-info`` files to the ``expected_content`` list. """ expected_content = set(expected_content) expected_content |= { f"{expected_distribution_name}.dist-info/top_level.txt", f"{expected_distribution_name}.dist-info/WHEEL", f"{expected_distribution_name}.dist-info/RECORD", f"{expected_distribution_name}.dist-info/METADATA", } wheel_version = Version(metadata.version("wheel")) if wheel_version < Version("0.31.0"): # These files were specified in the now-withdrawn PEP 426 # See https://github.com/pypa/wheel/issues/195 expected_content |= { f"{expected_distribution_name}.dist-info/DESCRIPTION.rst", f"{expected_distribution_name}.dist-info/metadata.json", } if Version("0.33.1") < wheel_version < Version("0.33.4"): # Include directory entries when building wheel # See https://github.com/pypa/wheel/issues/287 and https://github.com/pypa/wheel/issues/294 for entry in expected_content: expected_content |= {entry + "/" for entry in list_ancestors(entry)} if pure: assert wheel_archive.endswith("-none-any.whl") else: assert not wheel_archive.endswith("-none-any.whl") with ZipFile(wheel_archive) as archive: member_list = set(archive.namelist()) assert member_list == expected_content # PEP-0427: Generator is the name and optionally the version of the # software that produced the archive. # See https://www.python.org/dev/peps/pep-0427/#file-contents current_generator = None with archive.open(f"{expected_distribution_name}.dist-info/WHEEL") as wheel_file: for line in wheel_file: if line.startswith(b"Generator"): current_generator = line.split(b":")[1].strip() break assert current_generator == f"skbuild {skbuild_version}".encode() scikit-build-0.18.1/tests/samples/000077500000000000000000000000001466366435500170145ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/cmakelists-not-in-top-level-dir/000077500000000000000000000000001466366435500250365ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/cmakelists-not-in-top-level-dir/hello/000077500000000000000000000000001466366435500261415ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/cmakelists-not-in-top-level-dir/hello/CMakeLists.txt000066400000000000000000000003311466366435500306760ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(hello) find_package(PythonExtensions REQUIRED) add_library(_hello MODULE _hello.cxx) python_extension_module(_hello) install(TARGETS _hello LIBRARY DESTINATION hello) scikit-build-0.18.1/tests/samples/cmakelists-not-in-top-level-dir/hello/__init__.py000066400000000000000000000000001466366435500302400ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/cmakelists-not-in-top-level-dir/hello/__main__.py000066400000000000000000000001731466366435500302340ustar00rootroot00000000000000from __future__ import annotations if __name__ == "__main__": from . import _hello as hello hello.hello("World") scikit-build-0.18.1/tests/samples/cmakelists-not-in-top-level-dir/hello/_hello.cxx000066400000000000000000000030211466366435500301230ustar00rootroot00000000000000 // Python includes #include // STD includes #include //----------------------------------------------------------------------------- static PyObject *hello_example(PyObject *self, PyObject *args) { // Unpack a string from the arguments const char *strArg; if (!PyArg_ParseTuple(args, "s", &strArg)) return NULL; // Print message and return None PySys_WriteStdout("Hello, %s! :)\n", strArg); Py_RETURN_NONE; } //----------------------------------------------------------------------------- static PyObject *elevation_example(PyObject *self, PyObject *args) { // Return an integer return PyLong_FromLong(21463L); } //----------------------------------------------------------------------------- static PyMethodDef hello_methods[] = { { "hello", hello_example, METH_VARARGS, "Prints back 'Hello ', for example example: hello.hello('you')" }, { "size", elevation_example, METH_VARARGS, "Returns elevation of Nevado Sajama." }, {NULL, NULL, 0, NULL} /* Sentinel */ }; //----------------------------------------------------------------------------- #if PY_MAJOR_VERSION < 3 PyMODINIT_FUNC init_hello(void) { (void) Py_InitModule("_hello", hello_methods); } #else /* PY_MAJOR_VERSION >= 3 */ static struct PyModuleDef hello_module_def = { PyModuleDef_HEAD_INIT, "_hello", "Internal \"_hello\" module", -1, hello_methods }; PyMODINIT_FUNC PyInit__hello(void) { return PyModule_Create(&hello_module_def); } #endif /* PY_MAJOR_VERSION >= 3 */ scikit-build-0.18.1/tests/samples/cmakelists-not-in-top-level-dir/setup.py000066400000000000000000000004531466366435500265520ustar00rootroot00000000000000from __future__ import annotations from skbuild import setup setup( name="hello", version="1.2.3", description="a minimal example package (CMakeLists not in top-level dir)", author="The scikit-build team", license="MIT", packages=["hello"], cmake_source_dir="hello", ) scikit-build-0.18.1/tests/samples/cython-flags/000077500000000000000000000000001466366435500214125ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/cython-flags/CMakeLists.txt000066400000000000000000000002351466366435500241520ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(cython_flags) find_package(PythonExtensions REQUIRED) find_package(Cython REQUIRED) add_subdirectory(hello) scikit-build-0.18.1/tests/samples/cython-flags/hello/000077500000000000000000000000001466366435500225155ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/cython-flags/hello/CMakeLists.txt000066400000000000000000000003101466366435500252470ustar00rootroot00000000000000 set(CYTHON_FLAGS "-X c_string_encoding=default") add_cython_target(_hello CXX) add_library(_hello MODULE ${_hello}) python_extension_module(_hello) install(TARGETS _hello LIBRARY DESTINATION hello) scikit-build-0.18.1/tests/samples/cython-flags/hello/__init__.py000066400000000000000000000000001466366435500246140ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/cython-flags/hello/__main__.py000066400000000000000000000001731466366435500246100ustar00rootroot00000000000000from __future__ import annotations if __name__ == "__main__": from . import _hello as hello hello.hello("World") scikit-build-0.18.1/tests/samples/cython-flags/hello/_hello.pyx000066400000000000000000000002671466366435500245260ustar00rootroot00000000000000from libc.stdio cimport printf cpdef void hello(str strArg): "Prints back 'Hello ', for example example: hello.hello('you')" printf("Hello, %s! :)\n", strArg) scikit-build-0.18.1/tests/samples/cython-flags/setup.py000066400000000000000000000004651466366435500231310ustar00rootroot00000000000000from __future__ import annotations from skbuild import setup setup( name="cython-flags", version="1.2.3", description="a minimal example package (cython version)", author="The scikit-build team", license="MIT", packages=["cython_flags"], package_dir={"cython_flags": "hello"}, ) scikit-build-0.18.1/tests/samples/fail-hello-with-compile-error/000077500000000000000000000000001466366435500245565ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/fail-hello-with-compile-error/CMakeLists.txt000066400000000000000000000001701466366435500273140ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(hello) find_package(PythonExtensions REQUIRED) add_subdirectory(hello) scikit-build-0.18.1/tests/samples/fail-hello-with-compile-error/hello/000077500000000000000000000000001466366435500256615ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/fail-hello-with-compile-error/hello/CMakeLists.txt000066400000000000000000000001711466366435500304200ustar00rootroot00000000000000add_library(_hello MODULE _hello.cxx) python_extension_module(_hello) install(TARGETS _hello LIBRARY DESTINATION hello) scikit-build-0.18.1/tests/samples/fail-hello-with-compile-error/hello/__init__.py000066400000000000000000000000001466366435500277600ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/fail-hello-with-compile-error/hello/__main__.py000066400000000000000000000001731466366435500277540ustar00rootroot00000000000000from __future__ import annotations if __name__ == "__main__": from . import _hello as hello hello.hello("World") scikit-build-0.18.1/tests/samples/fail-hello-with-compile-error/hello/_hello.cxx000066400000000000000000000031151466366435500276470ustar00rootroot00000000000000 // Python includes #include // STD includes #include //----------------------------------------------------------------------------- static PyObject *hello_example(PyObject *self, PyObject *args) { // Unpack a string from the arguments const char *strArg; if (!PyArg_ParseTuple(args, "s", &strArg)) return NULL // Purposely removed the semicolon to cause a compiler error // Print message and return None PySys_WriteStdout("Hello, %s! :)\n", strArg); Py_RETURN_NONE; } //----------------------------------------------------------------------------- static PyObject *elevation_example(PyObject *self, PyObject *args) { // Return an integer return PyLong_FromLong(21463L); } //----------------------------------------------------------------------------- static PyMethodDef hello_methods[] = { { "hello", hello_example, METH_VARARGS, "Prints back 'Hello ', for example example: hello.hello('you')" }, { "size", elevation_example, METH_VARARGS, "Returns elevation of Nevado Sajama." }, {NULL, NULL, 0, NULL} /* Sentinel */ }; //----------------------------------------------------------------------------- #if PY_MAJOR_VERSION < 3 PyMODINIT_FUNC init_hello(void) { (void) Py_InitModule("_hello", hello_methods); } #else /* PY_MAJOR_VERSION >= 3 */ static struct PyModuleDef hello_module_def = { PyModuleDef_HEAD_INIT, "_hello", "Internal \"_hello\" module", -1, hello_methods }; PyMODINIT_FUNC PyInit__hello(void) { return PyModule_Create(&hello_module_def); } #endif /* PY_MAJOR_VERSION >= 3 */ scikit-build-0.18.1/tests/samples/fail-hello-with-compile-error/setup.py000066400000000000000000000005361466366435500262740ustar00rootroot00000000000000from __future__ import annotations from skbuild import setup setup( name="hello", version="1.2.3", description=( "a minimal example packagze that should always fail to build " "because it provides a _hello.cxx with a compile error" ), author="The scikit-build team", license="MIT", packages=["hello"], ) scikit-build-0.18.1/tests/samples/fail-outside-project-root/000077500000000000000000000000001466366435500240265ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/fail-outside-project-root/CMakeLists.txt000066400000000000000000000015001466366435500265620ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(fail_outside_project_root NONE) install(CODE "execute_process(COMMAND \${CMAKE_COMMAND} -E sleep 0)") if(INSTALL_FILE) install(FILES dummy DESTINATION ..) endif() if(INSTALL_PROJECT) set(other_project_source_dir ${CMAKE_SOURCE_DIR}/other_project) set(other_project_build_dir ${CMAKE_BINARY_DIR}/../other_project-build) set(other_project_install_dir ${CMAKE_BINARY_DIR}/../other_project-install) file(MAKE_DIRECTORY ${other_project_build_dir}) # Configure execute_process( COMMAND ${CMAKE_COMMAND} ${other_project_source_dir} WORKING_DIRECTORY ${other_project_build_dir} ) # Install other_project install(CODE " set(CMAKE_INSTALL_PREFIX \"${other_project_install_dir}\") include(\"${other_project_build_dir}/cmake_install.cmake\") ") endif() scikit-build-0.18.1/tests/samples/fail-outside-project-root/dummy000066400000000000000000000000001466366435500250720ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/fail-outside-project-root/other_project/000077500000000000000000000000001466366435500266755ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/fail-outside-project-root/other_project/CMakeLists.txt000066400000000000000000000001761466366435500314410ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(other_project NONE) install(FILES ${CMAKE_SOURCE_DIR}/../dummy DESTINATION .) scikit-build-0.18.1/tests/samples/fail-outside-project-root/setup.py000066400000000000000000000005261466366435500255430ustar00rootroot00000000000000from __future__ import annotations from skbuild import setup setup( name="fail_outside_project_root", version="0.0.1", description=( "test project that should always fail to build because it " "tries to CMake-install something outside of its root" ), author="The scikit-build team", license="MIT", ) scikit-build-0.18.1/tests/samples/fail-unless-skbuild-set/000077500000000000000000000000001466366435500234625ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/fail-unless-skbuild-set/CMakeLists.txt000066400000000000000000000003421466366435500262210ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(fail_unless_skbuild_set NONE) if(NOT DEFINED SKBUILD) message(FATAL_ERROR "SKBUILD is not set!") endif() install(CODE "execute_process(COMMAND \${CMAKE_COMMAND} -E sleep 0)") scikit-build-0.18.1/tests/samples/fail-unless-skbuild-set/setup.py000066400000000000000000000004261466366435500251760ustar00rootroot00000000000000from __future__ import annotations from skbuild import setup setup( name="fail_unless_skbuild_set", version="0.0.1", description=('test project that should fail unless the CMake variable "SKBUILD" is set'), author="The scikit-build team", license="MIT", ) scikit-build-0.18.1/tests/samples/fail-with-fatal-error-cmakelists/000077500000000000000000000000001466366435500252515ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/fail-with-fatal-error-cmakelists/CMakeLists.txt000066400000000000000000000003141466366435500300070ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(fail_with_fatal_error_cmakelists NONE) install(CODE "execute_process(COMMAND \${CMAKE_COMMAND} -E sleep 0)") message(FATAL_ERROR "Invalid CMakeLists.txt") scikit-build-0.18.1/tests/samples/fail-with-fatal-error-cmakelists/setup.py000066400000000000000000000005171466366435500267660ustar00rootroot00000000000000from __future__ import annotations from skbuild import setup setup( name="fail_with_fatal_error_cmakelists", version="0.0.1", description=( "test project that should always fail to build because it provides a CMakeLists.txt reporting a FATAL_ERROR" ), author="The scikit-build team", license="MIT", ) scikit-build-0.18.1/tests/samples/fail-with-syntax-error-cmakelists/000077500000000000000000000000001466366435500255105ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/fail-with-syntax-error-cmakelists/CMakeLists.txt000066400000000000000000000003431466366435500302500ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(fail_with_syntax_error_cmakelists NONE) install(CODE "execute_process(COMMAND \${CMAKE_COMMAND} -E sleep 0)") message(STATUS "This is incorrect # Parenthesis omitted on purpose scikit-build-0.18.1/tests/samples/fail-with-syntax-error-cmakelists/setup.py000066400000000000000000000005141466366435500272220ustar00rootroot00000000000000from __future__ import annotations from skbuild import setup setup( name="fail_with_syntax_error_cmakelists", version="0.0.1", description=( "test project that should always fail to build because it provides a CMakeLists.txt with a syntax error" ), author="The scikit-build team", license="MIT", ) scikit-build-0.18.1/tests/samples/hello-cpp/000077500000000000000000000000001466366435500206775ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/hello-cpp/CMakeLists.txt000066400000000000000000000010471466366435500234410ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(hello) find_package(PythonExtensions REQUIRED) add_subdirectory(hello) file(WRITE "${CMAKE_BINARY_DIR}/helloModule.py" "# helloModule.py") install(FILES "${CMAKE_BINARY_DIR}/helloModule.py" DESTINATION ".") # # This code is here *ONLY* to test that the different signatures of # "check_dynamic_lookup" functions are not causing errors. # include(targetLinkLibrariesWithDynamicLookup) check_dynamic_lookup("MODULE" "SHARED" "HELLO_HAS_DYNAMIC_LOOKUP") check_dynamic_lookup("HELLO_HAS_DYNAMIC_LOOKUP") scikit-build-0.18.1/tests/samples/hello-cpp/bonjour/000077500000000000000000000000001466366435500223555ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/hello-cpp/bonjour/__init__.py000066400000000000000000000000001466366435500244540ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/hello-cpp/bonjour/data/000077500000000000000000000000001466366435500232665ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/hello-cpp/bonjour/data/ciel.txt000066400000000000000000000000051466366435500247360ustar00rootroot00000000000000ciel scikit-build-0.18.1/tests/samples/hello-cpp/bonjour/data/soleil.txt000066400000000000000000000000071466366435500253130ustar00rootroot00000000000000soleil scikit-build-0.18.1/tests/samples/hello-cpp/bonjour/data/terre.txt000066400000000000000000000000061466366435500251440ustar00rootroot00000000000000terre scikit-build-0.18.1/tests/samples/hello-cpp/bonjourModule.py000066400000000000000000000000001466366435500240630ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/hello-cpp/hello/000077500000000000000000000000001466366435500220025ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/hello-cpp/hello/CMakeLists.txt000066400000000000000000000003571466366435500245470ustar00rootroot00000000000000add_library(_hello MODULE _hello.cxx) python_extension_module(_hello) install(TARGETS _hello LIBRARY DESTINATION hello) set(world "${CMAKE_CURRENT_SOURCE_DIR}/world.py") file(WRITE ${world} "") install(FILES ${world} DESTINATION hello) scikit-build-0.18.1/tests/samples/hello-cpp/hello/__init__.py000066400000000000000000000000001466366435500241010ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/hello-cpp/hello/__main__.py000066400000000000000000000001731466366435500240750ustar00rootroot00000000000000from __future__ import annotations if __name__ == "__main__": from . import _hello as hello hello.hello("World") scikit-build-0.18.1/tests/samples/hello-cpp/hello/_hello.cxx000066400000000000000000000030211466366435500237640ustar00rootroot00000000000000 // Python includes #include // STD includes #include //----------------------------------------------------------------------------- static PyObject *hello_example(PyObject *self, PyObject *args) { // Unpack a string from the arguments const char *strArg; if (!PyArg_ParseTuple(args, "s", &strArg)) return NULL; // Print message and return None PySys_WriteStdout("Hello, %s! :)\n", strArg); Py_RETURN_NONE; } //----------------------------------------------------------------------------- static PyObject *elevation_example(PyObject *self, PyObject *args) { // Return an integer return PyLong_FromLong(21463L); } //----------------------------------------------------------------------------- static PyMethodDef hello_methods[] = { { "hello", hello_example, METH_VARARGS, "Prints back 'Hello ', for example example: hello.hello('you')" }, { "size", elevation_example, METH_VARARGS, "Returns elevation of Nevado Sajama." }, {NULL, NULL, 0, NULL} /* Sentinel */ }; //----------------------------------------------------------------------------- #if PY_MAJOR_VERSION < 3 PyMODINIT_FUNC init_hello(void) { (void) Py_InitModule("_hello", hello_methods); } #else /* PY_MAJOR_VERSION >= 3 */ static struct PyModuleDef hello_module_def = { PyModuleDef_HEAD_INIT, "_hello", "Internal \"_hello\" module", -1, hello_methods }; PyMODINIT_FUNC PyInit__hello(void) { return PyModule_Create(&hello_module_def); } #endif /* PY_MAJOR_VERSION >= 3 */ scikit-build-0.18.1/tests/samples/hello-cpp/setup.py000066400000000000000000000005471466366435500224170ustar00rootroot00000000000000from __future__ import annotations from skbuild import setup setup( name="hello", version="1.2.3", description="a minimal example package", author="The scikit-build team", license="MIT", packages=["bonjour", "hello"], package_data={"bonjour": ["data/*.txt", "data/terre.txt"]}, py_modules=["bonjourModule", "helloModule"], ) scikit-build-0.18.1/tests/samples/hello-cython/000077500000000000000000000000001466366435500214215ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/hello-cython/CMakeLists.txt000066400000000000000000000002351466366435500241610ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(hello_cython) find_package(PythonExtensions REQUIRED) find_package(Cython REQUIRED) add_subdirectory(hello) scikit-build-0.18.1/tests/samples/hello-cython/hello/000077500000000000000000000000001466366435500225245ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/hello-cython/hello/CMakeLists.txt000066400000000000000000000002271466366435500252650ustar00rootroot00000000000000 add_cython_target(_hello CXX) add_library(_hello MODULE ${_hello}) python_extension_module(_hello) install(TARGETS _hello LIBRARY DESTINATION hello) scikit-build-0.18.1/tests/samples/hello-cython/hello/__init__.py000066400000000000000000000000001466366435500246230ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/hello-cython/hello/__main__.py000066400000000000000000000001731466366435500246170ustar00rootroot00000000000000from __future__ import annotations if __name__ == "__main__": from . import _hello as hello hello.hello("World") scikit-build-0.18.1/tests/samples/hello-cython/hello/_hello.pyx000066400000000000000000000003441466366435500245310ustar00rootroot00000000000000 cpdef void hello(str strArg): "Prints back 'Hello ', for example example: hello.hello('you')" print("Hello, {}! :)".format(strArg)) cpdef long size(): "Returns elevation of Nevado Sajama." return 21463L scikit-build-0.18.1/tests/samples/hello-cython/setup.py000066400000000000000000000006051466366435500231340ustar00rootroot00000000000000from __future__ import annotations from skbuild import setup setup( name="hello-cython", version="1.2.3", description="a minimal example package (cython version)", author="The scikit-build team", license="MIT", packages=["hello_cython"], # The extra '/' was *only* added to check that scikit-build can handle it. package_dir={"hello_cython": "hello/"}, ) scikit-build-0.18.1/tests/samples/hello-fortran/000077500000000000000000000000001466366435500215705ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/hello-fortran/CMakeLists.txt000066400000000000000000000003351466366435500243310ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(hello_fortran C Fortran) find_package(PythonExtensions REQUIRED) find_package(NumPy REQUIRED) find_package(F2PY REQUIRED) add_subdirectory(hello) add_subdirectory(bonjour) scikit-build-0.18.1/tests/samples/hello-fortran/bonjour/000077500000000000000000000000001466366435500232465ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/hello-fortran/bonjour/CMakeLists.txt000066400000000000000000000006451466366435500260130ustar00rootroot00000000000000set(_module_name _bonjour) add_f2py_target(_bonjour "${CMAKE_CURRENT_SOURCE_DIR}/_bonjour.pyf" OUTPUT_VAR _pyf_target_output ) add_library(${_module_name} MODULE ${_module_name}) target_include_directories(${_module_name} PRIVATE ${F2PY_INCLUDE_DIRS}) target_link_libraries(${_module_name} ${F2PY_LIBRARIES}) python_extension_module(${_module_name}) install(TARGETS ${_module_name} LIBRARY DESTINATION hello ) scikit-build-0.18.1/tests/samples/hello-fortran/bonjour/_bonjour.f90000066400000000000000000000003761466366435500254110ustar00rootroot00000000000000subroutine bonjour print *, "Bonjour le monde!" end subroutine bonjour integer function change_integer(n) implicit none integer, intent(in) :: n integer, parameter :: power = 2 change_integer = n ** power end function change_integer scikit-build-0.18.1/tests/samples/hello-fortran/bonjour/_bonjour.pyf000066400000000000000000000010201466366435500255740ustar00rootroot00000000000000! -*- f90 -*- ! Note: the context of this file is case sensitive. python module _bonjour ! in interface ! in :_bonjour subroutine bonjour ! in :_bonjour:_bonjour.f90 end subroutine bonjour function change_integer(n) ! in :bonjour/_bonjour.f90 integer intent(in) :: n integer :: change_integer end function change_integer end interface end python module _bonjour ! This file was auto-generated with f2py (version:2). ! See http://cens.ioc.ee/projects/f2py2e/ scikit-build-0.18.1/tests/samples/hello-fortran/hello/000077500000000000000000000000001466366435500226735ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/hello-fortran/hello/CMakeLists.txt000066400000000000000000000010731466366435500254340ustar00rootroot00000000000000set(_module_name "_hello") set(fortran_src_file "${CMAKE_CURRENT_SOURCE_DIR}/_hello.f90") set(generated_module_file ${CMAKE_CURRENT_BINARY_DIR}/${_module_name}${PYTHON_EXTENSION_MODULE_SUFFIX}) add_f2py_target(${_module_name} "${fortran_src_file}" OUTPUT_VAR _module_target_output) add_library(${_module_name} MODULE ${_module_name}) target_include_directories(${_module_name} PRIVATE ${F2PY_INCLUDE_DIRS}) target_link_libraries(${_module_name} ${F2PY_LIBRARIES}) python_extension_module(${_module_name}) install(TARGETS ${_module_name} LIBRARY DESTINATION hello ) scikit-build-0.18.1/tests/samples/hello-fortran/hello/__init__.py000066400000000000000000000000001466366435500247720ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/hello-fortran/hello/__main__.py000066400000000000000000000002601466366435500247630ustar00rootroot00000000000000from __future__ import annotations if __name__ == "__main__": from . import _bonjour as bonjour from . import _hello as hello hello.hello() bonjour.bonjour() scikit-build-0.18.1/tests/samples/hello-fortran/hello/_hello.f90000066400000000000000000000001001466366435500244440ustar00rootroot00000000000000subroutine hello print *, "Hello world!" end subroutine hello scikit-build-0.18.1/tests/samples/hello-fortran/setup.py000066400000000000000000000006161466366435500233050ustar00rootroot00000000000000from __future__ import annotations from skbuild import setup setup( name="hello-fortran", version="1.2.3", description="a minimal example package (fortran version)", author="The scikit-build team", license="MIT", packages=["hello", "bonjour"], # package_dir={"hello_fortran": "hello"}, cmake_languages=("C", "Fortran"), cmake_minimum_required_version="3.5", ) scikit-build-0.18.1/tests/samples/hello-no-language/000077500000000000000000000000001466366435500223125ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/hello-no-language/CMakeLists.txt000066400000000000000000000002021466366435500250440ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(hello NONE) install(CODE "execute_process(COMMAND \${CMAKE_COMMAND} -E sleep 0)") scikit-build-0.18.1/tests/samples/hello-no-language/setup.py000066400000000000000000000003371466366435500240270ustar00rootroot00000000000000from __future__ import annotations from skbuild import setup setup( name="hello_no_language", version="1.2.3", description="a minimal example package", author="The scikit-build team", license="MIT", ) scikit-build-0.18.1/tests/samples/hello-numpy/000077500000000000000000000000001466366435500212655ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/hello-numpy/CMakeLists.txt000066400000000000000000000015441466366435500240310ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.18...3.22) project(hello-numpy VERSION "0.1") # Define CMAKE_INSTALL_xxx: LIBDIR, INCLUDEDIR include(GNUInstallDirs) find_package(Python REQUIRED COMPONENTS Development NumPy) # Fetch pybind11 include(FetchContent) FetchContent_Declare( pybind11 URL https://github.com/pybind/pybind11/archive/refs/tags/v2.10.0.tar.gz URL_HASH SHA256=eacf582fa8f696227988d08cfc46121770823839fe9e301a20fbce67e7cd70ec ) FetchContent_MakeAvailable(pybind11) set(python_module_name _hello) pybind11_add_module(${python_module_name} MODULE src/hello/hello_py.cpp ) target_link_libraries( ${python_module_name} PUBLIC Python::NumPy pybind11::module ) target_compile_definitions(${python_module_name} PRIVATE VERSION_INFO=${EXAMPLE_VERSION_INFO}) install(TARGETS ${python_module_name} DESTINATION .) scikit-build-0.18.1/tests/samples/hello-numpy/MANIFEST000066400000000000000000000002031466366435500224110ustar00rootroot00000000000000CMakeLists.txt README.md pyproject.toml setup.py src/hello/__init__.py src/hello/hello_py.cpp tests/test_hello_pybind11.py tox.ini scikit-build-0.18.1/tests/samples/hello-numpy/README.md000066400000000000000000000005671466366435500225540ustar00rootroot00000000000000# PyBind11 + Scikit Build example ## Building To build, you must have pip 10 or greater, *or* you need to manually install `scikit-build` and `cmake`. Once you create a wheel, that wheel can be used in earlier versions of pip. Example build and install sequence: ```bash pip install . python -c "import hello; hello.hello()" ``` This should print "Hello, World!". ``` scikit-build-0.18.1/tests/samples/hello-numpy/pyproject.toml000066400000000000000000000002421466366435500241770ustar00rootroot00000000000000[build-system] requires = [ "setuptools>=42", "scikit-build", "cmake>=3.18", "ninja", "numpy>=1.7", ] build-backend = "setuptools.build_meta" scikit-build-0.18.1/tests/samples/hello-numpy/setup.py000066400000000000000000000012401466366435500227740ustar00rootroot00000000000000from __future__ import annotations import sys try: import skbuild from skbuild import setup except ImportError: print( "Please update pip, you need pip 10 or greater,\n" " or you need to install the PEP 518 requirements in pyproject.toml yourself", file=sys.stderr, ) raise print("Scikit-build version:", skbuild.__version__) setup( name="hello-numpy", version="1.2.3", description="a minimal example package (with pybind11 and NumPy)", author="Hameer Abbasi", license="MIT", packages=["hello"], package_dir={"": "src"}, install_requires=["numpy>=1.7"], cmake_install_dir="src/hello", ) scikit-build-0.18.1/tests/samples/hello-numpy/src/000077500000000000000000000000001466366435500220545ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/hello-numpy/src/hello/000077500000000000000000000000001466366435500231575ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/hello-numpy/src/hello/__init__.py000066400000000000000000000001511466366435500252650ustar00rootroot00000000000000from __future__ import annotations from ._hello import hello, zeros2x2 __all__ = ("hello", "zeros2x2") scikit-build-0.18.1/tests/samples/hello-numpy/src/hello/hello_py.cpp000066400000000000000000000011101466366435500254670ustar00rootroot00000000000000#include #include #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include namespace py = pybind11; void hello() { std::cout << "Hello, World!" << std::endl; } py::object zeros2x2() { npy_intp dims[] = {2, 2}; return py::reinterpret_steal( PyArray_Zeros(2, dims, PyArray_DescrFromType(NPY_FLOAT64), 0) ); } PYBIND11_MODULE(_hello, m) { m.doc() = "_hello"; m.def("hello", &hello, "Prints \"Hello, World!\""); m.def("zeros2x2", &zeros2x2, "Returns a 2x2 array of zeros."); } scikit-build-0.18.1/tests/samples/hello-pep518/000077500000000000000000000000001466366435500211375ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/hello-pep518/CMakeLists.txt000066400000000000000000000012521466366435500236770ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.14...3.24) project(hello-pybind11 VERSION "0.1") # Define CMAKE_INSTALL_xxx: LIBDIR, INCLUDEDIR include(GNUInstallDirs) # Fetch pybind11 include(FetchContent) FetchContent_Declare( pybind11 URL https://github.com/pybind/pybind11/archive/refs/tags/v2.10.0.tar.gz URL_HASH SHA256=eacf582fa8f696227988d08cfc46121770823839fe9e301a20fbce67e7cd70ec ) FetchContent_MakeAvailable(pybind11) set(python_module_name _hello) pybind11_add_module(${python_module_name} MODULE src/hello/hello_py.cpp ) install(TARGETS ${python_module_name} DESTINATION .) # Quiet a warning, since this project is only valid with SKBUILD set(ignoreMe "${SKBUILD}") scikit-build-0.18.1/tests/samples/hello-pep518/MANIFEST000066400000000000000000000002031466366435500222630ustar00rootroot00000000000000CMakeLists.txt README.md pyproject.toml setup.py src/hello/__init__.py src/hello/hello_py.cpp tests/test_hello_pybind11.py tox.ini scikit-build-0.18.1/tests/samples/hello-pep518/README.md000066400000000000000000000005671466366435500224260ustar00rootroot00000000000000# PyBind11 + Scikit Build example ## Building To build, you must have pip 10 or greater, *or* you need to manually install `scikit-build` and `cmake`. Once you create a wheel, that wheel can be used in earlier versions of pip. Example build and install sequence: ```bash pip install . python -c "import hello; hello.hello()" ``` This should print "Hello, World!". ``` scikit-build-0.18.1/tests/samples/hello-pep518/pyproject.toml000066400000000000000000000002061466366435500240510ustar00rootroot00000000000000[build-system] requires = [ "setuptools", "scikit-build", "cmake", "ninja", ] build-backend = "setuptools.build_meta" scikit-build-0.18.1/tests/samples/hello-pep518/setup.py000066400000000000000000000011751466366435500226550ustar00rootroot00000000000000from __future__ import annotations import sys try: import skbuild from skbuild import setup except ImportError: print( "Please update pip, you need pip 10 or greater,\n" " or you need to install the PEP 518 requirements in pyproject.toml yourself", file=sys.stderr, ) raise print("Scikit-build version:", skbuild.__version__) setup( name="hello-pybind11", version="1.2.3", description="a minimal example package (with pybind11)", author="Pablo Hernandez-Cerdan", license="MIT", packages=["hello"], package_dir={"": "src"}, cmake_install_dir="src/hello", ) scikit-build-0.18.1/tests/samples/hello-pep518/src/000077500000000000000000000000001466366435500217265ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/hello-pep518/src/hello/000077500000000000000000000000001466366435500230315ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/hello-pep518/src/hello/__init__.py000066400000000000000000000001551466366435500251430ustar00rootroot00000000000000from __future__ import annotations from ._hello import hello, return_two __all__ = ("hello", "return_two") scikit-build-0.18.1/tests/samples/hello-pep518/src/hello/hello_py.cpp000066400000000000000000000005251466366435500253520ustar00rootroot00000000000000#include #include namespace py = pybind11; void hello() { std::cout << "Hello, World!" << std::endl; } int return_two() { return 2; } PYBIND11_MODULE(_hello, m) { m.doc() = "_hello"; m.def("hello", &hello, "Prints \"Hello, World!\""); m.def("return_two", &return_two, "Returns 2"); } scikit-build-0.18.1/tests/samples/hello-pure/000077500000000000000000000000001466366435500210705ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/hello-pure/hello/000077500000000000000000000000001466366435500221735ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/hello-pure/hello/__init__.py000066400000000000000000000000001466366435500242720ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/hello-pure/setup.py000066400000000000000000000003601466366435500226010ustar00rootroot00000000000000from __future__ import annotations from skbuild import setup setup( name="hello-pure", version="1.2.3", description="a minimal example package", author="The scikit-build team", license="MIT", packages=["hello"], ) scikit-build-0.18.1/tests/samples/issue-274-support-default-package-dir/000077500000000000000000000000001466366435500257575ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/issue-274-support-default-package-dir/CMakeLists.txt000066400000000000000000000004521466366435500305200ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(hello NONE) set(module "${CMAKE_CURRENT_BINARY_DIR}/cmake_generated_module.py") file(WRITE ${module} "# Generated from ${CMAKE_CURRENT_LIST_FILE} def what(): return \"cmake_generated_module\" ") install(FILES ${module} DESTINATION src/hello/) scikit-build-0.18.1/tests/samples/issue-274-support-default-package-dir/hello_tests/000077500000000000000000000000001466366435500303045ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/issue-274-support-default-package-dir/hello_tests/__init__.py000066400000000000000000000007231466366435500324170ustar00rootroot00000000000000from __future__ import annotations import unittest import hello from hello import cmake_generated_module class HelloTest(unittest.TestCase): def test_hello(self): assert hello.who() == "world" with open("test_hello.completed.txt", "w") as marker: marker.write("") def test_cmake_generated_module(self): assert cmake_generated_module.what() == "cmake_generated_module" if __name__ == "__main__": unittest.main() scikit-build-0.18.1/tests/samples/issue-274-support-default-package-dir/setup.py000066400000000000000000000004101466366435500274640ustar00rootroot00000000000000from __future__ import annotations from skbuild import setup setup( name="hello", version="1.2.3", description="a minimal example package", author="The scikit-build team", license="MIT", packages=["hello"], package_dir={"": "src"}, ) scikit-build-0.18.1/tests/samples/issue-274-support-default-package-dir/src/000077500000000000000000000000001466366435500265465ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/issue-274-support-default-package-dir/src/hello/000077500000000000000000000000001466366435500276515ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/issue-274-support-default-package-dir/src/hello/__init__.py000066400000000000000000000001031466366435500317540ustar00rootroot00000000000000from __future__ import annotations def who(): return "world" scikit-build-0.18.1/tests/samples/issue-274-support-one-package-without-package-dir/000077500000000000000000000000001466366435500302065ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/issue-274-support-one-package-without-package-dir/CMakeLists.txt000066400000000000000000000004461466366435500327520ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(hello NONE) set(module "${CMAKE_CURRENT_BINARY_DIR}/cmake_generated_module.py") file(WRITE ${module} "# Generated from ${CMAKE_CURRENT_LIST_FILE} def what(): return \"cmake_generated_module\" ") install(FILES ${module} DESTINATION hello/) scikit-build-0.18.1/tests/samples/issue-274-support-one-package-without-package-dir/hello/000077500000000000000000000000001466366435500313115ustar00rootroot00000000000000__init__.py000066400000000000000000000001031466366435500333350ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/issue-274-support-one-package-without-package-dir/hellofrom __future__ import annotations def who(): return "world" scikit-build-0.18.1/tests/samples/issue-274-support-one-package-without-package-dir/hello_tests/000077500000000000000000000000001466366435500325335ustar00rootroot00000000000000__init__.py000066400000000000000000000007231466366435500345670ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/issue-274-support-one-package-without-package-dir/hello_testsfrom __future__ import annotations import unittest import hello from hello import cmake_generated_module class HelloTest(unittest.TestCase): def test_hello(self): assert hello.who() == "world" with open("test_hello.completed.txt", "w") as marker: marker.write("") def test_cmake_generated_module(self): assert cmake_generated_module.what() == "cmake_generated_module" if __name__ == "__main__": unittest.main() scikit-build-0.18.1/tests/samples/issue-274-support-one-package-without-package-dir/setup.py000066400000000000000000000003531466366435500317210ustar00rootroot00000000000000from __future__ import annotations from skbuild import setup setup( name="hello", version="1.2.3", description="a minimal example package", author="The scikit-build team", license="MIT", packages=["hello"], ) scikit-build-0.18.1/tests/samples/issue-284-build-ext-inplace/000077500000000000000000000000001466366435500237635ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/issue-284-build-ext-inplace/CMakeLists.txt000066400000000000000000000003531466366435500265240ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(hello) find_package(PythonExtensions REQUIRED) add_library(_hello_sk MODULE hello/_hello_sk.cxx) python_extension_module(_hello_sk) install(TARGETS _hello_sk LIBRARY DESTINATION hello) scikit-build-0.18.1/tests/samples/issue-284-build-ext-inplace/hello/000077500000000000000000000000001466366435500250665ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/issue-284-build-ext-inplace/hello/__init__.py000066400000000000000000000000001466366435500271650ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/issue-284-build-ext-inplace/hello/_hello_ext.cxx000066400000000000000000000030451466366435500277360ustar00rootroot00000000000000 // Python includes #include // STD includes #include //----------------------------------------------------------------------------- static PyObject *hello_example(PyObject *self, PyObject *args) { // Unpack a string from the arguments const char *strArg; if (!PyArg_ParseTuple(args, "s", &strArg)) return NULL; // Print message and return None PySys_WriteStdout("Hello, %s! :)\n", strArg); Py_RETURN_NONE; } //----------------------------------------------------------------------------- static PyObject *elevation_example(PyObject *self, PyObject *args) { // Return an integer return PyLong_FromLong(21463L); } //----------------------------------------------------------------------------- static PyMethodDef hello_methods[] = { { "hello", hello_example, METH_VARARGS, "Prints back 'Hello ', for example example: hello.hello('you')" }, { "size", elevation_example, METH_VARARGS, "Returns elevation of Nevado Sajama." }, {NULL, NULL, 0, NULL} /* Sentinel */ }; //----------------------------------------------------------------------------- #if PY_MAJOR_VERSION < 3 PyMODINIT_FUNC init_hello_ext(void) { (void) Py_InitModule("_hello_ext", hello_methods); } #else /* PY_MAJOR_VERSION >= 3 */ static struct PyModuleDef hello_module_def = { PyModuleDef_HEAD_INIT, "_hello_ext", "Internal \"_hello_ext\" module", -1, hello_methods }; PyMODINIT_FUNC PyInit__hello_ext(void) { return PyModule_Create(&hello_module_def); } #endif /* PY_MAJOR_VERSION >= 3 */ scikit-build-0.18.1/tests/samples/issue-284-build-ext-inplace/hello/_hello_sk.cxx000066400000000000000000000030401466366435500275460ustar00rootroot00000000000000 // Python includes #include // STD includes #include //----------------------------------------------------------------------------- static PyObject *hello_example(PyObject *self, PyObject *args) { // Unpack a string from the arguments const char *strArg; if (!PyArg_ParseTuple(args, "s", &strArg)) return NULL; // Print message and return None PySys_WriteStdout("Hello, %s! :)\n", strArg); Py_RETURN_NONE; } //----------------------------------------------------------------------------- static PyObject *elevation_example(PyObject *self, PyObject *args) { // Return an integer return PyLong_FromLong(21463L); } //----------------------------------------------------------------------------- static PyMethodDef hello_methods[] = { { "hello", hello_example, METH_VARARGS, "Prints back 'Hello ', for example example: hello.hello('you')" }, { "size", elevation_example, METH_VARARGS, "Returns elevation of Nevado Sajama." }, {NULL, NULL, 0, NULL} /* Sentinel */ }; //----------------------------------------------------------------------------- #if PY_MAJOR_VERSION < 3 PyMODINIT_FUNC init_hello_sk(void) { (void) Py_InitModule("_hello_sk", hello_methods); } #else /* PY_MAJOR_VERSION >= 3 */ static struct PyModuleDef hello_module_def = { PyModuleDef_HEAD_INIT, "_hello_sk", "Internal \"_hello_sk\" module", -1, hello_methods }; PyMODINIT_FUNC PyInit__hello_sk(void) { return PyModule_Create(&hello_module_def); } #endif /* PY_MAJOR_VERSION >= 3 */ scikit-build-0.18.1/tests/samples/issue-284-build-ext-inplace/setup.py000066400000000000000000000006211466366435500254740ustar00rootroot00000000000000from __future__ import annotations from setuptools import Extension from skbuild import setup setup( name="hello", version="1.2.3", description="a minimal example package", author="The scikit-build team", license="MIT", packages=["hello"], ext_modules=[ Extension( "hello._hello_ext", sources=["hello/_hello_ext.cxx"], ) ], ) scikit-build-0.18.1/tests/samples/issue-334-configure-cmakelist-non-cp1252-encoding/000077500000000000000000000000001466366435500276725ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/issue-334-configure-cmakelist-non-cp1252-encoding/CMakeLists.txt000066400000000000000000000004121466366435500324270ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(foobar NONE) # Sanskrit for "I can eat glass, it does not hurt me" # See http://kermitproject.org/utf8.html install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E echo \"kācaṃ śaknomyattum; nopahinasti mām\")") scikit-build-0.18.1/tests/samples/issue-334-configure-cmakelist-non-cp1252-encoding/setup.py000066400000000000000000000003441466366435500314050ustar00rootroot00000000000000from __future__ import annotations from skbuild import setup setup( name="hello", version="1.2.3", description="a minimal example package", author="The scikit-build team", license="MIT", packages=[], ) scikit-build-0.18.1/tests/samples/issue-335-support-cmake-source-dir/000077500000000000000000000000001466366435500253165ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/issue-335-support-cmake-source-dir/CMakeLists.txt000066400000000000000000000011271466366435500300570ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.2) project(hello NONE) # Headers file(WRITE "bar.h" "") file(WRITE "foo.h" "") install(FILES "bar.h" "foo.h" DESTINATION include) # Static libraries file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/libbar.a" "") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/libfoo.a" "") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libbar.a" "${CMAKE_CURRENT_BINARY_DIR}/libfoo.a" DESTINATION lib/static ) # Executables file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/hello" "") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/hello" DESTINATION bin ) add_subdirectory(wrapping/python) scikit-build-0.18.1/tests/samples/issue-335-support-cmake-source-dir/wrapping/000077500000000000000000000000001466366435500271455ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/issue-335-support-cmake-source-dir/wrapping/python/000077500000000000000000000000001466366435500304665ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/issue-335-support-cmake-source-dir/wrapping/python/CMakeLists.txt000066400000000000000000000006261466366435500332320ustar00rootroot00000000000000# Use .pyd extension on all platforms. This will avoid failure # on macOS where wheel wheel/macosx_libfile.py attempts to extract # shared library information. file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/swig_mwe.py" "") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/_swig_mwe.pyd" "") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/swig_mwe.py ${CMAKE_CURRENT_BINARY_DIR}/_swig_mwe.pyd DESTINATION hello ) scikit-build-0.18.1/tests/samples/issue-335-support-cmake-source-dir/wrapping/python/hello/000077500000000000000000000000001466366435500315715ustar00rootroot00000000000000__init__.py000066400000000000000000000000001466366435500336110ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/issue-335-support-cmake-source-dir/wrapping/python/helloscikit-build-0.18.1/tests/samples/issue-335-support-cmake-source-dir/wrapping/python/setup.py000066400000000000000000000004571466366435500322060ustar00rootroot00000000000000from __future__ import annotations from skbuild import setup setup( name="hello", version="1.2.3", description="a minimal example package (cpp version)", author="The scikit-build team", license="MIT", packages=["hello"], setup_requires=[], cmake_source_dir="../../", ) scikit-build-0.18.1/tests/samples/issue-401-sdist-with-symlinks/000077500000000000000000000000001466366435500244125ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/issue-401-sdist-with-symlinks/MANIFEST.in000066400000000000000000000000371466366435500261500ustar00rootroot00000000000000include README include VERSION scikit-build-0.18.1/tests/samples/issue-401-sdist-with-symlinks/README000066400000000000000000000000271466366435500252710ustar00rootroot00000000000000This is a README file. scikit-build-0.18.1/tests/samples/issue-401-sdist-with-symlinks/VERSION000077700000000000000000000000001466366435500277752VERSION.actualustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/issue-401-sdist-with-symlinks/VERSION.actual000066400000000000000000000000061466366435500267260ustar00rootroot000000000000001.2.3 scikit-build-0.18.1/tests/samples/issue-401-sdist-with-symlinks/setup.py000066400000000000000000000003511466366435500261230ustar00rootroot00000000000000from __future__ import annotations from skbuild import setup setup( name="hello", version="1.2.3", author="John Doe", author_email="test@example.com", url="https://example.com", include_package_data=True, ) scikit-build-0.18.1/tests/samples/issue-668-symbol-visibility/000077500000000000000000000000001466366435500241555ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/issue-668-symbol-visibility/CMakeLists.txt000066400000000000000000000003431466366435500267150ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5...3.22) project(hello) find_package(PythonExtensions REQUIRED) add_library(_hello MODULE hello/_hello.cxx) python_extension_module(_hello) install(TARGETS _hello LIBRARY DESTINATION hello) scikit-build-0.18.1/tests/samples/issue-668-symbol-visibility/hello/000077500000000000000000000000001466366435500252605ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/issue-668-symbol-visibility/hello/__init__.py000066400000000000000000000001621466366435500273700ustar00rootroot00000000000000from __future__ import annotations from ._hello import ( elevation, # noqa: F401 hello, # noqa: F401 ) scikit-build-0.18.1/tests/samples/issue-668-symbol-visibility/hello/_hello.cxx000066400000000000000000000036141466366435500272520ustar00rootroot00000000000000 // Python includes #include // STD includes #include // Macros used for defining symbol visibility #if (defined(__GNUC__)) #define _EXPORT __attribute__((visibility("default"))) #define _HIDDEN __attribute__((visibility("hidden"))) #else #define _EXPORT #define _HIDDEN #endif #include extern "C" _EXPORT auto& get_map() { static std::map id_to_resource; return id_to_resource; } //----------------------------------------------------------------------------- static PyObject *hello_example(PyObject *self, PyObject *args) { get_map(); // Unpack a string from the arguments const char *strArg; if (!PyArg_ParseTuple(args, "s", &strArg)) return NULL; // Print message and return None PySys_WriteStdout("Hello, %s!\n", strArg); Py_RETURN_NONE; } //----------------------------------------------------------------------------- static PyObject *elevation_example(PyObject *self, PyObject *args) { get_map(); // Return an integer return PyLong_FromLong(21463L); } //----------------------------------------------------------------------------- static PyMethodDef hello_methods[] = { { "hello", hello_example, METH_VARARGS, "Prints back 'Hello ', for example example: hello.hello('you')" }, { "elevation", elevation_example, METH_VARARGS, "Returns elevation of Nevado Sajama." }, {NULL, NULL, 0, NULL} /* Sentinel */ }; //----------------------------------------------------------------------------- #if PY_MAJOR_VERSION < 3 PyMODINIT_FUNC init_hello(void) { (void) Py_InitModule("_hello", hello_methods); } #else /* PY_MAJOR_VERSION >= 3 */ static struct PyModuleDef hello_module_def = { PyModuleDef_HEAD_INIT, "_hello", "Internal \"_hello\" module", -1, hello_methods }; PyMODINIT_FUNC PyInit__hello(void) { return PyModule_Create(&hello_module_def); } #endif /* PY_MAJOR_VERSION >= 3 */ scikit-build-0.18.1/tests/samples/issue-668-symbol-visibility/pyproject.toml000066400000000000000000000002261466366435500270710ustar00rootroot00000000000000[build-system] requires = [ "setuptools>=42", "scikit-build>=0.13", "cmake>=3.18", "ninja", ] build-backend = "setuptools.build_meta" scikit-build-0.18.1/tests/samples/issue-668-symbol-visibility/setup.py000066400000000000000000000004321466366435500256660ustar00rootroot00000000000000from __future__ import annotations from skbuild import setup setup( name="hello-cpp", version="1.2.3", description="a minimal example package (cpp version)", author="The scikit-build team", license="MIT", packages=["hello"], python_requires=">=3.7", ) scikit-build-0.18.1/tests/samples/issue-707-nested-packages/000077500000000000000000000000001466366435500235135ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/issue-707-nested-packages/CMakeLists.txt000066400000000000000000000011631466366435500262540ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5...3.22) project(hello_nested) find_package(PythonExtensions REQUIRED) find_package(Cython REQUIRED) add_cython_target(_hello_nested hello_nested/_hello_nested.pyx CXX) add_library(_hello_nested MODULE ${_hello_nested}) python_extension_module(_hello_nested) install(TARGETS _hello_nested LIBRARY DESTINATION hello_nested) add_cython_target(_goodbye_nested hello_nested/goodbye_nested/_goodbye_nested.pyx CXX) add_library(_goodbye_nested MODULE ${_goodbye_nested}) python_extension_module(_goodbye_nested) install(TARGETS _goodbye_nested LIBRARY DESTINATION hello_nested/goodbye_nested) scikit-build-0.18.1/tests/samples/issue-707-nested-packages/hello_nested/000077500000000000000000000000001466366435500261605ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/issue-707-nested-packages/hello_nested/__init__.py000066400000000000000000000002111466366435500302630ustar00rootroot00000000000000from __future__ import annotations from . import goodbye_nested from ._hello_nested import hello __all__ = ("hello", "goodbye_nested") scikit-build-0.18.1/tests/samples/issue-707-nested-packages/hello_nested/_hello_nested.pyx000066400000000000000000000002201466366435500315200ustar00rootroot00000000000000cpdef void hello(str strArg): "Prints back 'Hello ', for example example: hello.hello('you')" print("Hello, {}!".format(strArg)) scikit-build-0.18.1/tests/samples/issue-707-nested-packages/hello_nested/goodbye_nested/000077500000000000000000000000001466366435500311525ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/issue-707-nested-packages/hello_nested/goodbye_nested/__init__.py000066400000000000000000000001411466366435500332570ustar00rootroot00000000000000from __future__ import annotations from ._goodbye_nested import goodbye __all__ = ("goodbye",) _goodbye_nested.pyx000066400000000000000000000002321466366435500347630ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/issue-707-nested-packages/hello_nested/goodbye_nestedcpdef void goodbye(str strArg): "Prints back 'Goodbye ', for example example: goodbye.goodbye('you')" print("Goodbye, {}!".format(strArg)) scikit-build-0.18.1/tests/samples/issue-707-nested-packages/pyproject.toml000066400000000000000000000002441466366435500264270ustar00rootroot00000000000000[build-system] requires = [ "setuptools>=42", "scikit-build>=0.13", "cmake>=3.18", "ninja", "cython", ] build-backend = "setuptools.build_meta" scikit-build-0.18.1/tests/samples/issue-707-nested-packages/setup.py000066400000000000000000000005101466366435500252210ustar00rootroot00000000000000from __future__ import annotations from skbuild import setup setup( name="hello-nested", version="1.2.3", description="Nested packages with a single CMakeLists.txt", author="The scikit-build team", license="MIT", packages=["hello_nested", "hello_nested.goodbye_nested"], python_requires=">=3.7", ) scikit-build-0.18.1/tests/samples/manifest-in/000077500000000000000000000000001466366435500212265ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/manifest-in/MANIFEST.in000066400000000000000000000000431466366435500227610ustar00rootroot00000000000000include setup.py hello/__init__.py scikit-build-0.18.1/tests/samples/manifest-in/hello/000077500000000000000000000000001466366435500223315ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/manifest-in/hello/__init__.py000066400000000000000000000000001466366435500244300ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/manifest-in/not_included.txt000066400000000000000000000000551466366435500244360ustar00rootroot00000000000000This file is not included in the MANIFEST.in scikit-build-0.18.1/tests/samples/manifest-in/setup.py000066400000000000000000000004041466366435500227360ustar00rootroot00000000000000from __future__ import annotations from skbuild import setup setup( name="manifest-in", version="1.2.3", description="a minimal example package with a MANIFEST.in", author="The scikit-build team", license="MIT", packages=["hello"], ) scikit-build-0.18.1/tests/samples/test-cmake-target/000077500000000000000000000000001466366435500223355ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-cmake-target/CMakeLists.txt000066400000000000000000000012651466366435500251010ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(test-cmake-target NONE) file(WRITE "${CMAKE_BINARY_DIR}/foo.txt" "# foo") file(WRITE "${CMAKE_BINARY_DIR}/runtime.txt" "# runtime") install(FILES "${CMAKE_BINARY_DIR}/foo.txt" DESTINATION ".") install(CODE "message(STATUS \"Project has been installed\")") install(FILES "${CMAKE_BINARY_DIR}/runtime.txt" DESTINATION "." COMPONENT runtime) install(CODE "message(STATUS \"Runtime component has been installed\")" COMPONENT runtime) # Add custom target to only install component: runtime (libraries) add_custom_target(install-runtime ${CMAKE_COMMAND} -DCMAKE_INSTALL_COMPONENT=runtime -P "${PROJECT_BINARY_DIR}/cmake_install.cmake" ) scikit-build-0.18.1/tests/samples/test-cmake-target/setup.py000066400000000000000000000004461466366435500240530ustar00rootroot00000000000000from __future__ import annotations from skbuild import setup setup( name="test-cmake-target", version="1.2.3", description="a minimal example package using a non-default target", author="The scikit-build team", license="MIT", cmake_install_target="install-runtime", ) scikit-build-0.18.1/tests/samples/test-filter-manifest/000077500000000000000000000000001466366435500230625ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-filter-manifest/CMakeLists.txt000066400000000000000000000011271466366435500256230ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5) project(hello NONE) # Headers file(WRITE "bar.h" "") file(WRITE "foo.h" "") install(FILES "bar.h" "foo.h" DESTINATION include) # Static libraries file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/libbar.a" "") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/libfoo.a" "") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libbar.a" "${CMAKE_CURRENT_BINARY_DIR}/libfoo.a" DESTINATION lib/static ) # Executables file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/hello" "") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/hello" DESTINATION bin ) add_subdirectory(wrapping/python) scikit-build-0.18.1/tests/samples/test-filter-manifest/wrapping/000077500000000000000000000000001466366435500247115ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-filter-manifest/wrapping/python/000077500000000000000000000000001466366435500262325ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-filter-manifest/wrapping/python/CMakeLists.txt000066400000000000000000000006271466366435500307770ustar00rootroot00000000000000 # Use .pyd extension on all platforms. This will avoid failure # on macOS where wheel wheel/macosx_libfile.py attempts to extract # shared library information. file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/swig_mwe.py" "") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/_swig_mwe.pyd" "") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/swig_mwe.py ${CMAKE_CURRENT_BINARY_DIR}/_swig_mwe.pyd DESTINATION hello ) scikit-build-0.18.1/tests/samples/test-filter-manifest/wrapping/python/hello/000077500000000000000000000000001466366435500273355ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-filter-manifest/wrapping/python/hello/__init__.py000066400000000000000000000000001466366435500314340ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-filter-manifest/wrapping/python/setup.py000066400000000000000000000007421466366435500277470ustar00rootroot00000000000000from __future__ import annotations from skbuild import setup def exclude_dev_files(cmake_manifest): return list(filter(lambda name: not name.endswith((".a", ".h")), cmake_manifest)) setup( name="hello", version="1.2.3", description="a minimal example package (cpp version)", author="The scikit-build team", license="MIT", packages=["hello"], setup_requires=[], cmake_source_dir="../../", cmake_process_manifest_hook=exclude_dev_files, ) scikit-build-0.18.1/tests/samples/test-hide-listing/000077500000000000000000000000001466366435500223515ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-hide-listing/CMakeLists.txt000066400000000000000000000003511466366435500251100ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(test_hide_listing NONE) add_subdirectory(hello) file(WRITE "${CMAKE_BINARY_DIR}/helloModule.py" "# helloModule.py") install(FILES "${CMAKE_BINARY_DIR}/helloModule.py" DESTINATION ".") scikit-build-0.18.1/tests/samples/test-hide-listing/bonjour/000077500000000000000000000000001466366435500240275ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-hide-listing/bonjour/__init__.py000066400000000000000000000000001466366435500261260ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-hide-listing/bonjour/data/000077500000000000000000000000001466366435500247405ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-hide-listing/bonjour/data/ciel.txt000066400000000000000000000000051466366435500264100ustar00rootroot00000000000000ciel scikit-build-0.18.1/tests/samples/test-hide-listing/bonjour/data/soleil.txt000066400000000000000000000000071466366435500267650ustar00rootroot00000000000000soleil scikit-build-0.18.1/tests/samples/test-hide-listing/bonjour/data/terre.txt000066400000000000000000000000061466366435500266160ustar00rootroot00000000000000terre scikit-build-0.18.1/tests/samples/test-hide-listing/bonjourModule.py000066400000000000000000000000001466366435500255350ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-hide-listing/hello/000077500000000000000000000000001466366435500234545ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-hide-listing/hello/CMakeLists.txt000066400000000000000000000001651466366435500262160ustar00rootroot00000000000000set(world "${CMAKE_CURRENT_SOURCE_DIR}/world.py") file(WRITE ${world} "") install(FILES ${world} DESTINATION hello) scikit-build-0.18.1/tests/samples/test-hide-listing/hello/__init__.py000066400000000000000000000001041466366435500255600ustar00rootroot00000000000000from __future__ import annotations def hello(msg): print(msg) scikit-build-0.18.1/tests/samples/test-hide-listing/hello/__main__.py000066400000000000000000000001611466366435500255440ustar00rootroot00000000000000from __future__ import annotations if __name__ == "__main__": from . import hello hello.hello("World") scikit-build-0.18.1/tests/samples/test-hide-listing/setup.py000066400000000000000000000005471466366435500240710ustar00rootroot00000000000000from __future__ import annotations from skbuild import setup setup( name="hello", version="1.2.3", description="a minimal example package", author="The scikit-build team", license="MIT", packages=["bonjour", "hello"], package_data={"bonjour": ["data/*.txt", "data/terre.txt"]}, py_modules=["bonjourModule", "helloModule"], ) scikit-build-0.18.1/tests/samples/test-include-exclude-data-with-base/000077500000000000000000000000001466366435500256335ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data-with-base/CMakeLists.txt000066400000000000000000000036741466366435500304050ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(test_include_exclude_data_with_base NONE) set(base "src/") set(module "${CMAKE_CURRENT_BINARY_DIR}/cmake_generated_module.py") file(WRITE ${module} "# Generated from ${CMAKE_CURRENT_LIST_FILE} def what(): return \"cmake_generated_module\" ") install(FILES ${module} DESTINATION ${base}hello) # hello data set(data "${CMAKE_CURRENT_BINARY_DIR}/hello_data1_cmake_generated.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello) set(data "${CMAKE_CURRENT_BINARY_DIR}/hello_data2_cmake_generated.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello) set(data "${CMAKE_CURRENT_BINARY_DIR}/hello_data3_cmake_generated.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello/data/subdata) set(data "${CMAKE_CURRENT_BINARY_DIR}/hello_data4_cmake_generated_and_exclude_from_setup.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello/data/subdata) # hello2 data set(data "${CMAKE_CURRENT_BINARY_DIR}/hello2_data1_cmake_generated.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello2) set(data "${CMAKE_CURRENT_BINARY_DIR}/hello2_data2_cmake_generated.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello2) set(data "${CMAKE_CURRENT_BINARY_DIR}/hello2_data3_cmake_generated.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello2/data2/subdata2) set(data "${CMAKE_CURRENT_BINARY_DIR}/hello2_data4_cmake_generated_and_exclude_from_setup.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello2/data2/subdata2) scikit-build-0.18.1/tests/samples/test-include-exclude-data-with-base/MANIFEST.in000066400000000000000000000004021466366435500273650ustar00rootroot00000000000000include src/hello/hello_include_from_manifest.txt include src/hello2/hello2_include_from_manifest.txt include src/hello/data/subdata/*.txt include src/hello2/data2/subdata2/*.txt exclude src/*/data*/subdata*/*_exclude_from_manifest.txt exclude MANIFEST.in scikit-build-0.18.1/tests/samples/test-include-exclude-data-with-base/setup.py000066400000000000000000000013001466366435500273370ustar00rootroot00000000000000from __future__ import annotations from skbuild import setup setup( name="test_include_exclude_data_with_base", version="0.1.0", cmake_languages=(), packages=["hello", "hello2", "hello.data.subdata", "hello2.data2.subdata2"], package_dir={"": "src"}, include_package_data=True, exclude_package_data={ "hello.data.subdata": [ "*_data4_cmake_generated_and_exclude_from_setup.txt", "*data4_include_from_manifest_and_exclude_from_setup.txt", ], "hello2.data2.subdata2": [ "*_data4_cmake_generated_and_exclude_from_setup.txt", "*_data4_include_from_manifest_and_exclude_from_setup.txt", ], }, ) scikit-build-0.18.1/tests/samples/test-include-exclude-data-with-base/src/000077500000000000000000000000001466366435500264225ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data-with-base/src/hello/000077500000000000000000000000001466366435500275255ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data-with-base/src/hello/__init__.py000066400000000000000000000000001466366435500316240ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data-with-base/src/hello/data/000077500000000000000000000000001466366435500304365ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data-with-base/src/hello/data/subdata/000077500000000000000000000000001466366435500320615ustar00rootroot00000000000000hello_data1_include_from_manifest.txt000066400000000000000000000000271466366435500413330ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data-with-base/src/hello/data/subdatathis is just data file hello_data2_include_from_manifest.txt000066400000000000000000000000001466366435500413230ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data-with-base/src/hello/data/subdatahello_data3_include_from_manifest_and_exclude_from_manifest.txt000066400000000000000000000000001466366435500465700ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data-with-base/src/hello/data/subdatahello_data4_include_from_manifest_and_exclude_from_setup.txt000066400000000000000000000000001466366435500461230ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data-with-base/src/hello/data/subdatahello_include_from_manifest.txt000066400000000000000000000000001466366435500357140ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data-with-base/src/helloscikit-build-0.18.1/tests/samples/test-include-exclude-data-with-base/src/hello2/000077500000000000000000000000001466366435500276075ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data-with-base/src/hello2/__init__.py000066400000000000000000000000001466366435500317060ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data-with-base/src/hello2/data2/000077500000000000000000000000001466366435500306025ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data-with-base/src/hello2/data2/subdata2/000077500000000000000000000000001466366435500323075ustar00rootroot00000000000000hello2_data1_include_from_manifest.txt000066400000000000000000000000271466366435500416430ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data-with-base/src/hello2/data2/subdata2this is just data file hello2_data2_include_from_manifest.txt000066400000000000000000000000001466366435500416330ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data-with-base/src/hello2/data2/subdata2hello2_data3_include_from_manifest_and_exclude_from_manifest.txt000066400000000000000000000000001466366435500471000ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data-with-base/src/hello2/data2/subdata2hello2_data4_include_from_manifest_and_exclude_from_setup.txt000066400000000000000000000000001466366435500464330ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data-with-base/src/hello2/data2/subdata2hello2_include_from_manifest.txt000066400000000000000000000000001466366435500360600ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data-with-base/src/hello2scikit-build-0.18.1/tests/samples/test-include-exclude-data/000077500000000000000000000000001466366435500237525ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data/CMakeLists.txt000066400000000000000000000036561466366435500265240ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(test_include_exclude_data NONE) set(base "") set(module "${CMAKE_CURRENT_BINARY_DIR}/cmake_generated_module.py") file(WRITE ${module} "# Generated from ${CMAKE_CURRENT_LIST_FILE} def what(): return \"cmake_generated_module\" ") install(FILES ${module} DESTINATION ${base}hello) # hello data set(data "${CMAKE_CURRENT_BINARY_DIR}/hello_data1_cmake_generated.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello) set(data "${CMAKE_CURRENT_BINARY_DIR}/hello_data2_cmake_generated.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello) set(data "${CMAKE_CURRENT_BINARY_DIR}/hello_data3_cmake_generated.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello/data/subdata) set(data "${CMAKE_CURRENT_BINARY_DIR}/hello_data4_cmake_generated_and_exclude_from_setup.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello/data/subdata) # hello2 data set(data "${CMAKE_CURRENT_BINARY_DIR}/hello2_data1_cmake_generated.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello2) set(data "${CMAKE_CURRENT_BINARY_DIR}/hello2_data2_cmake_generated.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello2) set(data "${CMAKE_CURRENT_BINARY_DIR}/hello2_data3_cmake_generated.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello2/data2/subdata2) set(data "${CMAKE_CURRENT_BINARY_DIR}/hello2_data4_cmake_generated_and_exclude_from_setup.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello2/data2/subdata2) scikit-build-0.18.1/tests/samples/test-include-exclude-data/MANIFEST.in000066400000000000000000000003561466366435500255140ustar00rootroot00000000000000include hello/hello_include_from_manifest.txt include hello2/hello2_include_from_manifest.txt include hello/data/subdata/*.txt include hello2/data2/subdata2/*.txt exclude */data*/subdata*/*_exclude_from_manifest.txt exclude MANIFEST.in scikit-build-0.18.1/tests/samples/test-include-exclude-data/hello/000077500000000000000000000000001466366435500250555ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data/hello/__init__.py000066400000000000000000000000001466366435500271540ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data/hello/data/000077500000000000000000000000001466366435500257665ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data/hello/data/subdata/000077500000000000000000000000001466366435500274115ustar00rootroot00000000000000hello_data1_include_from_manifest.txt000066400000000000000000000000271466366435500366630ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data/hello/data/subdatathis is just data file hello_data2_include_from_manifest.txt000066400000000000000000000000001466366435500366530ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data/hello/data/subdatahello_data3_include_from_manifest_and_exclude_from_manifest.txt000066400000000000000000000000001466366435500441200ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data/hello/data/subdatahello_data4_include_from_manifest_and_exclude_from_setup.txt000066400000000000000000000000001466366435500434530ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data/hello/data/subdatascikit-build-0.18.1/tests/samples/test-include-exclude-data/hello/hello_include_from_manifest.txt000066400000000000000000000000001466366435500333230ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data/hello2/000077500000000000000000000000001466366435500251375ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data/hello2/__init__.py000066400000000000000000000000001466366435500272360ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data/hello2/data2/000077500000000000000000000000001466366435500261325ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data/hello2/data2/subdata2/000077500000000000000000000000001466366435500276375ustar00rootroot00000000000000hello2_data1_include_from_manifest.txt000066400000000000000000000000271466366435500371730ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data/hello2/data2/subdata2this is just data file hello2_data2_include_from_manifest.txt000066400000000000000000000000001466366435500371630ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data/hello2/data2/subdata2hello2_data3_include_from_manifest_and_exclude_from_manifest.txt000066400000000000000000000000001466366435500444300ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data/hello2/data2/subdata2hello2_data4_include_from_manifest_and_exclude_from_setup.txt000066400000000000000000000000001466366435500437630ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data/hello2/data2/subdata2scikit-build-0.18.1/tests/samples/test-include-exclude-data/hello2/hello2_include_from_manifest.txt000066400000000000000000000000001466366435500334670ustar00rootroot00000000000000scikit-build-0.18.1/tests/samples/test-include-exclude-data/setup.py000066400000000000000000000012311466366435500254610ustar00rootroot00000000000000from __future__ import annotations from skbuild import setup setup( name="test_include_exclude_data", version="0.1.0", cmake_languages=(), packages=["hello", "hello2", "hello.data.subdata", "hello2.data2.subdata2"], include_package_data=True, exclude_package_data={ "hello.data.subdata": [ "*_data4_cmake_generated_and_exclude_from_setup.txt", "*data4_include_from_manifest_and_exclude_from_setup.txt", ], "hello2.data2.subdata2": [ "*_data4_cmake_generated_and_exclude_from_setup.txt", "*_data4_include_from_manifest_and_exclude_from_setup.txt", ], }, ) scikit-build-0.18.1/tests/test_broken_project.py000066400000000000000000000110041466366435500217630ustar00rootroot00000000000000"""test_broken_cmakelists ---------------------------------- Tries to build the `fail-with-*-cmakelists` sample projects. Ensures that the attempt fails with a SystemExit exception that has an SKBuildError exception as its value. """ from __future__ import annotations from subprocess import CalledProcessError, run import pytest from skbuild.constants import CMAKE_DEFAULT_EXECUTABLE from skbuild.exceptions import SKBuildError from skbuild.platform_specifics import CMakeGenerator, get_platform from skbuild.utils import push_dir from . import project_setup_py_test, push_env def test_cmakelists_with_fatalerror_fails(capfd): with push_dir(): @project_setup_py_test("fail-with-fatal-error-cmakelists", ["build"], disable_languages_test=True) def should_fail(): pass with pytest.raises(SystemExit) as excinfo: should_fail() e = excinfo.value assert isinstance(e.code, SKBuildError) _, err = capfd.readouterr() assert "Invalid CMakeLists.txt" in err assert "An error occurred while configuring with CMake." in str(e) def test_cmakelists_with_syntaxerror_fails(capfd): with push_dir(): @project_setup_py_test("fail-with-syntax-error-cmakelists", ["build"], disable_languages_test=True) def should_fail(): pass with pytest.raises(SystemExit) as excinfo: should_fail() e = excinfo.value assert isinstance(e.code, SKBuildError) _, err = capfd.readouterr() assert 'Parse error. Function missing ending ")"' in err assert "An error occurred while configuring with CMake." in str(e) def test_hello_with_compileerror_fails(capfd): with push_dir(): @project_setup_py_test("fail-hello-with-compile-error", ["build"]) def should_fail(): pass with pytest.raises(SystemExit) as excinfo: should_fail() e = excinfo.value assert isinstance(e.code, SKBuildError) out, err = capfd.readouterr() assert "_hello.cxx" in out or "_hello.cxx" in err assert "An error occurred while building with CMake." in str(e) @pytest.mark.parametrize("exception", [CalledProcessError, OSError]) def test_invalid_cmake(exception, mocker): exceptions = { OSError: OSError("Unknown error"), CalledProcessError: CalledProcessError(1, [CMAKE_DEFAULT_EXECUTABLE, "--version"]), } run_original = run def run_mock(*args, **kwargs): if args[0] == [CMAKE_DEFAULT_EXECUTABLE, "--version"]: raise exceptions[exception] return run_original(*args, **kwargs) mocker.patch("skbuild.cmaker.subprocess.run", new=run_mock) with push_dir(): @project_setup_py_test("hello-no-language", ["build"], disable_languages_test=True) def should_fail(): pass with pytest.raises(SystemExit) as excinfo: should_fail() e = excinfo.value assert isinstance(e.code, SKBuildError) assert "Problem with the CMake installation, aborting build." in str(e) def test_first_invalid_generator(mocker, capfd): platform = get_platform() default_generators = [CMakeGenerator("Invalid")] default_generators.extend(platform.default_generators) mocker.patch.object( type(platform), "default_generators", new_callable=mocker.PropertyMock, return_value=default_generators ) mocker.patch("skbuild.cmaker.get_platform", return_value=platform) with push_dir(), push_env(CMAKE_GENERATOR=None): @project_setup_py_test("hello-no-language", ["build"]) def run_build(): pass run_build() _, err = capfd.readouterr() assert "CMake Error: Could not create named generator Invalid" in err def test_invalid_generator(mocker, capfd): platform = get_platform() mocker.patch.object( type(platform), "default_generators", new_callable=mocker.PropertyMock, return_value=[CMakeGenerator("Invalid")] ) mocker.patch("skbuild.cmaker.get_platform", return_value=platform) with push_dir(), push_env(CMAKE_GENERATOR=None): @project_setup_py_test("hello-no-language", ["build"]) def should_fail(): pass with pytest.raises(SystemExit) as excinfo: should_fail() e = excinfo.value assert isinstance(e.code, SKBuildError) _, err = capfd.readouterr() assert "CMake Error: Could not create named generator Invalid" in err assert "scikit-build could not get a working generator for your system. Aborting build." in str(e) scikit-build-0.18.1/tests/test_cmake_target.py000066400000000000000000000010701466366435500214050ustar00rootroot00000000000000"""test_cmake_target ---------------------------------- Tries to build and test the `test-cmake-target` sample project. It basically checks that using the `cmake_target` keyword in setup.py works. """ from __future__ import annotations from . import project_setup_py_test @project_setup_py_test("test-cmake-target", ["build"], disable_languages_test=True) def test_cmake_target_build(capsys): out, err = capsys.readouterr() dist_warning = "Unknown distribution option: 'cmake_target'" assert dist_warning not in err assert dist_warning not in out scikit-build-0.18.1/tests/test_cmakelists_not_in_top_level_dir.py000066400000000000000000000047471466366435500254110ustar00rootroot00000000000000"""test_cmakelists_not_in_top_level_dir ---------------------------------- Tries to build and test the `cmakelists_not_in_top_level_dir` sample project. It basically checks that using the `cmake_source_dir` setup keyword works. """ from __future__ import annotations import glob import textwrap import pytest from skbuild.exceptions import SKBuildError from . import _tmpdir, execute_setup_py, project_setup_py_test from .pytest_helpers import check_sdist_content @project_setup_py_test("cmakelists-not-in-top-level-dir", ["build"], disable_languages_test=True) def test_build(capsys): out, err = capsys.readouterr() dist_warning = "Unknown distribution option: 'cmake_source_dir'" assert dist_warning not in err assert dist_warning not in out @pytest.mark.parametrize( ("cmake_source_dir", "expected_failed"), [ ("invalid", True), ("", False), (".", False), ], ) def test_cmake_source_dir(cmake_source_dir, expected_failed): tmp_dir = _tmpdir("test_cmake_source_dir") tmp_dir.join("setup.py").write( textwrap.dedent( f""" from skbuild import setup setup( name="test_cmake_source_dir", version="1.2.3", description="a minimal example package", author='The scikit-build team', license="MIT", cmake_source_dir="{cmake_source_dir}" ) """ ) ) failed = False message = "" try: with execute_setup_py(tmp_dir, ["build"], disable_languages_test=True): pass except SystemExit as e: failed = isinstance(e.code, SKBuildError) message = str(e) assert failed == expected_failed if failed: assert "'cmake_source_dir' set to a nonexistent directory." in message @project_setup_py_test("cmakelists-not-in-top-level-dir", ["sdist"], disable_languages_test=True) def test_hello_sdist(): sdists_tar = glob.glob("dist/*.tar.gz") sdists_zip = glob.glob("dist/*.zip") assert sdists_tar or sdists_zip expected_content = [ "hello-1.2.3/hello/_hello.cxx", "hello-1.2.3/hello/CMakeLists.txt", "hello-1.2.3/hello/__init__.py", "hello-1.2.3/hello/__main__.py", "hello-1.2.3/setup.py", ] sdist_archive = None if sdists_tar: sdist_archive = "dist/hello-1.2.3.tar.gz" elif sdists_zip: sdist_archive = "dist/hello-1.2.3.zip" check_sdist_content(sdist_archive, "hello-1.2.3", expected_content) scikit-build-0.18.1/tests/test_cmaker.py000066400000000000000000000213001466366435500202170ustar00rootroot00000000000000"""test_cmaker ---------------------------------- Tests for CMaker functionality. """ from __future__ import annotations import os import re import textwrap import pytest from skbuild.cmaker import CMaker, has_cmake_cache_arg from skbuild.constants import ( CMAKE_BUILD_DIR, CMAKE_DEFAULT_EXECUTABLE, CMAKE_INSTALL_DIR, ) from skbuild.exceptions import SKBuildError from skbuild.utils import push_dir, to_unix_path from . import _tmpdir, get_cmakecache_variables def test_get_python_version(): assert re.match(r"^[23](\.?)\d+$", CMaker.get_python_version()) def test_get_python_include_dir(): python_include_dir = CMaker.get_python_include_dir(CMaker.get_python_version()) assert python_include_dir assert os.path.exists(python_include_dir) def test_get_python_library(): python_library = CMaker.get_python_library(CMaker.get_python_version()) assert python_library assert os.path.exists(python_library) def test_cmake_executable(): assert CMaker().cmake_executable == CMAKE_DEFAULT_EXECUTABLE def test_has_cmake_cache_arg(): cmake_args = ["-DFOO:STRING=42", "-DBAR", "-DCLIMBING:BOOL=ON"] assert has_cmake_cache_arg(cmake_args, "FOO", "42") assert not has_cmake_cache_arg(cmake_args, "foo", "42") assert not has_cmake_cache_arg(cmake_args, "FOO", "43") assert not has_cmake_cache_arg(cmake_args, "BAR") assert not has_cmake_cache_arg(cmake_args, "BA") assert not has_cmake_cache_arg(cmake_args, "BAR", None) assert not has_cmake_cache_arg(cmake_args, "BAR", "42") assert has_cmake_cache_arg(cmake_args, "CLIMBING") assert has_cmake_cache_arg(cmake_args, "CLIMBING", None) assert has_cmake_cache_arg(cmake_args, "CLIMBING", "ON") override = ["-DOTHER:STRING=C", "-DOVERRIDE:STRING=A", "-DOVERRIDE:STRING=B"] assert has_cmake_cache_arg(override, "OVERRIDE") assert has_cmake_cache_arg(override, "OVERRIDE", "B") assert not has_cmake_cache_arg(override, "OVERRIDE", "A") # ensure overriding doesn't magically have side effects. assert has_cmake_cache_arg(override, "OTHER") assert has_cmake_cache_arg(override, "OTHER", "C") assert not has_cmake_cache_arg(override, "OTHER", "A") assert not has_cmake_cache_arg(override, "OTHER", "B") def test_make_without_build_dir_fails(): src_dir = _tmpdir("test_make_without_build_dir_fails") with push_dir(str(src_dir)), pytest.raises(SKBuildError) as excinfo: CMaker().make() assert "Did you forget to run configure before make" in str(excinfo.value) def test_make_without_configure_fails(capfd): src_dir = _tmpdir("test_make_without_configure_fails") src_dir.ensure(CMAKE_BUILD_DIR(), dir=1) with push_dir(str(src_dir)), pytest.raises(SKBuildError) as excinfo: CMaker().make() _, err = capfd.readouterr() assert "An error occurred while building with CMake." in str(excinfo.value) assert "Error: could not load cache" in err @pytest.mark.parametrize("configure_with_cmake_source_dir", [True, False]) def test_make(configure_with_cmake_source_dir, capfd): tmp_dir = _tmpdir("test_make") with push_dir(str(tmp_dir)): src_dir = tmp_dir.ensure("SRC", dir=1) src_dir.join("CMakeLists.txt").write( textwrap.dedent( """ cmake_minimum_required(VERSION 3.5.0) project(foobar NONE) file(WRITE "${CMAKE_BINARY_DIR}/foo.txt" "# foo") install(FILES "${CMAKE_BINARY_DIR}/foo.txt" DESTINATION ".") install(CODE "message(STATUS \\"Project has been installed\\")") message(STATUS "CMAKE_SOURCE_DIR:${CMAKE_SOURCE_DIR}") message(STATUS "CMAKE_BINARY_DIR:${CMAKE_BINARY_DIR}") """ ) ) src_dir.ensure(CMAKE_BUILD_DIR(), dir=1) with push_dir(str(src_dir) if not configure_with_cmake_source_dir else str(tmp_dir.ensure("BUILD", dir=1))): cmkr = CMaker() config_kwargs = {} if configure_with_cmake_source_dir: config_kwargs["cmake_source_dir"] = str(src_dir) env = cmkr.configure(**config_kwargs) # type: ignore[arg-type] cmkr.make(env=env) messages = ["Project has been installed"] if configure_with_cmake_source_dir: messages += [ "/SRC", f"/BUILD/{to_unix_path(CMAKE_BUILD_DIR())}", f"/BUILD/{to_unix_path(CMAKE_INSTALL_DIR())}/./foo.txt", ] else: messages += [ "/SRC", f"/SRC/{to_unix_path(CMAKE_BUILD_DIR())}", f"/SRC/{to_unix_path(CMAKE_INSTALL_DIR())}/./foo.txt", ] out, _ = capfd.readouterr() for message in messages: assert message in out @pytest.mark.parametrize("install_target", ["", "install", "install-runtime", "nonexistant-install-target"]) def test_make_with_install_target(install_target, capfd): tmp_dir = _tmpdir("test_make_with_install_target") with push_dir(str(tmp_dir)): tmp_dir.join("CMakeLists.txt").write( textwrap.dedent( """ cmake_minimum_required(VERSION 3.5.0) project(foobar NONE) file(WRITE "${CMAKE_BINARY_DIR}/foo.txt" "# foo") file(WRITE "${CMAKE_BINARY_DIR}/runtime.txt" "# runtime") install(FILES "${CMAKE_BINARY_DIR}/foo.txt" DESTINATION ".") install(CODE "message(STATUS \\"Project has been installed\\")") install(FILES "${CMAKE_BINARY_DIR}/runtime.txt" DESTINATION "." COMPONENT runtime) install(CODE "message(STATUS \\"Runtime component has been installed\\")" COMPONENT runtime) # Add custom target to only install component: runtime (libraries) add_custom_target(install-runtime ${CMAKE_COMMAND} -DCMAKE_INSTALL_COMPONENT=runtime -P "${PROJECT_BINARY_DIR}/cmake_install.cmake" ) """ ) ) with push_dir(str(tmp_dir)): cmkr = CMaker() env = cmkr.configure() if install_target in ["", "install", "install-runtime"]: cmkr.make(install_target=install_target, env=env) else: with pytest.raises(SKBuildError) as excinfo: cmkr.make(install_target=install_target, env=env) assert "check the install target is valid" in str(excinfo.value) out, err = capfd.readouterr() # This message appears with both install_targets: default 'install' and # 'install-runtime' message = "Runtime component has been installed" if install_target in ["install", "install-runtime"]: assert message in out # One of these error appears with install_target: nonexistant-install-target err_message1 = "No rule to make target" err_message2 = "unknown target" err_message3 = "CMAKE_MAKE_PROGRAM is not set" # Showing up on windows-2016 if install_target == "nonexistant-install-target": assert err_message1 in err or err_message2 in err or err_message3 in err def test_configure_with_cmake_args(capfd): tmp_dir = _tmpdir("test_configure_with_cmake_args") with push_dir(str(tmp_dir)): tmp_dir.join("CMakeLists.txt").write( textwrap.dedent( """ cmake_minimum_required(VERSION 3.5.0) project(foobar NONE) # Do not complain about missing arguments passed to the main # project """ ) ) with push_dir(str(tmp_dir)): cmkr = CMaker() cmkr.configure(clargs=["-DCMAKE_EXPECTED_FOO:STRING=foo", "-DCMAKE_EXPECTED_BAR:STRING=bar"], cleanup=False) cmakecache = tmp_dir.join("_cmake_test_compile", "build", "CMakeCache.txt") assert cmakecache.exists() variables = get_cmakecache_variables(str(cmakecache)) assert variables.get("CMAKE_EXPECTED_FOO", (None, None))[1] == "foo" assert variables.get("CMAKE_EXPECTED_BAR", (None, None))[1] == "bar" unexpected = "Manually-specified variables were not used by the project" _, err = capfd.readouterr() assert unexpected not in err def test_check_for_bad_installs(tmpdir): with push_dir(str(tmpdir)): tmpdir.ensure(CMAKE_BUILD_DIR(), "cmake_install.cmake").write( textwrap.dedent( """ file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/../hello" TYPE FILE FILES "/path/to/hello/world.py") """ ) ) with pytest.raises(SKBuildError) as excinfo: CMaker.check_for_bad_installs() assert "CMake-installed files must be within the project root" in str(excinfo.value) scikit-build-0.18.1/tests/test_command_line.py000066400000000000000000000120721466366435500214100ustar00rootroot00000000000000"""test_command_line ---------------------------------- Tests for various command line functionality. """ from __future__ import annotations import os import pytest from skbuild.constants import CMAKE_BUILD_DIR from skbuild.exceptions import SKBuildError from skbuild.utils import push_dir, to_platform_path from . import ( execute_setup_py, get_cmakecache_variables, initialize_git_repo_and_commit, prepare_project, project_setup_py_test, ) @project_setup_py_test("hello-no-language", ["--help"], disable_languages_test=True) def test_help(capsys): out, err = capsys.readouterr() assert "scikit-build options" not in out assert "Global options:" in out assert "usage:" in out @project_setup_py_test("hello-no-language", ["--help-commands"], disable_languages_test=True) def test_help_commands(capsys): out, err = capsys.readouterr() assert "scikit-build options" in out assert "--build-type" in out assert "Global options:" not in out assert "usage:" in out @project_setup_py_test("hello-no-language", ["--author", "--name"], disable_languages_test=True) def test_metadata_display(capsys): out, err = capsys.readouterr() assert "scikit-build options" not in out assert "Global options:" not in out assert "usage:" not in out assert out.splitlines()[0] == "The scikit-build team" assert out.splitlines()[1] == "hello_no_language" def test_no_command(): with push_dir(): @project_setup_py_test("hello-no-language", [], disable_languages_test=True) def run(): pass failed = False try: run() except SystemExit as e: failed = "error: no commands supplied" in e.args[0] assert failed assert not os.path.exists("_skbuild") def test_invalid_command(): with push_dir(): @project_setup_py_test("hello-no-language", ["unknown"], disable_languages_test=True) def run(): pass failed = False try: run() except SystemExit as e: failed = "error: invalid command" in e.args[0] assert failed assert not os.path.exists("_skbuild") def test_too_many_separators(): with push_dir(): @project_setup_py_test("hello-no-language", ["--"] * 3, disable_languages_test=True) def run(): pass failed = False try: run() except SystemExit as e: failed = e.args[0].startswith("ERROR: Too many") assert failed def test_cmake_initial_cache_as_global_option(tmpdir): project = "hello-no-language" prepare_project(project, tmpdir) initialize_git_repo_and_commit(tmpdir, verbose=True) initial_cache = tmpdir.join("initial-cache.txt") initial_cache.write("""set(MY_CMAKE_VARIABLE "1" CACHE BOOL "My cache variable")""") try: with execute_setup_py(tmpdir, [f"-C{initial_cache}", "build"], disable_languages_test=True): pass except SystemExit as exc: assert exc.code == 0 # noqa: PT017 cmakecache_txt = tmpdir.join(CMAKE_BUILD_DIR(), "CMakeCache.txt") assert cmakecache_txt.exists() assert get_cmakecache_variables(str(cmakecache_txt)).get("MY_CMAKE_VARIABLE", (None, None)) == ("BOOL", "1") def test_cmake_executable_arg(): cmake_executable = "/path/to/invalid/cmake" @project_setup_py_test( "hello-no-language", ["--cmake-executable", cmake_executable, "build"], disable_languages_test=True ) def should_fail(): pass failed = False message = "" try: should_fail() except SystemExit as e: failed = isinstance(e.code, SKBuildError) message = str(e) assert failed assert f"Problem with the CMake installation, aborting build. CMake executable is {cmake_executable}" in message @pytest.mark.parametrize("action", ["sdist", "bdist_wheel"]) @pytest.mark.parametrize("hide_listing", [True, False]) def test_hide_listing(action, hide_listing, capfd, caplog): cmd = [action] if hide_listing: cmd.insert(0, "--hide-listing") @project_setup_py_test("test-hide-listing", cmd, verbose_git=False, disable_languages_test=True) def run(): pass run() out, err = capfd.readouterr() out += err + caplog.text if hide_listing: assert to_platform_path("bonjour/__init__.py") not in out else: assert to_platform_path("bonjour/__init__.py") in out if action == "sdist": assert "copied 15 files" in out elif action == "bdist_wheel": assert "copied 6 files" in out # build_py assert "copied 9 files" in out # install_lib assert "copied 0 files" in out # install_scripts @project_setup_py_test("hello-no-language", ["--force-cmake", "--help"], disable_languages_test=True) def test_run_cmake_arg(capfd): out, _ = capfd.readouterr() assert "Generating done" in out @project_setup_py_test("hello-no-language", ["--skip-cmake", "build"], disable_languages_test=True) def test_skip_cmake_arg(capfd): out, _ = capfd.readouterr() assert "Generating done" not in out scikit-build-0.18.1/tests/test_constants.py000066400000000000000000000007001466366435500207720ustar00rootroot00000000000000from __future__ import annotations import os import sys from skbuild.constants import SKBUILD_DIR, set_skbuild_plat_name, skbuild_plat_name def test_set_skbuild_plat_name(): try: previous_plat_name = skbuild_plat_name() set_skbuild_plat_name("plat-name") assert os.path.join("_skbuild", "plat-name-{}.{}".format(*sys.version_info[:2])) == SKBUILD_DIR() finally: set_skbuild_plat_name(previous_plat_name) scikit-build-0.18.1/tests/test_cython_flags.py000066400000000000000000000004251466366435500214420ustar00rootroot00000000000000"""test_cython_flags ---------------------------------- Tries to build the `cython-flags` sample project. """ from __future__ import annotations from . import project_setup_py_test @project_setup_py_test("cython-flags", ["build"]) def test_hello_cython_builds(): pass scikit-build-0.18.1/tests/test_distribution.py000066400000000000000000000024231466366435500215010ustar00rootroot00000000000000from __future__ import annotations from pathlib import Path import pytest from . import initialize_git_repo_and_commit, prepare_project DIR = Path(__file__).parent.resolve() @pytest.mark.isolated() def test_source_distribution(isolated, tmp_path): sdist_dir = tmp_path / "dist" workspace = tmp_path / "workspace" workspace.mkdir() isolated.install("build[virtualenv]") isolated.module("build", "--sdist", "--outdir", sdist_dir, cwd=DIR.parent) (sdist,) = sdist_dir.glob("*.tar.gz") isolated.install(sdist) prepare_project("hello-no-language", str(workspace), force=True) initialize_git_repo_and_commit(str(workspace), verbose=False) isolated.run("python", "setup.py", "bdist_wheel", cwd=workspace) @pytest.mark.isolated() def test_wheel(isolated, tmp_path): wheel_dir = tmp_path / "dist" workspace = tmp_path / "workspace" workspace.mkdir() isolated.install("build[virtualenv]") isolated.module("build", "--wheel", "--outdir", wheel_dir, cwd=DIR.parent) (wheel,) = wheel_dir.glob("*.whl") isolated.install(wheel) prepare_project("hello-no-language", str(workspace), force=True) initialize_git_repo_and_commit(str(workspace), verbose=False) isolated.run("python", "setup.py", "bdist_wheel", cwd=workspace) scikit-build-0.18.1/tests/test_filter_manifest.py000066400000000000000000000015761466366435500221450ustar00rootroot00000000000000from __future__ import annotations import glob from . import _tmpdir, execute_setup_py, initialize_git_repo_and_commit, prepare_project from .pytest_helpers import check_wheel_content def test_bdist_wheel_command(): project = "test-filter-manifest" expected_content = [ "hello/__init__.py", "hello/swig_mwe.py", "hello/_swig_mwe.pyd", "hello-1.2.3.data/data/bin/hello", ] expected_distribution_name = "hello-1.2.3" tmp_dir = _tmpdir("test_bdist_wheel_command") prepare_project(project, tmp_dir) initialize_git_repo_and_commit(tmp_dir, verbose=True) relative_setup_path = "wrapping/python/" with execute_setup_py(tmp_dir.join(relative_setup_path), ["bdist_wheel"]): whls = glob.glob("dist/*.whl") assert len(whls) == 1 check_wheel_content(whls[0], expected_distribution_name, expected_content) scikit-build-0.18.1/tests/test_hello_cpp.py000066400000000000000000000130761466366435500207350ustar00rootroot00000000000000"""test_hello_cpp ---------------------------------- Tries to build and test the `hello-cpp` sample project. """ from __future__ import annotations import glob import os import pytest from skbuild.constants import CMAKE_BUILD_DIR, SKBUILD_DIR from skbuild.utils import push_dir from . import SAMPLES_DIR, _copy_dir, _tmpdir, get_ext_suffix, project_setup_py_test from .pytest_helpers import check_sdist_content, check_wheel_content def test_hello_builds(): with push_dir(): @project_setup_py_test("hello-cpp", ["build"], ret=True) def run(): pass # Check that a project can be build twice in a row # See issue scikit-build#120 tmp_dir = run()[0] @project_setup_py_test("hello-cpp", ["build"], tmp_dir=tmp_dir) def another_run(): pass another_run() @project_setup_py_test("hello-cpp", ["sdist"]) def test_hello_sdist(): sdists_tar = glob.glob("dist/*.tar.gz") sdists_zip = glob.glob("dist/*.zip") assert sdists_tar or sdists_zip expected_content = [ "hello-1.2.3/CMakeLists.txt", "hello-1.2.3/bonjour/__init__.py", "hello-1.2.3/bonjour/data/ciel.txt", "hello-1.2.3/bonjour/data/soleil.txt", "hello-1.2.3/bonjour/data/terre.txt", "hello-1.2.3/bonjourModule.py", "hello-1.2.3/hello/_hello.cxx", "hello-1.2.3/hello/CMakeLists.txt", "hello-1.2.3/hello/__init__.py", "hello-1.2.3/hello/__main__.py", "hello-1.2.3/setup.py", ] sdist_archive = "dist/hello-1.2.3.zip" if sdists_tar: sdist_archive = "dist/hello-1.2.3.tar.gz" check_sdist_content(sdist_archive, "hello-1.2.3", expected_content) def test_hello_wheel(): expected_content = [ f"hello/_hello{get_ext_suffix()}", "hello/__init__.py", "hello/__main__.py", "hello/world.py", "helloModule.py", "bonjour/__init__.py", "bonjour/data/ciel.txt", "bonjour/data/soleil.txt", "bonjour/data/terre.txt", "bonjourModule.py", ] expected_distribution_name = "hello-1.2.3" @project_setup_py_test("hello-cpp", ["bdist_wheel"], ret=True) def build_wheel(): whls = glob.glob("dist/*.whl") assert len(whls) == 1 check_wheel_content(whls[0], expected_distribution_name, expected_content) os.remove(whls[0]) assert not os.path.exists(whls[0]) assert os.path.exists(os.path.join(CMAKE_BUILD_DIR(), "CMakeCache.txt")) os.remove(os.path.join(CMAKE_BUILD_DIR(), "CMakeCache.txt")) tmp_dir = build_wheel()[0] @project_setup_py_test("hello-cpp", ["--skip-cmake", "bdist_wheel"], tmp_dir=tmp_dir) def build_wheel_skip_cmake(): assert not os.path.exists(os.path.join(CMAKE_BUILD_DIR(), "CMakeCache.txt")) whls = glob.glob("dist/*.whl") assert len(whls) == 1 check_wheel_content(whls[0], expected_distribution_name, expected_content) build_wheel_skip_cmake() @pytest.mark.parametrize("dry_run", ["with-dry-run", "without-dry-run"]) def test_hello_clean(dry_run, capfd): with push_dir(): dry_run = dry_run == "with-dry-run" @project_setup_py_test("hello-cpp", ["build"], ret=True) def run_build(): pass tmp_dir = run_build()[0] assert tmp_dir.join(SKBUILD_DIR()).exists() # XXX Since using capfd.disabled() context manager prevents # the output from being captured atfer it exits, we display # a separator allowing to differentiate the build and clean output. print("<<-->>") clean_args = ["clean"] if dry_run: clean_args.append("--dry-run") @project_setup_py_test("hello-cpp", clean_args, tmp_dir=tmp_dir) def run_clean(): pass run_clean() if not dry_run: assert not tmp_dir.join(SKBUILD_DIR()).exists() else: assert tmp_dir.join(SKBUILD_DIR()).exists() build_out, clean_out = capfd.readouterr()[0].split("<<-->>") assert "Build files have been written to" in build_out assert "Build files have been written to" not in clean_out def test_hello_cleans(capfd, caplog): with push_dir(): tmp_dir = _tmpdir("test_hello_cleans") _copy_dir(tmp_dir, os.path.join(SAMPLES_DIR, "hello-cpp")) @project_setup_py_test("hello-cpp", ["build"], tmp_dir=tmp_dir) def run_build(): pass @project_setup_py_test("hello-cpp", ["clean"], tmp_dir=tmp_dir) def run_clean(): pass # Check that a project can be cleaned twice in a row run_build() capfd.readouterr() caplog.clear() run_clean() txt1 = caplog.text msg = capfd.readouterr().out + txt1 assert "running clean" in msg caplog.clear() run_clean() txt2 = caplog.text msg = capfd.readouterr().out + txt2 assert "running clean" in msg @pytest.mark.deprecated() @project_setup_py_test("hello-cpp", ["develop"]) def test_hello_develop(): for expected_file in [ # These files are the "regular" source files "setup.py", "CMakeLists.txt", "bonjour/__init__.py", "bonjourModule.py", "hello/__init__.py", "hello/__main__.py", "hello/_hello.cxx", "hello/CMakeLists.txt", # These files are "generated" by CMake and # are copied from CMAKE_INSTALL_DIR f"hello/_hello{get_ext_suffix()}", "hello/world.py", "helloModule.py", ]: assert os.path.exists(expected_file) scikit-build-0.18.1/tests/test_hello_cython.py000066400000000000000000000034761466366435500214620ustar00rootroot00000000000000"""test_hello_cython ---------------------------------- Tries to build and test the `hello-cython` sample project. """ from __future__ import annotations import glob import pytest from . import get_ext_suffix, project_setup_py_test from .pytest_helpers import check_sdist_content, check_wheel_content pytestmark = pytest.mark.filterwarnings( "ignore:.*ends with a trailing slash, which is not supported by setuptools:FutureWarning" ) @project_setup_py_test("hello-cython", ["build"]) def test_hello_cython_builds(): pass @project_setup_py_test("hello-cython", ["sdist"]) def test_hello_cython_sdist(): sdists_tar = glob.glob("dist/*.tar.gz") sdists_zip = glob.glob("dist/*.zip") assert sdists_tar or sdists_zip dirname = "hello-cython-1.2.3" # setuptools 69.3.0 and above now canonicalize the filename as well. if any("hello_cython" in x for x in sdists_zip + sdists_tar): dirname = "hello_cython-1.2.3" expected_content = [ f"{dirname}/CMakeLists.txt", f"{dirname}/hello/_hello.pyx", f"{dirname}/hello/CMakeLists.txt", f"{dirname}/hello/__init__.py", f"{dirname}/hello/__main__.py", f"{dirname}/setup.py", ] sdist_archive = f"dist/{dirname}.zip" if sdists_tar: sdist_archive = f"dist/{dirname}.tar.gz" check_sdist_content(sdist_archive, dirname, expected_content, package_dir="hello") @project_setup_py_test("hello-cython", ["bdist_wheel"]) def test_hello_cython_wheel(): expected_content = [ f"hello_cython/_hello{get_ext_suffix()}", "hello_cython/__init__.py", "hello_cython/__main__.py", ] expected_distribution_name = "hello_cython-1.2.3" whls = glob.glob("dist/*.whl") assert len(whls) == 1 check_wheel_content(whls[0], expected_distribution_name, expected_content) scikit-build-0.18.1/tests/test_hello_fortran.py000066400000000000000000000045101466366435500216170ustar00rootroot00000000000000"""test_hello_fortran --------------------- Tries to build and test the `hello-fortran` sample project. """ from __future__ import annotations import glob import os import shutil import sys import pytest from . import get_ext_suffix, project_setup_py_test from .pytest_helpers import check_sdist_content, check_wheel_content pytest.importorskip("numpy") @pytest.mark.fortran() @pytest.mark.skipif(sys.platform.startswith("win"), reason="Fortran not supported on Windows") @pytest.mark.skipif(not ("FC" in os.environ or shutil.which("gfortran")), reason="GFortran required") @project_setup_py_test("hello-fortran", ["build"]) def test_hello_fortran_build(): pass @pytest.mark.fortran() @project_setup_py_test("hello-fortran", ["sdist"]) def test_hello_fortran_sdist(): sdists_tar = glob.glob("dist/*.tar.gz") sdists_zip = glob.glob("dist/*.zip") assert sdists_tar or sdists_zip dirname = "hello-fortran-1.2.3" # setuptools 69.3.0 and above now canonicalize the filename as well. if any("hello_fortran" in x for x in sdists_zip + sdists_tar): dirname = "hello_fortran-1.2.3" expected_content = [ f"{dirname}/bonjour/_bonjour.f90", f"{dirname}/bonjour/_bonjour.pyf", f"{dirname}/bonjour/CMakeLists.txt", f"{dirname}/CMakeLists.txt", f"{dirname}/hello/_hello.f90", f"{dirname}/hello/CMakeLists.txt", f"{dirname}/hello/__init__.py", f"{dirname}/hello/__main__.py", f"{dirname}/setup.py", ] sdist_archive = f"dist/{dirname}.zip" if sdists_tar: sdist_archive = f"dist/{dirname}.tar.gz" check_sdist_content(sdist_archive, dirname, expected_content) @pytest.mark.fortran() @pytest.mark.skipif(sys.platform.startswith("win"), reason="Fortran not supported on Windows") @pytest.mark.skipif(not ("FC" in os.environ or shutil.which("gfortran")), reason="GFortran required") @project_setup_py_test("hello-fortran", ["bdist_wheel"]) def test_hello_fortran_wheel(): expected_content = [ f"hello/_bonjour{get_ext_suffix()}", f"hello/_hello{get_ext_suffix()}", "hello/__init__.py", "hello/__main__.py", ] expected_distribution_name = "hello_fortran-1.2.3" whls = glob.glob("dist/*.whl") assert len(whls) == 1 check_wheel_content(whls[0], expected_distribution_name, expected_content) scikit-build-0.18.1/tests/test_hello_pure.py000066400000000000000000000043001466366435500211140ustar00rootroot00000000000000"""test_hello_pure ---------------------------------- Tries to build and test the `hello-pure` sample project. """ from __future__ import annotations import glob from skbuild.constants import SKBUILD_DIR from skbuild.utils import push_dir from . import project_setup_py_test from .pytest_helpers import check_sdist_content, check_wheel_content @project_setup_py_test("hello-pure", ["build"], disable_languages_test=True) def test_hello_pure_builds(capsys): out, _ = capsys.readouterr() assert "skipping skbuild (no CMakeLists.txt found)" in out @project_setup_py_test("hello-pure", ["sdist"], disable_languages_test=True) def test_hello_pure_sdist(): sdists_tar = glob.glob("dist/*.tar.gz") sdists_zip = glob.glob("dist/*.zip") assert sdists_tar or sdists_zip dirname = "hello-pure-1.2.3" # setuptools 69.3.0 and above now canonicalize the filename as well. if any("hello_pure" in x for x in sdists_zip + sdists_tar): dirname = "hello_pure-1.2.3" expected_content = [ f"{dirname}/hello/__init__.py", f"{dirname}/setup.py", ] sdist_archive = f"dist/{dirname}.zip" if sdists_tar: sdist_archive = f"dist/{dirname}.tar.gz" check_sdist_content(sdist_archive, dirname, expected_content) @project_setup_py_test("hello-pure", ["bdist_wheel"], disable_languages_test=True) def test_hello_pure_wheel(): expected_content = ["hello/__init__.py"] expected_distribution_name = "hello_pure-1.2.3" whls = glob.glob("dist/*.whl") assert len(whls) == 1 check_wheel_content(whls[0], expected_distribution_name, expected_content, pure=True) def test_hello_clean(capfd): with push_dir(): @project_setup_py_test("hello-pure", ["build"], disable_languages_test=True, ret=True) def run_build(): pass tmp_dir = run_build()[0] assert tmp_dir.join(SKBUILD_DIR()).exists() @project_setup_py_test("hello-pure", ["clean"], tmp_dir=tmp_dir, disable_languages_test=True) def run_clean(): pass run_clean() assert not tmp_dir.join(SKBUILD_DIR()).exists() out = capfd.readouterr()[0] assert "Build files have been written to" not in out scikit-build-0.18.1/tests/test_include_exclude_data.py000066400000000000000000000067731466366435500231230ustar00rootroot00000000000000from __future__ import annotations import glob import os import pytest from skbuild.utils import to_unix_path from . import project_setup_py_test from .pytest_helpers import check_sdist_content, check_wheel_content def check_whls(project_name): whls = glob.glob("dist/*.whl") assert len(whls) == 1 assert not whls[0].endswith("-none-any.whl") expected_content = [ "hello/__init__.py", "hello/cmake_generated_module.py", "hello/data/subdata/hello_data1_include_from_manifest.txt", "hello/data/subdata/hello_data2_include_from_manifest.txt", "hello/data/subdata/hello_data3_cmake_generated.txt", "hello/hello_data1_cmake_generated.txt", "hello/hello_data2_cmake_generated.txt", "hello/hello_include_from_manifest.txt", "hello2/__init__.py", "hello2/hello2_data1_cmake_generated.txt", "hello2/hello2_data2_cmake_generated.txt", "hello2/data2/subdata2/hello2_data3_cmake_generated.txt", "hello2/data2/subdata2/hello2_data1_include_from_manifest.txt", "hello2/data2/subdata2/hello2_data2_include_from_manifest.txt", "hello2/hello2_include_from_manifest.txt", ] expected_distribution_name = project_name check_wheel_content(whls[0], expected_distribution_name, expected_content) def check_sdist(proj, base=""): sdists_tar = glob.glob("dist/*.tar.gz") sdists_zip = glob.glob("dist/*.zip") assert sdists_tar or sdists_zip expected_content = [ to_unix_path(os.path.join(proj, "setup.py")), to_unix_path(os.path.join(proj, base, "hello/__init__.py")), to_unix_path(os.path.join(proj, base, "hello/data/subdata/hello_data1_include_from_manifest.txt")), to_unix_path(os.path.join(proj, base, "hello/data/subdata/hello_data2_include_from_manifest.txt")), to_unix_path( os.path.join(proj, base, "hello/data/subdata/hello_data4_include_from_manifest_and_exclude_from_setup.txt") ), to_unix_path(os.path.join(proj, base, "hello/hello_include_from_manifest.txt")), to_unix_path(os.path.join(proj, base, "hello2/__init__.py")), to_unix_path(os.path.join(proj, base, "hello2/data2/subdata2/hello2_data1_include_from_manifest.txt")), to_unix_path(os.path.join(proj, base, "hello2/data2/subdata2/hello2_data2_include_from_manifest.txt")), to_unix_path( os.path.join( proj, base, "hello2/data2/subdata2/hello2_data4_include_from_manifest_and_exclude_from_setup.txt" ) ), to_unix_path(os.path.join(proj, base, "hello2/hello2_include_from_manifest.txt")), ] sdist_archive = f"dist/{proj}.zip" if sdists_tar: sdist_archive = f"dist/{proj}.tar.gz" check_sdist_content(sdist_archive, proj, expected_content, package_dir=base) @project_setup_py_test("test-include-exclude-data", ["bdist_wheel"]) def test_include_exclude_data(): check_whls("test_include_exclude_data-0.1.0") @pytest.mark.nosetuptoolsscm() @project_setup_py_test("test-include-exclude-data", ["sdist"]) def test_hello_sdist(): check_sdist("test_include_exclude_data-0.1.0") @project_setup_py_test("test-include-exclude-data-with-base", ["bdist_wheel"]) def test_include_exclude_data_with_base(): check_whls("test_include_exclude_data_with_base-0.1.0") @pytest.mark.nosetuptoolsscm() @project_setup_py_test("test-include-exclude-data-with-base", ["sdist"]) def test_hello_sdist_with_base(): check_sdist("test_include_exclude_data_with_base-0.1.0", base="src") scikit-build-0.18.1/tests/test_issue274_support_default_package_dir.py000066400000000000000000000004131466366435500261550ustar00rootroot00000000000000from __future__ import annotations import pytest from . import ( project_setup_py_test, ) @pytest.mark.deprecated() @project_setup_py_test("issue-274-support-default-package-dir", ["install"], disable_languages_test=True) def test_install_command(): pass scikit-build-0.18.1/tests/test_issue274_support_one_package_without_package_dir.py000066400000000000000000000004271466366435500305550ustar00rootroot00000000000000from __future__ import annotations import pytest from . import ( project_setup_py_test, ) @pytest.mark.deprecated() @project_setup_py_test("issue-274-support-one-package-without-package-dir", ["install"], disable_languages_test=True) def test_install_command(): pass scikit-build-0.18.1/tests/test_issue284_build_ext_inplace.py000066400000000000000000000010521466366435500240770ustar00rootroot00000000000000from __future__ import annotations import os import platform import pytest from . import get_ext_suffix, project_setup_py_test @pytest.mark.skipif( platform.python_implementation() == "PyPy", reason="PyPy is reporting an empty linker, doesn't seem to be our fault" ) @project_setup_py_test("issue-284-build-ext-inplace", ["build_ext", "--inplace"], disable_languages_test=True) def test_build_ext_inplace_command(): assert os.path.exists(f"hello/_hello_sk{get_ext_suffix()}") assert os.path.exists(f"hello/_hello_ext{get_ext_suffix()}") scikit-build-0.18.1/tests/test_issue334_configure_cmakelists_non_cp1252_encoding.py000066400000000000000000000004161466366435500303400ustar00rootroot00000000000000from __future__ import annotations import pytest from . import project_setup_py_test @pytest.mark.deprecated() @project_setup_py_test("issue-334-configure-cmakelist-non-cp1252-encoding", ["install"], disable_languages_test=True) def test_install_command(): pass scikit-build-0.18.1/tests/test_issue335_support_cmake_source_dir.py000066400000000000000000000021241466366435500255150ustar00rootroot00000000000000from __future__ import annotations import glob from . import _tmpdir, execute_setup_py, initialize_git_repo_and_commit, prepare_project from .pytest_helpers import check_wheel_content def test_bdist_wheel_command(): project = "issue-335-support-cmake-source-dir" expected_content = [ "hello/__init__.py", "hello/swig_mwe.py", "hello/_swig_mwe.pyd", "hello-1.2.3.data/data/bin/hello", "hello-1.2.3.data/data/lib/static/libbar.a", "hello-1.2.3.data/data/lib/static/libfoo.a", "hello-1.2.3.data/data/include/bar.h", "hello-1.2.3.data/data/include/foo.h", ] expected_distribution_name = "hello-1.2.3" tmp_dir = _tmpdir("test_bdist_wheel_command") prepare_project(project, tmp_dir) initialize_git_repo_and_commit(tmp_dir, verbose=True) relative_setup_path = "wrapping/python/" with execute_setup_py(tmp_dir.join(relative_setup_path), ["bdist_wheel"]): whls = glob.glob("dist/*.whl") assert len(whls) == 1 check_wheel_content(whls[0], expected_distribution_name, expected_content) scikit-build-0.18.1/tests/test_issue342_cmake_osx_args_in_setup.py000066400000000000000000000137401466366435500253220ustar00rootroot00000000000000from __future__ import annotations import platform import sys import textwrap import pytest import skbuild.constants from . import _tmpdir, execute_setup_py, push_env params = ( "osx_deployment_target_env_var,cli_setup_args," "keyword_cmake_args,cli_cmake_args,expected_cmake_osx_deployment_target" ) @pytest.mark.parametrize( params, [ # default plat_name is 'macosx-10.9-x86_64' ( # osx_deployment_target_env_var None, # cli_setup_args [], # keyword_cmake_args [], # cli_cmake_args [], # expected_cmake_osx_deployment_target "10.9", ), ( # osx_deployment_target_env_var "10.7", # cli_setup_args [], # keyword_cmake_args [], # cli_cmake_args [], # expected_cmake_osx_deployment_target "10.7", ), ( # osx_deployment_target_env_var "10.7", # cli_setup_args ["--plat-name", "macosx-10.9-x86_64"], # keyword_cmake_args [], # cli_cmake_args [], # expected_cmake_osx_deployment_target "10.9", ), ( # osx_deployment_target_env_var None, # cli_setup_args ["--plat-name", "macosx-10.6-x86_64"], # keyword_cmake_args [], # cli_cmake_args [], # expected_cmake_osx_deployment_target "10.6", ), ( # osx_deployment_target_env_var None, # cli_setup_args ["--plat-name", "macosx-10.7-x86_64"], # keyword_cmake_args [], # cli_cmake_args [], # expected_cmake_osx_deployment_target "10.7", ), ( # osx_deployment_target_env_var None, # cli_setup_args [], # keyword_cmake_args ["-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.7"], # cli_cmake_args [], # expected_cmake_osx_deployment_target "10.7", ), ( # osx_deployment_target_env_var None, # cli_setup_args ["--plat-name", "macosx-10.12-x86_64"], # keyword_cmake_args ["-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.7"], # cli_cmake_args [], # expected_cmake_osx_deployment_target "10.7", ), ( # osx_deployment_target_env_var None, # cli_setup_args [], # keyword_cmake_args ["-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.7"], # cli_cmake_args ["-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.8"], # expected_cmake_osx_deployment_target "10.8", ), ( # osx_deployment_target_env_var None, # cli_setup_args ["--plat-name", "macosx-10.12-x86_64"], # keyword_cmake_args ["-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.7"], # cli_cmake_args ["-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.8"], # expected_cmake_osx_deployment_target "10.8", ), ], ) def test_cmake_args_keyword_osx_default( osx_deployment_target_env_var, cli_setup_args, keyword_cmake_args, cli_cmake_args, expected_cmake_osx_deployment_target, mocker, monkeypatch, ): tmp_dir = _tmpdir("cmake_args_keyword_osx_default") tmp_dir.join("setup.py").write( textwrap.dedent( """ from skbuild import setup setup( name="test_cmake_args_keyword_osx_default", version="1.2.3", description="A minimal example package", author="The scikit-build team", license="MIT", cmake_args=[{cmake_args}] ) """.format(cmake_args=",".join([f"'{arg}'" for arg in keyword_cmake_args])) ) ) tmp_dir.join("CMakeLists.txt").write( textwrap.dedent( """ message(FATAL_ERROR "This error message should not be displayed") """ ) ) mock_configure = mocker.patch("skbuild.cmaker.CMaker.configure", side_effect=RuntimeError("exit skbuild")) monkeypatch.setattr(platform, "mac_ver", lambda: ("10.9", None, "x84_64")) monkeypatch.setattr(platform, "machine", lambda: "x86_64") monkeypatch.setattr(sys, "platform", "darwin") with push_env(MACOSX_DEPLOYMENT_TARGET=osx_deployment_target_env_var): monkeypatch.setattr(skbuild.constants, "_SKBUILD_PLAT_NAME", skbuild.constants._default_skbuild_plat_name()) with pytest.raises(RuntimeError, match="exit skbuild"): with execute_setup_py(tmp_dir, ["build", *cli_setup_args, "--", *cli_cmake_args]): pass assert mock_configure.call_count == 1 current_cmake_args = mock_configure.call_args[0][0] # Since additional cmake argument are appended, it is not possible to simply # compare lists. found_cmake_osx_deployment_target = False for cmake_arg in reversed(current_cmake_args): if cmake_arg.startswith("-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING="): if cmake_arg.endswith(expected_cmake_osx_deployment_target): found_cmake_osx_deployment_target = True break assert found_cmake_osx_deployment_target, textwrap.dedent( f""" Argument -DCMAKE_OSX_DEPLOYMENT_TARGET:STRING={expected_cmake_osx_deployment_target} is NOT found near the end of current list of arguments: keyword_cmake_args : {keyword_cmake_args} cli_cmake_args : {cli_cmake_args} current_cmake_args: {current_cmake_args} """ ) scikit-build-0.18.1/tests/test_issue352_isolated_environment_support.py000066400000000000000000000073041466366435500264530ustar00rootroot00000000000000from __future__ import annotations import os import textwrap import pytest import skbuild from skbuild.constants import CMAKE_BUILD_DIR from . import _tmpdir, execute_setup_py def test_isolated_env_trigger_reconfigure(mocker): tmp_dir = _tmpdir("isolated_env_trigger_reconfigure") tmp_dir.join("setup.py").write( textwrap.dedent( """ from skbuild import setup setup( name="test_isolated_env_trigger_reconfigure", version="1.2.3", description="A minimal example package", author="The scikit-build team", license="MIT", ) """ ) ) tmp_dir.join("CMakeLists.txt").write( textwrap.dedent( """ message(FATAL_ERROR "This error message should not be displayed") """ ) ) # # mock configure # def fake_configure(*args, **kwargs): # Simulate a successful configuration creating a CMakeCache.txt tmp_dir.ensure(CMAKE_BUILD_DIR(), dir=1).join("CMakeCache.txt").write( textwrap.dedent( """ //Name of generator. CMAKE_GENERATOR:INTERNAL=Ninja """ ) ) # Skip real configuration creating the CMakeCache.txt expected by # "skbuild.setuptools_wrap._load_cmake_spec()" function mocker.patch("skbuild.cmaker.CMaker.configure", new=fake_configure) # # mock _save_cmake_spec # _save_cmake_spec_original = skbuild.setuptools_wrap._save_cmake_spec exit_after_saving_cmake_spec = "exit skbuild saving cmake spec" def _save_cmake_spec_mock(args): _save_cmake_spec_original(args) raise RuntimeError(exit_after_saving_cmake_spec) mocker.patch("skbuild.setuptools_wrap._save_cmake_spec", new=_save_cmake_spec_mock) # # mock make # exit_before_running_cmake = "exit skbuild running make" mocker.patch("skbuild.cmaker.CMaker.make", side_effect=RuntimeError(exit_before_running_cmake)) # first build: "configure" and "_save_cmake_spec" are expected to be called with pytest.raises(RuntimeError, match=exit_after_saving_cmake_spec): with execute_setup_py(tmp_dir, ["build"], disable_languages_test=True): pass # second build: no reconfiguration should happen, only "make" is expected to be called with pytest.raises(RuntimeError, match=exit_before_running_cmake): with execute_setup_py(tmp_dir, ["build"], disable_languages_test=True): pass # since pip updates PYTHONPATH with the temporary path # where the project dependencies are installed, we simulate # this by updating the corresponding environment variable. os.environ["PYTHONPATH"] = "/path/to/anything" # after updating the env, reconfiguration is expected with pytest.raises(RuntimeError, match=exit_after_saving_cmake_spec): with execute_setup_py(tmp_dir, ["build"], disable_languages_test=True): pass # no reconfiguration should happen with pytest.raises(RuntimeError, match=exit_before_running_cmake): with execute_setup_py(tmp_dir, ["build"], disable_languages_test=True): pass # this is the other variable set by pip when doing isolated build os.environ["PYTHONNOUSERSITE"] = "1" # after updating the env, reconfiguration is expected with pytest.raises(RuntimeError, match=exit_after_saving_cmake_spec): with execute_setup_py(tmp_dir, ["build"], disable_languages_test=True): pass # no reconfiguration should happen with pytest.raises(RuntimeError, match=exit_before_running_cmake): with execute_setup_py(tmp_dir, ["build"], disable_languages_test=True): pass scikit-build-0.18.1/tests/test_issue401_sdist_with_symlinks.py000066400000000000000000000022301466366435500245250ustar00rootroot00000000000000from __future__ import annotations import glob import sys import tarfile import py.path import pytest from . import project_setup_py_test from .pytest_helpers import check_sdist_content @pytest.mark.nosetuptoolsscm() @pytest.mark.skipif(sys.platform == "win32", reason="Symlinks not supported on Windows") @project_setup_py_test("issue-401-sdist-with-symlinks", ["sdist"]) def test_sdist_with_symlinks(): assert py.path.local("hello/VERSION").islink sdists_tar = glob.glob("dist/*.tar.gz") sdists_zip = glob.glob("dist/*.zip") assert sdists_tar or sdists_zip expected_content = [ "hello-1.2.3/MANIFEST.in", "hello-1.2.3/README", "hello-1.2.3/setup.py", "hello-1.2.3/VERSION", ] if sdists_tar: check_sdist_content(sdists_tar[0], "hello-1.2.3", expected_content) with tarfile.open(sdists_tar[0], "r:gz") as tf: member_list = tf.getnames() assert "hello-1.2.3/VERSION" in member_list mbr = tf.getmember("hello-1.2.3/VERSION") assert not mbr.issym() elif sdists_zip: check_sdist_content(sdists_zip[0], "hello-1.2.3", expected_content) scikit-build-0.18.1/tests/test_issue668_symbol_visibility.py000066400000000000000000000043501466366435500242130ustar00rootroot00000000000000from __future__ import annotations import glob import platform import shutil import subprocess import pytest from skbuild.constants import CMAKE_BUILD_DIR from . import ( _tmpdir, execute_setup_py, initialize_git_repo_and_commit, prepare_project, push_dir, ) @pytest.mark.skipif( platform.system().lower() not in ["linux"], reason="Executable and Linkable Format (ELF) is specific to Linux" ) @pytest.mark.parametrize("skip_override", ["ON", "OFF"]) def test_symbol_visibility(skip_override): with push_dir(): tmp_dir = _tmpdir("test_issue668_symbol_visibility") project = "issue-668-symbol-visibility" prepare_project(project, tmp_dir) initialize_git_repo_and_commit(tmp_dir, verbose=True) with execute_setup_py( tmp_dir, ["build", f"-DSKBUILD_GNU_SKIP_LOCAL_SYMBOL_EXPORT_OVERRIDE:BOOL={skip_override}"] ): pass print(f"Running test with SKBUILD_GNU_SKIP_LOCAL_SYMBOL_EXPORT_OVERRIDE:BOOL={skip_override}") lib_dir = str(tmp_dir) + "/" + CMAKE_BUILD_DIR() libs = glob.glob(lib_dir + "/*.so") assert libs print(f"Examining the library file: {libs[0]}") readelf = shutil.which("readelf") assert readelf cppfilt = shutil.which("c++filt") assert cppfilt result = subprocess.Popen([readelf, "-s", "--wide", libs[0]], stdout=subprocess.PIPE) output = str(subprocess.check_output((cppfilt), stdin=result.stdout), "UTF-8") result.wait() assert result.stdout is not None result.stdout.close() for line in output.splitlines(): # Looking for entries associated with get_map # ex. 62: 0000000000001260 164 FUNC GLOBAL DEFAULT 14 get_map\n # NOTE: We want to ignore get_map::id_to_resourse entries if "get_map" in line.split(): print(line) if skip_override == "ON": assert "GLOBAL" in line else: assert "LOCAL" in line # Looking for the PyInit_ entries # These should always be GLOBAL if "PyInit_" in line: print(line) assert "GLOBAL" in line scikit-build-0.18.1/tests/test_issue707_tested_packages.py000066400000000000000000000011751466366435500235610ustar00rootroot00000000000000from __future__ import annotations from . import project_setup_py_test def test_install_command(capfd, monkeypatch): @project_setup_py_test("issue-707-nested-packages", ["build_ext", "--inplace"], disable_languages_test=True) def build(): pass build() capfd.readouterr() monkeypatch.syspath_prepend(build.tmp_dir) # Verify that both import hello_nested hello_nested.hello("World") captured = capfd.readouterr() assert captured.out == "Hello, World!\n" hello_nested.goodbye_nested.goodbye("World") captured = capfd.readouterr() assert captured.out == "Goodbye, World!\n" scikit-build-0.18.1/tests/test_logging.py000066400000000000000000000015551466366435500204150ustar00rootroot00000000000000from __future__ import annotations import logging from types import SimpleNamespace import pytest from skbuild.utils import distribution_hide_listing setuptools_logging = pytest.importorskip("setuptools.logging") def test_hide_listing(caplog): setuptools_logging.configure() distribution = SimpleNamespace() distribution.hide_listing = True with distribution_hide_listing(distribution): # type: ignore[arg-type] logging.getLogger("wheel").info("This is hidden") assert "This is hidden" not in caplog.text def test_no_hide_listing(caplog): setuptools_logging.configure() distribution = SimpleNamespace() distribution.hide_listing = False with distribution_hide_listing(distribution): # type: ignore[arg-type] logging.getLogger("wheel").info("This is not hidden") assert "This is not hidden" in caplog.text scikit-build-0.18.1/tests/test_manifest_in.py000066400000000000000000000026441466366435500212630ustar00rootroot00000000000000"""test_manifest_in ---------------------------------- Tries to build and test the `manifest-in` sample project. """ from __future__ import annotations import glob import pytest from . import project_setup_py_test from .pytest_helpers import check_sdist_content, check_wheel_content @pytest.mark.nosetuptoolsscm() @project_setup_py_test("manifest-in", ["sdist"], disable_languages_test=True) def test_manifest_in_sdist(): sdists_tar = glob.glob("dist/*.tar.gz") sdists_zip = glob.glob("dist/*.zip") assert sdists_tar or sdists_zip dirname = "manifest-in-1.2.3" # setuptools 69.3.0 and above now canonicalize the filename as well. if any("manifest_in" in x for x in sdists_zip + sdists_tar): dirname = "manifest_in-1.2.3" expected_content = [ f"{dirname}/hello/__init__.py", f"{dirname}/setup.py", f"{dirname}/MANIFEST.in", ] sdist_archive = f"dist/{dirname}.zip" if sdists_tar: sdist_archive = f"dist/{dirname}.tar.gz" check_sdist_content(sdist_archive, dirname, expected_content) @project_setup_py_test("manifest-in", ["bdist_wheel"], disable_languages_test=True) def test_manifest_in_wheel(): whls = glob.glob("dist/*.whl") assert len(whls) == 1 expected_content = ["hello/__init__.py"] expected_distribution_name = "manifest_in-1.2.3" check_wheel_content(whls[0], expected_distribution_name, expected_content, pure=True) scikit-build-0.18.1/tests/test_numpy.py000066400000000000000000000014351466366435500201340ustar00rootroot00000000000000from __future__ import annotations import os import platform import subprocess import sys import pytest DIR = os.path.dirname(os.path.abspath(__file__)) HELLO_NUMPY = os.path.join(DIR, "samples/hello-numpy") BASE = os.path.dirname(DIR) @pytest.mark.isolated() @pytest.mark.skipif(sys.platform.startswith("cygwin"), reason="Needs release of scikit-build to make cmake work") @pytest.mark.skipif( platform.python_implementation() == "PyPy" and sys.version_info >= (3, 9), reason="NumPy not released for PyPy 3.9 yet", ) @pytest.mark.skipif( sys.version_info >= (3, 12), reason="NumPy not released for Python 3.12 yet", ) @pytest.mark.usefixtures("pep518") def test_pep518_findpython(): subprocess.run([sys.executable, "-m", "build", "--wheel"], cwd=HELLO_NUMPY, check=True) scikit-build-0.18.1/tests/test_outside_project_root.py000066400000000000000000000024221466366435500232260ustar00rootroot00000000000000"""test_outside_project_root ---------------------------------- Tries to build the `fail-outside-project-root` sample project. Ensures that the attempt fails with a SystemExit exception that has an SKBuildError exception as its value. """ from __future__ import annotations import pytest from skbuild.exceptions import SKBuildError from skbuild.utils import push_dir from . import project_setup_py_test @pytest.mark.parametrize("option", [None, "-DINSTALL_FILE:BOOL=1", "-DINSTALL_PROJECT:BOOL=1"]) def test_outside_project_root_fails(option): with push_dir(): expected_failure = False cmd = ["install"] if option is not None: expected_failure = True cmd.extend(["--", option]) @project_setup_py_test("fail-outside-project-root", cmd, disable_languages_test=True) def should_fail(): pass failed = False msg = "" try: should_fail() except SystemExit as e: failed = isinstance(e.code, SKBuildError) msg = str(e) except SKBuildError as e: failed = True msg = str(e) assert expected_failure == failed if expected_failure: assert "CMake-installed files must be within the project root." in msg scikit-build-0.18.1/tests/test_pep518.py000066400000000000000000000016011466366435500200010ustar00rootroot00000000000000from __future__ import annotations import os import subprocess import sys import pytest DIR = os.path.dirname(os.path.abspath(__file__)) HELLO_PEP518 = os.path.join(DIR, "samples/hello-pep518") BASE = os.path.dirname(DIR) @pytest.mark.isolated() @pytest.mark.skipif(sys.platform.startswith("cygwin"), reason="Needs release of scikit-build to make cmake work") @pytest.mark.usefixtures("pep518") def test_pep518(): subprocess.run([sys.executable, "-m", "build", "--wheel"], cwd=HELLO_PEP518, check=True) @pytest.mark.isolated() @pytest.mark.skipif(sys.platform.startswith("cygwin"), reason="Needs release of scikit-build to make cmake work") @pytest.mark.usefixtures("pep518") def test_dual_pep518(): subprocess.run([sys.executable, "-m", "build", "--wheel"], cwd=HELLO_PEP518, check=True) subprocess.run([sys.executable, "-m", "build", "--wheel"], cwd=HELLO_PEP518, check=True) scikit-build-0.18.1/tests/test_platform.py000066400000000000000000000102151466366435500206040ustar00rootroot00000000000000"""test_platform ---------------------------------- Tests for platforms, to verify that CMake correctly does a test compilation. """ from __future__ import annotations import os import platform import sys import pytest from skbuild.platform_specifics import get_platform from skbuild.utils import mkdir_p # XXX This should probably be a constant imported from skbuild.constants test_folder = "_cmake_test_compile" # skbuild_platform is shared across each test. It's a platform-specific object # that defines default CMake generator strings. skbuild_platform = get_platform() def test_platform_has_entries(): assert len(skbuild_platform.default_generators) > 0 def test_write_compiler_test_file(): # write the file that CMake will use to test compile (empty list indicates # we're testing no languages.) skbuild_platform.write_test_cmakelist([]) try: # verify that the test file exists (it's not valid, because it has no # languages) assert os.path.exists(os.path.join(test_folder, "CMakeLists.txt")) finally: skbuild_platform.cleanup_test() def test_cxx_compiler(): # Create a unique subdirectory 'foo' that is expected to be removed. test_build_folder = os.path.join(test_folder, "build", "foo") mkdir_p(test_build_folder) generator = skbuild_platform.get_best_generator(languages=["CXX", "C"], cleanup=False) # TODO: this isn't a true unit test. It depends on the test CMakeLists.txt # file having been written correctly. # with the known test file present, this tries to generate a makefile # (or solution, or whatever). # This test verifies that a working compiler is present on the system, but # doesn't actually compile anything. try: assert generator is not None assert not os.path.exists(test_build_folder) finally: skbuild_platform.cleanup_test() @pytest.mark.skipif( platform.system().lower() in ["darwin", "windows"], reason="no fortran compiler is available by default" ) @pytest.mark.fortran() def test_fortran_compiler(): generator = skbuild_platform.get_best_generator(languages=["Fortran"]) # TODO: this isn't a true unit test. It depends on the test # CMakeLists.txt file having been written correctly. # with the known test file present, this tries to generate a # makefile (or solution, or whatever). # This test verifies that a working compiler is present on the system, but # doesn't actually compile anything. try: assert generator is not None finally: skbuild_platform.cleanup_test() def test_generator_cleanup(): # TODO: this isn't a true unit test. It is checking that none of the # other tests have left a mess. assert not os.path.exists(test_folder) @pytest.mark.parametrize( "supported_platform", ["darwin", "freebsd", "openbsd", "linux", "windows", "os400", "cygwin", "sunos", "aix"] ) def test_known_platform(supported_platform, mocker): mocker.patch("platform.system", return_value=supported_platform) platforms = { "freebsd": "BSD", "openbsd": "BSD", "linux": "Linux", "darwin": "OSX", "windows": "Windows", "os400": "BSD", "cygwin": "Cygwin", "sunos": "SunOS", "aix": "AIX", } expected_platform_classname = f"{platforms[supported_platform]}Platform" assert get_platform().__class__.__name__ == expected_platform_classname def test_unsupported_platform(mocker): mocker.patch("platform.system", return_value="bogus") failed = False message = "" try: get_platform() except RuntimeError as e: failed = True message = str(e) assert failed assert "Unsupported platform: bogus." in message @pytest.mark.skipif(sys.platform != "win32", reason="Requires Windows") def test_cached_generator(): def is_configured_generator(generator): env = generator.env env_lib = env.get("LIB", "") return "Visual Studio" in env_lib or "Visual C++" in env_lib platform = get_platform() ninja_generators = platform.get_generators("Ninja") assert any(is_configured_generator(g) for g in ninja_generators) scikit-build-0.18.1/tests/test_setup.py000066400000000000000000001035741466366435500201330ustar00rootroot00000000000000"""test_setup ---------------------------------- Tests for `skbuild.setup` function. """ from __future__ import annotations import os import pprint import sys import textwrap from collections.abc import Sequence from unittest.mock import patch import py.path import pytest from setuptools import Distribution as setuptool_Distribution from distutils.core import Distribution as distutils_Distribution from skbuild import setup as skbuild_setup from skbuild.constants import CMAKE_INSTALL_DIR, SKBUILD_DIR from skbuild.exceptions import SKBuildError from skbuild.platform_specifics import get_platform from skbuild.setuptools_wrap import strip_package from skbuild.utils import push_dir, to_platform_path from . import ( _tmpdir, execute_setup_py, initialize_git_repo_and_commit, is_site_reachable, push_argv, ) @pytest.mark.parametrize("distribution_type", ["unknown", "py_modules", "packages", "skbuild"]) def test_distribution_is_pure(distribution_type, tmpdir): skbuild_setup_kwargs = {} if distribution_type == "unknown": is_pure = False elif distribution_type == "py_modules": is_pure = True hello_py = tmpdir.join("hello.py") hello_py.write("") skbuild_setup_kwargs["py_modules"] = ["hello"] elif distribution_type == "packages": is_pure = True init_py = tmpdir.mkdir("hello").join("__init__.py") init_py.write("") skbuild_setup_kwargs["packages"] = ["hello"] elif distribution_type == "skbuild": is_pure = False cmakelists_txt = tmpdir.join("CMakeLists.txt") cmakelists_txt.write( """ cmake_minimum_required(VERSION 3.5.0) project(test NONE) install(CODE "execute_process( COMMAND \\${CMAKE_COMMAND} -E sleep 0)") """ ) else: msg = f"Unknown distribution_type: {distribution_type}" raise Exception(msg) platform = get_platform() original_write_test_cmakelist = platform.write_test_cmakelist def write_test_cmakelist_no_languages(_self, _languages): original_write_test_cmakelist([]) with patch.object(type(platform), "write_test_cmakelist", new=write_test_cmakelist_no_languages): with push_dir(str(tmpdir)), push_argv(["setup.py", "build"]): distribution = skbuild_setup( name="test", version="0.0.1", description="test object returned by setup function", author="The scikit-build team", license="MIT", **skbuild_setup_kwargs, # type: ignore[arg-type] ) assert issubclass(distribution.__class__, (distutils_Distribution, setuptool_Distribution)) assert is_pure == distribution.is_pure() @pytest.mark.parametrize("cmake_args", [[], ["--", "-DVAR:STRING=43", "-DVAR_WITH_SPACE:STRING=Ciao Mondo"]]) def test_cmake_args_keyword(cmake_args, capfd): tmp_dir = _tmpdir("cmake_args_keyword") tmp_dir.join("setup.py").write( textwrap.dedent( """ from skbuild import setup setup( name="test_cmake_args_keyword", version="1.2.3", description="a minimal example package", author='The scikit-build team', license="MIT", cmake_args=[ "-DVAR:STRING=42", "-DVAR_WITH_SPACE:STRING=Hello World" ] ) """ ) ) tmp_dir.join("CMakeLists.txt").write( textwrap.dedent( """ cmake_minimum_required(VERSION 3.5.0) project(test NONE) message(STATUS "VAR[${VAR}]") message(STATUS "VAR_WITH_SPACE[${VAR_WITH_SPACE}]") install(CODE "execute_process( COMMAND \\${CMAKE_COMMAND} -E sleep 0)") """ ) ) with execute_setup_py(tmp_dir, ["build", *cmake_args], disable_languages_test=True): pass out, _ = capfd.readouterr() if not cmake_args: assert "VAR[42]" in out assert "VAR_WITH_SPACE[Hello World]" in out else: assert "VAR[43]" in out assert "VAR_WITH_SPACE[Ciao Mondo]" in out @pytest.mark.parametrize( ("cmake_install_dir", "expected_failed", "error_code_type"), [ (None, True, str), ("", True, str), (str(py.path.local.get_temproot().join("scikit-build")), True, SKBuildError), ("banana", False, str), ], ) def test_cmake_install_dir_keyword(cmake_install_dir, expected_failed, error_code_type, capsys, caplog): # ------------------------------------------------------------------------- # "SOURCE" tree layout: # # ROOT/ # # CMakeLists.txt # setup.py # # apple/ # __init__.py # # ------------------------------------------------------------------------- # "BINARY" distribution layout # # ROOT/ # # apple/ # __init__.py # tmp_dir = _tmpdir("cmake_install_dir_keyword") setup_kwarg = "" if cmake_install_dir is not None: setup_kwarg = f"cmake_install_dir={str(cmake_install_dir)!r}" tmp_dir.join("setup.py").write( textwrap.dedent( f""" from skbuild import setup setup( name="test_cmake_install_dir", version="1.2.3", description="a package testing use of cmake_install_dir", author='The scikit-build team', license="MIT", packages=['apple', 'banana'], {setup_kwarg} ) """ ) ) # Install location purposely set to "." so that we can test # usage of "cmake_install_dir" skbuild.setup keyword. tmp_dir.join("CMakeLists.txt").write( textwrap.dedent( """ cmake_minimum_required(VERSION 3.5.0) project(banana NONE) file(WRITE "${CMAKE_BINARY_DIR}/__init__.py" "") install(FILES "${CMAKE_BINARY_DIR}/__init__.py" DESTINATION ".") """ ) ) tmp_dir.ensure("apple", "__init__.py") failed = False message = "" try: with execute_setup_py(tmp_dir, ["build"], disable_languages_test=True): pass except SystemExit as e: # Error is not of type SKBuildError, it is expected to be # raised by distutils.core.setup failed = isinstance(e.code, error_code_type) message = str(e) out, _ = capsys.readouterr() out += caplog.text assert failed == expected_failed if failed: if error_code_type is str: assert message == "error: package directory '{}' does not exist".format( os.path.join(CMAKE_INSTALL_DIR(), "banana") ) else: assert message.strip().startswith("setup parameter 'cmake_install_dir' is set to an absolute path.") else: init_py = to_platform_path(f"{CMAKE_INSTALL_DIR()}/banana/__init__.py") assert f"copying {init_py}" in out @pytest.mark.parametrize("cmake_with_sdist", [True, False]) def test_cmake_with_sdist_keyword(cmake_with_sdist, capfd): tmp_dir = _tmpdir("cmake_with_sdist") tmp_dir.join("setup.py").write( textwrap.dedent( f""" from skbuild import setup setup( name="cmake_with_sdist_keyword", version="1.2.3", description="a minimal example package", author='The scikit-build team', license="MIT", cmake_with_sdist={cmake_with_sdist} ) """ ) ) tmp_dir.join("CMakeLists.txt").write( textwrap.dedent( """ cmake_minimum_required(VERSION 3.5.0) project(test NONE) install(CODE "execute_process( COMMAND \\${CMAKE_COMMAND} -E sleep 0)") """ ) ) initialize_git_repo_and_commit(tmp_dir) with execute_setup_py(tmp_dir, ["sdist"], disable_languages_test=True): pass out, _ = capfd.readouterr() if cmake_with_sdist: assert "Generating done" in out else: assert "Generating done" not in out def test_cmake_minimum_required_version_keyword(): tmp_dir = _tmpdir("cmake_minimum_required_version") tmp_dir.join("setup.py").write( textwrap.dedent( """ from skbuild import setup setup( name="cmake_with_sdist_keyword", version="1.2.3", description="a minimal example package", author='The scikit-build team', license="MIT", cmake_minimum_required_version='99.98.97' ) """ ) ) tmp_dir.join("CMakeLists.txt").write( textwrap.dedent( """ cmake_minimum_required(VERSION 3.5.0) project(test NONE) install(CODE "execute_process( COMMAND \\${CMAKE_COMMAND} -E sleep 0)") """ ) ) try: with execute_setup_py(tmp_dir, ["build"], disable_languages_test=True): pass except SystemExit as e: # Error is not of type SKBuildError, it is expected to be # raised by distutils.core.setup failed = isinstance(e.code, SKBuildError) message = str(e) assert failed assert "CMake version 99.98.97 or higher is required." in message @pytest.mark.deprecated() @pytest.mark.filterwarnings("ignore:setuptools.installer is deprecated:Warning") @pytest.mark.skipif( os.environ.get("CONDA_BUILD", "0") == "1", reason="running tests expecting network connection in Conda is not possible. " "See https://github.com/conda/conda/issues/508", ) @pytest.mark.skipif(not is_site_reachable("https://pypi.org/simple/cmake/"), reason="pypi.org website not reachable") @pytest.mark.xfail( sys.platform.startswith("cygwin"), strict=False, reason="Cygwin needs a release of scikit-build first" ) def test_setup_requires_keyword_include_cmake(mocker, capsys): mock_setup = mocker.patch("skbuild.setuptools_wrap.setuptools.setup") tmp_dir = _tmpdir("setup_requires_keyword_include_cmake") setup_requires = ["cmake>=3.10"] tmp_dir.join("setup.py").write( textwrap.dedent( """ from skbuild import setup setup( name="cmake_with_sdist_keyword", version="1.2.3", description="a minimal example package", author='The scikit-build team', license="MIT", setup_requires=[{setup_requires}] ) """.format(setup_requires=",".join([f"'{package}'" for package in setup_requires])) ) ) tmp_dir.join("CMakeLists.txt").write( textwrap.dedent( """ cmake_minimum_required(VERSION 3.5.0) project(test NONE) install(CODE "execute_process( COMMAND \\${CMAKE_COMMAND} -E sleep 0)") """ ) ) with execute_setup_py(tmp_dir, ["build"], disable_languages_test=True): assert mock_setup.call_count == 1 setup_kw = mock_setup.call_args[1] assert setup_kw["setup_requires"] == setup_requires import cmake out, _ = capsys.readouterr() if "Searching for cmake>=3.10" in out: assert cmake.__file__.lower().startswith(str(tmp_dir).lower()) @pytest.mark.parametrize("distribution_type", ["pure", "skbuild"]) def test_script_keyword(distribution_type, capsys, caplog): # ------------------------------------------------------------------------- # # "SOURCE" tree layout for "pure" distribution: # # ROOT/ # setup.py # foo.py # bar.py # # "SOURCE" tree layout for "pure" distribution: # # ROOT/ # setup.py # CMakeLists.txt # # ------------------------------------------------------------------------- # "BINARY" distribution layout is identical for both # # ROOT/ # foo.py # bar.py # tmp_dir = _tmpdir("script_keyword") tmp_dir.join("setup.py").write( textwrap.dedent( """ from skbuild import setup setup( name="test_script_keyword", version="1.2.3", description="a package testing use of script keyword", author='The scikit-build team', license="MIT", scripts=['foo.py', 'bar.py'], packages=[], ) """ ) ) if distribution_type == "skbuild": tmp_dir.join("CMakeLists.txt").write( textwrap.dedent( """ cmake_minimum_required(VERSION 3.5.0) project(foo NONE) file(WRITE "${CMAKE_BINARY_DIR}/foo.py" "# foo.py") file(WRITE "${CMAKE_BINARY_DIR}/bar.py" "# bar.py") install( FILES "${CMAKE_BINARY_DIR}/foo.py" "${CMAKE_BINARY_DIR}/bar.py" DESTINATION "." ) """ ) ) messages = [ f"copying {CMAKE_INSTALL_DIR()}/{module}.py -> {SKBUILD_DIR()}/setuptools/scripts-" for module in ["foo", "bar"] ] elif distribution_type == "pure": tmp_dir.join("foo.py").write("# foo.py") tmp_dir.join("bar.py").write("# bar.py") messages = [f"copying {module}.py -> {SKBUILD_DIR()}/setuptools/scripts-" for module in ["foo", "bar"]] with execute_setup_py(tmp_dir, ["build"], disable_languages_test=True): pass out, _ = capsys.readouterr() out += caplog.text for message in messages: assert to_platform_path(message) in out @pytest.mark.parametrize("distribution_type", ["pure", "skbuild"]) def test_py_modules_keyword(distribution_type, capsys, caplog): # ------------------------------------------------------------------------- # # "SOURCE" tree layout for "pure" distribution: # # ROOT/ # setup.py # foo.py # bar.py # # "SOURCE" tree layout for "skbuild" distribution: # # ROOT/ # setup.py # CMakeLists.txt # # ------------------------------------------------------------------------- # "BINARY" distribution layout is identical for both # # ROOT/ # foo.py # bar.py # tmp_dir = _tmpdir("py_modules_keyword") tmp_dir.join("setup.py").write( textwrap.dedent( """ from skbuild import setup setup( name="test_py_modules_keyword", version="1.2.3", description="a package testing use of py_modules keyword", author='The scikit-build team', license="MIT", py_modules=['foo', 'bar'] ) """ ) ) if distribution_type == "skbuild": tmp_dir.join("CMakeLists.txt").write( textwrap.dedent( """ cmake_minimum_required(VERSION 3.5.0) project(foobar NONE) file(WRITE "${CMAKE_BINARY_DIR}/foo.py" "# foo.py") file(WRITE "${CMAKE_BINARY_DIR}/bar.py" "# bar.py") install( FILES "${CMAKE_BINARY_DIR}/foo.py" "${CMAKE_BINARY_DIR}/bar.py" DESTINATION "." ) """ ) ) messages = [ f"copying {CMAKE_INSTALL_DIR()}/{module}.py -> {SKBUILD_DIR()}/setuptools/lib" for module in ["foo", "bar"] ] elif distribution_type == "pure": tmp_dir.join("foo.py").write("# foo.py") tmp_dir.join("bar.py").write("# bar.py") messages = [f"copying {module}.py -> {SKBUILD_DIR()}/setuptools/lib" for module in ["foo", "bar"]] with execute_setup_py(tmp_dir, ["build"], disable_languages_test=True): pass out, _ = capsys.readouterr() out += caplog.text for message in messages: assert to_platform_path(message) in out @pytest.mark.parametrize( ("package_parts", "module_file", "expected"), [ ([], "", ""), ([""], "file.py", "file.py"), ([], "foo/file.py", "foo/file.py"), (["foo"], "", ""), (["foo"], "foo/file.py", "file.py"), (["foo"], "foo\\file.py", "file.py"), (["foo", "bar"], "foo/file.py", "foo/file.py"), (["foo", "bar"], "foo/bar/file.py", "file.py"), (["foo", "bar"], "foo/bar/baz/file.py", "baz/file.py"), (["foo"], "/foo/file.py", "/foo/file.py"), ], ) def test_strip_package(package_parts, module_file, expected): assert strip_package(package_parts, module_file) == expected @pytest.mark.parametrize("has_cmake_package", [0, 1]) @pytest.mark.parametrize("has_cmake_module", [0, 1]) @pytest.mark.parametrize("has_hybrid_package", [0, 1]) @pytest.mark.parametrize("has_pure_package", [0, 1]) @pytest.mark.parametrize("has_pure_module", [0, 1]) @pytest.mark.parametrize("with_package_base", [0, 1]) def test_setup_inputs( has_cmake_package, has_cmake_module, has_hybrid_package, has_pure_package, has_pure_module, with_package_base, mocker, ): """This test that a project can have a package with some modules installed using setup.py and some other modules installed using CMake. """ tmp_dir = _tmpdir("test_setup_inputs") package_base = "to/the/base" if with_package_base else "" package_base_dir = package_base + "/" if package_base else "" cmake_source_dir = package_base if cmake_source_dir and (has_cmake_package or has_cmake_module): pytest.skip( "unsupported configuration: " "python package fully generated by CMake does *NOT* work. " "At least __init__.py should be in the project source tree" ) # ------------------------------------------------------------------------- # Here is the "SOURCE" tree layout: # # ROOT/ # # setup.py # # [/] # # pureModule.py # # pure/ # __init__.py # pure.py # # data/ # pure.dat # # [/] # # hybrid/ # CMakeLists.txt # __init__.py # hybrid_pure.dat # hybrid_pure.py # # data/ # hybrid_data_pure.dat # # hybrid_2/ # __init__.py # hybrid_2_pure.py # # hybrid_2_pure/ # __init__.py # hybrid_2_pure_1.py # hybrid_2_pure_2.py # # # ------------------------------------------------------------------------- # and here is the "BINARY" distribution layout: # # The comment "CMake" or "Setuptools" indicates which tool is responsible # for placing the file in the tree used to create the binary distribution. # # ROOT/ # # cmakeModule.py # CMake # # cmake/ # __init__.py # CMake # cmake.py # CMake # # hybrid/ # hybrid_cmake.dat # CMake # hybrid_cmake.py # CMake # hybrid_pure.dat # Setuptools # hybrid_pure.py # Setuptools # # data/ # hybrid_data_pure.dat # CMake or Setuptools # hybrid_data_cmake.dat # CMake *NO TEST* # # hybrid_2/ # __init__.py # CMake or Setuptools # hybrid_2_pure.py # CMake or Setuptools # hybrid_2_cmake.py # CMake # # hybrid_2_pure/ # __init__.py # CMake or Setuptools # hybrid_2_pure_1.py # CMake or Setuptools # hybrid_2_pure_2.py # CMake or Setuptools # # pureModule.py # Setuptools # # pure/ # __init__.py # Setuptools # pure.py # Setuptools # # data/ # pure.dat # Setuptools tmp_dir.join("setup.py").write( textwrap.dedent( """ from skbuild import setup #from setuptools import setup setup( name="test_hybrid_project", version="1.2.3", description=("an hybrid package mixing files installed by both " "CMake and setuptools"), author='The scikit-build team', license="MIT", cmake_source_dir='{cmake_source_dir}', cmake_install_dir='{cmake_install_dir}', # Arbitrary order of packages packages=[ {p_off} 'pure', {h_off} 'hybrid.hybrid_2', {h_off} 'hybrid', {c_off} 'cmake', {p_off} 'hybrid.hybrid_2_pure', ], py_modules=[ {pm_off} '{package_base}pureModule', {cm_off} '{package_base}cmakeModule', ], package_data={{ {p_off} 'pure': ['data/pure.dat'], {h_off} 'hybrid': ['hybrid_pure.dat', 'data/hybrid_data_pure.dat'], }}, # Arbitrary order of package_dir package_dir = {{ {p_off} 'hybrid.hybrid_2_pure': '{package_base}hybrid/hybrid_2_pure', {p_off} 'pure': '{package_base}pure', {h_off} 'hybrid': '{package_base}hybrid', {h_off} 'hybrid.hybrid_2': '{package_base}hybrid/hybrid_2', {c_off} 'cmake': '{package_base}cmake', }} ) """.format( cmake_source_dir=cmake_source_dir, cmake_install_dir=package_base, package_base=package_base_dir, c_off="" if has_cmake_package else "#", cm_off="" if has_cmake_module else "#", h_off="" if has_hybrid_package else "#", p_off="" if has_pure_package else "#", pm_off="" if has_pure_module else "#", ) ) ) src_dir = tmp_dir.ensure(package_base, dir=1) src_dir.join("CMakeLists.txt").write( textwrap.dedent( """ cmake_minimum_required(VERSION 3.5.0) project(hybrid NONE) set(build_dir ${{CMAKE_BINARY_DIR}}) {c_off} file(WRITE ${{build_dir}}/__init__.py "") {c_off} file(WRITE ${{build_dir}}/cmake.py "") {c_off} install( {c_off} FILES {c_off} ${{build_dir}}/__init__.py {c_off} ${{build_dir}}/cmake.py {c_off} DESTINATION cmake {c_off} ) {cm_off} file(WRITE ${{build_dir}}/cmakeModule.py "") {cm_off} install( {cm_off} FILES ${{build_dir}}/cmakeModule.py {cm_off} DESTINATION .) {h_off} file(WRITE ${{build_dir}}/hybrid_cmake.dat "") {h_off} install( {h_off} FILES ${{build_dir}}/hybrid_cmake.dat {h_off} DESTINATION hybrid) {h_off} file(WRITE ${{build_dir}}/hybrid_cmake.py "") {h_off} install( {h_off} FILES ${{build_dir}}/hybrid_cmake.py {h_off} DESTINATION hybrid) {h_off} file(WRITE ${{build_dir}}/hybrid_data_cmake.dat "") {h_off} install( {h_off} FILES ${{build_dir}}/hybrid_data_cmake.dat {h_off} DESTINATION hybrid/data) {h_off} file(WRITE ${{build_dir}}/hybrid_2_cmake.py "") {h_off} install( {h_off} FILES ${{build_dir}}/hybrid_2_cmake.py {h_off} DESTINATION hybrid/hybrid_2) install(CODE "message(STATUS \\\"Installation complete\\\")") """.format( c_off="" if has_cmake_package else "#", cm_off="" if has_cmake_module else "#", h_off="" if has_hybrid_package else "#", ) ) ) # List path types: 'c', 'cm', 'h', 'p' or 'pm' path_types: Sequence[str] try: path_types = next( iter( zip( *filter( lambda i: i[1], [ ("c", has_cmake_package), ("cm", has_cmake_module), ("h", has_hybrid_package), ("p", has_pure_package), ("pm", has_pure_module), ], ) ) ) ) except StopIteration: path_types = [] def select_paths(annotated_paths): """Return a filtered list paths considering ``path_types``. `annotated_paths`` is list of tuple ``(type, path)`` where type is either `c`, 'cm', `h`, `p` or 'pm'. """ return filter(lambda i: i[0] in path_types, annotated_paths) # Commented paths are the one expected to be installed by CMake. For # this reason, corresponding files should NOT be created in the source # tree. for _type, path in select_paths( [ # ('c', 'cmake/__init__.py'), # ('c', 'cmake/cmake.py'), # ('cm', 'cmakeModule.py'), ("h", "hybrid/__init__.py"), # ('h', 'hybrid/hybrid_cmake.dat'), # ('h', 'hybrid/hybrid_cmake.py'), ("h", "hybrid/hybrid_pure.dat"), ("h", "hybrid/hybrid_pure.py"), # ('h', 'hybrid/data/hybrid_data_cmake.dat'), ("h", "hybrid/data/hybrid_data_pure.dat"), ("h", "hybrid/hybrid_2/__init__.py"), # ('h', 'hybrid/hybrid_2/hybrid_2_cmake.py'), ("h", "hybrid/hybrid_2/hybrid_2_pure.py"), ("p", "hybrid/hybrid_2_pure/__init__.py"), ("p", "hybrid/hybrid_2_pure/hybrid_2_pure_1.py"), ("p", "hybrid/hybrid_2_pure/hybrid_2_pure_2.py"), ("pm", "pureModule.py"), ("p", "pure/__init__.py"), ("p", "pure/pure.py"), ("p", "pure/data/pure.dat"), ] ): assert _type in {"p", "pm", "h"} root = package_base if _type in {"p", "pm"} else cmake_source_dir tmp_dir.ensure(os.path.join(root, path)) # Do not call the real setup function. Instead, replace it with # a MagicMock allowing to check with which arguments it was invoked. mock_setup = mocker.patch("skbuild.setuptools_wrap.setuptools.setup") # Convenience print function def _pprint(desc, value=None): print( "-----------------\n" f"{desc}:\n" "\n" f"{pprint.pformat(setup_kw.get(desc, {}) if value is None else value, indent=2)}\n" ) with execute_setup_py(tmp_dir, ["build"], disable_languages_test=True): assert mock_setup.call_count == 1 setup_kw = mock_setup.call_args[1] # packages expected_packages = [] if has_cmake_package: expected_packages += ["cmake"] if has_hybrid_package: expected_packages += ["hybrid", "hybrid.hybrid_2"] if has_pure_package: expected_packages += ["hybrid.hybrid_2_pure", "pure"] _pprint("expected_packages", expected_packages) _pprint("packages") # package dir expected_package_dir = { package: (os.path.join(CMAKE_INSTALL_DIR(), package_base, package.replace(".", "/"))) for package in expected_packages } _pprint("expected_package_dir", expected_package_dir) _pprint("package_dir") # package data expected_package_data = {} if has_cmake_package: expected_package_data["cmake"] = ["__init__.py", "cmake.py"] if has_hybrid_package: expected_package_data["hybrid"] = [ "__init__.py", "hybrid_cmake.dat", "hybrid_cmake.py", "hybrid_pure.dat", "hybrid_pure.py", "data/hybrid_data_cmake.dat", "data/hybrid_data_pure.dat", ] expected_package_data["hybrid.hybrid_2"] = ["__init__.py", "hybrid_2_cmake.py", "hybrid_2_pure.py"] if has_pure_package: expected_package_data["hybrid.hybrid_2_pure"] = ["__init__.py", "hybrid_2_pure_1.py", "hybrid_2_pure_2.py"] expected_package_data["pure"] = [ "__init__.py", "pure.py", "data/pure.dat", ] if has_cmake_module or has_pure_module: expected_modules = [] if has_cmake_module: expected_modules.append(package_base_dir + "cmakeModule.py") if has_pure_module: expected_modules.append(package_base_dir + "pureModule.py") expected_package_data[""] = expected_modules _pprint("expected_package_data", expected_package_data) package_data = {p: sorted(files) for p, files in setup_kw["package_data"].items()} _pprint("package_data", package_data) # py_modules (corresponds to files associated with empty package) expected_py_modules = [] if "" in expected_package_data: expected_py_modules = [os.path.splitext(module_file)[0] for module_file in expected_package_data[""]] _pprint("expected_py_modules", expected_py_modules) _pprint("py_modules") # scripts _pprint("expected_scripts", []) _pprint("scripts") # data_files _pprint("expected_data_files", []) _pprint("data_files") assert sorted(setup_kw["packages"]) == sorted(expected_packages) assert sorted(setup_kw["package_dir"]) == sorted(expected_package_dir) assert package_data == {p: sorted(files) for p, files in expected_package_data.items()} assert sorted(setup_kw["py_modules"]) == sorted(expected_py_modules) assert sorted(setup_kw["scripts"]) == sorted([]) assert sorted(setup_kw["data_files"]) == sorted([]) @pytest.mark.parametrize("with_cmake_source_dir", [0, 1]) def test_cmake_install_into_pure_package(with_cmake_source_dir, capsys, caplog): # ------------------------------------------------------------------------- # "SOURCE" tree layout: # # (1) with_cmake_source_dir == 0 # # ROOT/ # # CMakeLists.txt # setup.py # # fruits/ # __init__.py # # # (2) with_cmake_source_dir == 1 # # ROOT/ # # setup.py # # fruits/ # __init__.py # # src/ # # CMakeLists.txt # # ------------------------------------------------------------------------- # "BINARY" distribution layout: # # ROOT/ # # fruits/ # # __init__.py # apple.py # banana.py # # data/ # # apple.dat # banana.dat # tmp_dir = _tmpdir("cmake_install_into_pure_package") cmake_source_dir = "src" if with_cmake_source_dir else "" tmp_dir.join("setup.py").write( textwrap.dedent( f""" from skbuild import setup setup( name="test_py_modules_keyword", version="1.2.3", description="a package testing use of py_modules keyword", author='The scikit-build team', license="MIT", packages=['fruits'], cmake_install_dir='fruits', cmake_source_dir='{cmake_source_dir}', ) """ ) ) cmake_src_dir = tmp_dir.ensure(cmake_source_dir, dir=1) cmake_src_dir.join("CMakeLists.txt").write( textwrap.dedent( """ cmake_minimum_required(VERSION 3.5.0) project(test NONE) file(WRITE "${CMAKE_BINARY_DIR}/apple.py" "# apple.py") file(WRITE "${CMAKE_BINARY_DIR}/banana.py" "# banana.py") install( FILES "${CMAKE_BINARY_DIR}/apple.py" "${CMAKE_BINARY_DIR}/banana.py" DESTINATION "." ) file(WRITE "${CMAKE_BINARY_DIR}/apple.dat" "# apple.dat") file(WRITE "${CMAKE_BINARY_DIR}/banana.dat" "# banana.dat") install( FILES "${CMAKE_BINARY_DIR}/apple.dat" "${CMAKE_BINARY_DIR}/banana.dat" DESTINATION "data" ) """ ) ) tmp_dir.ensure("fruits/__init__.py") with execute_setup_py(tmp_dir, ["build"], disable_languages_test=True): pass messages = [ f"copying {CMAKE_INSTALL_DIR()}/{module} -> {SKBUILD_DIR()}/setuptools/lib" for module in [ "fruits/__init__.py", "fruits/apple.py", "fruits/banana.py", "fruits/data/apple.dat", "fruits/data/banana.dat", ] ] out, _ = capsys.readouterr() out += caplog.text for message in messages: assert to_platform_path(message) in out @pytest.mark.parametrize("zip_safe", [None, False, True]) def test_zip_safe_default(zip_safe, mocker): mock_setup = mocker.patch("skbuild.setuptools_wrap.setuptools.setup") tmp_dir = _tmpdir("zip_safe_default") setup_kwarg = "" if zip_safe is not None: setup_kwarg = f"zip_safe={zip_safe}" tmp_dir.join("setup.py").write( textwrap.dedent( f""" from skbuild import setup setup( name="zip_safe_default", version="1.2.3", description="a minimal example package", author='The scikit-build team', license="MIT", {setup_kwarg} ) """ ) ) tmp_dir.join("CMakeLists.txt").write( textwrap.dedent( """ cmake_minimum_required(VERSION 3.5.0) project(test NONE) install(CODE "execute_process( COMMAND \\${CMAKE_COMMAND} -E sleep 0)") """ ) ) with execute_setup_py(tmp_dir, ["build"], disable_languages_test=True): pass assert mock_setup.call_count == 1 setup_kw = mock_setup.call_args[1] assert "zip_safe" in setup_kw if zip_safe is None: assert not setup_kw["zip_safe"] elif zip_safe: assert setup_kw["zip_safe"] else: # zip_safe is False assert not setup_kw["zip_safe"] scikit-build-0.18.1/tests/test_skbuild.py000066400000000000000000000147241466366435500204260ustar00rootroot00000000000000"""test_skbuild ---------------------------------- Tests for `skbuild` module. """ from __future__ import annotations import os import platform import shutil import sys import pytest from skbuild.constants import CMAKE_BUILD_DIR from skbuild.exceptions import SKBuildError from skbuild.platform_specifics import get_platform from skbuild.platform_specifics.windows import VS_YEAR_TO_VERSION, find_visual_studio from . import get_cmakecache_variables, project_setup_py_test, push_dir, push_env def test_generator_selection(): env_generator = os.environ.get("CMAKE_GENERATOR") this_platform = platform.system().lower() get_best_generator = get_platform().get_best_generator arch = platform.architecture()[0] if env_generator: assert get_best_generator(env_generator).name == env_generator if this_platform == "windows": # Expected Visual Studio version vs_generator = "Visual Studio 15 2017" vs_version = 15 vs_generator += " Win64" if arch == "64bit" else "" has_vs_2017 = find_visual_studio(vs_version=VS_YEAR_TO_VERSION["2017"]) has_vs_2019 = find_visual_studio(vs_version=VS_YEAR_TO_VERSION["2019"]) has_vs_2022 = find_visual_studio(vs_version=VS_YEAR_TO_VERSION["2022"]) # As of Dec 2016, this is available only for VS 9.0 has_vs_for_python_vcvars = any( os.path.exists(os.path.expanduser(path_pattern % vs_version)) for path_pattern in [ "~/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/%.1f/vcvarsall.bat", "C:/Program Files (x86)/Common Files/Microsoft/Visual C++ for Python/%.1f/vcvarsall.bat", ] ) # If environment exists, update the expected generator if has_vs_for_python_vcvars and shutil.which("ninja.exe"): assert get_best_generator().name == "Ninja" elif has_vs_2017: vs_generator = "Visual Studio 15 2017" # Early versions of 2017 may not ship with Ninja (TODO: check) assert get_best_generator().name in {"Ninja", vs_generator} elif has_vs_2019 or has_vs_2022: # ninja is provided by the CMake extension bundled with Visual Studio 2017 # C:/Program Files (x86)/Microsoft Visual Studio/2017/Professional/Common7/IDE/CommonExtensions/Microsoft/CMake/Ninja/ninja.exe assert get_best_generator().name == "Ninja" elif has_vs_for_python_vcvars: assert get_best_generator().name == "NMake Makefiles" elif this_platform in ["darwin", "linux"]: generator = "Ninja" if shutil.which("ninja") else "Unix Makefiles" assert get_best_generator().name == generator @pytest.mark.parametrize( ("generator", "expected_make_program"), [("NMake Makefiles", "nmake"), ("Unix Makefiles", "make")] ) def test_generator(generator, expected_make_program): generator_platform = {"NMake Makefiles": ["windows"], "Unix Makefiles": ["darwin", "linux"]} assert generator in generator_platform this_platform = platform.system().lower() if this_platform not in generator_platform[generator]: pytest.skip(f"{generator} generator is not available on {this_platform.title()}") if shutil.which(expected_make_program) is None: pytest.skip(f"{expected_make_program} not available") @project_setup_py_test("hello-cpp", ["build"], ret=True) def run_build(): pass with push_env(CMAKE_GENERATOR=generator): tmp_dir = run_build()[0] cmakecache = tmp_dir.join(CMAKE_BUILD_DIR()).join("CMakeCache.txt") assert cmakecache.exists() variables = get_cmakecache_variables(str(cmakecache)) make_program = variables["CMAKE_MAKE_PROGRAM"][1] if "CMAKE_MAKE_PROGRAM" in variables else "" assert make_program.endswith((expected_make_program, f"{expected_make_program}.exe")) @pytest.mark.parametrize( "generator_args", [ ["-G", "invalid"], ["--", "-G", "invalid"], ], ) def test_invalid_generator(generator_args): with push_dir(): build_args = ["build"] build_args.extend(generator_args) @project_setup_py_test("hello-no-language", build_args, disable_languages_test=True) def run(): pass failed = False message = "" try: run() except SystemExit as e: failed = isinstance(e.code, SKBuildError) message = str(e) assert failed assert "scikit-build could not get a working generator for your system. Aborting build." in message @pytest.mark.skipif(sys.platform != "win32", reason="Requires Windows") @pytest.mark.parametrize("vs_year", ["2017", "2019", "2022"]) def test_platform_windows_find_visual_studio(vs_year, capsys): """If the environment variable ``SKBUILD_TEST_FIND_VS_INSTALLATION_EXPECTED`` is set, this test asserts the value returned by :func:`skbuild.platforms.windows.find_visual_studio()`. It skips the test otherwise. Setting the environment variable to 1 means that the corresponding Visual Studio version is expected to be installed. Setting it to 0, means otherwise. """ env_var = f"SKBUILD_TEST_FIND_VS{vs_year}_INSTALLATION_EXPECTED" if env_var not in os.environ: pytest.skip(f"env. variable {env_var} is not set") valid_path_expected = bool(int(os.environ[env_var])) vs_path = find_visual_studio(VS_YEAR_TO_VERSION[vs_year]) if valid_path_expected: with capsys.disabled(): print(f"\nFound VS {vs_year} @ {vs_path}") assert os.path.exists(vs_path) else: assert not vs_path @pytest.mark.skipif(sys.platform != "win32", reason="Requires Windows") def test_toolset(): has_vs_2017 = find_visual_studio(vs_version=VS_YEAR_TO_VERSION["2017"]) if not has_vs_2017: pytest.skip("Visual Studio 15 2017 is not found") arch = platform.architecture()[0] vs_generator = "Visual Studio 15 2017" orig_generator = vs_generator if arch == "64bit": vs_generator += " Win64" @project_setup_py_test("hello-cpp", ["build", "-G", vs_generator], ret=True) def run_build(): pass tmp_dir = run_build()[0] cmakecache = tmp_dir.join(CMAKE_BUILD_DIR()).join("CMakeCache.txt") variables = get_cmakecache_variables(str(cmakecache)) generator = variables["CMAKE_GENERATOR"][1] assert generator == orig_generator var_toolset = variables["CMAKE_GENERATOR_TOOLSET"] toolset = var_toolset[1] assert toolset == "v141" scikit-build-0.18.1/tests/test_skbuild_variable.py000066400000000000000000000012511466366435500222620ustar00rootroot00000000000000"""test_skbuild_variable ------------------------ Tries to build the `fail-unless-skbuild-set` sample project. The CMake variable "SKBUILD" must be set in order for the build to succeed. """ from __future__ import annotations from . import project_setup_py_test @project_setup_py_test("fail-unless-skbuild-set", ["build"], disable_languages_test=True) def test_skbuild_variable_builds(): pass @project_setup_py_test("fail-unless-skbuild-set", ["sdist"], disable_languages_test=True) def test_skbuild_variable_sdist(): pass @project_setup_py_test("fail-unless-skbuild-set", ["bdist_wheel"], disable_languages_test=True) def test_skbuild_variable_wheel(): pass scikit-build-0.18.1/tests/test_utils.py000066400000000000000000000145331466366435500201270ustar00rootroot00000000000000"""test_utils ------------------------ Tests for utils functions. """ from __future__ import annotations import os import pytest from skbuild.utils import ( PythonModuleFinder, mkdir_p, push_dir, to_platform_path, to_unix_path, ) from . import SAMPLES_DIR, list_ancestors, push_env saved_cwd = os.getcwd() def setup_module(): """setup any state specific to the execution of the given module.""" os.chdir(os.path.dirname(__file__)) def teardown_module(): """teardown any state that was previously setup with a setup_module method. """ os.chdir(saved_cwd) def test_push_dir(tmpdir): old_cwd = os.getcwd() try: level1 = tmpdir.mkdir("level1") level2 = level1.mkdir("level2") os.chdir(str(level2)) assert os.path.split(os.getcwd())[-1] == "level2" # No directory with push_dir(): os.chdir(os.path.join(os.getcwd(), "..")) assert os.path.split(os.getcwd())[-1] == "level1" assert os.path.split(os.getcwd())[-1] == "level2" # With existing directory with push_dir(directory=os.path.join(os.getcwd(), "..")): assert os.path.split(os.getcwd())[-1] == "level1" assert os.path.split(os.getcwd())[-1] == "level2" foo_directory = os.path.join(str(tmpdir), "foo") # With non existing directory failed = False try: with push_dir(directory=foo_directory): pass except OSError: failed = True assert failed assert not os.path.isdir(foo_directory) # With make_directory option with push_dir(directory=foo_directory, make_directory=True): assert os.getcwd() == foo_directory assert os.path.split(os.getcwd())[-1] == "level2" assert os.path.isdir(foo_directory) finally: os.chdir(old_cwd) def test_push_dir_decorator(tmpdir): old_cwd = os.getcwd() try: level1 = tmpdir.mkdir("level1") level2 = level1.mkdir("level2") os.chdir(str(level2)) assert os.path.split(os.getcwd())[-1] == "level2" # No directory @push_dir() def test_default(): os.chdir(os.path.join(os.getcwd(), "..")) assert os.path.split(os.getcwd())[-1] == "level1" test_default() assert os.path.split(os.getcwd())[-1] == "level2" # With existing directory @push_dir(directory=os.path.join(os.getcwd(), "..")) def test(): assert os.path.split(os.getcwd())[-1] == "level1" test() assert os.path.split(os.getcwd())[-1] == "level2" foo_directory = os.path.join(str(tmpdir), "foo") # With non existing directory failed = False try: @push_dir(directory=foo_directory) def test(): pass test() except OSError: failed = True assert failed assert not os.path.isdir(foo_directory) # With make_directory option @push_dir(directory=foo_directory, make_directory=True) def test(): assert os.getcwd() == foo_directory test() assert os.path.split(os.getcwd())[-1] == "level2" assert os.path.isdir(foo_directory) finally: os.chdir(old_cwd) def test_mkdir_p(tmpdir): tmp_dir = str(tmpdir) assert os.path.isdir(tmp_dir) foo_bar_dir = os.path.join(tmp_dir, "foo", "bar") mkdir_p(foo_bar_dir) assert os.path.isdir(foo_bar_dir) # Make sure calling function twice does not raise an exception mkdir_p(foo_bar_dir) assert os.path.isdir(foo_bar_dir) def test_push_env(): assert "SKBUILD_NEW_VAR" not in os.environ os.environ["SKBUILD_ANOTHER_VAR"] = "abcd" assert "SKBUILD_ANOTHER_VAR" in os.environ saved_env = dict(os.environ) # Setting and un-setting variables can be done simultaneously with push_env(SKBUILD_NEW_VAR="1234", SKBUILD_ANOTHER_VAR=None): assert "SKBUILD_NEW_VAR" in os.environ assert "SKBUILD_ANOTHER_VAR" not in os.environ assert os.getenv("SKBUILD_NEW_VAR") == "1234" assert "SKBUILD_NEW_VAR" not in os.environ assert "SKBUILD_ANOTHER_VAR" in os.environ assert saved_env == dict(os.environ) # Trying to unset an unknown variable should be a no-op with push_env(SKBUILD_NOT_SET=None): assert saved_env == dict(os.environ) assert saved_env == dict(os.environ) # Calling without argument should be a no-op with push_env(): assert saved_env == dict(os.environ) assert saved_env == dict(os.environ) def test_python_module_finder(): modules = PythonModuleFinder(["bonjour", "hello"], {}, []).find_all_modules(os.path.join(SAMPLES_DIR, "hello-cpp")) assert sorted(modules) == sorted( [ ("bonjour", "__init__", to_platform_path("bonjour/__init__.py")), ("hello", "__init__", to_platform_path("hello/__init__.py")), ("hello", "__main__", to_platform_path("hello/__main__.py")), ] ) @pytest.mark.parametrize( ("input_path", "expected_path"), [ (None, None), ("", ""), ("/bar/foo/baz", f"{os.sep}bar{os.sep}foo{os.sep}baz"), ("C:\\bar\\foo\\baz", f"C:{os.sep}bar{os.sep}foo{os.sep}baz"), ("C:\\bar/foo\\baz/", f"C:{os.sep}bar{os.sep}foo{os.sep}baz{os.sep}"), ], ) def test_to_platform_path(input_path, expected_path): assert to_platform_path(input_path) == expected_path @pytest.mark.parametrize( ("input_path", "expected_path"), [ (None, None), ("", ""), ("/bar/foo/baz", "/bar/foo/baz"), ("C:\\bar\\foo\\baz", "C:/bar/foo/baz"), ("C:\\bar/foo\\baz/", "C:/bar/foo/baz/"), ], ) def test_to_unix_path(input_path, expected_path): assert to_unix_path(input_path) == expected_path @pytest.mark.parametrize( ("input_path", "expected_ancestors"), [ ("", []), (".", []), ("part1/part2/part3/part4", ["part1/part2/part3", "part1/part2", "part1"]), ("part1\\part2\\part3\\part4", []), ("/part1/part2/part3/part4", ["/part1/part2/part3", "/part1/part2", "/part1", "/"]), ("C:/part1/part2/part3/part4", ["C:/part1/part2/part3", "C:/part1/part2", "C:/part1", "C:"]), ], ) def test_list_ancestors(input_path, expected_ancestors): assert list_ancestors(input_path) == expected_ancestors