Creating working dylibs

So creating a working dylib of one of your own libraries is not a very well documented process. So I've decided to write a short summary of some things that will aid other people in creating their own dylibs that they can just stick into their app package and have work.

I assume that you already have knowledge of how to add a framework and get it to show up in the frameworks folder in your app package. You can find out how to do this sort of stuff from other pages, so I won't take the time to cover it here.

Some tools that will come in handy:
otool -L

So, chances probably are that you've figured out how to compile your library into a dylib already, but when you add it to your project builder project and compile your code, you find that the dylib is being properly put into the right folder (the Frameworks folder) in your bundle. But your application won't work because it can't find the dylib! What's up?

Well, the problem isn't actually with project builder (it's actually sort of a bug with OS X, since it all worked properly in OS 9), it's with the library. If you open up the terminal and cd into your app package and find the binary that's in the Contents/MacOS folder, you can run otool -L on the binary that you find there, and you might see something like the following:
% otool -L MyFunBinary
		/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 71.0.0)
		libbz2.1.0.2.dylib (compatibility version 1.0.0, current version 1.0.2)
In this example, I'm trying to link in my own copy of the bzip2 library dylib that was created for me with bzip2 and a patch that's available on the internet.

As it turns out, in OS X 10.0 - 10.2, you need the path to the library to show up in there -- what you need is for the second line to show something like this:
		@executable_path/../Frameworks/libbz2.1.0.2.dylib (compatibility version 1.0.0, current version 1.0.2)
This tells OS X that it should look in the place where the binary is, move up a directory and into the Framworks folder, and the library will be sitting there. Which it is. Indeed, if you run otool -L on the binary of some application in your /Applications folder that has a working frameworks or dylibs in the app package Frameworks folder (try one of the applications from Omni), you can see that this is the case -- all of their binaries comtain the right path. But the compiler is not figuring things out right... In fact, as it is right now, you can place the dylib in the same folder that the .app package is in, and things will run properly. But that's not what we want.

Well, the first thing you can try to do is to use install_name_tool. If you read the man page for install_name_tool, you can actually change the information inside of the executable so that it has the right location. Depending on how the application was compiled, this may or may not work correctly. More specifically, you'll be able to change the location if there was extra space compiled into the binary to allow space for a longer pathname. Generally install_name_tool will work on binaries, but not libaries. So you can now do this:

% install_name_tool -change libbz2.1.0.2.dylib @executable_path/../Frameworks/libbz2.1.0.2.dylib MyFunBinary

Now try to run the file. Voila! It should work.
However this is somewhat unacceptable, as whenever the binary gets changed -- taht is, whenever you recompile your code, this binary is going to get the short end of the shaft and get replaced by a new binary. Now, you could actually set up a shell script that runs install_name_tool every time you build your program, but that's somewhat of a hack. We might as well try to fix things at the origin of the problem.

As it turns out, the actual problem resides with the library itself. If you take your library, and run otool -L on it like this:

% otool -L libbz2.1.0.2.dylib

		/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 71.0.0)
		libbz2.1.0.2.dylib (compatibility version 1.0.0, current version 1.0.2)

Well, this looks familiar! Well, in fact the computer picks up the location of the library from the library itself -- what we need to do is to get @executable_path/../Frameworks/ to show up in front of the binary name. We can try using install_name_tool, but this time we find out that things don't work. Why is this? There's no extra room compiled into the library to allow for a longer pathname, so we'd have to compile the original library with an extra flag to allow for more room in the path. But even better, we might as well compile things correctly the firs time! Note that I can't give you exact directions from this point on, since things will differ slightly for different applications you are compiling. But in general, you should go back to the source code for your application, find the linker line in the Makefile that generates the dylib, and change it, adding the -install_name flag to the libtool line. You'll want to do something like:

libtool -dynamic -flat_namespace -install_name @executable_path/../Frameworks/libbz2.1.0.2.dylib \ -lSystem -compatibility_version 1.0 -current_version 1.0.2 \ -o libbz2.1.0.2.dylib -undefined suppress $(OBJS)

Anyway, I hope that helps some people out. Good luck!
Also of interest may be the Apple Documentation on Shared Libraries and Frameworks.