1. Core Principles of CMake
CMake is not a build tool (like <span>make</span> or <span>ninja</span>), but rather a build system generator, such as generating Makefiles.
Its core working principle can be summarized in the following three steps:
- 1. Configuration:
- • You write a script file named
<span>CMakeLists.txt</span>that describes your project structure, source files, dependencies, etc. - • Execute the
<span>cmake /path/to/source</span>command. - • CMake reads the
<span>CMakeLists.txt</span>file and analyzes and configures based on the current platform (Windows, Linux, macOS) and generator (Visual Studio, Makefiles, Ninja, etc.). - • It checks the availability of compilers, linkers, third-party libraries, etc. This stage generates
<span>CMakeCache.txt</span>and some other files.
- • After a successful configuration phase, CMake will generate the necessary native build files for the corresponding platform and generator based on the analysis results from the previous step.
- • On Linux/macOS, it defaults to generating a
<span>Makefile</span>. - • On Windows, it can generate a
<span>Visual Studio</span><span>.sln</span>solution file. - • You can also specify to generate files for faster build systems like
<span>Ninja</span>.
- • Now, you no longer use CMake, but rather the corresponding native build tool for your platform to compile and link your project.
- • For example, in the directory where the
<span>Makefile</span>was generated, you just need to enter the<span>make</span>command to start compiling. - • In the directory where the
<span>Visual Studio</span>solution was generated, you can use the<span>MSBuild</span>command or directly open it in the VS IDE to compile.
Simple Analogy: CMake is like a translator and a blueprint designer. You write your project requirements in <span>CMakeLists.txt</span> (a universal language), and CMake “translates” it into the “construction drawings” that the “foremen” (<span>make</span>, <span>ninja</span>, <span>MSBuild</span>) on different “construction sites” (operating systems) can understand (<span>Makefile</span>, <span>.ninja</span>, <span>.sln</span>). Then the “foremen” are responsible for the actual “construction” (compilation, linking).
Core Advantage: Cross-platform. Developers only need to maintain one universal <span>CMakeLists.txt</span> file to generate the corresponding build files on various platforms, greatly simplifying the build management of cross-platform projects.
2. Basic Syntax of CMake
The syntax of CMake is very intuitive, mainly consisting of Commands, Variables, and Comments.
- 1. Commands:
- • The format is similar to function calls:
<span>command_name(parameter1 parameter2 ...)</span>. - • Case insensitive, but it is generally recommended to use all lowercase, for example,
<span>project()</span>,<span>add_executable()</span>. - • Command parameters are separated by spaces or semicolons
<span>;</span>.
- • Set using the
<span>set()</span>command:<span>set(MY_VARIABLE "some value")</span>. - • Referenced using
<span>${}</span>syntax:<span>message(${MY_VARIABLE})</span>. - • Variable types are usually strings.
- • Use the
<span>#</span>symbol for single-line comments. - •
<span># This is a comment</span>
- • Supports conditional statements (
<span>if()</span>,<span>else()</span>,<span>endif()</span>) and loops (<span>foreach()</span>,<span>endforeach()</span>,<span>while()</span>,<span>endwhile()</span>).
3. Common Commands and Variables
Below is a simple project <span>CMakeLists.txt</span> example that includes the most core and commonly used commands:
# 1. Specify the minimum required version of CMake
cmake_minimum_required(VERSION 3.10)
# 2. Define the project name, version, and programming languages used (C/CXX, etc.)
project(MyProject VERSION 1.0 DESCRIPTION "A simple project" LANGUAGES C CXX)
# 3. Set the C++ standard (very common!)
set(CMAKE_CXX_STANDARD 11) # or 14, 17, 20...
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 4. Add all .cpp files in the current directory to the SOURCES variable
file(GLOB SOURCES "src/*.cpp")
# 5. Add an executable target named hello_world, built from the source files in the SOURCES variable
add_executable(hello_world ${SOURCES})
# 6. Add header file search paths for the target
# PUBLIC means that not only hello_world will use it, but also other targets linking to hello_world will use it
target_include_directories(hello_world
PUBLIC
${PROJECT_SOURCE_DIR}/include
)
# 7. If you need to link a library
# 7.1 First find the library (e.g., find the Threads library on the system)
find_package(Threads REQUIRED)
# 7.2 Then link the library to your target
target_link_libraries(hello_world
PRIVATE
Threads::Threads # Link the Threads library
)
# 8. Add a subdirectory, which must also have a CMakeLists.txt
# add_subdirectory(mylib)
Detailed Explanation of Core Commands:
- •
<span>cmake_minimum_required(VERSION x.x)</span>: Must be placed at the top, specifying the required CMake version. - •
<span>project(<PROJECT-NAME>)</span>: Defines the project name, which sets some key variables, such as<span>PROJECT_SOURCE_DIR</span>(current project source path) and<span>PROJECT_BINARY_DIR</span>(build output path). - •
<span>add_executable(<name> <sources>)</span>: Defines an executable build target. - •
<span>add_library(<name> [STATIC|SHARED] <sources>)</span>: Defines a library build target (static library<span>.a/.lib</span>or dynamic library<span>.so/.dll</span>). - •
<span>target_include_directories(<target> [PUBLIC|PRIVATE|INTERFACE] <dirs>)</span>: Modern CMake best practice, specifies header file include directories for specific targets. Avoids the old global command<span>include_directories()</span>. - •
<span>target_link_libraries(<target> [PUBLIC|PRIVATE|INTERFACE] <libraries>)</span>: Modern CMake best practice, specifies libraries to link for specific targets. Avoids the old global command<span>link_libraries()</span>. - •
<span>find_package(<PackageName> REQUIRED)</span>: Finds external dependency packages in the system (like OpenCV, Boost, etc.).<span>REQUIRED</span>means it will error out if not found. - •
<span>set(<variable> <value>)</span>: Sets a variable. - •
<span>message(<mode> "message to display")</span>: Prints a message for debugging (<span>STATUS</span>) or error (<span>FATAL_ERROR</span>).
Common Variables:
- •
<span>CMAKE_CXX_STANDARD</span>: Sets the C++ standard. - •
<span>CMAKE_BUILD_TYPE</span>: Sets the build type, such as<span>Debug</span>,<span>Release</span>,<span>RelWithDebInfo</span>,<span>MinSizeRel</span>. - •
<span>CMAKE_CXX_FLAGS</span>: Sets the flags for the C++ compiler. - •
<span>PROJECT_SOURCE_DIR</span>: Path to the project root directory. - •
<span>PROJECT_BINARY_DIR</span>: Path to the build output directory (the directory where the<span>cmake</span>command is run). - •
<span>CMAKE_PREFIX_PATH</span>: Sets the path for<span>find_package</span>to search for third-party libraries, for example,<span>set(CMAKE_PREFIX_PATH "/path/to/your/lib")</span>.
4. Summary and Usage Process
- 1. Write
<span>CMakeLists.txt</span>: Create this file in your project root directory, using the commands above to describe your project. - 2. Create a build directory and configure:
mkdir build && cd build # Strongly recommended! Out-of-source build to keep the source directory clean cmake .. # .. indicates that CMakeLists.txt is in the parent directory - 3. Generate the build system and compile:
cmake --build . # Use CMake to call the underlying build tool (generic) # or make # If a Makefile was generated - 4. Run the program:
./hello_world # Find the generated executable in the build directory
I hope this brief overview helps you quickly understand the core concepts and workings of CMake!