-
Notifications
You must be signed in to change notification settings - Fork 3
feat: add pyproject.toml and cibuildwheel support to pythonbindings #110
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
base: master
Are you sure you want to change the base?
Conversation
…on of deviceaccess
|
I fear this comes with a lot of caveats:
Maybe we can address the first point by somehow disabling the backend loading mechanism (let it print some error message explaining why it was disabled). This requires a change in DeviceAccess though. Regarding the second point: Can we maybe just not support buildwheels and always force people to compile from source? Also: you are installing system packages via dnf, which is Redhat specific. That has to be changed somehow. Is there an official way to require system dependencies in a pip build? |
|
Ok, the dnf might be fine, since it will be running inside the buildwheel docker container, right? Still, I see problems with binary compatibility, because BOOST will not necessarily be compatible with the system BOOST library and other python modules might bring in other BOOST versions. The same is true for libxml++, although less likely. Also we think that the pure pip installation is rather useless, since you expect all dependencies to be present in the system already. If you can satisfy them, you could also install the Python bindings in the same way. Isn't it possible somehow to build the dependencies also when running |
|
Hi, thanks for looking into this
If needed, in the past I did some experiments on this. I wrote a script that builds all the plugins and patched the CMake file to force linking them to the deviceaccess-pythonbindings library so See: https://github.com/bellaz89/DeviceAccess-PythonBindings/blob/battery_included/CMakeLists.txt#L58-L63 Apparently, cibuildwheel also fixes these dependencies by pulling the libraries/dependencies required by the Doocs,EPICS,OPC-UA and modbus backends (Though, the amount of testing was limited). Even if it is an inelegant and compilation-time consuming solution, this effectively allows delivering all the deviceaccess plugins/backends in one single package.
Yes exactly. All the build process is containerized to enforce reproducibility. Even if it the container uses
Is this a problem though? If the module is linked to its private version of BOOST (and libxml++) embedded in the .wheel this won't be visible to other parts of the program no? If another part of the program uses another version of BOOST, both versions will be loaded. The only problem is if one wants to load deviceaccess backends that were build with different versions of the dependencies. For them, the only "easy" solution I see is the one discussed above. EDIT: I see that it is not easy as it seems, to be checked.
Yes. 'pure' pip installation (without cibuildwheel) is rather useless. It is there just because cibuildwheel needs it. It first compiles the dependencies and then it triggers the wheel building process with Anyways, a PEP compliant
I am not expert about that, but this should be possible through CMake (see https://cmake.org/cmake/help/latest/module/FetchContent.html) |
|
Regarding the ABI version: I picked E.g. the latest image, |
|
To simplify the discussion I will concentrate on a single point for now:
This is not how the dynamic linker in Linux works. If a shared library depends on another shared library, it will first check whether a shared library of this name has been loaded already. If this is the case, no attempt will be made to load the dependency. There is no version checking in this. Even if you somehow manage to load that other dependency version, only those symbols will be imported which are not already present. You cannot have the same symbol loaded twice in the process. So even linking in all our dependencies statically would not help. I had done some extensive research in this direction a couple of years ago and discussed this on stackoverflow: https://stackoverflow.com/questions/55008283/dlmopen-and-c-libraries In theory we could check if there has been any improvement in this direction, but using dlmopen in this context would be a huge hassle which IMO isn't worth it. I think, if anything like this is done, we should aim for an official publication as a PyPI package, otherwise the benefit is too small. That sets a rather high requirement for portability (i.e. we really have to comply to manylinux) and interoperability with other packages. PS: If you are simply concerned how to build the Python Bindings and its dependencies from source easily, I recommend dragon (https://gitlab.desy.de/msk-sw/utilities/workflow-scripts - might need modification for you to disable the Gitlab part, since you are only interested in ChimeraTK on Github). PPS: We were not able to build the package as you documented it, so it seems not even to work for our main platform Ubuntu 24.04... |
If you are still interested in trying it. Could you please post a log? Thanks |
|
So, I tried to read the for i in * ; do readelf -d $i | grep SONAME; done
0x000000000000000e (SONAME) Library soname: [libboost_atomic-00e6ede8.so.1.78.0]
0x000000000000000e (SONAME) Library soname: [libboost_chrono-5bc8051c.so.1.78.0]
0x000000000000000e (SONAME) Library soname: [libboost_filesystem-e9e1d9ac.so.1.78.0]
0x000000000000000e (SONAME) Library soname: [libboost_system-c0fa27da.so.1.78.0]
0x000000000000000e (SONAME) Library soname: [libboost_thread-f12e0bef.so.1.78.0]
0x000000000000000e (SONAME) Library soname: [libChimeraTK-DeviceAccess-bc5f9758.so.03.24.01]
0x000000000000000e (SONAME) Library soname: [libffi-3a37023a.so.6.0.2]
0x000000000000000e (SONAME) Library soname: [libglibmm-2-85c25c82.4.so.1.3.0]
0x000000000000000e (SONAME) Library soname: [libgmodule-2-6c5d2a3f.0.so.0.5600.4]
0x000000000000000e (SONAME) Library soname: [libgmp-d944b113.so.10.3.2]
0x000000000000000e (SONAME) Library soname: [libgnutls-e5fc1c5f.so.30.28.2]
0x000000000000000e (SONAME) Library soname: [libhogweed-cd4c53be.so.4.5]
0x000000000000000e (SONAME) Library soname: [libidn2-2f4a5893.so.0.3.6]
0x000000000000000e (SONAME) Library soname: [liblzma-51a76f52.so.5.2.4]
0x000000000000000e (SONAME) Library soname: [libnettle-37944285.so.6.5]
0x000000000000000e (SONAME) Library soname: [libp11-kit-ac9dcd7e.so.0.3.0]
0x000000000000000e (SONAME) Library soname: [libpcre-0dd207b5.so.1.2.10]
0x000000000000000e (SONAME) Library soname: [libsigc-2-86d3a688.0.so.0.0.0]
0x000000000000000e (SONAME) Library soname: [libtasn1-564de53e.so.6.5.5]
0x000000000000000e (SONAME) Library soname: [libunistring-05abdd40.so.2.1.0]
0x000000000000000e (SONAME) Library soname: [libxml2-39f609e7.so.2.9.7]
0x000000000000000e (SONAME) Library soname: [libxml++-2-48354e93.6.so.2.0.7]It seems that the embedded libraries have a
Are you sure about that? If two libraries have different |
That doesn't work due to some long standing bug in glibc, please read the StackOverflow post :-) |
Correct me if I am wrong. In your stackoverflow post you try to load the same library twice since EDIT: In addition, I also see that this issue is related to |
No, I was trying to load different OR identical versions (both must work) of the same library in the same process, such that they don't disturb each other. The use case back then was a DeviceAccess backend depending e.g. on DOOCS, which would be loaded through the DMAP file in a process, which is already using DOOCS. Everything is fine as long as the libraries are in the same version, now the problem to solve was when the backend depends on a different version of DOOCS than the main process. I tried to load the library in such a way that it would allow the the symbols of DOOCS in the version required by the backend to coexist with the symbols of DOOCS in the version required by the main process. I tried a lot of different mechanisms, starting with dlopen, which does not allow any such separation. dlmopen in theory can do such separation through namespaces, but it is broken. Now, if I think more about it, there may be a difference between what I attempted earlier and the situation now: We are now probably only concerned about conflicting symbols which are all imported via dlopen. I had the situation that one "combatant" of the conflict was loaded in the main executable, where RTLD_LOCAL could not be specified. Hence specifying RTLD_LOCAL when loading the backend was not helping, as all symbols loaded by the main executable are visible by the backend anyway (RTLD_LOCAL only separates from future imports). So maybe we can give this a try, but we should really check this. The theory could be wrong. In pybind11 they are playing extra tricks to prevent different modules built with different versions of pybind11 to conflict with each other - this would be unnecessary if the separation by RTLD_LOCAL would be sufficient. (I am thinking of writing two small Python modules which depend on each one library which would conflict each other, similar to my segregated linking example of the stackoverflow post.) |
|
If this might help, I wrote a small example for this in C: https://github.com/bellaz89/test_patchelf It requires the command Here the following things happen:
So I would say that this effectively proves that changing the |
Unrelated to this topic. Could this issue be due to the two different versions of E.g. In my system libraries, if I run So the |
|
No, doocs has the version number properly in the soname, otherwise we would have huge problems already. Unfortunately I don't have time to go deeper into this topic right now, but I guess it is not so urgent right now. Btw: Here is the error I get when running the cbuildwheel: |
|
Thanks. I fixed the address to download the image from |
What
This PR add a pip-compliant
pyproject.tomlproject file. In addition the configuration forcibuildwheelis added (https://github.com/pypa/cibuildwheel).scikit-buildis used as thepyproject.tomlbuild backend. The normal way of building the package should be unaffected.Why
This PR enables the deviceaccess-pythonbinding module to be installed via pip. Therefore, the integration of this module with a typical python development workflow is greatly improved. In addition, when built via
cibuildwheel, distribution-independent portable .wheel packages are generated for CPythonv3.8tov3.13. Thecibuildwheelconfiguration automatically fetch/compile/install the package dependencies inside a container thus simplifying the generation of the python package by third-parties.How
pip
This requires the correct versions of the DeviceAccess-PythonBindings dependencies to be installed system-wide.
python -m pip install .cibuildwheel
This requires cibuildwheel and docker/podman. Additionally, if cross compiled, binfmt/qemu support should be enabled
python -m pip install cibuildwheel # export CIBW_CONTAINER_ENGINE=podman # only if podman is used cibuildwheelThis generates the .wheel packages in ./wheelhouse at the end of the process