[CMake] Re: Mac OS X Framework support

E. Wing ewmailing at gmail.com
Thu Dec 22 19:13:06 EST 2005


On 12/22/05, William A. Hoffman <billlist at nycap.rr.com> wrote:
> At 06:58 PM 12/21/2005, E. Wing wrote:
>
> >> 1. Finding and using existing frameworks.
> >>
>
> OK, so after thinking about this some, reading emails, and
> talking with some folks here at Kitware, I think I have figured out
> the best way to do this.
>
> FIND_LIBRARY, should be able to find frameworks.  It will look at all the
> NAMES specified, and look for name.framework in
> the standard locations for frameworks on the mac.   The result
> will be to set the find library variable to the full path to the framework
> root directory.
>
> For example:
>
> FIND_LIBRARY(FOO_LIBRARY NAMES foo )
>

I like this very much.


> Will search the following places for foo.framework,
> (plus the normal cmake search paths):
>
> ~/Library/Frameworks
> /Library/Frameworks
> /System/Library/Frameworks
> /Network/Library/Frameworks/

I like this too and it works for me very well. I like very much that I
won't have to modify everybody's Find*.cmake scripts. However, to be
fair, I probably need to stick up for people that may want to override
the default search order.

So, for example, with SDL on OS X, we have a very interesting
situation. There are at least 4 different ways to install SDL on OS X,
and they can all (mostly) peacefully coexist on the same system. We
have the SDL.framework which people download from the website and
install to /Library/Frameworks or ~/Library/Frameworks. We have the
build-it-yourself .dylib from the Autoconf system which installs to
/usr/local. There is the packaging system called Fink (which is based
off of Debian's Apt system) which installs .dylib stuff to /sw and
there is DarwinPorts (similar to FreeBSD ports) which installs .dylib
to /opt/local.

So a case arises when a developer is working on SDL and they may be
testing new patches or different versions of SDL. They know which
version they want to link against at any given time. I think these
people might get upset if the Framework version is always linked
against first (or last) and they have no way of overriding this
behavior.

In the FindSDL*.cmake scripts I submitted, you might notice I try to
handle this case by putting $ENV{SDLDIR}/lib as the first path
searched and then the frameworks right after. This at least gives the
user a chance to override with an environmental variable. (However
this falls short of supporting a framework in a non-standard location
with the environmental variable as $ENV{SDLDIR}/lib will never
possibly find a framework.)

I'm not sure how CMake could handle this. I also didn't realize CMake
had default search paths already, so maybe this can be addressed
there. If not, maybe this could be handled by a flag that says "Search
Framework Paths First: YES/NO" (NO searches last). Or maybe we need to
revert to users manually listing all the paths in their FIND_LIBRARY
commands. I don't like this so much, but maybe the pain can be
minimized if some poster-child Find*.cmake script that has paths to
all known packaging systems (this should also include systems from
Solaris, IRIX, etc.) is pointed to for all new package writers to see.


> Lets say it finds ~/Library/Frameworks/foo.framework, then
>
> FOO_LIBRARY=~/Library/Frameworks/foo.framework
> Now, if you use FOO_LIBRARY like this:
>
> TARGET_LINK_LIBRARIES(bar $${FOO_LIBRARY})

Why are there two '$$' signs? I hope that is a typo.

>
> Then cmake will add -F~/Library/Frameworks to all
> .o files in the target bar.  Also, a -F~/Library/Frameworks
> and -framework foo will be added to the link line
> for bar.
>
> -F will not be added if the framework is in
> /Library/Frameworks
> /System/Library/Frameworks
> as these are always included by the compiler.

You might have to include the -F for /Library/Frameworks. As I
mentioned about the Universal Binary SDKs, the default search paths
change when you use the SDKs. The problem is that /Library/Frameworks
is one of those that gets dropped out. This has bitten me and has
bitten other people on the Xcode mailing list as it was not obvious
what was happening.

> If you write a FindSomething.cmake module, you will use
> the FIND_LIBRARY command to find frameworks, and not try to
> use FIND_PATH to find include files buried in the framework.
> The framework will be treated as a complete package, and cmake
> will assume it contains the correct includes and libraries.

This one makes me nervous and I think it may need work. There are two issues:

Issue 1: What happens if you do have FIND_PATH in your existing
Find*.cmake script, which is certainly the case for all existing
scripts? I suspect that existing scripts are going to break when
FIND_PATH is called and no header is found. I think either FIND_PATH
needs to be altered to be able to find Frameworks like FIND_LIBRARY,
or we need a FIND_INCLUDE_PATH and encourage all script writers to
migrate to that when finding headers.

Issue 2: The usage of
#include <Foo/TheHeader.h>
vs
#include "TheHeader.h"
has two very different implications. It's a pain in the butt, but it a
serious reality.

When using a Framework, by Apple guidelines, you are supposed to do:
#include <Foo/TheHeader.h>
like in
#include <OpenGL/gl.h>
#include <OpenAL/al.h>
#include <Cocoa/Cocoa.h>
#include <osg/PositionAttitudeTransform>
#include <osgText/String>
#include <SDL/SDL.h>

However, this is not always practical or portable. Some projects
recognize that not everybody puts headers in a subdirectory (I
probably should say hierarchy) where this works. For example, SDL on
FreeBSD puts its headers in
/usr/local/include/SDL11
not
/usr/local/include/SDL

So this breaks any usage of #include <SDL/SDL.h> and you must do
#include <SDL11/SDL.h>

But this obviously only works on FreeBSD. So for portability, SDL
guidelines say that everybody should do
#include "SDL.h"
and build systems must set their respective include search paths.

So with OS X, if we had done #include <SDL/SDL.h>, then the -F flag
does some magic, and knows that it needs to pick out headers from
inside the framework. But since we have to use #include "SDL.h", the
-F flag does nothing for us, and we must invoke the -I flag:
-I/Library/Frameworks/SDL.framework/Headers

So my original thinking was, "Why not just use -I for everything
instead and ditch the -F flag". Well it doesn't work for the opposite
case.

So OpenSceneGraph for example has basically dictated the other
direction that you must use the form.
#include <osg/PositionAttitudeTransform>
#include <osgText/String>
This is partly due to the enormous number of headers OSG provides, and
partly done to avoid name collision.

Anyway, for #include <osgText/String>, I foolishly tried to set
-I/Library/Frameworks/osgText.framework/Headers

So first, there was no guarantee that this would work since there is
no osgText/ directory that encloses "String". Second, it turns out
that in this case, I was hit by a name collision problem because OS X
has a case-insensitive file system and the
C++ Standard Library #include <string> was pulling in osgText's "String" file.

Anyway, the moral of the story is you need to case the situation to
use -F or -I depending on usage.

So this gets me back to FIND_PATH (or FIND_INCLUDE_PATH).
I'm thinking that this function (and INCLUDE_DIRECTORIES) need to be
enhanced to figure out these cases.

So for example, in the SDL case, we might have:
FIND_PATH(SDL_INCLUDE_DIR SDL.h
/usr/local/include
...
)
And in the OSG case, we might have
FIND_PATH(OSG_INCLUDE_DIR osg/PositionAttitudeTransform
/usr/local/include
...
)

In both cases, using a similar mechanism to what FIND_LIBRARY goes
though to find frameworks, the frameworks will be searched for these
headers.

In the SDL case, the path returned could be
/Library/Frameworks/SDL.framework/Headers

In the OSG case, the path returned could be
/Library/Frameworks/osg.framework (I suggest we append the
osg.framework part so we know we are dealing with a framework. Just
having the string to /Library/Frameworks might be ambiguous if the
framework is located in a non-standard directory.)

Then when these paths are passed to INCLUDE_DIRECTORIES,
INCLUDE_DIRECTORIES parses the string and decides if -I or -F should
be used. With SDL, since there is a trailing "Headers" directory, -I
should be used an the path is just used as is.
With OSG, since there is no trailing Headers, truncate off the
osg.framework part and use -F/Library/Frameworks



>
> >> 2. Creating new frameworks.  I suppose we could add support for
> >>   ADD_LIBRARY(foo FRAMEWORK ${srcs})
> >>   This would bundle a shared library into a framework as described here:
> >>
>
> For creating frameworks, a new type of library will be created FRAMEWORK.

This seems reasonable.

> Also, some additional target properties will be created so that you
> can specify the include files that go with the framework.
> SET_TARGET_PROPERTIES(foo PROPERTIES
>                       FRAMEWORK_INCLUDES "foo.h;bar.h;car.h"
> )

This seems reasonable too, but is there a way we can list the headers
in a looser form (without the quotes and semicolons in a single
string) like:
SET_TARGET_PROPERTIES(foo PROPERTIES
    FRAMEWORK_INCLUDES
foo.h
bar.h
car.h dar.h
)

For example, just the osg core has nearly 150 headers that need to be
listed. I can't imagine writing a one line string that holds all
these.

By the way, I should probably mention that a framework also can hold
other things. Frameworks have a Resources directory and there are some
other directories that are pseudo-standardized (but lesser used).
Maybe these could also be a Target Property?


> The framework structure will be created at build time, and copied
> at install time.

I interpret that means the headers will be placed in the correct
locations at build time. If so, sounds good me to me.

>
> Now the only thing left, is to implement this stuff!
> Any volunteers?
>

I don't think I would be very effective as I still don't know the code
base. That, and much of my time will be sucked into actually
implementing a CMake system for OpenSceneGraph (and potentially SDL)
if this happens.

Thanks,
Eric


More information about the CMake mailing list