diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 56b529c..f1936f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,7 +70,7 @@ jobs: - '3.13' meson: - - pyproject_metadata: + dependencies: - hatchling: - @@ -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 @@ -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,6 +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' @@ -153,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 @@ -303,38 +314,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 diff --git a/src/hatch_meson/_tags.py b/src/hatch_meson/_tags.py index 51741f9..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] @@ -164,10 +163,29 @@ 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: + 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] + + # 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..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} @@ -696,4 +697,34 @@ 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] + + 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] + 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++' + strip = '{arch}-apple-{subsystem}-strip' + + [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/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() 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/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_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..f63979b 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 @@ -60,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") @@ -80,6 +92,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( diff --git a/tests/test_wheel.py b/tests/test_wheel.py index 326bda4..01e4105 100644 --- a/tests/test_wheel.py +++ b/tests/test_wheel.py @@ -177,6 +177,21 @@ 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(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" # )