why does cmake compiles everything after git commi

2019-08-08 23:36发布

问题:

Lets say I have a code compiling at some time with cmake 2.8 on linux.

I change a file "my_changed_file", run cmake, and only this one file is built. So far so good.

Now i want to commit this:

git add my_changed_file
git commit

If I run cmake again, i'd expect nothing happens. But all my files are recompiled, despite I didn't touched anything! The timestamp appears to be untouched when I do ls -l.

I do have these lines:

execute_process(
  COMMAND git describe --abbrev=8 --dirty --always --tags
  WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
  OUTPUT_VARIABLE GIT_CODE_VERSION
  OUTPUT_STRIP_TRAILING_WHITESPACE
)
add_definitions("-DCODE_VERSION=${GIT_CODE_VERSION}")

But it only affect the file main.cpp

What is happening ?

thanks

回答1:

CMake doesn't track, which source file can be affected by particular compile definition. When compile definitions changes, CMake assumes that all sources should be rebuilt.

Better approach is to use configured header file. So, when content of this file is changed, only those sources will be recompiled which includes this file(directly or indirectly):

version.h.in:

#define CODE_VERSION @GIT_CODE_VERSION@

main.cpp:

#include "version.h"
...

CMakeLists.txt:

# Calculate value of variable GIT_CODE_VERSION
...
configure_file("version.h.in" "version.h")

Nice thing with configure_file is that it doesn't update resulted file's timestamp if its content wouldn't be changed. So, if you rerun cmake without git commit, nothing will be recompiled on next build. Only rerunning cmake after git commit will force main.cpp file(and only it) to be recompiled on next build.


Another way is to use COMPILE_DEFINITIONS property on specific source files instead of target-wide one (which is affected by add_definition() call):

set_property(SOURCE main.cpp APPEND
    PROPERTY COMPILE_DEFINITIONS "-DCODE_VERSION=${GIT_CODE_VERSION}")

Changing this property via cmake call will be detected by build system, so next build will recompile main.cpp and only it.

Unfortunately, this approach doesn't work as expected in case of makefile generators: even if compile definitions are changed for specific source, all sources (for same target) will be rebuilt. This is known limitation.