creating a self-contained app that uses external libraries

Hi

I want to release an application as a self-contained project so that anyone can download it, double click it and run it in their Macs (I’m working on Snow Leopard with XCode). The thing is this code makes use of external libraries such as FontForge and ImageMagick. The libraries are open-source so no worries on that but I don’t know how to compile/package the project so that it includes them.

You can see the project here:

http://vimeo.com/25793769

Any help will be appreciated.

Hi ,

I suppose that these are shared libraries right ?? You can either check if there is an option to compile them as static libraries (if size is not a problem) and link them to your app so that you dont have to package anything and just upload your application binary for download ( which will then contain all the necessary parts ) or if there is no option for static compilation you could create a Disk Image Installer with all the necessary components ( libs , binaries etc ) which then could be downloaded and installed in the system.

It shouldn’t be too hard ( my experience is only with linux ) but I think here you can find some useful infos :

http://digital-sushi.org/entry/how-to-create-a-disk-image-installer-for-apple-mac-os-x/

Or either just google " how to create a dmg installer " and you ll find a lot of info…

HTH,

Petros

Like petroskataras said, if there are static versions of the libraries available, use them and you’re done.

Otherwise, check what dynamic (.dylib or .framework) versions of the libraries are available. On Mac OS X, dynamic libraries are built to be placed in a specific location on the end-user’s hard disk (that is, the absolute path to the location where the library will be installed is “burned” into the library when it is built). There is a special magic variable that one can use at the beginning of this path to make a path that is relative to the executable that uses the library, called “@executable_path”. Using this variable, it is possible to build dynamic libraries that are supposed to live inside the application bundle of apps that use them, instead of being placed in some global location on the hard drive. For private frameworks, the path should then be “@executable_path/…/Frameworks/[frameworkname]”. For private dylibs, that path is instead “@executable_path/[dylibname]”.

Check what dynamic (.dylib or .framework) versions are available of the libraries you are interested in. If you are lucky, there are “private” versions available, if so just link against those versions, and then copy the .framework/.dylib to the appropriate location inside your bundle using a Run Script build phase.

If there are no private builds of the libraries available, there is still a way to make them private: there is a command-line tool called install_name_tool that can patch a framework/dylib and change the install path; this way you can convert a “public” library into a “private” one. All OF apps actually do this on Mac, check the build phases of your app target, there should be a Run Script phase that copies the fmodex dylib into the MacOS folder of the application bundle, and then uses the install_name_tool to change the dylib path to “@executable_path/libfmodex.dylib”. Just add your own Run Script build phase to do the same with tour other dylibs/frameworks (or add new lines in this one with your stuff).

There is some background info on how to build private frameworks here:
http://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPFrameworks/Tasks/CreatingFrameworks.html#//apple-ref/doc/uid/20002258-106880-BAJJBIEF

HTH
/Jesper

Thanks for the response. Both libraries/programs are executables I run via the command line. One is ImageMagick:
http://www.imagemagick.org/script/convert.php?ImageMagick=ne4gmobrbllqfjiqs84lsm9sl4

I use it like this in oF (to convert between image formats):

  
  
string basedir = ofToDataPath("output/", true);  
cmd = "/opt/local/bin/convert " + basedir + prefix + ".bmp " + basedir + prefix + ".png";  
system(cmd.c_str());  
  

The other is FontForge:
http://fontforge.sourceforge.net/mac-install.html

That one requires a script file to be generated and then sent as a parameter to the command-line program:

  
  
string basedir = ofToDataPath("output/", true);  
string commanddir = basedir + "fontforgecommand.pe";  
string cmd = "";  
ofstream outfile (commanddir.c_str());  
outfile << "#!/opt/local/bin/fontforge and_some_other_stuff_here)";  
outfile.close();  
// make executable  
cmd = "chmod +x " + commanddir;  
system(cmd.c_str());  
// execute command  
cmd = commanddir + " " + basedir + "*.bmp";  
system(cmd.c_str());  
  

I basically need a way to include ‘/opt/local/bin/fontforge’ and ‘/opt/local/bin/convert’ inside of the oF app. It sounds as if @executable_path is the solution, putting those executables somewhere inside the project as resources?

Sorry, I misunderstood you, I thought you had the external code in shared libraries, not in separate executables.

In that case, @executable_path is of no help to you. Instead, I would copy the needed executables to the MacOS folder inside you application bundle (using a Run Script build phase), and then run them from there at runtime. You can use CFBundleCopyAuxiliaryExecutableURL() to get the absolute paths to the executables. Something like this:

  
std::string getAuxiliaryExecutablePath(const std::string& executableName) {  
	CFStringRef executableNameString = CFStringCreateWithCString(kCFAllocatorDefault, executableName.c_str(), kCFStringEncodingUTF8);  
	CFURLRef convertURL = CFBundleCopyAuxiliaryExecutableURL(CFBundleGetMainBundle(), executableNameString);  
	assert(convertURL != NULL);  
	std::vector<char> stringBuffer(1024 + 1, ' ');	// Include space for zero terminator  
	Boolean success = CFURLGetFileSystemRepresentation(convertURL, true, reinterpret_cast<UInt8*>(&stringBuffer[0]), stringBuffer.size());  
	assert(success);  
	CFRelease(executableNameString);  
	CFRelease(convertURL);  
	return std::string(&stringBuffer[0]);  
}  

Though it seems to me that using separate executables and system() calls like this is probably a pretty slow way to do this, my guess is that it would be a lot faster (and less prone to security issues due to bringing in an entire shell every time you want to convert an image) to call a function in a shred library, if there is one available that provides the same functionality.

/Jesper

Hey thanks for the reply. An image is converted (ImageMagick) only when the user presses a specific key, and this is only needed every ten seconds or so. The other executable is only needed once the user wants to “save” a file so that won’t happen very often either. It is more of an experimental app that would be cool other people could sort of easily use (that is, if they have a Mac OS 10.6.8 or later and a Kinect lying around). I will look into the code you sent.

Thanks

I tried your recommendation and it is almost working. The code you sent works as specified (gives me the path to the executable just right).

But I am getting “dyld: Library not loaded: /opt/local/lib/libfontforge.1.dylib” referenced from the executable path.

So it seems the executable doesn’t know that the libs are inside it? Should I use install_name_tool also?

This is my code in the Run Script:

  
  
cp -f /opt/local/bin/fontforge "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/MacOS/fontforge"  
cp -f /opt/local/bin/convert "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/MacOS/convert"  
  

My executables are in /opt/local/bin and they DO get copied to the .app once built. But when I put that .app in another Mac I get the mentioned error.

Thanks!

Hi

I also added the libfontforge.1.dylib and libMagickCore.4.dylib but am not sure as how to make fontforge “know” the new path. This is my current script:

  
  
cp -f /opt/local/bin/fontforge "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/MacOS/fontforge"  
cp -f /opt/local/lib/libfontforge.1.dylib "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/MacOS/libfontforge.1.dylib"  
install_name_tool -change ./libfontforge.1.dylib @executable_path/libfontforge.1.dylib "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/MacOS/$PRODUCT_NAME"   
cp -f /opt/local/bin/convert "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/MacOS/convert"  
cp -f /opt/local/lib/libMagickCore.4.dylib "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/MacOS/libMagickCore.4.dylib"  
install_name_tool -change ./libMagickCore.4.dylib @executable_path/libMagickCore.4.dylib "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/MacOS/$PRODUCT_NAME"   
  

Thanks for the help.

I haven’t used FontForge or ImageMagick, but I think it depends exactly on how they’re built. You can check that using the otool command in the terminal:

  
  
otool -D [path to the dylib/framework you want to check]  
  

This will print out the path where this library expects to be installed. This path will be copied into your application when it is linked by Xcode.
If this path is something other than “@executable_path/[dylibname]” (or “@executable_path/…/Frameworks/[frameworkname]” for frameworks), you will want to change it.
If the path is simply “./[dylib/framework name]” (just a path that points to it int he current directory), I think you can get away with just patching the dylib using install_name_tool at the end of the build process, after linking, just as all Openframeworks apps do on Mac using the Run Script build phase to patch the libfmodex.dylib (which I guess you have found and used as a template for your script above).
However, if the path contains a “real” path, for example to /opt/local/lib/, I think you will have to patch both the dylib and your application. See the Shared Libraries section of this page, for example:
http://doc.qt.nokia.com/qq/qq09-mac-deployment.html#sharedlibraries
As an alternative to patching both the dylib and your application each time you build your app, you could make a copy of the dylib and patch that, and then link against your patched copy of the dylib when you build your app. That way, you only have to patch the dylib once (for each new version you download at least), and the app will be built with the correct dylib path from the get-go. The downside is that you will have to remember to patch the dylib manually each time you download a new copy.

HTH
/Jesper

Oh, and one more thing I forgot to mention: check out the man page for install_name_tool. The first parameter after the -change option should be the current install name of the library, that is the output from otool -D.

@pappis thanks a lot… will let you know how it goes!

Well the indications did work.

The issue I ran into was that I had ~10 dylibs many of them mutually dependant. This means I need to write the install_name_tool process many, many times. I think I will just require the user to install fontforge, potrace and imagemagick (although most OS X versions have it by default) on their own, before using my app for now.

Thanks a lot!

Hi

I have created a binary of the project in case you want to play with it. You need to install ImageMagick, FontForge and Potrace using MacPorts and do some environment variables addition.

The binary:
http://www.mauriciogiraldo.com/lab/bodytype/bodytype.0.1.zip

Project description:
http://www.mauriciogiraldo.com/blog/2011/06/29/body-type/

Unfortunately I couldn’t make it 100% standalone (too many creepy dependencies).

Thanks for your help!