Monday, 22 June 2009

Managing static versioned libraries in OS X

The Mac OS X offers great tools to developers in order to manage libraries with many versions: Frameworks.

Anyway sometimes you may want to use static libraries instead. Maybe just as a means of (psycologically) obfuscating your preciuos code you don't want to put in a framework for the world to know...

I'm currently experimenting with an idea that should make easier to selectively link different libraries versions to your application. The trivial idea is to create a folder for each of your libraries, for example in your Library home folder, named after their version:

/home/dan/Library/ACME/MySecretLib-1.0.0/
/home/dan/Library/ACME/MySecretLib-1.0.1/

Inside each folder there is your library archive (or archives if you want debug and release flavours) and a Headers folder. Your client projects will simply include the headers and link the library archive.

I will show you how to obtain this with Xcode. Refer to the Apple Xcode Build System Guide and to the Xcode Build Settings Reference if you're not at home with the (complex) Xcode build stuff.

Open the library Project Info window and in the Build tab define, for all configurations, the user-defined build settings:
  • ACME_LIBRARY_DIR = $(USER_LIBRARY_DIR)/ACME
  • ACME_LIBRARY_NAME = MySecretLib
  • ACME_LIBRARY_VERSION = 1.0.0
Then open your static library Target Info window and in the Build tab edit the settings:
  • DEPLOYMENT_LOCATION = YES
  • PRODUCT_NAME = $(ACME_LIBRARY_NAME)
  • DSTROOT = $(ACME_LIBRARY_DIR)
  • INSTALL_PATH = /$(ACME_LIBRARY_NAME)-$(ACME_LIBRARY_VERSION)
  • PUBLIC_HEADERS_FOLDER_PATH = $(INSTALL_PATH)/Headers
Then add a Copy Headers Build Phase to your target and copy your public headers to it. Remember to set their Role to Public.

If you need a debug library with symbols simply add a "debug" line to the BUILD_VARIANTS setting.

To link your library you will define the following build settings:
  • ACME_SECRETLIBRARY_DIR = $(USER_LIBRARY_DIR)/ACME/MySecretLib-1.0.0
  • ACME_SECRETLIBRARY_NAME = libMySecretLib
  • HEADER_SEARCH_PATHS = $(ACME_SECRETLIBRARY_DIR)/Headers
  • OTHER_LDFLAGS = $(ACME_SECRETLIBRARY_DIR)/$(ACME_SECRETLIBRARY_DIR).a
changing the .a to .da if you're using the debug/release flavours.

:)