<div dir="ltr"><span style="font-size:12.8px">> What are Debian/Ubuntu doing in distutils so that extensions don't </span><span style="font-size:12.8px">link to libpython by default? </span><br><div><span style="font-size:12.8px"><br></span></div><div><span style="font-size:12.8px">I don't know exactly, but one way to reproduce this is simply to build the interpreter without `--enable-shared`.</span></div><div><span style="font-size:12.8px"><br></span></div><div><span style="font-size:12.8px">I don't know that their reasons are, but I presume that the Debian maintainers have a well-considered reason for this design.</span></div><div><br></div><div>The PEP 513 text currently says that it's permissible for manylinux1 wheels to link against <a href="http://libpythonX.Y.so">libpythonX.Y.so</a>. So presumably for a platform to be manylinux1-compatible, <a href="http://libpythonX.Y.so">libpythonX.Y.so</a> should be available. I guess my preference would be for pip to simply check as to whether or not <a href="http://libpythonX.Y.so">libpythonX.Y.so</a> is available in its platform detection code (pypa/pip/pull/3446).</div><div><br></div><div>Because Debian/Ubuntu is such a big target, instead of just bailing out and forcing the user to install the sdist from PyPI (which is going to fail, because Debian installations that lack <a href="http://libpythonX.Y.so">libpythonX.Y.so</a> also lack Python.h), I would be +1 for adding some kind of message for this case that says, "maybe you should `sudo apt-get install python-dev` to get these fancy new wheels rolling."</div><div><br></div><div>-Robert</div></div><div class="gmail_extra"><br><div class="gmail_quote">On Sun, Feb 7, 2016 at 12:01 AM, Nathaniel Smith <span dir="ltr"><<a href="mailto:njs@pobox.com" target="_blank">njs@pobox.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">So we found another variation between how different distros build<br>
CPython [1], and I'm very confused.<br>
<br>
Fedora (for example) turns out to work the way I naively expected:<br>
taking py27 as our example, they have:<br>
- libpython2.7.so.1.0 contains the actual python runtime<br>
- /usr/bin/python2.7 is a tiny (~7 KiB) executable that links to<br>
libpython2.7.so.1 to do the actual work; the main python package<br>
depends on the libpython package<br>
- python extension module packages depend on the libpython package,<br>
and contain extension modules linked against libpython2.7.so.1<br>
- python extension modules compiled locally get linked against<br>
libpython2.7.so.1 by default<br>
<br>
Debian/Ubuntu do things differently:<br>
- libpython2.7.so.1.0 exists and contains the full python runtime, but<br>
is not installed by default<br>
- /usr/bin/python2.7 *also* contains a *second* copy of the full<br>
python runtime; there is no dependency relationship between these, and<br>
you don't even get libpython2.7.so.1.0 installed unless you explicitly<br>
request it or it gets pulled in through some other dependency<br>
- most python extension module packages do *not* depend on the<br>
libpython2.7 package, and contain extension modules that are *not*<br>
linked against libpython2.7.so.1.0 (but there are exceptions!)<br>
- python extension modules compiled locally do *not* get linked<br>
against libpython2.7.so.1 by default.<br>
<br>
The only things that seem to link against libpython2.7.so.1.0 in debian are:<br>
a) other packages that embed python (e.g. gnucash, paraview, perf, ...)<br>
b) some minority of python packages (e.g. the PySide/QtOpenGL.so<br>
module is one that I found that directly links to libpython2.7.so.1.0)<br>
<br>
I guess that the reason this works is that according to ELF linking<br>
rules, the symbols defined in the main executable, or in the<br>
transitive closure of the libraries that the main executable is linked<br>
to via DT_NEEDED entries, are all injected into the global scope of<br>
any dlopen'ed libraries.<br>
<br>
Uh, let me try saying that again.<br>
<br>
When you dlopen() a library -- like, for example, a python extension<br>
module -- then the extension automatically gets access to any symbols<br>
that are exported from either (a) the main executable itself, or (b)<br>
any of the libraries that are listed if you run 'ldd <the main<br>
executable>'. It also gets access to any symbols that are exported by<br>
itself, or any of the libraries listed if you run 'ldd <the dlopen'ed<br>
library>'. OTOH it does *not* get access to any symbols exported by<br>
other libraries that get dlopen'ed -- each dlopen specifically creates<br>
its own "scope".<br>
<br>
So the reason this works is that Debian's /usr/bin/python2.7 itself<br>
exports all the standard Python C ABI symbols, so any extension module<br>
that it loads automatically get access to the CPython ABI, even if<br>
they don't explicitly link to it. And programs like gnucash are linked<br>
directly to libpython2.7.so.1, so they also end up exporting the<br>
CPython ABI to any libraries that they dlopen.<br>
<br>
But, it seems to me that there are two problems with the Debian/Ubuntu<br>
way of doing things:<br>
1) it's rather wasteful of space, since there are two complete<br>
independent copies of the whole CPython runtime (one inside<br>
/usr/bin/python2.7, the other inside libpython2.7.so.1).<br>
2) if I ever embed cpython by doing dlopen("libpython2.7.so.1"), or<br>
dlopen("some_plugin_library_linked_to_libpython.so"), then the<br>
embedded cpython will not be able to load python extensions that are<br>
compiled in the Debian-style (but will be able to load python<br>
extensions compiled in the Fedora-style), because the dlopen() the<br>
loaded the python runtime and the dlopen() that loads the extension<br>
module create two different scopes that can't see each other's<br>
symbols. [I'm pretty sure this is right, but linking is arcane and<br>
probably I should write some tests to double check.]<br>
<br>
I guess (2) might be why some of Debian's extension modules do link to<br>
libpython2.7.so.1 directly? Or maybe that's just a bug?<br>
<br>
Is there any positive reason in favor of the Debian style approach?<br>
Clearly someone put some work into setting things up this way, so<br>
there must be some motivation, but I'm not sure what it is?<br>
<br>
The immediate problem for us is that if a manylinux1 wheel links to<br>
<a href="http://libpythonX.Y.so" rel="noreferrer" target="_blank">libpythonX.Y.so</a> (Fedora-style), and then it gets run on a Debian<br>
system that doesn't have <a href="http://libpythonX.Y.so" rel="noreferrer" target="_blank">libpythonX.Y.so</a> installed, it will crash<br>
with:<br>
<br>
ImportError: libpython2.7.so.1.0: cannot open shared object file: No<br>
such file or directory<br>
<br>
Maybe this is okay and the solution is to tell people that they need<br>
to 'apt install libpython2.7'. In a sense this isn't even a<br>
regression, because every system that is capable of installing a<br>
binary extension from an sdist has python2.7-dev installed, which<br>
depends on libpython2.7 --> therefore every system that used to be<br>
able to do 'pip install somebinary' with sdists will still be able to<br>
do it with manylinux1 builds.<br>
<br>
The alternative is to declare that manylinux1 extensions should not<br>
link to libpython. This should I believe work fine on both<br>
Debian-style and Fedora-style installations -- though the PySide<br>
example, and the theoretical issue with embedding python through<br>
dlopen, both give me some pause.<br>
<br>
Two more questions:<br>
- What are Debian/Ubuntu doing in distutils so that extensions don't<br>
link to libpython by default? If we do go with the option of saying<br>
that manylinux extensions shouldn't link to libpython, then that's<br>
something auditwheel *can* fix up, but it'd be even nicer if we could<br>
set up the docker image to get it right in the first place.<br>
<br>
- Can/should Debian/Ubuntu switch to the Fedora model? Obviously it<br>
would take quite some time before a generic platform like manylinux<br>
could assume that this had happened, but it does seem better to me...?<br>
And if it's going to happen at all it might be nice to get the switch<br>
into 16.04 LTS? Of course that's probably ambitious, even if I'm not<br>
missing some reason why the Debian/Ubuntu model is actually<br>
advantageous.<br>
<br>
-n<br>
<br>
[1] <a href="https://github.com/pypa/manylinux/issues/30" rel="noreferrer" target="_blank">https://github.com/pypa/manylinux/issues/30</a><br>
<span class="HOEnZb"><font color="#888888"><br>
--<br>
Nathaniel J. Smith -- <a href="https://vorpus.org" rel="noreferrer" target="_blank">https://vorpus.org</a><br>
_______________________________________________<br>
Distutils-SIG maillist - <a href="mailto:Distutils-SIG@python.org">Distutils-SIG@python.org</a><br>
<a href="https://mail.python.org/mailman/listinfo/distutils-sig" rel="noreferrer" target="_blank">https://mail.python.org/mailman/listinfo/distutils-sig</a><br>
</font></span></blockquote></div><br><br clear="all"><div><br></div>-- <br><div class="gmail_signature"><div dir="ltr"><div>-Robert</div></div></div>
</div>