View Issue Details Jump to Notes ] Print ]
IDProjectCategoryView StatusDate SubmittedLast Update
0002250CMakeCMakepublic2005-09-18 04:492006-04-13 08:42
ReporterEric Wing 
Assigned ToBill Hoffman 
PrioritylowSeverityfeatureReproducibilityalways
StatusclosedResolutionfixed 
PlatformOSOS Version
Product Version 
Target VersionFixed in Version 
Summary0002250: Incorporating OS X Framework detection into FIND_LIBRARY()
DescriptionIt would be extremely nice if the CMake core could handle library detection for OS X style frameworks through the FIND_LIBRARY command. Currently, there is a workaround for this, but it is less than ideal. It requires FindFoo.cmake module writers to be aware of OS X style frameworks which most people are not.
Furthermore, the workarounds that I have come up with are very ugly and error prone. And because the code is not centralized, a major effort to fix all modules.

So the problems with my workarounds are:
1) They are fragile. For example string parsing and regex is used. This might miss some cases because I may have done it wrong.
 
2) My work around fails to handle alternative names of the libraries in an elegant manner. CMake's FIND_LIBRARY's NAMES parameter is much more elegant.

3) My system needs to remember to manually add the -F flag which sets the search path for frameworks like -L does for unix style libraries. My system also makes it very hard to remove duplicates. Since the CMake core seems to handle -L, it seems appropriate for it to handle -F as well.

4) There's lots of extra code and branches in my workaround which makes it hard to read. There are lots of temporary variables and lots of places for cut-and-paste errors.

This is still lower priority than having the ability to actually build a framework (opposed to finding/using a framework) because this at least has a workaround while there is no mechanism I know of to build a framework. I have already filed a feature request for building frameworks.

You can take a look at the Find*.cmake modules I have submitted to see how ugly this code is. Since you have once dived into my FindSDL_sound.cmake module before, I encourage you to look at the new one I just submitted which had to add a bunch of ugly code to deal with the search paths (-F flag). And you can see that I had to apply the same fix to 7 other modules I submitted. (And there are more I haven't yet submitted.)

Thank you
TagsNo tags attached.
Attached Files

 Relationships

  Notes
(0003466)
Bill Hoffman (manager)
2005-12-20 21:44

So, just to be clear, can you explain exactly how this would work? Maybe a few use cases and example cmake code would help. From what I understand a framework may consist of multiple libraries, so a -F... -framework foo could link in foobar and foocar. What would FIND_LIBRARY do?
(0003468)

2005-12-21 04:10

> From what I understand a framework may consist of multiple libraries, so a -F... -framework foo could link in foobar and foocar. 


I'm not quite sure what you're getting at here or where you've seen this, but I don't think this is the full story. But I would say don't worry about it. Generally, Frameworks map to dylibs in a one-to-one relationship. So if in Unix style you linked to -lfoobar and -lfoocar, you would link to -framework foobar and -framework foocar.


Now OpenGL is a little interesting (but not that interesting). Instead of having a separate library for -lGL and -lGLU, they built GL and GLU into one framework so you do -framework GL (Technically its -framework OpenGL because Apple changed the name, but that is a totally different matter). But it really isn't that interesting because it's just like if a Unix vendor had built their GL and GLU into one giant libGL.so.


The reason they combined it is mainly because of the header include path and the way they interact with frameworks.
With a framework, if you #include <GL/gl.h>, it maps to the Framework named GL.framework. If you #include <GL/glu.h>, it would also map to the framework GL.framework. If you did two different frameworks, you would have to change the include to #include <GLU/glu.h>


Now there is something called an umbrella framework. Most people don't understand these (and I really don't either) and Apple doesn't want you creating your own. But the bottom line with umbrella frameworks is that you must link against the umbrella framework itself and not the subframeworks. So I think if you treat it as a black box and do as they say, everything will just work and you don't have to special case anything.



With respect to FIND_LIBRARY, I think my ideal situation is a world where FIND_LIBRARY handles finding a library transparently so the script writer need not know if they are dealing with a framework or currently supported library style.


The typical usage pattern seems to be:


FIND_LIBRARY(SDL_LIBRARY
    $ENV{SDLDIR}/lib
    /usr/lib
    /usr/local/lib
    ...
)
TARGET_LINK_LIBRARIES(MyProgram
    ${SDL_LIBRARY}
)


So I'm my thinking would be nice if I could just do something like this:
FIND_LIBRARY(SDL_LIBRARY
    $ENV{SDLDIR}/lib
    ~/Library/Frameworks
    /Library/Frameworks
    /usr/lib
    /usr/local/lib
    ...
)
TARGET_LINK_LIBRARIES(MyProgram
    ${SDL_LIBRARY}
)
and CMake would do the right thing.


In fact, I write the Findosg*.cmake and FindSDL*.cmake modules in this style where they act as a black-box and magically set SDL_LIBRARY to the correct value that can be blindly passed to TARGET_LINK_LIBRARIES. The problem is that I have to work around FIND_LIBRARY to set the value correctly and I'm explicitly passing -F flags and -framework to handle the linking. It would be nice if maybe TARGET_LINK_LIBRARIES could figure out which flags are needed. There already seems to be a little bit of precedent for this as CMake already seems to be able to distinguish between Windows and Unix conventions where the later prepends the name "lib" in front of all the library names.


FIND_PATH is another complication. I tried to document the Findosg*.cmake modules that describe this problem. Basically for #include <osg/StateSet>, with a framework, there is no actual physical directory called osg/. On a different platform, there would be something like /usr/local/include/osg/, but with the framework, the framework base name takes the place of the path. Perhaps a new function called FIND_INCLUDE_PATH could be introduced to handle this situation if this risks breaking FIND_PATH too much.
(0003469)
Eric Wing (reporter)
2005-12-21 04:12

> From what I understand a framework may consist of multiple libraries, so a -F... -framework foo could link in foobar and foocar. 


I'm not quite sure what you're getting at here or where you've seen this, but I don't think this is the full story. But I would say don't worry about it. Generally, Frameworks map to dylibs in a one-to-one relationship. So if in Unix style you linked to -lfoobar and -lfoocar, you would link to -framework foobar and -framework foocar.


Now OpenGL is a little interesting (but not that interesting). Instead of having a separate library for -lGL and -lGLU, they built GL and GLU into one framework so you do -framework GL (Technically its -framework OpenGL because Apple changed the name, but that is a totally different matter). But it really isn't that interesting because it's just like if a Unix vendor had built their GL and GLU into one giant libGL.so.


The reason they combined it is mainly because of the header include path and the way they interact with frameworks.
With a framework, if you #include <GL/gl.h>, it maps to the Framework named GL.framework. If you #include <GL/glu.h>, it would also map to the framework GL.framework. If you did two different frameworks, you would have to change the include to #include <GLU/glu.h>


Now there is something called an umbrella framework. Most people don't understand these (and I really don't either) and Apple doesn't want you creating your own. But the bottom line with umbrella frameworks is that you must link against the umbrella framework itself and not the subframeworks. So I think if you treat it as a black box and do as they say, everything will just work and you don't have to special case anything.



With respect to FIND_LIBRARY, I think my ideal situation is a world where FIND_LIBRARY handles finding a library transparently so the script writer need not know if they are dealing with a framework or currently supported library style.


The typical usage pattern seems to be:


FIND_LIBRARY(SDL_LIBRARY
    $ENV{SDLDIR}/lib
    /usr/lib
    /usr/local/lib
    ...
)
TARGET_LINK_LIBRARIES(MyProgram
    ${SDL_LIBRARY}
)


So I'm my thinking would be nice if I could just do something like this:
FIND_LIBRARY(SDL_LIBRARY
    $ENV{SDLDIR}/lib
    ~/Library/Frameworks
    /Library/Frameworks
    /usr/lib
    /usr/local/lib
    ...
)
TARGET_LINK_LIBRARIES(MyProgram
    ${SDL_LIBRARY}
)
and CMake would do the right thing.


In fact, I write the Findosg*.cmake and FindSDL*.cmake modules in this style where they act as a black-box and magically set SDL_LIBRARY to the correct value that can be blindly passed to TARGET_LINK_LIBRARIES. The problem is that I have to work around FIND_LIBRARY to set the value correctly and I'm explicitly passing -F flags and -framework to handle the linking. It would be nice if maybe TARGET_LINK_LIBRARIES could figure out which flags are needed. There already seems to be a little bit of precedent for this as CMake already seems to be able to distinguish between Windows and Unix conventions where the later prepends the name "lib" in front of all the library names.


FIND_PATH is another complication. I tried to document the Findosg*.cmake modules that describe this problem. Basically for #include <osg/StateSet>, with a framework, there is no actual physical directory called osg/. On a different platform, there would be something like /usr/local/include/osg/, but with the framework, the framework base name takes the place of the path. Perhaps a new function called FIND_INCLUDE_PATH could be introduced to handle this situation if this risks breaking FIND_PATH too much.
(0003470)
Bill Hoffman (manager)
2005-12-21 09:21

There is something more to this. If I look at an application that links to framework OpenGL with otool -L, you can see the application links to OpenGL, but if I run otool -L on OpenGL it is actually a whole group of .dylib files that are actually being linked to. So, there is not a one to one mapping.

I think we would need a FIND_FRAMEWORK command for this to work. I don't think we can use the existing commands.


caladan:~/Dashboards/MyTests/VTK-make/bin dashboard$ otool -L GraphicsCxxTests
GraphicsCxxTests:
        /System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
        /System/Library/Frameworks/AGL.framework/Versions/A/AGL (compatibility version 1.0.0, current version 1.0.0)
        /System/Library/Frameworks/Carbon.framework/Versions/A/Carbon (compatibility version 2.0.0, current version 128.0.0)
        /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 824.23.0)
        /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 88.1.2)
        /usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.3.0)
        /usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)
caladan:~/Dashboards/MyTests/VTK-make/bin dashboard$ otool -L /System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL
/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL:
        /System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
        /System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices (compatibility version 1.0.0, current version 22.0.0)
        /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility version 1.0.0, current version 275.0.0)
        /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 368.18.0)
        /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLU.dylib (compatibility version 1.0.0, current version 1.0.0)
        /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib (compatibility version 1.0.0, current version 1.0.0)
        /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 88.1.2)
(0003473)
Eric Wing (reporter)
2005-12-21 15:04

Again, I think you are over-thinking the situation. You'll get a very similar long list if otool -L /usr/X11R6/lib/libGL.dylib. One interesting twist is that this specific dylib depends on the OpenGL.framework. But in user land, nobody cares. So effectively there is a one-to-one mapping between the dylib version of OpenGL and the framework version of OpenGL (ignoring the GLU stuff). Now if I picked a better example of a library that existed as both a dylib and a framework, you would see an identical list of dependencies using otool -L. But you would generally never care about that list. (OpenAL is a candidate. They don't ship with a dylib, but the source is available so you could compile it yourself as a dylib and see for yourself.)

The results of otool -L are showing you how their OpenGL was compiled (linked). But to we the end users who need to build an OpenGL based app, this information is mostly irrelevant. In practice, we just do
gcc.c -lGL
gcc prog.c -framework OpenGL

We generally don't care if OpenGL was written in C++ with gcc 4.0 (hence libstd++), needs Carbon, Cocoa, AGL, etc. Everything on OS X always links against libSystem.B, but most people don't even know the component exists. We don't link explicitly against though flags if we don't use their components directly in our own code. (I remembered once questioning why you are explicitly linking against AGL in your FindOpenGL.cmake package. I think it is only necessary if you use AGL stuff in your code, or something in a public header #includes something AGL specific.)

So I reiterate, the Find*.cmake modules I have submitted already accomplishes the illusion of a unified FIND_LIBRARY command. If I use FindPackage(osg) to do the effective FIND_LIBRARY command, it brings me back the same set of libraries regardless of whether it is a framework or dylib.
I will either get libosg.dylib or osg.framework. It is one-to-one in this respect.

Now osg includes OpenGL and OpenThreads in its public headers so there is a direct dependency for your app on these two other libraries. But this is the same regardless of whether you have a dylib or framework. So you either have
gcc prog.cpp -losg -lOpenThreads -lGL
or
gcc prog.cpp -framework osg -framework OpenThreads -framework OpenGL.

The above is also true for Linux.

So again, you see the relationship is one-to-one (ignoring the GLU aspect). This is why I think you're over-thinking the problem and this should all just work without needing to dissect things with otool.




 

(0003474)
Bill Hoffman (manager)
2005-12-21 15:21

Really, I am not trying to be difficult... ):

I just do not fully understand frameworks.

From here:
http://developer.apple.com/documentation/DeveloperTools/Conceptual/MachOTopics/index.html [^]

It says you do this to create a framework:

gcc -c -o Peace.o Peace.c

gcc -c -o Love.o Love.c

mkdir -p Bliss.framework/Versions/A

gcc -dynamiclib -o Bliss.framework/Versions/A/Bliss Peace.o Love.o

cd ./Bliss.framework/Versions && ln -sf A Current

cd ./Bliss.framework && ln -sf Versions/Current/Bliss Bliss

This will have to work for both Xcode and makefiles to add it to cmake. So, in this case there is no .dylib file, instead there is a framework. So, I suppose FIND_LIBRARY on the Mac could look for Frameworks if the framework name was in the NAMES section of FIND_LIBRARY. Then CMake would keep the whole path to the framework around, and when you linked it to a target, instead of breaking it into -L/path -lname, it would do -F/path -framework name, if the thing it found did not end in .a or .dylib, but was found in a directory structure that was a framework.
(0003475)
Bill Hoffman (manager)
2005-12-21 15:34

I took a look at FindSLD.cmake, and it uses FIND_PATH not FIND_LIBRARY to find the location of a framework. I am thinking that cmake really needs a FIND_FRAMEWORK command. It can then be used in things like FindSDL.cmake. So, from a cmake users point of view, they would just do FindPackage(SLD) and inside FindSLD, it would try to find the library, includes, or package depending on the OS and what it found.
(0003476)
Eric Wing (reporter)
2005-12-21 17:08

> Really, I am not trying to be difficult... ):
> I just do not fully understand frameworks.

Right. Don't over-think them. Ultimately they are just a dylib with a fancy wrapper.
Now the fancy wrapping allows for several interesting features:
+ Header files are included with the package
+ Versioning
+ Central location for release, debug, profile binaries

And typically, framework developers utilize features that are not framework specific (i.e. apply to dylibs also), but tend to be less utilized by dylib developers.
+ install_name
+ Prebinding

With respect to how you link to them, it is just a substitution from -lfoo to -framework
foo. Don't think about umbrella frameworks or indirect dependencies. These problems you are focusing would affect dylibs in the exact same way. So the fact that dylibs currently work means that frameworks should work. If the framework has a problem, then the dylib would have that same problem.

In my experience, I think the tricker part of the framework is finding the header files for the fact that there is no physical directory with the name that exists in your include (e.g. no directory OpenGL for <OpenGL/gl.h>). This difficulty is compounded by usage patterns, e.g. does the user do #include "SDL.h" or <SDL/SDL.h>. (SDL guidelines require the former for portability because of oddballs like FreeBSD ports, while things like OpenSceneGraph require the latter usage because of the enormous number of headers and potential name conflicts.









(0003477)
Eric Wing (reporter)
2005-12-21 17:18

> It says you do this to create a framework:
> gcc -c -o Peace.o Peace.c
> gcc -c -o Love.o Love.c
> mkdir -p Bliss.framework/Versions/A
> gcc -dynamiclib -o Bliss.framework/Versions/A/Bliss Peace.o Love.o
> cd ./Bliss.framework/Versions && ln -sf A Current
> cd ./Bliss.framework && ln -sf Versions/Current/Bliss Bliss

Okay. But I would recommend you support or leave open the possibility of versioning. I'm not an expert on this, but I believe the symbolic links to Current get remapped accordingly (e.g. link to B instead of A). There seem to be two build flags associated with this too. -compatibility_version and -current_version.

Frameworks typically seem to have a Resources directory too (and Headers). These also get directories created and symbolic links setup even if nothing is to reside in them I believe.
(0003478)
Eric Wing (reporter)
2005-12-21 18:10

> This will have to work for both Xcode and makefiles to add it to cmake.  

Yes. By the way, if you do this for the makefiles, you will probably be the first and only publicly available build system that has done this. It might earn bragging rights. For Xcode, I would encourage you to leverage the native framework support in Xcode rather than bootstrapping a manual construction. I suspect more advanced features will be accessible this way so CMake need not explicitly support all of them. And I suspect there may be magic with the Xcode debugger with native target features that know how to set the DYLD_LIBRARY_PATH environment, etc.

> I took a look at FindSLD.cmake, and it uses FIND_PATH not FIND_LIBRARY to
> find the location of a framework.

Yes, my FindSDL.cmake script bypasses FIND_LIBRARY for frameworks. But this is only because I have to trick CMake into supporting this. The ideal solution would let me use a unified FIND_LIBRARY to do this, and TARGET_LINK_LIBRARIES would invoke the proper flags behind the scenes. My scripts are ugly kludges that I don't think are necessary because I think CMake can be enhanced do this a clean and elegant way.

>  I am thinking that cmake really needs a FIND_FRAMEWORK command. 
No, I disagree strongly with this. I really think this can and should be made transparent to the end scripters. I am personally very sick of having to go in and hack up somebody else's Find*.cmake script because they weren't OS X framework aware. I think there should just be one API that covers everybody. Now if OS X needs to add something to the names list in the NAMES section (e.g. NAMES gl OpenGL), or add a path to the search list (e.g. ~/Library/Frameworks), that's reasonable, but I shouldn't have to write code that introduces conditionals and branches to make decisions if I'm using a framework or dylib. That's just a pain. And it starts feeling like the criticisms I have of Autotools not being very auto because I have to implement everything myself.

Now I would say there might be a need for a new command called FIND_INCLUDE_PATH to deal with the "physical directory doesn't actually exist problem". You could just change FIND_PATH to deal with this, but FIND_PATH implies (to me) there really is a directory that exists. So when developers are looking for include paths, they should use FIND_INCLUDE_PATH, and when they need just to find a regular old, but real, path, use FIND_PATH. This new API call might allow for the <SDL/SDL.h> vs "SDL.h" problem to be handled better too.


> So, in this case there is no .dylib file, instead there is a framework.  So, I suppose
> FIND_LIBRARY on the Mac could look for Frameworks if the framework name
> was in the NAMES section of FIND_LIBRARY.  Then CMake would keep the
> whole path to the framework around, and when you linked it to a target, instead of
> breaking it into -L/path -lname, it would do -F/path -framework name, if the thing it
> found did not end in .a or .dylib, but was found in a directory structure that was a
> framework.

Yes, I think this is the more reasonable way to do it. The goal should be to keep the process as transparent and general as possible. So I think the usage pattern might look like this:

FIND_LIBRARY(SDL_LIBRARY
    NAMES
    SDL # Will handle SDL.framework, libSDL.dylib, libSDL.so, SDL.lib, etc
    SDL11 # For FreeBSD ports naming quirk: libSDL11.so
    PATHS
    $ENV{SDLDIR}/lib
    ~/Library/Frameworks
    /Library/Frameworks
    /usr/local/lib
    /usr/lib
    /sw/lib
    /opt/local/lib
)

My emphasis is that the name "SDL" should automatically try to resolve all the variants. Presumably search path order matters. So if you have both the Fink libSDL.dylib version installed and the SDL.framework version installed, because I specified the framework version will be picked because I specified the /Library/Frameworks path before the /sw/lib path. If I had swapped the order, the Fink version would be picked first.

And then, as you said, the TARGET_LINK_LIBRARIES (or whatever uses SDL_LIBRARY for linking) will automatically determine if the -L/-l or -F/-framework flags should be used.

 Issue History
Date Modified Username Field Change


Copyright © 2000 - 2018 MantisBT Team