Sometimes you want to distribute a program without requiring users to install a long chain of dependencies. Typically we can do this by building a statically linked binary; for example, by passing the
-static flag when linking with gcc. Apple, however, does not support statically linked binaries, so this approach won’t work on Mac OS X. Apple does, however, support statically linked libraries. This means we can get pretty close to what we want: we’ll statically link user-space libraries into our binary, but we won’t statically link with system libraries or the kernel.
To make this approach work, we need to hide all of the user-space shared libraries during the linking phase of our build process. The Apple linker always prefers shared libraries when it can find them, but will revert to static libraries in the absence of a shared library. I’ve yet to find a command-line parameter to change this behavior, but a brute-force solution is available. Essentially, we want to rename every shared library in /usr/local such that it no longer ends in .dylib, link our program, then put all of the shared libraries back the way we found them. I wrote this Python 3 script to automate the process. To temporarily hide the shared libraries on your system, run it like this:
$ ./hide-shared-libs.py -d /usr/local --hide
To restore your shared libraries, use the following command:
$ ./hide-shared-libs.py -d /usr/local --restore
Of course, this assumes that you have static versions of all of the libraries you intend to link with. By default, not all software will install static libraries. I use Homebrew to build software on Mac OS X, and have needed to add
--enable-static to the configure parameters for a few packages. Once you rebuild with this parameter, however, static linking should work as expected.