Skip to main content

Move OSX dynamic libraries into package

Project description

https://travis-ci.org/matthew-brett/delocate.svg?branch=master

Delocate

OSX utilities to:

  • find dynamic libraries imported from python extensions

  • copy needed dynamic libraries to directory within package

  • update OSX install_names and rpath to cause code to load from copies of libraries

Provides scripts:

  • delocate-listdeps – show libraries a tree depends on

  • delocate-path – copy libraries a tree depends on into the tree and relink

  • delocate-wheel – rewrite wheel having copied and relinked library dependencies into the wheel tree.

The problem

Let’s say you have built a wheel somewhere, but it’s linking to dynamic libraries elsewhere on the machine, so you can’t distribute it, because others may not have these same libraries. Here we analyze the dependencies for a scipy wheel:

$ delocate-listdeps scipy-0.14.0b1-cp34-cp34m-macosx_10_6_intel.whl
/usr/local/Cellar/gfortran/4.8.2/gfortran/lib/libgcc_s.1.dylib
/usr/local/Cellar/gfortran/4.8.2/gfortran/lib/libgfortran.3.dylib
/usr/local/Cellar/gfortran/4.8.2/gfortran/lib/libquadmath.0.dylib

By default, this does not include libraries in /usr/lib and /System. See those too with:

$ delocate-listdeps --all scipy-0.14.0-cp34-cp34m-macosx_10_6_intel.whl
/System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate
/usr/lib/libSystem.B.dylib
/usr/lib/libstdc++.6.dylib
/usr/local/Cellar/gfortran/4.8.2/gfortran/lib/libgcc_s.1.dylib
/usr/local/Cellar/gfortran/4.8.2/gfortran/lib/libgfortran.3.dylib
/usr/local/Cellar/gfortran/4.8.2/gfortran/lib/libquadmath.0.dylib

The output tells me that scipy has picked up dynamic libraries from my homebrew installation of gfortran (as well as the system libs).

You can get a listing of the files depending on each of the libraries, using the --depending flag:

$ delocate-listdeps --depending scipy-0.14.0-cp34-cp34m-macosx_10_6_intel.whl
/usr/local/Cellar/gfortran/4.8.2/gfortran/lib/libgcc_s.1.dylib:
    scipy/interpolate/dfitpack.so
    scipy/special/specfun.so
    scipy/interpolate/_fitpack.so
    ...

A solution

We can fix like this:

$ delocate-wheel -w fixed_wheels -v scipy-0.14.0-cp34-cp34m-macosx_10_6_intel.whl
Fixing: scipy-0.14.0-cp34-cp34m-macosx_10_6_intel.whl
Copied to package .dylibs directory:
    /usr/local/Cellar/gfortran/4.8.2/gfortran/lib/libgcc_s.1.dylib
    /usr/local/Cellar/gfortran/4.8.2/gfortran/lib/libgfortran.3.dylib
    /usr/local/Cellar/gfortran/4.8.2/gfortran/lib/libquadmath.0.dylib

The -w flag tells delocate-wheel to output to a new wheel directory instead of overwriting the old wheel. -v (verbose) tells you what delocate-wheel is doing. In this case it has made a new directory in the wheel zipfile, named scipy/.dylibs. It has copied all the library dependencies that are outside the OSX system trees into this directory, and patched the python .so extensions in the wheel to use these copies instead of looking in /usr/local/Cellar/gfortran/4.8.2/gfortran/lib.

Check the links again to confirm:

$ delocate-listdeps --all fixed_wheels/scipy-0.14.0-cp34-cp34m-macosx_10_6_intel.whl
/System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate
/usr/lib/libSystem.B.dylib
/usr/lib/libstdc++.6.0.9.dylib
@loader_path/../../../../.dylibs/libgcc_s.1.dylib
@loader_path/../../../../.dylibs/libgfortran.3.dylib
@loader_path/../../../../.dylibs/libquadmath.0.dylib
@loader_path/../../../.dylibs/libgcc_s.1.dylib
@loader_path/../../../.dylibs/libgfortran.3.dylib
@loader_path/../../../.dylibs/libquadmath.0.dylib
@loader_path/../../.dylibs/libgcc_s.1.dylib
@loader_path/../../.dylibs/libgfortran.3.dylib
@loader_path/../../.dylibs/libquadmath.0.dylib
@loader_path/../.dylibs/libgcc_s.1.dylib
@loader_path/../.dylibs/libgfortran.3.dylib
@loader_path/../.dylibs/libquadmath.0.dylib
@loader_path/libgcc_s.1.dylib
@loader_path/libquadmath.0.dylib

So - system dylibs the same, but the others moved into the wheel tree.

This makes the wheel more likely to work on another machine which does not have the same version of gfortran installed - in this example.

Checking required architectures

Current Python.org Python and the OSX system Python (/usr/bin/python) are both dual Intel architecture binaries. For example:

$ lipo -info /usr/bin/python
Architectures in the fat file: /usr/bin/python are: x86_64 i386

This means that wheels built for Python.org Python or system Python should also have i386 and x86_64 versions of the Python extensions and their libraries. It is easy to link Python extensions against single architecture libraries by mistake, and therefore get single architecture extensions and / or libraries. In fact my scipy wheel is one such example, because I inadvertently linked against the homebrew libraries, which are x86_64 only. To check this you can use the --require-archs flag:

$ delocate-wheel --require-archs=intel scipy-0.14.0-cp34-cp34m-macosx_10_6_intel.whl
Traceback (most recent call last):
File "/Users/mb312/.virtualenvs/delocate/bin/delocate-wheel", line 77, in <module>
    main()
File "/Users/mb312/.virtualenvs/delocate/bin/delocate-wheel", line 69, in main
    check_verbose=opts.verbose)
File "/Users/mb312/.virtualenvs/delocate/lib/python2.7/site-packages/delocate/delocating.py", line 477, in delocate_wheel
    "Some missing architectures in wheel")
delocate.delocating.DelocationError: Some missing architectures in wheel

The “intel” argument requires dual architecture extensions and libraries. You can see which extensions are at fault by adding the -v (verbose) flag:

$ delocate-wheel -w fixed_wheels --require-archs=intel -v scipy-0.14.0-cp34-cp34m-macosx_10_6_intel.whl
Fixing: scipy-0.14.0-cp34-cp34m-macosx_10_6_intel.whl
Required arch i386 missing from /usr/local/Cellar/gfortran/4.8.2/gfortran/lib/libgfortran.3.dylib
Required arch i386 missing from /usr/local/Cellar/gfortran/4.8.2/gfortran/lib/libquadmath.0.dylib
Required arch i386 missing from scipy/fftpack/_fftpack.so
Required arch i386 missing from scipy/fftpack/convolve.so
Required arch i386 missing from scipy/integrate/_dop.so
...

I need to rebuild this wheel to link with dual-architecture libraries.

Troubleshooting

When running delocate-wheel or its sister command delocate-path, you may get errors like this:

delocate.delocating.DelocationError: library "<long temporary path>/wheel/libme.dylib" does not exist

This happens when one of your libraries gives a library dependency with a relative path. For example, let’s say that some file in my wheel has this for the output of otool -L myext.so:

myext.so:
    libme.dylib (compatibility version 10.0.0, current version 10.0.0)
    /usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 60.0.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1197.1.1)

The first line means that myext.so expects to find libme.dylib at exactly the path ./libme.dylib - the current working directory from which you ran the executable. The output should be something like:

myext.so:
    /path/to/libme.dylib (compatibility version 10.0.0, current version 10.0.0)
    /usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 60.0.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1197.1.1)

To set the path to the library, the linker is using the install name id of the linked library. In this bad case, then otool -L libme.dylib will give something like:

libme.dylib (compatibility version 10.0.0, current version 10.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1197.1.1)

where the first line is the install name id that the linker picked up when linking myext.so to libme.dylib. You job is to fix the build process so that libme.dylib has install name id /path/to/libme.dylib. This is not a problem specific to delocate; you will need to do this to make sure that myext.so can load libme.dylib without libme.dylib being in the current working directory. For CMAKE builds you may want to check out CMAKE_INSTALL_NAME_DIR.

Code

See https://github.com/matthew-brett/delocate

Released under the BSD two-clause license - see the file LICENSE in the source distribution.

travis-ci kindly tests the code automatically under Python 2.7, 3.3 and 3.4.

The latest released version is at https://pypi.python.org/pypi/delocate

Support

Please put up issues on the delocate issue tracker.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

delocate-0.6.3.zip (93.7 kB view details)

Uploaded Source

delocate-0.6.3.tar.gz (69.1 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

delocate-0.6.3-py33-none-any.whl (84.5 kB view details)

Uploaded Python 3.3

delocate-0.6.3-py3-none-any.whl (84.5 kB view details)

Uploaded Python 3

delocate-0.6.3-py2-none-any.whl (84.5 kB view details)

Uploaded Python 2

File details

Details for the file delocate-0.6.3.zip.

File metadata

  • Download URL: delocate-0.6.3.zip
  • Upload date:
  • Size: 93.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No

File hashes

Hashes for delocate-0.6.3.zip
Algorithm Hash digest
SHA256 35fdc01d639a2c338fc6ca4940cb98897546beb44e2f2a0410ac3e2e38e03a21
MD5 20901917e68faf3672ac335bf4509c5d
BLAKE2b-256 24aadfaf0f72a322574954d6e1dc84d60d79f446e143a22251ec7249bb8099d2

See more details on using hashes here.

File details

Details for the file delocate-0.6.3.tar.gz.

File metadata

  • Download URL: delocate-0.6.3.tar.gz
  • Upload date:
  • Size: 69.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No

File hashes

Hashes for delocate-0.6.3.tar.gz
Algorithm Hash digest
SHA256 b7cb4cb19d81cf7b75e881bdfc79d7572903de2f17783464b0236ea4164d8101
MD5 6682e8f37fe07511061b50ff210f8386
BLAKE2b-256 8f6070cbbc9cd41c12c3890a62b947814877a23277167276adb6f108cbf801dd

See more details on using hashes here.

File details

Details for the file delocate-0.6.3-py33-none-any.whl.

File metadata

File hashes

Hashes for delocate-0.6.3-py33-none-any.whl
Algorithm Hash digest
SHA256 8094a006f02119b668161714c61442ca7f45772df9399fb893dcdb2c5acc50ef
MD5 1c8bdc0412746d21b39551e8cff3e2b6
BLAKE2b-256 a090b60d8bf47599e6f42a51d6fc66e53214ba40164a1995fa94319c0be2db00

See more details on using hashes here.

File details

Details for the file delocate-0.6.3-py3-none-any.whl.

File metadata

File hashes

Hashes for delocate-0.6.3-py3-none-any.whl
Algorithm Hash digest
SHA256 825a6f289b08fec4c235ed050787c1a0c56f82430a6ba3f05a9375611721fcb5
MD5 164e0292d597dcc87a414f69c8389bef
BLAKE2b-256 339649660fdffb5e54b3a4b0bfb537101f8bbeb55b2223c373773d7e465e3351

See more details on using hashes here.

File details

Details for the file delocate-0.6.3-py2-none-any.whl.

File metadata

File hashes

Hashes for delocate-0.6.3-py2-none-any.whl
Algorithm Hash digest
SHA256 d4fbaf929c06506250b64d6ca6bb4f9cb9e45064a748eebaa6e6d0d0c8ce8b25
MD5 cd9dabd87fc5fd9aa3ce63390e212126
BLAKE2b-256 71ccaa0d5ad9b356125422692ad0ca04469324342f59c8ba2734803e4e44a155

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page