Mac – How to tell the MacPorts Python to look in /opt/local/lib for dylibs

dynamic librarymacportspython

I'm running a Python script with the MacPorts version of Python (2.7), i.e, I've run:

port select --set python python27

However, when I run my script, it can't find a dylib in /opt/local/lib:

ImportError: dlopen(/opt/local/lib/python2.7/site-packages/grgsm/_grgsm_swig.so, 2): Library not loaded: libgnuradio-grgsm.dylib
Referenced from: /opt/local/lib/python2.7/site-packages/grgsm/_grgsm_swig.so
Reason: image not found

The dylib in question resides here:

-rwxr-xr-x  1 root  admin    816036 Dec 16 11:24 /opt/local/lib/libgnuradio-grgsm.dylib

Setting export DYLIB_LIBRARY_PATH=/opt/local/lib/ doesn't help in this case (nor should it really be necessary for each user on the system to set that environment variable). I've already run update_dyld_shared_cache.

The Python module in question resides in /opt/local/lib/python2.7/site-packages so I made a symlink to it in /opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/.

otool -L on the loading .so reports:

otool -L /opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/grgsm/_grgsm_swig.so

/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/grgsm/_grgsm_swig.so:
libgnuradio-grgsm.dylib (compatibility version 0.0.0, current version 0.0.0)
/opt/local/lib/libboost_filesystem-mt.dylib (compatibility version 0.0.0, current version 0.0.0)
/opt/local/lib/libboost_system-mt.dylib (compatibility version 0.0.0, current version 0.0.0)
/opt/local/lib/libboost_thread-mt.dylib (compatibility version 0.0.0, current version 0.0.0)
/opt/local/lib/libgnuradio-runtime.3.7.8.1.dylib (compatibility version 3.7.8, current version 0.0.0)
/opt/local/lib/libgnuradio-pmt.3.7.8.1.dylib (compatibility version 3.7.8, current version 0.0.0)
/opt/local/lib/libvolk.1.1.1.dylib (compatibility version 1.1.1, current version 0.0.0)
/usr/local/lib/libosmocore.7.dylib (compatibility version 8.0.0, current version 8.0.0)
/usr/local/lib/libosmogsm.5.dylib (compatibility version 7.0.0, current version 7.0.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.1.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1)

Notice that the library in question doesn't have a path.

Best Answer

Libraries are usually referenced using absolute paths on OS X, making it unnecessary to set any variables. Your otool -L output on the Python module should thus contain

/opt/local/lib/libgnuradio-grgsm.$version.dylib (...)

This path is copied from what Apple calls the "shared library identification name" in a library when you link against it. That means that whatever value is in the output of otool -L on the Python module is the same value that you get by running otool -D on /opt/local/lib/libgnuradio-grgsm.dylib (or just using otool -L and looking at the first line). If you do this on your machine, you will notice that it also does not contain an absolute path. That's the root cause of the issue you see.

First, this is a bug in GNU Radio, or MacPorts' packaging of GNU Radio. Please file a ticket so this can get fixed or addressed with the GNU Radio developers.

Second, there are a couple of ways to work around this issue for now:

  • Method A: Fix the identification name of libgnuradio-grgsm.dylib. This can be done using install_name_tool -id $correctID /opt/local/lib/libgnuradio-grgsm.dylib. Note that the absolute path to the unversioned dylib is likely not the correct value for this. libgnuradio-grgsm.dylib is likely a symlink to another symlink with the major version embedded. This path is the one that should be used as install name. After this change, rebuilding the Python module should solve the issue.

  • Method B: Fix the path in the load command of the Python module. Use install_name_tool -change libgnuradio-grgsm.dylib $correctID /opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/grgsm/_grgsm_swig.so to do this. $correctID should be chosen as explained in Method A.