c++ directory - How to properly add include directories with CMake




command line (5)

About a year ago I asked about header dependencies in CMake.

I realized recently that the issue seemed to be that CMake considered those header files to be external to the project. At least, when generating a Code::Blocks project the header files do not appear within the project (the source files do). It therefore seems to me that CMake consider those headers to be external to the project, and does not track them in the depends.

A quick search in the CMake tutorial only pointed to include_directories which does not seem to do what I wish...

What is the proper way to signal to CMake that a particular directory contains headers to be included, and that those headers should be tracked by the generated Makefile?


Answers

Two things must be done.

First add the directory to be included:

target_include_directories(test PRIVATE ${YOUR_DIRECTORY})

In case you are stuck with a very old CMake version (2.8.10 or older) without support for target_include_directories, you can also use the legacy include_directories instead:

include_directories(${YOUR_DIRECTORY})

Then you also must add the header files to the list of your source files for the current target, for instance:

set(SOURCES file.cpp file2.cpp ${YOUR_DIRECTORY}/file1.h ${YOUR_DIRECTORY}/file2.h)
add_executable(test ${SOURCES})

This way, the header files will appear as dependencies in the Makefile, and also for example in the generated Visual Studio project, if you generate one.

How to use those header files for several targets:

set(HEADER_FILES ${YOUR_DIRECTORY}/file1.h ${YOUR_DIRECTORY}/file2.h)

add_library(mylib libsrc.cpp ${HEADER_FILES})
target_include_directories(mylib PRIVATE ${YOUR_DIRECTORY})
add_executable(myexec execfile.cpp ${HEADER_FILES})
target_include_directories(myexec PRIVATE ${YOUR_DIRECTORY})

First, you use include_directories() to tell CMake to add the directory as -I to the compilation command line. Second, you list the headers in your add_executable() or add_library() call.

As an example, if your project's sources are in src, and you need headers from include, you could do it like this:

include_directories(include)

add_executable(MyExec
  src/main.c
  src/other_source.c
  include/header1.h
  include/header2.h
)

I had the same problem.

My project directory was like this:

    --project
    ---Classes
    ----Application
    -----.h and .c files
    ----OtherFolders
    --main.cpp

And what I used to include the files in all those folders:

    file(GLOB source_files
            "*.h"
            "*.cpp"
            "Classes/*/*.cpp"
            "Classes/*/*.h"
    )

    add_executable(Server ${source_files})

And it totally worked.


CMake is more like a script language if comparing it with other ways to create Makefile (e.g. make or qmake). It is not very cool like Python, but still.

There are no such thing like a "proper way" if looking in various opensource projects how people include directories. But there are two ways to do it.

  1. Crude include_directories will append a directory to the current project and all other descendant projects which you will append via a series of add_subdirectory commands. Sometimes people say that such approach is legacy.

  2. A more elegant way is with target_include_directories. It allows to append a directory for a specific project/target without (maybe) unnecessary inheritance or clashing of various include directories. Also allow to perform even a subtle configuration and append one of the following markers for this command.

PRIVATE - use only for this specified build target

PUBLIC - use it for specified target and for targets which links with this project

INTERFACE -- use it only for targets which links with the current project

PS:

  1. Both commands allow to mark a directory as SYSTEM to give a hint that it is not your business that specified directories will contain warnings.

  2. A similar answer is with other pairs of commands target_compile_definitions/add_definitions, target_compile_options/CMAKE_C_FLAGS


If you're looking for strictly ease-of-use, I'd highly recommend Panda3D. It's cross-platform (Windows, Unix, Mac), really easy to use, has extensive documentation, and a nice community around it.





c++ ubuntu cmake