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 :: pipy install on ubuntu 20.04 does not find the libs #1668

Closed
adriansev opened this issue Apr 4, 2022 · 32 comments · Fixed by #1672
Closed

python :: pipy install on ubuntu 20.04 does not find the libs #1668

adriansev opened this issue Apr 4, 2022 · 32 comments · Fixed by #1672

Comments

@adriansev
Copy link
Contributor

Hi! @matthewfeickert sorry to bother you again :) any idea why this still does not work?
AFAIR this was solved, but i just hit this up on a ubuntu 20.04 .. the output is here

@matthewfeickert
Copy link
Contributor

matthewfeickert commented Apr 4, 2022

I would say two things (in decreasing order of relevance):

  1. Debian is not very good about discovery, so if you're going to put things in locations (e.g. /home/adrian/.local/lib) that are not already on its default paths, and aren't using virtual environments, then you are responsible for telling Debian about this

- name: Verify Python bindings
run: |
export LD_LIBRARY_PATH="/usr/local/lib:${LD_LIBRARY_PATH}"
export PYTHON_VERSION_MINOR=$(_tmp=$(find /usr/local/lib/ -type d -iname "python*") && echo ${_tmp: -3}; unset _tmp)
export PYTHONPATH="/usr/local/lib/python${PYTHON_VERSION_MINOR}/site-packages:${PYTHONPATH}"
python3 -m pip list
python3 -m pip show xrootd
python3 -c 'import XRootD; print(XRootD)'
python3 -c 'import pyxrootd; print(pyxrootd)'
python3 -c 'from XRootD import client; print(client.FileSystem("root://someserver:1094"))'

(maybe in .profile (what I do on my machine to get CUDA libraries working) or something that will get loaded in a more reasonable time frame for CI)

  1. I don't think this is affecting you in any way, but you are lacking modern setuptools (and maybe wheel too, idk) in this environment as you're still falling back to the deprecated legacy build and getting an egg and not a wheel. Is there a reason for this? While the CI is building with the latest setuptools

python3 -m pip --no-cache-dir install --upgrade pip setuptools wheel

I would assume that the default version of setuptools that is packaged should still be sufficient to build the wheel.

$ docker run --rm -ti ubuntu:20.04 /bin/bash
root@be875f1dd958:/# apt update -y && apt install -y python3 python3-dev python3-venv python3-pip
root@be875f1dd958:/# python3 -m pip list
Package    Version
---------- -------
pip        20.0.2 
setuptools 45.2.0 
wheel      0.34.2 
root@be875f1dd958:/#

What version of setuptools do you have in this build?

@adriansev
Copy link
Contributor Author

hi @matthewfeickert ! so, first i made sure that i have:

[Monday 04.04.22 19:14] adrian@ALICE2 : ~  $ 
python3 -m pip install --no-cache-dir --user --upgrade pip setuptools wheel
/usr/lib/python3/dist-packages/secretstorage/dhcrypto.py:15: CryptographyDeprecationWarning: int_from_bytes is deprecated, use int.from_bytes instead
  from cryptography.utils import int_from_bytes
/usr/lib/python3/dist-packages/secretstorage/util.py:19: CryptographyDeprecationWarning: int_from_bytes is deprecated, use int.from_bytes instead
  from cryptography.utils import int_from_bytes
Requirement already satisfied: pip in ./.local/lib/python3.8/site-packages (22.0.4)
Requirement already satisfied: setuptools in ./.local/lib/python3.8/site-packages (62.0.0)
Requirement already satisfied: wheel in ./.local/lib/python3.8/site-packages (0.37.1)

also

root@ALICE2:~# apt install -y python3 python3-dev python3-venv python3-pip
Reading package lists... Done
Building dependency tree       
Reading state information... Done
python3 is already the newest version (3.8.2-0ubuntu2).
python3 set to manually installed.
python3-dev is already the newest version (3.8.2-0ubuntu2).
python3-venv is already the newest version (3.8.2-0ubuntu2).
python3-pip is already the newest version (20.0.2-5ubuntu1.6)

so, answering by points:

  1. so, would it means that on Debian derivatives, binary python packages are not functional unless one, after pip install --user modify by hand the environment?
    how the others (rhel/fedora) can make it work and cannot be replicated by the pip install process?
  2. i see that i already answered above :)

Thanks a lot!

@matthewfeickert
Copy link
Contributor

matthewfeickert commented Apr 4, 2022

  1. so, would it means that on Debian derivatives, binary python packages are not functional unless one, after pip install --user modify by hand the environment?
    how the others (rhel/fedora) can make it work and cannot be replicated by the pip install process?

This has a complicated answer, that I don't know if I can give a good summary to beyond showing it (as I think this also gets down into the fundamental differences about where Debian and RHEL place things), but this comes down to how different OS define and treat the user site. I would also highly recommend reading the pip docs segment on user installs which highlights this further.

There is nothing special about --user that should tell the entire OS that it should care about that path now if it didn't already before. The user is responsible for making the OS know about it.

In general I personally recommend avoiding --user installs and instead just setting up a default virtual environment (e.g. create a virtual environment and then activate it in your shell's profile or in a more specific case add its bin to PATH so it will get activated by default). It also makes it far easier to reason about what is actually happening as you can be assured that you're using the Python you think you are.

  1. i see that i already answered above :)

Hm. This is strange given that you have sufficient versions, yet they are not being found. My suggestion is that you replicate this error in a Docker build in ubuntu:20.04 or in an interactive session in ubuntu:20.04 (maybe where you are not root at pip install time so that the --user flags will actually matter?). This also gives an easy way to reason about the environment.

@agoose77
Copy link

agoose77 commented Apr 4, 2022

It's strange that you have pip installed into your user site-packages (./.local/lib/python3.8/site-packages (22.0.4)).

Ah, I see that you did an --upgrade to force the installation.

I suspect a lot of these problems stem from installing into the user site rather than using a virtual-env. Once you break your environment, it can be tricky to fix it by hand (vs just wiping and starting over).

Also, you generally do not want to update your system-managed packages (like python3-pip etc) via pip.

Out of curiosity, what does python3 -m site output? What do you see after running ls ~/.local/lib/python3.8/site-packages/pyxrootd/lib*? The pyxrootd dependency has a client.XXX.so shared library that looks for the libXrdCl shared lib. For some reason, it can't find it. On a Centos7 container, the installed package correctly sets RPATH on client.so:

$  objdump -x /home/user/.local/lib/python3.6/site-packages/pyxrootd/client.cpython-36m-x86_64-linux-gnu.so | grep RPATH
  RPATH                $ORIGIN/lib64

@adriansev
Copy link
Contributor Author

@matthewfeickert @agoose77 thanks a lot for all the info and taking time to look over this! i never used/had to use virtual-env on rhel family as i did not needed and also this is my first encounter with debian family (beside googling "how to install packages on ubuntu" and install some packages on the machines of my colleagues)
So, i will see how can i avoid this default venv (personally i fail to see difference between --user pip install and an always activated default venv) moreover if one is broken (--user) then IMHO it should not exist and the option should have be removed from pip ..
also on the subject of updating pip, this is actually something that pip requires from me (at least on rhel family) when is older than upstream.. it will issue a warning that is older and that i should update it in my user (below output on centos 7)

Installing collected packages: websockets
Successfully installed websockets-9.1
You are using pip version 10.0.1, however version 21.3.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.

@agoose77 so, the answers:

  1. the site output
[Tuesday 05.04.22 11:13] adrian@ALICE2 : ~  $ 
python3 -m site
sys.path = [
    '/home/adrian',
    '/usr/lib/python38.zip',
    '/usr/lib/python3.8',
    '/usr/lib/python3.8/lib-dynload',
    '/home/adrian/.local/lib/python3.8/site-packages',
    '/usr/local/lib/python3.8/dist-packages',
    '/usr/lib/python3/dist-packages',
]
USER_BASE: '/home/adrian/.local' (exists)
USER_SITE: '/home/adrian/.local/lib/python3.8/site-packages' (exists)
ENABLE_USER_SITE: True
ls ~/.local/lib/python3.8/site-packages/pyxrootd/lib*
libXrdAppUtils.so         libXrdCl.so          libXrdCryptoLite.so.2      libXrdCrypto.so.2.0.0  libXrdSecgsiAUTHZVO-5.so  libXrdSecpwd-5.so   libXrdUtils.so.3      libXrdXml.so.3.0.0
libXrdAppUtils.so.2       libXrdCl.so.3        libXrdCryptoLite.so.2.0.0  libXrdCryptossl-5.so   libXrdSecgsiGMAPDN-5.so   libXrdSecsss-5.so   libXrdUtils.so.3.0.0
libXrdAppUtils.so.2.0.0   libXrdCl.so.3.0.0    libXrdCrypto.so            libXrdSec-5.so         libXrdSeckrb5-5.so        libXrdSecunix-5.so  libXrdXml.so
libXrdClProxyPlugin-5.so  libXrdCryptoLite.so  libXrdCrypto.so.2          libXrdSecgsi-5.so      libXrdSecProt-5.so        libXrdUtils.so      libXrdXml.so.3
  1. there is no RPATH and notice the location (as opposed to centos 7)
objdump -x ~/.local/lib/python3.8/site-packages/xrootd-5.4.2-py3.8-linux-x86_64.egg/pyxrootd/client.cpython-38-x86_64-linux-gnu.so | grep RPATH

BUT OTOH the situation is identical on fedora 35 and everything just works:

[test@hal ~]$ find ~/.local/lib/python3.10/site-packages -name "client.cpython*"
/home/test/.local/lib/python3.10/site-packages/xrootd-5.4.2-py3.10-linux-x86_64.egg/pyxrootd/client.cpython-310-x86_64-linux-gnu.so
/home/test/.local/lib/python3.10/site-packages/xrootd-5.4.2-py3.10-linux-x86_64.egg/pyxrootd/__pycache__/client.cpython-310.pyc

so, at this moment i just need to establish if it is my problem that what works on centos/fedora does not work on ubuntu, or is a packaging problem (i mean, if someone do pip install xrootd at least it should receive a message that: on ubuntu this will not work, install only in a virtual env or just refuse to install with the reason of virtual env not detected..)
Thanks a lot!!

@matthewfeickert
Copy link
Contributor

matthewfeickert commented Apr 5, 2022

@adriansev (It is late for me so I will respond to the specifics about package management later, but...) looking at your problem again I think I now understand two things:

  1. You are trying to install the Python bindings from the sdist on PyPI.
  2. You are missing the Debian package pkg-config.

Am I correct on both of those things?

If so, then take a look at the following building Dockerfile that is Debian based

Dockerfile:

N.B.: the addition of pkg-config compared to the CI job apt-get list!

ARG BASE_IMAGE=debian:bullseye
FROM ${BASE_IMAGE} as base

SHELL [ "/bin/bash", "-c" ]

ENV PATH=/usr/local/venv/bin:"${PATH}"
RUN apt-get update && \
    DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
        g++ \
        git \
        cmake \
        uuid-dev \
        dpkg-dev \
        libssl-dev \
        libx11-dev \
        libxml2-dev \
        libkrb5-dev \
        libgsl0-dev \
        pkg-config \
        python3 \
        python3-pip \
        python3-venv \
        python3-dev && \
    apt-get autoclean -y && \
    python3 -m pip --no-cache-dir install --upgrade pip setuptools wheel && \
    python3 -m pip list

WORKDIR /code/
ENV LD_LIBRARY_PATH="/usr/local/lib:${LD_LIBRARY_PATH}"
# Install XRootD v5.4.2 with no Python bindings so that libraries exist
RUN git clone --depth 1 https://github.com/xrootd/xrootd \
        --branch v5.4.2 \
        --single-branch && \
    cmake \
        -DCMAKE_INSTALL_PREFIX=/usr/local/ \
        -S xrootd \
        -B build && \
    cmake build -LH && \
    cmake \
        --build build \
        --clean-first \
        --parallel $(($(nproc) - 1)) && \
    cmake --build build --target install && \
    command -v xrootd && \
    command -v xrdcp && \
    xrdcp --version

WORKDIR /
# Show --user even though I don't recommend it just to show it works
RUN python3 -m pip --no-cache-dir install --user --upgrade pip setuptools wheel && \
    python3 -m pip install --user --verbose 'xrootd==5.4.2' && \
    python3 -m pip list

RUN python3 -m pip list && \
    python3 -m pip show xrootd && \
    python3 -c 'import XRootD; print(XRootD)' && \
    python3 -c 'import pyxrootd; print(pyxrootd)' && \
    python3 -c 'from XRootD import client; print(client.FileSystem("root://someserver:1094"))'

WORKDIR /

CMD ["/bin/bash"]

If you build it with

docker build . -f Dockerfile -t xrootd/xrootd:issue-1668

then

$ docker run --rm -ti xrootd/xrootd:issue-1668 
root@e864a61b3442:/# python3 -m pip list
Package    Version
---------- -------
pip        22.0.4
setuptools 62.0.0
wheel      0.37.1
xrootd     5.4.2
root@e864a61b3442:/# python3 -m pip show xrootd
Name: xrootd
Version: 5.4.2
Summary: XRootD Python bindings
Home-page: http://xrootd.org
Author: XRootD Developers
Author-email: xrootd-dev@slac.stanford.edu
License: LGPLv3+
Location: /root/.local/lib/python3.9/site-packages/xrootd-5.4.2-py3.9-linux-x86_64.egg
Requires: 
Required-by:
root@e864a61b3442:/# python3 -c 'from XRootD import client; print(client.FileSystem("root://someserver:1094"))'
<XRootD.client.filesystem.FileSystem object at 0x7f8c7fa9af40>
root@e864a61b3442:/# 

@agoose77
Copy link

agoose77 commented Apr 5, 2022

Virtual Environments

personally i fail to see difference between --user pip install and an always activated default venv

There are three main benefits to virtual environments

Compatibility

The difference is more pronounced when you have multiple environments. Even if you're only ever using one environment, though, there are similar benefits. If you have Python tools (e.g. poetry/pdm, tox/nox, black etc) installed into the same environment, you eventually will run into the scenario where you cannot update your libraries because one of the packages imposes an upper-bound on the version Even though upper limits are not best practice in Python, they still exist in the wild, and can cause these problems. Worse, you can end up with two packages with incompatible version constraints (e.g. scipy wants a newer numpy than some other library). Virtual/Conda environments avoid this by allowing you to separate distinct packages into their own environments.

Related to this is pipx, which lets you install Python "applications" (libraries that you're not really expected to import from Python, e.g. pip or black) in their own separate environments, but launch them without activating the env.

Safety

Sometimes you can break your environment, especially when using pip directly instead of a project manager like pdm or poetry. Virtual environments make this less problematic - you can easily delete and recreate the environment, and only have to re-install those packages which were installed in that particular environment.

Python Version

With Conda environments (or virtualenvs if you use something like pyenv), you can install a different version of Python to that of your system. This is generally useful.

pip --user deprecation

moreover if one is broken (--user) then IMHO it should not exist and the option should have be removed from pip

Well, there is gradual talk of doing this. It's important to remember that Python is very old, and a lot has happened in the packaging space since pip was first developed. Environment variables like PIP_REQUIRE_VIRTUALENV are useful to force the user to use virtual-envs.

Upgrading pip

will issue a warning that is older and that i should update it in my user

You should try to use the latest pip, but if you can update the system package (e.g. via apt) then you should try that first. Otherwise, using a virtualenv is preferred over installing it in --user, but --user is also fine :)

Troubleshooting

In general, I'd highly recommend looking into Docker/Podman if you're not yet familiar, because it makes it a lot easier to isolate the causes of these sorts of problems. By creating a new Ubuntu 20.04 container, you can confirm whether it works "out of the box" (i.e. there's a problem with your current system configuration) or whether something bizarre is happening for a particular OS. In general, Python should behave properly across all major platforms; if you're using relatively up-to-date versions of the core packages like pip and setuptools, any errors are usually caused by something specific to your current system configuration.

@adriansev
Copy link
Contributor Author

@matthewfeickert thanks for answering at such a hour! but this is just a leisure conversation, we can discuss this whenever :)

  1. actually i still do not know the difference between sdist and wheels and so on (well, i know what they are, but the rules of of pip does, or building rules and so on are way above my python knowledge) so i can only tell you that i tried to do pip install --user --upgrade xrootd
  2. actually seems to be present:
root@ALICE2:~# apt list --installed 2>/dev/null | grep pkg-config
pkg-config/focal,now 0.29.1-0ubuntu4 amd64 [installed]

also, it seems that i have all the required dependencies used by the Dockerfile
Thanks a lot!

@agoose77
Copy link

agoose77 commented Apr 5, 2022

OK, I took a deeper look at this, and I've realised that the build chain is a lot more complicated than I expected. The sdist on PyPI is a "pseudo package" that has its own setup.py, which launches a separate build step for the bindings.

I first tested on Scientific Linux (Python 3.6), and it seems that there RPATH is set, whereas on Debian I'm only seeing RUNPATH, which has a lower precedence (but functionally still works). What is strange to me initially is that in the Debian image, it has @origin/lib which does not exist in the installed layout.

The installed layouts on Ubuntu is:

tree /home/user/.local/lib/python3.8/site-packages/
/home/user/.local/lib/python3.8/site-packages/
|-- easy-install.pth
|-- pyxrootd
|   |-- include
|   |   `-- xrootd
|   |       |-- XProtocol
|   |       |   |-- XProtocol.hh
|   |       |   `-- XPtypes.hh
|   |       |-- Xrd
|   |       |   |-- XrdBuffer.hh
|   |       |   |-- XrdJob.hh
|   |       |   |-- XrdLink.hh
|   |       |   |-- XrdLinkMatch.hh
|   |       |   |-- XrdProtocol.hh
|   |       |   |-- XrdScheduler.hh
|   |       |   `-- XrdTcpMonPin.hh
|   |       |-- XrdCl
|   |       |   |-- XrdClAnyObject.hh
|   |       |   |-- XrdClBuffer.hh
|   |       |   |-- XrdClConstants.hh
|   |       |   |-- XrdClCopyProcess.hh
|   |       |   |-- XrdClDefaultEnv.hh
|   |       |   |-- XrdClEnv.hh
|   |       |   |-- XrdClFile.hh
|   |       |   |-- XrdClFileSystem.hh
|   |       |   |-- XrdClFileSystemUtils.hh
|   |       |   |-- XrdClLog.hh
|   |       |   |-- XrdClMonitor.hh
|   |       |   |-- XrdClOptional.hh
|   |       |   |-- XrdClPlugInInterface.hh
|   |       |   |-- XrdClPlugInManager.hh
|   |       |   |-- XrdClPropertyList.hh
|   |       |   |-- XrdClStatus.hh
|   |       |   |-- XrdClURL.hh
|   |       |   `-- XrdClXRootDResponses.hh
|   |       |-- XrdHttp
|   |       |   `-- XrdHttpSecXtractor.hh
|   |       |-- XrdNet
|   |       |   |-- XrdNet.hh
|   |       |   |-- XrdNetAddr.hh
|   |       |   |-- XrdNetAddrInfo.hh
|   |       |   |-- XrdNetCmsNotify.hh
|   |       |   |-- XrdNetConnect.hh
|   |       |   |-- XrdNetOpts.hh
|   |       |   |-- XrdNetSockAddr.hh
|   |       |   |-- XrdNetSocket.hh
|   |       |   `-- XrdNetUtils.hh
|   |       |-- XrdOuc
|   |       |   |-- XrdOucBuffer.hh
|   |       |   |-- XrdOucCRC.hh
|   |       |   |-- XrdOucCacheCM.hh
|   |       |   |-- XrdOucCacheStats.hh
|   |       |   |-- XrdOucCallBack.hh
|   |       |   |-- XrdOucChain.hh
|   |       |   |-- XrdOucCompiler.hh
|   |       |   |-- XrdOucDLlist.hh
|   |       |   |-- XrdOucEnum.hh
|   |       |   |-- XrdOucEnv.hh
|   |       |   |-- XrdOucErrInfo.hh
|   |       |   |-- XrdOucGMap.hh
|   |       |   |-- XrdOucHash.hh
|   |       |   |-- XrdOucHash.icc
|   |       |   |-- XrdOucIOVec.hh
|   |       |   |-- XrdOucLock.hh
|   |       |   |-- XrdOucName2Name.hh
|   |       |   |-- XrdOucPinObject.hh
|   |       |   |-- XrdOucPinPath.hh
|   |       |   |-- XrdOucRash.hh
|   |       |   |-- XrdOucRash.icc
|   |       |   |-- XrdOucSFVec.hh
|   |       |   |-- XrdOucStream.hh
|   |       |   |-- XrdOucString.hh
|   |       |   |-- XrdOucTList.hh
|   |       |   |-- XrdOucTable.hh
|   |       |   |-- XrdOucTokenizer.hh
|   |       |   |-- XrdOucTrace.hh
|   |       |   |-- XrdOucUtils.hh
|   |       |   `-- XrdOuca2x.hh
|   |       |-- XrdSec
|   |       |   |-- XrdSecAttr.hh
|   |       |   |-- XrdSecEntity.hh
|   |       |   |-- XrdSecEntityAttr.hh
|   |       |   |-- XrdSecEntityPin.hh
|   |       |   `-- XrdSecInterface.hh
|   |       |-- XrdSys
|   |       |   |-- XrdSysAtomics.hh
|   |       |   |-- XrdSysError.hh
|   |       |   |-- XrdSysFD.hh
|   |       |   |-- XrdSysHeaders.hh
|   |       |   |-- XrdSysLogPI.hh
|   |       |   |-- XrdSysLogger.hh
|   |       |   |-- XrdSysPageSize.hh
|   |       |   |-- XrdSysPlatform.hh
|   |       |   |-- XrdSysPlugin.hh
|   |       |   |-- XrdSysPthread.hh
|   |       |   |-- XrdSysSemWait.hh
|   |       |   |-- XrdSysTimer.hh
|   |       |   |-- XrdSysXAttr.hh
|   |       |   `-- XrdSysXSLock.hh
|   |       |-- XrdVersion.hh
|   |       |-- XrdXml
|   |       |   `-- XrdXmlReader.hh
|   |       |-- XrdXrootd
|   |       |   |-- XrdXrootdBridge.hh
|   |       |   |-- XrdXrootdGStream.hh
|   |       |   `-- XrdXrootdMonData.hh
|   |       `-- private
|   |           |-- Xrd
|   |           |   `-- XrdPoll.hh
|   |           |-- XrdCl
|   |           |   |-- XrdClArg.hh
|   |           |   |-- XrdClCtx.hh
|   |           |   |-- XrdClFileOperations.hh
|   |           |   |-- XrdClFileSystemOperations.hh
|   |           |   |-- XrdClFinalOperation.hh
|   |           |   |-- XrdClFwd.hh
|   |           |   |-- XrdClMessage.hh
|   |           |   |-- XrdClOperationHandlers.hh
|   |           |   |-- XrdClOperationTimeout.hh
|   |           |   |-- XrdClOperations.hh
|   |           |   |-- XrdClParallelOperation.hh
|   |           |   |-- XrdClPostMaster.hh
|   |           |   |-- XrdClPostMasterInterfaces.hh
|   |           |   |-- XrdClResponseJob.hh
|   |           |   |-- XrdClTransportManager.hh
|   |           |   |-- XrdClZipArchive.hh
|   |           |   |-- XrdClZipCache.hh
|   |           |   `-- XrdClZipOperations.hh
|   |           |-- XrdNet
|   |           |   |-- XrdNetBuffer.hh
|   |           |   |-- XrdNetIF.hh
|   |           |   `-- XrdNetPeer.hh
|   |           |-- XrdOuc
|   |           |   |-- XrdOucExport.hh
|   |           |   |-- XrdOucGatherConf.hh
|   |           |   |-- XrdOucN2NLoader.hh
|   |           |   `-- XrdOucPList.hh
|   |           |-- XrdPosix
|   |           |   `-- XrdPosixMap.hh
|   |           |-- XrdSecsss
|   |           |   `-- XrdSecsssID.hh
|   |           |-- XrdSys
|   |           |   `-- XrdSysPriv.hh
|   |           `-- XrdZip
|   |               |-- XrdZipCDFH.hh
|   |               |-- XrdZipEOCD.hh
|   |               |-- XrdZipExtra.hh
|   |               |-- XrdZipLFH.hh
|   |               |-- XrdZipUtils.hh
|   |               |-- XrdZipZIP64EOCD.hh
|   |               `-- XrdZipZIP64EOCDL.hh
|   |-- lib
|   |   |-- libXrdAppUtils.so -> libXrdAppUtils.so.2
|   |   |-- libXrdAppUtils.so.2 -> libXrdAppUtils.so.2.0.0
|   |   |-- libXrdAppUtils.so.2.0.0
|   |   |-- libXrdCl.so -> libXrdCl.so.3
|   |   |-- libXrdCl.so.3 -> libXrdCl.so.3.0.0
|   |   |-- libXrdCl.so.3.0.0
|   |   |-- libXrdClProxyPlugin-5.so
|   |   |-- libXrdCrypto.so -> libXrdCrypto.so.2
|   |   |-- libXrdCrypto.so.2 -> libXrdCrypto.so.2.0.0
|   |   |-- libXrdCrypto.so.2.0.0
|   |   |-- libXrdCryptoLite.so -> libXrdCryptoLite.so.2
|   |   |-- libXrdCryptoLite.so.2 -> libXrdCryptoLite.so.2.0.0
|   |   |-- libXrdCryptoLite.so.2.0.0
|   |   |-- libXrdCryptossl-5.so
|   |   |-- libXrdSec-5.so
|   |   |-- libXrdSecProt-5.so
|   |   |-- libXrdSecgsi-5.so
|   |   |-- libXrdSecgsiAUTHZVO-5.so
|   |   |-- libXrdSecgsiGMAPDN-5.so
|   |   |-- libXrdSeckrb5-5.so
|   |   |-- libXrdSecpwd-5.so
|   |   |-- libXrdSecsss-5.so
|   |   |-- libXrdSecunix-5.so
|   |   |-- libXrdUtils.so -> libXrdUtils.so.3
|   |   |-- libXrdUtils.so.3 -> libXrdUtils.so.3.0.0
|   |   |-- libXrdUtils.so.3.0.0
|   |   |-- libXrdXml.so -> libXrdXml.so.3
|   |   |-- libXrdXml.so.3 -> libXrdXml.so.3.0.0
|   |   `-- libXrdXml.so.3.0.0
|   `-- share
|       `-- man
|           |-- man1
|           |   `-- xrdadler32.1
|           `-- man8
|               `-- mpxstats.8
`-- xrootd-5.4.2-py3.8-linux-x86_64.egg
    |-- EGG-INFO
    |   |-- PKG-INFO
    |   |-- SOURCES.txt
    |   |-- dependency_links.txt
    |   |-- native_libs.txt
    |   |-- not-zip-safe
    |   `-- top_level.txt
    |-- XRootD
    |   |-- __init__.py
    |   |-- __pycache__
    |   |   `-- __init__.cpython-38.pyc
    |   `-- client
    |       |-- __init__.py
    |       |-- __pycache__
    |       |   |-- __init__.cpython-38.pyc
    |       |   |-- _version.cpython-38.pyc
    |       |   |-- copyprocess.cpython-38.pyc
    |       |   |-- env.cpython-38.pyc
    |       |   |-- file.cpython-38.pyc
    |       |   |-- filesystem.cpython-38.pyc
    |       |   |-- finalize.cpython-38.pyc
    |       |   |-- flags.cpython-38.pyc
    |       |   |-- glob_funcs.cpython-38.pyc
    |       |   |-- responses.cpython-38.pyc
    |       |   |-- url.cpython-38.pyc
    |       |   `-- utils.cpython-38.pyc
    |       |-- _version.py
    |       |-- copyprocess.py
    |       |-- env.py
    |       |-- file.py
    |       |-- filesystem.py
    |       |-- finalize.py
    |       |-- flags.py
    |       |-- glob_funcs.py
    |       |-- responses.py
    |       |-- url.py
    |       `-- utils.py
    `-- pyxrootd
        |-- __init__.py
        |-- __pycache__
        |   |-- __init__.cpython-38.pyc
        |   `-- client.cpython-38.pyc
        |-- client.cpython-38-x86_64-linux-gnu.so
        `-- client.py

@matthewfeickert knows a lot more about what is going on here than I do, but I wonder if this layout is intentional? In the Dockerfile above, nothing seems wrong, because we set $LD_LIBRARY_PATH (and also installed xrootd into system paths found by ldconfig). But, in my case (on Ubuntu) the libXrdCl.so.3 lib is not found, raising an ImportError. I assume that the user doesn't need xrootd to be installed, because the client library should contain everything they need to talk with a remote server? If so, the install here isn't behaving properly, but I'll wait on @matthewfeickert to help me understand the details further :)

So, the intermediate summary here is that the error in your log:

ImportError: libXrdCl.so.3: cannot open shared object file: No such file or directory

is probably happening because you don't have xrootd installed locally (even though I think you aren't normally required to). If this is the case, can you run

LD_LIBRARY_PATH="$(python3 -m site --user-site)/pyxrootd/lib/" python3 -c "import XRootD.client"

and report back whether it raises an ImportError?

Dockerfile
ARG BASE_IMAGE=ubuntu:20.04
FROM ${BASE_IMAGE} as base

SHELL [ "/bin/bash", "-c" ]

ENV PATH=/usr/local/venv/bin:"${PATH}"
RUN apt-get update && \
    DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
        g++ \
        git \
        cmake \
        make \
        uuid-dev \
        libssl-dev \
        pkg-config \
        python3 \
        python3-pip \
        python3-dev && \
    apt-get autoclean -y && \
    python3 -m pip --no-cache-dir install --upgrade pip setuptools wheel && \
    python3 -m pip list

WORKDIR /
RUN useradd -m user 
USER user
# Show --user even though I don't recommend it just to show it works
RUN python3 -m pip --no-cache-dir install --user --upgrade pip setuptools wheel && \
    python3 -m pip install --user --verbose 'xrootd==5.4.2' && \
    python3 -m pip list

WORKDIR /

CMD ["/bin/bash"]

@matthewfeickert
Copy link
Contributor

I assume that the user doesn't need xrootd to be installed, because the client library should contain everything they need to talk with a remote server?

Correct, as it is done as part of the CI in a gitlab-registry.cern.ch/linuxsupport/cc7-base:latest container:

python3 -m pip --verbose install xrootd/dist/xrootd-*.tar.gz
python3 -m pip list
- name: Verify Python bindings
run: |
python3 -m pip list
python3 -m pip show xrootd
python3 -c 'import XRootD; print(XRootD)'
python3 -c 'import pyxrootd; print(pyxrootd)'
python3 -c 'from XRootD import client; print(client.FileSystem("root://someserver:1094"))'

So I guess we need to understand what isn't being set right in Debian based systems.

I also didn't do a good enough job showing last night (sorry, probably better to just not post at all late at night) that the Dockerfile I shared was to demonstrate that the libraries being found is the core problem that is being faced.

OK, I took a deeper look at this, and I've realised that the build chain is a lot more complicated than I expected. The sdist on PyPI is a "pseudo package" that has its own setup.py, which launches a separate build step for the bindings.

Yeah, while I don't think I will be the person to spend time revising the build system, it would be great to see a more modern approach (e.g. pybind11 / scikit-build) here in general. A GitHub Issue for it would probably be good to have, even to mark it for posterity.

@matthewfeickert
Copy link
Contributor

matthewfeickert commented Apr 5, 2022

can you run

LD_LIBRARY_PATH="$(python3 -m site --user-site)/pyxrootd/lib/" python3 -c "import XRootD.client"

and report back whether it raises an ImportError?

@adriansev @agoose77 Yeah, this

export LD_LIBRARY_PATH="$(python3 -m site --user-site)/pyxrootd/lib:${LD_LIBRARY_PATH}"

works. In a Dockerfile you need to manually set this though as ENV can't be set by runtime variables, so here's that in ubuntu:20.04

Dockerfile:
ARG BASE_IMAGE=ubuntu:20.04
FROM ${BASE_IMAGE} as base

SHELL [ "/bin/bash", "-c" ]

ENV PATH=/usr/local/venv/bin:"${PATH}"
RUN apt-get update && \
    DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
        g++ \
        git \
        cmake \
        uuid-dev \
        dpkg-dev \
        libssl-dev \
        libx11-dev \
        libxml2-dev \
        libkrb5-dev \
        libgsl0-dev \
        pkg-config \
        python3 \
        python3-pip \
        python3-venv \
        python3-dev && \
    apt-get autoclean -y && \
    python3 -m pip --no-cache-dir install --upgrade pip setuptools wheel && \
    python3 -m pip list

WORKDIR /
# export LD_LIBRARY_PATH="$(python3 -m site --user-site)/pyxrootd/lib:${LD_LIBRARY_PATH}"
ENV LD_LIBRARY_PATH="/root/.local/lib/python3.8/site-packages/pyxrootd/lib:${LD_LIBRARY_PATH}"
# Show --user even though I don't recommend it just to show it works
RUN python3 -m pip --no-cache-dir install --user --upgrade pip setuptools wheel && \
    python3 -m pip install --user --verbose 'xrootd==5.4.2' && \
    python3 -m pip list

RUN python3 -m pip list && \
    python3 -m pip show xrootd && \
    python3 -c 'import XRootD; print(XRootD)' && \
    python3 -c 'import pyxrootd; print(pyxrootd)' && \
    python3 -c 'from XRootD import client; print(client.FileSystem("root://someserver:1094"))'

CMD ["/bin/bash"]
docker build . -f Dockerfile -t xrootd/xrootd:issue-1668
$ docker run --rm -ti xrootd/xrootd:issue-1668
root@e63fb03decdb:/# python3 -m pip list
Package    Version
---------- -------
pip        22.0.4
setuptools 60.9.3
wheel      0.37.1
xrootd     5.4.2
root@e63fb03decdb:/# python3 -m pip show xrootd 
Name: xrootd
Version: 5.4.2
Summary: XRootD Python bindings
Home-page: http://xrootd.org
Author: XRootD Developers
Author-email: xrootd-dev@slac.stanford.edu
License: LGPLv3+
Location: /root/.local/lib/python3.8/site-packages/xrootd-5.4.2-py3.8-linux-x86_64.egg
Requires: 
Required-by: 
root@e63fb03decdb:/# python3 -c 'from XRootD import client; print(client.FileSystem("root://someserver:1094"))'
<XRootD.client.filesystem.FileSystem object at 0x7fc3038794c0>
root@e63fb03decdb:/# 

So yeah, the key thing here is: ensure the libraries are on LD_LIBRARY_PATH. This could be made easier in general, but this is how things are for the time being.

Dockerfile using a virtual environment:
ARG BASE_IMAGE=ubuntu:20.04
FROM ${BASE_IMAGE} as base

SHELL [ "/bin/bash", "-c" ]

ENV PATH=/usr/local/venv/bin:"${PATH}"
RUN apt-get update && \
    DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
        g++ \
        git \
        cmake \
        uuid-dev \
        dpkg-dev \
        libssl-dev \
        libx11-dev \
        libxml2-dev \
        libkrb5-dev \
        libgsl0-dev \
        pkg-config \
        python3 \
        python3-pip \
        python3-venv \
        python3-dev && \
    apt-get autoclean -y && \
    python3 -m pip --no-cache-dir install --upgrade pip setuptools wheel && \
    python3 -m pip list

WORKDIR /

# Set PATH to pickup virtualenv by default
ENV PATH=/usr/local/venv/bin:"${PATH}"
ENV LD_LIBRARY_PATH="/usr/local/venv/lib/python3.8/site-packages/pyxrootd/lib:${LD_LIBRARY_PATH}"
RUN python3 -m venv /usr/local/venv && \
    . /usr/local/venv/bin/activate && \
    python -m pip --no-cache-dir install --upgrade pip setuptools wheel && \
    python -m pip install --verbose 'xrootd==5.4.2' && \
    python -m pip list

RUN python -m pip list && \
    python -m pip show xrootd && \
    python -c 'import XRootD; print(XRootD)' && \
    python -c 'import pyxrootd; print(pyxrootd)' && \
    python -c 'from XRootD import client; print(client.FileSystem("root://someserver:1094"))'

CMD ["/bin/bash"]

@agoose77
Copy link

agoose77 commented Apr 5, 2022

@matthewfeickert setting LD_LIBRARY_PATH is a workaround in my opinion. The shared library (client) has RPATH / RUNPATH set to find the dependency libraries, but for some reason the install layout isn't correct on Debian / Fedora (at least, not for the matrix that I tried): the RUNPATH is being set, but it points to a non-existent directory. Specifically, the pyxrootd client lib is installed into the .egg-info directory instead of the pyxrootd directory in site-packages. This looks like a bug rather than a deliberate layout decision, although I'm not familiar with the platform nuances here to know for certain!

Yeah, while I don't think I will be the person to spend time revising the build system, it would be great to see a more modern approach (e.g. pybind11 / scikit-build) here in general. A GitHub Issue for it would probably be good to have, even to mark it for posterity.

That sounds like the best way to kill two birds with one stone :)

@matthewfeickert
Copy link
Contributor

setting LD_LIBRARY_PATH is a workaround in my opinion. The shared library (client) has RPATH / RUNPATH set to find the dependency libraries, but for some reason the install layout isn't correct on Debian / Fedora (at least, not for the matrix that I tried): the RUNPATH is being set, but it points to a non-existent directory. Specifically, the pyxrootd client lib is installed into the .egg-info directory instead of the pyxrootd directory in site-packages. This looks like a bug rather than a deliberate layout decision, although I'm not familiar with the platform nuances here to know for certain!

Agreed — definitely a "get things working now" approach (though I've become far too used to having to update LD_LIBRARY_PATH for working with Debian packages in general, so this is sadly not rare). Once it is understood what it should be and what it is now I would recommend opening up a new Issue that documents all of this clearly and asks an XRootD maintainer to fix it. 👍

@matthewfeickert
Copy link
Contributor

matthewfeickert commented Apr 5, 2022

To expand on this a bit using the Dockerfile built with a virtual environment image:

$ docker run --rm -ti xrootd/xrootd:issue-1668 /bin/bash -c 'objdump -x $(find -L /usr/local/venv/lib -type f -iname "libXrdUtils.so.3")  | grep RUNPATH'
  RUNPATH              $ORIGIN

and as git grep rpath shows

# Make the RPATH relative to the python module
cfg_vars['LDSHARED'] = cfg_vars['LDSHARED'] + " -Wl,-rpath,${XRDCL_RPATH}"

where

if( PYPI_BUILD )
set(XRDCL_RPATH "$ORIGIN/${CMAKE_INSTALL_LIBDIR}")
else()
set(XRDCL_RPATH "$ORIGIN/../../..")
endif()

which was all set in 6fba5b0, so maybe @simonmichal has some general thoughts or recommendations here on how to get RUNPATH set properly if just doing an install from the sdist on PyPI, or how to fix this in the code.

edit: As @agoose77 pointed out earlier though, if you make a similar Dockerfile for CentOS7, it will work right away and have RPATH not RUNPATH

Dockerfile based on CentOS 7:
ARG BASE_IMAGE=gitlab-registry.cern.ch/linuxsupport/cc7-base:latest
FROM ${BASE_IMAGE} as base

SHELL [ "/bin/bash", "-c" ]

RUN yum update -y && \
    yum install -y \
        cmake3 \
        make \
        krb5-devel \
        libuuid-devel \
        libxml2-devel \
        openssl-devel \
        systemd-devel \
        zlib-devel \
        devtoolset-7-gcc-c++ \
        python3-devel \
        python3-setuptools \
        git \
        cppunit-devel && \
    yum clean all && \
    python3 -m pip install --no-cache-dir --upgrade pip setuptools wheel

WORKDIR /

# Set PATH to pickup virtualenv by default
ENV PATH=/usr/local/venv/bin:"${PATH}"
RUN source scl_source enable devtoolset-7 && \
    python3 -m venv /usr/local/venv && \
    . /usr/local/venv/bin/activate && \
    python -m pip --no-cache-dir install --upgrade pip setuptools wheel && \
    python -m pip install --verbose 'xrootd==5.4.2' && \
    python -m pip list

RUN python -m pip list && \
    python -m pip show xrootd && \
    python -c 'import XRootD; print(XRootD)' && \
    python -c 'import pyxrootd; print(pyxrootd)' && \
    python -c 'from XRootD import client; print(client.FileSystem("root://someserver:1094"))'

SHELL [ "/usr/bin/scl", "enable", "devtoolset-7", "/bin/bash", "-c"]
$ docker run --rm -ti xrootd/xrootd:issue-1668-centos /bin/bash -c 'objdump -x $(find -L /usr/local/venv/lib -type f -iname "libXrdUtils.so.3")  | grep PATH'
  RPATH                $ORIGIN

@adriansev
Copy link
Contributor Author

Hi @matthewfeickert @agoose77 so, (with the stressed disclaimer that i still lack a lot of knowledge about the python ecosystem) i found these bits of info:

  1. RPATH vs RUNPATH : RPATH is search before LD_LIBRARY_PATH while RUNPATH after it.
    so i would say, that for python bindings RPATH should be used and pointing to the xrootd binary libs from $(python3 -m site --user-site)/pyxrootd/lib64 lib64 on rhel family, lib on debian family. this way, the libraries will always be found and we can avoid any problems given by LD_LIBRARY_PATH (the python bindings come as a package, we should not risk any mixing of some other versions)

  2. i'm not sure how $ORIGIN is supposed to work as RUNPATH, as this is the location of an executable, but what is the executable in this case?

  3. wheel vs egg : before some fancy/new/latest building tools (pybind11/scikit-build) that would be required to work on centos7/python3.6 i think that we should establish why is egg used instead of wheel .. from what i see in packaging/wheel/publish.sh a sdist wheel should be created not an egg one .. so, anyone any idea what is going on?

Thanks a lot!

@agoose77
Copy link

agoose77 commented Apr 6, 2022

I think RUNPATH is fine - it does mean that users could override the library if they wanted to (and when installing on a host which has xrootd, maybe that's a good thing, ignoring versioning issues?).

$ORIGIN is Unix-specific, and refers to the current library for which external symbols are being resolved. In a "typical" install, setting RUNPATH=$ORIGIN/lib would allow the developer to bundle dependencies into the lib folder. The problem here is (I think) that the extension module (client) is placed in the wrong location with respect to its RUNPATH. Of course it could be that the RUNPATH is wrong, but I'm not so sure. On Scientific Linux the layout on disk is what I expect, the RPATH points to the correct place ( RPATH $ORIGIN/lib64) and given the legacy build chain I am inclined to assume that the issue lies there rather than in a Python packaging platform-dependency, but I could be wrong!

tree of site-packages
/home/user/.local/lib/python3.6/site-packages
|-- XRootD
|   |-- __init__.py
|   |-- __pycache__
|   |   `-- __init__.cpython-36.pyc
|   `-- client
|       |-- __init__.py
|       |-- __pycache__
|       |   |-- __init__.cpython-36.pyc
|       |   |-- _version.cpython-36.pyc
|       |   |-- copyprocess.cpython-36.pyc
|       |   |-- env.cpython-36.pyc
|       |   |-- file.cpython-36.pyc
|       |   |-- filesystem.cpython-36.pyc
|       |   |-- finalize.cpython-36.pyc
|       |   |-- flags.cpython-36.pyc
|       |   |-- glob_funcs.cpython-36.pyc
|       |   |-- responses.cpython-36.pyc
|       |   |-- url.cpython-36.pyc
|       |   `-- utils.cpython-36.pyc
|       |-- _version.py
|       |-- copyprocess.py
|       |-- env.py
|       |-- file.py
|       |-- filesystem.py
|       |-- finalize.py
|       |-- flags.py
|       |-- glob_funcs.py
|       |-- responses.py
|       |-- url.py
|       `-- utils.py
|-- pyxrootd
|   |-- __init__.py
|   |-- __pycache__
|   |   `-- __init__.cpython-36.pyc
|   |-- client.cpython-36m-x86_64-linux-gnu.so
|   |-- include
|   |   `-- xrootd
|   |       |-- XProtocol
|   |       |   |-- XProtocol.hh
|   |       |   `-- XPtypes.hh
|   |       |-- Xrd
|   |       |   |-- XrdBuffer.hh
|   |       |   |-- XrdJob.hh
|   |       |   |-- XrdLink.hh
|   |       |   |-- XrdLinkMatch.hh
|   |       |   |-- XrdProtocol.hh
|   |       |   |-- XrdScheduler.hh
|   |       |   `-- XrdTcpMonPin.hh
|   |       |-- XrdCl
|   |       |   |-- XrdClAnyObject.hh
|   |       |   |-- XrdClBuffer.hh
|   |       |   |-- XrdClConstants.hh
|   |       |   |-- XrdClCopyProcess.hh
|   |       |   |-- XrdClDefaultEnv.hh
|   |       |   |-- XrdClEnv.hh
|   |       |   |-- XrdClFile.hh
|   |       |   |-- XrdClFileSystem.hh
|   |       |   |-- XrdClFileSystemUtils.hh
|   |       |   |-- XrdClLog.hh
|   |       |   |-- XrdClMonitor.hh
|   |       |   |-- XrdClOptional.hh
|   |       |   |-- XrdClPlugInInterface.hh
|   |       |   |-- XrdClPlugInManager.hh
|   |       |   |-- XrdClPropertyList.hh
|   |       |   |-- XrdClStatus.hh
|   |       |   |-- XrdClURL.hh
|   |       |   `-- XrdClXRootDResponses.hh
|   |       |-- XrdHttp
|   |       |   `-- XrdHttpSecXtractor.hh
|   |       |-- XrdNet
|   |       |   |-- XrdNet.hh
|   |       |   |-- XrdNetAddr.hh
|   |       |   |-- XrdNetAddrInfo.hh
|   |       |   |-- XrdNetCmsNotify.hh
|   |       |   |-- XrdNetConnect.hh
|   |       |   |-- XrdNetOpts.hh
|   |       |   |-- XrdNetSockAddr.hh
|   |       |   |-- XrdNetSocket.hh
|   |       |   `-- XrdNetUtils.hh
|   |       |-- XrdOuc
|   |       |   |-- XrdOucBuffer.hh
|   |       |   |-- XrdOucCRC.hh
|   |       |   |-- XrdOucCacheCM.hh
|   |       |   |-- XrdOucCacheStats.hh
|   |       |   |-- XrdOucCallBack.hh
|   |       |   |-- XrdOucChain.hh
|   |       |   |-- XrdOucCompiler.hh
|   |       |   |-- XrdOucDLlist.hh
|   |       |   |-- XrdOucEnum.hh
|   |       |   |-- XrdOucEnv.hh
|   |       |   |-- XrdOucErrInfo.hh
|   |       |   |-- XrdOucGMap.hh
|   |       |   |-- XrdOucHash.hh
|   |       |   |-- XrdOucHash.icc
|   |       |   |-- XrdOucIOVec.hh
|   |       |   |-- XrdOucLock.hh
|   |       |   |-- XrdOucName2Name.hh
|   |       |   |-- XrdOucPinObject.hh
|   |       |   |-- XrdOucPinPath.hh
|   |       |   |-- XrdOucRash.hh
|   |       |   |-- XrdOucRash.icc
|   |       |   |-- XrdOucSFVec.hh
|   |       |   |-- XrdOucStream.hh
|   |       |   |-- XrdOucString.hh
|   |       |   |-- XrdOucTList.hh
|   |       |   |-- XrdOucTable.hh
|   |       |   |-- XrdOucTokenizer.hh
|   |       |   |-- XrdOucTrace.hh
|   |       |   |-- XrdOucUtils.hh
|   |       |   `-- XrdOuca2x.hh
|   |       |-- XrdSec
|   |       |   |-- XrdSecAttr.hh
|   |       |   |-- XrdSecEntity.hh
|   |       |   |-- XrdSecEntityAttr.hh
|   |       |   |-- XrdSecEntityPin.hh
|   |       |   `-- XrdSecInterface.hh
|   |       |-- XrdSys
|   |       |   |-- XrdSysAtomics.hh
|   |       |   |-- XrdSysError.hh
|   |       |   |-- XrdSysFD.hh
|   |       |   |-- XrdSysHeaders.hh
|   |       |   |-- XrdSysLogPI.hh
|   |       |   |-- XrdSysLogger.hh
|   |       |   |-- XrdSysPageSize.hh
|   |       |   |-- XrdSysPlatform.hh
|   |       |   |-- XrdSysPlugin.hh
|   |       |   |-- XrdSysPthread.hh
|   |       |   |-- XrdSysSemWait.hh
|   |       |   |-- XrdSysTimer.hh
|   |       |   |-- XrdSysXAttr.hh
|   |       |   `-- XrdSysXSLock.hh
|   |       |-- XrdVersion.hh
|   |       |-- XrdXml
|   |       |   `-- XrdXmlReader.hh
|   |       |-- XrdXrootd
|   |       |   |-- XrdXrootdBridge.hh
|   |       |   |-- XrdXrootdGStream.hh
|   |       |   `-- XrdXrootdMonData.hh
|   |       `-- private
|   |           |-- Xrd
|   |           |   `-- XrdPoll.hh
|   |           |-- XrdCl
|   |           |   |-- XrdClArg.hh
|   |           |   |-- XrdClCtx.hh
|   |           |   |-- XrdClFileOperations.hh
|   |           |   |-- XrdClFileSystemOperations.hh
|   |           |   |-- XrdClFinalOperation.hh
|   |           |   |-- XrdClFwd.hh
|   |           |   |-- XrdClMessage.hh
|   |           |   |-- XrdClOperationHandlers.hh
|   |           |   |-- XrdClOperationTimeout.hh
|   |           |   |-- XrdClOperations.hh
|   |           |   |-- XrdClParallelOperation.hh
|   |           |   |-- XrdClPostMaster.hh
|   |           |   |-- XrdClPostMasterInterfaces.hh
|   |           |   |-- XrdClResponseJob.hh
|   |           |   |-- XrdClTransportManager.hh
|   |           |   |-- XrdClZipArchive.hh
|   |           |   |-- XrdClZipCache.hh
|   |           |   `-- XrdClZipOperations.hh
|   |           |-- XrdNet
|   |           |   |-- XrdNetBuffer.hh
|   |           |   |-- XrdNetIF.hh
|   |           |   `-- XrdNetPeer.hh
|   |           |-- XrdOuc
|   |           |   |-- XrdOucExport.hh
|   |           |   |-- XrdOucGatherConf.hh
|   |           |   |-- XrdOucN2NLoader.hh
|   |           |   `-- XrdOucPList.hh
|   |           |-- XrdPosix
|   |           |   `-- XrdPosixMap.hh
|   |           |-- XrdSecsss
|   |           |   `-- XrdSecsssID.hh
|   |           |-- XrdSys
|   |           |   `-- XrdSysPriv.hh
|   |           `-- XrdZip
|   |               |-- XrdZipCDFH.hh
|   |               |-- XrdZipEOCD.hh
|   |               |-- XrdZipExtra.hh
|   |               |-- XrdZipLFH.hh
|   |               |-- XrdZipUtils.hh
|   |               |-- XrdZipZIP64EOCD.hh
|   |               `-- XrdZipZIP64EOCDL.hh
|   |-- lib64
|   |   |-- libXrdAppUtils.so -> libXrdAppUtils.so.2
|   |   |-- libXrdAppUtils.so.2 -> libXrdAppUtils.so.2.0.0
|   |   |-- libXrdAppUtils.so.2.0.0
|   |   |-- libXrdCl.so -> libXrdCl.so.3
|   |   |-- libXrdCl.so.3 -> libXrdCl.so.3.0.0
|   |   |-- libXrdCl.so.3.0.0
|   |   |-- libXrdClProxyPlugin-5.so
|   |   |-- libXrdCrypto.so -> libXrdCrypto.so.2
|   |   |-- libXrdCrypto.so.2 -> libXrdCrypto.so.2.0.0
|   |   |-- libXrdCrypto.so.2.0.0
|   |   |-- libXrdCryptoLite.so -> libXrdCryptoLite.so.2
|   |   |-- libXrdCryptoLite.so.2 -> libXrdCryptoLite.so.2.0.0
|   |   |-- libXrdCryptoLite.so.2.0.0
|   |   |-- libXrdCryptossl-5.so
|   |   |-- libXrdSec-5.so
|   |   |-- libXrdSecProt-5.so
|   |   |-- libXrdSecgsi-5.so
|   |   |-- libXrdSecgsiAUTHZVO-5.so
|   |   |-- libXrdSecgsiGMAPDN-5.so
|   |   |-- libXrdSeckrb5-5.so
|   |   |-- libXrdSecpwd-5.so
|   |   |-- libXrdSecsss-5.so
|   |   |-- libXrdSecunix-5.so
|   |   |-- libXrdUtils.so -> libXrdUtils.so.3
|   |   |-- libXrdUtils.so.3 -> libXrdUtils.so.3.0.0
|   |   |-- libXrdUtils.so.3.0.0
|   |   |-- libXrdXml.so -> libXrdXml.so.3
|   |   |-- libXrdXml.so.3 -> libXrdXml.so.3.0.0
|   |   `-- libXrdXml.so.3.0.0
|   `-- share
|       `-- man
|           |-- man1
|           |   `-- xrdadler32.1
|           `-- man8
|               `-- mpxstats.8
`-- xrootd-5.4.2-py3.6.egg-info
    |-- METADATA
    |-- RECORD
    `-- WHEEL
SL7 Dockerfile
FROM scientificlinux/sl:7

RUN yum update -y && \
    yum install -y yum-conf-repos && \
    yum install -y yum-conf-softwarecollections epel-release && \
    yum install -y \
        cmake3 \
        make \
        libuuid-devel \
        libxml2-devel \
        openssl-devel \
        systemd-devel \
        zlib-devel \
        devtoolset-7-gcc-c++ \
        python3-devel \
        python3-setuptools \
    yum clean all && \
    python3 -m pip install --no-cache-dir --upgrade pip setuptools wheel

WORKDIR /
RUN useradd -m user
USER user
RUN source scl_source enable devtoolset-7 && \
    python3 -m pip install --user --verbose 'xrootd==5.4.2' && \
    python3 -m pip list

RUN python3 -m pip list && \
    python3 -m pip show xrootd && \
    python3 -c 'import XRootD; print(XRootD)' && \
    python3 -c 'import pyxrootd; print(pyxrootd)' && \
    python3 -c 'from XRootD import client'

@adriansev
Copy link
Contributor Author

well, the weird thing is this: on both ubuntu and fedora XRootD directory is placed in xrootd-5.4.2-py3.8-linux-x86_64.egg / xrootd-5.4.2-py3.10-linux-x86_64.egg directory which contain also a some a part of pyxrootd directory (as opposed to centos 7 as you shown):

├── pyxrootd
│   ├── client.cpython-310-x86_64-linux-gnu.so
│   ├── client.py
│   ├── __init__.py
│   └── __pycache__
│       ├── client.cpython-310.pyc
│       └── __init__.cpython-310.pyc

the rest of pyxrootd (with lib{,64} and include and share) is still found in $(python3 -m site --user-site)

the core of weirdness is that while they have the same locations, on fedora there are no problems while ubuntu needs that LD_LIBRARY_PATH export
if i succeed to formulate an accepted question format i will try on SO...

@agoose77
Copy link

agoose77 commented Apr 6, 2022

Yes, it's the same layout that is problematic. I only looked at this superficially, but upon closer inspection it looks as though it's the setuptools version that's leading to the failing easy_install. The SL7 image installs setuptools==59.6.0 which produces the expected layout.

However, on forcing these versions on Ubuntu I don't see any change in behaviour. Time to properly drill down into what's happening here.

@adriansev
Copy link
Contributor Author

i was looking over https://setuptools.pypa.io/en/latest/history.html and there are a number
of items that raise a ? to me but that's all, as i lack the overview knowledge of these..
moreover setuptools 59.6.0 is the last one to work on python 3.6 so we will have to take into account different behaviors (depending on platform + python version)
just skimming trough the list i see a lot of changes after 59.6.0 like:

  • #2896: Setuptools once again makes its local copy of distutils the default. To override, set SETUPTOOLS_USE_DISTUTILS=stdlib.

@matthewfeickert
Copy link
Contributor

matthewfeickert commented Apr 6, 2022

given the legacy build chain I am inclined to assume that the issue lies there rather than in a Python packaging platform-dependency, but I could be wrong!

I only looked at this superficially, but upon closer inspection it looks as though it's the setuptools version that's leading to the failing easy_install

moreover setuptools 59.6.0 is the last one to work on python 3.6 so we will have to take into account different behaviors (depending on platform + python version)

Issue #1579 and PR #1586 were meant to address most of this, and in CI we can see that they are working as expected. Though these focused on CMake builds, and the only builds that test the install purely from the sdist are for CentOS 7.

Though this seems to be an issue with what is actually getting packaged and put up on PyPI (which if you look is just a tarfile of the whole repo, and not a normal sdist). There is something wrong with the PyPI version, as compare the following builds on Ubuntu where you install from PyPI

Dockerfile with PyPI install:
ARG BASE_IMAGE=ubuntu:20.04
FROM ${BASE_IMAGE} as base

SHELL [ "/bin/bash", "-c" ]

RUN apt-get update && \
    DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
        g++ \
        git \
        cmake \
        uuid-dev \
        dpkg-dev \
        libssl-dev \
        libx11-dev \
        libxml2-dev \
        libkrb5-dev \
        libgsl0-dev \
        pkg-config \
        tree \
        python3 \
        python3-pip \
        python3-venv \
        python3-dev && \
    apt-get autoclean -y && \
    python3 -m pip --no-cache-dir install --upgrade pip setuptools wheel && \
    python3 -m pip list

WORKDIR /

# Set PATH to pickup virtualenv by default
ENV PATH=/usr/local/venv/bin:"${PATH}"
ENV LD_LIBRARY_PATH="/usr/local/venv/lib/python3.8/site-packages/pyxrootd/lib:${LD_LIBRARY_PATH}"
RUN python3 -m venv /usr/local/venv && \
    . /usr/local/venv/bin/activate && \
    python3 -m pip --no-cache-dir install --upgrade pip setuptools wheel && \
    python3 -m pip install --verbose 'xrootd==5.4.2' && \
    python3 -m pip list

RUN python3 -m pip list && \
    python3 -m pip show xrootd && \
    python3 -c 'import XRootD; print(XRootD)' && \
    python3 -c 'import pyxrootd; print(pyxrootd)' && \
    python3 -c 'from XRootD import client; print(client.FileSystem("root://someserver:1094"))'

WORKDIR /

CMD ["/bin/bash"]
layout with egg:
root@486091e0fdac:/# ls /usr/local/venv/lib/python3.8/site-packages/
_distutils_hack           pip                   pkg_resources-0.0.0.dist-info  setuptools-62.0.0.dist-info  xrootd-5.4.2-py3.8-linux-x86_64.egg
distutils-precedence.pth  pip-22.0.4.dist-info  pyxrootd                       wheel
easy-install.pth          pkg_resources         setuptools                     wheel-0.37.1.dist-info

and where you install from the tarball you make with the publish.sh script.

Dockerfile for install from sdist from publish.sh:
ARG BASE_IMAGE=ubuntu:20.04
FROM ${BASE_IMAGE} as base

SHELL [ "/bin/bash", "-c" ]

RUN apt-get update && \
    DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
        g++ \
        git \
        cmake \
        uuid-dev \
        dpkg-dev \
        libssl-dev \
        libx11-dev \
        libxml2-dev \
        libkrb5-dev \
        libgsl0-dev \
        pkg-config \
        tree \
        python3 \
        python3-pip \
        python3-venv \
        python3-dev && \
    apt-get autoclean -y && \
    python3 -m pip --no-cache-dir install --upgrade pip setuptools wheel && \
    python3 -m pip list

# Set PATH to pickup virtualenv by default
ENV PATH=/usr/local/venv/bin:"${PATH}"
RUN python3 -m venv /usr/local/venv && \
    . /usr/local/venv/bin/activate

WORKDIR /code

RUN git clone https://github.com/xrootd/xrootd \
        --branch v5.4.2 \
        --single-branch && \
    cd xrootd && \
    cp packaging/wheel/* . && \
    ./publish.sh && \
    cd .. && \
    python3 -m pip --verbose install xrootd/dist/xrootd-*.tar.gz && \
    python3 -m pip list

RUN python3 -m pip list && \
    python3 -m pip show xrootd && \
    python3 -c 'import XRootD; print(XRootD)' && \
    python3 -c 'import pyxrootd; print(pyxrootd)' && \
    python3 -c 'from XRootD import client; print(client.FileSystem("root://someserver:1094"))'

WORKDIR /code

CMD ["/bin/bash"]
site-packages layout with wheel:
root@257f1f5fa793:~# ls /usr/local/venv/lib/python3.8/site-packages/
XRootD                 packaging                 pip-20.0.2.dist-info           pyxrootd                     wheel
__pycache__            packaging-21.3.dist-info  pkg_resources                  setuptools                   wheel-0.37.1.dist-info
build                  pep517                    pkg_resources-0.0.0.dist-info  setuptools-44.0.0.dist-info  xrootd-5.4.2_.dist-info
build-0.7.0.dist-info  pep517-0.12.0.dist-info   pyparsing                      tomli
easy_install.py        pip
root@257f1f5fa793:~# tree /usr/local/venv/lib/python3.8/site-packages/xrootd-5.4.2_.dist-info/
/usr/local/venv/lib/python3.8/site-packages/xrootd-5.4.2_.dist-info/
|-- METADATA
|-- RECORD
`-- WHEEL

0 directories, 3 files
root@257f1f5fa793:~# tree /usr/local/venv/lib/python3.8/site-packages/XRootD/                 
/usr/local/venv/lib/python3.8/site-packages/XRootD/
|-- __init__.py
|-- __pycache__
|   `-- __init__.cpython-38.pyc
`-- client
    |-- __init__.py
    |-- __pycache__
    |   |-- __init__.cpython-38.pyc
    |   |-- _version.cpython-38.pyc
    |   |-- copyprocess.cpython-38.pyc
    |   |-- env.cpython-38.pyc
    |   |-- file.cpython-38.pyc
    |   |-- filesystem.cpython-38.pyc
    |   |-- finalize.cpython-38.pyc
    |   |-- flags.cpython-38.pyc
    |   |-- glob_funcs.cpython-38.pyc
    |   |-- responses.cpython-38.pyc
    |   |-- url.cpython-38.pyc
    |   `-- utils.cpython-38.pyc
    |-- _version.py
    |-- copyprocess.py
    |-- env.py
    |-- file.py
    |-- filesystem.py
    |-- finalize.py
    |-- flags.py
    |-- glob_funcs.py
    |-- responses.py
    |-- url.py
    `-- utils.py

3 directories, 26 files
root@257f1f5fa793:~# tree /usr/local/venv/lib/python3.8/site-packages/pyxrootd/
/usr/local/venv/lib/python3.8/site-packages/pyxrootd/
|-- __init__.py
|-- __pycache__
|   `-- __init__.cpython-38.pyc
|-- client.cpython-38-x86_64-linux-gnu.so
|-- include
|   `-- xrootd
|       |-- XProtocol
|       |   |-- XProtocol.hh
|       |   `-- XPtypes.hh
|       |-- Xrd
|       |   |-- XrdBuffer.hh
|       |   |-- XrdJob.hh
|       |   |-- XrdLink.hh
|       |   |-- XrdLinkMatch.hh
|       |   |-- XrdProtocol.hh
|       |   |-- XrdScheduler.hh
|       |   `-- XrdTcpMonPin.hh
|       |-- XrdCl
|       |   |-- XrdClAnyObject.hh
|       |   |-- XrdClBuffer.hh
|       |   |-- XrdClConstants.hh
|       |   |-- XrdClCopyProcess.hh
|       |   |-- XrdClDefaultEnv.hh
|       |   |-- XrdClEnv.hh
|       |   |-- XrdClFile.hh
|       |   |-- XrdClFileSystem.hh
|       |   |-- XrdClFileSystemUtils.hh
|       |   |-- XrdClLog.hh
|       |   |-- XrdClMonitor.hh
|       |   |-- XrdClOptional.hh
|       |   |-- XrdClPlugInInterface.hh
|       |   |-- XrdClPlugInManager.hh
|       |   |-- XrdClPropertyList.hh
|       |   |-- XrdClStatus.hh
|       |   |-- XrdClURL.hh
|       |   `-- XrdClXRootDResponses.hh
|       |-- XrdHttp
|       |   `-- XrdHttpSecXtractor.hh
|       |-- XrdNet
|       |   |-- XrdNet.hh
|       |   |-- XrdNetAddr.hh
|       |   |-- XrdNetAddrInfo.hh
|       |   |-- XrdNetCmsNotify.hh
|       |   |-- XrdNetConnect.hh
|       |   |-- XrdNetOpts.hh
|       |   |-- XrdNetSockAddr.hh
|       |   |-- XrdNetSocket.hh
|       |   `-- XrdNetUtils.hh
|       |-- XrdOuc
|       |   |-- XrdOucBuffer.hh
|       |   |-- XrdOucCRC.hh
|       |   |-- XrdOucCacheCM.hh
|       |   |-- XrdOucCacheStats.hh
|       |   |-- XrdOucCallBack.hh
|       |   |-- XrdOucChain.hh
|       |   |-- XrdOucCompiler.hh
|       |   |-- XrdOucDLlist.hh
|       |   |-- XrdOucEnum.hh
|       |   |-- XrdOucEnv.hh
|       |   |-- XrdOucErrInfo.hh
|       |   |-- XrdOucGMap.hh
|       |   |-- XrdOucHash.hh
|       |   |-- XrdOucHash.icc
|       |   |-- XrdOucIOVec.hh
|       |   |-- XrdOucLock.hh
|       |   |-- XrdOucName2Name.hh
|       |   |-- XrdOucPinObject.hh
|       |   |-- XrdOucPinPath.hh
|       |   |-- XrdOucRash.hh
|       |   |-- XrdOucRash.icc
|       |   |-- XrdOucSFVec.hh
|       |   |-- XrdOucStream.hh
|       |   |-- XrdOucString.hh
|       |   |-- XrdOucTList.hh
|       |   |-- XrdOucTable.hh
|       |   |-- XrdOucTokenizer.hh
|       |   |-- XrdOucTrace.hh
|       |   |-- XrdOucUtils.hh
|       |   `-- XrdOuca2x.hh
|       |-- XrdSec
|       |   |-- XrdSecAttr.hh
|       |   |-- XrdSecEntity.hh
|       |   |-- XrdSecEntityAttr.hh
|       |   |-- XrdSecEntityPin.hh
|       |   `-- XrdSecInterface.hh
|       |-- XrdSys
|       |   |-- XrdSysAtomics.hh
|       |   |-- XrdSysError.hh
|       |   |-- XrdSysFD.hh
|       |   |-- XrdSysHeaders.hh
|       |   |-- XrdSysLogPI.hh
|       |   |-- XrdSysLogger.hh
|       |   |-- XrdSysPageSize.hh
|       |   |-- XrdSysPlatform.hh
|       |   |-- XrdSysPlugin.hh
|       |   |-- XrdSysPthread.hh
|       |   |-- XrdSysSemWait.hh
|       |   |-- XrdSysTimer.hh
|       |   |-- XrdSysXAttr.hh
|       |   `-- XrdSysXSLock.hh
|       |-- XrdVersion.hh
|       |-- XrdXml
|       |   `-- XrdXmlReader.hh
|       |-- XrdXrootd
|       |   |-- XrdXrootdBridge.hh
|       |   |-- XrdXrootdGStream.hh
|       |   `-- XrdXrootdMonData.hh
|       `-- private
|           |-- Xrd
|           |   `-- XrdPoll.hh
|           |-- XrdCl
|           |   |-- XrdClArg.hh
|           |   |-- XrdClCtx.hh
|           |   |-- XrdClFileOperations.hh
|           |   |-- XrdClFileSystemOperations.hh
|           |   |-- XrdClFinalOperation.hh
|           |   |-- XrdClFwd.hh
|           |   |-- XrdClMessage.hh
|           |   |-- XrdClOperationHandlers.hh
|           |   |-- XrdClOperationTimeout.hh
|           |   |-- XrdClOperations.hh
|           |   |-- XrdClParallelOperation.hh
|           |   |-- XrdClPostMaster.hh
|           |   |-- XrdClPostMasterInterfaces.hh
|           |   |-- XrdClResponseJob.hh
|           |   |-- XrdClTransportManager.hh
|           |   |-- XrdClZipArchive.hh
|           |   |-- XrdClZipCache.hh
|           |   `-- XrdClZipOperations.hh
|           |-- XrdNet
|           |   |-- XrdNetBuffer.hh
|           |   |-- XrdNetIF.hh
|           |   `-- XrdNetPeer.hh
|           |-- XrdOuc
|           |   |-- XrdOucExport.hh
|           |   |-- XrdOucGatherConf.hh
|           |   |-- XrdOucN2NLoader.hh
|           |   `-- XrdOucPList.hh
|           |-- XrdPosix
|           |   `-- XrdPosixMap.hh
|           |-- XrdSecsss
|           |   `-- XrdSecsssID.hh
|           |-- XrdSys
|           |   `-- XrdSysPriv.hh
|           `-- XrdZip
|               |-- XrdZipCDFH.hh
|               |-- XrdZipEOCD.hh
|               |-- XrdZipExtra.hh
|               |-- XrdZipLFH.hh
|               |-- XrdZipUtils.hh
|               |-- XrdZipZIP64EOCD.hh
|               `-- XrdZipZIP64EOCDL.hh
|-- lib
|   |-- libXrdAppUtils.so -> libXrdAppUtils.so.2
|   |-- libXrdAppUtils.so.2 -> libXrdAppUtils.so.2.0.0
|   |-- libXrdAppUtils.so.2.0.0
|   |-- libXrdCl.so -> libXrdCl.so.3
|   |-- libXrdCl.so.3 -> libXrdCl.so.3.0.0
|   |-- libXrdCl.so.3.0.0
|   |-- libXrdClProxyPlugin-5.so
|   |-- libXrdCrypto.so -> libXrdCrypto.so.2
|   |-- libXrdCrypto.so.2 -> libXrdCrypto.so.2.0.0
|   |-- libXrdCrypto.so.2.0.0
|   |-- libXrdCryptoLite.so -> libXrdCryptoLite.so.2
|   |-- libXrdCryptoLite.so.2 -> libXrdCryptoLite.so.2.0.0
|   |-- libXrdCryptoLite.so.2.0.0
|   |-- libXrdCryptossl-5.so
|   |-- libXrdSec-5.so
|   |-- libXrdSecProt-5.so
|   |-- libXrdSecgsi-5.so
|   |-- libXrdSecgsiAUTHZVO-5.so
|   |-- libXrdSecgsiGMAPDN-5.so
|   |-- libXrdSeckrb5-5.so
|   |-- libXrdSecpwd-5.so
|   |-- libXrdSecsss-5.so
|   |-- libXrdSecunix-5.so
|   |-- libXrdUtils.so -> libXrdUtils.so.3
|   |-- libXrdUtils.so.3 -> libXrdUtils.so.3.0.0
|   |-- libXrdUtils.so.3.0.0
|   |-- libXrdXml.so -> libXrdXml.so.3
|   |-- libXrdXml.so.3 -> libXrdXml.so.3.0.0
|   `-- libXrdXml.so.3.0.0
`-- share
    `-- man
        |-- man1
        |   `-- xrdadler32.1
        `-- man8
            `-- mpxstats.8

27 directories, 161 files
root@257f1f5fa793:~#

So for debugging I'd suggest not trying to use the files on PyPI given that they are somehow different from what the publish.sh script builds. Not also that the publish.sh script build doesn't need LD_LIBRARY_PATH set.

(To be clear, this should all be redone, but to make sure we're testing against the thing that matters (the source we can control now)).

@agoose77
Copy link

agoose77 commented Apr 6, 2022

Ah, excellent point @matthewfeickert!

@matthewfeickert
Copy link
Contributor

matthewfeickert commented Apr 6, 2022

given the legacy build chain I am inclined to assume that the issue lies there rather than in a Python packaging platform-dependency, but I could be wrong!

Seems that you're not (wrong), @agoose77, as if you download this tar.gz

xrootd-5.4.2.tar.gz

that is from the built Docker image that successfully installed the same distribution as a wheel, and then docker cp it inside of Docker containers and try to install it there, I can get it to install as a wheel inside of gitlab-registry.cern.ch/linuxsupport/cc7-base:latest but not inside of ubuntu:20.04...where it was built.

My takeaway from this is that the while the tar.gz file we've been discussing/debugging with is technically a sdist

$ python -m pip install --upgrade pip twine
$ python -m pip download 'xrootd==5.4.2'
$ twine check xrootd-5.4.2.tar.gz 
Checking xrootd-5.4.2.tar.gz: PASSED with warnings
WARNING  `long_description_content_type` missing. defaulting to `text/x-rst`.

it (so really the build system) needs to be revised to be more robust to the OS it is installed on. Though at this point you should just start shipping wheels.

Maybe someone on the XRootD team (I assume from the git history that would be @simonmichal) can talk to @henryiii or someone else on the cibuildwheel team. :)

@agoose77
Copy link

agoose77 commented Apr 6, 2022

Thanks for the detective work @matthewfeickert!

Maybe someone on the XRootD team (I assume from the git history that would be @simonmichal) can talk to @henryiii or someone else on the cibuildwheel team. :)

I spent a couple of minutes looking at moving to scikit-build but there's not a lot of documentation on best-practice for in-tree bindings (hmm) vs building a wheel (easy). It's certainly doable, but not in the time I had today :)

@matthewfeickert
Copy link
Contributor

matthewfeickert commented Apr 7, 2022

moreover setuptools 59.6.0 is the last one to work on python 3.6 so we will have to take into account different behaviors (depending on platform + python version) just skimming trough the list i see a lot of changes after 59.6.0 like:

  • #2896: Setuptools once again makes its local copy of distutils the default. To override, set SETUPTOOLS_USE_DISTUTILS=stdlib.

@adriansev just to follow up on your good point, while this is correct (for installs from PyPI only — CMake builds from source are fine with any version of setuptools) this isn't so bad in terms of (more temporary) fixes.

If you simply just do

SETUPTOOLS_USE_DISTUTILS=stdlib python3 -m pip install --verbose 'xrootd==5.4.2'

then this will work right out of the box on Ubuntu for setuptools>60.0.0. So not bad for a temporary workaround 👍 — thank you for pointing that out!

Here's a Dockerfile showing that working without any LD_LIBRARY_PATH manipulation and (to really show that this would work for your current methods) even using pip install --user with no virtual environment.

Dockerfile for setuptools>60.0.0 on Ubuntu 20.04:
ARG BASE_IMAGE=ubuntu:20.04
FROM ${BASE_IMAGE} as base

SHELL [ "/bin/bash", "-c" ]

ENV PATH=/usr/local/venv/bin:"${PATH}"
RUN apt-get update && \
    DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
        g++ \
        git \
        cmake \
        uuid-dev \
        dpkg-dev \
        libssl-dev \
        libx11-dev \
        libxml2-dev \
        libkrb5-dev \
        libgsl0-dev \
        pkg-config \
        tree \
        python3 \
        python3-pip \
        python3-venv \
        python3-dev && \
    apt-get autoclean -y && \
    python3 -m pip --no-cache-dir install --upgrade pip setuptools wheel && \
    python3 -m pip list

WORKDIR /
# Show --user even though I don't recommend it just to show it works
RUN python3 -m pip --no-cache-dir install --user --upgrade pip 'setuptools>60.0.0' wheel && \
    SETUPTOOLS_USE_DISTUTILS=stdlib python3 -m pip install --user --verbose 'xrootd==5.4.2' && \
    python3 -m pip list

RUN python3 -m pip list && \
    python3 -m pip show xrootd && \
    python3 -c 'import XRootD; print(XRootD)' && \
    python3 -c 'import pyxrootd; print(pyxrootd)' && \
    python3 -c 'from XRootD import client; print(client.FileSystem("root://someserver:1094"))'

WORKDIR /

CMD ["/bin/bash"]
docker build . -f Dockerfile -t xrootd/xrootd:issue-1668-setuptools-60-plus
$ docker run --rm -ti xrootd/xrootd:issue-1668-setuptools-60-plus
root@debb0c80d455:/# python3 -m pip show xrootd
Name: xrootd
Version: 5.4.2-
Summary: 
Home-page: 
Author: 
Author-email: 
License: 
Location: /root/.local/lib/python3.8/site-packages
Requires: 
Required-by: 
root@debb0c80d455:/# ls -lhtra /root/.local/lib/python3.8/site-packages
total 20K
drwxr-xr-x 3 root root 4.0K Apr  7 06:02 ..
drwxr-xr-x 2 root root 4.0K Apr  7 06:03 xrootd-5.4.2_.dist-info
drwxr-xr-x 6 root root 4.0K Apr  7 06:03 pyxrootd
drwxr-xr-x 4 root root 4.0K Apr  7 06:03 XRootD
drwx------ 5 root root 4.0K Apr  7 06:03 .
root@debb0c80d455:/# 

Future thoughts

This should still be fixed though in a robust way. Note that these problems are happening because of the reliance on distutils for the build, as noted in #1585 (comment)

... was meant to also change:

  • distutils.setup -> setuptools.setup
  • distutils.sysconfig -> CPython's sysconfig

but it seems that these are still required given the current state of the build and will need to be revised in a later PR.

As distutils is already deprecated, reliance on it in the future will just cause more things to break on all platforms that aren't super old like CentOS 7.

@matthewfeickert
Copy link
Contributor

matthewfeickert commented Apr 15, 2022

@adriansev @agoose77 Circling back to this very late, I think I have the start of an idea of the problem: The RPATH (CentOS) or RUNPATH (Debian) needs to be different if the installation is an egg or a wheel for things to work. This is because the RPATH/RUNPATH needs to take us from the client library .so (e.g. client.cpython-36m-x86_64-linux-gnu.so) to the dependency library .sos (e.g. libXrdCl.so.3).

Wheel

If you have a wheel then from your site-packages you'll have a nice layout something like

[root@e2b96418e2e5 site-packages]# tree -L 1 | grep -i "xrootd"
|-- XRootD
|-- pyxrootd
`-- xrootd-5.4.2-py3.6.egg-info
[root@e2b96418e2e5 site-packages]# tree -L 1 pyxrootd/
pyxrootd/
|-- __init__.py
|-- __pycache__
|-- client.cpython-36m-x86_64-linux-gnu.so
|-- include
|-- lib64
`-- share

4 directories, 2 files

and the important thing is that the client .so has a RPATH/RUNPATH that points to $ORIGIN/lib64 ($ORIGIN/lib on Debian)

[root@e2b96418e2e5 site-packages]# cd pyxrootd/
[root@e2b96418e2e5 pyxrootd]# objdump -x client.cpython-36m-x86_64-linux-gnu.so | grep PATH
  RPATH                $ORIGIN/lib64      
[root@e2b96418e2e5 pyxrootd]# ls -l lib64/libXrdCl.so.3
lrwxrwxrwx 1 root root 17 Apr  5 23:09 lib64/libXrdCl.so.3 -> libXrdCl.so.3.0.0
[root@e2b96418e2e5 site-packages]# cd 
[root@e2b96418e2e5 ~]# python3 -c 'from XRootD import client; print(client.FileSystem("root://someserver:1094"))'
<XRootD.client.filesystem.FileSystem object at 0x7fd541df7f98>

egg

However, if you have an egg, then the layout changes and from your site-packages you'll have something like

(venv) root@34321f613143:/code/venv/lib/python3.9/site-packages# tree -L 1 | grep -i "xrootd"
|-- pyxrootd
`-- xrootd-2022.415-py3.9-linux-x86_64.egg
(venv) root@34321f613143:/code/venv/lib/python3.9/site-packages# tree -L 2 xrootd-2022.415-py3.9-linux-x86_64.egg/
xrootd-2022.415-py3.9-linux-x86_64.egg/
|-- EGG-INFO
|   |-- PKG-INFO
|   |-- SOURCES.txt
|   |-- dependency_links.txt
|   |-- native_libs.txt
|   |-- not-zip-safe
|   `-- top_level.txt
|-- XRootD
|   |-- __init__.py
|   |-- __pycache__
|   `-- client
`-- pyxrootd
    |-- __init__.py
    |-- __pycache__
    |-- client.cpython-39-x86_64-linux-gnu.so
    `-- client.py

6 directories, 10 files

and now the client .so has a RPATH/RUNPATH that points to $ORIGIN/../../pyxrootd/lib64 ($ORIGIN/../../pyxrootd/lib on Debian)

(venv) root@34321f613143:/code/venv/lib/python3.9/site-packages# cd xrootd-2022.415-py3.9-linux-x86_64.egg/pyxrootd/
(venv) root@34321f613143:/code/venv/lib/python3.9/site-packages/xrootd-2022.415-py3.9-linux-x86_64.egg/pyxrootd# objdump -x client.cpython-39-x86_64-linux-gnu.so | grep PATH
  RUNPATH              $ORIGIN/../../pyxrootd/lib
(venv) root@34321f613143:/code/venv/lib/python3.9/site-packages/xrootd-2022.415-py3.9-linux-x86_64.egg/pyxrootd# ls -l ../../pyxrootd/lib/libXrdCl.so.3
lrwxrwxrwx 1 root root 17 Apr 15 06:44 ../../pyxrootd/lib/libXrdCl.so.3 -> libXrdCl.so.3.0.0
(venv) root@34321f613143:/code/venv/lib/python3.9/site-packages/xrootd-2022.415-py3.9-linux-x86_64.egg/pyxrootd# cd
(venv) root@34321f613143:~# python3 -c 'from XRootD import client; print(client.FileSystem("root://someserver:1094"))'
<XRootD.client.filesystem.FileSystem object at 0x7f5105ee12b0>

This works without any LD_LIBRARY_PATH manipulation as RPATH/RUNPATH are actually being set correctly!

Next question towards solution

As the RPATH/RUNPATH is dependent on the distribution layout then

if( PYPI_BUILD )
set(XRDCL_RPATH "$ORIGIN/${CMAKE_INSTALL_LIBDIR}")
else()
set(XRDCL_RPATH "$ORIGIN/../../..")
endif()

needs to be more robust and be able to determine what the layout will be and set the XRDCL_RPATH accordingly if PYPI_BUILD:

  • "$ORIGIN/${CMAKE_INSTALL_LIBDIR}" if wheel
  • "$ORIGIN/../../pyxrootd/${CMAKE_INSTALL_LIBDIR}" if egg

So how to determine the layout in advance?

Question after all of this is fixed

Why are wheels not working with build?

Summary

I don't have a working fix yet, but I think this is actually understandable now. 👍

@adriansev
Copy link
Contributor Author

great analysis @matthewfeickert !! thanks a lot!
on this question i was also wondering about at point 3. of #1668 (comment)

@matthewfeickert
Copy link
Contributor

Question after all of this is fixed

Why are wheels not working with build?

Because I made a mistake in PR #1585 and

${6} -c 'import shutil.which' &> /dev/null # $6 holds the python sys.executable
shutil_which_available=$?

will always have shutil_which_available equal 0 as the syntax is wrong: It should be

from shutil import which

:(

I have fixes on my fork that implement this as well as other things that should make things fully distutils deprecation safe. Though I have a question for @simonmichal and @adriansev that might impact how fast I am able to get a PR that can support everything: What is the minimum version of pip that you need to be able to build the Python wheel on?

CentOS7 ships very old pip3 with python3-devel (pip v9.0.3). Are you willing to allow for things that require pip v20.0.1 (released in 2020) if if only affected building wheels from the sdist with packaging/wheel/install.sh and not the CMake builds from source?

If you are okay with that then I can simplify all of this

# TODO: Remove all of the below and build a wheel using PyPA tools
# convert the egg-info into a proper dist-info
egginfo_path=$(ls $1/xrootd-*.egg-info)
core="${egginfo_path%.*}"
core="${egginfo_path#$1/}"
sufix="${core#xrootd-*.*.*_-}"
core="${core%_-*}"
if [[ "$core" == "$sufix" ]]
then
distinfo_path="${egginfo_path%.*}.dist-info"
else
distinfo_path="$1/$core-$sufix"
fi
echo $distinfo_path >> /tmp/out.txt
mkdir $distinfo_path
mv $egginfo_path $distinfo_path/METADATA
echo -e "Wheel-Version: 1.0\nGenerator: bdist_wheel (0.35.1)\nRoot-Is-Purelib: true\nTag: py2-none-any\nTag: py3-none-any" > $distinfo_path/WHEEL
touch $distinfo_path/RECORD
distinfo_name=${distinfo_path#"$1"}
find $1/pyxrootd/ -type f -exec sha256sum {} \; | awk '{printf$2 ",sha256=" $1 "," ; system("stat --printf=\"%s\" "$2) ; print '\n'; }' >> $distinfo_path/RECORD
find $1/$distinfo_name -type f -exec sha256sum {} \; | awk '{printf$2 ",sha256=" $1 "," ; system("stat --printf=\"%s\" "$2) ; print '\n'; }' >> $distinfo_path/RECORD
find $1/XRootD/ -type f -exec sha256sum {} \; | awk '{printf$2 ",sha256=" $1 "," ; system("stat --printf=\"%s\" "$2) ; print '\n'; }' >> $distinfo_path/RECORD
find $1/pyxrootd/ -type l | awk '{print$1 ",,"}' >> $distinfo_path/RECORD

to 4 lines using wheel and pip (and that would only be to support CPython versions older than 3.3 — for more modern things pip can do it all itself.). Is that okay?

@adriansev
Copy link
Contributor Author

@matthewfeickert kudos for finding the problem!!
on the matter of versions, i think that the philosophy is that of a OS to be supported this implies the default software that comes with it.. One cannot package with custom software and then distribute the packages to systems that don't have by default the said dependencies .. so, IMHO, as long as EL7 is on the list of the supported software, the dependencies are what the native OS provides (plus the ones in the repository that provides the package, so if the package is provided by epel, then it is allowed to have requires from epel)
For discrimination between rpm packaging and pip install i do not know what to say ..
In principle when you are doing a pip install (--user as doing it as root i'm told is not good) you can also do a python3 -m pip install --no-cache-dir --user --upgrade pip setuptools wheel so i do not see problems .. so, i do not know what to say ..
but the authoritative answer is to be given by @simonmichal :)

@matthewfeickert
Copy link
Contributor

matthewfeickert commented Apr 18, 2022

@adriansev I think it will actually be a non-issue, as by removing some additional code that was doing work that pip install can now handle all by itself I've just verified it can build the sdist into a wheel using the very old pip and setuptools CentOS packages and then installing wheel and build.

build (0.7.0)
pip (9.0.3)
setuptools (39.2.0)
wheel (0.37.1)

all_green

I'll make a new PR later today (US time) and tag you both for review there.

@matthewfeickert
Copy link
Contributor

To further alleviate any concerns about the next sdist released on PyPI, here's an upload of an sdist built from master at fa83acf (which I've tagged v6.0.0 just to give it a tag and NOT because this would be anything even close to v6.0.0!)

xrootd-6.0.0.tar.gz

You can try to pip install it from this Issue

python3 -m pip install --upgrade https://github.com/xrootd/xrootd/files/8655963/xrootd-6.0.0.tar.gz

and it will install just fine! So for v5.4.3 onwards you should be able to just do

python3 -m pip install --upgrade xrootd

and get the Python bindings installed (as long as you have a valid CMake). 👍

@adriansev
Copy link
Contributor Author

@matthewfeickert Thanks a lot for your great work!!!
I can confirm that using the above command worked on ubuntu 20.04 as advertised and
confirmed with:

[Tuesday 10.05.22 10:28] adrian@ALICE2 : ~  $ 
python3 -c 'from XRootD import client as xrd_client;import logging;print(xrd_client.__version__);'
6.0.0
[Tuesday 10.05.22 10:28] adrian@ALICE2 : ~  $ 
python3 -c 'from XRootD import client; print(client.FileSystem("root://mgm.spacescience.ro:1094"))'
<XRootD.client.filesystem.FileSystem object at 0x7efdd789a4c0>

@matthewfeickert
Copy link
Contributor

Great! Thank you for the independent check — the more machines tested the better. 🚀 And (unsurprisingly) things are working as expected on Ubuntu 22.04 Jammy Jellyfish too

$ docker run --rm -ti ubuntu:22.04 /bin/bash
$ apt-get update
$ apt-get -y install python3 python3-dev python3-venv pkg-config cmake uuid-dev libssl-dev libz-dev g++
$ python3 -m venv venv && . venv/bin/activate
(venv) $ python3 -m pip install --upgrade pip setuptools wheel
(venv) $ python3 -m pip install --upgrade --verbose https://github.com/xrootd/xrootd/files/8655963/xrootd-6.0.0.tar.gz
(venv) $ python3 -c 'from XRootD import client; print(client.FileSystem("root://mgm.spacescience.ro:1094"))'
<XRootD.client.filesystem.FileSystem object at 0x7f102ae3fc10>

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants