From 34f02effb970385b4350f6d2d5beb4e6252890d0 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Tue, 21 Oct 2025 23:24:56 -0400 Subject: [PATCH 01/11] CI: drop Pyston from test matrix - meson-python commit 2320a4b0d06cecebd59aa80b52e1bc974ce02a83 --- .github/workflows/ci.yml | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 56b529c..72ef648 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -303,38 +303,6 @@ jobs: 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: needs: [build] runs-on: macos-latest From d9968cfff5431fd08d08cf978caeba2b50aa60f1 Mon Sep 17 00:00:00 2001 From: Ralf Gommers Date: Sun, 27 Oct 2024 18:40:55 +0100 Subject: [PATCH 02/11] TST: add test package with internal shared libraries, installed in site-packages - meson-python commit 1f45c2157d97cd7e469f64f4f8d669435c8f7be8 --- .../packages/sharedlib-in-package/meson.build | 9 ++++ .../sharedlib-in-package/mypkg/__init__.py | 52 +++++++++++++++++++ .../sharedlib-in-package/mypkg/_examplemod.c | 51 ++++++++++++++++++ .../sharedlib-in-package/mypkg/examplelib.c | 9 ++++ .../sharedlib-in-package/mypkg/examplelib.h | 7 +++ .../sharedlib-in-package/mypkg/meson.build | 41 +++++++++++++++ .../mypkg/sub/examplelib2.c | 9 ++++ .../mypkg/sub/examplelib2.h | 7 +++ .../mypkg/sub/meson.build | 16 ++++++ .../mypkg/sub/mypkg_dll.h | 23 ++++++++ .../sharedlib-in-package/pyproject.toml | 16 ++++++ tests/test_wheel.py | 8 +++ 12 files changed, 248 insertions(+) create mode 100644 tests/packages/sharedlib-in-package/meson.build create mode 100644 tests/packages/sharedlib-in-package/mypkg/__init__.py create mode 100644 tests/packages/sharedlib-in-package/mypkg/_examplemod.c create mode 100644 tests/packages/sharedlib-in-package/mypkg/examplelib.c create mode 100644 tests/packages/sharedlib-in-package/mypkg/examplelib.h create mode 100644 tests/packages/sharedlib-in-package/mypkg/meson.build create mode 100644 tests/packages/sharedlib-in-package/mypkg/sub/examplelib2.c create mode 100644 tests/packages/sharedlib-in-package/mypkg/sub/examplelib2.h create mode 100644 tests/packages/sharedlib-in-package/mypkg/sub/meson.build create mode 100644 tests/packages/sharedlib-in-package/mypkg/sub/mypkg_dll.h create mode 100644 tests/packages/sharedlib-in-package/pyproject.toml diff --git a/tests/packages/sharedlib-in-package/meson.build b/tests/packages/sharedlib-in-package/meson.build new file mode 100644 index 0000000..71921cf --- /dev/null +++ b/tests/packages/sharedlib-in-package/meson.build @@ -0,0 +1,9 @@ +# SPDX-FileCopyrightText: 2022 The meson-python developers +# +# SPDX-License-Identifier: MIT + +project('sharedlib-in-package', 'c', version: '1.0.0') + +py = import('python').find_installation(pure: false) + +subdir('mypkg') diff --git a/tests/packages/sharedlib-in-package/mypkg/__init__.py b/tests/packages/sharedlib-in-package/mypkg/__init__.py new file mode 100644 index 0000000..f312675 --- /dev/null +++ b/tests/packages/sharedlib-in-package/mypkg/__init__.py @@ -0,0 +1,52 @@ +# SPDX-FileCopyrightText: 2024 The meson-python developers +# +# SPDX-License-Identifier: MIT + +import os +import sys + + +# start-literalinclude +def _append_to_sharedlib_load_path(): + """ + Ensure the shared libraries in this package can be loaded on Windows. + + Windows lacks a concept equivalent to RPATH: Python extension modules + cannot find DLLs installed outside the DLL search path. This function + ensures that the location of the shared libraries distributed inside this + Python package is in the DLL search path of the process. + + The Windows DLL search path includes the object depending on it is located: + the DLL search path needs to be augmented only when the Python extension + modules and the DLLs they require are installed in separate directories. + Cygwin does not have the same default library search path: all locations + where the shared libraries are installed need to be added to the search + path. + + This function is very similar to the snippet inserted into the main + ``__init__.py`` of a package by ``delvewheel`` when it vendors external + shared libraries. + + .. note:: + + `os.add_dll_directory` is only available for Python 3.8 and later, and + in the Conda ``python`` packages it works as advertised only for + version 3.10 and later. For older Python versions, pre-loading the DLLs + with `ctypes.WinDLL` may be preferred. + """ + basedir = os.path.dirname(__file__) + subdir = os.path.join(basedir, "sub") + if os.name == "nt": + os.add_dll_directory(subdir) + elif sys.platform == "cygwin": + os.environ["PATH"] = os.pathsep.join((os.environ["PATH"], basedir, subdir)) + + +_append_to_sharedlib_load_path() +# end-literalinclude + + +from ._example import example_prod, example_sum # noqa: E402 + + +__all__ = ["example_prod", "example_sum"] diff --git a/tests/packages/sharedlib-in-package/mypkg/_examplemod.c b/tests/packages/sharedlib-in-package/mypkg/_examplemod.c new file mode 100644 index 0000000..080e03c --- /dev/null +++ b/tests/packages/sharedlib-in-package/mypkg/_examplemod.c @@ -0,0 +1,51 @@ +// SPDX-FileCopyrightText: 2022 The meson-python developers +// +// SPDX-License-Identifier: MIT + +#include + +#include "examplelib.h" +#include "examplelib2.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 PyObject* example_prod(PyObject* self, PyObject *args) +{ + int a, b; + if (!PyArg_ParseTuple(args, "ii", &a, &b)) { + return NULL; + } + + long result = prod(a, b); + + return PyLong_FromLong(result); +} + +static PyMethodDef methods[] = { + {"example_prod", (PyCFunction)example_prod, METH_VARARGS, NULL}, + {"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); +} diff --git a/tests/packages/sharedlib-in-package/mypkg/examplelib.c b/tests/packages/sharedlib-in-package/mypkg/examplelib.c new file mode 100644 index 0000000..f486bd7 --- /dev/null +++ b/tests/packages/sharedlib-in-package/mypkg/examplelib.c @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2022 The meson-python developers +// +// SPDX-License-Identifier: MIT + +#include "sub/mypkg_dll.h" + +MYPKG_DLL int sum(int a, int b) { + return a + b; +} diff --git a/tests/packages/sharedlib-in-package/mypkg/examplelib.h b/tests/packages/sharedlib-in-package/mypkg/examplelib.h new file mode 100644 index 0000000..c09f4f7 --- /dev/null +++ b/tests/packages/sharedlib-in-package/mypkg/examplelib.h @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2022 The meson-python developers +// +// SPDX-License-Identifier: MIT + +#include "sub/mypkg_dll.h" + +MYPKG_DLL int sum(int a, int b); diff --git a/tests/packages/sharedlib-in-package/mypkg/meson.build b/tests/packages/sharedlib-in-package/mypkg/meson.build new file mode 100644 index 0000000..75904be --- /dev/null +++ b/tests/packages/sharedlib-in-package/mypkg/meson.build @@ -0,0 +1,41 @@ +# SPDX-FileCopyrightText: 2022 The meson-python developers +# +# SPDX-License-Identifier: MIT + +if meson.get_compiler('c').get_id() in ['msvc', 'clang-cl', 'intel-cl'] + export_dll_args = ['-DMYPKG_DLL_EXPORTS'] + import_dll_args = ['-DMYPKG_DLL_IMPORTS'] +else + export_dll_args = [] + import_dll_args = [] +endif + +example_lib = shared_library( + 'examplelib', + 'examplelib.c', + c_args: export_dll_args, + install: true, + install_dir: py.get_install_dir() / 'mypkg', +) + +example_lib_dep = declare_dependency( + compile_args: import_dll_args, + link_with: example_lib, +) + +subdir('sub') + +py.extension_module( + '_example', + '_examplemod.c', + dependencies: [example_lib_dep, example_lib2_dep], + include_directories: 'sub', + install: true, + subdir: 'mypkg', + install_rpath: '$ORIGIN', +) + +py.install_sources( + ['__init__.py'], + subdir: 'mypkg', +) diff --git a/tests/packages/sharedlib-in-package/mypkg/sub/examplelib2.c b/tests/packages/sharedlib-in-package/mypkg/sub/examplelib2.c new file mode 100644 index 0000000..12f5b87 --- /dev/null +++ b/tests/packages/sharedlib-in-package/mypkg/sub/examplelib2.c @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2022 The meson-python developers +// +// SPDX-License-Identifier: MIT + +#include "mypkg_dll.h" + +MYPKG_DLL int prod(int a, int b) { + return a * b; +} diff --git a/tests/packages/sharedlib-in-package/mypkg/sub/examplelib2.h b/tests/packages/sharedlib-in-package/mypkg/sub/examplelib2.h new file mode 100644 index 0000000..64b6a90 --- /dev/null +++ b/tests/packages/sharedlib-in-package/mypkg/sub/examplelib2.h @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2022 The meson-python developers +// +// SPDX-License-Identifier: MIT + +#include "mypkg_dll.h" + +MYPKG_DLL int prod(int a, int b); diff --git a/tests/packages/sharedlib-in-package/mypkg/sub/meson.build b/tests/packages/sharedlib-in-package/mypkg/sub/meson.build new file mode 100644 index 0000000..7a1978d --- /dev/null +++ b/tests/packages/sharedlib-in-package/mypkg/sub/meson.build @@ -0,0 +1,16 @@ +# SPDX-FileCopyrightText: 2022 The meson-python developers +# +# SPDX-License-Identifier: MIT + +example_lib2 = shared_library( + 'examplelib2', + 'examplelib2.c', + c_args: export_dll_args, + install: true, + install_dir: py.get_install_dir() / 'mypkg/sub', +) + +example_lib2_dep = declare_dependency( + compile_args: import_dll_args, + link_with: example_lib2, +) diff --git a/tests/packages/sharedlib-in-package/mypkg/sub/mypkg_dll.h b/tests/packages/sharedlib-in-package/mypkg/sub/mypkg_dll.h new file mode 100644 index 0000000..8460e6c --- /dev/null +++ b/tests/packages/sharedlib-in-package/mypkg/sub/mypkg_dll.h @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2022 The meson-python developers +// +// SPDX-License-Identifier: MIT + +#pragma once + +// MYPKG_DLL +// inspired by https://github.com/abseil/abseil-cpp/blob/20240116.2/absl/base/config.h#L736-L753 +// and https://github.com/scipy/scipy/blob/9ded83b51099eee745418ccbb30196db96c81f3f/scipy/_build_utils/src/scipy_dll.h +// +// When building the `examplelib` DLL, this macro expands to `__declspec(dllexport)` +// so we can annotate symbols appropriately as being exported. When used in +// headers consuming a DLL, this macro expands to `__declspec(dllimport)` so +// that consumers know the symbol is defined inside the DLL. In all other cases, +// the macro expands to nothing. +// Note: MYPKG_DLL_{EX,IM}PORTS are set in mypkg/meson.build +#if defined(MYPKG_DLL_EXPORTS) + #define MYPKG_DLL __declspec(dllexport) +#elif defined(MYPKG_DLL_IMPORTS) + #define MYPKG_DLL __declspec(dllimport) +#else + #define MYPKG_DLL +#endif diff --git a/tests/packages/sharedlib-in-package/pyproject.toml b/tests/packages/sharedlib-in-package/pyproject.toml new file mode 100644 index 0000000..81565f8 --- /dev/null +++ b/tests/packages/sharedlib-in-package/pyproject.toml @@ -0,0 +1,16 @@ +# SPDX-FileCopyrightText: 2022 The meson-python developers +# +# SPDX-License-Identifier: MIT + +[build-system] +build-backend = "hatchling.build" +requires = ["hatch-meson"] + +[project] +dynamic = ["name", "version"] + +[tool.hatch.version] +source = "meson" + +[tool.hatch.build.hooks.meson] +[tool.hatch.metadata.hooks.meson] diff --git a/tests/test_wheel.py b/tests/test_wheel.py index 326bda4..fb36c36 100644 --- a/tests/test_wheel.py +++ b/tests/test_wheel.py @@ -177,6 +177,14 @@ def test_configure_data(wheel_configure_data): # assert int(output) == 3 +def test_sharedlib_in_package(venv, wheel_sharedlib_in_package): + venv.pip("install", wheel_sharedlib_in_package) + output = venv.python("-c", "import mypkg; print(mypkg.example_sum(2, 5))") + assert int(output) == 7 + output = venv.python("-c", "import mypkg; print(mypkg.example_prod(6, 7))") + assert int(output) == 42 + + # @pytest.mark.skipif( # sys.platform not in {"linux", "darwin"}, reason="Not supported on this platform" # ) From d768a041a689afb157810983467ce03ceda0a0e4 Mon Sep 17 00:00:00 2001 From: Ralf Gommers Date: Mon, 28 Oct 2024 18:02:11 +0100 Subject: [PATCH 03/11] TST: add package linking a library from a subproject Note that for Meson versions older than 1.2.0, CI failed with: ``` mesonpy.BuildError: Could not map installation path to an equivalent wheel directory: '{libdir_static}/libexamplelib.a' ``` because the `--skip-subprojects` install option isn't honored. Hence the test skip on older versions. In addition, the `c_shared_libs` usage requires Meson 1.3.0 - meson-python commit 2a9179d86c488d27e9614dcfdd082cc8668572e2 --- .../foo/__init__.py | 8 ++++ .../foo/_examplemod.c | 37 +++++++++++++++++++ .../foo/meson.build | 16 ++++++++ .../link-library-in-subproject/meson.build | 17 +++++++++ .../link-library-in-subproject/pyproject.toml | 15 ++++++++ .../subprojects/bar/bar_dll.h | 19 ++++++++++ .../subprojects/bar/examplelib.c | 9 +++++ .../subprojects/bar/examplelib.h | 7 ++++ .../subprojects/bar/meson.build | 36 ++++++++++++++++++ tests/test_wheel.py | 7 ++++ 10 files changed, 171 insertions(+) create mode 100644 tests/packages/link-library-in-subproject/foo/__init__.py create mode 100644 tests/packages/link-library-in-subproject/foo/_examplemod.c create mode 100644 tests/packages/link-library-in-subproject/foo/meson.build create mode 100644 tests/packages/link-library-in-subproject/meson.build create mode 100644 tests/packages/link-library-in-subproject/pyproject.toml create mode 100644 tests/packages/link-library-in-subproject/subprojects/bar/bar_dll.h create mode 100644 tests/packages/link-library-in-subproject/subprojects/bar/examplelib.c create mode 100644 tests/packages/link-library-in-subproject/subprojects/bar/examplelib.h create mode 100644 tests/packages/link-library-in-subproject/subprojects/bar/meson.build diff --git a/tests/packages/link-library-in-subproject/foo/__init__.py b/tests/packages/link-library-in-subproject/foo/__init__.py new file mode 100644 index 0000000..04eeddf --- /dev/null +++ b/tests/packages/link-library-in-subproject/foo/__init__.py @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: 2024 The meson-python developers +# +# SPDX-License-Identifier: MIT + +from ._example import example_sum + + +__all__ = ["example_sum"] diff --git a/tests/packages/link-library-in-subproject/foo/_examplemod.c b/tests/packages/link-library-in-subproject/foo/_examplemod.c new file mode 100644 index 0000000..d695569 --- /dev/null +++ b/tests/packages/link-library-in-subproject/foo/_examplemod.c @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2022 The meson-python developers +// +// SPDX-License-Identifier: MIT + +#include + +#include "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); +} diff --git a/tests/packages/link-library-in-subproject/foo/meson.build b/tests/packages/link-library-in-subproject/foo/meson.build new file mode 100644 index 0000000..a171be5 --- /dev/null +++ b/tests/packages/link-library-in-subproject/foo/meson.build @@ -0,0 +1,16 @@ +# SPDX-FileCopyrightText: 2022 The meson-python developers +# +# SPDX-License-Identifier: MIT + +py.extension_module( + '_example', + '_examplemod.c', + dependencies: bar_dep, + install: true, + subdir: 'foo', +) + +py.install_sources( + ['__init__.py'], + subdir: 'foo', +) diff --git a/tests/packages/link-library-in-subproject/meson.build b/tests/packages/link-library-in-subproject/meson.build new file mode 100644 index 0000000..eaa8295 --- /dev/null +++ b/tests/packages/link-library-in-subproject/meson.build @@ -0,0 +1,17 @@ +# SPDX-FileCopyrightText: 2022 The meson-python developers +# +# SPDX-License-Identifier: MIT + +project( + 'link-library-in-subproject', + 'c', + version: '1.0.0', + meson_version: '>=1.2.0', +) + +py = import('python').find_installation(pure: false) + +bar_proj = subproject('bar') +bar_dep = bar_proj.get_variable('bar_dep') + +subdir('foo') diff --git a/tests/packages/link-library-in-subproject/pyproject.toml b/tests/packages/link-library-in-subproject/pyproject.toml new file mode 100644 index 0000000..02e07eb --- /dev/null +++ b/tests/packages/link-library-in-subproject/pyproject.toml @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2022 The meson-python developers +# +# SPDX-License-Identifier: MIT + +[build-system] +build-backend = "hatchling.build" +requires = ["hatch-meson"] + +[project] +name = 'link-library-in-subproject' +version = '1.2.3' + +[tool.hatch.build.hooks.meson.args] +setup = ['--default-library=static'] +install = ['--skip-subprojects'] diff --git a/tests/packages/link-library-in-subproject/subprojects/bar/bar_dll.h b/tests/packages/link-library-in-subproject/subprojects/bar/bar_dll.h new file mode 100644 index 0000000..61a6d7b --- /dev/null +++ b/tests/packages/link-library-in-subproject/subprojects/bar/bar_dll.h @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2024 The meson-python developers +// +// SPDX-License-Identifier: MIT + +#pragma once + +// When building the `examplelib` DLL, this macro expands to `__declspec(dllexport)` +// so we can annotate symbols appropriately as being exported. When used in +// headers consuming a DLL, this macro expands to `__declspec(dllimport)` so +// that consumers know the symbol is defined inside the DLL. In all other cases, +// the macro expands to nothing. +// Note: BAR_DLL_{EX,IM}PORTS are set in meson.build +#if defined(BAR_DLL_EXPORTS) + #define BAR_DLL __declspec(dllexport) +#elif defined(BAR_DLL_IMPORTS) + #define BAR_DLL __declspec(dllimport) +#else + #define BAR_DLL +#endif diff --git a/tests/packages/link-library-in-subproject/subprojects/bar/examplelib.c b/tests/packages/link-library-in-subproject/subprojects/bar/examplelib.c new file mode 100644 index 0000000..7efb366 --- /dev/null +++ b/tests/packages/link-library-in-subproject/subprojects/bar/examplelib.c @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2022 The meson-python developers +// +// SPDX-License-Identifier: MIT + +#include "bar_dll.h" + +BAR_DLL int sum(int a, int b) { + return a + b; +} diff --git a/tests/packages/link-library-in-subproject/subprojects/bar/examplelib.h b/tests/packages/link-library-in-subproject/subprojects/bar/examplelib.h new file mode 100644 index 0000000..c0f9e5e --- /dev/null +++ b/tests/packages/link-library-in-subproject/subprojects/bar/examplelib.h @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2022 The meson-python developers +// +// SPDX-License-Identifier: MIT + +#include "bar_dll.h" + +BAR_DLL int sum(int a, int b); diff --git a/tests/packages/link-library-in-subproject/subprojects/bar/meson.build b/tests/packages/link-library-in-subproject/subprojects/bar/meson.build new file mode 100644 index 0000000..503257b --- /dev/null +++ b/tests/packages/link-library-in-subproject/subprojects/bar/meson.build @@ -0,0 +1,36 @@ +# SPDX-FileCopyrightText: 2022 The meson-python developers +# +# SPDX-License-Identifier: MIT + +project('bar', 'c', version: '1.2.3', meson_version: '>= 1.3.0') + +if get_option('default_library') == 'shared' and meson.get_compiler('c').get_id() in ['msvc', 'clang-cl', 'intel-cl'] + export_dll_args = ['-DBAR_DLL_EXPORTS'] + import_dll_args = ['-DBAR_DLL_IMPORTS'] +else + export_dll_args = [] + import_dll_args = [] +endif + +example_lib = library( + 'examplelib', + 'examplelib.c', + c_shared_args: export_dll_args, + install: true, +) + +# A second library that we don't link from `foo`. If we install the subproject, +# this second library also ends up in the wheel. To prevent that, we need to +# skip installing this `bar` subproject, and statically link `example_lib`. +unwanted_lib = library( + 'unwantedlib', + 'examplelib.c', + c_shared_args: export_dll_args, + install: true, +) + +bar_dep = declare_dependency( + compile_args: import_dll_args, + link_with: example_lib, + include_directories: '.', +) diff --git a/tests/test_wheel.py b/tests/test_wheel.py index fb36c36..01e4105 100644 --- a/tests/test_wheel.py +++ b/tests/test_wheel.py @@ -185,6 +185,13 @@ def test_sharedlib_in_package(venv, wheel_sharedlib_in_package): assert int(output) == 42 +@pytest.mark.skipif(MESON_VERSION < (1, 3, 0), reason="Meson version too old") +def test_link_library_in_subproject(venv, wheel_link_library_in_subproject): + venv.pip("install", wheel_link_library_in_subproject) + output = venv.python("-c", "import foo; print(foo.example_sum(3, 6))") + assert int(output) == 9 + + # @pytest.mark.skipif( # sys.platform not in {"linux", "darwin"}, reason="Not supported on this platform" # ) From 3db74f223ee604e696b444d7de4199086422db89 Mon Sep 17 00:00:00 2001 From: Daniele Nicolodi Date: Tue, 25 Feb 2025 20:23:50 +0100 Subject: [PATCH 04/11] CI: test with PyPy 3.11, drop PyPy 3.10 - meson-python commit 1c8092dc477cbc7e1e4d40913608d9daae75f793 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 72ef648..f59d591 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -78,7 +78,7 @@ jobs: - os: ubuntu-latest python: 'pypy-3.9' - os: ubuntu-latest - python: 'pypy-3.10' + python: 'pypy-3.11' - os: ubuntu-latest python: '3.8' - os: ubuntu-latest From e813fdbff15b1c5ca7ffa8b9d5d70a2955ffcc9e Mon Sep 17 00:00:00 2001 From: Daniele Nicolodi Date: Wed, 30 Apr 2025 10:37:52 +0200 Subject: [PATCH 05/11] CI: test with Meson versions 1.6.1 and 1.7.2 Meson 1.8.0 has been released. Add the older releases to the CI matrix. - meson-python commit ff3ba62fcb6f389e46f96e173252c8ec2f2b3f79 --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f59d591..937e6c4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -109,6 +109,12 @@ jobs: - os: ubuntu-latest python: '3.12' meson: '~=1.5.0' + - os: ubuntu-latest + python: '3.12' + meson: '~=1.6.0' + - os: ubuntu-latest + python: '3.12' + meson: '~=1.7.0' # Test with Meson master branch. - os: ubuntu-latest python: '3.12' From 47646c49aba77a2bac6e8959eab5a81cbae0eb43 Mon Sep 17 00:00:00 2001 From: Daniele Nicolodi Date: Wed, 30 Apr 2025 10:45:43 +0200 Subject: [PATCH 06/11] CI: test with minimum version of dependencies - meson-python commit 352ded1ff5329fb2b1655586df24d37abaf81b65 --- .github/workflows/ci.yml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 937e6c4..f1936f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,7 +70,7 @@ jobs: - '3.13' meson: - - pyproject_metadata: + dependencies: - hatchling: - @@ -88,6 +88,8 @@ jobs: # 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. + # To avoid having too many CI jobs, combine this with tests + # with the minimum version of other dependencies. - os: ubuntu-latest python: '3.11' meson: '~=0.64.0' @@ -109,12 +111,15 @@ jobs: - os: ubuntu-latest python: '3.12' meson: '~=1.5.0' + dependencies: 'packaging==23.2 pyproject-metadata==0.9.0' - os: ubuntu-latest python: '3.12' meson: '~=1.6.0' + dependencies: 'pyproject-metadata==0.9.0' - os: ubuntu-latest python: '3.12' meson: '~=1.7.0' + dependencies: 'packaging==23.2' # Test with Meson master branch. - os: ubuntu-latest python: '3.12' @@ -159,14 +164,14 @@ jobs: 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 hatchling run: python -m pip install "hatchling ${{ matrix.hatchling }}" if: ${{ matrix.hatchling }} + - name: Install dependencies + run: python -m pip install ${{ matrix.dependencies }} + if: ${{ matrix.dependencies }} + - name: Install shell: bash working-directory: dist From 2a74f3fe9dd82d76a8e5e31b198ff0f6a2a12c02 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 27 May 2025 09:57:13 +0800 Subject: [PATCH 07/11] ENH: add support for targeting iOS - meson-python commit dbc2c480dacc9a5031451c84088b66ae48c79618 --- src/hatch_meson/_tags.py | 20 ++++++++++++++++++ src/hatch_meson/plugin.py | 27 ++++++++++++++++++++++++ tests/test_project.py | 44 +++++++++++++++++++++++++++++++++++++++ tests/test_tags.py | 28 +++++++++++++++++++++++++ 4 files changed, 119 insertions(+) diff --git a/src/hatch_meson/_tags.py b/src/hatch_meson/_tags.py index 51741f9..149c3a5 100644 --- a/src/hatch_meson/_tags.py +++ b/src/hatch_meson/_tags.py @@ -164,10 +164,30 @@ def _get_macosx_platform_tag() -> str: return f"macosx_{major}_{minor}_{arch}" +def _get_ios_platform_tag() -> str: + # Override the iOS version if one is provided via the + # IPHONEOS_DEPLOYMENT_TARGET environment variable. + try: + version = tuple( + map(int, os.environ.get("IPHONEOS_DEPLOYMENT_TARGET", "").split(".")) + )[:2] + except ValueError: + version = tuple(map(int, platform.ios_ver().release.split(".")))[:2] # type: ignore[attr-defined] + + # Although _multiarch is an internal implementation detail, it's a core part + # of how CPython is implemented on iOS; this attribute is also relied upon + # by `packaging` as part of tag determiniation. + multiarch = sys.implementation._multiarch.replace("-", "_") + + return f"ios_{version[0]}_{version[1]}_{multiarch}" + + def get_platform_tag() -> str: platform = sysconfig.get_platform() if platform.startswith("macosx"): return _get_macosx_platform_tag() + if platform.startswith("ios"): + return _get_ios_platform_tag() if _32_BIT_INTERPRETER: # 32-bit Python running on a 64-bit kernel. if platform == "linux-x86_64": diff --git a/src/hatch_meson/plugin.py b/src/hatch_meson/plugin.py index 25c9b5f..e785ce0 100644 --- a/src/hatch_meson/plugin.py +++ b/src/hatch_meson/plugin.py @@ -696,4 +696,31 @@ def _create_macos_crossfile(crossfile_path: pathlib.Path) -> bool: crossfile_path.write_text(cross_file_data, encoding="utf-8") return True + elif sysconfig.get_platform().startswith("ios-"): + ios_ver = platform.ios_ver() # type: ignore[attr-defined] + + arch = platform.machine() + family = "aarch64" if arch == "arm64" else arch + subsystem = "ios-simulator" if ios_ver.is_simulator else "ios" + + cross_file_data = textwrap.dedent( + f""" + [binaries] + c = '{arch}-apple-{subsystem}-clang' + cpp = '{arch}-apple-{subsystem}-clang++' + objc = '{arch}-apple-{subsystem}-clang' + objcpp = '{arch}-apple-{subsystem}-clang++' + ar = '{arch}-apple-{subsystem}-ar' + + [host_machine] + system = 'ios' + subsystem = {subsystem!r} + cpu = {arch!r} + cpu_family = {family!r} + endian = 'little' + """ + ) + crossfile_path.write_text(cross_file_data, encoding="utf-8") + return True + return False diff --git a/tests/test_project.py b/tests/test_project.py index 4ae373b..0a2bd0e 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -185,3 +185,47 @@ def test_archflags_envvar_parsing_invalid( finally: # revert environment variable setting done by the in-process build os.environ.pop("_PYTHON_HOST_PLATFORM", None) + + +# @pytest.mark.skipif(sys.version_info < (3, 13), reason="requires Python 3.13 or higher") +# @pytest.mark.parametrize( +# "multiarch", +# [ +# "arm64-iphoneos", +# "arm64-iphonesimulator", +# "x86_64-iphonesimulator", +# ], +# ) +# def test_ios_project(package_simple, monkeypatch, multiarch, tmp_path): +# arch, abi = multiarch.split("-") +# subsystem = "ios-simulator" if abi == "iphonesimulator" else "ios" + +# # Mock being on iOS +# monkeypatch.setattr(sys, "platform", "ios") +# monkeypatch.setattr(platform, "machine", Mock(return_value=arch)) +# monkeypatch.setattr( +# sysconfig, "get_platform", Mock(return_value=f"ios-13.0-{multiarch}") +# ) +# ios_ver = platform.IOSVersionInfo( +# "iOS", "13.0", "iPhone", multiarch.endswith("simulator") +# ) +# monkeypatch.setattr(platform, "ios_ver", Mock(return_value=ios_ver)) + +# # Create an iOS project. +# project = mesonpy.Project(source_dir=package_simple, build_dir=tmp_path) + +# # Meson configuration points at the cross file +# assert project._meson_args["setup"] == [ +# "--cross-file", +# os.fspath(tmp_path / "meson-python-cross-file.ini"), +# ] + +# # Meson config files exist, and have some relevant keys +# assert (tmp_path / "meson-python-native-file.ini").exists() +# assert (tmp_path / "meson-python-cross-file.ini").exists() + +# cross_config = (tmp_path / "meson-python-cross-file.ini").read_text() + +# assert "\nsystem = 'ios'\n" in cross_config +# assert f"\nc = '{arch}-apple-{subsystem}-clang'\n" in cross_config +# assert f"\nsubsystem = '{subsystem}'\n" in cross_config diff --git a/tests/test_tags.py b/tests/test_tags.py index 091973f..9728249 100644 --- a/tests/test_tags.py +++ b/tests/test_tags.py @@ -10,6 +10,7 @@ import sysconfig from collections import defaultdict +from unittest.mock import Mock import packaging.tags import pytest @@ -80,6 +81,33 @@ def test_python_host_platform(monkeypatch): assert hatch_meson._tags.get_platform_tag().endswith("x86_64") +@pytest.mark.skipif(sys.version_info < (3, 13), reason="requires Python 3.13 or higher") +@pytest.mark.skipif(sys.platform != "darwin", reason="macOS specific test") +def test_ios_platform_tag(monkeypatch): + # Mock being on iOS + monkeypatch.setattr(sys.implementation, "_multiarch", "arm64-iphoneos") + monkeypatch.setattr( + sysconfig, "get_platform", Mock(return_value="ios-13.0-arm64-iphoneos") + ) + ios_ver = platform.IOSVersionInfo("iOS", "13.0", "iPhone", False) + monkeypatch.setattr(platform, "ios_ver", Mock(return_value=ios_ver)) + + # Check the default value + assert ( + next(packaging.tags.ios_platforms((13, 0))) + == hatch_meson._tags.get_platform_tag() + ) + + # Check the value when IPHONEOS_DEPLOYMENT_TARGET is set. + for major in range(13, 20): + for minor in range(3): + monkeypatch.setenv("IPHONEOS_DEPLOYMENT_TARGET", f"{major}.{minor}") + assert ( + next(packaging.tags.ios_platforms((major, minor))) + == hatch_meson._tags.get_platform_tag() + ) + + def wheel_builder_test_factory(content, pure=True, limited_api=False): manifest = defaultdict(list) manifest.update( From d920367f63e232b8d2b5b0f8e1f531ea10c8fc6b Mon Sep 17 00:00:00 2001 From: Daniele Nicolodi Date: Sun, 8 Jun 2025 13:45:29 +0200 Subject: [PATCH 08/11] TST: apply environment variable cleanup to all tests This has no adverse consequence and makes it impossible to forget to add it to relevant tests, which is important because the tests are not commonly run with the environment variables set, making the failure hard to discover. - meson-python commit 063b0e072b8996acc2bcb113127b5701152a4f91 --- tests/conftest.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/conftest.py b/tests/conftest.py index 9cecabd..54778b2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -214,3 +214,12 @@ def disable_pip_version_check(): mpatch = pytest.MonkeyPatch() yield mpatch.setenv("PIP_DISABLE_PIP_VERSION_CHECK", "1") mpatch.undo() + + +@pytest.fixture(autouse=True, scope="session") +def cleanenv(): + # Cannot use the 'monkeypatch' fixture because of scope mismatch. + mpatch = pytest.MonkeyPatch() + # $MACOSX_DEPLOYMENT_TARGET affects the computation of the platform tag on macOS. + yield mpatch.delenv("MACOSX_DEPLOYMENT_TARGET", raising=False) + mpatch.undo() From 6b33966e498ae1cc39e0e84c8f99f078bbf91177 Mon Sep 17 00:00:00 2001 From: Daniele Nicolodi Date: Sun, 8 Jun 2025 22:50:40 +0200 Subject: [PATCH 09/11] BUG: allow $MACOSX_DEPLOYMENT_TARGET to be set to major version only Accept version specifiers containing only the major version, for example "11", in addition to the already supported major.minor and major.minor.patch formats, for example "11.0" or "11.0.0". - meson-python commit cd4421478f7c112e85c7feacb5034bb80d653222 --- src/hatch_meson/_tags.py | 10 ++++------ tests/test_tags.py | 11 +++++++++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/hatch_meson/_tags.py b/src/hatch_meson/_tags.py index 149c3a5..a3aba13 100644 --- a/src/hatch_meson/_tags.py +++ b/src/hatch_meson/_tags.py @@ -111,9 +111,8 @@ def _get_macosx_platform_tag() -> str: # 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] + parts = os.environ.get("MACOSX_DEPLOYMENT_TARGET", "").split(".")[:2] + version = tuple(map(int, parts + ["0"] * (2 - len(parts)))) except ValueError: version = tuple(map(int, ver.split(".")))[:2] @@ -168,9 +167,8 @@ def _get_ios_platform_tag() -> str: # Override the iOS version if one is provided via the # IPHONEOS_DEPLOYMENT_TARGET environment variable. try: - version = tuple( - map(int, os.environ.get("IPHONEOS_DEPLOYMENT_TARGET", "").split(".")) - )[:2] + parts = os.environ.get("IPHONEOS_DEPLOYMENT_TARGET", "").split(".")[:2] + version = tuple(map(int, parts + ["0"] * (2 - len(parts)))) except ValueError: version = tuple(map(int, platform.ios_ver().release.split(".")))[:2] # type: ignore[attr-defined] diff --git a/tests/test_tags.py b/tests/test_tags.py index 9728249..f63979b 100644 --- a/tests/test_tags.py +++ b/tests/test_tags.py @@ -61,6 +61,17 @@ def test_macos_platform_tag(monkeypatch): next(packaging.tags.mac_platforms((major, minor))) == hatch_meson._tags.get_platform_tag() ) + for major in range(11, 13): + monkeypatch.setenv("MACOSX_DEPLOYMENT_TARGET", f"{major}.0") + assert ( + next(packaging.tags.mac_platforms((major, 0))) + == hatch_meson._tags.get_platform_tag() + ) + monkeypatch.setenv("MACOSX_DEPLOYMENT_TARGET", f"{major}") + assert ( + next(packaging.tags.mac_platforms((major, 0))) + == hatch_meson._tags.get_platform_tag() + ) @pytest.mark.skipif(sys.platform != "darwin", reason="macOS specific test") From 96a43b9664a4d43b0915be0f0a4b84c28b20557a Mon Sep 17 00:00:00 2001 From: Daniele Nicolodi Date: Sat, 14 Jun 2025 14:46:58 +0200 Subject: [PATCH 10/11] MAINT: add comment - meson-python commit 7efa4176758106374e7e6ef2b5d4c1968a38c570 --- src/hatch_meson/plugin.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hatch_meson/plugin.py b/src/hatch_meson/plugin.py index e785ce0..e917557 100644 --- a/src/hatch_meson/plugin.py +++ b/src/hatch_meson/plugin.py @@ -696,6 +696,8 @@ def _create_macos_crossfile(crossfile_path: pathlib.Path) -> bool: crossfile_path.write_text(cross_file_data, encoding="utf-8") return True + # Support iOS targets. iOS does not have native build tools and always + # requires cross compilation: synthesize the appropriate cross file. elif sysconfig.get_platform().startswith("ios-"): ios_ver = platform.ios_ver() # type: ignore[attr-defined] From 68e793706485cc9e53b0d9439f016ae540d75742 Mon Sep 17 00:00:00 2001 From: Daniele Nicolodi Date: Sat, 14 Jun 2025 18:42:35 +0200 Subject: [PATCH 11/11] ENH: define strip binary in synthesized cross files - meson-python commit d8ed59994e77864a797c1b590bbd1b364fb08d7b --- src/hatch_meson/plugin.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/hatch_meson/plugin.py b/src/hatch_meson/plugin.py index e917557..5ceacf5 100644 --- a/src/hatch_meson/plugin.py +++ b/src/hatch_meson/plugin.py @@ -686,6 +686,7 @@ def _create_macos_crossfile(crossfile_path: pathlib.Path) -> bool: cpp = ['c++', '-arch', {arch!r}] objc = ['cc', '-arch', {arch!r}] objcpp = ['c++', '-arch', {arch!r}] + strip = ['strip', '-arch', {arch!r}] [host_machine] system = 'darwin' cpu = {arch!r} @@ -708,11 +709,12 @@ def _create_macos_crossfile(crossfile_path: pathlib.Path) -> bool: cross_file_data = textwrap.dedent( f""" [binaries] + ar = '{arch}-apple-{subsystem}-ar' c = '{arch}-apple-{subsystem}-clang' cpp = '{arch}-apple-{subsystem}-clang++' objc = '{arch}-apple-{subsystem}-clang' objcpp = '{arch}-apple-{subsystem}-clang++' - ar = '{arch}-apple-{subsystem}-ar' + strip = '{arch}-apple-{subsystem}-strip' [host_machine] system = 'ios'