Skip navigation.
 
mlRe: Different versions of framework?
FROM : Bob Ippolito
DATE : Fri Dec 03 19:13:17 2004

On Dec 3, 2004, at 10:40 AM, Fabian Lidman wrote:

> Q: What is the correct procedure to link a framework into the
> application bundle, making sure the application looks there first
> instead of in /Library/Frameworks?
>
> We have run into a situation where several different applications each
> require a separate version of a framework; namely, Apache's Xerces.
> Version 2.3 of the framework is installed into Library/Frameworks. The
> application i'm responsible for needs to use Xerces 2.6, so i guess
> i'll need to link it into the bundle somehow. Unfortunately, i'm
> unable to figure out how to handle this without creating conflicts
> between the two frameworks.


I've been meaning to post this to my blog, but I've been having
software issues so I'm in between blogging software.. but here's the
content, which might answer some of your question:

------------------

It has recently come to my attention that using versioned frameworks
without binary compatible APIs simply doesn't work.  This isn't a
problem with the dyld runtime, simply a problem with gcc's compiler and
linker.  A big one.

**Frameworks**

Mac OS X frameworks are structured in such a way that allow for
versioned code, data, and headers.  A typical multi-versioned framework
looks something like this::

    Foo.framework/
        Foo -> Versions/Current/Foo
        Headers -> Versions/Current/Headers
        Resources -> Versions/Current/Resources
        Versions/
            Current -> 2.0
            2.0/
                Foo
                Headers/
                Resources/
            1.0/
                Foo
                Headers/
                Resources/

**Mach-O, MH_DYLIB, and the dyld runtime**

The ``MH_DYLIB`` object file, the part that you link to, is "Foo".  If
this were not a framework, it would have the following layout, and it
would be application-specific to put the headers and resources in the
right place::

    libFoo.dylib -> libFoo.2.0.dylib
    libFoo.1.0.dylib
    libFoo.2.0.dylib

Due to the way that ``MH_DYLIB`` object files work, each of these
dylibs know their own (supposed) location on the filesystem.  This is
called the "install name" or the "id" of the dylib.  Technically, this
is a ``LC_ID_DYLIB`` load command in the Mach-O header.  When you
create an Mach-O object file (executable, dylib, bundle, etc.) that
depends on another, it will generate a load command (``LC_LOAD_DYLIB``)
referencing the other dylib.  The data associated with the
``LC_LOAD_DYLIB`` is exactly the same data that was provided by the
linked-to ``MH_DYLIB`` in its ``LC_ID_DYLIB`` load command, unless
explicitly overrided with the ``-dylib_file`` option to `ld(1)`_ or
rewritten post-link with a tool such as `install_name_tool(1)`_.  The
`otool(1)`_  tool can be used to view these load commands in a Mach-O
file, among other things.  It's very important to have these values set
correctly because they are used by the dyld runtime to locate the
intended ``MH_DYLIB``.

**gcc, ld, and frameworks**

Apple's GCC includes many changes to support Objective C/C++ and
framework based development.  Unfortunately, none of them have any
explicit support for versioned frameworks.  For example, `gcc(1)`_
states that it uses the following algorithm for finding frameworks in
headers::

        -Fdir
            In Apple's version of GCC only, add the directory dir to the
head
            of the list of directories to be searched for frameworks.

            The framework search algorithm is, for an inclusion of
            <Fmwk/Header.h>, to look for files named
path/Fmwk.framework/Head-
            ers/Header.h or path/Fmwk.framework/PrivateHeaders/Header.h
where
            path includes /System/Library/Frameworks/
/Library/Frameworks/, and
            /Local/Library/Frameworks/, plus any additional paths
specified by
            -F.

            All the ``-F`` options are also passed to the linker.

`ld(1)`_ has an similar option, ``-framework``, which has a similar
algorithm::

        -framework name[,suffix]
              Specifies a framework to link against.  Frameworks  are 
dynamic
              shared  libraries,  but  they are stored in different
locations,
              and therefore must be searched for differently. When this
option
              is  specified,  ld  searches for framework
`name.framework/name'
              first in any directories specified with the -F option, 
then  in
              the  standard  framework  directories
/Library/Frameworks, /Net-
              work/Library/Frameworks,  and 
/System/Library/Frameworks.  The
              placement  of the -framework option is significant, as it
deter-
              mines when and how the framework is searched.  If  the 
optional
              suffix is specified the framework is first searched for
the name
              with the suffix and then without.

Note that neither of these options allow for any consideration for the
Versions directory in a framework, and therefore only link to whichever
version was installed last, because the installation process for a
framework will create the symlinks that point to locations inside the
Versions directly.

**Workaround**

Unfortunately, since there is no support or hook that will allow proper
usage of versioned frameworks, the workarounds are all ugly.  I think
the following workaround is the most appropriate: *Just Don't Use GCC's
Search Algorithms*.

- Always use the compiler option
``-I/Path/To/Foo.framework/Versions/IntendedVersion/Headers``
- use ``#include "Foo.h"`` instead of ``#include <Foo/Foo.h>`` in your
sources
- Always use the direct path to the ``MH_DYLIB`` rather than any
combination of ``-framework`` and ``-F``.
- Or, if you're building extension bundles that will be used by an
executable that already has the correct version of Foo linked in, use
the ``-undefined dynamic_lookup`` linker option

A minimal compiler/link line for an executable would look like the
following::

    cc -I/Path/To/Foo.framework/Versions/IntendedVersion/Headers -o
usesFoo usesFoo.m /Path/To/Foo.framework/IntendedVersion/Foo

And a minimal compiler/link line for an extension bundle would look
like::

    env MACOSX_DEPLOYMENT_TARGET=10.3 cc
-I/Path/To/Foo.framework/Versions/IntendedVersion/Headers -o
fooUsingExtension.bundle fooUsingExtension.m -bundle -undefined
dynamic_lookup

Quite ugly, eh?  At least it works as intended.  Unfortunately you'll
need to emulate enough of GCC's search algorithms to find the
framework, which is probably quite problematic from Xcode, but
shouldn't be too hard from say, `distutils`_, `SCons`_, `autoconf`_,
etc.  Note that you should also probably consider the ``NEXT_ROOT``
environment variable for when building against an SDK.

.. _`gcc(1)`: x-man-page://1/gcc
.. _`ld(1)`: x-man-page://1/ld
.. _`install_name_tool(1)`: x-man-page://1/install_name_tool
.. _`otool(1)`: x-man-page://1/otool
.. _`distutils`:
http://www.python.org/doc/current/lib/module-distutils.html
.. _`SCons`: http://www.scons.org/
.. _`autoconf`: http://www.gnu.org/software/autoconf/

Related mailsAuthorDate
mlDifferent versions of framework? Fabian Lidman Dec 3, 16:40
mlRe: Different versions of framework? Bob Ippolito Dec 3, 19:13
mlRe: Different versions of framework? Mark Lilback Dec 4, 16:26