Search code examples
cmakegcovninja

Force CMake to use absolute include path


I have a project whose directory layout looks like:

- src/ #Contains main source code
- ext/ #Contains external libraries and headers from GitHub
- CMakeLists.txt

The problem is that no matter what I do, CMake always seems to pass ext/ to the compiler as a relative path, like this:

/usr/bin/c++ -I../ext mysrc.cpp

I've tried doing both:

include_directories("${PROJECT_SOURCE_DIR}/ext")
include_directories("/home/user/project/ext")

But it doesn't seem to matter. The directory is always passed to -I as ../ext.

Why does this matter? At the end of my build I invoke gcov -r <source file> which tells gcov to generate coverage reports from my source file and any relative paths found within. As a result, gcov is going into ext/ and generating reports for tons of stuff I don't care about and it's taking up a lot of time. If CMake would instead pass in -I/home/user/project/ext then gcov -r would ignore everything in ext/.

As far as I can tell from: https://cmake.org/cmake/help/v3.13/command/include_directories.html ... this isn't possible, but maybe I'm just missing something?

Edit: This appears to be a problem with specifically the ninja generator. When using the Unix Makefiles generator, everything is passed via absolute paths.

https://gitlab.kitware.com/cmake/cmake/issues/18666

Edit2:

user@antimony:~/cmake_test$ ls
CMakeLists.txt  ext  src
user@antimony:~/cmake_test$ cat CMakeLists.txt 
project(Hello)

add_subdirectory(src)
user@antimony:~/cmake_test$ cat src/CMakeLists.txt 
include_directories(
    .
    ${PROJECT_SOURCE_DIR}/ext
)

add_executable(hello_world hello.cpp)
user@antimony:~/cmake_test$ cat src/hello.cpp 
#include <useless.h>

int main()
{
    hello h;
    return 0;
}
user@antimony:~/cmake_test$ cat ext/useless.h 
struct hello {
    int x;
};
user@antimony:~/cmake_test$ ~/Downloads/cmake-3.13.1-Linux-x86_64/bin/cmake --version
cmake version 3.13.1

CMake suite maintained and supported by Kitware (kitware.com/cmake).
user@antimony:~/cmake_test$ mkdir build && cd build
user@antimony:~/cmake_test/build$ ~/Downloads/cmake-3.13.1-Linux-x86_64/bin/cmake .. -G Ninja
-- The C compiler identification is GNU 7.3.0
-- The CXX compiler identification is GNU 7.3.0
...
-- Build files have been written to: /home/user/cmake_test/build
user@antimony:~/cmake_test/build$ ninja -v
[1/2] /usr/bin/c++   -I../src/. -I../ext  -MD -MT src/CMakeFiles/hello_world.dir/hello.o -MF src/CMakeFiles/hello_world.dir/hello.o.d -o src/CMakeFiles/hello_world.dir/hello.o -c ../src/hello.cpp
[2/2] : && /usr/bin/c++    -rdynamic src/CMakeFiles/hello_world.dir/hello.o  -o src/hello_world   && :
user@antimony:~/cmake_test/build$ cat build.ninja
# CMAKE generated file: DO NOT EDIT!
# Generated by "Ninja" Generator, CMake Version 3.13

# This file contains all the build statements describing the
# compilation DAG.

...

#############################################
# Order-only phony target for hello_world

build cmake_object_order_depends_target_hello_world: phony || src/CMakeFiles/hello_world.dir
build src/CMakeFiles/hello_world.dir/hello.o: CXX_COMPILER__hello_world ../src/hello.cpp || cmake_object_order_depends_target_hello_world
  DEP_FILE = src/CMakeFiles/hello_world.dir/hello.o.d
  INCLUDES = -I../src/. -I../ext
  OBJECT_DIR = src/CMakeFiles/hello_world.dir
  OBJECT_FILE_DIR = src/CMakeFiles/hello_world.dir
  TARGET_COMPILE_PDB = src/CMakeFiles/hello_world.dir/
  TARGET_PDB = src/hello_world.pdb

# =============================================================================
# Link build statements for EXECUTABLE target hello_world

Solution

  • The example shows what may be considered an in-source build. That is when the build directory is the same or a sub-directory of the src folder (not that there is a hard definition or anything, but this does trigger the ninja issue of using relative paths on the command line). Try mkdir ~/cmake_build && cd ~/cmake_build && cmake ~/cmake_test then it should use absolute paths for everything.

    Either way there really isn't a specific way to force one or the other. In general cmake generators will use absolute paths for everything that ends up used on the command line. There seems to be issues with Ninja that prevent the generator from using absolute paths for in-source builds (https://github.com/ninja-build/ninja/issues/1251).