[CMake] Following dependencies through generated header file

Michael Hertling mhertling at online.de
Sun Oct 3 00:55:11 EDT 2010


On 10/03/2010 02:01 AM, Óscar Fuentes wrote:
> Michael Hertling <mhertling at online.de>
> writes:
> 
>>> It would be very convenient to inspect the header files referenced from
>>> `zoo.cpp' and associate them with `bar.inc', so when some of those
>>> header files are touched `bar.inc' (and hence `foo.cpp') is
>>> automatically re-built.
>>>
>>> Is this possible?
>>
>> Yes, with a bit of trickiness:
> 
> [snip]
> 
> Thanks for idea, Michael, sounds interesting, although having to use a
> library is an incovenience, mainly for the people who use IDEs. (The
> project I'm working on would need more than 20 such libraries)

Yes, that's annoying. AFAIK, you cannot hook into CMake's built-in
dependency scanner for the time being, so it's quite difficult to
achieve your goal in a convenient manner, but perhaps there's a
possibility to do better than using a dummy library target:

Place the following files in your CMAKE_SOURCE_DIR:

foo.cpp:
#include "bar.inc"

zoo.cpp:
#include "zoo.h"

zoo.h:
#define ZOO_H

scandeps.cmake:
EXECUTE_PROCESS(COMMAND gcc -M ${SOURCE} OUTPUT_VARIABLE DEPS)
STRING(REGEX REPLACE "^.*: " "" DEPS ${DEPS})
STRING(REGEX REPLACE " \\\\\n " "\n" DEPS ${DEPS})
FILE(WRITE ${OUTPUT}.in "SET(${VARIABLE}\n${DEPS})\n")
CONFIGURE_FILE(${OUTPUT}.in ${OUTPUT} COPYONLY)

CMakeLists.txt:
CMAKE_MINIMUM_REQUIRED(VERSION 2.8 FATAL_ERROR)
PROJECT(GENDEP CXX)
IF(NOT EXISTS ${CMAKE_BINARY_DIR}/deps.cmake)
    FILE(WRITE ${CMAKE_BINARY_DIR}/deps.cmake "")
ENDIF()
INCLUDE(${CMAKE_BINARY_DIR}/deps.cmake)
MESSAGE("DEPS: ${DEPS}")
INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR})
ADD_LIBRARY(foo.aux SHARED EXCLUDE_FROM_ALL foo.cpp bar.inc)
SET_TARGET_PROPERTIES(foo.aux PROPERTIES OUTPUT_NAME foo)
ADD_CUSTOM_COMMAND(OUTPUT bar.inc
    COMMAND ${CMAKE_COMMAND} -E touch bar.inc DEPENDS ${DEPS})
ADD_CUSTOM_TARGET(foo ALL
    COMMAND ${CMAKE_COMMAND}
            -DSOURCE=${CMAKE_SOURCE_DIR}/zoo.cpp
            -DOUTPUT=${CMAKE_BINARY_DIR}/deps.cmake
            -DVARIABLE=DEPS
            -P ${CMAKE_SOURCE_DIR}/scandeps.cmake
    COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR}
                             --target foo.aux)

The basic idea is the following: The custom target foo invokes a script
scandeps.cmake which uses an external dependency scanner - here, gcc's
-M facility - to get the files zoo.cpp depends on. This list is tweaked
a bit and written to deps.cmake as a CMake SET() command for a variable
DEPS; the deps.cmake is included in the CMakeLists.txt. Finally, target
foo builds the actual library as target foo.aux using CMake's --build
option. During the initial CMake run, bar.inc receives no dependencies,
but the subsequent build will generate deps.cmake, and as this file is
included in CMakeLists.txt, the final "cmake --build" command will re-
run CMake before building foo.aux - then with the correct and updated
dependencies of bar.inc. The same holds if bar.inc's dependencies are
changed later, i.e. the gcc -M output sometime differs. Additionally,
scandeps.cmake uses CONFIGURE_FILE() for the generation of deps.cmake
in order to avoid unnecessary CMake runs. This approach works on *nix
with Makefiles, but I don't know if it's suitable for other generators
or IDE's.

Regards,

Michael


More information about the CMake mailing list