Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

python.extension_module() linking with libpython when it should not #11097

Closed
dnicolodi opened this issue Nov 22, 2022 · 11 comments · Fixed by #11104
Closed

python.extension_module() linking with libpython when it should not #11097

dnicolodi opened this issue Nov 22, 2022 · 11 comments · Fixed by #11104

Comments

@dnicolodi
Copy link
Member

Since version 0.63.0 Meson automatically adds a dependency on libpython when needed compiling extension modules. To determine whether to link to libpython Meson consults distutils. On Python 3.7 on Linux, distutils says that libpython should not be linked:

>>> import distutils.core
>>> cmd = distutils.core.Distribution().get_command_obj('build_ext')
>>> cmd.ensure_finalized()
>>> cmd.get_libraries(distutils.core.Extension('dummy', []))
[]

However, the pkg-config file for python contains these linker arguments

$ pkg-config python3 --libs
-L/opt/_internal/cpython-3.7.15/lib -lpython3.7m

This code

new_deps = []
has_pydep = False
for dep in mesonlib.extract_as_list(kwargs, 'dependencies'):
if isinstance(dep, _PythonDependencyBase):
has_pydep = True
# On macOS and some Linux distros (Debian) distutils doesn't link
# extensions against libpython. We call into distutils and mirror its
# behavior. See https://github.com/mesonbuild/meson/issues/4117
if not self.link_libpython:
dep = dep.get_partial_dependency(compile_args=True)
new_deps.append(dep)
if not has_pydep:
pydep = self._dependency_method_impl({})
if not pydep.found():
raise mesonlib.MesonException('Python dependency not found')
new_deps.append(pydep)
FeatureNew.single_use('python_installation.extension_module with implicit dependency on python',
'0.63.0', self.subproject, 'use python_installation.dependency()',
self.current_node)
kwargs['dependencies'] = new_deps

deals with adding the libpython dependency as need. In the case the libpython dependency is explicitly requested, only the compile arguments are used, however, in the case when it is not explicitly requested, all the arguments are added, including the link arguments found via pkg-config. I think that only the compile arguments should be included in this case too when the extension module should not be linked with libpython.

I'll prepare a PR, but I would like to check with @eli-schwartz if he agrees on the diagnosis.

@eli-schwartz
Copy link
Member

eli-schwartz commented Nov 22, 2022

This diagnosis confuses me since we specifically check for link_libpython and remove the link arguments (keeping only the compile ones).

However, on 3.7 you are supposed to link to libpython, except on Debian...

@eli-schwartz
Copy link
Member

eli-schwartz commented Nov 22, 2022

Oh, I think I see it now.

Edit: or not? This should already work?

dnicolodi added a commit to dnicolodi/meson that referenced this issue Nov 22, 2022
Apply the information gathered through distutils also to the case when
the dependecy has not been expressed explicitly. The spurious linking
has been observed on Python 3.7 on Linux where distutils does not
instruct to link libpython but the Python's pkg-config contains linker
flags to link to libpython.

Fixes mesonbuild#11097.
@dnicolodi
Copy link
Member Author

I think the patch makes it clearer. I actually encountered the issue on the manylinux docker images, which are based on AlmaLinux. I suspect Debian may be patching the pkg-condig too.

@eli-schwartz
Copy link
Member

eli-schwartz commented Nov 22, 2022

The thing is that the patch is exactly what I thought you meant, and I don't see how that's possible to be a problem.

The internal dependency is embed: false. If you provide one we don't know what it is so we partial it.

@dnicolodi
Copy link
Member Author

dnicolodi commented Nov 22, 2022

I stepped through the thing with pdb and the linker flags are added. I'm afraid I don't know enough of the surrounding code to understand exactly why. However, I just verified that this solves the issue on Linux and a segfault on macOS.

The pkg-config definition oh the system looks like this:

# cat /opt/_internal/cpython-3.7.15/lib/pkgconfig/python3.pc
# See: man pkg-config
prefix=/opt/_internal/cpython-3.7.15
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include

Name: Python
Description: Python library
Requires:
Version: 3.7
Libs.private: -lcrypt -lpthread -ldl  -lutil
Libs: -L${libdir} -lpython3.7m
Cflags: -I${includedir}/python3.7m

Let me know if there is anything else that may be useful to better understand the root cause.

Edit: yes, I verified that this is the pkg-config picked up by Meson.

@dnicolodi
Copy link
Member Author

dnicolodi commented Nov 22, 2022

I think the bug is most likely in the discrepancy between distutils and pkg-config for Python 3.7. But fixing it in the Python seems unlikely to happen at this point. I think that dealing with it in Meson is the only option.

@dnicolodi
Copy link
Member Author

Also note that the distinction from python3.pc and python3-embed.c comes only starting with Python 3.8, so as long as Meson supports Python 3.7, we need to filter out the link flags.

@dnicolodi
Copy link
Member Author

The internal dependency is embed: false.

I had a closer look at the Meson python module code and I understood better what you mean with the above sentence. When the dependency is looked up with embed=False the python3.pc is looked up and when embed=True the python3-embed.pc is used instead. As I was writing above, up to Python 3.7 there is no python3-embed.pc thus this does not have the effect of removing the linking to libpython as per the python3.pc reported above.

dnicolodi added a commit to dnicolodi/meson that referenced this issue Nov 23, 2022
Apply the information gathered through distutils also to the case when
the dependecy has not been expressed explicitly. The spurious linking
has been observed on Python 3.7 on Linux where distutils does not
instruct to link libpython but the Python's pkg-config contains linker
flags to link to libpython.

Fixes mesonbuild#11097.
eli-schwartz added a commit to eli-schwartz/meson that referenced this issue Nov 23, 2022
The `py.dependency(embed: false)` method is supposed to consistently
provide a distutils-like `python.pc` / `python-embed.pc` interface
regardless of Python version. It handles both pkg-config and sysconfig
scraping. For the latter, we respect the value of self.link_libpython
as determined by distutils, and construct a fully custom dependency. For
the former, we blindly assume pkg-config is correct.

It isn't correct, not until Python 3.8 when embed was added. Before
then, we need to process the pkg-config dependency based on
link_libpython. We did this, but only inside the extension_module
method, which is obviously wrong.

Delete the special casing from extension_module, and handle it inside
the dependency.

Fixes mesonbuild#11097
@eli-schwartz
Copy link
Member

eli-schwartz commented Nov 23, 2022

The internal dependency is embed: false. If you provide one we don't know what it is so we partial it.

So for clarity, I looked at this last night, and I apologize, because what I said was incredibly bogus and clueless. I double and triple checked, and actually py.dependency() itself is broken with pkg-config, then crudely hacked around inside extension_module using partial_dependency.

The correct fix is: eli-schwartz@889f913

...

When I added the implicit dependency I did probably assume that embed: false actually did what it's supposed to, and didn't bother to copy the hack over. And yesterday, I assumed that embed: false actually did what it's supposed to, and the hack "must have some reason to exist, not just that py.dependency() is broken".

@dnicolodi
Copy link
Member Author

Thanks @eli-schwartz Your fix looks good to me, FWIW.

eli-schwartz added a commit to eli-schwartz/meson that referenced this issue Nov 24, 2022
The `py.dependency(embed: false)` method is supposed to consistently
provide a distutils-like `python.pc` / `python-embed.pc` interface
regardless of Python version. It handles both pkg-config and sysconfig
scraping. For the latter, we respect the value of self.link_libpython
as determined by distutils, and construct a fully custom dependency. For
the former, we blindly assume pkg-config is correct.

It isn't correct, not until Python 3.8 when embed was added. Before
then, we need to process the pkg-config dependency based on
link_libpython. We did this, but only inside the extension_module
method, which is obviously wrong.

Delete the special casing from extension_module, and handle it inside
the dependency.

Fixes mesonbuild#11097
eli-schwartz added a commit to eli-schwartz/meson that referenced this issue Nov 24, 2022
The `py.dependency(embed: false)` method is supposed to consistently
provide a distutils-like `python.pc` / `python-embed.pc` interface
regardless of Python version. It handles both pkg-config and sysconfig
scraping. For the latter, we respect the value of self.link_libpython
as determined by distutils, and construct a fully custom dependency. For
the former, we blindly assume pkg-config is correct.

It isn't correct, not until Python 3.8 when embed was added. Before
then, we need to process the pkg-config dependency based on
link_libpython. We did this, but only inside the extension_module
method, which is obviously wrong.

Delete the special casing from extension_module, and handle it inside
the dependency.

Fixes mesonbuild#11097
@eli-schwartz
Copy link
Member

Alright, PR'ed.

dnicolodi added a commit to dnicolodi/python-siphash24 that referenced this issue Nov 26, 2022
Require unreleased version of Meson to fix Python 3.7 on macOS and
Linux build with cibuildwheel, see mesonbuild/meson#11097.  Require
unreleased meson-python to enable cross compilation on macOS.
dnicolodi added a commit to dnicolodi/python-siphash24 that referenced this issue Nov 26, 2022
Require unreleased version of Meson to fix Python 3.7 on macOS and
Linux build with cibuildwheel, see mesonbuild/meson#11097.  Require
unreleased meson-python to enable cross compilation on macOS.
dnicolodi added a commit to dnicolodi/python-siphash24 that referenced this issue Nov 26, 2022
Require unreleased version of Meson to fix Python 3.7 on macOS and
Linux build with cibuildwheel, see mesonbuild/meson#11097.  Require
unreleased meson-python to enable cross compilation on macOS.
dnicolodi added a commit to dnicolodi/python-siphash24 that referenced this issue Nov 26, 2022
Require unreleased version of Meson to fix Python 3.7 on macOS and
Linux build with cibuildwheel, see mesonbuild/meson#11097.  Require
unreleased meson-python to enable cross compilation on macOS.
dnicolodi added a commit to dnicolodi/python-siphash24 that referenced this issue Nov 26, 2022
Require unreleased version of Meson to fix Python 3.7 on macOS and
Linux build with cibuildwheel, see mesonbuild/meson#11097.  Require
unreleased meson-python to enable cross compilation on macOS.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants