Discussion:
[CMake] Mac OS X Framework support
Bill Hoffman
2005-12-21 16:03:00 UTC
Permalink
Hi all,

I am looking for some advise from Mac OS X developers/users. There
are some requests for CMake to better support Frameworks. There
are two main requests:

1. Finding and using existing frameworks.

For this feature, it seems that we need a new cmake command.
FIND_FRAMEWORK. I don't think that FIND_FILE or FIND_LIBRARY can
be overloaded to correctly handle frameworks. Also, there may have
to be a ADD_FRAMEWORK_DIRECTORY command. Although, you could say that
only fullpath frameworks found in FIND_FRAMEWORK add the -F, and only
if it is not in a standard framework location. Is this correct:
-F/path/to/framework (this adds the path to a framework and is like -I and -L.
-framework name (this is like -l framework)

Most frameworks only have one shared library in them. However, there are umbrella frameworks,
like OpenGL that have more than one?


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:
http://developer.apple.com/documentation/DeveloperTools/Conceptual/MachOTopics/index.html
(see Loading Code at Runtime, Packaging a Shared Library as a Framework)
However, I am not clear how the header files would get into the right place?
This almost seems like it should be part of the install process.


So, if you are a Mac developer and have any ideas on how this stuff should be done,
please speak up.

Thanks.

-Bill
E. Wing
2005-12-21 23:58:55 UTC
Permalink
From: Bill Hoffman
Hi all,
I am looking for some advise from Mac OS X developers/users. There
are some requests for CMake to better support Frameworks. There
OS X developers, please check my bug report responses at these locations:

Bug #: 2250 (http://public.kitware.com/Bug/bug.php?op=show&bugid=2250)
Bug #: 886 (http://public.kitware.com/Bug/bug.php?op=show&bugid=886)

They cover much of this discussion. You can also correct me as necessary.

A quick response to these (most of which has been covered in my bug reports).
1. Finding and using existing frameworks.
For this feature, it seems that we need a new cmake command.
FIND_FRAMEWORK. I don't think that FIND_FILE or FIND_LIBRARY can
be overloaded to correctly handle frameworks. Also, there may have
to be a ADD_FRAMEWORK_DIRECTORY command.
I think FIND_LIBRARY can and should be overloaded to handle this case
rather than creating a new FIND_FRAMEWORK API call. (See bug report
).
Although, you could say that
only fullpath frameworks found in FIND_FRAMEWORK add the -F, and only
if it is not in a standard framework location.
Generally yes, but bear in mind that "standard framework location"
changes once you invoke the 10.4 Universal SDK (or any SDK for that
matter) for Universal Binaries.
-F/path/to/framework (this adds the path to a framework and is like -I
and -L.
-framework name (this is like -l framework)
Yes,
-framework name is like -lname
-F/path/to/framework is kind of like both -I and -L. There is a tricky
part that comes up in usage patterns, e.g. #include "SDL.h" vs
#include <SDL/SDL.h>. -F covers the latter, but not the former. You'll
probably need me to elaborate on this when the time comes. I have
documented the situation in my FindOpenThreads.cmake or Findosg.cmake
module which uses the latter form. SDL itself actually requires the
former form.
Most frameworks only have one shared library in them. However, there are
umbrella frameworks,
like OpenGL that have more than one?
Don't worry about umbrella frameworks or indirect dependencies. OpenGL
technically isn't an umbrella framework by the way. I think Accelerate
is the poster child for umbrella frameworks. But I digress. A
framework is just a glorified dylib. The linking patterns aren't any
different, just the flags. If you could link to the dylib before, you
can link to the counterpart framework. For umbrella frameworks, by
Apple's documentation you are not supposed to interact/link with the
subframeworks so treat the top level framework like any other and
ignore the subframeworks.
2. Creating new frameworks. I suppose we could add support for
ADD_LIBRARY(foo FRAMEWORK ${srcs})
http://developer.apple.com/documentation/DeveloperTools/Conceptual/MachOTopics/index.html
(see Loading Code at Runtime, Packaging a Shared Library as a Framework)
However, I am not clear how the header files would get into the right
place?
This almost seems like it should be part of the install process.
2) Yes, header files will need to be copied into the correct place. It
may seem natural to put this in the install process, but I don't think
it belongs there for several reasons. The first reason is that often I
just build so I can package something up for shipping. I don't really
want to "install". Second, I often need separate projects to reference
others' products directories. In the framework case, I would want the
framework ready to go once it's build and not require some
intermediate install process. Also, since frameworks seem to have some
kind of versioning system, it seems that additional logic should be
supported to ensure these headers are copied to the correct header
version location and the correct symbolic links are set up. This
should be done as part of the framework build and not an install
process somebody needs to setup. (Nobody is going to know how.)

Also there seems to be a notion of public, project, and private
headers in Xcode. Public gets copied into the framework. Project
doesn't get copied. I don't really understand what Private does. But
if these distinctions turn out to be important, the CMake API probably
should be ready to handle this.

So I don't think a new API call would be unreasonable to handle this.
Or conversely, you could overload ADD_LIBRARY to handle this, but it
could get messy.

Additional things to consider...should there be support to handle
-compatibility_version
-current_version
-install_name
-prebind (with -seg1addr <address>)
-integrated release, _debug, and _profile builds in the same framework

Thanks,
Eric
William A. Hoffman
2005-12-22 19:52:20 UTC
Permalink
Post by Bill Hoffman
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 )

Will search the following places for foo.framework,
(plus the normal cmake search paths):

~/Library/Frameworks
/Library/Frameworks
/System/Library/Frameworks
/Network/Library/Frameworks/

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})

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.

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.
Post by Bill Hoffman
2. Creating new frameworks. I suppose we could add support for
ADD_LIBRARY(foo FRAMEWORK ${srcs})
For creating frameworks, a new type of library will be created FRAMEWORK.
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"
)

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


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

-Bill
E. Wing
2005-12-23 00:13:06 UTC
Permalink
Post by William A. Hoffman
Post by Bill Hoffman
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.
FIND_LIBRARY(FOO_LIBRARY NAMES foo )
I like this very much.
Post by William A. Hoffman
Will search the following places for foo.framework,
~/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.
Post by William A. Hoffman
Lets say it finds ~/Library/Frameworks/foo.framework, then
FOO_LIBRARY=~/Library/Frameworks/foo.framework
TARGET_LINK_LIBRARIES(bar $${FOO_LIBRARY})
Why are there two '$$' signs? I hope that is a typo.
Post by William A. Hoffman
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.
Post by William A. Hoffman
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
Post by William A. Hoffman
Post by Bill Hoffman
2. Creating new frameworks. I suppose we could add support for
ADD_LIBRARY(foo FRAMEWORK ${srcs})
For creating frameworks, a new type of library will be created FRAMEWORK.
This seems reasonable.
Post by William A. Hoffman
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?
Post by William A. Hoffman
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.
Post by William A. Hoffman
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
William A. Hoffman
2005-12-23 13:39:50 UTC
Permalink
Post by E. Wing
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
OK, this sounds good, although I think there needs to be someway
to name the framework we are looking for in the FIND_PATH, although
that would mean changing existing FIND_PATH calls, which we want to avoid.
I am concerned that an exhaustive search of all found/installed frameworks for
SDL.h may be slow. Using FIND_LIBRARY to find a framework is easy because
the name of the thing plus a .framework is the name of the framework. However,
a call to FIND_PATH (foo.h) gives us no information about what package foo.h is
in, and requires a modification for the mac. Any ideas?

-Bill
E. Wing
2005-12-23 23:15:19 UTC
Permalink
Post by William A. Hoffman
OK, this sounds good, although I think there needs to be someway
to name the framework we are looking for in the FIND_PATH, although
that would mean changing existing FIND_PATH calls, which we want to avoid.
I am concerned that an exhaustive search of all found/installed frameworks for
SDL.h may be slow. Using FIND_LIBRARY to find a framework is easy because
the name of the thing plus a .framework is the name of the framework.
However,
a call to FIND_PATH (foo.h) gives us no information about what package foo.h is
in, and requires a modification for the mac. Any ideas?
-Bill
Yeah, this could be tricky. I cannot think of any definitive ways of
dealing with this unless you change FIND_PATH. Maybe an optional
parameter that specifies the framework for those who care about
performance?

But, if you are willing to change FIND_PATH (or invent
FIND_INCLUDE_PATH), maybe the API can be refactored to realize that
somethings reside in "namespaces" (for a lack of a better term).

For example, OpenGL puts stuff in GL/
wxWidgets puts stuff in wx/
SDL puts stuff in SDL/ (usually)

So for OpenGL, the typical FIND_PATH looks something like this:
FIND_PATH(MY_INC_DIR GL/gl.h
/usr/local/include
/usr/include
/usr/X11R6/include
...
)

Notice that the "namespace" is part of the header string. Contrast
this to how we search for SDL.
FIND_PATH(MY_INC_DIR SDL.h
/usr/local/include/SDL
/usr/local/include/SDL11 # For FreeBSD
/usr/local/include/SDL12 # Just in case FreeBSD realizes we've been at
1.2 for years
/usr/local/include # Just in case SDL.h is not in a "namespace"
/usr/include/SDL
/usr/include/SDL11 # just in case
/usr/include/SDL12 # just in case
/usr/include #just in case
...
)
Notice that we have a generally very nessy/verbose way of dealing with
the varying "namespaces" which hasn't even taken into account OS X
frameworks.

So what if did decide to change FIND_PATH, we could do it in a
universal way so all platforms benefit and not just for OS X
Frameworks' sake.

So I propose something like the following:
FIND_INCLUDE_PATH(MY_INC_DIR
gl.h
NAMESPACES GL OpenGL
FLAT_NAMESPACE_USAGE NO
PATHS
/usr/local/include
/usr/include
...
)

And for SDL
FIND_INCLUDE_PATH(MY_INC_DIR
SDL.h
NAMESPACES SDL SDL11 SDL12
FLAT_NAMESPACE_USEAGE YES
PATHS
/usr/local/include
/usr/include
)

You can see particularly in the SDL case, the list of paths I have to
list becomes much shorter and nicer by a factor of 3 (or 4). This is
significant as there are more paths to search which I didn't list.


So in the first case (OpenGL), the FLAT_NAMESPACE_USAGE=NO informs the
API that your code uses the usage pattern: <Foo/bar.h> instead of
"bar.h".
Combined with NAMESPACES CMake will try looking for #include <GL/gl.h>
and <OpenGL/gl.h> against
/usr/local/include
/usr/include
and of course look for the frameworks
GL.framework
OpenGL.framework.

In the SDL case, the FLAT_NAMESPACE_USAGE=YES informs CMake the usage
pattern is "SDL.h". This flag also says the -I flag must be used
instead of -F for frameworks.
It will then look in
/usr/local/include/SDL
/usr/local/include/SDL11
/usr/local/include/SDL12
/usr/local/include # Maybe this too?
/usr/include/SDL
/usr/include/SDL11
/usr/include/SDL12
/usr/include
and of course look for the frameworks
SDL.framework
SDL11.framework.
SDL12.framework


Obviously, my terminology need to be cleaned up, and maybe the API
design could be cleaned up/streamlined, but I hope it gets the idea
across.


For not changing or adding a new API, I only have heuristics you might try.
You could first try to use the basename of the header as the Framework name:
If SDL.h, then look for SDL/SDL.h,
but this fails for things like
gl.h because it is in OpenGL.h
and fails even worse for things like PositionAttitudeTransform which resides in
<osg/PositionAttitudeTransform>

If CMake has any notion of state, then maybe if FIND_LIBRARY has
already been used, then you could search the libraries it has found
first.

Then you could try leveraging the Unix "locate" database. Then if you
are in Tiger and still haven't found anything, you could try a
Spotlight query (mdfind). However, I don't remember if Spotlight
indexes framework directories by default. (I changed the default
settings on my system so mine do get searched.)

CMake could also try keeping an internal OS X specific database of
/System frameworks which never change per OS major version. Most of
the frameworks reside here.

After this point, I think it becomes a brute force GLOB approach which
you mentioned in a following email which seems reasonable.


Thanks,
Eric
William A. Hoffman
2005-12-23 14:22:33 UTC
Permalink
Post by E. Wing
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
CMake supports PATH and CMAKE_LIBRARY_PATH env variables. So, if you have
those set correctly CMake will find the right thing. You can of course always
change the cache with ccmake or CMakeSetup, if it gets the wrong one the first time.

-Bill
William A. Hoffman
2005-12-23 15:04:45 UTC
Permalink
Post by William A. Hoffman
Post by E. Wing
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
CMake supports PATH and CMAKE_LIBRARY_PATH env variables. So, if you have
those set correctly CMake will find the right thing. You can of course always
change the cache with ccmake or CMakeSetup, if it gets the wrong one the first time.
OK, so I guess I could use the glob facility in CMake to find headers inside
frameworks. Although, VTK for example does something like this:

#ifdef APPPLE
#include <OpenGL/Gl.h>
#else
#include <Gl/GL.h>
#endif


Seems like if you are going to use a framework, you should use the framework
structure....

-Bill
E. Wing
2005-12-23 23:38:54 UTC
Permalink
Post by William A. Hoffman
OK, so I guess I could use the glob facility in CMake to find headers inside
#ifdef APPPLE
#include <OpenGL/Gl.h>
#else
#include <Gl/GL.h>
#endif
Seems like if you are going to use a framework, you should use the framework
structure....
-Bill
These are really two different issues. The first issue is, where to I
get my header file?SDL dictates the usage of "SDL.h" because it is not
guaranteed to be in the same place everywhere. Most Unices put it in
SDL/SDL.h, but FreeBSD puts it in SDL11/SDL.h, and Windows can put it
anywhere like Stuff/SDL.h. The framework really has nothing to do with
this. And the coder shouldn't be expected to case this as there are
infinite combinations why it gets pushed to the build system.

Frameworks are just a packaging system. They are supposed to be
interchangable with a .dylib. So most times, the coder doesn't know or
care what they're using. This works for most cases, It's just that the
"SDL.h" usage pattern requires everybody (not just OS X Frameworks) to
adjust their default search paths. But it allows for a uniform code
base.

Now for system stuff, like OpenGL which has basically been
standardized, then the #ifdef APPLE approach tends to work better. But
the majority of libraries out there are not standardized. Package
maintainers will put stuff wherever they want, and for OS X, you'll
never know if you're going to be using a dylib or framework.

Thanks,
Eric
Bill Hoffman
2005-12-26 20:25:27 UTC
Permalink
I can make FIND_FILE(FOO foo.h) find foo.h in installed frameworks,
the globing approach works well, and is fast enough.
However, I am not sure what to do with it. I think there is not choice
but to add some extra MAC specific syntax to FIND_FILE.

Here is the problem, lets say we have this:

/System/Library/Frameworks/foo.framework/Headers/foo.h

So, we do this:

FIND_PATH(FOO foo.h)

I have no problem finding foo.h in foo.framework/Headers/foo.h.
The question is what to do with it?

As I see it my options for setting FOO are:

1. FOO=/System/Library/Frameworks/foo.framework
If INCLUDE_DIRECTORIES see something like this, then it adds
a -F/System/Library instead of the usual -I.

2. FOO=/System/Library/Frameworks/foo.framework/Headers
If INCLUDE_DIRECTOIRES sees this, it just adds a
-I/System/Library/Frameworks/foo.framework/Headers

However, previous emails show the problem with either of these assumptions,
sometimes you want 1, and sometimes you want 2. So, there will
have to be specific syntax to take care of this. However, this means
that Find*.cmake authors will have to be Mac/Framework aware...
It sounds like by default, cmake should do option 1. This way existing
Find*.cmake stuff will work if the code uses frameworks as indented on
the Mac, no -I into framework guts, just -F and -framework. This will
avoid namespace problems.

For case 2, we are going to have to tell FIND_PATH that it should return
a path that is inside the framework structure.

Something like this:
FIND_PATH(FOO FRAMEWORK_INTERNAL foo.h )

Which will use the same searching done in 1. but will return:
FOO=/System/Library/Frameworks/foo.framework/Headers for the path.


BTW, I just checked in some changes to FIND_LIBRARY that now
make it framework aware, and it seems to work well.

-Bill
E. Wing
2005-12-27 13:07:54 UTC
Permalink
If you want to go this route, I think we can and should be more
clever/aggressive. Instead of requiring special framework keywords to
always being used, I think we can infer which version to use and will
usually be right. This at least gives a OS X a fighting chance of
working out of the box without the Find*.cmake authors being OS X
aware.


So in the scenario /System/Library/Frameworks/Bar.framework/Headers/Foo.h
(note I changed the framework name to Bar to give a little more
distinction, but this still works for Bar=Foo)

If the usage is
FIND_PATH(FOO Foo.h),
then use FOO=/System/Library/Frameworks/Bar.framework/Headers and
-I/System/Library/Frameworks/Bar.framework/Headers

If the usage is FIND_PATH(FOO Bar/Foo.h)
then use FOO=/System/Library/Frameworks/Bar.framework and -F/System/Library


I think this will cover the typical usage cases. But just in case
there is a corner case where somebody needs to do the opposite, having
an override for FRAMEWORK_INTERNAL and FRAMEWORK_EXTERNAL might be a
good idea:

# The overrides for each scenario:
FIND_PATH(FOO FRAMEWORK_EXTERNAL Foo.h),
FIND_PATH(FOO FRAMEWORK_INTERNAL Bar/Foo.h)


Also, just a note, you might want to use a heuristic first before
using the GLOB, particularly for the FIND_PATH(FOO Foo.h) usage
scenario.
I'm worried there might be a case where Foo.h exists in multiple
frameworks. You probably should try locating a Foo/Foo.h first before
anything else.



The reason I suggested all the namespace stuff in the previous emails
was to handle the variations of "Bar" which is a more general problem
and not necessarily framework/OSX specific as I showed with the SDL11
problem with FreeBSD. This approach might be worth considering because
it is general enough to address a problem that affects all platforms.

Thanks,
Eric
Post by Bill Hoffman
I can make FIND_FILE(FOO foo.h) find foo.h in installed frameworks,
the globing approach works well, and is fast enough.
However, I am not sure what to do with it. I think there is not choice
but to add some extra MAC specific syntax to FIND_FILE.
/System/Library/Frameworks/foo.framework/Headers/foo.h
FIND_PATH(FOO foo.h)
I have no problem finding foo.h in foo.framework/Headers/foo.h.
The question is what to do with it?
1. FOO=/System/Library/Frameworks/foo.framework
If INCLUDE_DIRECTORIES see something like this, then it adds
a -F/System/Library instead of the usual -I.
2. FOO=/System/Library/Frameworks/foo.framework/Headers
If INCLUDE_DIRECTOIRES sees this, it just adds a
-I/System/Library/Frameworks/foo.framework/Headers
However, previous emails show the problem with either of these assumptions,
sometimes you want 1, and sometimes you want 2. So, there will
have to be specific syntax to take care of this. However, this means
that Find*.cmake authors will have to be Mac/Framework aware...
It sounds like by default, cmake should do option 1. This way existing
Find*.cmake stuff will work if the code uses frameworks as indented on
the Mac, no -I into framework guts, just -F and -framework. This will
avoid namespace problems.
For case 2, we are going to have to tell FIND_PATH that it should return
a path that is inside the framework structure.
FIND_PATH(FOO FRAMEWORK_INTERNAL foo.h )
FOO=/System/Library/Frameworks/foo.framework/Headers for the path.
BTW, I just checked in some changes to FIND_LIBRARY that now
make it framework aware, and it seems to work well.
-Bill
Bill Hoffman
2005-12-27 13:20:45 UTC
Permalink
Post by E. Wing
If you want to go this route, I think we can and should be more
clever/aggressive. Instead of requiring special framework keywords to
always being used, I think we can infer which version to use and will
usually be right. This at least gives a OS X a fighting chance of
working out of the box without the Find*.cmake authors being OS X
aware.
So in the scenario /System/Library/Frameworks/Bar.framework/Headers/Foo.h
(note I changed the framework name to Bar to give a little more
distinction, but this still works for Bar=Foo)
If the usage is
FIND_PATH(FOO Foo.h),
then use FOO=/System/Library/Frameworks/Bar.framework/Headers and
-I/System/Library/Frameworks/Bar.framework/Headers
If the usage is FIND_PATH(FOO Bar/Foo.h)
then use FOO=/System/Library/Frameworks/Bar.framework and -F/System/Library
My concern here is what you said in an earlier email. You said that
when you attempted to just use -I for everything, you ended up with build
Post by E. Wing
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.
I would rather have cmake not find something, then to find something and
cause a very strange error because some internal file was found. Either
way I think we are stuck with changing the Find*.cmake stuff to support
the Mac in one way or another. So, I would think that the default should
be the correct and intended way of using Frameworks on the Mac, -F and
not -I. It is much easier to track down a can not find include file X.h, than
a strange syntax error caused by including the wrong header file.

-Bill
E. Wing
2005-12-27 14:32:30 UTC
Permalink
Post by Bill Hoffman
My concern here is what you said in an earlier email. You said that
when you attempted to just use -I for everything, you ended up with build
Yes, there is a risk of name collision, but there is always risk of
name collision when using the form "Foo.h" on all platforms, and I
think the risk will be minimized since you are handling the flags
instead people like me hacking stuff to get frameworks to work.
William A. Hoffman
2005-12-27 20:31:28 UTC
Permalink
OK, I have checked in support for frameworks in FIND_FILE, and FIND_LIBRARY.
It works like this:

Assume we have some framework foo, with
/System/Library/Frameworks/foo.framework/Headers/foo.h

FIND_FILE(FOO foo.h) -- Sets FOO to /System/Library/Frameworks/foo.framework/Headers

FIND_FILE(FOOFRAME foo/foo.h) -- Sets FOOFRAME to /System/Library/Frameworks/foo.framework

FIND_LIBRARY(FOOLIB foo) -- Sets FOOLIB to /System/Library/Frameworks/foo.framework

ADD_EXECUTABLE(bar bar.c)

TARGET_LINK_LINBRARY(bar ${FOOLIB}) - foreach .o that is part of bar -F/System/Library is added
to the compile line. For linking -F/System/Library and -framework foo are added to the link line.


INCLUDE_DIRECTORIES(${FOO}) -- Adds a -I/System/Library/Frameworks/foo.framework/Headers
to the compile line.

INCLUDE_DIRECTORIES(${FOOFRAME}) -- Adds a -F/System/Library to the compile line.

It will only add a -F once, duplicates are removed. Also, I did not stop it from
adding -F for system locations like /System/Library because it does not cause a
warning, and is more simple.

In searching for frameworks, the following search order is used:

(path listed in command)
$(CMAKE_LIBRARY_PATH)
$(PATH)
~/Library/Frameworks
/Library/Frameworks
/System/Library/Frameworks
/Network/Library/Frameworks

-Bill
E. Wing
2005-12-28 18:47:42 UTC
Permalink
Post by William A. Hoffman
OK, I have checked in support for frameworks in FIND_FILE, and FIND_LIBRARY.
Assume we have some framework foo, with
/System/Library/Frameworks/foo.framework/Headers/foo.h
FIND_FILE(FOO foo.h) -- Sets FOO to
/System/Library/Frameworks/foo.framework/Headers
FIND_FILE(FOOFRAME foo/foo.h) -- Sets FOOFRAME to
/System/Library/Frameworks/foo.framework
FIND_LIBRARY(FOOLIB foo) -- Sets FOOLIB to
/System/Library/Frameworks/foo.framework
ADD_EXECUTABLE(bar bar.c)
TARGET_LINK_LINBRARY(bar ${FOOLIB}) - foreach .o that is part of bar
-F/System/Library is added
to the compile line. For linking -F/System/Library and -framework foo are
added to the link line.
INCLUDE_DIRECTORIES(${FOO}) -- Adds a
-I/System/Library/Frameworks/foo.framework/Headers
to the compile line.
INCLUDE_DIRECTORIES(${FOOFRAME}) -- Adds a -F/System/Library to the compile line.
Okay, sounds very good. I'll try to get to testing later today by
attempting to port some of my Find*.cmake scripts.
Post by William A. Hoffman
It will only add a -F once, duplicates are removed. Also, I did not stop it from
adding -F for system locations like /System/Library because it does not cause a
warning, and is more simple.
This is probably okay. You definately will want the -F for the other
search paths because the Universal SDK tries to remap the search
paths. I'm not sure how /System is going to react though since /System
is the one that has to be remapped. But I guess we can worry about
that later when we focus on the Universal Binary stuff.
Post by William A. Hoffman
(path listed in command)
$(CMAKE_LIBRARY_PATH)
$(PATH)
~/Library/Frameworks
/Library/Frameworks
/System/Library/Frameworks
/Network/Library/Frameworks
One minor thing, I think /Network/Library/Frameworks should be
searched before /System/Library/Frameworks. I'm not totally sure about
this though so I could be wrong. Only Apple frameworks are supposed to
go in /System/Library/Frameworks, so I think the /Network directory is
supposed to be searched before it just in case you need one last
chance to override the system installed one. (For example, OpenAL is
currently in flux due to the 1.1 migration. Since Apple open sourced
their implementation, we are encouraged to build our own version and
override theirs until they can finish and ship a 1.1 for the official
OS.)

So I think the order should be:
~/Library/Frameworks
/Library/Frameworks
/Network/Library/Frameworks
/System/Library/Frameworks

Nobody I know actually has setup a network share like this though so I
don't know anybody with practical experience with this.

Thanks,
Eric

Sean McBride
2005-12-22 15:21:25 UTC
Permalink
(resent without attachment, since it was too big apparently)
Post by Bill Hoffman
For this feature, it seems that we need a new cmake command.
FIND_FRAMEWORK. I don't think that FIND_FILE or FIND_LIBRARY can
be overloaded to correctly handle frameworks. Also, there may have
to be a ADD_FRAMEWORK_DIRECTORY command. Although, you could say that
only fullpath frameworks found in FIND_FRAMEWORK add the -F, and only
-F/path/to/framework (this adds the path to a framework and is like
-I and -L.
-framework name (this is like -l framework)
I only know enough about cmake to build vtk, so I'm not sure I'll have
many useful comments, but...
Post by Bill Hoffman
Most frameworks only have one shared library in them. However, there
are umbrella frameworks,
like OpenGL that have more than one?
Some frameworks can get really big. The Apple supplied
ApplicationServices.framework for example. See the attached
screenshot. The framework itself contains other frameworks, and also
contains 'variants', that is, a debug and profile version of the dylib.
Users of umbrella frameworks are supposed to consider them opaque however.
Post by Bill Hoffman
2. Creating new frameworks. I suppose we could add support for
ADD_LIBRARY(foo FRAMEWORK ${srcs})
http://developer.apple.com/documentation/DeveloperTools/Conceptual/
MachOTopics/index.html
(see Loading Code at Runtime, Packaging a Shared Library as a Framework)
However, I am not clear how the header files would get into the right
place?
Post by Bill Hoffman
This almost seems like it should be part of the install process.
I'm not sure what you mean by 'install process' but when building a
framework with Xcode, there is a 'build phase' which copies user-
selected headers into the /Headers folder of the framework. See:
<http://developer.apple.com/documentation/DeveloperTools/Conceptual/
XcodeUserGuide20/Contents/Resources/en.lproj/bs_build_phases/
chapter_30_section_3.html>

Another thing to know is that dylibs on Mac OS X annoyingly have the
full path of where they are expected to be in the file system when they
are dynamically linked. Hardcoded paths suck for obvious reasons. See
'man install_name_tool' or here:
<http://www.hmug.org/man/1/install_name_tool.php>
--
____________________________________________________________
Sean McBride, B. Eng ***@rogue-research.com
Rogue Research www.rogue-research.com
Mac Software Developer Montréal, Québec, Canada
Continue reading on narkive:
Loading...