Skip to content

Conversation

@neutrinoceros
Copy link
Owner

@neutrinoceros neutrinoceros commented Sep 15, 2024

This is highly experimental and I'm not sure I'll merge this, but this is a dump of my current progress.

TODO:

  • figure out how to pass macros (NPY_TARGET_VERSION)
  • [N/A] simplify numpy.include() if possible
  • fix coverage (figure out how to run with editable installs on CI)
  • fix windows
  • fix free-threading builds (ninja is missing)
  • clean up setuptools-specific stuff (setup.py, MANIFEST.in)
  • reinstate GPGI_PY_LIB if I can
  • check that artifacts are comparable in size to the baseline
  • Look into ABI3 wheels with meson

@neutrinoceros
Copy link
Owner Author

@rgommers, since you offered, would you have any clue as to what I'm doing wrong here ?

Copy link

@rgommers rgommers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, here are some hints.

meson.build Outdated


# https://github.com/mesonbuild/meson/issues/9598
incdir_numpy = run_command(py,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the fancier version, you can copy the code block at https://github.com/PyWavelets/pywt/blob/becef5486174c77727ea2a60e6392744b2cc1d4a/pywt/_extensions/meson.build#L13

Once we get to numpy >=2.0 only (one more year ....), it becomes simply:

numpy_dep = dependency('numpy')

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One other advantage is that the longer code block takes care of in-tree venv's . Meson by default doesn't like to see include_directories('abspath-within-source-tree'), which is usually a mistake, but in-tree venv's overlap with this.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's also a build warning related to NPY_NO_DEPRECATED_API that you can take care of with the code linked above. Warning-free builds are one of the upsides of moving to meson-python:)

The define_macros thing in setuptools has more generic alternatives in Meson. In this case, a c_args keyword for passing any argument to a C compiler. In the case of NPY_TARGET_VERSION (which doesn't need setting I think, but it can't hurt either): c_args: ['-DNPY_TARGET_VERSION=NPY_1_25_API_VERSION']

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once we get to numpy >=2.0 only (one more year ....), it becomes simply:

just to be sure, you mean that the required: false argument can be removed once numpy 1 is dropped ?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No I mean the 25 lines of code at https://github.com/PyWavelets/pywt/blob/becef5486174c77727ea2a60e6392744b2cc1d4a/pywt/_extensions/meson.build#L13-L43 can be replaced by the one-liner then.

To support that, I had to add the numpy-config executable, and that only landed in NumPy 2.0

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When dropping the required: false keyword argument, the remainder of those 25 lines can be removed due to dead code elimination. Numpy 2 will always be detectable via dependency('numpy', required: true) (the default is true) and this .found() can never evaluate to false so the code block becomes unreachable.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. This specific package isn't distributed anywhere other than PyPI (that I know of), so I suppose I don't need to support building with numpy 1, but I'll try to make this PR a canonical example when I later try to migrate heavier projects, so I guess I'll keep this complexity for now. Thanks !

meson.build Outdated
inc_np = include_directories(incdir_numpy)

py.extension_module(
'gpgi._lib',

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't look right, it produces an extension module named gpgi._lib.cpython-xxx.so, rather than a module _lib inside a package gpgi. This will fix it:

diff --git a/meson.build b/meson.build
index fcd7a4e..53da894 100644
--- a/meson.build
+++ b/meson.build
@@ -19,8 +19,9 @@ incdir_numpy = run_command(py,
 inc_np = include_directories(incdir_numpy)
 
 py.extension_module(
-    'gpgi._lib',
+    '_lib',
     'src/gpgi/_lib.pyx',
     install: true,
     include_directories : [inc_np],
+    subdir: 'gpgi',
 )

@neutrinoceros
Copy link
Owner Author

I'm still having a hard time figuring out what I need to do to make this work. This might be a silly question but since I can't find an example that does this in the wild: @rgommers, could you confirm that meson-python supports src layouts ?
At the moment, building locally appears to run smoothly but then the only thing that "works" at runtime is import gpgi (trying to access anything from the namespace results in an AttributeError being raised). I tried all combinations of (no-)editable and (no-)build-isolation.

@rgommers
Copy link

Yes, src layout should be supported. Let me try and find some time tonight and test this branch locally again.

The basic points are:

  • all files need to be explicitly marked for installation (including .py files - a bit more work, but also you get way more control, no more incorrectly installed files like setup.py files and codegen scripts)
  • the subdir: argument should be set correctly for all those files

I try this locally with:

$ meson setup build --prefix=$PWD/build-install
$ ninja -C build
$ meson install -C build

That gives easier to introspect results than going via pip/build. I'm fairly sure your issues here are in meson.build files, not in meson-python's wheel build support.

meson.build Outdated
endif

py.extension_module(
'_lib',

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Installing the cython module is a good first step. In pyproject.toml you currently have setuptools.packages.find which hasn't been ported to meson yet, so you can do that with:

py.install_sources(
    'src/gpgi/foo.py',
    'src/gpgi/bar.py',
    subdir: 'gpgi'
)

Or if you want to just install the whole subdirectory you could try:

install_subdir('src/gpgi', install_dir: py.get_install_dir())

though this would install all files, including *.pyx. There is an exclude_files argument, but my personal inclination would be to list the files that do get installed, not the ones that don't get installed. I've been bitten by weird autodetection results with stale files and directories that shouldn't have existed, in the past, including "we refactored some code into a subpackage but did not run git clean so we ended up with two copies autodetected in the wheel, and the wheel had to be yanked because it was so broken and then nobody was ever able to pip install that version without building from an sdist because PyPI doesn't support fixing existing releases".

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks a lot, this seems to be the one crucial piece I was missing !

@neutrinoceros
Copy link
Owner Author

Thanks a lot @rgommers and @eli-schwartz for your help so far !
I notice a couple pain points remaining.

  • editable builds installs on CI don't seem to work yet (I use them for simplicity in coverage measurement)
  • portability:
    • windows, being windows, is currently failing
    • free-threading builds are choking on ninja missing a wheel. Do I need to build it from source ? (I assume this is supported)

@eli-schwartz
Copy link

Ninja is a C++ program, you should be able to install it via apt or brew, and for windows I guess choco install.

You can also get it from pip because members of the Python community who make use of ninja in python build backends, wanted it to be easier to install / rely on its availability, so they copied the ninja C++ program into a wheel and used PyPI as a handy distribution channel. There's no particular reason why one or the other method needs to be used.

If ninja is installed already, meson-python won't add it via the build-backend hook as an added build requirement. It will just use the one you already have.

I'm not sure why it can't find a prebuilt free-threading wheel. The ninja wheel is "py-none-${manylinux_tag}" so it should be compatible?

@eli-schwartz
Copy link

Wait.

  ╰─▶ Call to `mesonpy.build_wheel` failed (exit status: 1)

      [stdout]

      meson-python: error: Could not find ninja version 1.8.2 or newer.

      hint: This usually indicates a problem with the package or the build
      environment.

Why isn't it adding this to get_requires_for_build_*? This shouldn't happen unless for example $NINJA is an exported environment variable that evaluates to nothing / no valid program executable. I don't think this workflow should be setting it at all...

@rgommers
Copy link

Having a look at the free-threading issue. I'm not yet friends with uv, it seems like there's no great way to ask uv build for a free-threading interpreter, it seems to randomly select one interpreter out of the many available on my system. The solution for now is to activate a conda env with python-freethreading in it, and then:

uv build --wheel --python $(which python)

This uses build isolation, it fails because of Cython 3.0.12. After committing (not just saving the file, actually committing) this diff, the isolated build works:

--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
 [build-system]
 requires = [
     "meson-python",
-    "Cython>=3.0",
+    "Cython>=3.1.0a1",
     "numpy>=2.0.0",
 ]
 build-backend = "mesonpy"

I have ninja locally though, in /opt/homebrew/bin/ninja.


For the CI job the explanation seems simpler: build isolation is disabled, and this is run:

  uv pip install meson-python numpy Cython
  uv pip install --no-build-isolation --no-deps .

ninja is simply missing here, add it to the first of these lines and things should work.

Why isn't it adding this to get_requires_for_build_*?

Because of --no-build-isolation.

@rgommers
Copy link

@neutrinoceros for context: meson-python does not have a direct dependency on ninja because it's very often already installed as a system package, so installing it over and over in venv's is pretty wasteful. Instead, on isolated builds it checks if ninja is available, and if not it then adds it to the build dependencies. In the cp313t case though, build isolation is disabled, meaning the user prepares the build env (so must install ninja if not already present).

I'll note that scikit-build-core uses the same mechanism for both cmake and ninja. It's considered good practice to not unconditionally depend on system tools that are repackaged on PyPI.

@rgommers
Copy link

The Windows CI failure is fixed by changing the first line of meson.build to:

project('gpgi', 'c', 'cython', meson_version: '>=1.5.0')

You have to add the 'c' language, so Meson goes and finds a C compiler (it's then used to detect libpython). It's not implicit from 'cython', because you may also want to use Cython in C++ mode.

Passing CI log: https://github.com/rgommers/gpgi/actions/runs/13319374517/job/37200822857

uv eats the build log if it doesn't fail, all that is shown is:

       Built gpgi @ file:///D:/a/gpgi/gpgi

It may be useful to force it to show the log, since the details there (versions of compilers, numpy, etc.) are often helpful.

@eli-schwartz
Copy link

Why isn't it adding this to get_requires_for_build_*?

Because of --no-build-isolation.

Oh right, duh. 🤡 No idea why I blanked on that.

@neutrinoceros
Copy link
Owner Author

I'm not yet friends with uv, it seems like there's no great way to ask uv build for a free-threading interpreter

@rgommers how about this ?

uv build -p 3.13t

@rgommers
Copy link

ah that works, thanks!

@neutrinoceros neutrinoceros force-pushed the bld/migrate_to_meson branch 7 times, most recently from 9fb4420 to 529f65c Compare February 14, 2025 15:16
@neutrinoceros
Copy link
Owner Author

I'm attempting to re-implement my build-time option, previously implemented via an env var (GPGI_PY_LIB) as a meson option. This is done in my second commit. Even though the installation runs without error, it doesn't seem like the argument I'm passing (-Cinstall-args="pylib=true") is having any effect. This is visible in the pytest preamble at
https://github.com/neutrinoceros/gpgi/actions/runs/13332061055/job/37238374395
where I get gpgi._IS_PY_LIB = False.

@neutrinoceros neutrinoceros force-pushed the bld/migrate_to_meson branch 2 times, most recently from 0b4aff5 to d8a3f37 Compare February 14, 2025 15:37
@eli-schwartz
Copy link

https://mesonbuild.com/meson-python/reference/config-settings.html

setup-args

Extra arguments to be passed to the meson setup command.

install-args

Extra arguments to be passed to the meson install command.

You are using the latter but I think you want the former...

@rgommers
Copy link

You'll want -Csetup-args rather than -Cinstall-args. You want to avoid building and installing the Cython code completely, right?

In general, custom options in a meson.options file are going to be setup options. compile/install are for controlling some default behaviors.

@neutrinoceros
Copy link
Owner Author

That uv ticket seems weird to me as it's about a setuptools behavior, not a pip or uv behavior.

You're right; I think I'm simply confused by the amount of novelty I'm facing and trying to combine. I'm sure I'll get it sorted out in the end, and I cannot thank you enough for your support thus far!

I finally managed to get the pure-python build to work on CI by ... disabling setup-uv's cache. I didn't expect that it would cache anything from gpgi itself but apparently it does. Not calling it a bug yet as I'm really not confident I understand how uv's cache is supposed to work, but I might open a thread upstream about it at some point.

In the mean time, I just added a comment to the issue about lock files and build-time dependencies, which I think should be relevant to anyone willing to combine uv-based workflows and meson-python. Linking it here in case you guys would like to have a look (obviously, feel free to correct me if my explanation about meson is wrong in any way).
astral-sh/uv#7052 (comment)

@neutrinoceros neutrinoceros force-pushed the bld/migrate_to_meson branch 2 times, most recently from 33445b9 to d143e89 Compare February 16, 2025 12:00
@rgommers
Copy link

I finally managed to get the pure-python build to work on CI by ... disabling setup-uv's cache. I didn't expect that it would cache anything from gpgi itself but apparently it does.

This used to be something that pip did (caching locally built wheels) before that finally got dropped. It's a bit of a footgun; since the built frontend is unaware of build options, dependencies linked against, etc. so a pip install . and a pip install . -Ckey=value look identical to it (same filename, metadata, etc.). I think conceptually it's a bug to cache anything built from local sources. So say you built v1.0.0 with -Cdebug=true, if that were cached you could get a slow debug wheel in future installs.

@neutrinoceros
Copy link
Owner Author

While inspecting distribution artifacts in search for missing files (or files wrongfully included), I discovered that the generated .so files are surprisingly smaller with meson for manylinux2014 (628 KiB), as compared to my latest batch with setuptools (4.6 MiB). I see size differences on other archs too, albeit a lot less significant.

I would assume that this is because meson-python defaults to a release build while, presumably, setuptools doesn't, but I couldn't check this hypothesis reading through its docs yet.

@eli-schwartz
Copy link

That one is complicated. Setuptools can inherit default flags from cpython itself instead of using e.g. $CFLAGS, which means that it may be getting debug info settings from however cpython was built, and generally also gets -fno-strict-overflow which pessimizes codegen by assuming your code has lots of Undefined Behavior that cannot have optimizer passes run on it or everything will fail. (CPython itself had some UB a couple decades ago and has used that flag ever since out of caution.)

It's honestly possible this is almost entirely down to the debug info alone. You could try stripping the setuptools versions to check if that makes them closer to the size produced by meson.

@neutrinoceros
Copy link
Owner Author

Indeed it is apparent that setuptools calls gcc with -fno-strict-overflow, as can be seen at https://github.com/neutrinoceros/gpgi/actions/runs/13357831587/job/37303031155#step:4:651
I ran an experiment using -fstrict-overflow with setuptools and got the total size of the zipfile containing all linux wheels in CI from 6.96 MB to 1.59 MB with this change alone (for reference, it's 1.29 MB on this branch). This seems pretty conclusive to me. Thanks a bunch !
So far, the list of nice surprises keeps getting longer:

  • warning-free builds
  • clean dist artifacts (historical remnants like egg-info et al. are gone)
  • smaller, more optimized binaries by default

@rgommers
Copy link

Good news:) Re build flags, there's meson introspect which I found really helpful when first translating SciPy's build system. It can show you a lot of things, including exactly which build flags will be used during the build, without actually having to run the build.

Here's the output for this project for meson setup build && meson introspect build --targets -i for me:

Details
[
    {
        "name": "_lib.cpython-313-darwin",
        "id": "_lib.cpython-313-darwin.so@sha",
        "type": "shared module",
        "defined_in": "/Users/rgommers/code/tmp/gpgi/meson.build",
        "filename": [
            "/Users/rgommers/code/tmp/gpgi/build/_lib.cpython-313-darwin.so"
        ],
        "build_by_default": true,
        "target_sources": [
            {
                "language": "c",
                "compiler": [
                    "arm64-apple-darwin20.0.0-clang"
                ],
                "parameters": [
                    "-I/Users/rgommers/code/tmp/gpgi/build/_lib.cpython-313-darwin.so.p",
                    "-I/Users/rgommers/code/tmp/gpgi/build",
                    "-I/Users/rgommers/code/tmp/gpgi",
                    "-I/Users/rgommers/mambaforge/envs/scipy-dev-313/lib/python3.13/site-packages/numpy/_core/include",
                    "-I/Users/rgommers/mambaforge/envs/scipy-dev-313/include/python3.13",
                    "-fvisibility=hidden",
                    "-fdiagnostics-color=always",
                    "-Wall",
                    "-Winvalid-pch",
                    "-O0",
                    "-g",
                    "-ftree-vectorize",
                    "-fPIC",
                    "-fstack-protector-strong",
                    "-O2",
                    "-pipe",
                    "-isystem",
                    "/Users/rgommers/mambaforge/envs/scipy-dev-313/include",
                    "-D_FORTIFY_SOURCE=2",
                    "-isystem",
                    "/Users/rgommers/mambaforge/envs/scipy-dev-313/include",
                    "-DNPY_TARGET_VERSION=NPY_1_25_API_VERSION",
                    "-DNPY_NO_DEPRECATED_API=NPY_1_25_API_VERSION"
                ],
                "sources": [],
                "generated_sources": [
                    "/Users/rgommers/code/tmp/gpgi/build/_lib.cpython-313-darwin.so.p/src/gpgi/_lib.pyx.c"
                ],
                "unity_sources": []
            },
            {
                "linker": [
                    "arm64-apple-darwin20.0.0-clang"
                ],
                "parameters": [
                    "-L/Users/rgommers/mambaforge/envs/scipy-dev-313/lib",
                    "-Wl,-dead_strip_dylibs",
                    "-Wl,-headerpad_max_install_names",
                    "-Wl,-undefined,dynamic_lookup",
                    "-bundle",
                    "-Wl,-undefined,dynamic_lookup",
                    "-Wl,-headerpad_max_install_names",
                    "-Wl,-dead_strip_dylibs",
                    "-Wl,-rpath,/Users/rgommers/mambaforge/envs/scipy-dev-313/lib",
                    "-ftree-vectorize",
                    "-fPIC",
                    "-fstack-protector-strong",
                    "-O2",
                    "-pipe",
                    "-isystem",
                    "/Users/rgommers/mambaforge/envs/scipy-dev-313/include",
                    "-D_FORTIFY_SOURCE=2",
                    "-isystem",
                    "/Users/rgommers/mambaforge/envs/scipy-dev-313/include"
                ]
            }
        ],
        "extra_files": [],
        "subproject": null,
        "dependencies": [
            "dep4401978960",
            "numpy",
            "python-3.13"
        ],
        "depends": [],
        "installed": true,
        "install_filename": [
            "/usr/local/lib/python3.13/site-packages/gpgi/_lib.cpython-313-darwin.so"
        ]
    }
]

(disclaimer: for the result to be accurate for wheel builds, you should run it on a builddir generated by meson-python, or manually pass the few flags it adds, like -Dbuildtype=release -Db_ndebug=false)

So far, the list of nice surprises keeps getting longer:

In my experience, that continues happening once in a while. Recent example: we just started using and set up AddressSanitizer and ThreadSanitizer CI jobs in NumPy, and found that those had built-in support in Meson, it's a single argument to pass and things just worked.

@neutrinoceros
Copy link
Owner Author

neutrinoceros commented Feb 17, 2025

Last point on my list before I call this a success:
I wanted to look into the possibility to build abi3 wheels (in replacement of #304). I tried the following patch

diff --git a/meson.build b/meson.build
index dd69fe8..34c3889 100644
--- a/meson.build
+++ b/meson.build
@@ -58,6 +58,7 @@ else
       install: true,
       dependencies : [np_dep],
       c_args: numpy_compile_flags,
+      limited_api: '3.11', # keep in sync with requires-python (pyproject.toml)
   )
 endif
 
diff --git a/pyproject.toml b/pyproject.toml
index ed6aff0..afc895b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -20,6 +20,7 @@ classifiers = [
     "Programming Language :: Cython",
     "Programming Language :: Python :: 3",
 ]
+# keep in sync with limited_api (meson.build)
 requires-python = ">=3.11"
 dependencies = [
     # keep in sync with build-time requirements (dev group)

and built locally with Cython 3.1 (master). The resulting wheel isn't actually tagged as abi3 and the compiled .so doesn't pass abi3audit (even if I artificially rename it as if it was properly tagged). Is there something I'm missing ?

@rgommers
Copy link

You'll want to tell meson-python that you are opting in to the limited C API and stable ABI (since it's hard to detect fully automated, and there are cases where one may want to build a package in cp3xx mode even if abi3 mode is also possible). It can be done in two ways:

@neutrinoceros
Copy link
Owner Author

Thanks !

I'm getting closer with the following patch

diff --git a/meson.build b/meson.build
index dd69fe8..34c3889 100644
--- a/meson.build
+++ b/meson.build
@@ -58,6 +58,7 @@ else
       install: true,
       dependencies : [np_dep],
       c_args: numpy_compile_flags,
+      limited_api: '3.11', # keep in sync with requires-python (pyproject.toml)
   )
 endif
 
diff --git a/pyproject.toml b/pyproject.toml
index e8eb87b..8e0c752 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -20,6 +20,7 @@ classifiers = [
     "Programming Language :: Cython",
     "Programming Language :: Python :: 3",
 ]
+# keep in sync with limited_api (meson.build)
 requires-python = ">=3.11"
 dependencies = [
     # keep in sync with build-time requirements (dev group)
@@ -79,6 +80,9 @@ dev = [
     "numpy>=1.25.0, <3", # keep in sync with runtime requirement
 ]
 
+[tool.meson-python]
+limited-api = true
+
 [tool.uv]
 no-build-isolation-package = ["gpgi"]

However:

  • while the .so file is properly tagged (_lib.abi3.so), the wheel as a whole isn't (I get gpgi-2.0.0-cp313-cp313-macosx_15_0_arm64.whl when building locally)
  • _lib.abi3.so still doesn't pass abi3audit:
❯ abi3audit gpgi/_lib.abi3.so --verbose
[14:16:26] 👎 : _lib.abi3.so uses the Python 3.11 ABI, but is tagged for the Python 3.2 ABI
           ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┓                                  
           ┃ Symbol                           ┃ Version ┃                                  
           ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━┩                                  
           │ PyImport_GetModule               │ 3.8     │                                  
           │ PyUnicode_ReadChar               │ 3.7     │                                  
           │ PyIndex_Check                    │ 3.8     │                                  
           │ PyCMethod_New                    │ 3.9     │                                  
           │ PyBuffer_Release                 │ 3.11    │                                  
           │ PyErr_SetExcInfo                 │ 3.7     │                                  
           │ PyUnicode_GetLength              │ 3.7     │                                  
           │ PyType_GetSlot                   │ 3.4     │                                  
           │ PyErr_GetExcInfo                 │ 3.7     │                                  
           │ PyType_GetQualName               │ 3.11    │                                  
           │ Py_Version                       │ 3.11    │                                  
           │ PyErr_SetHandledException        │ 3.11    │                                  
           │ PyType_FromModuleAndSpec         │ 3.10    │                                  
           │ PyExc_ModuleNotFoundError        │ 3.6     │                                  
           │ PyImport_ImportModuleLevelObject │ 3.7     │                                  
           │ PyObject_GetBuffer               │ 3.11    │                                  
           └──────────────────────────────────┴─────────┘                                  
           💁 _lib.abi3.so: 1 extensions scanned; 1 ABI version mismatches and 0 ABI       
           violations found

is this expected, am I still anything else, or is this a bug in meson-python that I should report ?

@rgommers
Copy link

Is that the whole patch? I'm getting this, when building in a 3.13 env:

_lib.abi3.so.p/src/gpgi/_lib.pyx.c:671:9: error: call to undeclared function 'PyUnstable_Code_NewWithPosOnlyArgs'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
  671 |         PyUnstable_Code_NewWithPosOnlyArgs
      |         ^

Cython's Limited C API support is still WIP, so you may see something different in another env.

The pyproject.toml content looks correct though.

@neutrinoceros
Copy link
Owner Author

I did install Cython from source. The master branch now has everything needed, at least for gpgi.

@eli-schwartz
Copy link

I'm not sure how abi3audit works, but I was under the impression that abi3 notably doesn't have any version tagging at all, and version tagging is handled solely through the wheel filename (which should match the highest in-use ABI for all abi3 extensions in the project).

Where on earth is it getting "3.2" from? Does it simply report vague nonsense when run directly on a *.so, based on when the concept of abi3 was first invented?

The only thing necessary here should be to rename the wheel file in accordance with 3.11, then audit the wheel as a whole.

@rgommers
Copy link

rgommers commented Feb 17, 2025

Thanks, should have remembered that Cython is WIP. After installing Cython master:

% python -m build -wnx
+ meson setup /Users/rgommers/code/tmp/gpgi /Users/rgommers/code/tmp/gpgi/.mesonpy-o2n9mr4y -Dbuildtype=release -Db_ndebug=if-release -Db_vscrt=md --native-file=/Users/rgommers/code/tmp/gpgi/.mesonpy-o2n9mr4y/meson-python-native-file.ini
The Meson build system
Version: 1.6.0
Source dir: /Users/rgommers/code/tmp/gpgi
Build dir: /Users/rgommers/code/tmp/gpgi/.mesonpy-o2n9mr4y
Build type: native build
Project name: gpgi
Project version: undefined
C compiler for the host machine: arm64-apple-darwin20.0.0-clang (clang 17.0.6 "clang version 17.0.6")
C linker for the host machine: arm64-apple-darwin20.0.0-clang ld64 951.9
Cython compiler for the host machine: cython (cython 3.1.0)
Host machine cpu family: aarch64
Host machine cpu: aarch64
Program python found: YES (/Users/rgommers/mambaforge/envs/scipy-dev-313/bin/python)
Program cython found: YES (/Users/rgommers/mambaforge/envs/scipy-dev-313/bin/cython)
Found pkg-config: YES (/Users/rgommers/mambaforge/envs/scipy-dev-313/bin/pkg-config) 0.29.2
numpy-config found: YES (/Users/rgommers/mambaforge/envs/scipy-dev-313/bin/numpy-config) 2.2.3
Run-time dependency numpy found: YES 2.2.3
Run-time dependency python found: YES 3.13
Build targets in project: 1

gpgi undefined

  User defined options
    Native files: /Users/rgommers/code/tmp/gpgi/.mesonpy-o2n9mr4y/meson-python-native-file.ini
    b_ndebug    : if-release
    b_vscrt     : md
    buildtype   : release

Found ninja-1.12.1 at /Users/rgommers/mambaforge/envs/scipy-dev-313/bin/ninja
+ /Users/rgommers/mambaforge/envs/scipy-dev-313/bin/ninja
[3/3] Linking target _lib.abi3.so
[10/10] /Users/rgommers/code/tmp/gpgi/src/gpgi/typing.py
Successfully built gpgi-2.0.0-cp313-abi3-macosx_15_0_arm64.whl

File name gpgi-2.0.0-cp313-abi3-macosx_15_0_arm64.whl looks different from yours, and as expected (one hitch: if you want cp311 there, you should build with a 3.11 interpreter, which I didn't bother doing here).

diff --git a/meson.build b/meson.build
index dd69fe8..03ea4f6 100644
--- a/meson.build
+++ b/meson.build
@@ -56,6 +56,7 @@ else
       'src/gpgi/_lib.pyx',
       subdir: 'gpgi',
       install: true,
+      limited_api: '3.11',
       dependencies : [np_dep],
       c_args: numpy_compile_flags,
   )
diff --git a/pyproject.toml b/pyproject.toml
index e8eb87b..3f87e7f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -177,3 +177,6 @@ archs = "auto64"
 
 [tool.cibuildwheel.windows]
 archs = "AMD64"
+
+[tool.meson-python]
+limited-api = true

And abi3audit works fine.

@rgommers
Copy link

Oh, I think I know what happened: did you use uv build . or some such thing and not commit the diff above @neutrinoceros? Then the sdist would discard the local diff, and the wheel built from the sdist would be a regular version-specific one.

@eli-schwartz
Copy link

From the abi3audit README:

When auditing a "bare" shared object (e.g. foo.abi3.so), abi3audit cannot assume anything about the minimum intended abi3 version. Instead, it defaults to the lowest known abi3 version (abi3-cp32) and warns on any version mismatches (e.g., a symbol that was only stabilized in 3.6). This can result in false positives, so users are encouraged to audit entire wheels or packages instead (since they contain the sufficient metadata).

That seems... wrongly designed, dunno what else to say.

@neutrinoceros
Copy link
Owner Author

Oh, I think I know what happened: did you use uv build . or some such thing and not commit the diff above @neutrinoceros?

no, I indeed committed the change, but thanks for pointing this out, I didn't realize that uncommitted changes wouldn't make it to the artifact. It also pushed me in the right direction: I think the reason I got a broken build was that I had some meson-setup generated files left over from a previous build. Running again from a clean slate I get the expected, correct result !

That seems... wrongly designed, dunno what else to say.

Oh dear... I really should have paid more attention to that readme. Sorry about that !

Anyway, I think we're all done here, and I'm now convinced this is a net win.
Again, many thanks to both of you, I learned a lot here !

@neutrinoceros neutrinoceros marked this pull request as ready for review February 18, 2025 09:16
@neutrinoceros neutrinoceros merged commit 9b91589 into main Feb 18, 2025
23 checks passed
@neutrinoceros neutrinoceros deleted the bld/migrate_to_meson branch February 18, 2025 09:18
@rgommers
Copy link

Awesome 🎉

@eli-schwartz
Copy link

Oh dear... I really should have paid more attention to that readme. Sorry about that !

I don't think it's your fault, honestly. If we look at their readme...

It seems trivially obvious at least to me, that given a

cannot assume anything about the minimum intended abi3 version

event, it is wrongly designed to respond with

Instead, it defaults to the lowest known abi3 version

and needlessly causes deep confusion and potential panic and threatens to undermine the fundamental goal of having an abi checker application in the first place. The straightforward and logical design would be, instead of this:

[14:16:26] 👎 : _lib.abi3.so uses the Python 3.11 ABI, but is tagged for the Python 3.2 ABI

to do this:

[14:16:26] 🤨 : _lib.abi3.so uses the Python 3.11 ABI, but isn't tagged for a minimum ABI. Only
[14:16:26]      wheels can be tagged. Try passing --assume-minimum-abi3 "3.x"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants