I was roaming around the net looking for a simple answer on how to link a dylib into Unity3D, without finding a simple answer. It seemed really complicated. I am used to developing on Windows and to add a DLL into Unity3D, you simply just drop the DLL int he Plugins folder and there you go.
For Mac OS X it turned out to be a bit more complicated but I managed to find a solution that worked and I will show it to you here so that You won’t have to go through the same trouble as I did.
On Mac OSX, plugins are deployed as bundles. Create the bundle project with XCode by selecting File->NewProject… and then selecting Bundle -> Carbon/Cocoa Loadable Bundle (in XCode 3) or OS X -> Framework & Library -> Bundle (in XCode 4)
My bundle will be named Cereproc. First thing to do is to go to Build settings and chose 32 bit architecture instead of 64bit. This is very important step. Unity3d will not read 64 bit bundles. Now go to the Build Phases tab for your active target.
Now open up the Finder and copy your dylibs to the project-folder. These dylibs will be manipulated by a script so be sure to save the old ones as backup.
Now link these binaries with the bundle by adding them to “Link Binaries With Libraries”. These libraries are now linked to library and xcode will compile the code, but there is still a few steps to make the bundle work with Unity3D.
Still in the Build Phases tab, add a new Build Phase step. Locate the button in the bottom right corner “Add Build Phase”. Add Run Script. The buildphases will be executed in a linear order as represented by the order they are listed in the Build Phases tab. We wan’t the script to be executed as early as possible so drag and drop the Run Script build phase to the top of the list, but below “Target Dependencies”
As you can see, I have added 5 dylibs. and have a “Run Script” build phase placed as early as possible in the list of build phases. Now we will do some scripting.
Add following code for each one of your dylib files. Replace libcerevoice_aud_shared.dylib with your respective dylib.
echo OTOOL BEGIN
otool -L libcerevoice_aud_shared.dylib
echo OTOOL END
Compile the code and go to the log navigator and watch the log.
Watch the OTOOL printout for libcerevoice_eng_shared.dylib.
The first line is the “ID” of the library itself. It’s also the working directory for it telling the library itself where it’s living. In my case it’s set to “/temp/trunk/cerevoice_eng/lib/libcerevoice_eng_shared.dylib”. This means that the dylib has to be located in this folder for my bundle to be able to accually load it.
Now read the rest of the lines. They are dependencies that this library rely on. Three of them is pointing towards other dylibs that I have located. This is the tricky part about building bundles and applications on Mac OS X. Atleast it seems tricky for who are used to working with DLLs on Windows. Anyway we need to modify these links now.
Mac OS X provides a tool for this called “install_name_tool”. We need to go through each one of our dylib files and modify the paths to point to a relative path instead of a fixed path as now.
Start by modifying all IDs of all the files. This is how my script looks like. I think you can figure out how to apply it on your files. Add it to the top of script in the the Run Script Build Phase.
install_name_tool -id @loader_path/libcerevoice_aud_shared.dylib libcerevoice_aud_shared.dylib
install_name_tool -id @loader_path/libcerevoice_shared.dylib libcerevoice_shared.dylib
install_name_tool -id @loader_path/libcerehts_shared.dylib libcerehts_shared.dylib
install_name_tool -id @loader_path/libcerevoice_pmod_shared.dylib libcerevoice_pmod_shared.dylib
install_name_tool -id @loader_path/libcerevoice_eng_shared.dylib libcerevoice_eng_shared.dylib
Note that this will modify the dylib itself, that is why I told you to save a backup incase something goes wrong.
Compile the code and watch the output by otool again.
If you did everything right, your output should be modified. You can see from my image that the id is now pointing to @loader_path/libcerevoice_eng_shared.dylib.
@loader_path is replaced with the path to the directory containing the mach-o binary which contains the load command using @loader_path. @loader_path is useful as the load path for a framework/dylib embedded in a plug-in, if the final file system location of the plugin-in unknown (so absolute paths cannot be used).
Next step is to modify the links to the dependencies. This is how my script looks like, apply it for your use. You will have to read the output from the otool to decide which links you have, if any at all. In my case I have a total of 7 links in all 5 dylibs that I fixed. If your lucky you got none. Here is my script:
install_name_tool -change /tmp/trunk/cerevoice/lib/libcerevoice_shared.dylib @loader_path/libcerevoice_shared.dylib libcerevoice_shared.dylib
install_name_tool -change /tmp/trunk/cerehts/lib/libcerehts_shared.dylib @loader_path/libcerehts_shared.dylib libcerevoice_eng_shared.dylib
install_name_tool -change /tmp/trunk/cerevoice/lib/libcerevoice_shared.dylib @loader_path/libcerevoice_shared.dylib libcerevoice_eng_shared.dylib
install_name_tool -change /tmp/trunk/cerevoice_pmod/lib/libcerevoice_pmod_shared.dylib @loader_path/libcerevoice_pmod_shared.dylib libcerevoice_eng_shared.dylib
install_name_tool -change /tmp/trunk/cerevoice_pmod/lib/libcerevoice_pmod_shared.dylib @loader_path/libcerevoice_pmod_shared.dylib libcerevoice_pmod_shared.dylib
install_name_tool -change /tmp/trunk/cerevoice/lib/libcerevoice_shared.dylib @loader_path/libcerevoice_shared.dylib libcerevoice_pmod_shared.dylib
install_name_tool -change /tmp/trunk/cerevoice/lib/libcerevoice_shared.dylib @loader_path/libcerevoice_shared.dylib libcerehts_shared.dylib
Compile the code.
And here it is. All pointing to a relative path of @loader_path. Just a few more steps now!
Load up Build Phases tab again and Add a new Build Phase, Add Copy Files.
Set the destination to “Executables” and then add the dylibs.
Last step now, add a new C-fle to the project. It does not contain any code but its needed for the linking to work. I added a c-file named “Cereproc.c”. Go to Build Phases and add the script to “Compile Sources” build phase.
Compile and there you have it! Your bundle is ready for use with Unity3D.
Load up Unity and add the bundle to the Plugins folder.
Create a c# script and hook up all the functions in the dylibs by using the name of the bundle. In my case it looks like this:
private static extern CPRCEN_engine CPRCEN_engine_new();
This function is accually in one of the dylibs but it works by importing “Cereproc”.
I hope this helped you out. Good luck creating plugins for Unity3D!