[CMake] intercomponent link dependencies?

Ryan Pavlik rpavlik at iastate.edu
Thu Jan 7 20:26:54 EST 2010


 Not a problem - figured putting together some clean templates could
probably help me and my colleagues out too, anyway.

Your comment about the syntax makes me understand a bit better...
"MyPackage" isn't a name, it's a placeholder for one of the many general
packages of third party functionality that you use.  I am guessing that
you're putting the detection for every library that your app can/must use
into one "find" module, when you actually probably should have a directory
in your source tree added to your CMAKE_MODULE_PATH that contains a large
number of find modules, some of which are interdependent (just like how the
example I sent uses find_package(BLAS) - which is a module that comes
bundled with cmake).  Once you make enough cmake-based build systems, you'll
probably find yourself hanging on to a repository of assorted find modules.

I've pasted a simple example here, since it pretty well meshes with a common
use case.
The sample project uses OpenSceneGraph (plus its optional components osgDB
and osgUtil), as well as a large suite of libraries with internal
dependencies known as VR Juggler 2.2.  OSG has modules that come with CMake,
while I had to make my own for VR Juggler.

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

cmake_minimum_required(VERSION 2.6)
project(minimal-vrjuggler-osg)

# Locally-developed modules dist'ed with this app
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)

# This should handle all dependencies, including OpenGL, GMTL, and so on
find_package(VRJuggler22 REQUIRED)

find_package(OpenSceneGraph REQUIRED osgUtil osgDB)

# Search for includes in these directories
include_directories(
    ${OPENSCENEGRAPH_INCLUDE_DIRS}
    ${VRJUGGLER22_INCLUDE_DIRS})

# Build the project
set(SOURCES
    main.cpp
    theapp.cpp
    theapp.h)

add_executable(example ${SOURCES})
target_link_libraries(example ${OPENSCENEGRAPH_LIBRARIES}
${VRJUGGLER22_LIBRARIES})

-----------

That's an entire functional CMakeLists.txt file - though behind the scenes
we're linking to a ton of libraries in a ton of different directories.
(Read through the cmake docs online a few times - you got the imported
target thing but missed the "REQUIRED" flag that you can pass to
find_package...  If you use the FindPackageHandleDefaultArgs call as
suggested by the readme.txt, it takes care of handling QUIET and REQUIRED
for you.)

So, I made a separate FindWhatever.cmake file for each library that belongs
to VR Juggler, and set it up like the examples from the last email, treating
them each as a separately-usable library (since they are).   Since most of
the time, however, if you're writing a VR Juggler app, you're going to want
a large chunk of the tree by default, I made a FindVRJuggler22.cmake
"meta-module" sort of thing: means that for most folks, all that's needed in
the cmakelists is find_package(VRJuggler22 REQUIRED) or at most
find_package(VRJuggler22 COMPONENTS VRJOGL22 Tweek12).  So, I have a whole
bundle of find modules written (actually, in total, I have ~20 find modules
in a directory I share between all my projects, with another ~20 modules
that provide functions: some utilities for find scripts, some for use
directly by a cmakelists) that I share between my projects, but because each
find module knows its own dependencies, it works it out.  In that sense,
more files is better: it means you're keeping your find modules well-focused
and modular.  (If the library comes in a separate zip/targz file, it
probably should have a separate find module - don't just smush them all into
one massive file.)

Here's the dependency tree for the VR Juggler find modules - each node is a
separate .cmake file that goes in a directory in my source tree, and each of
those find modules are set up just like the examples from the last email.
(in graphviz/DOT format)  :

digraph {
    // a module that, when included, provides a function definition that can
remove duplicate
    // libraries from a _LIBRARIES list safely, without breaking the DEBUG
RELEASE GENERAL annotations
    // I wrote this one, so it's in my project in a directory with the
custom find modules
    CleanLibraryList;

    subgraph cluster_bundled {
        label = "Included with cmake 2.8.0";
        FindBoost;
        FindOpenSceneGraph -> { FindOpenThreads; FindosgALL; }
    }

subgraph cluster_vrjuggler22 {
            label = "VR Juggler 2.2 suite: All require CleanLibraryList and
CleanDirectoryList, and recommend FindFlagpoll";
            FindTweek12 -> FindVPR20;
            FindJCCL12 -> FindVPR20;
            FindGadgeteer12 -> { FindJCCL12; FindVPR20; }
            FindSonix12 -> FindVPR20;
            FindVRJ22 -> { FindJCCL12; FindGadgeteer12; FindSonix12;
FindVPR20; }
            FindVRJOGL22 -> FindVRJ22;
            FindVRJuggler22 -> FindVRJOGL22;
        } // commented out to make the diagram easier to read ->
{FindFlagpoll; CleanLibraryList; CleanDirectoryList;}
        FindVPR20 -> { FindBoost; FindCPPDOM; }
        FindGadgeteer12 -> FindGMTL;
        FindSonix12 -> FindGMTL;
}

Hope this helps!

Ryan



On 1/7/10 5:13 PM, Nico Schlömer wrote:

Wow, thanks for the elaborate answer!
I learn a lot just going through the examples.

One thing for me to understand first:



 Ah, so if those components are always necessary (that is, A always needs B,
and B always needs C), there's no need to use the "components" option.


 Ah, I found the components thing neat for its syntax, as one can
(quite semantically) say

FIND_PACKAGE( MyPackage COMPONENTS coolCompOfMyPackage anotherOne )
# very much like done in FindBoost.cmake

Of course there's not only a,b,c, but a whole set of libraries a,...,z
under the roof of MyPackage with a more or less complicated dependency
tree. Also, it would depend on the installation of MyPackage whether
all the libraries are actually there; for example, and installation
with only "b" and "c" would be possible.

Anyway, instead of hardcoding a,b,c one could possible FOREACH through
a (hardcoded) list of components, such as

SET( ALL_LIBS "a" "b" "c" "d" [...] "z" )

I guess how one would do that with the above suggestion is to further
define MYPACKAGE_{$LIB}_FOUND, and then in the CMakeLists.txt

FIND_PACKAGE( MyPackage )
IF( MYPACKAGE_a_FOUND )
  # add the necessary stuff to TARGET_LINK_LIBRARIES, for example
ELSE()
  MESSAGE( FATAL_ERROR "Too bad, we need a." )
END()

The disadvantage I see here is that FindMypackage.cmake had to be
adapted every time a new library makes it into MyPackage. Also, the
code in CMakeLists.txt would get longer than what I thought would be
nice [being:

FIND_PACKAGE( MyPackage COMPONENTS "a" "g" "t" )
# FATAL_ERRORing out if either of a, g, t hasn't been found,
# otherwise providing a slim ${MyPACKAGE_LIBRARIES} to be appended to
# TARGET_LINK_LIBRARIES or something.

].

Cheers,
Nico




-- 
Ryan Pavlik
Human-Computer Interaction Graduate Student
Virtual Reality Applications Center
Iowa State University
rpavlik at iastate.eduhttp://academic.cleardefinition.com/
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.cmake.org/pipermail/cmake/attachments/20100107/72530fb4/attachment.htm>


More information about the CMake mailing list