././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/.cirrus.yml0000664000000000000000000000311414706721634015203 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT test_template: &test test_script: - command -v python3 - python3 --version - python3 -m venv env - . env/bin/activate - python3 -m pip install .[test] - python3 -m pytest alpine-3_task: container: dockerfile: ci/alpine-3.docker cpu: 1 << : *test archlinux_task: container: dockerfile: ci/archlinux.docker cpu: 1 << : *test debian-11_task: container: dockerfile: ci/debian-11.docker cpu: 1 << : *test debian-12_task: container: dockerfile: ci/debian-12.docker cpu: 1 << : *test debian-unstable_task: container: dockerfile: ci/debian-unstable.docker cpu: 1 << : *test fedora-37_task: container: dockerfile: ci/fedora-37.docker cpu: 1 << : *test manylinux-python3.13t_task: container: dockerfile: ci/manylinux.docker cpu: 1 env: PATH: "/opt/python/cp313-cp313t/bin/:${PATH}" << : *test manylinux-python3.7_task: container: dockerfile: ci/manylinux.docker cpu: 1 env: PATH: "/opt/python/cp37-cp37m/bin/:${PATH}" << : *test miniconda_task: container: dockerfile: ci/miniconda.docker cpu: 1 << : *test opensuse-15_task: container: dockerfile: ci/opensuse-15.docker cpu: 1 << : *test freebsd_task: freebsd_instance: image_family: freebsd-14-0 install_script: pkg install -y git ninja << : *test macos-arm64_task: macos_instance: image: ghcr.io/cirruslabs/macos-sonoma-xcode install_script: brew install git ninja python@3.11 << : *test ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/.github/workflows/docs.yml0000664000000000000000000000206014706721634020142 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT name: docs on: push: tags: - '[0-9]+.[0-9]+.[0-9]+' pull_request: branches: - main - release-* paths: - docs/** - CHANGELOG.rst - README.md workflow_dispatch: inputs: tag: required: true jobs: build: runs-on: ubuntu-latest steps: - uses: actions/setup-python@v5 with: python-version: '3.11' - uses: actions/checkout@v4 with: ref: ${{ github.event.inputs.tag || github.ref }} - run: python -m pip install .[docs] - run: python -m sphinx -W docs/ build/docs/ - uses: actions/upload-pages-artifact@v3 with: path: build/docs/ deploy: needs: build permissions: pages: write id-token: write environment: name: github-pages runs-on: ubuntu-latest if: ${{ github.event_name == 'workflow_dispatch' || github.ref_type == 'tag' }} steps: - uses: actions/deploy-pages@v4 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/.github/workflows/tests.yml0000664000000000000000000002320114706721634020354 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT name: tests on: push: branches: - main - release-* pull_request: branches: - main - release-* paths: # This is spelt like this to do not ignore the docs/examples/ folder. - '**' - '!docs/**' - 'docs/examples/**' - '!CHANGELOG.rst' - '!LICENSE' - '!LICENSES/**' - '!README.rst' - '!.github/workflows/docs.yml' workflow_dispatch: # Allow to run manually env: FORCE_COLOR: 1 PIP_DISABLE_PIP_VERSION_CHECK: 1 concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true jobs: test: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: - ubuntu-latest # actions/setup-python do not support Python 3.7 on macOS on # arm64. The latest macOS version available is macOS 14 but # it is arm64 only. Stick to macos-13 as long as we want to # test Python 3.7 on macOS. macOS 14 on arm64 is covered by # the Cirrus CI jobs. - macos-13 - windows-latest python: - '3.7' - '3.13' meson: - pyproject_metadata: - include: - os: ubuntu-latest python: 'pypy-3.9' - os: ubuntu-latest python: 'pypy-3.10' - os: ubuntu-latest python: '3.8' - os: ubuntu-latest python: '3.9' - os: ubuntu-latest python: '3.10' # Test with older supported Meson version. Meson up to # version 1.2.3 requires distutils, which has been removed # from the stdlib in Python 3.12, thus test with Pythn 3.11. - os: ubuntu-latest python: '3.11' meson: '~=0.63.3' - os: ubuntu-latest python: '3.11' meson: '~=0.64.0' - os: ubuntu-latest python: '3.11' meson: '~=1.0.0' - os: ubuntu-latest python: '3.11' meson: '~=1.1.0' - os: ubuntu-latest python: '3.12' meson: '~=1.2.3' - os: ubuntu-latest python: '3.12' meson: '~=1.3' # Test with Meson master branch. - os: ubuntu-latest python: '3.12' meson: '@git+https://github.com/mesonbuild/meson.git' - os: windows-latest python: '3.12' meson: '@git+https://github.com/mesonbuild/meson.git' # Test with older supported pyproject-metadata - os: ubuntu-latest python: '3.12' pyproject_metadata: '==0.7.1' - os: ubuntu-latest python: '3.12' pyproject_metadata: '==0.8.0' steps: - name: Checkout uses: actions/checkout@v4 - name: Set up target Python uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} - name: Install Ninja run: sudo apt-get install ninja-build if: ${{ runner.os == 'Linux' }} - name: Install Ninja run: brew install ninja if: ${{ runner.os == 'macOS' }} - name: Install Ninja run: python -m pip install ninja if: ${{ runner.os == 'Windows' }} - name: Install Meson run: python -m pip install "meson ${{ matrix.meson }}" if: ${{ matrix.meson }} - name: Install pyproject-metadata run: python -m pip install "pyproject-metadata ${{ matrix.pyproject_metadata }}" if: ${{ matrix.pyproject_metadata }} - name: Install run: python -m pip install .[test] - name: Run tests run: python -m pytest --showlocals -vv --cov --cov-report=xml - name: Upload coverage report uses: codecov/codecov-action@v4 if: ${{ always() }} env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} msvc: runs-on: windows-latest strategy: fail-fast: false matrix: python: - '3.11' meson: - steps: - name: Checkout uses: actions/checkout@v4 - name: Set up target Python uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} - name: Install Ninja run: python -m pip install ninja - name: Setup MSVC uses: bus1/cabuild/action/msdevshell@e22aba57d6e74891d059d66501b6b5aed8123c4d # v1 with: architecture: x64 - name: Install Meson run: python -m pip install "meson==${{ matrix.meson }}" if: ${{ matrix.meson }} - name: Install run: python -m pip install .[test] - name: Run tests run: python -m pytest --showlocals -vv cygwin: runs-on: windows-latest strategy: fail-fast: false matrix: python: - '3.9' meson: - steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Cygwin uses: cygwin/cygwin-install-action@v2 with: packages: >- python39 python39-devel python39-pip python39-setuptools cmake gcc-core gcc-g++ git make ninja - name: Fix git dubious ownership # This addresses the "fatal: detected dubious ownership in # repository" and "fatal: not in a git directory" errors # encountered when trying to run Cygwin git in a directory not # owned by the current user. This happens when the tests run # Cygwin git in a directory outside the Cygwin filesystem. run: git config --global --add safe.directory '*' shell: C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 CHERE_INVOKING=1 C:\cygwin\bin\bash.exe -leo pipefail -o igncr {0} - name: Get pip cache path id: pip-cache-path run: echo "path=$(cygpath -w $(python -m pip cache dir))" >> $GITHUB_OUTPUT shell: C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 CHERE_INVOKING=1 C:\cygwin\bin\bash.exe -leo pipefail -o igncr {0} - name: Restore cache # Cygwin Python cannot use binary wheels from PyPI. Building # some dependencies takes considerable time. Caching the built # wheels speeds up the CI job quite a bit. uses: actions/cache@v4 with: path: ${{ steps.pip-cache-path.outputs.path }} key: cygwin-pip-${{ github.sha }} restore-keys: cygwin-pip- - name: Install Meson run: python -m pip install "meson ${{ matrix.meson }}" if: ${{ matrix.meson }} shell: C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 CHERE_INVOKING=1 C:\cygwin\bin\bash.exe -leo pipefail -o igncr {0} - name: Install # Cygwin patches Python's ensurepip module to look for the # wheels needed to initialize a new virtual environment in # /usr/share/python-wheels/ but nothing in Cygwin actually # puts the setuptools and pip wheels there. Fix this. run: | mkdir /usr/share/python-wheels/ pushd /usr/share/python-wheels/ python -m pip download setuptools pip popd python -m pip install .[test] shell: C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 CHERE_INVOKING=1 C:\cygwin\bin\bash.exe -leo pipefail -o igncr {0} - name: Run tests run: python -m pytest --showlocals -vv shell: C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 CHERE_INVOKING=1 C:\cygwin\bin\bash.exe -leo pipefail -o igncr {0} pyston: runs-on: ubuntu-20.04 strategy: fail-fast: false matrix: python: - '3.8' meson: - steps: - name: Checkout uses: actions/checkout@v4 - name: Install pyston run: | wget https://github.com/pyston/pyston/releases/download/pyston_2.3.5/pyston_2.3.5_20.04_amd64.deb sudo apt install $(pwd)/pyston_2.3.5_20.04_amd64.deb - name: Install Ninja run: sudo apt-get install ninja-build - name: Install Meson run: python -m pip install "meson ${{ matrix.meson }}" if: ${{ matrix.meson }} - name: Install run: pyston -m pip install .[test] - name: Run tests run: pyston -m pytest --showlocals -vv homebrew: runs-on: macos-latest strategy: fail-fast: false matrix: python: - '3.9' - '3.12' meson: - steps: - name: Checkout uses: actions/checkout@v4 - name: Install Homebrew Python run: | brew install --overwrite python@${{ matrix.python }} echo /usr/local/opt/python@${{ matrix.python }}/libexec/bin/ >> $GITHUB_PATH - name: Install Ninja run: brew install ninja - name: Update pip # pip >= 23.0 fixes https://github.com/pypa/pip/issues/11539 run: python -m pip install --upgrade "pip >= 23.0" - name: Install Meson run: python -m pip install "meson ${{ matrix.meson }}" if: ${{ matrix.meson }} - name: Install run: python -m pip install .[test] - name: Run tests run: python -m pytest --showlocals -vv mypy: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v5 with: python-version: 3.9 - name: Install dependencies run: python -m pip install . - name: Install mypy run: python -m pip install mypy==1.5.1 - name: Run mypy run: mypy -p mesonpy ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/.gitignore0000664000000000000000000000025214706721634015063 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT .mesonpy-native-file.ini .mesonpy/ docs/_build *.pyc .cache/ dist/ .coverage ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/.mailmap0000664000000000000000000000024714706721634014520 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT Matthias Köppe Yue Yang ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/.pre-commit-config.yaml0000664000000000000000000000173614706721634017364 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT ci: autofix_prs: false autoupdate_commit_msg: 'MAINT: bump pre-commit dependencies' autoupdate_schedule: 'quarterly' repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 hooks: - id: check-ast - id: check-builtin-literals - id: check-docstring-first - id: check-merge-conflict - id: check-yaml - id: check-toml - id: debug-statements - id: double-quote-string-fixer - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.6.9 hooks: - id: ruff args: [--fix, --show-fixes, --output-format, grouped] - repo: https://github.com/fsfe/reuse-tool rev: v4.0.3 hooks: - id: reuse name: add SPDX headers args: [annotate, --skip-existing, --skip-unrecognised, --copyright=The meson-python developers, --license=MIT] pass_filenames: true - id: reuse name: reuse lint ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/.readthedocs.yaml0000664000000000000000000000036414706721634016326 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT version: 2 build: os: ubuntu-22.04 tools: python: "3.11" python: install: - method: pip path: . extra_requirements: [docs] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/CHANGELOG.rst0000664000000000000000000003165114706721634015123 0ustar00rootroot.. SPDX-FileCopyrightText: 2021 The meson-python developers .. .. SPDX-License-Identifier: MIT .. Contributors list for the latest release can be generated with git log --format='%aN' $PREV..HEAD | sort -u | awk '$1=$1' RS='' FS='\n' OFS=', ' +++++++++ Changelog +++++++++ 0.17.1 ====== - Update tests to work with newly released ``pyproject-metadata`` 0.9.0. - Fix tests to work when not executed in a git work tree. Daniele Nicolodi --- 23-10-2024. 0.17.0 ====== - Refuse to build wheels targeting the limited API and free-threaded CPython: the free-threaded CPython build does not support the limited API yet. - Always use UTF-8 encoding for writing Meson native and cross files. Always use UTF-8 encoding for reading read Meson introspection data and ``pyproject.toml``. - Do not include uncommitted changes when creating source distributions. Previously, uncommitted changes to files under version control were included, but not untracked files. There was no strong use case for this behavior and it was too surprising to keep it. - Make source distribution reproducible: use the modification time of ``pyproject.toml`` for the generated files in the source distribution archives. - Disable the ``abi3`` wheel tag for PyPy when building wheels targeting the limited API: PyPy supports the limited API but not the stable ABI. - Raise ``ImportError`` when the package rebuild fails when importing an editable install. - Fix the wheel platform tag for GraalPy. - Add ``.gitignore`` and ``.hgignore`` files to build directory if it is empty. - Allow ``install_subdir()`` of missing directories. Christian Clauss, Daniele Nicolodi, Jonathan J. Helmus, Leo Singer, Loïc Estève, Michael Simacek, Ralf Gommers, Simon McVittie --- 19-10-2024. 0.16.0 ====== - Correctly detect a wheel shipping shared libraries as being platform dependent also when it does not ship compiled executables or Python extension modules. - Use 11.0 as minimum macOS platform ABI tag on arm64: lower versions do not exist on arm64. - Improve parsing of the ``$ARCHFLAGS`` environment variable. Correctly handle repeated ``-arch`` flags. - Do not break when trying to log filename containing Unicode characters that cannot be encoded in the encoding used by the standard output stream. - Drop dependency on ``colorama`` on Windows. - Suppress all uses of ANSI terminal escapes when logging to a stream that does not support them. - Fix ANSI escape coloring of error messages. - Respect the ``exclude_dirs`` and ``exclude_files`` arguments passed to the ``install_subdir()`` Meson function in editable installs too. - Make ``pkgutil.iter_packages()`` work properly for modules loaded from editable installs. - Add support for the ``custom_target()`` Meson function installing a directory. - In editable installs, when a rebuild is triggered on module import, emit a message indicating that the package is being rebuilt but suppress any output when there is no work to do. Daniele Nicolodi, Loïc Estève, Ralf Gommers --- 16-04-2024 0.15.0 ====== - Enable compression for wheel files. The may result in several times smaller wheels. - Require Meson 1.2.3 or later on Python 3.12 or later. Meson 1.2.3 does not require anymore ``distutils``, allowing to remove the dependency on ``setuptools`` on Python 3.12 or later. - Unconditionally require ``patchelf`` on Linux. The ``patchelf`` package is added to the build dependencies if a suitable ``patchelf`` executable is not find on the ``$PATH``. This avoids cases where ``meson setup`` was run twice during the build process to determine whether ``patchelf`` is required. - Allow to configure the ``meson`` executable to use for the build process through the ``$MESON`` environment variable or the ``meson`` key under ``[tool.meson-python]`` in ``pyproject.toml``. - Fix wheel platform tag generation on FreeBSD. - Extend support to other UNIX-like systems and make the tests pass on FreeBSD. - Fix package name normalization in package metadata and improve package name validation. - Fix ``RPATH`` handling when the build ``RPATH`` points to subdirectories of the build directory. - Fix support for the Python limited C API when compiling for PyPy. - Rename the ``builddir`` config-setting to ``build-dir``. For backwards compatibility, the ``buildir`` config-setting remains supported as an alias. Christoph Reiter, Daniele Nicolodi, Elliott Sales de Andrade, Ralf Gommers, Yue Yang --- 26-10-2023 0.14.0 ====== - Do not run ``meson install`` to build a wheel. This was unnecessary as files are added to the wheel from the build and source directories. This does not affect the handling of ``meson install`` options, which are interpreted by ``meson-python`` itself. - Obey the ``--skip-subprojects`` when specified for the ``meson install`` command. - Implement support for the ``exclude_directories`` and ``exclude_files`` arguments to Meson ``install_subdir()`` function and similar installation functions. This requires Meson version 1.1.0 or later. - Implement support for building wheels targeting the Python limited API. Extension modules targeting the Python limited API can be easily built starting with the upcoming Meson 1.3.0 release. - When ``pyproject.toml`` does not contain a ``version`` field and ``version`` is not declared dynamic, raise an error instead of silently using the version declared in ``meson.build``. - Fix the mtime of source files in the sdist tarball. - Add ``objc`` and ``objcpp`` compilers to the cross file generated when the ``$ARCHFLAGS`` is set. - Extensive documentation improvements. Charles Brunet, Daniele Nicolodi, Henry Schreiner, Michał Górny, Ralf Gommers --- 05-09-2023 0.13.2 ====== - Fix system name in cross file generated when using ``$ARCHFLAGS``. - Fix handling of ``null`` Meson install tags. Charles Brunet, Daniele Nicolodi --- 22-06-2023. 0.13.1 ====== - Fix regression in cross-compilation via ``$ARCHFLAGS`` on macOS where the cross file was written in the build directory before it was created, resulting in an error. - Do not require setting ``$_PYTHON_HOST_PLATFORM`` when cross-compiling via ``$ARCHFLAGS`` on macOS. - Add the ``--quiet`` option when invoking ``meson install``. The installation paths are a detail of the ``meson-python`` implementation and are generally not interesting for the user. - Fix terminal logging when overriding the current line when listing files added to the wheel. - Improve the error message emitted when a package split between the ``purelib`` and ``platlib`` wheel locations is detected. Daniele Nicolodi, Ralf Gommers --- 28-04-2023. 0.13.0 ====== - Add support for editable installs. - Adjust the default build options passed to ``meson setup``. - Make sure that the directory where the wheel or sdist build artifacts are created exists. Fixes building with PDM. - Fix the specification of the C++ compiler for cross-compilation with ``$ARCHFLAGS`` on macOS. - Pass the ``--reconfigure`` option to ``meson setup`` if and only if the specified build directory exists and is a valid Meson build directory. - Pass the ``--no-rebuild`` option to ``meson install``. - Allow to select the files to be included in the wheel via Meson install tags passing the ``--tags`` option to ``meson install`` via ``pyproject.toml`` or config settings. - Do not use the ``meson compile`` indirection to build the project, except on Windows, where it is required to setup the Visual Studio environment. - Do not add ``ninja`` to the build dependencies if ``$NINJA`` is set but it does not point to a ``ninja`` executable with the required minimum version. - Verify at run time that Meson satisfies the minimum required version. - Place native and cross files in the build directory instead of in the source directory. - Drop the ``typing-extensions`` package dependency. - Add dependency on ``setuptools`` on Python 3.12 and later. This fixes build error due to Meson depending on the ``distutils`` standard library module removed in Python 3.12. - Bump the required ``pyproject-metadata`` version to 0.7.1. - Allows some more cross-compilation setups by not checking extension modules filename suffixes against the suffixes accepted by the current interpreter. - Raise an error when a file that would be installed by Meson cannot be mapped to a wheel location. - Raise an error when a package is split between ``platlib`` and ``purelib``. - Do not generate a warning when ``pyproject.toml`` does not contain a ``project`` section and Python package metadata is derived from ``meson.build``. - Improve reporting of ``pyproject.toml`` validation errors. - Fix validation of tool specific options in ``pyproject.toml``. In particular, allows to specify an incomplete set of options in the ``tool.meson-python.args`` table. Daniele Nicolodi, Doron Behar, Eli Schwartz, Filipe Laíns, Lars Pastewka, Luigi Giugliano, Matthias Köppe, Peter Urban, Ralf Gommers, Stefan van der Walt, Thomas Li --- 18-04-2023. 0.12.1 ====== - Fix regression where the ``$MACOSX_DEPLOYMENT_TARGET`` environment variable was accidentally renamed to ``$MACOS_DEPLOYMENT_TARGET``. Filipe Laíns, Stefan van der Walt --- 17-02-2023. 0.12.0 ====== - Require the ``typing_extensions`` package for Python < 3.10 rather than for Python < 3.8 only. - Emit an error message and raise ``SystemExit`` on expected errors. - Revise error messages for consistency. - Support setuptools-style macOS cross compilation via ``$ARCHFLAGS``. - Allow to overwrite macOS platform tag via ``$_PYTHON_HOST_PLATFORM``. - Include an hint with the most similar known option names in the error message emitted when an unknown config setting is encountered. Daniele Nicolodi, Filipe Laíns, Henry Schreiner, Matthias Köppe, Thomas A Caswell --- 22-12-2022. 0.11.0 ====== - Project moved to the ``mesonbuild`` organization. - Determine wheel tags by introspecting the Python interpreter. - Allow users to pass options directly to Meson via the ``dist``, ``setup``, ``compile``, and ``install`` entries in the ``tools.meson-python.args`` table in ``pyproject.toml``, or via the ``dist-args``, ``setup-args``, ``compile-args``, and ``install-args`` config settings. - Use the system ``ninja`` if possible. Return ``ninja`` as a build dependency otherwise. - Include files generated by ``mesonadd_dist_script`` in the sdist. - Use ``tomllib`` on Python 3.11 or later. - Drop the ``wheel`` package dependency. - Fix bug where the ``entry_points.txt`` file was not generated. - Fix bug where Cygwin Python extensions were not being noticed. Ben Greiner, Daniele Nicolodi, Filipe Laíns, Henry Schreiner, Matthias Köppe, Ralf Gommers, Sam Thursfield, Thomas Li --- 21-11-2022. 0.10.0 ====== - Ignore the minor version on macOS 11 or later, to match the behavior of ``pypa/packaging``. Filipe Laíns, Ralf Gommers --- 05-10-2022. 0.9.0 ===== - More fixes on ABI tag detection. - Fix incorrect tag on 32-bit Python running on a x86_64 host. - Fix sdist permissions. - Fix incorrect PyPy tags. - Fix ``install_subdirs`` not being included in wheels. - Take ``MACOSX_DEPLOYMENT_TARGET`` into account for the platform tag. - Don't set the rpath on binaries if unneeded. Eli Schwartz, Filipe Laíns, Matthias Köppe, Peyton Murray, Ralf Gommers, Thomas Kluyver, Thomas Li --- 29-09-2022. 0.8.1 ===== - Fix ``UnboundLocalError`` in tag detection code. Filipe Laíns, Ralf Gommers --- 28-07-2022. 0.8.0 ===== - Fix sometimes the incorrect ABI tags being generated. - Add workaround for macOS 11 and 12 installations that are missing a minor version in the platform string. Filipe Laíns --- 26-07-2022. 0.7.0 ===== - Fix the wrong Python and ABI tags being generated in Meson 0.63.0. - Fix project license not being included in the project metadata. Filipe Laíns, Ralf Gommers --- 22-07-2022. 0.6.0 ===== - Project re-licensed to MIT. - Error out when running in an unsupported interpreter. - Fix slightly broken Debian heuristics. - Update ``pep621`` dependency to ``pyproject-metadata``. Filipe Laíns, Ralf Gommers, Thomas A Caswell --- 21-06-2022. 0.5.0 ===== - Improvements in dependency detections. - Include uncommited changes in sdists. Filipe Laíns --- 26-05-2022. 0.4.0 ===== - Set sane default arguments for release builds. Filipe Laíns --- 06-05-2022. 0.3.0 ===== - Initial cross-platform support. - Bundling libraries is still only supported on Linux. - Add initial documentation. - The build directory is now located in the project source. Filipe Laíns, Rafael Silva --- 23-03-2022. 0.2.1 ===== - Fix getting the project version dynamically from Meson. Filipe Laíns --- 26-02-2022. 0.2.0 ===== - Select the correct ABI and Python tags. - Force Meson to use the correct Python executable. - Replace auditwheel with in-house vendoring mechanism. Filipe Laíns --- 24-01-2022. 0.1.2 ===== - Fix auditwheel not being run. Filipe Laíns --- 12-11-2021. 0.1.1 ===== - Fix minor compatibility issue with Python < 3.9. Filipe Laíns --- 28-10-2021. 0.1.0 ===== - Initial release. Filipe Laíns --- 28-10-2021. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/LICENSE0000664000000000000000000000220314706721634014076 0ustar00rootrootCopyright © 2022 the meson-python contributors Copyright © 2021 Quansight Labs and Filipe Laíns Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/LICENSES/MIT.txt0000664000000000000000000000220314706721634015470 0ustar00rootrootCopyright © 2022 the meson-python contributors Copyright © 2021 Quansight Labs and Filipe Laíns Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/README.rst0000664000000000000000000000172214706721634014565 0ustar00rootroot.. SPDX-FileCopyrightText: 2021 The meson-python developers .. SPDX-License-Identifier: MIT meson-python ============ ``meson-python`` is a Python build back-end built on top of the Meson__ build system. It enables using Meson for the configuration and build steps of Python packages. Meson is an open source build system meant to be both extremely fast, and, even more importantly, as user friendly as possible. ``meson-python`` is best suited for building Python packages containing extension modules implemented in languages such as C, C++, Cython, Fortran, Pythran, or Rust. Consult the documentation__ for more details. Questions regarding the use of ``meson-python`` can be directed to the discussions__ space on GitHub. Bug reports and feature requests can be filed as GitHub issues__. __ https://mesonbuild.com/ __ https://mesonbuild.com/meson-python/ __ https://github.com/mesonbuild/meson-python/discussions/ __ https://github.com/mesonbuild/meson-python/issues/ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/RELEASE.rst0000664000000000000000000000311214706721634014703 0ustar00rootroot.. SPDX-FileCopyrightText: 2023 The meson-python developers .. .. SPDX-License-Identifier: MIT Release Process =============== All releases are PGP signed with one of the keys listed in ``docs/about.rst``. Before releasing please make sure your PGP key is listed there, and preferably signed by one of the other key holders. If your key is not signed by one of the other key holders, please make sure that the PR that added your key to ``docs/about.rst`` was approved by at least one other maintainer. After that is done, you may release the project by following these steps: #. Release to the Git repository on GitHub: #. Create the release commit #. Bump the versions in ``meson.build`` and ``mesonpy/__init__.py``. #. Create ``CHANGELOG.rst`` section for the new release and fill it. #. The commit message should read: ``REL: set version to X.Y.Z`` #. Create a GPG-signed tag for the release: .. code-block:: console $ git tag -s X.Y.Z The tag title should follow the ``meson-python X.Y.Z`` format, and the tag body should be a plain text version of the change-log for the current release. #. Push the commit and tag to the repository: .. code-block:: console $ git push $ git push --tags #. Release to PyPI: #. Build the Python artifacts: .. code-block:: console $ python -m build #. Push the artifacts to PyPI: .. code-block:: console $ twine upload dist/* There is no need to GPG-sign the artifacts: PyPI no longer supports uploading GPG signatures. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/ci/alpine-3.docker0000664000000000000000000000031714706721634016271 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT # 20240817 FROM alpine:3 RUN apk add --no-cache python3-dev py3-pip build-base ninja-is-really-ninja git patchelf ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/ci/archlinux.docker0000664000000000000000000000033414706721634016655 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT # 20221203 FROM archlinux:latest RUN pacman -Syu --noconfirm && pacman -S --noconfirm python python-pip gcc ninja git patchelf ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/ci/debian-11.docker0000664000000000000000000000036714706721634016327 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT # 20240204 FROM debian:bullseye RUN apt-get update && apt-get install -y git ninja-build patchelf python3-pip python3-venv && rm -rf /var/lib/apt/lists/* ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/ci/debian-12.docker0000664000000000000000000000036714706721634016330 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT # 20240204 FROM debian:bookworm RUN apt-get update && apt-get install -y git ninja-build patchelf python3-pip python3-venv && rm -rf /var/lib/apt/lists/* ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/ci/debian-unstable.docker0000664000000000000000000000036714706721634017723 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT # 20230816 FROM debian:unstable RUN apt-get update && apt-get install -y git ninja-build patchelf python3-pip python3-venv && rm -rf /var/lib/apt/lists/* ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/ci/fedora-37.docker0000664000000000000000000000034314706721634016347 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT # 20240204 FROM fedora:37 RUN dnf -y update && dnf -y install python3-devel python3-pip gcc ninja-build git patchelf && dnf clean all ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/ci/manylinux.docker0000664000000000000000000000032114706721634016700 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT # 20240513 FROM quay.io/pypa/manylinux_2_28_x86_64 RUN dnf -y update && dnf -y install ninja-build && dnf clean all ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/ci/miniconda.docker0000664000000000000000000000035114706721634016620 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT # 20240204 FROM continuumio/miniconda3 RUN apt-get update && apt-get install -y gcc ninja-build git patchelf && rm -rf /var/lib/apt/lists/* ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/ci/opensuse-15.docker0000664000000000000000000000044114706721634016743 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT # 20240817 FROM opensuse/leap:latest RUN zypper --non-interactive install python311 python311-pip python311-devel gcc ninja git patchelf && zypper clean --all && ln -s python3.11 /usr/bin/python3 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/codecov.yml0000664000000000000000000000037614706721634015247 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT comment: false coverage: status: project: default: target: auto threshold: 10% patch: default: informational: true ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/docs/about.rst0000664000000000000000000000403714706721634015674 0ustar00rootroot.. SPDX-FileCopyrightText: 2023 The meson-python developers .. .. SPDX-License-Identifier: MIT License ======= ``meson-python`` is distributed under the terms of the `MIT License`__. ``meson-python`` is tightly coupled to the Meson build system, which is distributed under the the terms of the `Apache License 2.0`__. __ https://github.com/mesonbuild/meson-python/blob/main/LICENSE __ https://github.com/mesonbuild/meson/blob/master/COPYING Maintainers =========== The current maintainers of ``meson-python`` are: - `Ralf Gommers `_ - `Daniele Nicolodi `_ - `Henry Schreiner `_ - `Thomas Li `_ Emeritus maintainers: - `Filipe Laíns `_ Funding ======= Ralf Gommers contributes to the development of ``meson-python`` as part of their job at `Quansight`_. Initial creation of ``meson-python`` by Filipe Laíns was also funded by `Quansight`_. .. _Quansight: https://www.quansight.com/ Packages ======== ``meson-python`` packages are distributed via `PyPI`_ as a source distribution and a single pure Python wheel that works across all supported Python versions. Distributors are recommended to fetch release sources from the corresponding tags on the Git repository_ or from source distributions on `PyPI`_. Git tags are PGP-signed with one of the following keys: - |3DCE51D60930EBA47858BA4146F633CBB0EB4BF2|_ Filipe Laíns - |52BDC33FD1FBAB569D47FFA923D9E5499A08BDC5|_ Ralf Gommers .. _repository: https://github.com/mesonbuild/meson-python .. _PyPI: https://pypi.org/project/meson-python/ .. |3DCE51D60930EBA47858BA4146F633CBB0EB4BF2| replace:: ``3DCE51D60930EBA47858BA4146F633CBB0EB4BF2`` .. _3DCE51D60930EBA47858BA4146F633CBB0EB4BF2: https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3dce51d60930eba47858ba4146f633cbb0eb4bf2 .. |52BDC33FD1FBAB569D47FFA923D9E5499A08BDC5| replace:: ``52BDC33FD1FBAB569D47FFA923D9E5499A08BDC5`` .. _52BDC33FD1FBAB569D47FFA923D9E5499A08BDC5: https://github.com/rgommers.gpg ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/docs/conf.py0000664000000000000000000000611314706721634015324 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT # Configuration file for the Sphinx documentation builder. # # This file only contains a selection of the most common options. For a full # list see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html # -- Path setup -------------------------------------------------------------- # 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. import datetime import os import time import sys sys.path.insert(0, os.path.abspath('..')) import mesonpy build_date = datetime.datetime.fromtimestamp( int(os.environ.get('SOURCE_DATE_EPOCH', time.time())), tz=datetime.timezone.utc, ) # -- Project information ----------------------------------------------------- project = 'meson-python' copyright = f'2021\N{EN DASH}{build_date.year} The meson-python developers' # The short X.Y version version = mesonpy.__version__ # The full version, including alpha/beta/rc tags release = mesonpy.__version__ # -- General configuration --------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.todo', 'sphinx_copybutton', 'sphinx_design', 'sphinxext.opengraph', 'sphinx.ext.intersphinx', ] try: import sphinxcontrib.spelling extensions.append('sphinxcontrib.spelling') spelling_show_suggestions = True spelling_warning = True except ImportError: pass # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. exclude_patterns = [] default_role = 'any' todo_include_todos = True intersphinx_mapping = {'python': ('https://docs.python.org/3', None)} # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'furo' html_title = f'meson-python {version}' html_static_path = ['static'] html_css_files = [ 'css/contributors.css', ] html_theme_options = { 'light_css_variables': { 'font-stack': ( 'system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,' 'Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji' ), }, } # Open Graph ogp_site_url = 'https://mesonbuild.com/meson-python/' ogp_site_name = 'meson-python documentation' # 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'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/docs/examples/spam/meson.build0000664000000000000000000000142714706721634020750 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT project('spam', 'c', version: '1.0.0') py = import('python').find_installation() # Specify directories to be included - usually this is where your header files live. # Note that specifying the path to Python.h is not necessary with meson-python. includes = include_directories('src') srcs = [ 'src/spammodule.c', ] # If some files (such as .py files) need to be copied to site-packages, # this is where they get specified. Files get copied to # /site-packages/ py.install_sources( 'src/__init__.py', subdir: 'spam', pure: false, ) py.extension_module( '_spam', srcs, install: true, subdir: 'spam', include_directories: includes, ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/docs/examples/spam/pyproject.toml0000664000000000000000000000043714706721634021522 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] requires = [ "meson-python", ] build-backend = "mesonpy" [project] name = "spam" description = "Example C extension for Python" version = "0.0.1" requires-python = ">=3.8.0" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/docs/examples/spam/src/__init__.py0000664000000000000000000000020214706721634021474 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT from ._spam import add # noqa: F401 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/docs/examples/spam/src/spammodule.c0000664000000000000000000000116514706721634021706 0ustar00rootroot// SPDX-FileCopyrightText: 2022 The meson-python developers // // SPDX-License-Identifier: MIT #define PY_SSIZE_T_CLEAN #include static PyObject * add(PyObject *self, PyObject *args) { int a, b; if (!PyArg_ParseTuple(args, "ii", &a, &b)) return NULL; return PyLong_FromLong(a + b); } static PyMethodDef methods[] = { {"add", add, METH_VARARGS, "Add two integers."}, {NULL, NULL, 0, NULL} }; static struct PyModuleDef spammodule = { PyModuleDef_HEAD_INIT, "spam", NULL, -1, methods }; PyMODINIT_FUNC PyInit__spam(void) { return PyModule_Create(&spammodule); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/docs/explanations/default-options.rst0000664000000000000000000000777714706721634022422 0ustar00rootroot.. SPDX-FileCopyrightText: 2023 The meson-python developers .. .. SPDX-License-Identifier: MIT .. _explanations-default-options: ********************* Default build options ********************* Meson offers many `built-in options`__ to control how the project is built and installed. In the vast majority of cases those have good defaults. There are however a few options that ``meson-python`` overrides with its own defaults to adjust the build process to the task of building Python wheels. The default options specified by ``meson-python`` are overridden by package specific options specified in ``pyproject.toml`` and by options provided by the user at build time via the Python build front-end. Refer to the :ref:`how-to-guides-meson-args` guide for details. The options used to build the project are summarized in the *User defined options* section of the output of the ``meson setup`` stage of the build, for example when running ``python -m build -w``. This will look something like: __ https://mesonbuild.com/Builtin-options.html .. code-block:: text User defined options Native files: $builddir/meson-python-native-file.ini buildtype : release b_ndebug : if-release b_vscrt : md where the path to the build directory has been replaced with ``$builddir`` for clarity. The options that ``meson-python`` specifies by default are: .. option:: native-file=$builddir/meson-python-native-file.ini ``meson-python`` uses a native file to point Meson at the ``python`` interpreter that the build must target. This is the Python interpreter that is used to run the Python build front-end. Meson would otherwise look for the first Python interpreter on the ``$PATH``, which may not be the same. Additional ``--native-file`` options can be passed to ``meson setup`` if further adjustments to the native environment need to be made. Meson will merge the contents of all machine files. To ensure everything works as expected, the ``meson-python`` native file is last in the command line, overriding the ``python`` binary path that may have been specified in user supplied native files. .. option:: buildtype=release The Meson default is to produce a debug build with binaries compiled with debug symbols and, when compiling with MSVC, linking to the Visual Studio debug runtime, see below. The main purpose of ``meson-python`` is to build release artifacts, therefore a more appropriate `build type`__ is selected. A release build is compiled without debug symbols and with compiler optimizations. Refer to the `Meson documentation`__ for more details. __ https://mesonbuild.com/Builtin-options.html#details-for-buildtype __ https://mesonbuild.com/Builtin-options.html#core-options .. option:: b_ndebug=if-release For reasons related to backward compatibility, Meson does not disable assertions for release builds. For most users this is a surprising and undesired behavior. This option instructs Meson to pass the ``-DNDEBUG`` option to the compilers, unless the build type is set to something else than release. .. option:: b_vscrt=md With the default options, when compiling a debug build, Meson instructs the MSVC compiler to use the debug version of the Visual Studio runtime library. This causes the MSVC linker to look for the debug build of all the linked DLLs. The Python distribution for Windows does not contain a debug version of the Python DLL and linking fails. These linking failures are surprising and hard to diagnose. To avoid this issue when users explicitly asks for a debug build, ``meson-python`` sets this options to instruct Meson to compile with the release version of the Visual Studio runtime. For more details, refer to the `Meson documentation`__ and to the `Visual Studio documentation`__ . This option is ignored when other compilers are used. __ https://mesonbuild.com/Builtin-options.html#base-options __ https://learn.microsoft.com/en-us/cpp/build/reference/md-mt-ld-use-run-time-library?view=msvc-170 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/docs/explanations/design-old.rst0000664000000000000000000000557514706721634021324 0ustar00rootroot.. SPDX-FileCopyrightText: 2023 The meson-python developers .. .. SPDX-License-Identifier: MIT :orphan: .. _explanations-design-old: ******************************* How does ``meson-python`` work? ******************************* .. admonition:: Old documentation :class: attention You are looking at the old documentation. We are currently in the process of refactoring the whole documentation and this page was part of the old version. While it still contains lots of relevant information, it is not necessarily in the format we ultimately want to present, and is not well integrated with the other parts of the documentation. How does it work? ================= We implement the Python build system hooks, enabling any standards compliant Python tool (pip_, `pypa/build`_, etc.) to build and install the project. ``meson-python`` will build a Python sdist (source distribution) or wheel (binary distribution) from Meson_ project. Binary distribution (wheels) ---------------------------- The binary distribution is built by running Meson_ and introspecting the build and trying to map the files to their correct location. Due to some limitations and/or bugs in Meson_, we might not be able to map some of the files with certainty. In these cases, we will take the safest approach and warn the user. In some cases, we might need to resort to heuristics to perform the mapping, similarly, the user will be warned. These warnings are not necessarily a reason for concern, they are there to help identifying issues. In these cases, we recommend that you check if the files in question were indeed mapped to the expected place, and open a bug report if they weren't. If the project itself includes shared libraries that are needed by other files, those libraries will be included in the wheel, and native files will have their library search path extended to include the directory where the libraries are placed. If the project depends on external shared libraries, those libraries will not be included in the wheel. This can be handled in several ways by the package developer using ``meson-python``: 1. Finding or creating a suitable Python package that provides those shared libraries and can be added to ``dependencies`` in ``pyproject.toml``. 2. Vendoring those libraries into the wheel. Currently ``meson-python`` does not provide an OS-agnostic way of doing that; on Linux ``auditwheel`` can be used, and on macOS ``delocate``. Or, the package developer can copy the shared libraries into the source tree and patch ``meson.build`` files to include them. 3. Documenting that the dependency is assumed to already be installed on the end user's system, and letting the end user install it themselves (e.g., through their Linux distro package manager). .. _pip: https://github.com/pypa/pip .. _pypa/build: https://github.com/pypa/build .. _Meson: https://github.com/mesonbuild/meson ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/docs/how-to-guides/config-settings.rst0000664000000000000000000001105014706721634022351 0ustar00rootroot.. SPDX-FileCopyrightText: 2023 The meson-python developers .. .. SPDX-License-Identifier: MIT .. _how-to-guides-config-settings: *************************** Using build config settings *************************** Build config settings can be used to customize some aspects of the build. See the :doc:`../reference/config-settings` reference for a list of the settings implemented by ``meson-python``. How build config settings are specified depends on the Python package build front-end used. The most popular build front-end are `build`_ and `pip`_. These use the ``--config-settings`` long command line option or the ``-C`` short command line option: .. tab-set:: .. tab-item:: pypa/build :sync: key_pypa_build .. code-block:: console $ python -m build \ -Csetup-args="-Doption=true" \ -Csetup-args="-Dvalue=1" \ -Ccompile-args="-j6" .. tab-item:: pip :sync: key_pip .. code-block:: console $ python -m pip wheel . \ --config-settings=setup-args="-Doption=disable" \ --config-settings=compile-args="-j6" Refer to the `build`_ and `pip`_ documentation for details. This example uses the ``python -m pip wheel`` command to build a Python wheel that can be later installed or distributed. To build a package and immediately install it, replace ``wheel`` with ``install``. See the :ref:`how-to-guides-meson-args` guide for more examples. .. admonition:: Passing multiple settings :class: caution Please note that ``pip`` prior to 23.1 did not offer a way to set a build config setting to a list of strings: later values for the same key passed to ``--config-settings`` override earlier ones, effectively limiting the number of options that can be passed to each command invoked in the build process to one. This limitation has been lifted in ``pip`` release 23.1. .. _build: https://pypa-build.readthedocs.io/en/stable/ .. _pip: https://pip.pypa.io/ Using a persistent build directory ================================== By default, ``meson-python`` uses a temporary build directory which is deleted when the build terminates. A persistent build directory allows faster incremental builds and to access build logs and intermediate build artifacts. The ``build-dir`` :ref:`config setting ` instructs ``meson-python`` to use a user-specified build directory which will not be deleted. For example: .. tab-set:: .. tab-item:: pypa/build :sync: key_pypa_build .. code-block:: console $ python -m build -Cbuild-dir=build .. tab-item:: pip :sync: key_pip .. code-block:: console $ python -m pip install . -Cbuild-dir=build After running this command, the ``build`` directory will contain all the build artifacts and support files created by ``meson``, ``ninja`` and ``meson-python``. The same build directory can be used by subsequent invocations of ``meson-python``, avoiding the need to rebuild the whole project when testing changes during development. Using a permanent build directory also aids in debugging a failing build by allowing access to build logs and intermediate build outputs, including the Meson introspection files and detailed log. The latter is stored in the ``meson-logs/meson-log.txt`` file in the build directory and can be useful to diagnose why a build fails at the project configuration stage. For example, to understand why dependency detection failed, it is often necessary to look at the ``pkg-config`` or other dependency detection methods output. Access to detailed logs and intermediate build outputs is particularly helpful in CI setups where introspecting the build environment is usually more difficult than on a local system. Therefore, it can be useful to show the more detailed log files when the CI build step fails. For example, the following GitHub Actions workflow file snippet shows the detailed Meson setup log when the build fails: .. code-block:: yaml - name: Build the package run: python -m build --wheel -Cbuild-dir=build - name: Show meson-log.txt if: failure() run: cat build/meson-logs/meson-log.txt Replacing ``failure()`` with ``always()`` in the code above will result in the Meson log file always being shown. See the GitHub Actions documentation__ for more details. Please be aware that the setup log can become very long for complex projects, and the GitHub Actions web interface becomes unresponsive when the running job emits many output lines. __ https://docs.github.com/en/actions/learn-github-actions/expressions#status-check-functions ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/docs/how-to-guides/debug-builds.rst0000664000000000000000000000423014706721634021616 0ustar00rootroot.. SPDX-FileCopyrightText: 2023 The meson-python developers .. .. SPDX-License-Identifier: MIT .. _how-to-guides-debug-builds: ****************** Using debug builds ****************** For development work on native code in your Python package, you may want to use a debug build. To do so, we need to pass the ``-Dbuildtype=debug`` option, which is equivalent to ``-Ddebug=true -Doptimization=0``, to ``meson setup``. In addition, it is likely most useful to set up an editable build with a fixed build directory. That way, the shared libraries in the installed debug build will contain correct paths, rather than paths pointing to a temporary build directory which ``meson-python`` will otherwise use. IDEs and other tools will then be able to show correct file locations and line numbers during debugging. We can do all that with the following ``pip`` invocation: .. code-block:: console $ pip install -e . --no-build-isolation \ -Csetup-args=-Dbuildtype=debug \ -Cbuild-dir=build-dbg This debug build of your package will work with either a regular or debug build of your Python interpreter. A debug Python interpreter isn't necessary, but may be useful. It can be built from source, or you may be able to get it from your package manager of choice (typically under a package name like ``python-dbg``). Note that using a debug Python interpreter does not yield a debug build of your own package - you must use ``-Dbuildtype=debug`` or an equivalent flag for that. .. warning:: Inside a Conda environment, environment variables like ``CFLAGS`` and ``CXXFLAGS`` are usually set when the environment is activated. These environment variables contain optimization flags like ``-O2``, which will override the optimization level implied by ``-Dbuildtype=debug``. In order to avoid this, unset these variables: .. code-block:: console $ unset CFLAGS $ unset CXXFLAGS Finally, note changing the buildtype from its default value of ``release`` to ``debug`` will also cause ``meson-python`` to enable (or better, not disable) assertions by not defining the ``NDEBUG`` macro (see ``b_ndebug`` under :ref:`explanations-default-options`). ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/docs/how-to-guides/editable-installs.rst0000664000000000000000000001232614706721634022655 0ustar00rootroot.. SPDX-FileCopyrightText: 2023 The meson-python developers .. .. SPDX-License-Identifier: MIT .. _how-to-guides-editable-installs: ***************** Editable installs ***************** Editable installs are a Python package installation mode designed to facilitate package development. When a package is installed in editable mode, edits to the project source code become effective without the need of a new installation step. Editable installs are a build backend independent formalization__ of the *development mode* introduced by setuptools__. __ https://peps.python.org/pep-0660/ __ https://setuptools.pypa.io/en/latest/userguide/development_mode.html While most Python build backends apply development mode or editable installs to the pure Python components, ``meson-python`` extends editable installs to package components that require a compilation step such as extension modules. To install a package in editable mode, pass the ``--editable`` or ``-e`` option to ``pip install``. The editable installation mode implies that the source code of the project being installed is available in a local directory. To install the project in the current directory in editable mode install the project's build dependencies in your development environment and run .. code-block:: console $ python -m pip install --no-build-isolation --editable . This will install a stub in the Python site packages directory that loads the package content from the sources and build directory. The same stub is responsible to rebuild the compiled parts of the package when the package is imported the first time in a given Python interpreter instance. Because of the very fast partial rebuilds allowed by Meson and ``ninja``, the rebuild has an almost negligible impact on the import times. Please note that some kind of changes, such as the addition or modification of `entry points`__, or the addition of new dependencies, and generally all changes involving package metadata, require a new installation step to become effective. __ https://packaging.python.org/en/latest/specifications/entry-points/ An editable install exposes at least all the files that would be available in a regular installation. However, depending on the file and directory organization in your project, it might also expose files that would not be normally available. Build dependencies ------------------ Because packages installed in editable mode are rebuilt on import, all build dependencies need to available at execution time in the development environment. By default, pip builds packages in a isolated environment where build dependencies are installed without affecting the user environment. Packages installed in editable mode using build isolation will fail to rebuild when imported, unless build dependencies are also installed in the development environment. Furthermore, when build using build isolation, a package that depends on headers or other resources provided by its build dependencies, would resolve the path to these in the isolated build environment. The isolated build environment is deleted after the build is completed, resulting in failures when the package in rebuild on import. For these reasons, when installing packages in editable mode, it is recommended to disable build isolation passing the ``--no-build-isolation`` argument to pip. At the time of writing, pip does not offer a command to install a the build dependencies for a package. The build dependencies requirements can be obtained inspecting ``pyproject.toml`` for the package to be installed. These include at least the ``meson-python`` Python package, and the ``meson`` and ``ninja`` Python packages, if the respective commands are not provided by the system, or if they are not the required version: .. code-block:: console $ python -m pip install meson-python meson ninja Build directory --------------- Because the compiled components of the package are loaded directly from the build directory, the build directory needs to be available alongside the source directory at execution time. When building a package in editable mode, ``meson-python`` uses a build directory named as the wheel ABI tag associated to the interpreter for which the package is being build. The build directory is placed in a directory named ``build`` inside the source tree. For example, an editable installation for CPython 3.11 will be associated to a ``build/cp311/`` build directory. This directory structure allows to install the same project in editable mode for multiple interpreters with different ABIs. An alternative build directory can be specified using the :option:`build-dir` config setting. .. _how-to-guides-editable-installs-verbose: Verbose mode ------------ Because editable installation are mostly a package development aid, it might be often useful to be able to inspect the compilation log. Setting the :envvar:`MESONPY_EDITABLE_VERBOSE` environment variable will result in the output of the build process to be emitted when a package is rebuilt on import. To enable this verbose mode permanently for a package, the :option:`editable-verbose` config setting can be set to a non-null value when installing the package: .. code-block:: console $ python -m pip install --no-build-isolation --config-settings=editable-verbose=true --editable . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/docs/how-to-guides/meson-args.rst0000664000000000000000000001467214706721634021336 0ustar00rootroot.. SPDX-FileCopyrightText: 2023 The meson-python developers .. .. SPDX-License-Identifier: MIT .. _how-to-guides-meson-args: ************************** Passing arguments to Meson ************************** ``meson-python`` invokes the ``meson setup``, ``ninja``, and ``meson install`` commands to build the files that will be included in the Python wheel, and ``meson dist`` to collect the files that will be included in the Python sdist. Arguments can be passed to these commands to modify their behavior. Refer to the `Meson documentation`_ and to the `ninja documentation`_ for details. .. _Meson documentation: https://mesonbuild.com/Commands.html .. _ninja documentation: https://ninja-build.org/manual.html#_running_ninja Command line arguments for ``meson`` and ``ninja`` can be specified via tool specific settings in ``pyproject.toml`` as lists of strings for the ``setup``, ``compile``, ``install``, and ``dist`` keys in the ``tool.meson-python.args`` table. For example: .. code-block:: toml [tool.meson-python.args] setup = ['-Doption=false', '-Dfeature=enabled', '-Dvalue=42'] compile = ['-j4'] install = ['--tags=bindings'] dist = ['--include-subprojects'] Or can be specified via configuration settings passed by the Python build front-end as ``setup-args``, ``compile-args``, ``install-args``, and ``dist-args`` :ref:`config settings `. Configuration settings specified via the Python build front-end have precedence over, and can be used to override, the ones specified in ``pyproject.toml``. ``meson-python`` overrides some of the default Meson options with :ref:`settings ` more appropriate for building a Python wheel. User options specified via ``pyproject.toml`` or via Python build front-end config settings override the ``meson-python`` defaults. When building on Windows, ``meson-python`` invokes the ``ninja`` command via the ``meson compile`` wrapper. When the GCC or the LLVM compilers are not found on the ``$PATH``, this activates the Visual Studio environment and allows ``ninja`` to use the MSVC compilers. To activate the Visual Studio environment unconditionally, pass the ``--vsenv`` option to ``meson setup``, see this :ref:`example `. When using the ``meson compile`` wrapper, the user supplied options for the compilation command are passed via the ``--ninja-args`` option. This ensures that the behaviour is independent of how the build is initiated. Refer to the `Meson documentation`__ for more details. __ https://mesonbuild.com/Commands.html#backend-specific-arguments Examples ======== Set the default libraries to static ----------------------------------- Set the default library type to static when building a binary wheel. To set this option permanently in the project's ``pyproject.toml``: .. code-block:: toml [tool.meson-python.args] setup = ['--default-library=static'] To set this option temporarily at build-time: .. tab-set:: .. tab-item:: pypa/build :sync: key_pypa_build .. code-block:: console $ python -m build -Csetup-args="--default-library=static" . .. tab-item:: pip :sync: key_pip .. code-block:: console $ python -m pip wheel --config-settings=setup-args="--default-library=static" . Select the build targets to include in the wheel ------------------------------------------------ It is possible to include in the Python wheel only a subset of the installable files using Meson `installation tags`_ via the ``meson install``'s ``--tags`` command line option. When ``--tags`` is specified, only files that have one of the specified the tags are going to be installed. Meson sets predefined tags on some files. Custom installation tags can be set using the ``install_tag`` keyword argument passed to the target definition function. In this example only targets tagged with ``runtime`` or ``python-runtime`` are included in the Python wheel. .. _installation tags: https://mesonbuild.com/Installing.html#installation-tags To set this option permanently in the project's ``pyproject.toml``: .. code-block:: toml [tool.meson-python.args] install = ['--tags=runtime,python-runtime'] To set this option temporarily at build-time: .. tab-set:: .. tab-item:: pypa/build :sync: key_pypa_build .. code-block:: console $ python -m build -Cinstall-args="--tags=runtime,python-runtime" . .. tab-item:: pip :sync: key_pip .. code-block:: console $ python -m pip wheel --config-settings=install-args="--tags=runtime,python-runtime" . Set the build optimization level -------------------------------- The default compile optimization level when building a binary wheel is currently set to 2. This can be overwritten by passing the ``-Doptimization`` argument to the ``meson setup`` command. To set this option permanently in the project's ``pyproject.toml``: .. code-block:: toml [tool.meson-python.args] setup = ['-Doptimization=3'] To set this option temporarily at build-time: .. tab-set:: .. tab-item:: pypa/build :sync: key_pypa_build .. code-block:: console $ python -m build -Csetup-args="-Doptimization=3" . .. tab-item:: pip :sync: key_pip .. code-block:: console $ python -m pip wheel --config-settings=setup-args="-Doptimization=3" . .. _vsenv-example: Force the use of the MSVC compilers on Windows ---------------------------------------------- The MSVC compilers are not installed in the ``$PATH``. The Visual Studio environment needs to be activated for ``ninja`` to be able to use these compilers. This is taken care of by ``meson compile`` but only when the GCC compilers or the LLVM compilers are not found on the ``$PATH``. Passing the ``--vsenv`` option to ``meson setup`` forces the activation of the Visual Studio environment and generates an error when the activation fails. This option has no effect on other platforms thus, if your project requires to be compiled with MSVC, you can consider to set this option permanently in the project's ``pyproject.toml``: .. code-block:: toml [tool.meson-python.args] setup = ['--vsenv'] To set this option temporarily at build-time: .. tab-set:: .. tab-item:: pypa/build :sync: key_pypa_build .. code-block:: console $ python -m build -Csetup-args="--vsenv" . .. tab-item:: pip :sync: key_pip .. code-block:: console $ python -m pip wheel --config-settings=setup-args="--vsenv" . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/docs/how-to-guides/sdist.rst0000664000000000000000000000437114706721634020404 0ustar00rootroot.. SPDX-FileCopyrightText: 2024 The meson-python developers .. .. SPDX-License-Identifier: MIT .. _sdist: ****************************** Creating a source distribution ****************************** A source distribution for the project can be created executing .. code-block:: console $ python -m build --sdist . in the project root folder. This will create a ``.tar.gz`` archive in the ``dist`` folder in the project root folder. This archive contains the full contents of the latest commit in revision control with all revision control metadata removed. Uncommitted modifications and files unknown to the revision control system are not included. The source distribution archive is created by adding the required metadata files to the archive obtained by executing the ``meson dist --no-tests --allow-dirty`` command. To generate a source distribution, ``meson-python`` must successfully configure the Meson project by running the ``meson setup`` command. Additional arguments can be passed to ``meson dist`` to alter its behavior. Refer to the relevant `Meson documentation`__ and to the :ref:`how-to-guides-meson-args` guide for details. The ``meson dist`` command uses the archival tool of the underlying revision control system for creating the archive. This implies that a source distribution can only be created for a project versioned in a revision control system. Meson supports the Git and Mercurial revision control systems. Files can be excluded from the source distribution via the relevant mechanism provided by the revision control system. When using Git as a revision control system, it is possible to exclude files from the source distribution setting the ``export-ignore`` attribute. For example, adding a ``.gitattributes`` files containing .. code-block:: none dev/** export-ignore would result in the ``dev`` folder to be excluded from the source distribution. Refer to the ``git archive`` documentation__ for details. Another mechanism to alter the content of the source distribution is offered by dist scripts. Refer to the relevant `Meson documentation`__ for details. __ https://mesonbuild.com/Creating-releases.html __ https://git-scm.com/docs/git-archive#ATTRIBUTES __ https://mesonbuild.com/Reference-manual_builtin_meson.html#mesonadd_dist_script ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/docs/index.rst0000664000000000000000000000671214706721634015673 0ustar00rootroot.. SPDX-FileCopyrightText: 2022 The meson-python developers .. .. SPDX-License-Identifier: MIT :hide-toc: ************ meson-python ************ .. highlights:: A Python package build backend leveraging the Meson build system. ``meson-python`` implement the Python build system hooks, enabling Python build front-ends such as ``pip`` and ``build`` to build and install Python packages based on a Meson_ build definition. Meson is characterized by build definitions written in a very readable domain-specific language and extremely fast builds. Meson support for Windows, macOS, Linux, and other UNIX-like operative systems, and for all the major compiler tool-chains. It allows to compile and link together code written in many programming languages, including C, C++, Cython, D, Fortran, Objective C, and Rust. It has built-in multi-platform dependency provider that works well with distribution packages, and the capability to build dependencies as sub-projects. If you are not familiar with Meson, we recommend checking the `Meson tutorial`_. ``meson-python`` inherits the strengths of Meson and is thus best suited for Python packages building extension modules in compiled languages. ``meson-python`` is suitable for small packages as well as very complex ones, see our :ref:`projects-using-meson-python` directory. To enable ``pip`` or ``build`` to build a Python source distribution (*sdist*) or a binary Python package (*wheel*) for a Meson project, it is sufficient to add to the root of the source tree next to the top-level ``meson.build`` a ``pyproject.toml`` file specifying ``meson-python`` as the Python build backend: .. code-block:: toml [build-system] build-backend = 'mesonpy' requires = ['meson-python'] The package name and version are extracted from the metadata provided to Meson via the ``project()`` function in the ``meson.build`` file. Package metadata can be overridden and extended using the standard package metadata format in the ``project`` section of ``pyproject.toml``: .. code-block:: toml [project] name = 'example' version = '1.0.0' description = 'Example package using the meson-python build backend' readme = 'README.rst' license = {file = 'LICENSE.txt'} authors = [ {name = 'Au Thor', email = 'author@example.com'}, ] [project.scripts] example = 'example.cli:main' Please refer to the `PyPA documentation`_ for detailed documentation about the ``pyproject.toml`` file. Please refer to our :ref:`tutorial` for guidance about the use of ``meson-python`` and Meson for Python packaging. .. _Meson: https://mesonbuild.com/ .. _PyPA documentation: https://packaging.python.org/en/latest/specifications/declaring-project-metadata/ .. _Meson tutorial: https://mesonbuild.com/Tutorial.html .. toctree:: :hidden: tutorials/introduction how-to-guides/sdist how-to-guides/editable-installs how-to-guides/config-settings how-to-guides/meson-args how-to-guides/debug-builds reference/limitations projects-using-meson-python .. toctree:: :caption: Reference :hidden: reference/config-settings reference/pyproject-settings reference/environment-variables explanations/default-options reference/meson-compatibility .. toctree:: :caption: Project :hidden: changelog about Discussions Source Code Issue Tracker ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/docs/projects-using-meson-python.rst0000664000000000000000000000216314706721634022172 0ustar00rootroot.. SPDX-FileCopyrightText: 2023 The meson-python developers .. .. SPDX-License-Identifier: MIT .. _projects-using-meson-python: ******** Examples ******** The following project use ``meson-python`` for their build system known to be adhering to best practices :`SciPy `_: Probably the most complex project using Meson and ``meson-python``. It combines CPython extensions and libraries written in C, C++, Fortran, Cython_, and Pythran_. :`scikit-image `_: Another complex project using ``meson-python``. :`siphash24 `_: A very simple project. It demonstrates how Meson makes it trivial to compile a CPython extension written in `Cython`_ via a simple template engine and link it to a library compiled from a Meson subproject. Also an example of how to use `cibuildwheel`_ to produce Python wheels for several platforms. .. _Cython: https://github.com/cython/cython .. _Pythran: https://github.com/serge-sans-paille/pythran .. _cibuildwheel: https://github.com/pypa/cibuildwheel ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/docs/reference/config-settings.rst0000664000000000000000000000274314706721634021625 0ustar00rootroot.. SPDX-FileCopyrightText: 2023 The meson-python developers .. .. SPDX-License-Identifier: MIT .. _reference-config-settings: ********************* Build config settings ********************* This page lists the build configuration settings, that is, the settings you can pass when building the project. Please refer to the :ref:`how-to-guides-config-settings` and :ref:`how-to-guides-meson-args` guides for information on how to use them. .. option:: build-dir By default ``meson-python`` uses a temporary build directory. This settings allows to select the Meson build directory and prevents it to be deleted when ``meson-python`` terminates. If the directory does not exists, it will be created. If the directory exists and contains a valid Meson build directory setup, the project will be reconfigured using ``meson setup --reconfigure``. For backward compatibility reasons, the alternative ``builddir`` spelling is also accepted. .. option:: dist-args Extra arguments to be passed to the ``meson dist`` command. .. option:: setup-args Extra arguments to be passed to the ``meson setup`` command. .. option:: compile-args Extra arguments to be passed to the ``ninja`` command. .. option:: install-args Extra arguments to be passed to the ``meson install`` command. .. option:: editable-verbose Enable :ref:`verbose mode ` when building for an :ref:`editable install `. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/docs/reference/environment-variables.rst0000664000000000000000000001173314706721634023033 0ustar00rootroot.. SPDX-FileCopyrightText: 2023 The meson-python developers .. .. SPDX-License-Identifier: MIT .. _reference-environment-variables: ********************* Environment variables ********************* Environment variables can be used to influence ``meson-python``'s behavior, as well as the behavior of Meson and other tools that may be used during the build. This page lists all the environment variables directly used by ``meson-python``. Meson recommends using command line arguments instead of environment variables, but does support a number of environment variables for compatibility with other build systems: - `Compiler and linker flag variables `__ - `Compiler and linker selection variables `__ Other environment variables may influence how other tools used during the setup or build steps operate. For example, ``pkg-config`` supports ``PKG_CONFIG_PATH`` which influences the search path for ``.pc`` files describing the available dependencies. .. warning:: Conda sets a number of environment variables during environment activation for compiler/linker selection (``CC``, ``CXX``, ``FC``, ``LD``) and compile/link flags (``CFLAGS``, ``CXXFLAGS``, ``FFLAGS``, ``LDFLAGS``) when compilers are installed in a conda environment. This may have unexpected side effects (see for example the note in :ref:`how-to-guides-debug-builds`). Environment variables used by meson-python ========================================== .. envvar:: ARCHFLAGS This environmental variable is used for supporting architecture cross compilation on macOS in a way compatible with setuptools_. It is ignored on all other platforms. It can be set to ``-arch arm64`` or to ``-arch x86_64`` for compiling for the arm64 and the x86_64 architectures respectively. Setting this environment variable to any other value is not supported. The macOS toolchain allows architecture cross compilation passing the ``-arch`` flat to the compilers. ``meson-python`` inspects the content of this environment variable and synthesizes a Meson `cross build definition file`_ with the appropriate content, and passes it to ``meson setup`` via the ``--cross-file`` option. Support for this environment variable is maintained only for compatibility with existing tools, cibuildwheel_ in particular, and is not the recommended solution for cross compilation. .. _setuptools: https://setuptools.pypa.io/en/latest/setuptools.html .. _cross build definition file: https://mesonbuild.com/Cross-compilation.html .. _cibuildwheel: https://cibuildwheel.readthedocs.io/en/stable/ .. envvar:: FORCE_COLOR Setting this environment variable to any value forces the use of ANSI escape sequences to colorize the ``meson-python``'s console output. Setting both ``NO_COLOR`` and ``FORCE_COLOR`` environment variables is an error. .. envvar:: MACOSX_DEPLOYMENT_TARGET This environment variables is used of specifying the target macOS platform major version to the Xcode development tools. If this environment variable is set, ``meson-python`` will use the specified macOS version for the Python wheel platform tag instead than the macOS version of the build machine. This variable must be set to macOS major versions only: ``10.9`` to ``10.15``, ``11``, ``12``, ``13``, ... Please note that the macOS versioning changed from macOS 11 onward. For macOS 10, the versioning scheme is ``10.$major.$minor``. From macOS 11 onward, it is ``$major.$minor.$bugfix``. Wheel tags and deployment targets are currently designed to specify compatibility only with major version number granularity. Another way of specifying the target macOS platform is to use the ``-mmacosx-version-min`` compile and link flags. However, it is not possible for ``meson-python`` to detect this, and it will not set the Python wheel platform tag accordingly. .. envvar:: MESON Specifies the ``meson`` executable or script to use. It overrides ``tool.meson-python.meson``. See :ref:`reference-pyproject-settings` for more details. .. envvar:: MESONPY_EDITABLE_VERBOSE Setting this environment variable to any value enables directing to the console the messages emitted during project rebuild triggered by imports of editable wheels generated by ``meson-python``. Refer to the :ref:`how-to-guides-editable-installs` guide for more information. .. envvar:: NINJA Specifies the ninja_ executable to use. It can also be used to select ninja_ alternatives like samurai_. .. _ninja: https://ninja-build.org .. _samurai: https://github.com/michaelforney/samurai .. envvar:: NO_COLOR Setting this environment variable to any value disables the use of ANSI terminal escape sequences to colorize ``meson-python``'s console output. Setting both ``NO_COLOR`` and ``FORCE_COLOR`` environment variables is an error. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/docs/reference/limitations.rst0000664000000000000000000000311714706721634021052 0ustar00rootroot.. SPDX-FileCopyrightText: 2023 The meson-python developers .. .. SPDX-License-Identifier: MIT .. _reference-limitations: *********** Limitations *********** Non-package data files ====================== It is possible to encapsulate arbitrary data files into Python wheels. ``meson-python`` will add to the wheel any data file installed into the Meson's ``{datadir}`` location, for example via Meson's |install_data()|_ function. However, when the resulting wheel is installed, these files are unpacked into a platform-specific location and there is no supported facility to reliably find them at run time. It is recommended to include data files than need to be accessible at run-time inside the package alongside the Python code, and use :mod:`importlib.resources` (or the `importlib-resources`_ backport) to access them. Shared libraries on Windows =========================== On Windows, ``meson-python`` cannot encapsulate shared libraries installed as part of the Meson project into the Python wheel for Python extension modules or executables, in a way suitable for them to be found at run-time. This limitation can be overcome with static linking or using `delvewheel`_ to post-process the Python wheel to bundle the required shared libraries and include the setup code to properly set the library search path. .. _install_data(): https://mesonbuild.com/Reference-manual_functions.html#install_data .. _importlib-resources: https://importlib-resources.readthedocs.io/en/latest/index.html .. _delvewheel: https://github.com/adang1345/delvewheel .. |install_data()| replace:: ``install_data()`` ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/docs/reference/meson-compatibility.rst0000664000000000000000000000360714706721634022512 0ustar00rootroot.. SPDX-FileCopyrightText: 2023 The meson-python developers .. .. SPDX-License-Identifier: MIT .. _meson-compatibility: ******************* Meson compatibility ******************* ``meson-python`` tightly integrates with Meson to produce Python wheels and sdists. Therefore, correct operation depends on functionality implemented by Meson. ``meson-python`` strives to maintain compatibility with as old as possible Meson releases. However, some functionality is available only with more recent Meson versions. .. option:: 0.63.3 Meson is 0.63.3 is the minimum required version. .. option:: 1.1.0 Meson 1.1.0 or later is required to support the ``exclude_files`` and ``exclude_directories`` arguments to Meson ``install_subdir()`` and similar installation functions. On older Meson versions, these arguments have no effect. .. option:: 1.2.3 Meson 1.2.3 or later is required to support Python 3.12 or later. Since version 1.2.3, Meson does not rely anymore on the ``distutils`` module, which was removed from the standard library in Python 3.12. When installed on Python 3.12, ``meson-python`` depends on Meson 1.2.3 or later. .. option:: 1.3.0 Meson 1.3.0 or later is required for compiling extension modules targeting the Python limited API. Build front-ends by default build packages in an isolated Python environment where build dependencies are installed. Most often, unless a package or its build dependencies declare explicitly a version constraint, this results in the most recent version of the build dependencies to be installed. However, if a package uses functionalities implemented only in combination with a specific Meson version, it is recommended to explicitly declare a version requirement in ``pyproject.toml``. For example: .. code-block:: toml [build-system] build-backend = 'mesonpy' requires = [ 'meson-python', 'meson >= 1.1.0', ] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/docs/reference/pyproject-settings.rst0000664000000000000000000000373314706721634022377 0ustar00rootroot.. SPDX-FileCopyrightText: 2023 The meson-python developers .. .. SPDX-License-Identifier: MIT .. _reference-pyproject-settings: **************** Project settings **************** This page lists the configuration settings supported by ``meson-python`` in the ``pyproject.toml`` file. Please refer to the :ref:`how-to-guides-meson-args` guide for for information on how to use them and examples. .. option:: tool.meson-python.limited-api A boolean indicating whether the extension modules contained in the Python package target the `Python limited API`__. Extension modules can be compiled for the Python limited API specifying the ``limited_api`` argument to the |extension_module()|__ function in the Meson Python module. When this setting is set to true, the value ``abi3`` is used for the Python wheel filename ABI tag. This setting is automatically reverted to false when the ``-Dpython.allow_limited_api=false`` option is passed to ``meson setup``. .. option:: tool.meson-python.meson A string specifying the ``meson`` executable or script to use. If it is a path to an existing file with a name ending in ``.py``, it will be invoked as a Python script using the same Python interpreter that is used to run ``meson-python`` itself. It can be overridden by the :envvar:`MESON` environment variable. .. option:: tool.meson-python.args.dist Extra arguments to be passed to the ``meson dist`` command. .. option:: tool.meson-python.args.setup Extra arguments to be passed to the ``meson setup`` command. .. option:: tool.meson-python.args.compile Extra arguments to be passed to the ``ninja`` command. .. option:: tool.meson-python.args.install Extra arguments to be passed to the ``meson install`` command. __ https://docs.python.org/3/c-api/stable.html?highlight=limited%20api#stable-application-binary-interface __ https://mesonbuild.com/Python-module.html#extension_module .. |extension_module()| replace:: ``extension_module()`` ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/docs/spelling_wordlist.txt0000664000000000000000000000033014706721634020325 0ustar00rootrootauditwheel backend backends backport config CPython Cygwin distro eg executables frontend Github macOS nox Numpy pre pluggy pypa pyproject pytest rpath sdist sdists setuptools todo toml uncommited vendored vendoring ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/docs/spelling_wordlist.txt.license0000664000000000000000000000013414706721634021750 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/docs/static/css/contributors.css0000664000000000000000000000105514706721634021353 0ustar00rootroot/* * SPDX-FileCopyrightText: 2023 The meson-python developers * * SPDX-License-Identifier: MIT */ .sphinx-contributors--avatars .sphinx-contributors_contributor__image { border-radius: 50%; max-width: 80px; margin: auto; } .sphinx-contributors--avatars .sphinx-contributors_contributor__username { font-size: 80%; text-align: center; } .sphinx-contributors--avatars .sphinx-contributors_contributor { flex-grow: 1; } .sphinx-contributors--avatars .sphinx-contributors_list { justify-content: flex-start; gap: 10px; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/docs/tutorials/introduction.rst0000664000000000000000000002366414706721634021340 0ustar00rootroot.. SPDX-FileCopyrightText: 2023 The meson-python developers .. .. SPDX-License-Identifier: MIT .. _tutorial: ******** Tutorial ******** If you are new to Python packaging, don't worry! We will give you a quick introduction to what steps releasing a Python package consists of, and walk you through them to get started. Creating a Meson project ======================== To get started, we need a project to publish. As ``meson-python`` is built on top of Meson_, we will create a really simple Meson project. You may already have a Meson project you wish to publish, in that case, you can simply skip this step. The module ---------- First, we create a simple Python module. We will go for a native module, as that's really where ``meson-python`` shines against other Python build backends. .. code-block:: c :caption: our_first_module.c #include static PyObject* foo(PyObject* self) { return PyUnicode_FromString("bar"); } static PyMethodDef methods[] = { {"foo", (PyCFunction)foo, METH_NOARGS, NULL}, {NULL, NULL, 0, NULL}, }; static struct PyModuleDef module = { PyModuleDef_HEAD_INIT, "our_first_module", NULL, -1, methods, }; PyMODINIT_FUNC PyInit_our_first_module(void) { return PyModule_Create(&module); } Here, we have a create a small module named ``our_first_module``, which has a function ``foo`` that simply returns ``"bar"``. .. admonition:: Using the C API :class: seealso If you need help writing a native module using Python's C API, we recommend checking out the following resources. - `The official Python C API documentation `_ - `RealPython's "Building a Python C Extension Module" introductory tutorial `_ - `pysheeet's "C Extensions" page `_ - `pysheeet-kr's "Python C API cheatsheet" page `_ The Meson build description --------------------------- Now, we need to create the Meson build description file. This tells Meson what we want it to build, and how to do it. .. code-block:: meson :caption: meson.build project('purelib-and-platlib', 'c') py = import('python').find_installation(pure: false) py.extension_module( 'our_first_module', 'our_first_module.c', install: true, ) Here, we use Meson's `Python module`_ to build our ``our_first_module`` module. We make sure to install it, by passing ``install: true`` to ``extension_module``, as ``meson-python`` will only include in the binary distribution artifacts targets that Meson would install onto system. Having non installed targets allows you to build targets for use within the build, or for tests. Configuring our Python package ============================== Now, we need to tell Python packaging tooling what build backend to use to build our package. We do this by creating a ``build-system`` section in the ``pyproject.toml`` file, which is the file used to configure Python packaging tooling. Inside the ``build-system`` section, we need to define two keys, ``build-backend`` and ``requires``. ``build-backend`` defines which build backend should be used for the project - set it to ``'mesonpy'`` to use ``meson-python``. ``requires`` lets us specify which packages need to be installed for the build process, it should include ``meson-python`` and any other dependencies you might need (e.g., ``Cython``). .. code-block:: toml :caption: pyproject.toml [build-system] build-backend = 'mesonpy' requires = ['meson-python'] After we specify which backend to use, we'll want to define the package metadata. This is done in the ``project`` section, and the format is pretty self-explanatory: .. code-block:: toml :caption: pyproject.toml ... [project] name = 'our-first-project' version = '0.0.1' description = 'Our first Python project, using meson-python!' readme = 'README.md' requires-python = '>=3.8' license = {file = 'LICENSE.txt'} authors = [ {name = 'Bowsette Koopa', email = 'bowsette@example.com'}, ] .. admonition:: Declaring project metadata :class: seealso Our example doesn't make use of all the fields available in the ``[project]`` section. Check out the `PyPA documentation on project metadata`_ for more examples and details. Testing the project ------------------- Now we should have a valid Python project, so let's test it. We will install it with pip_: .. code-block:: console $ pip install . $ pip list ... our-first-project 0.0.1 ... After this, we should be able to import and try out our module. .. code-block:: console $ python >>> import our_first_module >>> our_first_module.foo() 'bar' Creating your first release =========================== Now that we have a valid Python project, we can release it. To release the project we first need to generate the distribution artifacts, these are files in a standardized format that Python package installers understand. There are two kind of artifacts, `source distributions`_, which are commonly referred to as *sdists*, and `binary distributions`_, which use a custom format named *wheel*, so they're generally referred to as *wheels*. What are the roles of sdists and wheels? ---------------------------------------- As you might have figured out by the name, sdists contain the source code of the project, and wheels contain a compiled [#pure-wheels]_ version of the project, ready to be copied to the file system. If your project uses Python extension modules, your wheels will be specific to both the platform and the Python version [#stable-abi]_. While distributing wheels is not mandatory, they make the user experience much nicer. Unless you have any reason not to, we highly recommend you distribute wheels for at least the most common systems. When wheels are not available for a system, the project can still be installed, be it needs to be build from the sdist, which involves fetching all the build dependencies and going through the likely expensive build process. .. [#pure-wheels] Projects that don't have any compiled code will have a platform-independent -- *pure* -- wheel. .. [#stable-abi] Unless you are using the `stable ABI`_, which limits you to a subset of the Python C API, with the trade-off that your native code will be compatible with multiple Python versions. Building the project -------------------- Before continuing, ensure you have committed the three files we created so far to your Git repository - ``meson-python`` will only take into account the files that Git knows about. To generate the distribution artifacts we will use the `pypa/build`_ tool. It will create a temporary virtual environment, install all the required build dependencies, and ask ``meson-python`` to build the artifacts. .. code-block:: console $ pip install build $ python -m build If the build succeeded, you'll have the binary artifacts in the ``dist`` folder. .. admonition:: Building wheels for multiple platforms :class: tip If our project only contains pure-Python (``.py``) code, the wheel we just built will work on all platforms, as it is a pure wheel, but if the project contains native code, it will be specific for our machine's platform. When releasing, you'll usually want to build for at least most of the other more popular platforms (Linux, Windows, macOS, etc.). To make that work easier, we recommend checking out the cibuildwheel_ project, which allows you to automate it. Build isolation ``````````````` Building with ``python -m build`` or with ``pip`` uses build isolation by default. I.e., the build frontend creates a new, temporary virtual environment with all build dependencies before calling ``meson-python`` to build a wheel. If you disable build isolation, you are responsible for ensuring that ``meson-python`` and all other build dependencies for the package are installed already in the Python environment. Note that if you use a virtual environment to build in, it must be activated (otherwise ``meson`` or another executable may not be found). Distributing the project ------------------------ Now that we have the distribution artifacts, we can upload them to a repository. We will upload them to the `Python Package Index`_ (PyPI), which is repository that comes enabled by default in most tools. For this, we will use Twine_. .. code-block:: console $ pip install twine $ twine upload dist/* .. admonition:: Upload to the `Test PyPI`_ :class: tip If you don't want to upload to the real index, you can upload to the `Test PyPI`_ instead. .. code-block:: console $ twine upload -r testpypi dist/* You can find more about how to use the `Test PyPI`_ in `its PyPA documentation page `_. After this, your package should be available on PyPI_, and installable with pip_. .. code-block:: console $ pip install our-first-project .. _Meson: https://mesonbuild.com/ .. _Python module: https://mesonbuild.com/Python-module.html .. _PyPA documentation on project metadata: https://packaging.python.org/en/latest/specifications/declaring-project-metadata/ .. _source distributions: https://packaging.python.org/en/latest/specifications/source-distribution-format/ .. _binary distributions: https://packaging.python.org/en/latest/specifications/binary-distribution-format/ .. _stable ABI: https://docs.python.org/3/c-api/stable.html#stable-application-binary-interface .. _pypa/build: https://github.com/pypa/build .. _cibuildwheel: https://github.com/pypa/cibuildwheel .. _Python Package Index: https://pypi.org/ .. _pronounced "pie pea eye": https://pypi.org/help/#pronunciation .. _Twine: https://github.com/pypa/twine .. _Test PyPI: https://test.pypi.org/ .. _PyPI: https://pypi.org/ .. _pip: https://github.com/pypa/pip ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/meson.build0000664000000000000000000000063014706721634015235 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT project('meson-python', version: '0.17.1') py = import('python').find_installation() py.install_sources( 'mesonpy/__init__.py', 'mesonpy/_compat.py', 'mesonpy/_editable.py', 'mesonpy/_rpath.py', 'mesonpy/_tags.py', 'mesonpy/_util.py', 'mesonpy/_wheelfile.py', subdir: 'mesonpy', ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/mesonpy/__init__.py0000664000000000000000000013054714706721634016711 0ustar00rootroot# SPDX-FileCopyrightText: 2021 Filipe Laíns # SPDX-FileCopyrightText: 2021 Quansight, LLC # SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT """Meson Python build backend Implements PEP 517 hooks. """ from __future__ import annotations import argparse import collections import contextlib import difflib import functools import importlib.machinery import io import itertools import json import os import pathlib import platform import re import shutil import subprocess import sys import sysconfig import tarfile import tempfile import textwrap import typing import warnings if sys.version_info < (3, 11): import tomli as tomllib else: import tomllib import packaging.utils import packaging.version import pyproject_metadata import mesonpy._compat import mesonpy._rpath import mesonpy._tags import mesonpy._util import mesonpy._wheelfile from mesonpy._compat import cached_property, read_binary if typing.TYPE_CHECKING: # pragma: no cover from typing import Any, Callable, DefaultDict, Dict, List, Literal, Optional, Sequence, TextIO, Tuple, Type, TypeVar, Union from mesonpy._compat import Collection, Iterator, Mapping, ParamSpec, Path, Self P = ParamSpec('P') T = TypeVar('T') MesonArgsKeys = Literal['dist', 'setup', 'compile', 'install'] MesonArgs = Mapping[MesonArgsKeys, List[str]] __version__ = '0.17.1' _NINJA_REQUIRED_VERSION = '1.8.2' _MESON_REQUIRED_VERSION = '0.63.3' # keep in sync with the version requirement in pyproject.toml _MESON_ARGS_KEYS = ['dist', 'setup', 'compile', 'install'] _SUFFIXES = importlib.machinery.all_suffixes() _EXTENSION_SUFFIX_REGEX = re.compile(r'^[^.]+\.(?:(?P[^.]+)\.)?(?:so|pyd|dll)$') assert all(re.match(_EXTENSION_SUFFIX_REGEX, f'foo{x}') for x in importlib.machinery.EXTENSION_SUFFIXES) # Map Meson installation path placeholders to wheel installation paths. # See https://docs.python.org/3/library/sysconfig.html#installation-paths _INSTALLATION_PATH_MAP = { '{bindir}': 'scripts', '{py_purelib}': 'purelib', '{py_platlib}': 'platlib', '{moduledir_shared}': 'platlib', '{includedir}': 'headers', '{datadir}': 'data', # custom location '{libdir}': 'mesonpy-libs', '{libdir_shared}': 'mesonpy-libs', } def _map_to_wheel(sources: Dict[str, Dict[str, Any]]) -> DefaultDict[str, List[Tuple[pathlib.Path, str]]]: """Map files to the wheel, organized by wheel installation directory.""" wheel_files: DefaultDict[str, List[Tuple[pathlib.Path, str]]] = collections.defaultdict(list) packages: Dict[str, str] = {} for key, group in sources.items(): for src, target in group.items(): destination = pathlib.Path(target['destination']) anchor = destination.parts[0] dst = pathlib.Path(*destination.parts[1:]) path = _INSTALLATION_PATH_MAP.get(anchor) if path is None: raise BuildError(f'Could not map installation path to an equivalent wheel directory: {str(destination)!r}') if path == 'purelib' or path == 'platlib': package = destination.parts[1] other = packages.setdefault(package, path) if other != path: this = os.fspath(pathlib.Path(path, *destination.parts[1:])) that = os.fspath(other / next(d for d, s in wheel_files[other] if d.parts[0] == destination.parts[1])) raise BuildError( f'The {package} package is split between {path} and {other}: ' f'{this!r} and {that!r}, a "pure: false" argument may be missing in meson.build. ' f'It is recommended to set it in "import(\'python\').find_installation()"') if key == 'install_subdirs' or key == 'targets' and os.path.isdir(src): exclude_files = {os.path.normpath(x) for x in target.get('exclude_files', [])} exclude_dirs = {os.path.normpath(x) for x in target.get('exclude_dirs', [])} for root, dirnames, filenames in os.walk(src): for name in dirnames.copy(): dirsrc = os.path.join(root, name) relpath = os.path.relpath(dirsrc, src) if relpath in exclude_dirs: dirnames.remove(name) # sort to process directories determninistically dirnames.sort() for name in sorted(filenames): filesrc = os.path.join(root, name) relpath = os.path.relpath(filesrc, src) if relpath in exclude_files: continue filedst = dst / relpath wheel_files[path].append((filedst, filesrc)) else: wheel_files[path].append((dst, src)) return wheel_files class style: ERROR = '\33[31m' # red WARNING = '\33[93m' # bright yellow INFO = '\33[36m\33[1m' # cyan, bold RESET = '\33[0m' @staticmethod def strip(string: str) -> str: """Strip ANSI escape sequences from string.""" return re.sub(r'\033\[[;?0-9]*[a-zA-Z]', '', string) @functools.lru_cache() def _use_ansi_escapes() -> bool: """Determine whether logging should use ANSI escapes.""" # We print log messages and error messages that may contain file # names containing characters that cannot be represented in the # stdout encoding. Use replacement markers for those instead than # raising UnicodeEncodeError. sys.stdout.reconfigure(errors='replace') # type: ignore[attr-defined] if 'NO_COLOR' in os.environ: return False if 'FORCE_COLOR' in os.environ or sys.stdout.isatty() and os.environ.get('TERM') != 'dumb': if sys.platform == 'win32' and not os.environ.get('ANSICON'): return mesonpy._util.setup_windows_console() return True return False def _log(string: str , **kwargs: Any) -> None: if not _use_ansi_escapes(): string = style.strip(string) print(string, **kwargs) def _showwarning( message: Union[Warning, str], category: Type[Warning], filename: str, lineno: int, file: Optional[TextIO] = None, line: Optional[str] = None, ) -> None: # pragma: no cover """Callable to override the default warning handler, to have colored output.""" _log(f'{style.WARNING}meson-python: warning:{style.RESET} {message}') class _clicounter: def __init__(self, total: int) -> None: self._total = total self._count = itertools.count(start=1) def __enter__(self) -> Self: return self def update(self, description: str) -> None: line = f'[{next(self._count)}/{self._total}] {description}' if _use_ansi_escapes(): print('\r', line, sep='', end='\33[0K', flush=True) else: print(line) def __exit__(self, exc_type: Any, exc_value: Any, exc_tb: Any) -> None: if _use_ansi_escapes(): print() class Error(RuntimeError): def __str__(self) -> str: return str(self.args[0]) class ConfigError(Error): """Error in the backend configuration.""" class BuildError(Error): """Error when building the wheel.""" class MesonBuilderError(Error): """Error when building the Meson package.""" class Metadata(pyproject_metadata.StandardMetadata): def __init__(self, name: str, *args: Any, **kwargs: Any): super().__init__(name, *args, **kwargs) # Local fix for https://github.com/FFY00/python-pyproject-metadata/issues/60 self.name = self._validate_name(name) @staticmethod def _validate_name(name: str) -> str: # See https://packaging.python.org/en/latest/specifications/core-metadata/#name if not re.match(r'^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$', name, re.IGNORECASE): raise pyproject_metadata.ConfigurationError( f'Invalid project name "{name}". A valid name consists only of ASCII letters and ' f'numbers, period, underscore and hyphen. It must start and end with a letter or number') return name @classmethod def from_pyproject( # type: ignore[override] cls, data: Mapping[str, Any], project_dir: Path = os.path.curdir, metadata_version: Optional[str] = None ) -> Self: metadata = super().from_pyproject(data, project_dir) # Check for missing version field. if not metadata.version and 'version' not in metadata.dynamic: raise pyproject_metadata.ConfigurationError( 'Required "project.version" field is missing and not declared as dynamic') # Check for unsupported dynamic fields. unsupported_dynamic = set(metadata.dynamic) - {'version', } if unsupported_dynamic: fields = ', '.join(f'"{x}"' for x in unsupported_dynamic) raise pyproject_metadata.ConfigurationError(f'Unsupported dynamic fields: {fields}') return metadata # Local fix for a bug in pyproject-metadata. See # https://github.com/mesonbuild/meson-python/issues/454 def _update_dynamic(self, value: Any) -> None: if value and 'version' in self.dynamic: self.dynamic.remove('version') @property def canonical_name(self) -> str: # See https://packaging.python.org/en/latest/specifications/name-normalization/#normalization return packaging.utils.canonicalize_name(self.name) @property def distribution_name(self) -> str: """Name to be used in wheel and sdist file names.""" return self.canonical_name.replace('-', '_') def _is_native(file: Path) -> bool: """Check if file is a native file.""" with open(file, 'rb') as f: if sys.platform == 'darwin': return f.read(4) in ( b'\xfe\xed\xfa\xce', # 32-bit b'\xfe\xed\xfa\xcf', # 64-bit b'\xcf\xfa\xed\xfe', # arm64 b'\xca\xfe\xba\xbe', # universal / fat (same as java class so beware!) ) elif sys.platform == 'win32' or sys.platform == 'cygwin': return f.read(2) == b'MZ' else: # Assume that any other platform uses ELF binaries. return f.read(4) == b'\x7fELF' # ELF class _WheelBuilder(): """Helper class to build wheels from projects.""" def __init__( self, metadata: Metadata, manifest: Dict[str, List[Tuple[pathlib.Path, str]]], limited_api: bool, ) -> None: self._metadata = metadata self._manifest = manifest self._limited_api = limited_api @property def _has_internal_libs(self) -> bool: return bool(self._manifest.get('mesonpy-libs')) @property def _has_extension_modules(self) -> bool: # Assume that all code installed in {platlib} is Python ABI dependent. return bool(self._manifest.get('platlib')) @cached_property def _pure(self) -> bool: """Whether the wheel is architecture independent""" if self._manifest['platlib'] or self._manifest['mesonpy-libs']: return False for _, file in self._manifest['scripts']: if _is_native(file): return False return True @property def tag(self) -> mesonpy._tags.Tag: """Wheel tags.""" if self._pure: return mesonpy._tags.Tag('py3', 'none', 'any') if not self._has_extension_modules: # The wheel has platform dependent code (is not pure) but # does not contain any extension module (does not # distribute any file in {platlib}) thus use generic # implementation and ABI tags. return mesonpy._tags.Tag('py3', 'none', None) return mesonpy._tags.Tag(None, self._stable_abi, None) @property def name(self) -> str: """Wheel name, this includes the basename and tag.""" return f'{self._metadata.distribution_name}-{self._metadata.version}-{self.tag}' @property def _distinfo_dir(self) -> str: return f'{self._metadata.distribution_name}-{self._metadata.version}.dist-info' @property def _data_dir(self) -> str: return f'{self._metadata.distribution_name}-{self._metadata.version}.data' @property def _libs_dir(self) -> str: return f'.{self._metadata.distribution_name}.mesonpy.libs' @property def _license_file(self) -> Optional[pathlib.Path]: license_ = self._metadata.license if license_ and isinstance(license_, pyproject_metadata.License): return license_.file return None @property def wheel(self) -> bytes: """Return WHEEL file for dist-info.""" return textwrap.dedent(''' Wheel-Version: 1.0 Generator: meson Root-Is-Purelib: {is_purelib} Tag: {tag} ''').strip().format( is_purelib='true' if self._pure else 'false', tag=self.tag, ).encode() @property def entrypoints_txt(self) -> bytes: """dist-info entry_points.txt.""" data = self._metadata.entrypoints.copy() data.update({ 'console_scripts': self._metadata.scripts, 'gui_scripts': self._metadata.gui_scripts, }) text = '' for entrypoint in data: if data[entrypoint]: text += f'[{entrypoint}]\n' for name, target in data[entrypoint].items(): text += f'{name} = {target}\n' text += '\n' return text.encode() @cached_property def _stable_abi(self) -> Optional[str]: # PyPy supports the limited API but does not provide a stable # ABI, therefore extension modules using the limited API do # not use the stable ABI filename suffix and wheels should not # be tagged with the abi3 tag. if self._limited_api and '__pypy__' not in sys.builtin_module_names: # Verify stable ABI compatibility: examine files installed # in {platlib} that look like extension modules, and raise # an exception if any of them has a Python version # specific extension filename suffix ABI tag. for path, _ in self._manifest['platlib']: match = _EXTENSION_SUFFIX_REGEX.match(path.name) if match: abi = match.group('abi') if abi is not None and abi != 'abi3': raise BuildError( f'The package declares compatibility with Python limited API but extension ' f'module {os.fspath(path)!r} is tagged for a specific Python version.') return 'abi3' return None def _install_path(self, wheel_file: mesonpy._wheelfile.WheelFile, origin: Path, destination: pathlib.Path) -> None: """Add a file to the wheel.""" if self._has_internal_libs: if _is_native(origin): # When an executable, libray, or Python extension module is # dynamically linked to a library built as part of the project, # Meson adds a library load path to it pointing to the build # directory, in the form of a relative RPATH entry. meson-python # relocates the shared libraries to the $project.mesonpy.libs # folder. Rewrite the RPATH to point to that folder instead. libspath = os.path.relpath(self._libs_dir, destination.parent) mesonpy._rpath.fix_rpath(origin, libspath) try: wheel_file.write(origin, destination.as_posix()) except FileNotFoundError: # work around for Meson bug, see https://github.com/mesonbuild/meson/pull/11655 if not os.fspath(origin).endswith('.pdb'): raise def _wheel_write_metadata(self, whl: mesonpy._wheelfile.WheelFile) -> None: # add metadata whl.writestr(f'{self._distinfo_dir}/METADATA', bytes(self._metadata.as_rfc822())) whl.writestr(f'{self._distinfo_dir}/WHEEL', self.wheel) if self.entrypoints_txt: whl.writestr(f'{self._distinfo_dir}/entry_points.txt', self.entrypoints_txt) # add license (see https://github.com/mesonbuild/meson-python/issues/88) if self._license_file: whl.write(self._license_file, f'{self._distinfo_dir}/{os.path.basename(self._license_file)}') def build(self, directory: Path) -> pathlib.Path: wheel_file = pathlib.Path(directory, f'{self.name}.whl') with mesonpy._wheelfile.WheelFile(wheel_file, 'w') as whl: self._wheel_write_metadata(whl) with _clicounter(sum(len(x) for x in self._manifest.values())) as counter: root = 'purelib' if self._pure else 'platlib' for path, entries in self._manifest.items(): for dst, src in entries: counter.update(src) if path == root: pass elif path == 'mesonpy-libs': # custom installation path for bundled libraries dst = pathlib.Path(self._libs_dir, dst) else: dst = pathlib.Path(self._data_dir, path, dst) self._install_path(whl, src, dst) return wheel_file class _EditableWheelBuilder(_WheelBuilder): @property def _top_level_modules(self) -> Collection[str]: modules = set() for type_ in self._manifest: for path, _ in self._manifest[type_]: name, dot, ext = path.parts[0].partition('.') if dot: # module suffix = dot + ext if suffix in _SUFFIXES: modules.add(name) else: # package modules.add(name) return modules def build(self, directory: Path, source_dir: pathlib.Path, build_dir: pathlib.Path, # type: ignore[override] build_command: List[str], verbose: bool = False) -> pathlib.Path: wheel_file = pathlib.Path(directory, f'{self.name}.whl') with mesonpy._wheelfile.WheelFile(wheel_file, 'w') as whl: self._wheel_write_metadata(whl) whl.writestr( f'{self._distinfo_dir}/direct_url.json', source_dir.as_uri().encode('utf-8')) # install loader module loader_module_name = f'_{self._metadata.distribution_name}_editable_loader' whl.writestr( f'{loader_module_name}.py', read_binary('mesonpy', '_editable.py') + textwrap.dedent(f''' install( {self._metadata.name!r}, {self._top_level_modules!r}, {os.fspath(build_dir)!r}, {build_command!r}, {verbose!r}, )''').encode('utf-8')) # install .pth file whl.writestr( f'{self._metadata.canonical_name}-editable.pth', f'import {loader_module_name}'.encode('utf-8')) return wheel_file def _validate_pyproject_config(pyproject: Dict[str, Any]) -> Dict[str, Any]: def _table(scheme: Dict[str, Callable[[Any, str], Any]]) -> Callable[[Any, str], Dict[str, Any]]: def func(value: Any, name: str) -> Dict[str, Any]: if not isinstance(value, dict): raise ConfigError(f'Configuration entry "{name}" must be a table') table = {} for key, val in value.items(): check = scheme.get(key) if check is None: raise ConfigError(f'Unknown configuration entry "{name}.{key}"') table[key] = check(val, f'{name}.{key}') return table return func def _strings(value: Any, name: str) -> List[str]: if not isinstance(value, list) or not all(isinstance(x, str) for x in value): raise ConfigError(f'Configuration entry "{name}" must be a list of strings') return value def _bool(value: Any, name: str) -> bool: if not isinstance(value, bool): raise ConfigError(f'Configuration entry "{name}" must be a boolean') return value def _string_or_path(value: Any, name: str) -> str: if not isinstance(value, str): raise ConfigError(f'Configuration entry "{name}" must be a string') if os.path.isfile(value): value = os.path.abspath(value) return value scheme = _table({ 'meson': _string_or_path, 'limited-api': _bool, 'args': _table({ name: _strings for name in _MESON_ARGS_KEYS }), }) table = pyproject.get('tool', {}).get('meson-python', {}) return scheme(table, 'tool.meson-python') def _validate_config_settings(config_settings: Dict[str, Any]) -> Dict[str, Any]: """Validate options received from build frontend.""" def _string(value: Any, name: str) -> str: if not isinstance(value, str): raise ConfigError(f'Only one value for "{name}" can be specified') return value def _bool(value: Any, name: str) -> bool: return True def _string_or_strings(value: Any, name: str) -> List[str]: return list([value,] if isinstance(value, str) else value) options = { 'builddir': _string, 'build-dir': _string, 'editable-verbose': _bool, 'dist-args': _string_or_strings, 'setup-args': _string_or_strings, 'compile-args': _string_or_strings, 'install-args': _string_or_strings, } assert all(f'{name}-args' in options for name in _MESON_ARGS_KEYS) config = {} for key, value in config_settings.items(): parser = options.get(key) if parser is None: matches = difflib.get_close_matches(key, options.keys(), n=2) if matches: alternatives = ' or '.join(f'"{match}"' for match in matches) raise ConfigError(f'Unknown option "{key}". Did you mean {alternatives}?') else: raise ConfigError(f'Unknown option "{key}"') config[key] = parser(value, key) # Check backward compatibility aliases. aliases = { 'build-dir': 'builddir', } for key, alt in aliases.items(): if key in config and alt in config: raise ConfigError(f'Option "{alt}" is a backward compatibility alias for "{key}". Only one can be used') if alt in config: config[key] = config[alt] return config class Project(): """Meson project wrapper to generate Python artifacts.""" def __init__( self, source_dir: Path, build_dir: Path, meson_args: Optional[MesonArgs] = None, editable_verbose: bool = False, ) -> None: self._source_dir = pathlib.Path(source_dir).absolute() self._build_dir = pathlib.Path(build_dir).absolute() self._editable_verbose = editable_verbose self._meson_native_file = self._build_dir / 'meson-python-native-file.ini' self._meson_cross_file = self._build_dir / 'meson-python-cross-file.ini' self._meson_args: MesonArgs = collections.defaultdict(list) self._limited_api = False # load pyproject.toml pyproject = tomllib.loads(self._source_dir.joinpath('pyproject.toml').read_text(encoding='utf-8')) # load meson args from pyproject.toml pyproject_config = _validate_pyproject_config(pyproject) for key, value in pyproject_config.get('args', {}).items(): self._meson_args[key].extend(value) # meson arguments from the command line take precedence over # arguments from the configuration file thus are added later if meson_args: for key, value in meson_args.items(): self._meson_args[key].extend(value) # determine command to invoke meson self._meson = _get_meson_command(pyproject_config.get('meson')) self._ninja = _env_ninja_command() if self._ninja is None: raise ConfigError(f'Could not find ninja version {_NINJA_REQUIRED_VERSION} or newer.') os.environ.setdefault('NINJA', self._ninja) # make sure the build dir exists self._build_dir.mkdir(exist_ok=True, parents=True) # if the build dir is empty, add .gitignore and .hgignore files if not any(self._build_dir.iterdir()): _add_ignore_files(self._build_dir) # setuptools-like ARCHFLAGS environment variable support if sysconfig.get_platform().startswith('macosx-'): archflags = os.environ.get('ARCHFLAGS', '').strip() if archflags: # parse the ARCHFLAGS environment variable parser = argparse.ArgumentParser(add_help=False, allow_abbrev=False) parser.add_argument('-arch', action='append') args, unknown = parser.parse_known_args(archflags.split()) if unknown: raise ConfigError(f'Unknown flag specified in $ARCHFLAGS={archflags!r}') arch, *other = set(args.arch) if other: raise ConfigError(f'Multi-architecture builds are not supported but $ARCHFLAGS={archflags!r}') macver, _, nativearch = platform.mac_ver() if arch != nativearch: x = os.environ.setdefault('_PYTHON_HOST_PLATFORM', f'macosx-{macver}-{arch}') if not x.endswith(arch): raise ConfigError(f'$ARCHFLAGS={archflags!r} and $_PYTHON_HOST_PLATFORM={x!r} do not agree') family = 'aarch64' if arch == 'arm64' else arch cross_file_data = textwrap.dedent(f''' [binaries] c = ['cc', '-arch', {arch!r}] cpp = ['c++', '-arch', {arch!r}] objc = ['cc', '-arch', {arch!r}] objcpp = ['c++', '-arch', {arch!r}] [host_machine] system = 'darwin' cpu = {arch!r} cpu_family = {family!r} endian = 'little' ''') self._meson_cross_file.write_text(cross_file_data, encoding='utf-8') self._meson_args['setup'].extend(('--cross-file', os.fspath(self._meson_cross_file))) # write the native file native_file_data = textwrap.dedent(f''' [binaries] python = '{sys.executable}' ''') self._meson_native_file.write_text(native_file_data, encoding='utf-8') # reconfigure if we have a valid Meson build directory. Meson # uses the presence of the 'meson-private/coredata.dat' file # in the build directory as indication that the build # directory has already been configured and arranges this file # to be created as late as possible or deleted if something # goes wrong during setup. reconfigure = self._build_dir.joinpath('meson-private/coredata.dat').is_file() # run meson setup self._configure(reconfigure=reconfigure) # package metadata if 'project' in pyproject: self._metadata = Metadata.from_pyproject(pyproject, self._source_dir) # set version from meson.build if version is declared as dynamic if 'version' in self._metadata.dynamic: version = self._meson_version if version == 'undefined': raise pyproject_metadata.ConfigurationError( 'Field "version" declared as dynamic but version is not defined in meson.build') self._metadata.version = packaging.version.Version(version) else: # if project section is missing, use minimal metdata from meson.build name, version = self._meson_name, self._meson_version if version == 'undefined': raise pyproject_metadata.ConfigurationError( 'Section "project" missing in pyproject.toml and version is not defined in meson.build') self._metadata = Metadata(name=name, version=packaging.version.Version(version)) # verify that we are running on a supported interpreter if self._metadata.requires_python: self._metadata.requires_python.prereleases = True if platform.python_version().rstrip('+') not in self._metadata.requires_python: raise MesonBuilderError( f'The package requires Python version {self._metadata.requires_python}, ' f'running on {platform.python_version()}') # limited API self._limited_api = pyproject_config.get('limited-api', False) if self._limited_api: # check whether limited API is disabled for the Meson project options = self._info('intro-buildoptions') value = next((option['value'] for option in options if option['name'] == 'python.allow_limited_api'), None) if not value: self._limited_api = False if self._limited_api and bool(sysconfig.get_config_var('Py_GIL_DISABLED')): raise BuildError( 'The package targets Python\'s Limited API, which is not supported by free-threaded CPython. ' 'The "python.allow_limited_api" Meson build option may be used to override the package default.') def _run(self, cmd: Sequence[str]) -> None: """Invoke a subprocess.""" # Flush the line to ensure that the log line with the executed # command line appears before the command output. Without it, # the lines appear in the wrong order in pip output. _log('{style.INFO}+ {cmd}{style.RESET}'.format(style=style, cmd=' '.join(cmd)), flush=True) r = subprocess.run(cmd, cwd=self._build_dir) if r.returncode != 0: raise SystemExit(r.returncode) def _configure(self, reconfigure: bool = False) -> None: """Configure Meson project.""" setup_args = [ os.fspath(self._source_dir), os.fspath(self._build_dir), # default build options '-Dbuildtype=release', '-Db_ndebug=if-release', '-Db_vscrt=md', # user build options *self._meson_args['setup'], # pass native file last to have it override the python # interpreter path that may have been specified in user # provided native files f'--native-file={os.fspath(self._meson_native_file)}', ] if reconfigure: setup_args.insert(0, '--reconfigure') self._run(self._meson + ['setup', *setup_args]) @property def _build_command(self) -> List[str]: assert self._ninja is not None # help mypy out if sys.platform == 'win32': # On Windows use 'meson compile' to setup the MSVC compiler # environment. Using the --ninja-args option allows to # provide the exact same semantics for the compile arguments # provided by the users. cmd = self._meson + ['compile'] args = list(self._meson_args['compile']) if args: cmd.append(f'--ninja-args={args!r}') return cmd return [self._ninja, *self._meson_args['compile']] @functools.lru_cache(maxsize=None) def build(self) -> None: """Build the Meson project.""" self._run(self._build_command) @functools.lru_cache() def _info(self, name: str) -> Any: """Read info from meson-info directory.""" info = self._build_dir.joinpath('meson-info', f'{name}.json') return json.loads(info.read_text(encoding='utf-8')) @property def _manifest(self) -> DefaultDict[str, List[Tuple[pathlib.Path, str]]]: """The files to be added to the wheel, organized by wheel path.""" # Obtain the list of files Meson would install. install_plan = self._info('intro-install_plan') # Parse the 'meson install' args to extract --tags and --skip-subprojects parser = argparse.ArgumentParser(add_help=False) parser.add_argument('--tags') parser.add_argument('--skip-subprojects', nargs='?', const='*', default='') args, _ = parser.parse_known_args(self._meson_args['install']) install_tags = {t.strip() for t in args.tags.split(',')} if args.tags else None skip_subprojects = {p for p in (p.strip() for p in args.skip_subprojects.split(',')) if p} # Filter the install plan accordingly. sources: DefaultDict[str, Dict[str, Dict[str, str]]] = collections.defaultdict(dict) for key, targets in install_plan.items(): for target, details in targets.items(): if install_tags is not None and details['tag'] not in install_tags: continue subproject = details.get('subproject') if subproject is not None and (subproject in skip_subprojects or '*' in skip_subprojects): continue sources[key][target] = details # Map Meson installation locations to wheel paths. return _map_to_wheel(sources) @property def _meson_name(self) -> str: """Name in meson.build.""" name = self._info('intro-projectinfo')['descriptive_name'] assert isinstance(name, str) return name @property def _meson_version(self) -> str: """Version in meson.build.""" name = self._info('intro-projectinfo')['version'] assert isinstance(name, str) return name def sdist(self, directory: Path) -> pathlib.Path: """Generates a sdist (source distribution) in the specified directory.""" # Generate meson dist file. self._run(self._meson + ['dist', '--allow-dirty', '--no-tests', '--formats', 'gztar', *self._meson_args['dist']]) dist_name = f'{self._metadata.distribution_name}-{self._metadata.version}' meson_dist_name = f'{self._meson_name}-{self._meson_version}' meson_dist_path = pathlib.Path(self._build_dir, 'meson-dist', f'{meson_dist_name}.tar.gz') sdist_path = pathlib.Path(directory, f'{dist_name}.tar.gz') pyproject_toml_mtime = 0 with tarfile.open(meson_dist_path, 'r:gz') as meson_dist, mesonpy._util.create_targz(sdist_path) as sdist: for member in meson_dist.getmembers(): if member.isfile(): file = meson_dist.extractfile(member.name) # Reset pax extended header. The tar archive member may be # using pax headers to store some file metadata. The pax # headers are not reset when the metadata is modified and # they take precedence when the member is deserialized. # This is relevant because when rewriting the member name, # the length of the path may shrink from being more than # 100 characters (requiring the path to be stored in the # pax headers) to being less than 100 characters. When this # happens, the tar archive member is serialized with the # shorter name in the regular header and the longer one in # the extended pax header. The archives handled here are # not expected to use extended pax headers other than for # the ones required to encode file metadata. The easiest # solution is to reset the pax extended headers. member.pax_headers = {} # Rewrite the path to match the sdist distribution name. stem = member.name.split('/', 1)[1] member.name = '/'.join((dist_name, stem)) if stem == 'pyproject.toml': pyproject_toml_mtime = member.mtime # Reset owner and group to root:root. This mimics what # 'git archive' does and makes the sdist reproducible upon # being built by different users. member.uname = member.gname = 'root' member.uid = member.gid = 0 sdist.addfile(member, file) # Add 'PKG-INFO'. member = tarfile.TarInfo(f'{dist_name}/PKG-INFO') member.uid = member.gid = 0 member.uname = member.gname = 'root' # Set the 'PKG-INFO' modification time to the modification time of # 'pyproject.toml' in the archive generated by 'meson dist'. In # turn this is the last commit time, unless touched by a dist # script. This makes the sdist reproducible upon being built at # different times, when dist scripts are not used, which should be # the majority of cases. # # Note that support for dynamic version in project metadata allows # the version to depend on the build time. Therefore, setting the # 'PKG-INFO' modification time to the 'pyproject.toml' # modification time can be seen as not strictly correct. However, # the sdist standard does not dictate which modification time to # use for 'PKG-INFO'. This choice allows to make the sdist # byte-for-byte reproducible in the most common case. member.mtime = pyproject_toml_mtime metadata = bytes(self._metadata.as_rfc822()) member.size = len(metadata) sdist.addfile(member, io.BytesIO(metadata)) return sdist_path def wheel(self, directory: Path) -> pathlib.Path: """Generates a wheel in the specified directory.""" self.build() builder = _WheelBuilder(self._metadata, self._manifest, self._limited_api) return builder.build(directory) def editable(self, directory: Path) -> pathlib.Path: """Generates an editable wheel in the specified directory.""" self.build() builder = _EditableWheelBuilder(self._metadata, self._manifest, self._limited_api) return builder.build(directory, self._source_dir, self._build_dir, self._build_command, self._editable_verbose) @contextlib.contextmanager def _project(config_settings: Optional[Dict[Any, Any]] = None) -> Iterator[Project]: """Create the project given the given config settings.""" settings = _validate_config_settings(config_settings or {}) meson_args = typing.cast('MesonArgs', {name: settings.get(f'{name}-args', []) for name in _MESON_ARGS_KEYS}) source_dir = os.path.curdir build_dir = settings.get('build-dir') editable_verbose = bool(settings.get('editable-verbose')) with contextlib.ExitStack() as ctx: if build_dir is None: build_dir = ctx.enter_context(tempfile.TemporaryDirectory(prefix='.mesonpy-', dir=source_dir)) yield Project(source_dir, build_dir, meson_args, editable_verbose) def _parse_version_string(string: str) -> Tuple[int, ...]: """Parse version string.""" try: return tuple(map(int, string.split('.')[:3])) except ValueError: return (0, ) def _get_meson_command( meson: Optional[str] = None, *, version: str = _MESON_REQUIRED_VERSION ) -> List[str]: """Return the command to invoke meson.""" # The MESON env var, if set, overrides the config value from pyproject.toml. # The config value, if given, is an absolute path or the name of an executable. meson = os.environ.get('MESON', meson or 'meson') # If the specified Meson string ends in `.py`, we run it with the current # Python executable. This avoids problems for users on Windows, where # making a script executable isn't enough to get it to run when invoked # directly. For packages that vendor a forked Meson, the `meson.py` in the # root of the Meson repo can be used this way. if meson.endswith('.py'): if not os.path.exists(meson): raise ConfigError(f'Could not find the specified meson: "{meson}"') cmd = [sys.executable, meson] else: cmd = [meson] # The meson Python package is a dependency of the meson-python Python # package, however, it may occur that the meson Python package is installed # but the corresponding meson command is not available in $PATH. Implement # a runtime check to verify that the build environment is setup correcly. try: r = subprocess.run(cmd + ['--version'], text=True, capture_output=True) except FileNotFoundError as err: raise ConfigError(f'meson executable "{meson}" not found') from err if r.returncode != 0: raise ConfigError(f'Could not execute meson: {r.stderr.strip()}') meson_version = r.stdout.strip() if _parse_version_string(meson_version) < _parse_version_string(version): raise ConfigError(f'Could not find meson version {version} or newer, found {meson_version}.') return cmd def _env_ninja_command(*, version: str = _NINJA_REQUIRED_VERSION) -> Optional[str]: """Returns the path to ninja, or None if no ninja found.""" required_version = _parse_version_string(version) env_ninja = os.environ.get('NINJA') ninja_candidates = [env_ninja] if env_ninja else ['ninja', 'ninja-build', 'samu'] for ninja in ninja_candidates: ninja_path = shutil.which(ninja) if ninja_path is not None: version = subprocess.run([ninja_path, '--version'], check=False, text=True, capture_output=True).stdout if _parse_version_string(version) >= required_version: return ninja_path return None def _add_ignore_files(directory: pathlib.Path) -> None: directory.joinpath('.gitignore').write_text(textwrap.dedent(''' # This file is generated by meson-python. It will not be recreated if deleted or modified. * '''), encoding='utf-8') directory.joinpath('.hgignore').write_text(textwrap.dedent(''' # This file is generated by meson-python. It will not be recreated if deleted or modified. syntax: glob **/* '''), encoding='utf-8') def _pyproject_hook(func: Callable[P, T]) -> Callable[P, T]: @functools.wraps(func) def wrapper(*args: P.args, **kwargs: P.kwargs) -> T: warnings.showwarning = _showwarning try: return func(*args, **kwargs) except (Error, pyproject_metadata.ConfigurationError) as exc: prefix = f'{style.ERROR}meson-python: error:{style.RESET} ' _log('\n' + textwrap.indent(str(exc), prefix)) raise SystemExit(1) from exc return wrapper @_pyproject_hook def get_requires_for_build_sdist(config_settings: Optional[Dict[str, str]] = None) -> List[str]: dependencies = [] if os.environ.get('NINJA') is None and _env_ninja_command() is None: dependencies.append(f'ninja >= {_NINJA_REQUIRED_VERSION}') return dependencies @_pyproject_hook def get_requires_for_build_wheel(config_settings: Optional[Dict[str, str]] = None) -> List[str]: dependencies = [] if os.environ.get('NINJA') is None and _env_ninja_command() is None: dependencies.append(f'ninja >= {_NINJA_REQUIRED_VERSION}') if sys.platform.startswith('linux') and not shutil.which('patchelf'): dependencies.append('patchelf >= 0.11.0') return dependencies get_requires_for_build_editable = get_requires_for_build_wheel @_pyproject_hook def build_sdist( sdist_directory: str, config_settings: Optional[Dict[Any, Any]] = None, ) -> str: out = pathlib.Path(sdist_directory) with _project(config_settings) as project: return project.sdist(out).name @_pyproject_hook def build_wheel( wheel_directory: str, config_settings: Optional[Dict[Any, Any]] = None, metadata_directory: Optional[str] = None, ) -> str: out = pathlib.Path(wheel_directory) with _project(config_settings) as project: return project.wheel(out).name @_pyproject_hook def build_editable( wheel_directory: str, config_settings: Optional[Dict[Any, Any]] = None, metadata_directory: Optional[str] = None, ) -> str: # Force set a permanent build directory. if not config_settings: config_settings = {} if 'build-dir' not in config_settings and 'builddir' not in config_settings: config_settings['build-dir'] = 'build/' + mesonpy._tags.get_abi_tag() out = pathlib.Path(wheel_directory) with _project(config_settings) as project: return project.editable(out).name ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/mesonpy/_compat.py0000664000000000000000000000264214706721634016566 0ustar00rootroot# SPDX-FileCopyrightText: 2021 Filipe Laíns # SPDX-FileCopyrightText: 2021 Quansight, LLC # SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT from __future__ import annotations import functools import importlib.resources import os import sys import typing if sys.version_info >= (3, 9): from collections.abc import Collection, Iterable, Iterator, Mapping, Sequence else: from typing import Collection, Iterable, Iterator, Mapping, Sequence if sys.version_info >= (3, 8): from functools import cached_property else: cached_property = lambda x: property(functools.lru_cache(maxsize=None)(x)) # noqa: E731 if sys.version_info >= (3, 9): def read_binary(package: str, resource: str) -> bytes: return importlib.resources.files(package).joinpath(resource).read_bytes() else: read_binary = importlib.resources.read_binary if typing.TYPE_CHECKING: from typing import Union if sys.version_info >= (3, 10): from typing import ParamSpec else: from typing_extensions import ParamSpec if sys.version_info >= (3, 11): from typing import Self else: from typing_extensions import Self Path = Union[str, os.PathLike] __all__ = [ 'cached_property', 'read_binary', 'Collection', 'Iterable', 'Iterator', 'Mapping', 'Path', 'ParamSpec', 'Self', 'Sequence', ] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/mesonpy/_editable.py0000664000000000000000000003337714706721634017065 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT # This file should be standalone! It is copied during the editable hook installation. from __future__ import annotations import ast import functools import importlib.abc import importlib.machinery import importlib.util import inspect import json import os import pathlib import subprocess import sys import typing if typing.TYPE_CHECKING: from collections.abc import Sequence, Set from types import ModuleType from typing import Any, Dict, Iterator, List, Optional, Tuple, Union from typing_extensions import Buffer NodeBase = Dict[str, Union['Node', str]] PathStr = Union[str, os.PathLike[str]] else: NodeBase = dict if sys.version_info >= (3, 12): from importlib.resources.abc import Traversable, TraversableResources elif sys.version_info >= (3, 9): from importlib.abc import Traversable, TraversableResources else: class Traversable: pass class TraversableResources: pass MARKER = 'MESONPY_EDITABLE_SKIP' VERBOSE = 'MESONPY_EDITABLE_VERBOSE' class MesonpyOrphan(Traversable): def __init__(self, name: str): self._name = name @property def name(self) -> str: return self._name def is_dir(self) -> bool: return False def is_file(self) -> bool: return False def iterdir(self) -> Iterator[Traversable]: raise FileNotFoundError() def open(self, *args, **kwargs): # type: ignore raise FileNotFoundError() def joinpath(self, *descendants: PathStr) -> Traversable: if not descendants: return self name = os.fspath(descendants[-1]).split('/')[-1] return MesonpyOrphan(name) def __truediv__(self, child: PathStr) -> Traversable: return self.joinpath(child) def read_bytes(self) -> bytes: raise FileNotFoundError() def read_text(self, encoding: Optional[str] = None) -> str: raise FileNotFoundError() class MesonpyTraversable(Traversable): def __init__(self, name: str, tree: Node): self._name = name self._tree = tree @property def name(self) -> str: return self._name def is_dir(self) -> bool: return True def is_file(self) -> bool: return False def iterdir(self) -> Iterator[Traversable]: for name, node in self._tree.items(): yield MesonpyTraversable(name, node) if isinstance(node, dict) else pathlib.Path(node) # type: ignore def open(self, *args, **kwargs): # type: ignore raise IsADirectoryError() @staticmethod def _flatten(names: Tuple[PathStr, ...]) -> Iterator[str]: for name in names: yield from os.fspath(name).split('/') def joinpath(self, *descendants: PathStr) -> Traversable: if not descendants: return self names = self._flatten(descendants) name = next(names) node = self._tree.get(name) if isinstance(node, dict): return MesonpyTraversable(name, node).joinpath(*names) if isinstance(node, str): return pathlib.Path(node).joinpath(*names) return MesonpyOrphan(name).joinpath(*names) def __truediv__(self, child: PathStr) -> Traversable: return self.joinpath(child) def read_bytes(self) -> bytes: raise IsADirectoryError() def read_text(self, encoding: Optional[str] = None) -> str: raise IsADirectoryError() class MesonpyReader(TraversableResources): def __init__(self, name: str, tree: Node): self._name = name self._tree = tree def files(self) -> Traversable: return MesonpyTraversable(self._name, self._tree) class ExtensionFileLoader(importlib.machinery.ExtensionFileLoader): def __init__(self, name: str, path: str, tree: Node): super().__init__(name, path) self._tree = tree def get_resource_reader(self, name: str) -> TraversableResources: return MesonpyReader(name, self._tree) class SourceFileLoader(importlib.machinery.SourceFileLoader): def __init__(self, name: str, path: str, tree: Node): super().__init__(name, path) self._tree = tree def set_data(self, path: Union[bytes, str], data: Buffer, *, _mode: int = ...) -> None: # disable saving bytecode pass def get_resource_reader(self, name: str) -> TraversableResources: return MesonpyReader(name, self._tree) class SourcelessFileLoader(importlib.machinery.SourcelessFileLoader): def __init__(self, name: str, path: str, tree: Node): super().__init__(name, path) self._tree = tree def get_resource_reader(self, name: str) -> TraversableResources: return MesonpyReader(name, self._tree) LOADERS = \ [(ExtensionFileLoader, s) for s in importlib.machinery.EXTENSION_SUFFIXES] + \ [(SourceFileLoader, s) for s in importlib.machinery.SOURCE_SUFFIXES] + \ [(SourcelessFileLoader, s) for s in importlib.machinery.BYTECODE_SUFFIXES] def build_module_spec(cls: type, name: str, path: str, tree: Optional[Node]) -> importlib.machinery.ModuleSpec: loader = cls(name, path, tree) spec = importlib.machinery.ModuleSpec(name, loader, origin=path) spec.has_location = True if loader.is_package(name): spec.submodule_search_locations = [os.path.join(__file__, name)] return spec class Node(NodeBase): """Tree structure to store a virtual filesystem view.""" def __missing__(self, key: str) -> Node: value = self[key] = Node() return value def __setitem__(self, key: Union[str, Tuple[str, ...]], value: Union[Node, str]) -> None: node = self if isinstance(key, tuple): for k in key[:-1]: node = typing.cast(Node, node[k]) key = key[-1] dict.__setitem__(node, key, value) def __getitem__(self, key: Union[str, Tuple[str, ...]]) -> Union[Node, str]: node = self if isinstance(key, tuple): for k in key[:-1]: node = typing.cast(Node, node[k]) key = key[-1] return dict.__getitem__(node, key) def get(self, key: Union[str, Tuple[str, ...]]) -> Optional[Union[Node, str]]: # type: ignore[override] node = self if isinstance(key, tuple): for k in key[:-1]: v = dict.get(node, k) if v is None: return None node = typing.cast(Node, v) key = key[-1] return dict.get(node, key) def walk(src: str, exclude_files: Set[str], exclude_dirs: Set[str]) -> Iterator[str]: for root, dirnames, filenames in os.walk(src): for name in dirnames.copy(): dirsrc = os.path.join(root, name) relpath = os.path.relpath(dirsrc, src) if relpath in exclude_dirs: dirnames.remove(name) # sort to process directories determninistically dirnames.sort() for name in sorted(filenames): filesrc = os.path.join(root, name) relpath = os.path.relpath(filesrc, src) if relpath in exclude_files: continue yield relpath def collect(install_plan: Dict[str, Dict[str, Any]]) -> Node: tree = Node() for key, data in install_plan.items(): for src, target in data.items(): path = pathlib.Path(target['destination']) if path.parts[0] in {'{py_platlib}', '{py_purelib}'}: if key == 'install_subdirs' or key == 'targets' and os.path.isdir(src): exclude_files = {os.path.normpath(x) for x in target.get('exclude_files', [])} exclude_dirs = {os.path.normpath(x) for x in target.get('exclude_dirs', [])} for entry in walk(src, exclude_files, exclude_dirs): tree[(*path.parts[1:], *entry.split(os.sep))] = os.path.join(src, entry) else: tree[path.parts[1:]] = src return tree def find_spec(fullname: str, tree: Node) -> Optional[importlib.machinery.ModuleSpec]: namespace = False parts = fullname.split('.') # look for a package package = tree.get(tuple(parts)) if isinstance(package, Node): for loader, suffix in LOADERS: src = package.get('__init__' + suffix) if isinstance(src, str): return build_module_spec(loader, fullname, src, package) else: namespace = True # look for a module for loader, suffix in LOADERS: src = tree.get((*parts[:-1], parts[-1] + suffix)) if isinstance(src, str): return build_module_spec(loader, fullname, src, None) # namespace if namespace: spec = importlib.machinery.ModuleSpec(fullname, None, is_package=True) assert isinstance(spec.submodule_search_locations, list) # make mypy happy spec.submodule_search_locations.append(os.path.join(__file__, fullname)) return spec return None class MesonpyMetaFinder(importlib.abc.MetaPathFinder): def __init__(self, package: str, names: Set[str], path: str, cmd: List[str], verbose: bool = False): self._name = package self._top_level_modules = names self._build_path = path self._build_cmd = cmd self._verbose = verbose self._loaders: List[Tuple[type, str]] = [] def __repr__(self) -> str: return f'{self.__class__.__name__}({self._name!r}, {self._build_path!r})' def find_spec( self, fullname: str, path: Optional[Sequence[Union[bytes, str]]] = None, target: Optional[ModuleType] = None ) -> Optional[importlib.machinery.ModuleSpec]: if fullname.split('.', 1)[0] not in self._top_level_modules: return None if self._build_path in os.environ.get(MARKER, '').split(os.pathsep): return None tree = self._rebuild() return find_spec(fullname, tree) def _work_to_do(self, env: dict[str, str]) -> bool: if sys.platform == 'win32': # On Windows the build command is 'meson compile' eventually with a --ninja-args= option. if self._build_cmd[-1].startswith('--ninja-args='): ninja_args = ast.literal_eval(self._build_cmd[-1].split('=', 1)[1]) + ['-n'] dry_run_build_cmd = self._build_cmd[:-1] + [f'--ninja-args={ninja_args!r}'] else: dry_run_build_cmd = self._build_cmd + ['--ninja-args=-n'] else: dry_run_build_cmd = self._build_cmd + ['-n'] # Check adapted from # https://github.com/mesonbuild/meson/blob/a35d4d368a21f4b70afa3195da4d6292a649cb4c/mesonbuild/mtest.py#L1635-L1636 p = subprocess.run(dry_run_build_cmd, cwd=self._build_path, env=env, capture_output=True) return b'ninja: no work to do.' not in p.stdout and b'samu: nothing to do' not in p.stdout @functools.lru_cache(maxsize=1) def _rebuild(self) -> Node: try: # Skip editable wheel lookup during rebuild: during the build # the module we are rebuilding might be imported causing a # rebuild loop. env = os.environ.copy() env[MARKER] = os.pathsep.join((env.get(MARKER, ''), self._build_path)) if self._verbose or bool(env.get(VERBOSE, '')): # We want to show some output only if there is some work to do. if self._work_to_do(env): build_command = ' '.join(self._build_cmd) print(f'meson-python: building {self._name}: {build_command}', flush=True) subprocess.run(self._build_cmd, cwd=self._build_path, env=env, check=True) else: subprocess.run(self._build_cmd, cwd=self._build_path, env=env, stdout=subprocess.DEVNULL, check=True) except subprocess.CalledProcessError as exc: raise ImportError(f're-building the {self._name} meson-python editable wheel package failed') from exc install_plan_path = os.path.join(self._build_path, 'meson-info', 'intro-install_plan.json') with open(install_plan_path, 'r', encoding='utf8') as f: install_plan = json.load(f) return collect(install_plan) def _path_hook(self, path: str) -> MesonpyPathFinder: if os.altsep: path.replace(os.altsep, os.sep) path, _, key = path.rpartition(os.sep) if path == __file__: tree = self._rebuild() node = tree.get(tuple(key.split('.'))) if isinstance(node, Node): return MesonpyPathFinder(node) raise ImportError class MesonpyPathFinder(importlib.abc.PathEntryFinder): def __init__(self, tree: Node): self._tree = tree def find_spec(self, fullname: str, target: Optional[ModuleType] = None) -> Optional[importlib.machinery.ModuleSpec]: return find_spec(fullname, self._tree) def iter_modules(self, prefix: str) -> Iterator[Tuple[str, bool]]: yielded = set() for name, node in self._tree.items(): modname = inspect.getmodulename(name) if modname == '__init__' or modname in yielded: continue if isinstance(node, Node): modname = name for _, suffix in LOADERS: src = node.get('__init__' + suffix) if isinstance(src, str): yielded.add(modname) yield prefix + modname, True elif modname and '.' not in modname: yielded.add(modname) yield prefix + modname, False def install(package: str, names: Set[str], path: str, cmd: List[str], verbose: bool) -> None: finder = MesonpyMetaFinder(package, names, path, cmd, verbose) sys.meta_path.insert(0, finder) sys.path_hooks.insert(0, finder._path_hook) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/mesonpy/_rpath.py0000664000000000000000000000420514706721634016416 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT from __future__ import annotations import os import subprocess import sys import typing if typing.TYPE_CHECKING: from typing import List from mesonpy._compat import Iterable, Path if sys.platform == 'linux': def _get_rpath(filepath: Path) -> List[str]: r = subprocess.run(['patchelf', '--print-rpath', os.fspath(filepath)], capture_output=True, text=True) return r.stdout.strip().split(':') def _set_rpath(filepath: Path, rpath: Iterable[str]) -> None: subprocess.run(['patchelf','--set-rpath', ':'.join(rpath), os.fspath(filepath)], check=True) def fix_rpath(filepath: Path, libs_relative_path: str) -> None: old_rpath = _get_rpath(filepath) new_rpath = [] for path in old_rpath: if path.startswith('$ORIGIN/'): path = '$ORIGIN/' + libs_relative_path new_rpath.append(path) if new_rpath != old_rpath: _set_rpath(filepath, new_rpath) elif sys.platform == 'darwin': def _get_rpath(filepath: Path) -> List[str]: rpath = [] r = subprocess.run(['otool', '-l', os.fspath(filepath)], capture_output=True, text=True) rpath_tag = False for line in [x.split() for x in r.stdout.split('\n')]: if line == ['cmd', 'LC_RPATH']: rpath_tag = True elif len(line) >= 2 and line[0] == 'path' and rpath_tag: rpath.append(line[1]) rpath_tag = False return rpath def _replace_rpath(filepath: Path, old: str, new: str) -> None: subprocess.run(['install_name_tool', '-rpath', old, new, os.fspath(filepath)], check=True) def fix_rpath(filepath: Path, libs_relative_path: str) -> None: for path in _get_rpath(filepath): if path.startswith('@loader_path/'): _replace_rpath(filepath, path, '@loader_path/' + libs_relative_path) else: def fix_rpath(filepath: Path, libs_relative_path: str) -> None: raise NotImplementedError(f'Bundling libraries in wheel is not supported on {sys.platform}') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/mesonpy/_tags.py0000664000000000000000000001500514706721634016236 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT from __future__ import annotations import os import platform import struct import sys import sysconfig import typing if typing.TYPE_CHECKING: # pragma: no cover from typing import Optional, Union # https://peps.python.org/pep-0425/#python-tag INTERPRETERS = { 'python': 'py', 'cpython': 'cp', 'pypy': 'pp', 'ironpython': 'ip', 'jython': 'jy', } _32_BIT_INTERPRETER = struct.calcsize('P') == 4 def get_interpreter_tag() -> str: name = sys.implementation.name name = INTERPRETERS.get(name, name) version = sys.version_info return f'{name}{version[0]}{version[1]}' def _get_config_var(name: str, default: Union[str, int, None] = None) -> Union[str, int, None]: value: Union[str, int, None] = sysconfig.get_config_var(name) if value is None: return default return value def _get_cpython_abi() -> str: version = sys.version_info debug = pymalloc = '' if _get_config_var('Py_DEBUG', hasattr(sys, 'gettotalrefcount')): debug = 'd' if version < (3, 8) and _get_config_var('WITH_PYMALLOC', True): pymalloc = 'm' return f'cp{version[0]}{version[1]}{debug}{pymalloc}' def get_abi_tag() -> str: # The best solution to obtain the Python ABI is to parse the # $SOABI or $EXT_SUFFIX sysconfig variables as defined in PEP-314. # PyPy reports a $SOABI that does not agree with $EXT_SUFFIX. # Using $EXT_SUFFIX will not break when PyPy will fix this. # See https://foss.heptapod.net/pypy/pypy/-/issues/3816 and # https://github.com/pypa/packaging/pull/607. try: empty, abi, ext = str(sysconfig.get_config_var('EXT_SUFFIX')).split('.') except ValueError as exc: # CPython <= 3.8.7 on Windows does not implement PEP3149 and # uses '.pyd' as $EXT_SUFFIX, which does not allow to extract # the interpreter ABI. Check that the fallback is not hit for # any other Python implementation. if sys.implementation.name != 'cpython': raise NotImplementedError from exc return _get_cpython_abi() # The packaging module initially based his understanding of the # $SOABI variable on the inconsistent value reported by PyPy, and # did not strip architecture information from it. Therefore the # ABI tag for later Python implementations (all the ones not # explicitly handled below) contains architecture information too. # Unfortunately, fixing this now would break compatibility. if abi.startswith('cpython'): abi = 'cp' + abi.split('-')[1] elif abi.startswith('cp'): abi = abi.split('-')[0] elif abi.startswith('pypy'): abi = '_'.join(abi.split('-')[:2]) elif abi.startswith('graalpy'): abi = '_'.join(abi.split('-')[:3]) return abi.replace('.', '_').replace('-', '_') def _get_macosx_platform_tag() -> str: ver, _, arch = platform.mac_ver() # Override the architecture with the one provided in the # _PYTHON_HOST_PLATFORM environment variable. This environment # variable affects the sysconfig.get_platform() return value and # is used to cross-compile python extensions on macOS for a # different architecture. We base the platform tag computation on # platform.mac_ver() but respect the content of the environment # variable. try: arch = os.environ.get('_PYTHON_HOST_PLATFORM', '').split('-')[2] except IndexError: pass # Override the macOS version if one is provided via the # MACOSX_DEPLOYMENT_TARGET environment variable. try: version = tuple(map(int, os.environ.get('MACOSX_DEPLOYMENT_TARGET', '').split('.')))[:2] except ValueError: version = tuple(map(int, ver.split('.')))[:2] # Python built with older macOS SDK on macOS 11, reports an # unexising macOS 10.16 version instead of the real version. # # The packaging module introduced a workaround # https://github.com/pypa/packaging/commit/67c4a2820c549070bbfc4bfbf5e2a250075048da # # This results in packaging versions up to 21.3 generating # platform tags like "macosx_10_16_x86_64" and later versions # generating "macosx_11_0_x86_64". Using the latter would be more # correct but prevents the resulting wheel from being installed on # systems using packaging 21.3 or earlier (pip 22.3 or earlier). # # Fortunately packaging versions carrying the workaround still # accepts "macosx_10_16_x86_64" as a compatible platform tag. We # can therefore ignore the issue and generate the slightly # incorrect tag. # The minimum macOS ABI version on arm64 is 11.0. The macOS SDK # on arm64 silently bumps any compatibility version specified via # the MACOSX_DEPLOYMENT_TARGET environment variable to 11.0. # Despite the platform ABI tag being intended to be a minimum # compatibility version, pip refuses to install wheels with a # platform tag specifying an ABI version lower than 11.0. Use # 11.0 as minimum ABI version on arm64. if arch == 'arm64' and version < (11, 0): version = (11, 0) major, minor = version if major >= 11: # For macOS reelases up to 10.15, the major version number is # actually part of the OS name and the minor version is the # actual OS release. Starting with macOS 11, the major # version number is the OS release and the minor version is # the patch level. Reset the patch level to zero. minor = 0 if _32_BIT_INTERPRETER: # 32-bit Python running on a 64-bit kernel. if arch == 'ppc64': arch = 'ppc' if arch == 'x86_64': arch = 'i386' return f'macosx_{major}_{minor}_{arch}' def get_platform_tag() -> str: platform = sysconfig.get_platform() if platform.startswith('macosx'): return _get_macosx_platform_tag() if _32_BIT_INTERPRETER: # 32-bit Python running on a 64-bit kernel. if platform == 'linux-x86_64': return 'linux_i686' if platform == 'linux-aarch64': return 'linux_armv7l' return platform.replace('-', '_').replace('.', '_').lower() class Tag: def __init__(self, interpreter: Optional[str] = None, abi: Optional[str] = None, platform: Optional[str] = None): self.interpreter = interpreter or get_interpreter_tag() self.abi = abi or get_abi_tag() self.platform = platform or get_platform_tag() def __str__(self) -> str: return f'{self.interpreter}-{self.abi}-{self.platform}' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/mesonpy/_util.py0000664000000000000000000000352514706721634016261 0ustar00rootroot# SPDX-FileCopyrightText: 2021 Filipe Laíns # SPDX-FileCopyrightText: 2021 Quansight, LLC # SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT from __future__ import annotations import contextlib import gzip import os import tarfile import typing from typing import IO if typing.TYPE_CHECKING: # pragma: no cover from mesonpy._compat import Iterator, Path @contextlib.contextmanager def chdir(path: Path) -> Iterator[Path]: """Context manager helper to change the current working directory -- cd.""" old_cwd = os.getcwd() os.chdir(os.fspath(path)) try: yield path finally: os.chdir(old_cwd) @contextlib.contextmanager def create_targz(path: Path) -> Iterator[tarfile.TarFile]: """Opens a .tar.gz file in the file system for edition..""" os.makedirs(os.path.dirname(path), exist_ok=True) file = typing.cast(IO[bytes], gzip.GzipFile( path, mode='w', # Set the stream last modification time to 0. This mimics # what 'git archive' does and makes the archives byte-for-byte # reproducible. mtime=0, )) tar = tarfile.TarFile( mode='w', fileobj=file, format=tarfile.PAX_FORMAT, # changed in 3.8 to GNU ) with contextlib.closing(file), tar: yield tar def setup_windows_console() -> bool: from ctypes import byref, windll # type: ignore from ctypes.wintypes import DWORD STD_OUTPUT_HANDLE = -11 ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x04 kernel = windll.kernel32 stdout = kernel.GetStdHandle(STD_OUTPUT_HANDLE) mode = DWORD() if not kernel.GetConsoleMode(stdout, byref(mode)): return False if not kernel.SetConsoleMode(stdout, mode.value | ENABLE_VIRTUAL_TERMINAL_PROCESSING): return False return True ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/mesonpy/_wheelfile.py0000664000000000000000000001030114706721634017236 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT from __future__ import annotations import base64 import csv import hashlib import io import os import re import stat import time import typing import zipfile if typing.TYPE_CHECKING: # pragma: no cover from types import TracebackType from typing import List, Optional, Tuple, Type, Union from mesonpy._compat import Path MIN_TIMESTAMP = 315532800 # 1980-01-01 00:00:00 UTC WHEEL_FILENAME_REGEX = re.compile(r'^(?P[^-]+)-(?P[^-]+)(:?-(?P[^-]+))?-(?P[^-]+-[^-]+-[^-]+).whl$') def _b64encode(data: bytes) -> bytes: return base64.urlsafe_b64encode(data).rstrip(b'=') class WheelFile: """Implement the wheel package binary distribution format. https://packaging.python.org/en/latest/specifications/binary-distribution-format/ """ def __new__(cls, filename: Path, mode: str = 'r', compression: int = zipfile.ZIP_DEFLATED) -> 'WheelFile': if mode == 'w': return super().__new__(WheelFileWriter) raise NotImplementedError @staticmethod def timestamp(mtime: Optional[float] = None) -> Tuple[int, int, int, int, int, int]: timestamp = int(os.environ.get('SOURCE_DATE_EPOCH', mtime or time.time())) # The ZIP file format does not support timestamps before 1980. timestamp = max(timestamp, MIN_TIMESTAMP) return time.gmtime(timestamp)[0:6] @staticmethod def hash(data: bytes) -> str: return 'sha256=' + _b64encode(hashlib.sha256(data).digest()).decode('ascii') def writestr(self, zinfo_or_arcname: Union[str, zipfile.ZipInfo], data: bytes) -> None: raise NotImplementedError def write(self, filename: Path, arcname: Optional[str] = None) -> None: raise NotImplementedError def close(self) -> None: raise NotImplementedError def __enter__(self) -> WheelFile: return self def __exit__(self, exc_type: Type[BaseException], exc_val: BaseException, exc_tb: TracebackType) -> None: self.close() class WheelFileWriter(WheelFile): def __init__(self, filepath: Path, mode: str, compression: int = zipfile.ZIP_DEFLATED): filename = os.path.basename(filepath) match = WHEEL_FILENAME_REGEX.match(filename) if not match: raise ValueError(f'invalid wheel filename: {filename!r}') self.name = match.group('name') self.version = match.group('version') self.entries: List[Tuple[str, str, int]] = [] self.archive = zipfile.ZipFile(filepath, mode='w', compression=compression, allowZip64=True) def writestr(self, zinfo_or_arcname: Union[str, zipfile.ZipInfo], data: bytes) -> None: if isinstance(data, str): data = data.encode('utf-8') if isinstance(zinfo_or_arcname, zipfile.ZipInfo): zinfo = zinfo_or_arcname else: zinfo = zipfile.ZipInfo(zinfo_or_arcname, date_time=self.timestamp()) zinfo.external_attr = 0o664 << 16 self.archive.writestr( zinfo, data, compress_type=self.archive.compression, compresslevel=self.archive.compresslevel) self.entries.append((zinfo.filename, self.hash(data), len(data))) def write(self, filename: Path, arcname: Optional[str] = None) -> None: with open(filename, 'rb') as f: st = os.fstat(f.fileno()) data = f.read() zinfo = zipfile.ZipInfo(arcname or str(filename), date_time=self.timestamp(st.st_mtime)) zinfo.external_attr = (stat.S_IMODE(st.st_mode) | stat.S_IFMT(st.st_mode)) << 16 self.writestr(zinfo, data) def close(self) -> None: record = f'{self.name}-{self.version}.dist-info/RECORD' data = io.StringIO() writer = csv.writer(data, delimiter=',', quotechar='"', lineterminator='\n') writer.writerows(self.entries) writer.writerow((record, '', '')) zi = zipfile.ZipInfo(record, date_time=self.timestamp()) zi.external_attr = 0o664 << 16 self.archive.writestr( zi, data.getvalue(), compress_type=self.archive.compression, compresslevel=self.archive.compresslevel) self.archive.close() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/noxfile.py0000664000000000000000000000331514706721634015114 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT import os import os.path import nox nox.options.sessions = ['docs', 'mypy', 'test'] nox.options.reuse_existing_virtualenvs = True @nox.session() def docs(session): """ Build the docs. Pass "serve" to serve. """ session.install('.[docs]') session.install('sphinx-autobuild') session.install('sphinxcontrib-spelling >= 7.0.0') session.chdir('docs') spelling_args = ('-b', 'spelling') sphinx_build_args = ('.', '_build') if not session.posargs: # run spell-checking session.run('sphinx-build', *spelling_args, *sphinx_build_args) # run normal build session.run('sphinx-build', *sphinx_build_args) else: if 'serve' in session.posargs: session.run('sphinx-autobuild', *sphinx_build_args) else: print('Unsupported argument to docs') @nox.session(python='3.7') def mypy(session): session.install('mypy==0.991') session.run('mypy', '-p', 'mesonpy') @nox.session(python=['3.7', '3.8', '3.9', '3.10', '3.11', 'pypy3.8', 'pypy3.9']) def test(session): htmlcov_output = os.path.join(session.virtualenv.location, 'htmlcov') xmlcov_output = os.path.join(session.virtualenv.location, f'coverage-{session.python}.xml') session.install('.[test]') # optional github actions integration if os.environ.get('GITHUB_ACTIONS') == 'true': session.install('pytest-github-actions-annotate-failures') session.run( 'pytest', '--showlocals', '-vv', '--cov', f'--cov-report=html:{htmlcov_output}', f'--cov-report=xml:{xmlcov_output}', *session.posargs ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/pyproject.toml0000664000000000000000000000534014706721634016012 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' backend-path = ['.'] requires = [ 'meson >= 0.63.3; python_version < "3.12"', 'meson >= 1.2.3; python_version >= "3.12"', 'packaging >= 19.0', 'pyproject-metadata >= 0.7.1', 'tomli >= 1.0.0; python_version < "3.11"', ] [project] name = 'meson-python' description = 'Meson Python build backend (PEP 517)' readme = 'README.rst' requires-python = '>= 3.7' license = { file = 'LICENSES/MIT.txt' } keywords = ['meson', 'build', 'backend', 'pep517', 'package'] maintainers = [ { name = 'Ralf Gommers', email = 'ralf.gommers@gmail.com' }, { name = 'Daniele Nicolodi', email = 'daniele@grinta.net' }, { name = 'Henry Schreiner', email = 'HenrySchreinerIII@gmail.com' }, { name = 'Thomas Li', email = '47963215+lithomas1@users.noreply.github.com' }, ] classifiers = [ 'Development Status :: 5 - Production/Stable', 'Programming Language :: Python', 'Topic :: Software Development :: Build Tools', ] dependencies = [ 'meson >= 0.63.3; python_version < "3.12"', 'meson >= 1.2.3; python_version >= "3.12"', 'packaging >= 19.0', 'pyproject-metadata >= 0.7.1', 'tomli >= 1.0.0; python_version < "3.11"', ] dynamic = [ 'version', ] [project.optional-dependencies] test = [ 'build', 'packaging >= 23.1', 'pytest >= 6.0', 'pytest-cov[toml]', 'pytest-mock', 'cython >= 3.0.3', # required for Python 3.12 support 'wheel', 'typing-extensions >= 3.7.4; python_version < "3.11"', ] docs = [ 'furo >= 2023.5.20', 'sphinx ~= 6.2', 'sphinx-copybutton >= 0.5.0', 'sphinx-design >= 0.1.0', 'sphinxext-opengraph >= 0.7.0', ] [project.urls] homepage = 'https://github.com/mesonbuild/meson-python' repository = 'https://github.com/mesonbuild/meson-python' documentation = 'https://mesonbuild.com/meson-python/' changelog = 'https://mesonbuild.com/meson-python/changelog.html' [tool.mypy] show_error_codes = true ignore_missing_imports = true strict = true [tool.ruff] line-length = 127 lint.extend-ignore = [ 'B019', ] lint.select = [ 'B', # flake8-bugbear 'C4', # flake8-comprehensions 'E', # pycodestyle 'F', # pyflakes 'I', # isort 'W', # pycodestyle 'RUF100', # ruff ] exclude = [ 'docs/conf.py', ] [tool.ruff.lint.isort] lines-between-types = 1 lines-after-imports = 2 known-first-party = [ 'mesonpy', ] [tool.coverage.run] disable_warnings = [ 'couldnt-parse', ] [tool.coverage.html] show_contexts = true [tool.pytest.ini_options] minversion = '6.0' addopts = ['-ra', '--strict-markers', '--strict-config'] log_cli_level = 'info' norecursedirs = 'tests/packages/*' testpaths = ['tests'] xfail_strict = true filterwarnings = [ 'error', ] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/__init__.py0000664000000000000000000000013414706721634016345 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/conftest.py0000664000000000000000000001432014706721634016435 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT import contextlib import os import os.path import pathlib import re import shutil import subprocess import sys import sysconfig import tempfile import warnings from venv import EnvBuilder import packaging.metadata import packaging.version import pytest import mesonpy from mesonpy._util import chdir def metadata(data): meta, other = packaging.metadata.parse_email(data) assert not other return meta def adjust_packaging_platform_tag(platform: str) -> str: if platform.startswith(('manylinux', 'musllinux')): # The packaging module generates overly specific platforms tags on # Linux. The platforms tags on Linux evolved over time. # meson-python uses more relaxed platform tags to maintain # compatibility with old wheel installation tools. The relaxed # platform tags match the ones generated by the wheel package. # https://packaging.python.org/en/latest/specifications/platform-compatibility-tags/ return re.sub(r'^(many|musl)linux(1|2010|2014|_\d+_\d+)_(.*)$', r'linux_\3', platform) if platform.startswith('macosx'): # Python built with older macOS SDK on macOS 11, reports an # unexising macOS 10.16 version instead of the real version. # The packaging module introduced a workaround in version # 22.0. Too maintain compatibility with older packaging # releases we don't implement it. Reconcile this. from platform import mac_ver version = tuple(map(int, mac_ver()[0].split('.')))[:2] if version == (10, 16): return re.sub(r'^macosx_\d+_\d+_(.*)$', r'macosx_10_16_\1', platform) return platform package_dir = pathlib.Path(__file__).parent / 'packages' @contextlib.contextmanager def in_git_repo_context(path=os.path.curdir): # Resist the tentation of using pathlib.Path here: it is not # supporded by subprocess in Python 3.7. path = os.path.abspath(path) shutil.rmtree(os.path.join(path, '.git'), ignore_errors=True) try: subprocess.run(['git', 'init', '-b', 'main', path], check=True) subprocess.run(['git', 'config', 'user.email', 'author@example.com'], cwd=path, check=True) subprocess.run(['git', 'config', 'user.name', 'A U Thor'], cwd=path, check=True) subprocess.run(['git', 'add', '*'], cwd=path, check=True) subprocess.run(['git', 'commit', '-q', '-m', 'Test'], cwd=path, check=True) yield finally: # PermissionError raised on Windows. with contextlib.suppress(PermissionError): shutil.rmtree(os.path.join(path, '.git')) @pytest.fixture(scope='session') def tmp_path_session(tmp_path_factory): return pathlib.Path(tempfile.mkdtemp( prefix='mesonpy-test-', dir=tmp_path_factory.mktemp('test'), )) class VEnv(EnvBuilder): def __init__(self, env_dir): super().__init__(symlinks=True, with_pip=True) # This warning is mistakenly generated by CPython 3.11.0 # https://github.com/python/cpython/pull/98743 with warnings.catch_warnings(): if sys.version_info[:3] == (3, 11, 0): warnings.filterwarnings('ignore', 'check_home argument is deprecated and ignored.', DeprecationWarning) self.create(env_dir) # Free-threaded Python 3.13 requires pip 24.1b1 or later. if sysconfig.get_config_var('Py_GIL_DISABLED'): # importlib.metadata is not available on Python 3.7 and # earlier, however no-gil builds are available only for # Python 3.13 and later. import importlib.metadata if packaging.version.Version(importlib.metadata.version('pip')) < packaging.version.Version('24.1b1'): self.pip('install', '--upgrade', 'pip >= 24.1b1') def ensure_directories(self, env_dir): context = super().ensure_directories(env_dir) # Store the path to the venv Python interpreter. There does # not seem to be a way to do this without subclassing. self.executable = context.env_exe return context def python(self, *args: str): return subprocess.check_output([self.executable, *args]).decode() def pip(self, *args: str): return self.python('-m', 'pip', *args) @pytest.fixture() def venv(tmp_path_factory): path = pathlib.Path(tmp_path_factory.mktemp('mesonpy-test-venv')) return VEnv(path) def generate_package_fixture(package): @pytest.fixture def fixture(): with chdir(package_dir / package) as new_path: yield new_path return fixture def generate_sdist_fixture(package): @pytest.fixture(scope='session') def fixture(tmp_path_session): with chdir(package_dir / package), in_git_repo_context(): return tmp_path_session / mesonpy.build_sdist(tmp_path_session) return fixture def generate_wheel_fixture(package): @pytest.fixture(scope='session') def fixture(tmp_path_session): with chdir(package_dir / package), in_git_repo_context(): return tmp_path_session / mesonpy.build_wheel(tmp_path_session) return fixture def generate_editable_fixture(package): @pytest.fixture(scope='session') def fixture(tmp_path_session): shutil.rmtree(package_dir / package / '.mesonpy' / 'editable', ignore_errors=True) with chdir(package_dir / package), in_git_repo_context(): return tmp_path_session / mesonpy.build_editable(tmp_path_session) return fixture # inject {package,sdist,wheel}_* fixtures (https://github.com/pytest-dev/pytest/issues/2424) for package in os.listdir(package_dir): normalized = package.replace('-', '_') globals()[f'package_{normalized}'] = generate_package_fixture(package) globals()[f'sdist_{normalized}'] = generate_sdist_fixture(package) globals()[f'wheel_{normalized}'] = generate_wheel_fixture(package) globals()[f'editable_{normalized}'] = generate_editable_fixture(package) @pytest.fixture(autouse=True, scope='session') def disable_pip_version_check(): # Cannot use the 'monkeypatch' fixture because of scope mismatch. mpatch = pytest.MonkeyPatch() yield mpatch.setenv('PIP_DISABLE_PIP_VERSION_CHECK', '1') mpatch.undo() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/complex/complex/__init__.py0000664000000000000000000000013414706721634023241 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/complex/complex/more/__init__.py0000664000000000000000000000016714706721634024211 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT def test(): return 44 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/complex/complex/more/baz.pyx0000664000000000000000000000017114706721634023411 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT def answer(): return 42 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/complex/complex/more/meson.build0000664000000000000000000000027514706721634024242 0ustar00rootroot# SPDX-FileCopyrightText: 2024 The meson-python developers # # SPDX-License-Identifier: MIT py.extension_module( 'baz', 'baz.pyx', install: true, subdir: 'complex/more', ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/complex/complex/namespace/bar.py0000664000000000000000000000017114706721634024203 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT def bar(): return 'bar' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/complex/complex/namespace/foo.py0000664000000000000000000000017114706721634024222 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT def foo(): return 'foo' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/complex/extension.c0000664000000000000000000000122714706721634021645 0ustar00rootroot// SPDX-FileCopyrightText: 2024 The meson-python developers // // SPDX-License-Identifier: MIT #include static PyObject* answer(PyObject* self) { return PyLong_FromLong(42); } static PyMethodDef methods[] = { {"answer", (PyCFunction) answer, METH_NOARGS, NULL}, {NULL, NULL, 0, NULL}, }; static struct PyModuleDef module = { PyModuleDef_HEAD_INIT, "extension", NULL, -1, methods, }; PyMODINIT_FUNC PyInit_extension(void) { PyObject *m = PyModule_Create(&module); if (m == NULL) { return NULL; } #ifdef Py_GIL_DISABLED PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED); #endif return m; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/complex/foo.py0000664000000000000000000000017014706721634020616 0ustar00rootroot# SPDX-FileCopyrightText: 2024 The meson-python developers # # SPDX-License-Identifier: MIT def foo(): return True ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/complex/meson.build0000664000000000000000000000233014706721634021623 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT project('complex', 'c', 'cython', version: '1.0.0') # Work around Python bug on mingw-w64, fixed in Python 3.12 # https://github.com/python/cpython/pull/100137 # The same work-around is also included in Meson 1.1.0 and later. if meson.version().version_compare('< 1.1') if host_machine.system() == 'windows' and meson.get_compiler('c').get_id() == 'gcc' add_project_arguments('-DMS_WIN64=', language: 'c') endif endif py = import('python').find_installation() py.install_sources( 'move.py', subdir: 'complex/more', pure: false, ) install_data( 'foo.py', rename: 'bar.py', install_dir: py.get_install_dir(pure: false) / 'complex', ) install_subdir( 'complex', install_dir: py.get_install_dir(pure: false), exclude_files: ['more/meson.build', 'more/baz.pyx'], ) py.extension_module( 'extension', 'extension.c', install: true, subdir: 'complex', ) py.extension_module( 'test', 'test.pyx', install: true, subdir: 'complex', ) py.extension_module( 'baz', 'complex/more/baz.pyx', install: true, subdir: 'complex/more', ) subdir('complex/more') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/complex/move.py0000664000000000000000000000017114706721634021002 0ustar00rootroot# SPDX-FileCopyrightText: 2024 The meson-python developers # # SPDX-License-Identifier: MIT def test(): return True ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/complex/pyproject.toml0000664000000000000000000000024214706721634022375 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/complex/test.pyx0000664000000000000000000000017114706721634021203 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT def answer(): return 42 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/configure-data/configure_data.py.in0000664000000000000000000000021314706721634024631 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT def message(): return 'meson says: @MSG@' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/configure-data/meson.build0000664000000000000000000000055414706721634023052 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT project('configure-data', version: '1.0.0') py = import('python').find_installation() configure_file( input: 'configure_data.py.in', output: 'configure_data.py', configuration: configuration_data({'MSG': 'hello!'}), install_dir: py.get_install_dir(), ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/configure-data/pyproject.toml0000664000000000000000000000024214706721634023616 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/custom-target-dir/codegen.py0000664000000000000000000000047614706721634023353 0ustar00rootroot#!/usr/bin/env python3 # # SPDX-FileCopyrightText: 2024 The meson-python developers # # SPDX-License-Identifier: MIT import os import sys outdir = os.path.join(sys.argv[1], 'generated') os.makedirs(outdir, exist_ok=True) for name in 'one.py', 'two.py': with open(os.path.join(outdir, name), 'w'): pass ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/custom-target-dir/meson.build0000664000000000000000000000052114706721634023526 0ustar00rootroot# SPDX-FileCopyrightText: 2024 The meson-python developers # # SPDX-License-Identifier: MIT project('custom-target-dir', version: '1.0.0') py = import('python').find_installation() custom_target( command: ['codegen.py', '@OUTDIR@'], output: 'generated', install: true, install_dir: py.get_install_dir() / 'package', ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/custom-target-dir/pyproject.toml0000664000000000000000000000024214706721634024300 0ustar00rootroot# SPDX-FileCopyrightText: 2024 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/detect-compiler/detect_compiler.c0000664000000000000000000000133414706721634024403 0ustar00rootroot// SPDX-FileCopyrightText: 2023 The meson-python developers // // SPDX-License-Identifier: MIT #include #if defined _MSC_VER # define _COMPILER "msvc" #elif defined __clang__ # define _COMPILER "clang" #elif defined __GNUC__ # define _COMPILER "gcc" #else # define _COMPILER "unknown" #endif static PyObject* compiler(PyObject* self) { return PyUnicode_FromString(_COMPILER); } static PyMethodDef methods[] = { {"compiler", (PyCFunction)compiler, METH_NOARGS, NULL}, {NULL, NULL, 0, NULL}, }; static struct PyModuleDef module = { PyModuleDef_HEAD_INIT, "detect_compiler", NULL, -1, methods, }; PyMODINIT_FUNC PyInit_detect_compiler(void) { return PyModule_Create(&module); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/detect-compiler/meson.build0000664000000000000000000000040414706721634023234 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT project('detect-compiler', 'c', version: '1.0') py = import('python').find_installation() py.extension_module('detect_compiler', 'detect_compiler.c', install: true) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/detect-compiler/pyproject.toml0000664000000000000000000000024214706721634024006 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/dist-script/meson.build0000664000000000000000000000024614706721634022425 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT project('dist-script', 'c', version: '1.0.0') meson.add_dist_script('') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/dist-script/pyproject.toml0000664000000000000000000000024214706721634023173 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/dynamic-version/meson.build0000664000000000000000000000021214706721634023260 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT project('dynamic-version', version: '1.0.0') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/dynamic-version/pyproject.toml0000664000000000000000000000034314706721634024037 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] [project] name = 'dynamic-version' dynamic = [ 'version', ] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/encoding/meson.build0000664000000000000000000000032214706721634021741 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT project('encoding', version: '1.0.0') py = import('python').find_installation() py.install_sources('テスト.py') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/encoding/pyproject.toml0000664000000000000000000000024214706721634022514 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] ././@PaxHeader0000000000000000000000000000012700000000000010215 xustar0065 path=meson_python-0.17.1/tests/packages/encoding/テスト.py 22 mtime=1729864604.0 meson_python-0.17.1/tests/packages/encoding/???.py0000664000000000000000000000013414706721634020526 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/executable/example.c0000664000000000000000000000022614706721634021734 0ustar00rootroot// SPDX-FileCopyrightText: 2022 The meson-python developers // // SPDX-License-Identifier: MIT #include int main() { printf("hello!"); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/executable/meson.build0000664000000000000000000000031414706721634022275 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT project('executable', 'c', version: '1.0.0') executable( 'example', 'example.c', install: true, ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/executable/pyproject.toml0000664000000000000000000000024214706721634023047 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/executable-bit/example-script.py0000775000000000000000000000016714706721634024227 0ustar00rootroot#!python # SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT print('hello!') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/executable-bit/example.c0000664000000000000000000000022614706721634022510 0ustar00rootroot// SPDX-FileCopyrightText: 2022 The meson-python developers // // SPDX-License-Identifier: MIT #include int main() { printf("hello!"); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/executable-bit/executable_module.py0000775000000000000000000000025714706721634024760 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT def foo(): return 'bar' if __name__ == '__main__': print('foo?', foo()) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/executable-bit/meson.build0000664000000000000000000000062614706721634023057 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT project('executable-bit', 'c', version: '1.0.0') py = import('python').find_installation() executable( 'example', 'example.c', install: true, ) install_data( 'example-script.py', rename: 'example-script', install_dir: get_option('bindir'), ) py.install_sources('executable_module.py') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/executable-bit/pyproject.toml0000664000000000000000000000024214706721634023623 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/full-metadata/LICENSE0000664000000000000000000000001514706721634021535 0ustar00rootrootsome license ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/full-metadata/README.md0000664000000000000000000000024714706721634022016 0ustar00rootroot # full-metadata An example package with all of the PEP 621 metadata! ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/full-metadata/meson.build0000664000000000000000000000021014706721634022667 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT project('full-metadata', version: '1.0.0') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/full-metadata/pyproject.toml0000664000000000000000000000217614706721634023456 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] [project] name = 'full-metadata' version = '1.2.3' description = 'Some package with all of the PEP 621 metadata' readme = 'README.md' requires-python = '>=3.7' license = {file = 'LICENSE'} keywords = ['full', 'metadata'] authors = [ {email = 'jhon.doe@example.com'}, {name = 'Jane Doe'} ] maintainers = [ {name = 'Jane Doe', email = 'jane.doe@example.com'} ] classifiers = [ 'Development Status :: 4 - Beta', 'Programming Language :: Python', ] dependencies = [ 'a', 'b > 1', 'c > 2; os_name != "nt"', ] [project.optional-dependencies] test = [ 'd < 3', 'e[all]' ] [project.urls] homepage = 'https://example.com' documentation = 'https://readthedocs.org' repository = 'https://github.com/mesonbuild/meson-python' changelog = 'https://github.com/mesonbuild/meson-python/blob/master/CHANGELOG.rst' [project.scripts] example-cli = 'example:cli' [project.gui-scripts] example-gui = 'example:gui' [project.entry-points.'something.custom'] example = 'example:custom' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/generated-files/example-script.py0000775000000000000000000000016714706721634024370 0ustar00rootroot#!python # SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT print('hello!') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/generated-files/example.c0000664000000000000000000000022614706721634022651 0ustar00rootroot// SPDX-FileCopyrightText: 2022 The meson-python developers // // SPDX-License-Identifier: MIT #include int main() { printf("hello!"); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/generated-files/executable_module.py0000775000000000000000000000025714706721634025121 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT def foo(): return 'bar' if __name__ == '__main__': print('foo?', foo()) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/generated-files/generate_version.py0000664000000000000000000000171014706721634024762 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT import argparse import os def write_version_info(path): # A real project would call something to generate this dummy_version = '1.0.0' dummy_hash = '013j2fiejqea' if os.environ.get('MESON_DIST_ROOT'): path = os.path.join(os.environ.get('MESON_DIST_ROOT'), path) with open(path, 'w') as file: file.write(f'__version__="{dummy_version}"\n') file.write( f'__git_version__="{dummy_hash}"\n' ) def main(): parser = argparse.ArgumentParser() parser.add_argument( '-o', '--outfile', type=str, help='Path to write version info to' ) args = parser.parse_args() if not args.outfile.endswith('.py'): raise ValueError( f'Output file must be a Python file. ' f'Got: {args.outfile} as filename instead' ) write_version_info(args.outfile) main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/generated-files/meson.build0000664000000000000000000000162514706721634023220 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT project('executable-bit', 'c', version: '1.0.0') fs = import('fs') py = import('python').find_installation() executable( 'example', 'example.c', install: true, ) install_data( 'example-script.py', rename: 'example-script', install_dir: get_option('bindir'), ) py.install_sources('executable_module.py') version_gen = files('generate_version.py') if fs.exists('_version_meson.py') py.install_sources('_version_meson.py') else custom_target( 'write_version_file', output: '_version_meson.py', command: [py, version_gen, '-o', '@OUTPUT@'], build_by_default: true, build_always_stale: true, install: true, install_dir: py.get_install_dir(pure: false), ) meson.add_dist_script(py, version_gen, '-o', '_version_meson.py') endif ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/generated-files/pyproject.toml0000664000000000000000000000024214706721634023764 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/imports-itself-during-build/meson.build0000664000000000000000000000053614706721634025526 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT project('imports-itself-during-build', 'c', version: '1.0.0') py = import('python').find_installation() py.install_sources('pure.py') py.extension_module( 'plat', 'plat.c', install: true, ) run_command(py, '-c', 'import pure', check: false) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/imports-itself-during-build/plat.c0000664000000000000000000000076514706721634024474 0ustar00rootroot// SPDX-FileCopyrightText: 2022 The meson-python developers // // SPDX-License-Identifier: MIT #include static PyObject* data(PyObject* self) { return PyUnicode_FromString("ABC"); } static PyMethodDef methods[] = { {"data", (PyCFunction)data, METH_NOARGS, NULL}, {NULL, NULL, 0, NULL}, }; static struct PyModuleDef module = { PyModuleDef_HEAD_INIT, "plat", NULL, -1, methods, }; PyMODINIT_FUNC PyInit_plat(void) { return PyModule_Create(&module); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/imports-itself-during-build/pure.py0000664000000000000000000000017114706721634024704 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT def foo(): return 'bar' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/imports-itself-during-build/pyproject.toml0000664000000000000000000000024214706721634026272 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/install-subdir/meson.build0000664000000000000000000000111214706721634023105 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT project('install-subdir', version: '1.0.0') py = import('python').find_installation() install_subdir( 'subdir', exclude_files: 'excluded.py', exclude_directories: 'excluded', install_dir: py.get_install_dir(pure: false), ) install_subdir( 'strip', strip_directory: true, exclude_files: 'excluded.py', install_dir: py.get_install_dir(pure: false) / 'test', ) install_subdir( 'nested', exclude_files: 'deep/excluded.py', install_dir: py.get_install_dir(pure: false), ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/install-subdir/nested/deep/deep.py0000664000000000000000000000013414706721634024434 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/install-subdir/nested/deep/excluded.py0000664000000000000000000000013414706721634025314 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/install-subdir/nested/nested.py0000664000000000000000000000013414706721634024064 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/install-subdir/pyproject.toml0000664000000000000000000000024214706721634023662 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/install-subdir/strip/excluded.py0000664000000000000000000000013414706721634024256 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/install-subdir/strip/module.py0000664000000000000000000000013414706721634023746 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/install-subdir/subdir/__init__.py0000664000000000000000000000013414706721634024347 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/install-subdir/subdir/excluded/__init__.py0000664000000000000000000000013414706721634026144 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/install-subdir/subdir/excluded.py0000664000000000000000000000013414706721634024405 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/install-subdir/subdir/test.py0000664000000000000000000000013414706721634023567 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/library/example.c0000664000000000000000000000026614706721634021263 0ustar00rootroot// SPDX-FileCopyrightText: 2021 The meson-python developers // // SPDX-License-Identifier: MIT #include int sum(int, int); int main() { printf("sum: %d", sum(1, 2)); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/library/examplelib.c0000664000000000000000000000021114706721634021740 0ustar00rootroot// SPDX-FileCopyrightText: 2021 The meson-python developers // // SPDX-License-Identifier: MIT int sum(int a, int b) { return a + b; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/library/examplelib.h0000664000000000000000000000016714706721634021757 0ustar00rootroot// SPDX-FileCopyrightText: 2022 The meson-python developers // // SPDX-License-Identifier: MIT int sum(int a, int b); ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/library/meson.build0000664000000000000000000000053514706721634021625 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT project('library', 'c', version: '1.0.0') install_headers('examplelib.h') example_lib = shared_library( 'example', 'examplelib.c', install: true, ) executable( 'example', 'example.c', link_with: example_lib, install: true, ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/library/pyproject.toml0000664000000000000000000000024214706721634022372 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/license-file/meson.build0000664000000000000000000000020714706721634022514 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT project('license-file', version: '1.0.0') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/license-file/pyproject.toml0000664000000000000000000000040514706721634023266 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] [project] name = 'license-file' version = '1.0.0' license = { file = 'something/LICENSE.custom' } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/license-file/something/LICENSE.custom0000664000000000000000000000000714706721634024663 0ustar00rootrootHello! ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/limited-api/meson.build0000664000000000000000000000060314706721634022353 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT project('limited-api', 'c', version: '1.0.0') py = import('python').find_installation(pure: false) py.extension_module( 'module', 'module.c', limited_api: '3.7', install: true, ) if get_option('extra') py.extension_module( 'extra', 'module.c', install: true, ) endif ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/limited-api/meson.options0000664000000000000000000000021414706721634022745 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT option('extra', type: 'boolean', value: false) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/limited-api/module.c0000664000000000000000000000110714706721634021642 0ustar00rootroot// SPDX-FileCopyrightText: 2023 The meson-python developers // // SPDX-License-Identifier: MIT #include static PyObject* add(PyObject *self, PyObject *args) { int a, b; if (!PyArg_ParseTuple(args, "ii", &a, &b)) return NULL; return PyLong_FromLong(a + b); } static PyMethodDef methods[] = { {"add", add, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL}, }; static struct PyModuleDef module = { PyModuleDef_HEAD_INIT, "plat", NULL, -1, methods, }; PyMODINIT_FUNC PyInit_module(void) { return PyModule_Create(&module); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/limited-api/pyproject.toml0000664000000000000000000000031214706721634023122 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] [tool.meson-python] limited-api = true ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/link-against-local-lib/examplemod.c0000664000000000000000000000126314706721634024532 0ustar00rootroot// SPDX-FileCopyrightText: 2022 The meson-python developers // // SPDX-License-Identifier: MIT #include #include "lib/examplelib.h" static PyObject* example_sum(PyObject* self, PyObject *args) { int a, b; if (!PyArg_ParseTuple(args, "ii", &a, &b)) { return NULL; } long result = sum(a, b); return PyLong_FromLong(result); } static PyMethodDef methods[] = { {"example_sum", (PyCFunction)example_sum, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL}, }; static struct PyModuleDef module = { PyModuleDef_HEAD_INIT, "example", NULL, -1, methods, }; PyMODINIT_FUNC PyInit_example(void) { return PyModule_Create(&module); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/link-against-local-lib/lib/examplelib.c0000664000000000000000000000021114706721634025257 0ustar00rootroot// SPDX-FileCopyrightText: 2022 The meson-python developers // // SPDX-License-Identifier: MIT int sum(int a, int b) { return a + b; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/link-against-local-lib/lib/examplelib.h0000664000000000000000000000016714706721634025276 0ustar00rootroot// SPDX-FileCopyrightText: 2022 The meson-python developers // // SPDX-License-Identifier: MIT int sum(int a, int b); ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/link-against-local-lib/lib/meson.build0000664000000000000000000000026314706721634025142 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT example_lib = shared_library( 'example', 'examplelib.c', install: true, ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/link-against-local-lib/meson.build0000664000000000000000000000054614706721634024400 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT project('link-against-local-lib', 'c', version: '1.0.0') subdir('lib') py = import('python').find_installation() py.extension_module( 'example', 'examplemod.c', link_with: example_lib, link_args: ['-Wl,-rpath,custom-rpath'], install: true, ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/link-against-local-lib/pyproject.toml0000664000000000000000000000024214706721634025143 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/long-path/meson.build0000664000000000000000000000033714706721634022052 0ustar00rootroot# SPDX-FileCopyrightText: 2024 The meson-python developers # # SPDX-License-Identifier: MIT project('very-long-project-name-that-makes-the-paths-within-the-sdist-exceed-100-characters-xxxxxxxxxxxxxxxxx', version: '1.0.0') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/long-path/pyproject.toml0000664000000000000000000000032614706721634022622 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] [project] name = 'long-path' dynamic = ['version'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/missing-dynamic-version/meson.build0000664000000000000000000000020014706721634024724 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT project('missing-dynamic-version') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/missing-dynamic-version/pyproject.toml0000664000000000000000000000034414706721634025507 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] [project] name = 'missing-dynamic-version' dynamic = ['version'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/missing-meson-version/meson.build0000664000000000000000000000017614706721634024435 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT project('missing-meson-version') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/missing-meson-version/pyproject.toml0000664000000000000000000000024214706721634025201 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/missing-version/meson.build0000664000000000000000000000021214706721634023305 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT project('missing-version', version: '1.0.0') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/missing-version/pyproject.toml0000664000000000000000000000030614706721634024063 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] [project] name = 'missing-version' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/module-types/file.py0000664000000000000000000000013414706721634021732 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/module-types/meson.build0000664000000000000000000000066614706721634022615 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT project('module-types', 'c', version: '1.0.0') py = import('python').find_installation() py.install_sources('file.py') py.install_sources( 'package' / '__init__.py', subdir: 'package', ) py.install_sources( 'namespace' / 'data.py', subdir: 'namespace', ) py.extension_module( 'native', 'native.c', install: true, ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/module-types/namespace/data.py0000664000000000000000000000013414706721634023660 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/module-types/native.c0000664000000000000000000000076614706721634022106 0ustar00rootroot// SPDX-FileCopyrightText: 2022 The meson-python developers // // SPDX-License-Identifier: MIT #include static PyObject* foo(PyObject* self) { return PyUnicode_FromString("bar"); } static PyMethodDef methods[] = { {"foo", (PyCFunction)foo, METH_NOARGS, NULL}, {NULL, NULL, 0, NULL}, }; static struct PyModuleDef module = { PyModuleDef_HEAD_INIT, "native", NULL, -1, methods, }; PyMODINIT_FUNC PyInit_native(void) { return PyModule_Create(&module); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/module-types/package/__init__.py0000664000000000000000000000013414706721634024145 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/module-types/pyproject.toml0000664000000000000000000000024214706721634023355 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/pure/meson.build0000664000000000000000000000031114706721634021124 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT project('pure', version: '1.0.0') py = import('python').find_installation() py.install_sources('pure.py') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/pure/pure.py0000664000000000000000000000017114706721634020313 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT def foo(): return 'bar' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/pure/pyproject.toml0000664000000000000000000000024214706721634021701 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/purelib-and-platlib/meson.build0000664000000000000000000000054114706721634024005 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT project('purelib-and-platlib', 'c', version: '1.0.0') py = import('python').find_installation() py.install_sources( 'pure.py', install_tag: 'purelib', ) py.extension_module( 'plat', 'plat.c', install: true, install_tag: 'platlib', ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/purelib-and-platlib/plat.c0000664000000000000000000000076214706721634022754 0ustar00rootroot// SPDX-FileCopyrightText: 2021 The meson-python developers // // SPDX-License-Identifier: MIT #include static PyObject* foo(PyObject* self) { return PyUnicode_FromString("bar"); } static PyMethodDef methods[] = { {"foo", (PyCFunction)foo, METH_NOARGS, NULL}, {NULL, NULL, 0, NULL}, }; static struct PyModuleDef module = { PyModuleDef_HEAD_INIT, "plat", NULL, -1, methods, }; PyMODINIT_FUNC PyInit_plat(void) { return PyModule_Create(&module); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/purelib-and-platlib/pure.py0000664000000000000000000000017114706721634023167 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT def foo(): return 'bar' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/purelib-and-platlib/pyproject.toml0000664000000000000000000000024214706721634024555 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/purelib-platlib-split/meson.build0000664000000000000000000000054214706721634024377 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT project('purelib-platlib-split', version: '1.0.0') py = import('python').find_installation() py.install_sources( 'pure.py', subdir: 'purelib-platlib-split', ) py.install_sources( 'plat.py', subdir: 'purelib-platlib-split', pure: false, ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/purelib-platlib-split/plat.py0000664000000000000000000000017114706721634023545 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT def foo(): return 'bar' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/purelib-platlib-split/pure.py0000664000000000000000000000017114706721634023560 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT def foo(): return 'bar' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/purelib-platlib-split/pyproject.toml0000664000000000000000000000024214706721634025146 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/scipy-like/meson.build0000664000000000000000000000111714706721634022227 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT project('scipy-like', 'c', 'cython', version: '2.3.4') # Work around Python bug on mingw-w64, fixed in Python 3.12 # https://github.com/python/cpython/pull/100137 # The same work-around is also included in Meson 1.1.0 and later. if meson.version().version_compare('< 1.1') if host_machine.system() == 'windows' and meson.get_compiler('c').get_id() == 'gcc' add_project_arguments('-DMS_WIN64=', language: 'c') endif endif py = import('python').find_installation() subdir('mypkg') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/scipy-like/mypkg/__init__.py0000664000000000000000000000017114706721634023324 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT def foo(): return 'bar' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/scipy-like/mypkg/cy_extmod.pyx0000664000000000000000000000025114706721634023747 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT from libc.math cimport sin cdef double f(double x): return sin(x * x) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/scipy-like/mypkg/extmod.c0000664000000000000000000000076414706721634022667 0ustar00rootroot// SPDX-FileCopyrightText: 2022 The meson-python developers // // SPDX-License-Identifier: MIT #include static PyObject* foo(PyObject* self) { return PyUnicode_FromString("bar"); } static PyMethodDef methods[] = { {"foo", (PyCFunction)foo, METH_NOARGS, NULL}, {NULL, NULL, 0, NULL}, }; static struct PyModuleDef module = { PyModuleDef_HEAD_INIT, "extmod", NULL, -1, methods, }; PyMODINIT_FUNC PyInit_plat(void) { return PyModule_Create(&module); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/scipy-like/mypkg/meson.build0000664000000000000000000000153014706721634023355 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT generate_version = custom_target( 'generate-config-file', install: true, build_always_stale: true, build_by_default: true, output: '__config__.py', input: '../tools/generate_config.py', command: [py, '@INPUT@', '@OUTPUT@'], install_dir: py.get_install_dir(pure: false) / 'mypkg', ) py.extension_module( 'extmod', 'extmod.c', install: true, subdir: 'mypkg', ) py.extension_module( 'cy_extmod', 'cy_extmod.pyx', install: true, subdir: 'mypkg', ) py.install_sources( '__init__.py', pure: false, subdir: 'mypkg', ) # Still broken, see gh-105 and https://github.com/mesonbuild/meson/issues/10639 install_subdir( 'submod', install_dir: py.get_install_dir(pure: false) / 'mypkg', ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/scipy-like/mypkg/submod/__init__.py0000664000000000000000000000013414706721634024614 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/scipy-like/mypkg/submod/unknown_filetype.npq0000664000000000000000000000027314706721634026627 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT # Unknown file types (e.g. `.npy` or `.npz` data files) can be present in # a Python package. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/scipy-like/pyproject.toml0000664000000000000000000000040214706721634022775 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] [project] name = "mypkg" description = "A typical Python package layout" dynamic = ['version'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/scipy-like/tools/generate_config.py0000664000000000000000000000033314706721634024715 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT import sys from pathlib import Path outfile = Path(sys.argv[1]) outfile.write_text('# Some build-time configuration data') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/simple/__init__.py0000664000000000000000000000020214706721634021410 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT from .test import data # noqa: F401 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/simple/data.txt0000664000000000000000000000000414706721634020751 0ustar00rootrootABC ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/simple/data.txt.license0000664000000000000000000000013414706721634022376 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/simple/meson.build0000664000000000000000000000037014706721634021447 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT project('simple', version: '1.0.0') py = import('python').find_installation() py.install_sources('__init__.py', 'test.py', 'data.txt', subdir: 'simple') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/simple/pyproject.toml0000664000000000000000000000024214706721634022217 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/simple/test.py0000664000000000000000000000034414706721634020637 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT import pathlib def data(): with pathlib.Path(__file__).parent.joinpath('data.txt').open() as f: return f.read().rstrip() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/subdirs/meson.build0000664000000000000000000000111214706721634021624 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT project('subdirs', version: '1.0.0') py = import('python').find_installation() # in Meson >= 0.64 this could be replace with a single # py.install_sources() with the 'preserve_path: true' argument. py.install_sources( 'subdirs/__init__.py', subdir: 'subdirs', ) py.install_sources( 'subdirs/a/__init__.py', subdir: 'subdirs/a', ) py.install_sources( 'subdirs/a/b/c.py', subdir: 'subdirs/a/b', ) py.install_sources( 'subdirs/b/c.py', subdir: 'subdirs/b', ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/subdirs/pyproject.toml0000664000000000000000000000024214706721634022401 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/subdirs/subdirs/__init__.py0000664000000000000000000000013414706721634023251 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/subdirs/subdirs/a/__init__.py0000664000000000000000000000013414706721634023471 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/subdirs/subdirs/a/b/c.py0000664000000000000000000000013414706721634022375 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/subdirs/subdirs/b/c.py0000664000000000000000000000013414706721634022155 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/subproject/meson.build0000664000000000000000000000035014706721634022334 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT project('subproject', version: '1.0.0') subproject('dep') py = import('python').find_installation() py.install_sources('subproject.py') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/subproject/pyproject.toml0000664000000000000000000000024214706721634023106 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/subproject/subproject.py0000664000000000000000000000013414706721634022724 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/subproject/subprojects/dep/dep.py0000664000000000000000000000013414706721634024427 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/subproject/subprojects/dep/meson.build0000664000000000000000000000026514706721634025454 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT project('dep') py = import('python').find_installation() py.install_sources('dep.py') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/unknown-user-args-meson-args/pyproject.toml0000664000000000000000000000031114706721634026401 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] [tool.meson-python.args] unknown = [] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/unknown-user-args-top-level/pyproject.toml0000664000000000000000000000030414706721634026237 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] [tool.meson-python] unknown = {} ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/unsupported-python-version/meson.build0000664000000000000000000000022514706721634025547 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT project('unsupported-python-version', version: '1.0.0') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/unsupported-python-version/pyproject.toml0000664000000000000000000000037714706721634026331 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] [project] name = 'unsupported-python-version' version = '1.2.3' requires-python = '==1.0.0' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/user-args/meson.build0000664000000000000000000000031614706721634022066 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT project('user-args', version: '1.0.0') py = import('python').find_installation() py.install_sources('pure.py') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/user-args/pure.py0000664000000000000000000000017114706721634021250 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT def foo(): return 'bar' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/user-args/pyproject.toml0000664000000000000000000000044614706721634022644 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] [tool.meson-python.args] dist = ['config-dist'] setup = ['config-setup'] compile = ['config-compile'] install = ['config-install'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/vendored-meson/meson.build0000664000000000000000000000044514706721634023106 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT project('vendored-meson', version: '1.0.0') py = import('python').find_installation() if not get_option('custom-meson-used') error('Expected option "custom-meson-used" was not specified') endif ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/vendored-meson/meson_options.txt0000664000000000000000000000023014706721634024371 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT option('custom-meson-used', type: 'boolean', value: false) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/vendored-meson/pyproject.toml0000664000000000000000000000032614706721634023656 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT [build-system] build-backend = 'mesonpy' requires = ['meson-python'] [tool.meson-python] meson = 'third-party/meson.py' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/packages/vendored-meson/third-party/meson.py0000664000000000000000000000042014706721634024677 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT import sys from mesonbuild import mesonmain if 'setup' in sys.argv: sys.argv.append('-Dcustom-meson-used=true') if __name__ == '__main__': sys.exit(mesonmain.main()) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/test_consistency.py0000664000000000000000000000135414706721634020213 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT import pathlib import sys if sys.version_info >= (3, 11): import tomllib else: import tomli as tomllib def test_pyproject_dependencies(): pyproject = pathlib.Path(__file__).parent.parent.joinpath('pyproject.toml') with open(pyproject, 'rb') as f: data = tomllib.load(f) build_dependencies = data['build-system']['requires'] project_dependencies = data['project']['dependencies'] # verify that all build dependencies are project dependencies assert not set(build_dependencies) - set(project_dependencies), \ 'pyproject.toml is inconsistent: not all "build-system.requires" are in "project.dependencies"' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/test_editable.py0000664000000000000000000003266714706721634017436 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT import io import os import pathlib import pkgutil import re import subprocess import sys from contextlib import redirect_stdout import pytest import mesonpy from mesonpy import _editable from .test_wheel import EXT_SUFFIX, NOGIL_BUILD def find_cython_version(): cython_version_str = subprocess.run(['cython', '--version'], check=True, stdout=subprocess.PIPE, text=True).stdout version_str = re.search(r'(\d{1,4}\.\d{1,4}\.?\d{0,4})', cython_version_str).group(0) return tuple(map(int, version_str.split('.'))) CYTHON_VERSION = find_cython_version() def test_walk(package_complex): entries = _editable.walk( os.fspath(package_complex / 'complex'), [os.path.normpath('more/meson.build'), os.path.normpath('more/baz.pyx')], [os.path.normpath('namespace')], ) assert {pathlib.Path(x) for x in entries} == { pathlib.Path('__init__.py'), pathlib.Path('more/__init__.py'), } def test_nodes_tree(): tree = _editable.Node() tree[('aa', 'bb', 'cc')] = 'path1' tree[('aa', 'dd')] = 'path2' assert tree['aa']['bb']['cc'] == 'path1' assert tree['aa']['dd'] == 'path2' assert tree[('aa', 'bb', 'cc')] == 'path1' assert tree.get(('aa', 'bb', 'cc')) == 'path1' assert tree['aa'] == tree[('aa', )] assert tree.get(('aa', 'gg')) is None assert tree[('aa', 'gg')] == _editable.Node() def test_collect(package_complex): root = os.fspath(package_complex) install_plan = { 'targets': { os.path.join(root, 'build', f'test{EXT_SUFFIX}'): { 'destination': os.path.join('{py_platlib}', 'complex', f'test{EXT_SUFFIX}'), 'tag': 'runtime'}, }, 'install_subdirs': { os.path.join(root, 'complex'): { 'destination': os.path.join('{py_platlib}', 'complex'), 'tag': None} } } tree = _editable.collect(install_plan) assert tree['complex']['__init__.py'] == os.path.join(root, 'complex', '__init__.py') assert tree['complex'][f'test{EXT_SUFFIX}'] == os.path.join(root, 'build', f'test{EXT_SUFFIX}') assert tree['complex']['more']['__init__.py'] == os.path.join(root, 'complex', 'more', '__init__.py') @pytest.mark.skipif(NOGIL_BUILD and CYTHON_VERSION < (3, 1, 0), reason='Cython version too old, no free-threaded CPython support') def test_mesonpy_meta_finder(package_complex, tmp_path): # build a package in a temporary directory project = mesonpy.Project(package_complex, tmp_path) # point the meta finder to the build directory finder = _editable.MesonpyMetaFinder('complex', {'complex'}, os.fspath(tmp_path), project._build_command, True) # check repr assert repr(finder) == f'MesonpyMetaFinder(\'complex\', {str(tmp_path)!r})' # verify that we can look up a pure module in the source directory spec = finder.find_spec('complex') assert spec.name == 'complex' assert isinstance(spec.loader, _editable.SourceFileLoader) assert spec.origin == os.fspath(package_complex / 'complex/__init__.py') # and an extension module in the build directory spec = finder.find_spec('complex.test') assert spec.name == 'complex.test' assert isinstance(spec.loader, _editable.ExtensionFileLoader) assert spec.origin == os.fspath(tmp_path / f'test{EXT_SUFFIX}') try: # install the finder in the meta path sys.meta_path.insert(0, finder) # verify that we can import the modules import complex assert complex.__spec__.origin == os.fspath(package_complex / 'complex/__init__.py') assert complex.__file__ == os.fspath(package_complex / 'complex/__init__.py') import complex.extension assert complex.extension.__spec__.origin == os.fspath(tmp_path / f'extension{EXT_SUFFIX}') assert complex.extension.answer() == 42 import complex.namespace.foo assert complex.namespace.foo.__spec__.origin == os.fspath(package_complex / 'complex/namespace/foo.py') assert complex.namespace.foo.foo() == 'foo' finally: # remove finder from the meta path del sys.meta_path[0] def test_mesonpy_traversable(): tree = _editable.Node() tree[('package', '__init__.py')] = '/tmp/src/package/__init__.py' tree[('package', 'src.py')] = '/tmp/src/package/src.py' tree[('package', 'data.txt')] = '/tmp/src/package/data.txt' tree[('package', 'nested', '__init__.py')] = '/tmp/src/package/nested/__init__.py' tree[('package', 'nested', 'some.py')] = '/tmp/src/package/nested/some.py' tree[('package', 'nested', 'generated.txt')] = '/tmp/build/generated.txt' traversable = _editable.MesonpyTraversable('package', tree['package']) assert {x.name for x in traversable.iterdir()} == {'__init__.py', 'src.py', 'data.txt', 'nested'} nested = traversable / 'nested' assert nested.is_dir() assert {x.name for x in nested.iterdir()} == {'__init__.py', 'some.py', 'generated.txt'} generated = traversable.joinpath('nested', 'generated.txt') assert isinstance(generated, pathlib.Path) assert generated == pathlib.Path('/tmp/build/generated.txt') bad = traversable / 'bad' assert not bad.is_file() assert not bad.is_dir() with pytest.raises(FileNotFoundError): bad.open() def test_resources(tmp_path): # build a package in a temporary directory package_path = pathlib.Path(__file__).parent / 'packages' / 'simple' project = mesonpy.Project(package_path, tmp_path) # point the meta finder to the build directory finder = _editable.MesonpyMetaFinder('simple', {'simple'}, os.fspath(tmp_path), project._build_command, True) # verify that we can look up resources spec = finder.find_spec('simple') reader = spec.loader.get_resource_reader('simple') traversable = reader.files() assert {x.name for x in traversable.iterdir()} == {'__init__.py', 'test.py', 'data.txt'} with traversable.joinpath('data.txt').open() as f: text = f.read().rstrip() assert text == 'ABC' @pytest.mark.skipif(sys.version_info < (3, 9), reason='importlib.resources not available') def test_importlib_resources(tmp_path): # build a package in a temporary directory package_path = pathlib.Path(__file__).parent / 'packages' / 'simple' project = mesonpy.Project(package_path, tmp_path) # point the meta finder to the build directory finder = _editable.MesonpyMetaFinder('simple', {'simple'}, os.fspath(tmp_path), project._build_command, True) try: # install the finder in the meta path sys.meta_path.insert(0, finder) # verify that we can import the modules import simple assert simple.__spec__.origin == os.fspath(package_path / '__init__.py') assert simple.__file__ == os.fspath(package_path / '__init__.py') assert simple.data() == 'ABC' # load resources via importlib import importlib.resources with importlib.resources.files(simple).joinpath('data.txt').open() as f: text = f.read().rstrip() assert text == 'ABC' assert importlib.resources.files(simple).joinpath('data.txt').read_text().rstrip() == 'ABC' finally: # remove finder from the meta path del sys.meta_path[0] def test_editable_install(venv, editable_simple): venv.pip('install', os.fspath(editable_simple)) assert venv.python('-c', 'import simple; print(simple.data())').strip() == 'ABC' def test_editble_reentrant(venv, editable_imports_itself_during_build): venv.pip('install', os.fspath(editable_imports_itself_during_build)) assert venv.python('-c', 'import plat; print(plat.data())').strip() == 'ABC' path = pathlib.Path(__file__).parent / 'packages' / 'imports-itself-during-build' / 'plat.c' code = path.read_text() try: # edit souce code path.write_text(code.replace('ABC', 'DEF')) # check that the module is rebuilt on import. the build proess # imports python code from the source directory, thus this # test also check that this import does not cause an rebuild # loop assert venv.python('-c', 'import plat; print(plat.data())').strip() == 'DEF' finally: path.write_text(code) @pytest.mark.skipif(NOGIL_BUILD and CYTHON_VERSION < (3, 1, 0), reason='Cython version too old, no free-threaded CPython support') def test_editable_pkgutils_walk_packages(package_complex, tmp_path): # build a package in a temporary directory project = mesonpy.Project(package_complex, tmp_path) finder = _editable.MesonpyMetaFinder('complex', {'complex'}, os.fspath(tmp_path), project._build_command, True) try: # install editable hooks sys.meta_path.insert(0, finder) sys.path_hooks.insert(0, finder._path_hook) import complex packages = {m.name for m in pkgutil.walk_packages(complex.__path__, complex.__name__ + '.')} assert packages == { 'complex.bar', 'complex.extension', 'complex.more', 'complex.more.baz', 'complex.more.move', 'complex.test', } from complex import namespace packages = {m.name for m in pkgutil.walk_packages(namespace.__path__, namespace.__name__ + '.')} assert packages == { 'complex.namespace.bar', 'complex.namespace.foo', } finally: # remove hooks del sys.meta_path[0] del sys.path_hooks[0] def test_custom_target_install_dir(package_custom_target_dir, tmp_path): project = mesonpy.Project(package_custom_target_dir, tmp_path) finder = _editable.MesonpyMetaFinder('package', {'package'}, os.fspath(tmp_path), project._build_command, True) try: sys.meta_path.insert(0, finder) import package.generated.one import package.generated.two # noqa: F401 finally: del sys.meta_path[0] @pytest.mark.parametrize('verbose', [False, True], ids=('', 'verbose')) @pytest.mark.parametrize('args', [[], ['-j1']], ids=('', '-Ccompile-args=-j1')) def test_editable_rebuild(package_purelib_and_platlib, tmp_path, verbose, args): with mesonpy._project({'builddir': os.fspath(tmp_path), 'compile-args': args}) as project: finder = _editable.MesonpyMetaFinder( project._metadata.name, {'plat', 'pure'}, os.fspath(tmp_path), project._build_command, verbose=verbose, ) try: # Install editable hooks sys.meta_path.insert(0, finder) # Import module and trigger rebuild. Importing any module in the # Python package triggers the build. Use the the pure Python one as # Cygwin is not happy when reloading an extension module. stdout = io.StringIO() with redirect_stdout(stdout): import pure assert not verbose or stdout.getvalue().startswith('meson-python: building ') # Reset state. del sys.modules['pure'] finder._rebuild.cache_clear() # Importing again should result in no output. stdout = io.StringIO() with redirect_stdout(stdout): import pure # noqa: F401 assert stdout.getvalue() == '' finally: del sys.meta_path[0] sys.modules.pop('pure', None) @pytest.mark.skipif(NOGIL_BUILD and CYTHON_VERSION < (3, 1, 0), reason='Cython version too old, no free-threaded CPython support') def test_editable_verbose(venv, package_complex, editable_complex, monkeypatch): monkeypatch.setenv('MESONPY_EDITABLE_VERBOSE', '1') venv.pip('install', os.fspath(editable_complex)) # Importing the module should not result in any output since the project has already been built assert venv.python('-c', 'import complex').strip() == '' # Touch a compiled source file and make sure that the build info is output on import package_complex.joinpath('test.pyx').touch() output = venv.python('-c', 'import complex').strip() assert output.startswith('meson-python: building complex: ') # Another import without file changes should not show any output assert venv.python('-c', 'import complex') == '' @pytest.mark.parametrize('verbose', [False, True], ids=('', 'verbose')) def test_editable_rebuild_error(package_purelib_and_platlib, tmp_path, verbose): with mesonpy._project({'builddir': os.fspath(tmp_path)}) as project: finder = _editable.MesonpyMetaFinder( project._metadata.name, {'plat', 'pure'}, os.fspath(tmp_path), project._build_command, verbose=verbose, ) path = package_purelib_and_platlib / 'plat.c' code = path.read_text() try: # Install editable hooks sys.meta_path.insert(0, finder) # Insert invalid code in the extension module source code path.write_text('return') # Import module and trigger rebuild: the build fails and ImportErrror is raised stdout = io.StringIO() with redirect_stdout(stdout): with pytest.raises(ImportError, match='re-building the purelib-and-platlib '): import plat # noqa: F401 assert not verbose or stdout.getvalue().startswith('meson-python: building ') finally: del sys.meta_path[0] sys.modules.pop('pure', None) path.write_text(code) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/test_examples.py0000664000000000000000000000177114706721634017473 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT import pathlib import subprocess import sys import pytest import mesonpy from mesonpy._util import chdir examples_dir = pathlib.Path(__file__).parent.parent / 'docs' / 'examples' def test_spam(venv, tmp_path): """Test that the wheel for the example builds, installs, and imports.""" with chdir(examples_dir / 'spam'): if sys.version_info < (3, 8): # The test project requires Python >= 3.8. with pytest.raises(SystemExit): mesonpy.build_wheel(tmp_path) else: wheel = mesonpy.build_wheel(tmp_path) subprocess.run( [venv.executable, '-m', 'pip', 'install', tmp_path / wheel], check=True) output = subprocess.run( [venv.executable, '-c', 'import spam; print(spam.add(1, 2))'], check=True, stdout=subprocess.PIPE).stdout assert int(output) == 3 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/test_metadata.py0000664000000000000000000000366114706721634017435 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT import pathlib import re import packaging.version import pyproject_metadata import pytest from mesonpy import Metadata def test_package_name(): name = 'package.Test' metadata = Metadata(name='package.Test', version=packaging.version.Version('0.0.1')) assert metadata.name == name assert metadata.canonical_name == 'package-test' def test_package_name_from_pyproject(): name = 'package.Test' pyproject = {'project': { 'name': 'package.Test', 'version': '0.0.1', }} metadata = Metadata.from_pyproject(pyproject, pathlib.Path()) assert metadata.name == name assert metadata.canonical_name == 'package-test' def test_package_name_invalid(): with pytest.raises(pyproject_metadata.ConfigurationError, match='Invalid project name'): Metadata(name='.test', version=packaging.version.Version('0.0.1')) def test_unsupported_dynamic(): pyproject = {'project': { 'name': 'unsupported-dynamic', 'version': '0.0.1', 'dynamic': ['dependencies'], }} with pytest.raises(pyproject_metadata.ConfigurationError, match='Unsupported dynamic fields: "dependencies"'): Metadata.from_pyproject(pyproject, pathlib.Path()) def test_missing_version(package_missing_version): pyproject = {'project': { 'name': 'missing-version', }} match = '|'.join(( re.escape('Required "project.version" field is missing'), # pyproject-metatadata 0.8.0 and later re.escape('Field "project.version" missing and "version" not specified in "project.dynamic"'), # pyproject-metatadata 0.9.0 and later re.escape('Field "project.version" missing and \'version\' not specified in "project.dynamic"'), )) with pytest.raises(pyproject_metadata.ConfigurationError, match=match): Metadata.from_pyproject(pyproject, pathlib.Path()) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/test_options.py0000664000000000000000000000244614706721634017350 0ustar00rootroot# SPDX-FileCopyrightText: 2023 The meson-python developers # # SPDX-License-Identifier: MIT import os import subprocess import pytest import mesonpy ninja_ver_str = subprocess.run(['ninja', '--version'], check=True, stdout=subprocess.PIPE, text=True).stdout NINJA_VERSION = tuple(map(int, ninja_ver_str.split('.')[:3])) # Ninja 1.9 does not support the soruce^ syntax to specify a target. @pytest.mark.skipif(NINJA_VERSION < (1, 10), reason='Ninja version too old') @pytest.mark.parametrize( ('args', 'expected'), [ ([], True), (['-Dbuildtype=release'], True), (['-Dbuildtype=debug'], False), ], ids=['', '-Dbuildtype=release', '-Dbuildtype=debug'], ) def test_ndebug(package_purelib_and_platlib, tmp_path, args, expected): with mesonpy._project({'setup-args': args}) as project: command = subprocess.run( # Ask ninja what is the command that would be used to # compile a C source file (the trailing ^ is used to # specify the target that is the first output of the rule # containing the specified source file). ['ninja', '-C', os.fspath(project._build_dir), '-t', 'commands', '../plat.c^'], stdout=subprocess.PIPE, check=True).stdout assert (b'-DNDEBUG' in command) == expected ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/test_output.py0000664000000000000000000000177514706721634017221 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT import pytest import mesonpy @pytest.mark.parametrize(('tty', 'env', 'colors'), [ (False, {}, False), (True, {}, True), (False, {'NO_COLOR': ''}, False), (True, {'NO_COLOR': ''}, False), (False, {'FORCE_COLOR': ''}, True), (True, {'FORCE_COLOR': ''}, True), (True, {'FORCE_COLOR': '', 'NO_COLOR': ''}, False), (True, {'TERM': ''}, True), (True, {'TERM': 'dumb'}, False), ]) def test_use_ansi_escapes(mocker, monkeypatch, tty, env, colors): mocker.patch('sys.stdout.isatty', return_value=tty) mocker.patch('mesonpy._util.setup_windows_console', return_value=True) monkeypatch.delenv('NO_COLOR', raising=False) monkeypatch.delenv('FORCE_COLOR', raising=False) for key, value in env.items(): monkeypatch.setenv(key, value) # Clear caching by functools.lru_cache(). mesonpy._use_ansi_escapes.cache_clear() assert mesonpy._use_ansi_escapes() == colors ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/test_pep517.py0000664000000000000000000001274314706721634016677 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT import os import re import shutil import subprocess import sys import textwrap from typing import List import packaging.requirements import pytest import mesonpy @pytest.mark.parametrize('system_patchelf', ['patchelf', None], ids=['patchelf', 'nopatchelf']) @pytest.mark.parametrize('ninja', [None, '1.8.1', '1.8.3'], ids=['noninja', 'oldninja', 'newninja']) def test_get_requires_for_build_wheel(monkeypatch, package_pure, system_patchelf, ninja): # the NINJA environment variable affects the ninja executable lookup and breaks the test monkeypatch.delenv('NINJA', raising=False) def which(prog: str) -> bool: if prog == 'patchelf': return system_patchelf if prog == 'ninja': return ninja and 'ninja' if prog in ('ninja-build', 'samu'): return None # smoke check for the future if we add another usage raise AssertionError(f'Called with {prog}, tests not expecting that usage') subprocess_run = subprocess.run def run(cmd: List[str], *args: object, **kwargs: object) -> subprocess.CompletedProcess: if cmd == ['ninja', '--version']: return subprocess.CompletedProcess(cmd, 0, f'{ninja}\n', '') return subprocess_run(cmd, *args, **kwargs) monkeypatch.setattr(shutil, 'which', which) monkeypatch.setattr(subprocess, 'run', run) expected = set() if ninja is None or mesonpy._parse_version_string(ninja) < (1, 8, 2): expected.add('ninja') if system_patchelf is None and sys.platform.startswith('linux'): expected.add('patchelf') requirements = mesonpy.get_requires_for_build_wheel() # Check that the requirement strings are in the correct format. names = {packaging.requirements.Requirement(x).name for x in requirements} assert names == expected def test_invalid_config_settings(capsys, package_pure, tmp_path_session): for method in mesonpy.build_sdist, mesonpy.build_wheel: with pytest.raises(SystemExit): method(tmp_path_session, {'invalid': ()}) out, err = capsys.readouterr() assert 'Unknown option "invalid"' in out def test_invalid_config_settings_suggest(capsys, package_pure, tmp_path_session): for method in mesonpy.build_sdist, mesonpy.build_wheel: with pytest.raises(SystemExit): method(tmp_path_session, {'setup_args': ()}) out, err = capsys.readouterr() assert 'Unknown option "setup_args". Did you mean "setup-args" or "dist-args"?' in out def test_validate_config_settings_invalid(): with pytest.raises(mesonpy.ConfigError, match='Unknown option "invalid"'): mesonpy._validate_config_settings({'invalid': ()}) def test_validate_config_settings_repeated(): with pytest.raises(mesonpy.ConfigError, match='Only one value for "builddir" can be specified'): mesonpy._validate_config_settings({'builddir': ['one', 'two']}) def test_validate_config_settings_str(): config = mesonpy._validate_config_settings({'setup-args': '-Dfoo=true'}) assert config['setup-args'] == ['-Dfoo=true'] def test_validate_config_settings_list(): config = mesonpy._validate_config_settings({'setup-args': ['-Done=1', '-Dtwo=2']}) assert config['setup-args'] == ['-Done=1', '-Dtwo=2'] def test_validate_config_settings_tuple(): config = mesonpy._validate_config_settings({'setup-args': ('-Done=1', '-Dtwo=2')}) assert config['setup-args'] == ['-Done=1', '-Dtwo=2'] @pytest.mark.parametrize('meson', [None, 'meson']) def test_get_meson_command(monkeypatch, meson): # The MESON environment variable affects the meson executable lookup and breaks the test. monkeypatch.delenv('MESON', raising=False) assert mesonpy._get_meson_command(meson) == ['meson'] def test_get_meson_command_bad_path(monkeypatch): # The MESON environment variable affects the meson executable lookup and breaks the test. monkeypatch.delenv('MESON', raising=False) with pytest.raises(mesonpy.ConfigError, match=re.escape('meson executable "bad" not found')): mesonpy._get_meson_command('bad') def test_get_meson_command_bad_python_path(monkeypatch): # The MESON environment variable affects the meson executable lookup and breaks the test. monkeypatch.delenv('MESON', raising=False) with pytest.raises(mesonpy.ConfigError, match=re.escape('Could not find the specified meson: "bad-python-path.py"')): mesonpy._get_meson_command('bad-python-path.py') def test_get_meson_command_wrong_version(monkeypatch, tmp_path): # The MESON environment variable affects the meson executable lookup and breaks the test. monkeypatch.delenv('MESON', raising=False) meson = tmp_path / 'meson.py' meson.write_text(textwrap.dedent(''' print('0.0.1') ''')) with pytest.raises(mesonpy.ConfigError, match=r'Could not find meson version [0-9\.]+ or newer, found 0\.0\.1\.'): mesonpy._get_meson_command(os.fspath(meson)) def test_get_meson_command_error(monkeypatch, tmp_path): # The MESON environment variable affects the meson executable lookup and breaks the test. monkeypatch.delenv('MESON', raising=False) meson = tmp_path / 'meson.py' meson.write_text(textwrap.dedent(''' import sys print('Just testing', file=sys.stderr) sys.exit(1) ''')) with pytest.raises(mesonpy.ConfigError, match=re.escape('Could not execute meson: Just testing')): mesonpy._get_meson_command(os.fspath(meson)) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/test_project.py0000664000000000000000000002103314706721634017314 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT import ast import os import shutil import sys import textwrap if sys.version_info < (3, 11): import tomli as tomllib else: import tomllib import pyproject_metadata import pytest import mesonpy from .conftest import in_git_repo_context, package_dir def test_unsupported_python_version(package_unsupported_python_version): with pytest.raises(mesonpy.MesonBuilderError, match='The package requires Python version ==1.0.0'): with mesonpy._project(): pass def test_missing_meson_version(package_missing_meson_version): with pytest.raises(pyproject_metadata.ConfigurationError, match='Section "project" missing in pyproject.toml'): with mesonpy._project(): pass def test_missing_dynamic_version(package_missing_dynamic_version): with pytest.raises(pyproject_metadata.ConfigurationError, match='Field "version" declared as dynamic but'): with mesonpy._project(): pass def test_user_args(package_user_args, tmp_path, monkeypatch): project_run = mesonpy.Project._run cmds = [] args = [] def wrapper(self, cmd): # intercept and filter out test arguments and forward the call if cmd[:2] == ['meson', 'compile']: # when using meson compile instead of ninja directly, the # arguments needs to be unmarshalled from the form used to # pass them to the --ninja-args option assert cmd[-1].startswith('--ninja-args=') cmds.append(cmd[:2]) args.append(ast.literal_eval(cmd[-1].split('=')[1])) elif cmd[:1] == ['meson']: cmds.append(cmd[:2]) args.append(cmd[2:]) else: # direct ninja invocation cmds.append([os.path.basename(cmd[0])]) args.append(cmd[1:]) return project_run(self, [x for x in cmd if not x.startswith(('config-', 'cli-', '--ninja-args'))]) monkeypatch.setattr(mesonpy.Project, '_run', wrapper) config_settings = { 'dist-args': ('cli-dist',), 'setup-args': ('cli-setup',), 'compile-args': ('cli-compile',), 'install-args': ('cli-install',), } with in_git_repo_context(): mesonpy.build_sdist(tmp_path, config_settings) mesonpy.build_wheel(tmp_path, config_settings) # check that the right commands are executed, namely that 'meson # compile' is used on Windows rather than a 'ninja' direct # invocation. assert cmds == [ # sdist: calls to 'meson setup' and 'meson dist' ['meson', 'setup'], ['meson', 'dist'], # wheel: calls to 'meson setup', 'meson compile', and 'meson install' ['meson', 'setup'], ['meson', 'compile'] if sys.platform == 'win32' else ['ninja'], ] # check that the user options are passed to the invoked commands expected = [ # sdist: calls to 'meson setup' and 'meson dist' ['config-setup', 'cli-setup'], ['config-dist', 'cli-dist'], # wheel: calls to 'meson setup', 'meson compile', and 'meson install' ['config-setup', 'cli-setup'], ['config-compile', 'cli-compile'], ['config-install', 'cli-install'], ] for expected_args, cmd_args in zip(expected, args): for arg in expected_args: assert arg in cmd_args @pytest.mark.parametrize('package', ('top-level', 'meson-args')) def test_unknown_user_args(package, tmp_path_session): with pytest.raises(mesonpy.ConfigError): mesonpy.Project(package_dir / f'unknown-user-args-{package}', tmp_path_session) def test_install_tags(package_purelib_and_platlib, tmp_path_session): project = mesonpy.Project( package_purelib_and_platlib, tmp_path_session, meson_args={ 'install': ['--tags', 'purelib'], } ) assert 'platlib' not in project._manifest def test_validate_pyproject_config_one(): pyproject_config = tomllib.loads(textwrap.dedent(''' [tool.meson-python.args] setup = ['-Dfoo=true'] ''')) conf = mesonpy._validate_pyproject_config(pyproject_config) assert conf['args'] == {'setup': ['-Dfoo=true']} def test_validate_pyproject_config_all(): pyproject_config = tomllib.loads(textwrap.dedent(''' [tool.meson-python.args] setup = ['-Dfoo=true'] dist = [] compile = ['-j4'] install = ['--tags=python'] ''')) conf = mesonpy._validate_pyproject_config(pyproject_config) assert conf['args'] == { 'setup': ['-Dfoo=true'], 'dist': [], 'compile': ['-j4'], 'install': ['--tags=python']} def test_validate_pyproject_config_unknown(): pyproject_config = tomllib.loads(textwrap.dedent(''' [tool.meson-python.args] invalid = true ''')) with pytest.raises(mesonpy.ConfigError, match='Unknown configuration entry "tool.meson-python.args.invalid"'): mesonpy._validate_pyproject_config(pyproject_config) def test_validate_pyproject_config_empty(): pyproject_config = tomllib.loads(textwrap.dedent('')) config = mesonpy._validate_pyproject_config(pyproject_config) assert config == {} @pytest.mark.skipif( sys.version_info < (3, 8), reason="unittest.mock doesn't support the required APIs for this test", ) def test_invalid_build_dir(package_pure, tmp_path, mocker): meson = mocker.spy(mesonpy.Project, '_run') # configure the project project = mesonpy.Project(package_pure, tmp_path) assert len(meson.call_args_list) == 1 assert meson.call_args_list[0].args[1][1] == 'setup' assert '--reconfigure' not in meson.call_args_list[0].args[1] project.build() meson.reset_mock() # subsequent builds with the same build directory result in a setup --reconfigure project = mesonpy.Project(package_pure, tmp_path) assert len(meson.call_args_list) == 1 assert meson.call_args_list[0].args[1][1] == 'setup' assert '--reconfigure' in meson.call_args_list[0].args[1] project.build() meson.reset_mock() # corrupting the build direcory setup is run again tmp_path.joinpath('meson-private/coredata.dat').unlink() project = mesonpy.Project(package_pure, tmp_path) assert len(meson.call_args_list) == 1 assert meson.call_args_list[0].args[1][1] == 'setup' assert '--reconfigure' not in meson.call_args_list[0].args[1] project.build() meson.reset_mock() # removing the build directory things should still work shutil.rmtree(tmp_path) project = mesonpy.Project(package_pure, tmp_path) assert len(meson.call_args_list) == 1 assert meson.call_args_list[0].args[1][1] == 'setup' assert '--reconfigure' not in meson.call_args_list[0].args[1] project.build() @pytest.mark.skipif(not os.getenv('CI') or sys.platform != 'win32', reason='Requires MSVC') def test_compiler(venv, package_detect_compiler, tmp_path): # Check that things are setup properly to use the MSVC compiler on # Windows. This effectively means running the compilation step # with 'meson compile' instead of 'ninja' on Windows. Run this # test only on CI where we know that MSVC is available. wheel = mesonpy.build_wheel(tmp_path, {'setup-args': ['--vsenv']}) venv.pip('install', os.fspath(tmp_path / wheel)) compiler = venv.python('-c', 'import detect_compiler; print(detect_compiler.compiler())').strip() assert compiler == 'msvc' @pytest.mark.skipif(sys.platform != 'darwin', reason='macOS specific test') @pytest.mark.parametrize('archflags', [ '-arch x86_64', '-arch arm64', '-arch arm64 -arch arm64', ]) def test_archflags_envvar_parsing(package_purelib_and_platlib, monkeypatch, archflags): try: monkeypatch.setenv('ARCHFLAGS', archflags) arch = archflags.split()[-1] with mesonpy._project(): assert mesonpy._tags.Tag().platform.endswith(arch) finally: # revert environment variable setting done by the in-process build os.environ.pop('_PYTHON_HOST_PLATFORM', None) @pytest.mark.skipif(sys.platform != 'darwin', reason='macOS specific test') @pytest.mark.parametrize('archflags', [ '-arch arm64 -arch x86_64', '-arch arm64 -DFOO=1', ]) def test_archflags_envvar_parsing_invalid(package_purelib_and_platlib, monkeypatch, archflags): try: monkeypatch.setenv('ARCHFLAGS', archflags) with pytest.raises(mesonpy.ConfigError): with mesonpy._project(): pass finally: # revert environment variable setting done by the in-process build os.environ.pop('_PYTHON_HOST_PLATFORM', None) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/test_sdist.py0000664000000000000000000001677314706721634017013 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT import pathlib import stat import sys import tarfile import textwrap import time from itertools import chain import pytest import mesonpy from .conftest import in_git_repo_context, metadata def test_no_pep621(sdist_library): with tarfile.open(sdist_library, 'r:gz') as sdist: sdist_pkg_info = sdist.extractfile('library-1.0.0/PKG-INFO').read() assert metadata(sdist_pkg_info) == metadata(textwrap.dedent('''\ Metadata-Version: 2.1 Name: library Version: 1.0.0 ''')) def test_pep621(sdist_full_metadata): with tarfile.open(sdist_full_metadata, 'r:gz') as sdist: sdist_pkg_info = sdist.extractfile('full_metadata-1.2.3/PKG-INFO').read() meta = metadata(sdist_pkg_info) # pyproject-metadata prior to 0.8.0 uses spaces to join keywords meta['keywords'] = list(chain(*(v.split(' ') for v in meta['keywords']))) # pyproject-metadata prior to 0.9.0 strips trailing newlines meta['license'] = meta['license'].rstrip() # pyproject-metadata 0.9.0 and later does not emit Home-Page meta.pop('home_page', None) # nor normalizes Project-URL keys meta['project_urls'] = {k.lower(): v for k, v in meta['project_urls'].items()} assert meta == metadata(textwrap.dedent('''\ Metadata-Version: 2.1 Name: full-metadata Version: 1.2.3 Summary: Some package with all of the PEP 621 metadata Keywords: full,metadata Author: Jane Doe Author-Email: Unknown Maintainer-Email: Jane Doe License: some license Classifier: Development Status :: 4 - Beta Classifier: Programming Language :: Python Project-URL: homepage, https://example.com Project-URL: documentation, https://readthedocs.org Project-URL: repository, https://github.com/mesonbuild/meson-python Project-URL: changelog, https://github.com/mesonbuild/meson-python/blob/master/CHANGELOG.rst Requires-Python: >=3.7 Requires-Dist: a Requires-Dist: b>1 Requires-Dist: c>2; os_name != "nt" Requires-Dist: d<3; extra == "test" Requires-Dist: e[all]; extra == "test" Provides-Extra: test Description-Content-Type: text/markdown # full-metadata An example package with all of the PEP 621 metadata! ''')) def test_dynamic_version(sdist_dynamic_version): with tarfile.open(sdist_dynamic_version, 'r:gz') as sdist: sdist_pkg_info = sdist.extractfile('dynamic_version-1.0.0/PKG-INFO').read() assert metadata(sdist_pkg_info) == metadata(textwrap.dedent('''\ Metadata-Version: 2.1 Name: dynamic-version Version: 1.0.0 ''')) def test_contents(sdist_library): with tarfile.open(sdist_library, 'r:gz') as sdist: names = {member.name for member in sdist.getmembers()} mtimes = {member.mtime for member in sdist.getmembers()} assert names == { 'library-1.0.0/example.c', 'library-1.0.0/examplelib.c', 'library-1.0.0/examplelib.h', 'library-1.0.0/meson.build', 'library-1.0.0/pyproject.toml', 'library-1.0.0/PKG-INFO', } # All the archive members have a valid mtime. assert 0 not in mtimes def test_contents_subdirs(sdist_subdirs): with tarfile.open(sdist_subdirs, 'r:gz') as sdist: names = {member.name for member in sdist.getmembers()} mtimes = {member.mtime for member in sdist.getmembers()} assert names == { 'subdirs-1.0.0/PKG-INFO', 'subdirs-1.0.0/meson.build', 'subdirs-1.0.0/pyproject.toml', 'subdirs-1.0.0/subdirs/__init__.py', 'subdirs-1.0.0/subdirs/a/__init__.py', 'subdirs-1.0.0/subdirs/a/b/c.py', 'subdirs-1.0.0/subdirs/b/c.py', } # All the archive members have a valid mtime. assert 0 not in mtimes def test_contents_unstaged(package_pure, tmp_path): new = textwrap.dedent(''' def bar(): return 'foo' ''').strip() old = pathlib.Path('pure.py').read_text() with in_git_repo_context(): try: pathlib.Path('pure.py').write_text(new) pathlib.Path('other.py').touch() sdist_path = mesonpy.build_sdist(tmp_path) finally: pathlib.Path('pure.py').write_text(old) pathlib.Path('other.py').unlink() with tarfile.open(tmp_path / sdist_path, 'r:gz') as sdist: names = {member.name for member in sdist.getmembers()} mtimes = {member.mtime for member in sdist.getmembers()} data = sdist.extractfile('pure-1.0.0/pure.py').read().replace(b'\r\n', b'\n') # Verify that uncommitted changes are not included in the sdist. assert names == { 'pure-1.0.0/PKG-INFO', 'pure-1.0.0/meson.build', 'pure-1.0.0/pure.py', 'pure-1.0.0/pyproject.toml', } assert data == old.encode() # All the archive members have a valid mtime. assert 0 not in mtimes @pytest.mark.skipif(sys.platform in {'win32', 'cygwin'}, reason='Platform does not support executable bit') def test_executable_bit(sdist_executable_bit): expected = { 'executable_bit-1.0.0/PKG-INFO': False, 'executable_bit-1.0.0/example-script.py': True, 'executable_bit-1.0.0/example.c': False, 'executable_bit-1.0.0/executable_module.py': True, 'executable_bit-1.0.0/meson.build': False, 'executable_bit-1.0.0/pyproject.toml': False, } with tarfile.open(sdist_executable_bit, 'r:gz') as sdist: for member in sdist.getmembers(): assert bool(member.mode & stat.S_IXUSR) == expected[member.name] def test_generated_files(sdist_generated_files): with tarfile.open(sdist_generated_files, 'r:gz') as sdist: names = {member.name for member in sdist.getmembers()} mtimes = {member.mtime for member in sdist.getmembers()} assert names == { 'executable_bit-1.0.0/PKG-INFO', 'executable_bit-1.0.0/example-script.py', 'executable_bit-1.0.0/example.c', 'executable_bit-1.0.0/executable_module.py', 'executable_bit-1.0.0/meson.build', 'executable_bit-1.0.0/pyproject.toml', 'executable_bit-1.0.0/_version_meson.py', 'executable_bit-1.0.0/generate_version.py', } # All the archive members have a valid mtime. assert 0 not in mtimes def test_long_path(sdist_long_path): # See https://github.com/mesonbuild/meson-python/pull/587#pullrequestreview-2020891328 # and https://github.com/mesonbuild/meson-python/pull/587#issuecomment-2075973593 with tarfile.open(sdist_long_path, 'r:gz') as sdist: names = {member.name for member in sdist.getmembers()} assert names == { 'long_path-1.0.0/PKG-INFO', 'long_path-1.0.0/meson.build', 'long_path-1.0.0/pyproject.toml' } def test_reproducible(package_pure, tmp_path): with in_git_repo_context(): t1 = time.time() sdist_path_a = mesonpy.build_sdist(tmp_path / 'a') t2 = time.time() # Ensure that the two sdists are build at least one second apart. time.sleep(max(t1 + 1.0 - t2, 0.0)) sdist_path_b = mesonpy.build_sdist(tmp_path / 'b') assert sdist_path_a == sdist_path_b assert tmp_path.joinpath('a', sdist_path_a).read_bytes() == tmp_path.joinpath('b', sdist_path_b).read_bytes() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/test_tags.py0000664000000000000000000001023514706721634016606 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT import importlib.machinery import os import pathlib import platform import sys import sysconfig from collections import defaultdict import packaging.tags import pytest import mesonpy import mesonpy._tags from .conftest import adjust_packaging_platform_tag # Test against the wheel tag generated by packaging module. tag = next(packaging.tags.sys_tags()) ABI = tag.abi INTERPRETER = tag.interpreter PLATFORM = adjust_packaging_platform_tag(tag.platform) def get_abi3_suffix(): for suffix in importlib.machinery.EXTENSION_SUFFIXES: if '.abi3' in suffix: # Unix return suffix elif suffix == '.pyd': # Windows return suffix SUFFIX = sysconfig.get_config_var('EXT_SUFFIX') ABI3SUFFIX = get_abi3_suffix() def test_wheel_tag(): assert str(mesonpy._tags.Tag()) == f'{INTERPRETER}-{ABI}-{PLATFORM}' assert str(mesonpy._tags.Tag(abi='abi3')) == f'{INTERPRETER}-abi3-{PLATFORM}' @pytest.mark.skipif(sys.platform != 'darwin', reason='macOS specific test') def test_macos_platform_tag(monkeypatch): for minor in range(9, 16): monkeypatch.setenv('MACOSX_DEPLOYMENT_TARGET', f'10.{minor}') version = (10, minor) if platform.mac_ver()[2] != 'arm64' else (11, 0) assert next(packaging.tags.mac_platforms(version)) == mesonpy._tags.get_platform_tag() for major in range(11, 20): for minor in range(3): monkeypatch.setenv('MACOSX_DEPLOYMENT_TARGET', f'{major}.{minor}') assert next(packaging.tags.mac_platforms((major, minor))) == mesonpy._tags.get_platform_tag() @pytest.mark.skipif(sys.platform != 'darwin', reason='macOS specific test') def test_macos_platform_tag_arm64(monkeypatch): monkeypatch.setenv('_PYTHON_HOST_PLATFORM', 'macosx-12.0-arm64') # Verify that the minimum platform ABI version on arm64 is 11.0. monkeypatch.setenv('MACOSX_DEPLOYMENT_TARGET', '10.12') assert mesonpy._tags.get_platform_tag() == 'macosx_11_0_arm64' monkeypatch.setenv('MACOSX_DEPLOYMENT_TARGET', '12.34') assert mesonpy._tags.get_platform_tag() == 'macosx_12_0_arm64' @pytest.mark.skipif(sys.platform != 'darwin', reason='macOS specific test') def test_python_host_platform(monkeypatch): monkeypatch.setenv('_PYTHON_HOST_PLATFORM', 'macosx-12.0-arm64') assert mesonpy._tags.get_platform_tag().endswith('arm64') monkeypatch.setenv('_PYTHON_HOST_PLATFORM', 'macosx-11.1-x86_64') assert mesonpy._tags.get_platform_tag().endswith('x86_64') def wheel_builder_test_factory(content, pure=True, limited_api=False): manifest = defaultdict(list) manifest.update({key: [(pathlib.Path(x), os.path.join('build', x)) for x in value] for key, value in content.items()}) return mesonpy._WheelBuilder(None, manifest, limited_api) def test_tag_empty_wheel(): builder = wheel_builder_test_factory({}) assert str(builder.tag) == 'py3-none-any' def test_tag_purelib_wheel(): builder = wheel_builder_test_factory({ 'purelib': ['pure.py'], }) assert str(builder.tag) == 'py3-none-any' def test_tag_platlib_wheel(): builder = wheel_builder_test_factory({ 'platlib': [f'extension{SUFFIX}'], }) assert str(builder.tag) == f'{INTERPRETER}-{ABI}-{PLATFORM}' def test_tag_stable_abi(): builder = wheel_builder_test_factory({ 'platlib': [f'extension{ABI3SUFFIX}'], }, limited_api=True) # PyPy does not support the stable ABI. abi = 'abi3' if '__pypy__' not in sys.builtin_module_names else ABI assert str(builder.tag) == f'{INTERPRETER}-{abi}-{PLATFORM}' @pytest.mark.xfail(sys.version_info < (3, 8) and sys.platform == 'win32', reason='Extension modules suffix without ABI tags') @pytest.mark.xfail('__pypy__' in sys.builtin_module_names, reason='PyPy does not support the stable ABI') def test_tag_mixed_abi(): builder = wheel_builder_test_factory({ 'platlib': [f'extension{ABI3SUFFIX}', f'another{SUFFIX}'], }, pure=False, limited_api=True) with pytest.raises(mesonpy.BuildError, match='The package declares compatibility with Python limited API but '): assert str(builder.tag) == f'{INTERPRETER}-abi3-{PLATFORM}' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/test_wheel.py0000664000000000000000000003322114706721634016754 0ustar00rootroot# SPDX-FileCopyrightText: 2021 The meson-python developers # # SPDX-License-Identifier: MIT import os import re import shutil import stat import subprocess import sys import sysconfig import textwrap import packaging.tags import pytest import wheel.wheelfile import mesonpy from .conftest import adjust_packaging_platform_tag _meson_ver_str = subprocess.run(['meson', '--version'], check=True, stdout=subprocess.PIPE, text=True).stdout MESON_VERSION = tuple(map(int, _meson_ver_str.split('.')[:3])) EXT_SUFFIX = sysconfig.get_config_var('EXT_SUFFIX') if sys.version_info <= (3, 8, 7): if MESON_VERSION >= (0, 99): # Fixed in Meson 1.0, see https://github.com/mesonbuild/meson/pull/10961. from distutils.sysconfig import get_config_var EXT_SUFFIX = get_config_var('EXT_SUFFIX') if sys.platform in {'win32', 'cygwin'}: EXT_IMP_SUFFIX = re.sub(r'.(pyd|dll)$', '.lib' if shutil.which('cl.exe') else '.dll.a', EXT_SUFFIX) LIB_SUFFIX = { 'cygwin': '.dll', 'darwin': '.dylib', 'win32': '.dll', }.get(sys.platform, '.so') NOGIL_BUILD = bool(sysconfig.get_config_var('Py_GIL_DISABLED')) # Test against the wheel tag generated by packaging module. tag = next(packaging.tags.sys_tags()) ABI = tag.abi INTERPRETER = tag.interpreter PLATFORM = adjust_packaging_platform_tag(tag.platform) def wheel_contents(artifact): # Sometimes directories have entries, sometimes not, so we filter them out. return { entry for entry in artifact.namelist() if not entry.endswith('/') } def test_scipy_like(wheel_scipy_like): # This test is meant to exercise features commonly needed by a regular # Python package for scientific computing or data science: # - C and Cython extensions, # - including generated code, # - using `install_subdir`, # - packaging data files with extensions not known to Meson artifact = wheel.wheelfile.WheelFile(wheel_scipy_like) expecting = { 'mypkg-2.3.4.dist-info/METADATA', 'mypkg-2.3.4.dist-info/RECORD', 'mypkg-2.3.4.dist-info/WHEEL', 'mypkg/__init__.py', 'mypkg/__config__.py', f'mypkg/extmod{EXT_SUFFIX}', f'mypkg/cy_extmod{EXT_SUFFIX}', 'mypkg/submod/__init__.py', 'mypkg/submod/unknown_filetype.npq', } if sys.platform in {'win32', 'cygwin'}: # Currently Meson is installing .dll.a (import libraries) next # to .pyd extension modules. Those are very small, so it's not # a major issue - just sloppy. Ensure we don't fail on those. expecting.update({ f'mypkg/extmod{EXT_IMP_SUFFIX}', f'mypkg/cy_extmod{EXT_IMP_SUFFIX}', }) assert wheel_contents(artifact) == expecting name = artifact.parsed_filename assert name.group('pyver') == INTERPRETER assert name.group('abi') == ABI assert name.group('plat') == PLATFORM def test_purelib_and_platlib(wheel_purelib_and_platlib): artifact = wheel.wheelfile.WheelFile(wheel_purelib_and_platlib) expecting = { f'plat{EXT_SUFFIX}', 'purelib_and_platlib-1.0.0.data/purelib/pure.py', 'purelib_and_platlib-1.0.0.dist-info/METADATA', 'purelib_and_platlib-1.0.0.dist-info/RECORD', 'purelib_and_platlib-1.0.0.dist-info/WHEEL', } if sys.platform in {'win32', 'cygwin'}: # Currently Meson is installing .dll.a (import libraries) next # to .pyd extension modules. Those are very small, so it's not # a major issue - just sloppy. Ensure we don't fail on those. expecting.update({ f'plat{EXT_IMP_SUFFIX}' }) assert wheel_contents(artifact) == expecting def test_pure(wheel_pure): artifact = wheel.wheelfile.WheelFile(wheel_pure) assert wheel_contents(artifact) == { 'pure-1.0.0.dist-info/METADATA', 'pure-1.0.0.dist-info/RECORD', 'pure-1.0.0.dist-info/WHEEL', 'pure.py', } def test_configure_data(wheel_configure_data): artifact = wheel.wheelfile.WheelFile(wheel_configure_data) assert wheel_contents(artifact) == { 'configure_data.py', 'configure_data-1.0.0.dist-info/METADATA', 'configure_data-1.0.0.dist-info/RECORD', 'configure_data-1.0.0.dist-info/WHEEL', } def test_contents_license_file(wheel_license_file): artifact = wheel.wheelfile.WheelFile(wheel_license_file) assert artifact.read('license_file-1.0.0.dist-info/LICENSE.custom').rstrip() == b'Hello!' @pytest.mark.skipif(sys.platform not in {'linux', 'darwin'}, reason='Not supported on this platform') def test_contents(package_library, wheel_library): artifact = wheel.wheelfile.WheelFile(wheel_library) assert wheel_contents(artifact) == { f'.library.mesonpy.libs/libexample{LIB_SUFFIX}', 'library-1.0.0.data/headers/examplelib.h', 'library-1.0.0.data/scripts/example', 'library-1.0.0.dist-info/METADATA', 'library-1.0.0.dist-info/RECORD', 'library-1.0.0.dist-info/WHEEL', } @pytest.mark.skipif(sys.platform not in {'linux', 'darwin'}, reason='Not supported on this platform') def test_local_lib(venv, wheel_link_against_local_lib): venv.pip('install', wheel_link_against_local_lib) output = venv.python('-c', 'import example; print(example.example_sum(1, 2))') assert int(output) == 3 @pytest.mark.skipif(sys.platform not in {'linux', 'darwin'}, reason='Not supported on this platform') def test_rpath(wheel_link_against_local_lib, tmp_path): artifact = wheel.wheelfile.WheelFile(wheel_link_against_local_lib) artifact.extractall(tmp_path) origin = {'linux': '$ORIGIN', 'darwin': '@loader_path'}[sys.platform] expected = {f'{origin}/.link_against_local_lib.mesonpy.libs', 'custom-rpath',} rpath = set(mesonpy._rpath._get_rpath(tmp_path / f'example{EXT_SUFFIX}')) # Verify that rpath is a superset of the expected one: linking to # the Python runtime may require additional rpath entries. assert rpath >= expected @pytest.mark.skipif(sys.platform not in {'linux', 'darwin'}, reason='Not supported on this platform') def test_uneeded_rpath(wheel_purelib_and_platlib, tmp_path): artifact = wheel.wheelfile.WheelFile(wheel_purelib_and_platlib) artifact.extractall(tmp_path) origin = {'linux': '$ORIGIN', 'darwin': '@loader_path'}[sys.platform] rpath = mesonpy._rpath._get_rpath(tmp_path / f'plat{EXT_SUFFIX}') for path in rpath: assert origin not in path @pytest.mark.skipif(sys.platform not in {'linux', 'darwin'}, reason='Not supported on this platform') def test_executable_bit(wheel_executable_bit): artifact = wheel.wheelfile.WheelFile(wheel_executable_bit) executable_files = { 'executable_bit-1.0.0.data/purelib/executable_module.py', 'executable_bit-1.0.0.data/scripts/example', 'executable_bit-1.0.0.data/scripts/example-script', } for info in artifact.infolist(): mode = (info.external_attr >> 16) & 0o777 assert bool(mode & stat.S_IXUSR) == (info.filename in executable_files) def test_detect_wheel_tag_module(wheel_purelib_and_platlib): name = wheel.wheelfile.WheelFile(wheel_purelib_and_platlib).parsed_filename assert name.group('pyver') == INTERPRETER assert name.group('abi') == ABI assert name.group('plat') == PLATFORM def test_detect_wheel_tag_script(wheel_executable): name = wheel.wheelfile.WheelFile(wheel_executable).parsed_filename assert name.group('pyver') == 'py3' assert name.group('abi') == 'none' assert name.group('plat') == PLATFORM def test_entrypoints(wheel_full_metadata): artifact = wheel.wheelfile.WheelFile(wheel_full_metadata) with artifact.open('full_metadata-1.2.3.dist-info/entry_points.txt') as f: assert f.read().decode().strip() == textwrap.dedent(''' [something.custom] example = example:custom [console_scripts] example-cli = example:cli [gui_scripts] example-gui = example:gui ''').strip() def test_top_level_modules(package_module_types): with mesonpy._project() as project: builder = mesonpy._EditableWheelBuilder(project._metadata, project._manifest, project._limited_api) assert set(builder._top_level_modules) == { 'file', 'package', 'namespace', 'native', } def test_purelib_platlib_split(package_purelib_platlib_split, tmp_path): with pytest.raises(mesonpy.BuildError, match='The purelib-platlib-split package is split'): with mesonpy._project() as project: project.wheel(tmp_path) @pytest.mark.skipif(sys.platform != 'darwin', reason='macOS specific test') @pytest.mark.parametrize('arch', ['x86_64', 'arm64']) def test_archflags_envvar(package_purelib_and_platlib, monkeypatch, tmp_path, arch): try: monkeypatch.setenv('ARCHFLAGS', f'-arch {arch}') filename = mesonpy.build_wheel(tmp_path) name = wheel.wheelfile.WheelFile(tmp_path / filename).parsed_filename assert name.group('plat').endswith(arch) finally: # revert environment variable setting done by the in-process build os.environ.pop('_PYTHON_HOST_PLATFORM', None) def test_subprojects(package_subproject, tmp_path): filename = mesonpy.build_wheel(tmp_path) artifact = wheel.wheelfile.WheelFile(tmp_path / filename) assert wheel_contents(artifact) == { 'subproject-1.0.0.dist-info/METADATA', 'subproject-1.0.0.dist-info/RECORD', 'subproject-1.0.0.dist-info/WHEEL', 'subproject.py', 'dep.py', } # Requires Meson 1.2.0, see https://github.com/mesonbuild/meson/pull/11909. @pytest.mark.skipif(MESON_VERSION < (1, 1, 99), reason='Meson version too old') @pytest.mark.parametrize(('arg'), ['--skip-subprojects', '--skip-subprojects=dep']) def test_skip_subprojects(package_subproject, tmp_path, arg): filename = mesonpy.build_wheel(tmp_path, {'install-args': [arg]}) artifact = wheel.wheelfile.WheelFile(tmp_path / filename) assert wheel_contents(artifact) == { 'subproject-1.0.0.dist-info/METADATA', 'subproject-1.0.0.dist-info/RECORD', 'subproject-1.0.0.dist-info/WHEEL', 'subproject.py', } # Requires Meson 1.3.0, see https://github.com/mesonbuild/meson/pull/11745. @pytest.mark.skipif(MESON_VERSION < (1, 2, 99), reason='Meson version too old') @pytest.mark.skipif(NOGIL_BUILD, reason='Free-threaded CPython does not support the limited API') @pytest.mark.xfail('__pypy__' in sys.builtin_module_names, reason='PyPy does not support the abi3 platform tag for wheels') def test_limited_api(wheel_limited_api): artifact = wheel.wheelfile.WheelFile(wheel_limited_api) name = artifact.parsed_filename assert name.group('pyver') == INTERPRETER assert name.group('abi') == 'abi3' assert name.group('plat') == PLATFORM # Requires Meson 1.3.0, see https://github.com/mesonbuild/meson/pull/11745. @pytest.mark.skipif(MESON_VERSION < (1, 2, 99), reason='Meson version too old') @pytest.mark.skipif(NOGIL_BUILD, reason='Free-threaded CPython does not support the limited API') @pytest.mark.xfail('__pypy__' in sys.builtin_module_names, reason='PyPy does not use special modules suffix for stable ABI') def test_limited_api_bad(package_limited_api, tmp_path): with pytest.raises(mesonpy.BuildError, match='The package declares compatibility with Python limited API but '): with mesonpy._project({'setup-args': ['-Dextra=true']}) as project: project.wheel(tmp_path) # Requires Meson 1.3.0, see https://github.com/mesonbuild/meson/pull/11745. @pytest.mark.skipif(MESON_VERSION < (1, 2, 99), reason='Meson version too old') def test_limited_api_disabled(package_limited_api, tmp_path): filename = mesonpy.build_wheel(tmp_path, {'setup-args': ['-Dpython.allow_limited_api=false']}) artifact = wheel.wheelfile.WheelFile(tmp_path / filename) name = artifact.parsed_filename assert name.group('pyver') == INTERPRETER assert name.group('abi') == ABI assert name.group('plat') == PLATFORM def test_install_subdir(wheel_install_subdir): artifact = wheel.wheelfile.WheelFile(wheel_install_subdir) # Handling of the exclude_files and exclude_directories requires # Meson 1.1.0, see https://github.com/mesonbuild/meson/pull/11432. # Run the test anyway to ensure that meson-python can produce a # wheel also for older versions of Meson. if MESON_VERSION >= (1, 1, 99): assert wheel_contents(artifact) == { 'install_subdir-1.0.0.dist-info/METADATA', 'install_subdir-1.0.0.dist-info/RECORD', 'install_subdir-1.0.0.dist-info/WHEEL', 'subdir/__init__.py', 'subdir/test.py', 'test/module.py', 'nested/deep/deep.py', 'nested/nested.py', } def test_vendored_meson(wheel_vendored_meson): # This test will error if the vendored meson.py wrapper script in # the test package isn't used. pass def test_encoding(package_encoding, tmp_path): with mesonpy._project() as project: wheelname = project.wheel(tmp_path) artifact = wheel.wheelfile.WheelFile(tmp_path / wheelname) assert wheel_contents(artifact) == { 'encoding-1.0.0.dist-info/METADATA', 'encoding-1.0.0.dist-info/RECORD', 'encoding-1.0.0.dist-info/WHEEL', 'テスト.py', } def test_custom_target_install_dir(package_custom_target_dir, tmp_path): filename = mesonpy.build_wheel(tmp_path) artifact = wheel.wheelfile.WheelFile(tmp_path / filename) assert wheel_contents(artifact) == { 'custom_target_dir-1.0.0.dist-info/METADATA', 'custom_target_dir-1.0.0.dist-info/RECORD', 'custom_target_dir-1.0.0.dist-info/WHEEL', 'package/generated/one.py', 'package/generated/two.py', } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/tests/test_wheelfile.py0000664000000000000000000000350314706721634017614 0ustar00rootroot# SPDX-FileCopyrightText: 2022 The meson-python developers # # SPDX-License-Identifier: MIT import contextlib import time import zipfile import wheel.wheelfile import mesonpy._wheelfile def test_basic(tmp_path): path = tmp_path / 'test-1.0-py3-any-none.whl' bar = tmp_path / 'bar' bar.write_bytes(b'bar') with mesonpy._wheelfile.WheelFile(path, 'w') as w: assert w.name == 'test' assert w.version == '1.0' w.writestr('foo', b'test') w.write(bar, 'bar') with contextlib.closing(wheel.wheelfile.WheelFile(path, 'r')) as w: assert set(w.namelist()) == {'foo', 'bar', 'test-1.0.dist-info/RECORD'} with w.open('foo') as foo: assert foo.read() == b'test' with w.open('bar') as bar: assert bar.read() == b'bar' def test_source_date_epoch(tmp_path, monkeypatch): path = tmp_path / 'test-1.0-py3-any-none.whl' epoch = 1668871912 # The ZIP file format timestamps have 2 seconds resolution. assert epoch % 2 == 0 monkeypatch.setenv('SOURCE_DATE_EPOCH', str(epoch)) bar = tmp_path / 'bar' bar.write_bytes(b'bar') with mesonpy._wheelfile.WheelFile(path, 'w') as w: w.writestr('foo', b'test') w.write(bar, 'bar') with wheel.wheelfile.WheelFile(path, 'r') as w: for entry in w.infolist(): assert entry.date_time == time.gmtime(epoch)[:6] def test_compression(tmp_path): # create a wheel and test that everything is compressed path = tmp_path / 'test-1.0-py3-any-none.whl' bar = tmp_path / 'bar' bar.write_bytes(b'bar') with mesonpy._wheelfile.WheelFile(path, 'w') as w: w.writestr('foo', b'test') w.write(bar, 'bar') with zipfile.ZipFile(path, 'r') as w: for entry in w.infolist(): assert entry.compress_type == zipfile.ZIP_DEFLATED ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1729864604.0 meson_python-0.17.1/PKG-INFO0000644000000000000000000001000214706721634014160 0ustar00rootrootMetadata-Version: 2.1 Name: meson-python Version: 0.17.1 Summary: Meson Python build backend (PEP 517) Keywords: meson,build,backend,pep517,package Maintainer-Email: Ralf Gommers , Daniele Nicolodi , Henry Schreiner , Thomas Li <47963215+lithomas1@users.noreply.github.com> License: Copyright © 2022 the meson-python contributors Copyright © 2021 Quansight Labs and Filipe Laíns Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Classifier: Development Status :: 5 - Production/Stable Classifier: Programming Language :: Python Classifier: Topic :: Software Development :: Build Tools Project-URL: homepage, https://github.com/mesonbuild/meson-python Project-URL: repository, https://github.com/mesonbuild/meson-python Project-URL: documentation, https://mesonbuild.com/meson-python/ Project-URL: changelog, https://mesonbuild.com/meson-python/changelog.html Requires-Python: >=3.7 Requires-Dist: meson>=0.63.3; python_version < "3.12" Requires-Dist: meson>=1.2.3; python_version >= "3.12" Requires-Dist: packaging>=19.0 Requires-Dist: pyproject-metadata>=0.7.1 Requires-Dist: tomli>=1.0.0; python_version < "3.11" Provides-Extra: test Requires-Dist: build; extra == "test" Requires-Dist: packaging>=23.1; extra == "test" Requires-Dist: pytest>=6.0; extra == "test" Requires-Dist: pytest-cov[toml]; extra == "test" Requires-Dist: pytest-mock; extra == "test" Requires-Dist: cython>=3.0.3; extra == "test" Requires-Dist: wheel; extra == "test" Requires-Dist: typing-extensions>=3.7.4; python_version < "3.11" and extra == "test" Provides-Extra: docs Requires-Dist: furo>=2023.5.20; extra == "docs" Requires-Dist: sphinx~=6.2; extra == "docs" Requires-Dist: sphinx-copybutton>=0.5.0; extra == "docs" Requires-Dist: sphinx-design>=0.1.0; extra == "docs" Requires-Dist: sphinxext-opengraph>=0.7.0; extra == "docs" Description-Content-Type: text/x-rst .. SPDX-FileCopyrightText: 2021 The meson-python developers .. SPDX-License-Identifier: MIT meson-python ============ ``meson-python`` is a Python build back-end built on top of the Meson__ build system. It enables using Meson for the configuration and build steps of Python packages. Meson is an open source build system meant to be both extremely fast, and, even more importantly, as user friendly as possible. ``meson-python`` is best suited for building Python packages containing extension modules implemented in languages such as C, C++, Cython, Fortran, Pythran, or Rust. Consult the documentation__ for more details. Questions regarding the use of ``meson-python`` can be directed to the discussions__ space on GitHub. Bug reports and feature requests can be filed as GitHub issues__. __ https://mesonbuild.com/ __ https://mesonbuild.com/meson-python/ __ https://github.com/mesonbuild/meson-python/discussions/ __ https://github.com/mesonbuild/meson-python/issues/