Background information on frameworks: ------------------------------------- This will be a very quick and dirty introduction to what frameworks are, how they are organized, and how they are used. Frameworks are an organized directory hierarchy designed to manage binary compatibility among different (incompatible) versions of a given library. The basic directory structure of a framework looks something like this: Foo.framework/Versions//Foo <- dylib w/o suffix Foo.framework/Versions//Headers <- directory where headers reside Foo.framework/Versions//PrivateHeaders <- directory where headers for unsupported API reside Foo.framework/Versions/Current <- a symlink to most recent version Foo.framework/Foo <- symlink to Versions/Current/Foo Foo.framework/Headers <- symlink to Versions/Current/Headers Foo.framework/PrivateHeaders <- symlink to Versions/Current/PrivateHeaders The OS X toolchain has several additions to allow easy use of frameworks. First, the linking phase understands 2 flags: -F specifies a directory to look for frameworks in, analogous to -L -framework Foo says to link against the dylib in Foo.framework. Implicitly looks in /System/Library/Frameworks and /Library/Frameworks. Use -F to specify others. Both of these flags typically use the most recent version of the framework. Additionally, the C Preprocessor knows to look in the default and specified framework directories to look for headers. For instance: #include would look in Foo.framework/Headers for Foo.h. Problems Frameworks are meant to solve: --------------------------------------- Frameworks are meant to solve the problem of binary incompatibilities introduced between versions of dynamic libraries in a Mach-O environment. Frameworks allow different, incompatible, versions of dylibs to coexist in different directory paths, and because mach-o libraries have an install_path and linked executables have recorded each library's install_path, the executable knows which version of the library to use at run time. New development continues to use and link against the most recent version available, unless the developer has explicitly done something to change this. Problems with the Frameworks solution: -------------------------------------- If you were a vendor trying to use frameworks to manage your multiple incompatible versions of your dynamic library, this is a very heavy weight solution. First, whenever you provide a new version of your framework, you need to include all the old versions you wish to be compatible with. This becomes increasingly difficult the more revisions you keep around for compatibility, and the further back in time you try to stay compatible. There are some tricks that can be played to lighten the burden. For instance, if only a few symbols from the dylib have been updated, you can create a new dylib that contains only those symbols, and have the new dylib link against the old dylib. These types of tricks have their own drawbacks and maintenance headaches. Very few, if any, vendors actually use the compatibility features of frameworks because of their heavy weight nature. Most vendors choose other solutions to binary compatibility, where the actual dylib remains compatible. Other solutions to the same problem: ------------------------------------ Two other example approaches to the same problem: 1) dylib naming scheme, where the version number is encoded in the library name. For example: libfoo.0.dylib libfoo.1.dylib Then libfoo.dylib is a symlink, typically, to the most recent version. With mach-o, if the install_name of the dylib is set correctly, to the name with the version in it, this works fine for binary compatibility between incompatible versions of the same library. 2) versioned symbols. This allows the same library file to be used, despite having incompatible symbols. This is because the symbols themselves are versioned, and the calling library uses the "right" one. There are several ways to accomplish this, but it is really a developer's tool, more than a sysadmin or packager's layout choice. Other benefits of Frameworks: ----------------------------- Frameworks allow a tighter coupling of headers and libraries. It becomes obvious which headers are associated with which dynamic library. If you include , you then you need to link against -framework Foo, because that's the dylib that has the symbols you used. However, this isn't terribly different from the recent unix tradition of putting your header files in a directory named similar to the library. For example: /usr/include/foo/foo.h /usr/lib/libfoo.0.dylib The end result is very similar to what a framework provides, except you do not need to use the framework specific toolchain options. Disadvantages of Frameworks: ---------------------------- The main disadvantage of frameworks is that developers need to change their build environments (and sometimes source for #includes) to use the toolchain options. The traditional -L/path/.../ -lfoo does not work with frameworks. Depending on how the framework is structured, and how the original header files were included, source can need to be modified to get the proper #include paths. There can be workarounds to some of these by creating symlinks in "normal" unix locations such as /usr/include and /usr/lib. For example, for framework Foo.framework, you can create something like: /usr/lib/libfoo.dylib as a symlink to /path/to/Foo.framework/Foo And: /usr/include/foo/ as a symlink to /path/to/Foo.framework/Headers/ If the symlinks are setup appropriately, most of the time this is a workable solution. Many people do not like this for maintainability reasons (few unix software does this, so making it this is "nonstandard" and requires bringing forward changes from release to release), aesthetic reasons, or the fact that their formerly unix software is suddenly a "second class citizen". Another disadvantage can be attempting to frameworkify projects that have helpers, or things other than dylibs and headers. Often times *-config scripts do not lend themselves (or understand) frameworks, and they cannot be versioned well with the framework. What Frameworks do not solve: ----------------------------- - Frameworks do not provide a generic solution to having multiple versions of software installed at the same time. Frameworks really only provide versioning for libraries in a mach-o environment, although headers can be versioned as well (although developing with versions other than the most recent requires explicit steps on the part of the developer). The versioning they provide is intended for mach-o binary compatibility. Attempting to use such a scheme with non-mach-o shared libraries will prove to be problematic, since the dynamic linking process is different. If you desire a generic way for multiple versions of software to coexist simultaneously, you will want a way to version executables, log files, named pipes, config files, saved state, pidfiles, documentation, and all the other things that impact the filesystem namespace. Frameworks do not lend themselves to addressing these problems. - Frameworks do not automatically result in deterministic builds. Simply moving a file from one place to another, or renaming it, will not affect the determinism of builds. Additionally, if the workarounds to traditional build systems mentioned above are used, you haven't even gained any obfuscation. When to use Frameworks: ----------------------- Due to the coupling of Mach-o and Frameworks for their binary compatibility solution, Frameworks are only really suited for software specific to OS X, and you are interested in using the binary compatibility features of Frameworks, then the next step is to decide what type of framework is needed. If your project is building unix style executables that will go into ${prefix}/bin directories, you should rethink your decision to use a framework. Please see the "Framwork Philosophy" section. If you are building a .app bundle, strongly consider placing your framework inside the .app bundle instead of installing it into a canonical location on the system. This approach has a number of benefits, no the least of which is being able to drag around your .app bundle without needing users to install an extra Framework on their system. However, if you have several .app's that all use the same framework, then it may be time to forego the advantages of having the framework in each .app bundle, and install it into a canonical place on the filesystem. When not to use Frameworks: --------------------------- Frameworks should be avoided with anything you wish to be cross-platform. The main feature of frameworks is their ability to provide multiple versions of binary incompatible shared libraries is tied to Mach-O. If you are not using Mach-O as your executable format, you should not use Frameworks. If your project installs anything other than headers, shared libraries, shared library helpers, or .app bundles, you should not use Frameworks. This includes executables that go into or are linked into ${prefix}/bin. The reason to avoid frameworks here is you are mixing sorting philosophies in an inconsistent manner. If you plan to release frequent, binary incompatible shared libraries, you should not use Frameworks. Frameworks are a very heavy weight solution to binary incompatibility problems and should be used sparingly. Framework Philosophy: --------------------- First, a little background. The traditional unix philosophy for filesystem layout has been to group like files together, and to form secondary subgroups based on where the files came from. For example, executables are typically grouped together in ${prefix}/bin, libraries are typically grouped together in ${prefix}/lib, etc. Subgroupings can be done within this, such as all of apache's header files being grouped together in ${prefix}/include/httpd. However, the primary sorting mechanism is based on what type of file it is. This approach is fairly self-consistent, although ambiguity arises when a file can either fall into multiple categories or none of the above categories. Frameworks are part of a larger philosophy very similar to the traditional unix philosophy. The difference here is files are not the atomic units of the sorting methodology, instead, bundles are. Application bundles (.app's) reside in ${prefix}/Applications, Framework bundles reside in ${prefix}/Library/Frameworks, etc. The problem is, this methodology is incomplete. In many cases it does not address non-bundled files, for example, unix executables, configuration files, etc. This is why NeXTStep/OS X still has traditional unix paths for things that do not fit into this new sorting methodology. This sorting methodology was designed intentionally not addressing some of the needs of the system.