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

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • 1
    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? – Marcus D. Hanwell Sep 18 '11 at 18:51
  • @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. – Matthieu M. Sep 18 '11 at 18:58
  • 3
    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. – Marcus D. Hanwell Sep 18 '11 at 23:31
  • @Marcus: I only have Windows at home, unfortunately, so never witnessed it on other environments for I don't use them. – Matthieu M. Sep 19 '11 at 06:14
  • Have you tried listing the headers in `set(SUB3_SRCS …)` as well? I always do that, and haven't encountered any such problems. – Emil Laine Oct 07 '15 at 08:50

5 Answers5

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.

Yamaneko
  • 3,433
  • 2
  • 38
  • 57
Bill Hoffman
  • 1,742
  • 11
  • 12
  • Ah thanks, I search for the dependencies but could not locate where they are, I'll check this file. – Matthieu M. Sep 21 '11 at 16:29
  • 2
    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*... – Matthieu M. Oct 02 '11 at 10:54
16

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)
ony
  • 12,457
  • 1
  • 33
  • 41
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.

Edward Strange
  • 40,307
  • 7
  • 73
  • 125
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.

Mark Lakata
  • 19,989
  • 5
  • 106
  • 123
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.

Giuseppe Galano
  • 10,412
  • 1
  • 12
  • 10