pythonsetup.pypython-wheelsaprfcpyrfc

Creating self contained wheel


I try to create a self contained wheel for pyRFC: http://sap.github.io/PyRFC/install.html

I have the needed (closed sourced) libraries and header files.

Installing pyRFC works, if I copy the libraries and headers to $VIRTUAL_ENV/lib and $VIRTUAL_ENV/include.

Now I try to create a wheel which contains the closed source library files.

But I am failing.

If I unzip the wheel it only contains this:

(pypi)pypi@pypiserver:~> unzip -l packages/pyrfc-1.9.91-cp27-cp27mu-linux_x86_64.whl
Archive:  packages/pyrfc-1.9.91-cp27-cp27mu-linux_x86_64.whl
  Length      Date    Time    Name
---------  ---------- -----   ----
     5366  2018-07-23 13:38   pyrfc/_exception.py
     1045  2018-07-23 13:38   pyrfc/__init__.py
  1610216  2018-07-23 14:10   pyrfc/_pyrfc.so
     3835  2018-07-23 14:10   pyrfc-1.9.91.dist-info/DESCRIPTION.rst
      990  2018-07-23 14:10   pyrfc-1.9.91.dist-info/metadata.json
        6  2018-07-23 14:10   pyrfc-1.9.91.dist-info/top_level.txt
      105  2018-07-23 14:10   pyrfc-1.9.91.dist-info/WHEEL
     4666  2018-07-23 14:10   pyrfc-1.9.91.dist-info/METADATA
      715  2018-07-23 14:10   pyrfc-1.9.91.dist-info/RECORD
---------                     -------
  1626944                     9 files

The extra_objects (see below) are missing.

How can I modify the setup.py of pyRFC to make the wheel contain the libraries from $VIRTUAL_ENV/lib?

Here is the setup.py: https://github.com/SAP/PyRFC/blob/master/setup.py

I tried this patch for setup.py

@@ -48,7 +49,9 @@ PYRFC_EXT = Extension(
     , libraries=LIBS
     , define_macros=MACROS
     , extra_compile_args=COMPILE_ARGS
-    , extra_link_args=LINK_ARGS
+    , extra_link_args=LINK_ARGS,
+      library_dirs=['lib'],
+      extra_objects = ['lib/libicudata.so.50', 'lib/libsapnwrfc.so', 'lib/libicui18n.so.50', 'lib/libicuuc.so.50', 'lib/libicudecnumber.so', 'lib/libsapucum.so'],
 )

If I install and run the library without libsapnwrfc.so I get this error:

Traceback (most recent call last):
  File "test-pyrfc.py", line 1, in <module>
    from pyrfc import Connection
  File "/home/other/lib/python2.7/site-packages/pyrfc/__init__.py", line 22, in <module>
    from pyrfc._pyrfc import get_nwrfclib_version, Connection, TypeDescription, FunctionDescription, Server
ImportError: libsapnwrfc.so: cannot open shared object file: No such file or directory

If I trace the open calls, I see that it only looks at root-level for the library. The library does not get search in the virtualenv (which is /home/other):

strace python test-pyrfc.py 2>&1 | grep libsapnwrfc.so
open("/lib64/tls/x86_64/libsapnwrfc.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/lib64/tls/libsapnwrfc.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/lib64/x86_64/libsapnwrfc.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/lib64/libsapnwrfc.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/usr/lib64/tls/x86_64/libsapnwrfc.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/usr/lib64/tls/libsapnwrfc.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/usr/lib64/x86_64/libsapnwrfc.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/usr/lib64/libsapnwrfc.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
write(2, "libsapnwrfc.so: cannot open shar"..., 73libsapnwrfc.so: cannot open shared object file: No such file or directory) = 73

I would like to install "libsapnwrfc.so" via wheel into a virtualenv because this gives me the power to have several independent environments. I could install it via RPM or config-management, but I would like to avoid it.


Solution

  • The extra_objects are used only when linking the extension lib and are not bundled in the wheel. From the docs:

    extra_objects: list of extra files to link with (eg. object files not implied by ‘sources’, static library that must be explicitly specified, binary resource files, etc.)

    For bundling the libs into wheel, use auditwheel for Linux wheels, or delocate for MacOS wheels. SO already has an excellent question: How to build and distribute a Python/Cython package that depends on third party libFoo.so which covers the whole wheel repairing process using both auditwheel and delocate. Here, I will only summarize the necessary commands. The process is pretty similar with both tools:

    $ pip install auditwheel  # you may also need to install patchelf
    $ python setup.py bdist_wheel
    $ auditwheel show dist/*_linux_x86_64.whl  # will show the libs to be bundled
    $ auditwheel repair dist/*_linux_x86_64.whl
    

    This will create a new dir dist/wheelhouse, containing the new wheel with the bundled libs.