[CMake] ${PROJECT}-config.cmake

Michael Hertling mhertling at online.de
Tue Jun 8 17:33:48 EDT 2010


On 06/07/2010 05:24 PM, Biddiscombe, John A. wrote:
> When using the install target command as follows
> 
>   INSTALL (
>       TARGETS
>           ${HDF5_LIB_TARGET}
>       EXPORT
>           ${HDF5_EXPORTED_TARGETS}
>       LIBRARY DESTINATION lib COMPONENT libraries
>       ARCHIVE DESTINATION lib COMPONENT libraries
>       RUNTIME DESTINATION bin COMPONENT libraries
>   )
> 
> cmake very nicely generates an HDF5-config.cmake file in the build directory, and at install time, in the install directory. This is great.

AFAIK, those files generated by the INSTALL(TARGETS ... EXPORT ...)
and INSTALL(EXPORT ...) commands concerning imported targets are not
intended to be used as config files for a package; accordingly, they
should not be named *-config.cmake as this has a special meaning for
FIND_PACKAGE(). Preferably, they are named *-targets.cmake instead.

> However, I have project A(paraview) with two subdirectories B (hdf5) and C (mystuff), where C "uses" B. All is well except that if C includes the hdf5-config.cmake from the Build directory as generated by B (ADD_LIBRARY(vtkhdf5 SHARED IMPORTED)) , cmake produces error messages along the lines of
> 
> CMake Error at D:/cmakebuild/pv-shared/Utilities/hdf5-1.8/HDF5-config.cmake:16 (ADD_LIBRARY):
> add_library cannot create imported target "vtkhdf5" because another target
> with the same name already exists.

So, B and C are built within the same project A, right? In this case,
you could refer directly to B's targets whereas imported targets are
meant to be provided by outside projects. Btw, where do you get the
hdf5-config.cmake from for its inclusion at CMake time, i.e. before
installation? IIRC, the time it is generated at and the location it
is written to are not mentioned in the docs, and can you be sure it
is not changed during installation? Here, IMO, you're relying on
undocumented behaviour.

> Naturally, when building C standalone using the install directory of standalone B, all works fine.

This means C could *also* be built using an already installed B, right?
In this case, B should provide a config file, say b-config.cmake, that
includes the targets file, say b-targets.cmake, and protects that
inclusion to prevent multiple definitions of the targets, e.g.:

IF(NOT TARGET "vtkhdf5")
    INCLUDE(<ThePathTo>/b-targets.cmake)
ENDIF()
SET(B_LIBRARIES "vtkhdf5")
SET(B_INCLUDE_DIRS ...)
SET(B_DEFINITIONS ...)

When building C, you can issue FIND_PACKAGE(B ...) to enable B, but you
should not mix these cases, i.e. you should set up C to use either the
"builtin" B or the externally installed B, e.g. in C's CMakeLists.txt:

IF(USE_BUILTIN_B)
    LIST(APPEND LIBRARIES "vtkhdf5")
    ADD_DEFINITIONS(<TheDefinitionsOfB>)
    INCLUDE_DIRECTORIES(<ThePathToHeadersOfB>)
ELSE()
    FIND_PACKAGE(B REQUIRED ...)
    LIST(APPEND LIBRARIES ${B_LIBRARIES})
    ADD_DEFINITIONS(${B_DEFINITIONS})
    INCLUDE_DIRECTORIES(${B_INCLUDE_DIRS})
ENDIF()
...
TARGET_LINK_LIBRARIES(... ${LIBRARIES})

Thus, you have an explicit distinction between the alternatives without
relying on the availability of B's targets file in the build directory,
and if B can be installed as a stand-alone package it should provide a
config file for itself anyway.

> Everything would be simple, if cmake would simply skip the imported target from B the second time it is loaded when called from the subproject C, then a single set of config and cmakelists would work all the time, but as it is, extra logic needs to be added.
> 
> Is there any easy way of telling project C, load the config file if B is not part of the same build so that I can reuse the same syntax between separate build or common builds. I'd like to use the generated hdf5-config.cmake files because they have all the necessary properties set correctly (like when target name is not the same as lib name etc etc, which makes things harder like below).
> 
> # Import target "vtkhdf5" for configuration "Debug"
> SET_PROPERTY(TARGET vtkhdf5 APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
> SET_TARGET_PROPERTIES(vtkhdf5 PROPERTIES
>   IMPORTED_IMPLIB_DEBUG "D:/cmakebuild/pv-shared/bin/Debug/vtkhdf5ddll.lib"
>   IMPORTED_LINK_INTERFACE_LIBRARIES_DEBUG "ws2_32;wsock32;C:/Program Files/MPICH2/lib/mpi.lib;vtkzlib"
>   IMPORTED_LOCATION_DEBUG "D:/cmakebuild/pv-shared/bin/Debug/vtkhdf5ddll.dll"
>   )

If you really want to use the targets file of B internally during the
build - which I would not recommend - you can protect each INCLUDE()
of that file with "IF(NOT TARGET ...)" as shown above in order to
reliably prevent multiple inclusions.

Regards,

Michael


More information about the CMake mailing list