Search code examples
c++cmakeincludeinclude-path

Find the place where the current target directory is added as "include directory" to generated Visual Studio project


I have a quite large CMake-based project and found that while generating solution for Visual Studio on Windows I got current project home directory as an include directory. E.g. here's content of root directory:

- A
   - impl
     - source.cpp
   - header.h
   - CMakeLists.txt
- B
- CMakeLists.txt

In source.cpp I accidentally wrote #include "header.h" instead of #include <A/header.h>. And such solution was successfully compiled since the directory A for some reason was added to the list called "Additional Include Directories" in Visual Studio project (i.e. in command-line added as one more /I option).

Same source slice gives error on Linux build similar to header.h not found (and it's expected behavior).

Which steps should I take to find the source why does CMake adds target A's root directory to the list of include_directories. I used the answer to print a list of project related include directories. CMake version is 3.27.1.


Solution

  • You may want to print the [INTERFACE_]INCLUDE_DIRECTORIES via a custom target using generator expressions. This could allow you to go through the dependencies recursively until you find the root of the issue. This will not help you detect the use of the include_directories command though.

    print_target_info.cmake

    foreach(TGT IN LISTS TARGETS)
        message("---------- Target ${TGT} -----------------")
        message("Interface include dirs:")
        foreach(DIR IN LISTS ${TGT}_INTERFACE_INCLUDE_DIRS)
            message(" - '${DIR}'")
        endforeach()
        message("Include dirs:")
        foreach(DIR IN LISTS ${TGT}_INCLUDE_DIRS)
            message(" - '${DIR}'")
        endforeach()
    endforeach()
    

    CMakeLists.txt

    function(add_print_target_info_command)
        set(INFO)
        foreach(TGT IN LISTS ARGN)
            list(APPEND INFO
                -D "${TGT}_INTERFACE_INCLUDE_DIRS=$<TARGET_PROPERTY:${TGT},INTERFACE_INCLUDE_DIRECTORIES>"
                -D "${TGT}_INCLUDE_DIRS=$<TARGET_PROPERTY:${TGT},INCLUDE_DIRECTORIES>"
            )
        endforeach()
        
        message("INFO=${INFO}")
        add_custom_target(print_target_info
            COMMAND
                ${CMAKE_COMMAND}
                    -D "TARGETS=${ARGN}"
                    ${INFO}
                    -P ${CMAKE_CURRENT_SOURCE_DIR}/print_target_info.cmake
            VERBATIM
        )
    endfunction()
    
    add_print_target_info_command(
        A B C D # list of the names of the targets to print the include directories for 
    )
    

    This allows you to see the info in the command line output when building the target print_target_info.

    In addition you could use the --graphviz=... option to make identifying dependencies easier, perhaps even spotting differences between both platforms