Handling header files dependencies with cmake

2019-01-16 07:11发布

问题:

I am using CMake on a small C++ project and so far it works great... with one twist :x

When I change a header file, it typically requires recompiling a number of sources files (those which include it, directly or indirectly), however it seems that cmake only detects some of the source files to be recompiled, leading to a corrupted state. I can work around this by wiping out the project and rebuilding from scratch, but this circumvents the goal of using a make utility: only recompiling what is needed.

Therefore, I suppose I am doing something wrong.

My project is very simply organized:

  • a top directory where all resources sit, the main CMakeLists.txt sits there
  • a "include" directory where all public headers lies (in various subdirectories)
  • a "src" directory where all the subdirectories for sources files are, the src CMakeLists.txt sits there
  • a CMakeLists.txt per subdirectory in the "src" directory

The main directory has:

cmake_minimum_required(VERSION 2.8)

project(FOO)

set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)

# Compiler Options
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -std=c++0x -Wall -Wextra -Werror")

include_directories($(FOO_SOURCE_DIR)/include)

add_subdirectory(src)

The "src" directory:

add_subdirectory(sub1)
add_subdirectory(sub2)
add_subdirectory(sub3)
add_subdirectory(sub4)

add_executable(foo main.cpp)

target_link_libraries(foo sub1 sub2 sub3 sub4)

Where sub4 depends on sub3 which depends on sub2 which depends on sub1

And an example of a subdirectory (sub3):

set(SUB3_SRCS
    File1.cpp
    File2.cpp
    File3.cpp
    File4.cpp
    File5.cpp
    File6.cpp
    )

add_library(sub3 ${SUB3_SRCS})

target_link_libraries(sub3 sub1 sub2)

I'd be glad if anyone could point my mistake to me, searching here or on CMake didn't yield anything so I guess it's very easy or should work out of the box...

(for reference, I am using cmake version 2.8.2 on MSYS)

EDIT:

Thanks to Bill's suggestion I have checked the depend.make file generated by CMake, and it is indeed lacking (severely). Here is an example:

src/sub3/CMakeFiles/sub3.dir/File1.cpp.obj: ../src/sub3/File1.cpp

Yep, that's all, non of the includes were referenced at all :x

回答1:

You should look at the depend.make files in your binary tree. It will be in CMakeFiles/target.dir/depend.make. Try to find one of those that is missing a .h file that you think it should have. Then create a bug report for cmake or email the cmake mailing list.



回答2:

I just hit the same issue. After changing paths in include_directories() from absolute to relative it added appropriate dependencies.

Looks like CMake tries to guess which headers are system and which are project related. I suspect that directories that starts with / passed as -isystem /some/path and thus are not presented in generated dependencies.

If you can't replace ${FOO_SOURCE_DIR} with relative path you can try to calculate relative path using appropriate CMake functions. I.e.:

file(RELATIVE_PATH FOO_SOURCE_REL_DIR
     ${CMAKE_CURRENT_SOURCE_DIR}
     ${FOO_SOURCE_DIR}/.)
include_directories(${FOO_SOURCE_REL_DIR}/include)


回答3:

Apparently cmake removes system include paths from the dependency trees (thank you @ony for this hint). This probably makes sense most of the time, but sometimes cmake doesn't know what the compiler thinks is a system path or not. We are using a custom gcc build that ignores /usr/include, but cmake thinks it doesn't ignore it. To force cmake to make /usr/include a dependency that is NOT optimized away, try this trick: prepend /. to the path.

I am trying to make all of the library dependencies use the cmake dependency feature, including certain "3rd" party libraries that are not always installed by default on Linux or even available. For example, Z-lib compression.

The following interface target worked fine if the Z lib includes were not in /usr/include, but would break if they were.

find_package(ZLIB REQUIRED)
message(status "found zlib ${ZLIB_LIBRARIES}")
message(status "found zlib includes ${ZLIB_INCLUDE_DIRS}")
target_link_libraries(zlib_target INTERFACE ${ZLIB_LIBRARIES})
target_include_directories(zlib_target INTERFACE ${ZLIB_INCLUDE_DIRS})

I changed the last line to

target_include_directories(zlib_target INTERFACE /.${ZLIB_INCLUDE_DIRS})

and it worked. Now targets that depended on zlib_target would automatically get -I/./usr/include during compilation.



回答4:

Did you run cmake before or after adding the includes to your cpp files?

I ran into this problem and re-running cmake fixed it. I'd added the include post-cmake.