

In this example, we use the popular Eigen3 matrix-vector library by specifying the repository URL, a specific git tag to checkout, a source directory, and a binary directory as in an ordinary CMake project, as well as CMake arguments.

If you want to disable in-source builds, put the following lines in your root CMakeLists.txt file: if($" SOURCE_DIR eigen BINARY_DIR eigen-build CMAKE_ARGS -DBUILD_TESTING:BOOL=OFF -DBUILD_SHARED_LIBS:BOOL=ON ) This “in-source build” pollutes your project and creates many changes in git. However, sometimes you may forget to create the additional directory and run CMake in the root. The default steps you should perform before configuring any CMake build are creating a build subdirectory and then running CMake. The INTERFACE keyword is less frequently used and designates a dependency used in “upstream” targets without being used in the target where it is declared (in this example, sub_dependency_lib would be visible to main_project but wouldn’t be used in dependency_lib itself). This is useful if dependency_lib does not completely hide the dependency, which is often the case. If you substitute the keyword PRIVATE with PUBLIC, you will also be able to use sub_dependency_lib in main_project. This means that main_project won’t be able to use any functions of sub_dependency_lib directly. Written like this, sub_dependency_lib is not visible to main_project. main_project depends on dependency_lib, which in turn depends on sub_dependency_lib. In the subdirectory dependency_lib, we have the following CMakeLists.txt: project(dependency_lib) add_library(dependency_lib SHARED library.cpp) add_subdirectory(sub_dependency_lib) target_link_libraries(dependency_lib PRIVATE sub_dependency_lib)Ĭlearly, we have a hierarchy of dependencies. Let us consider, for example, the following code: project(main_project) add_executable(main_project main.cpp) add_subdirectory(dependency_lib) target_link_libraries(main_project PRIVATE dependency_lib) This is useful if you want to carry over a dependency from a child target to the parent. In modern CMake, target commands let you specify the command scope using INTERFACE, PRIVATE and PUBLIC keywords. Use Target Transitivity to Specify Dependency Hierarchies Furthermore, these commands let us optionally specify the scope to achieve the desired inheritance behavior (see next tip). On the other hand, using target_include_directories or target_link_libraries, we explicitly specify the target we want to use, avoiding any leakage problems. Especially with commands such as add_definitions or add_compile_options, this may cause some hard-to-find compilation errors. The same also applies to the other commands.

Thus, we might end up with a scenario of using the wrong include files in dependency_lib. Unfortunately, since the directories specified in include_directories are appended to the list of all targets in the current CMakeLists.txt file, they will also be appended to dependency_lib. For instance, if you want to build an executable target that additionally depends on a library defined in a subdirectory, you could specify it like this: project(main_project) add_executable(main_project main.cpp) add_subdirectory(dependency_lib) include_directories(include_files) Since the functions without target_ do not specify a scope, they will leak to all targets specified in the directory. However, whenever possible, you should prefer using their counterparts target_compile_definitions, target_include_directories, target_sources, or target_link_libraries instead. Always Use target_*() Commandsįunctions like add_definitions, include_directories, link_libraries, and others are still around in modern CMake due to backward compatibility. While you may already be familiar with some of them, I am sure you will find some useful ones as well. In this post, I would like to introduce you to some tips that might help you write better CMake scripts. Since it was introduced 21 years ago, it came a long way and added support for many great features, gaining popularity among C++ developers. It is widely used for multi-platform development and supports generating build files for most C++ compilers. CMake has become the de facto standard tool for C++build automation, testing, and packaging.
