[CMake] Looking for advice on creating targets that use external packages

Michael Hertling mhertling at online.de
Fri Feb 25 02:02:35 EST 2011


On 02/24/2011 06:00 PM, L. A. Pritchett-Sheats wrote:
> 
> I'm working on a software project that requires about a dozen external
> packages to build our libraries. What is the correct way to define the
> dependencies for our targets that depend on these external libraries?
> 
> So far I have created FindXXX.cmake files for each external library. These
> files set XXX_INCLUDE_DIRS and XXX_LIBRARY or XXX_LIBRARIES. Should I make
> a target in our build for XXX?
> 
> Example A:
> in the root CMakeLists.txt file:
> find_package(UnitTest)
> add_library(unittest++ STATIC IMPORTED)
> set_target_property(IMPORTED_LOCATION ${UnitTest_LIBRARY})
> 
> then in a subdirectory foo/CMakeLists.txt
> add_executable(test_foo test/Main.cpp test/test_foo.cpp)
> add_test(foo test_foo)
> target_link_libraries(test_foo foo unittest++)
> 
> or should each subdirectory that contains a CMakeLists.txt file that needs
> this library to link to an executable explicitly define the library in the
> target's target_link_libraries call? Like this....
> 
> Example B:
> in foo/CMakeLists.txt:
> find_package(UnitTest)
> add_executable(test_foo test/Main.cpp test/test_foo.cpp)
> add_test(foo test_foo)
> target_link_libraries(test_foo foo ${UnitTest_LIBRARY})
> 
> 
> We've been following Example A, but have run into numerous link problems
> because we are using NetCDF, HDF5, ExodusII and a mesh framework package
> that depends on all 3. I've looked at two other projects that use CMake:
> Trilinos and Yarp. Both follow Example B and since we are having so much
> difficulty resolving links at run time, I wanted to know if we are
> defining these dependencies correctly.
> 
> Thanks for your time.

At first, your FindXXX.cmake files, provided XXX is a library, should
define XXX_LIBRARIES in any case since this variable is meant to hold
XXX_LIBRARY - usually the result of a FIND_LIBRARY() call - along with
XXX's prerequisite libraries. In other words, XXX_LIBRARIES contains
anything necessary to link against XXX. Mentioning UnitTest_LIBRARY
instead of UnitTest_LIBRARIES in TARGET_LINK_LIBRARIES() might be a
cause for the problems you report on.

Furthermore, defining imported targets for external libraries should be
the find module's job; it's intended as a more flexible alternative to
the direct specification of a library's full path, e.g. The necessary
prerequisites are specified appropriately enough by target properties
IMPORTED_LINK_INTERFACE_LIBRARIES. As it were, dropping them might
result in unresolved dependencies at link time.

Finally, if the target "foo" is sufficiently independent, particularly
if it can be configured and built by itself, I would tend to place the
needed FIND_PACKAGE() calls in foo's CMakeLists.txt as in example B
above, even if these calls appear in other CMakeLists.txt files, too.
In that way, this part of the project becomes more self-contained and
you do not need to rely on any FIND_PACKAGE() calls to be made before
somewhere in the current scope. Additionally, well written find modules
can be safely called multiple times and due to the cache, they do their
work usually once. OTOH, if "foo" is tightly connected with other parts
of the project, e.g. if it is a module which isn't useful on its own or
doesn't make sense without a main program, it's probably appropriate to
have a superordinate CMakeLists.txt call FIND_PACKAGE() and rely on the
results.

In summary, I'd recommend to write FindXXX.cmake files for external
libraries which set up imported targets *with* their prerequisites,
i.e. IMPORTED_LOCATION and IMPORTED_LINK_INTERFACE_LIBRARIES target
properties. Note that the find modules need to protect the imported
targets from being defined twice; this is important when the same
FIND_PACKAGE() call is multiply issued within the same scope. The
imported targets, however, should be conveyed to the caller via the
XXX_LIBRARIES variables, so the calling CMakeLists.txt file can just
say TARGET_LINK_LIBRARIES(... ${XXX_LIBRARIES}) regardless whether the
FindXXX.cmake file uses imported targets or returns full paths directly.

BTW, which link problems do you have exactly? You have also mentioned
a "difficulty resolving links *at run time*", so are you sure these
problems are caused by the find modules and the way you use them?

Regards,

Michael


More information about the CMake mailing list