Building C and C++ projects can be really non trivial stuff, and people are struggling with this topic for years. I found CMake very useful, explicit, easy to use “project builder” . But what is a “Modern CMake” ? Lets dig into this topic …
It is a routine not a feature.
A s usual in programming, “new approach” for the problem is “not doing something” (ex. “not using goto” , “not modifying state”), for CMake it means “not using global properties”. Like not setting include paths for all code globally, and not setting linking parameters for all libraries globally.
Why?
For exactly this same reason that we are avoiding global variables and states in a programming. It may be convenient, and when I am lazy, I will use this, but for code that you share, and live in with another people it is just a bad practice.
How?
Instead of writing complete guide for a CMake (another one) I link you to one that in my opinion is good to learn from: cmake-fundamentals. Author of this blog Jeremi is my colleague, and he did really good job creating this guide.
Now lets focus on some key features:
What is target ?
Simplest explanation that i can give to you, is that it is a name that CMake will use to identify artifacts and “intermediate abstract artifacts” that it makes and uses to makes other targets. So add_library() will make you a target add_executable() will make you a target, also check add_custom_target().
add_executable(sanitize_file main.cpp)
target_link_libraries(sanitize_file
FileSanitizer::Lib)
How to set target properties ?
Maybe what target properties are? In the meaning of code compilation and linking of a target, properties are all the information that compiler and linker need to perform build. So: include paths, compilation parameters, compiler version, linking paths … source file names, library names …
So all functions that are starting with target_ are setting a target properties, like target_link_libraries() will set a list of a targets (in this case libraries) that need to be linked.
How to populate properties ?
So that is the magic of modern CMake, as you will use your target, all the properties will be populated inside. Passing target FileSanitizer::Lib will pass its include directory, and compilation settings, and all the build system expects to be associated with target you are using. And also you can get target properties explicit with get_target_property().
Beyond STATIC and SHARED libraries ?
Did you catch “intermediate abstract artifacts” couple lines above ? So target will not necessary create artifact on a file system (library or executable) sometimes it is used to populate properties around. Like add_library(name INTERFACE). So it may be useful when you want to use “header only library” (other creative application, in my next post).
Word about ALIAS(ing) and target naming conventions ?
Maybe as you are using google_test framework you notices that you can link to it using this strange GTest::GTest syntax. I is a convention of naming targets that are exported for external use. But you can also use it in internals of your project to link modules to each other. To create such target name you need to use add_library(name ALIAS).
add_library(${PROJECT_NAME} OBJECT ${file_sanitizer_sources})
target_include_directories(${PROJECT_NAME} PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
add_library(FileSanitizer::Lib ALIAS ${PROJECT_NAME})
Footnote
For complete, but simple example of CMake project using “Modern CMake” approach, please check my github.
With all the CMake goodness
Michał