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?
.cbp
project file. Now if the cmake dependency scanner fails to correctly identify a header file as a dependency for a Makefile there are ways to fix that but in some cases it will get it wrong because it doesn't include a full preprocessor.
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
)
add_executable
? I thought CMake figured out the include file dependencies automatically.
add_executable
? Because that's giving the paths to the files to CMake, not to the compiler. Source file arguments to add_executable
are relative to the current source directory.
Add include_directories("/your/path/here")
.
This will be similar to calling gcc
with -I/your/path/here/
option.
Make sure you put double quotes around the path. Other people didn't mention that and it made me stuck for 2 days. So this answer is for people who are very new to CMake and very confused.
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.
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. 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:
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. A similar answer is with other pairs of commands target_compile_definitions/add_definitions, target_compile_options/CMAKE_C_FLAGS
include_directories
call) and to targets in add_subdirectories
that came after the call (not before)... and this is why we say it's legacy. DO NOT USE include_directories
, EVER
Structure of project
.
├── CMakeLists.txt
├── external //We simulate that code is provided by an "external" library outside of src
│ ├── CMakeLists.txt
│ ├── conversion.cpp
│ ├── conversion.hpp
│ └── README.md
├── src
│ ├── CMakeLists.txt
│ ├── evolution //propagates the system in a time step
│ │ ├── CMakeLists.txt
│ │ ├── evolution.cpp
│ │ └── evolution.hpp
│ ├── initial //produces the initial state
│ │ ├── CMakeLists.txt
│ │ ├── initial.cpp
│ │ └── initial.hpp
│ ├── io //contains a function to print a row
│ │ ├── CMakeLists.txt
│ │ ├── io.cpp
│ │ └── io.hpp
│ ├── main.cpp //the main function
│ └── parser //parses the command-line input
│ ├── CMakeLists.txt
│ ├── parser.cpp
│ └── parser.hpp
└── tests //contains two unit tests using the Catch2 library
├── catch.hpp
├── CMakeLists.txt
└── test.cpp
How to do it
1. The top-level CMakeLists.txt is very similar to Recipe 1, Code reuse with functions and macros
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(recipe-07 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(GNUInstallDirs)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY
${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY
${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY
${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR})
# defines targets and sources
add_subdirectory(src)
# contains an "external" library we will link to
add_subdirectory(external)
# enable testing and define tests
enable_testing()
add_subdirectory(tests)
2.Targets and sources are defined in src/CMakeLists.txt (except the conversion target)
add_executable(automata main.cpp)
add_subdirectory(evolution)
add_subdirectory(initial)
add_subdirectory(io)
add_subdirectory(parser)
target_link_libraries(automata
PRIVATE
conversion
evolution
initial
io
parser
)
3.The conversion library is defined in external/CMakeLists.txt
add_library(conversion "")
target_sources(conversion
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/conversion.cpp
PUBLIC
${CMAKE_CURRENT_LIST_DIR}/conversion.hpp
)
target_include_directories(conversion
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
4.The src/CMakeLists.txt file adds further subdirectories, which in turn contain CMakeLists.txt files. They are all similar in structure; src/evolution/CMakeLists.txt contains the following:
add_library(evolution "")
target_sources(evolution
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/evolution.cpp
PUBLIC
${CMAKE_CURRENT_LIST_DIR}/evolution.hpp
)
target_include_directories(evolution
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
5.The unit tests are registered in tests/CMakeLists.txt
add_executable(cpp_test test.cpp)
target_link_libraries(cpp_test evolution)
add_test(
NAME
test_evolution
COMMAND
$<TARGET_FILE:cpp_test>
)
How to run it
$ mkdir -p build
$ cd build
$ cmake ..
$ cmake --build .
Refer to: https://github.com/sun1211/cmake_with_add_subdirectory
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 CONFIGURE_DEPENDS
"*.h"
"*.cpp"
"Classes/*/*.cpp"
"Classes/*/*.h"
)
add_executable(Server ${source_files})
And it totally worked.
CONFIGURE_DEPENDS
.
This worked for me:
set(SOURCE main.cpp)
add_executable(${PROJECT_NAME} ${SOURCE})
# target_include_directories must be added AFTER add_executable
target_include_directories(${PROJECT_NAME} PUBLIC ${INTERNAL_INCLUDES})
Don't forget to include ${CMAKE_CURRENT_LIST_DIR}
. That's what was causing problems for me.
Example should be like this:
target_include_directories(projectname
PUBLIC "${CMAKE_CURRENT_LIST_DIR}/include"
)
PUBLIC for dependencies which you want to be included by a parent project. PRIVATE for ones that you don't.
Yet another alternative:
set_property(
TARGET MyApp
APPEND PROPERTY
INCLUDE_DIRECTORIES "${CMAKE_CURRENT_LIST_DIR}/src"
)
I am using CLion also my project structure is the following :
--main.cpp
--Class.cpp
--Class.h
--CMakeLists.txt
The CMakeLists.txt
before the change:
add_executable(ProjectName main.cpp)
The CMakeLists.txt
after the change:
add_executable(ProjectName main.cpp Class.cpp Class.h)
By doing that the program compiled successfully.
Success story sharing
LIBROOT_HEADER_FILES
variable inliba/CMakefile
andlibb/CMakefile
then ?include_directories
overtarget_include_directories
. The former sets it recursively for all targets in that directory; whereas the latter sets it for a target. Doing the former breaks the notion of a target graph in CMake, and instead relies on side effects to your file hierarchy.target_include_directories
for modern CMake code. Feel free to invite me to a chat if you disagree with the changes..h
files intoadd_executable
. But, It does have the nice benefit of making the files show up inVisual Studio
projects in the expected location.Makefiles
uses the internalcmake -E cmake_depends
to generate dependencies from the source files (header files inadd_executable
are skipped). There are known issues with this the scanner. Also ` CMake's Makefile generator dependency scanner does only approximate preprocessing.` Computed header includes and that sort of thing will not work.