Handling header files dependencies with cmake
Asked Answered
S

5

57

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, none of the includes were referenced at all :x

Scrobiculate answered 18/9, 2011 at 10:54 Comment(5)
It would be helpful to have more details on the files that are not compiled correctly. The dependency scanner in CMake can certainly go through a header that includes another header for example, and correctly trigger a recompile of the dependent source file. Can you share a minimal example where you hit this behavior?Lammas
@Marcus: It's quite erratic and I am wont to dump my project here, bit big for a question. From your answer though I understand that it should not happen... I forgot to precise I am on Windows, invoking from the MSYS (based on MinGw) shell, could it be an issue ? Also, I use cmake to build the LLVM/Clang project on MSYS as well, and never had the issue on it.Scrobiculate
Sounds like it could be a bug in the dependency scanner on Windows/MSYS. I don't do a lot of work in that environment, and wonder if you have ever seen similar behaviour with Unix Makefiles projects on Linux/Mac or MSVC projects.Lammas
@Marcus: I only have Windows at home, unfortunately, so never witnessed it on other environments for I don't use them.Scrobiculate
Have you tried listing the headers in set(SUB3_SRCS …) as well? I always do that, and haven't encountered any such problems.Tripletail
J
20

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.

Jacobah answered 21/9, 2011 at 16:22 Comment(2)
Ah thanks, I search for the dependencies but could not locate where they are, I'll check this file.Scrobiculate
Finally got to check on it, depend.make basically indicates that a '.obj' only depends on the source file that generated it, and does not talk, at all, about the include files. Hum...Scrobiculate
U
17

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)
Update answered 4/11, 2014 at 17:52 Comment(0)
O
3

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.

Oder answered 30/12, 2012 at 2:28 Comment(0)
O
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.

Overload answered 22/3, 2016 at 21:50 Comment(0)
I
0

Make sure that the include statement for the missing header is placed before the first program instruction. In my case cmake depend.make was not including the header file, because it was following the first program instruction.

Ivaivah answered 12/9, 2022 at 9:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.