Hello, today we are going to delve into a topic that many people pay attention to but often do not take seriously—Getting Started with CMake: From Hello World to Complex Multi-Module Project Builds.
Today, we will start from scratch and explore how to use CMake to build projects, making it a powerful assistant for managing your code efficiently.
1. Let’s Start with Hello World
1.1. Creating a Simple CMake Project
First, let’s create a simple CMake project. We will start with the simplest Hello World program.
-
Create a new folder in your working directory, for example, named
HelloWorld
. -
In the
HelloWorld
folder, create two files:
-
main.cpp
-
CMakeLists.txt
In the main.cpp
file, write the following code:
#include <iostream>
int main() {
std::cout << "Hello, CMake World!" << std::endl;
return 0;
}
-
Then, open the CMakeLists.txt
file and write the following content:
# Set the minimum required version of CMake
cmake_minimum_required(VERSION 3.10)
# Set the project name
project(HelloWorld)
# Specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
# Add executable file
add_executable(HelloWorld main.cpp)
In this CMakeLists.txt
file, we have done the following:
-
cmake_minimum_required: Specifies the minimum required version of CMake. The lower the version number, the fewer features CMake has. -
**project(HelloWorld)**: Sets the project name. -
**set(CMAKE_CXX_STANDARD 11)**: Sets the C++ standard to C++11. -
**add_executable(HelloWorld main.cpp)**: Tells CMake to generate an executable named HelloWorld
from the source filemain.cpp
.
1.2. Running CMake to Build
-
Open the terminal (or command line tool) and navigate to the HelloWorld
folder. -
Create a build directory in that folder (it is usually recommended to have a separate directory for build files):
mkdir build
cd build
-
Execute the CMake command to configure the build:
cmake ..
-
After generating the build files, we can compile the project:
make
-
Finally, run the generated executable:
./HelloWorld
You should see the output:
Hello, CMake World!
Thus, we have completed the build and compile process of the simplest CMake project. You can now see that CMake automatically generates a Makefile and executes the compilation process based on the CMakeLists.txt
file.
2. From Single Module to Multi-Module Project Builds
The true power of CMake lies in its ability to manage complex multi-module projects. Next, we will expand our project by adding multiple modules (i.e., multiple source code files).
2.1. Multi-Module Project Structure
In large projects, we usually divide the code into multiple modules, each having its independent source code files. Let’s assume our project has two modules:
-
module1
: Containsmodule1.cpp
andmodule1.h
. -
module2
: Containsmodule2.cpp
andmodule2.h
.
Our project structure is as follows:
HelloWorld/
├── CMakeLists.txt
├── main.cpp
├── module1/
│ ├── module1.cpp
│ └── module1.h
└── module2/
├── module2.cpp
└── module2.h
2.2. Modify the CMakeLists.txt
Configuration File
To support multiple modules, we need to modify the CMakeLists.txt
file to let CMake know how to find and compile these modules.
cmake_minimum_required(VERSION 3.10)
# Set the project name
project(HelloWorld)
# Set the C++ standard
set(CMAKE_CXX_STANDARD 11)
# Add subdirectories for module1 and module2
add_subdirectory(module1)
add_subdirectory(module2)
# Create executable file and link module1 and module2
add_executable(HelloWorld main.cpp)
# Link the libraries of module1 and module2 to HelloWorld
target_link_libraries(HelloWorld module1 module2)
Then, in each module’s folder, add a CMakeLists.txt
file to define the build method for the module.
2.3. Example of module1/CMakeLists.txt
:
# Create a static library for module1
add_library(module1 STATIC module1.cpp)
2.4. Example of module2/CMakeLists.txt
:
# Create a static library for module2
add_library(module2 STATIC module2.cpp)
2.5. Example of main.cpp
:
#include <iostream>
#include "module1/module1.h"
#include "module2/module2.h"
int main() {
std::cout << "Hello from Module1 and Module2!" << std::endl;
module1_function();
module2_function();
return 0;
}
2.6. Example of module1/module1.cpp
:
#include "module1.h"
#include <iostream>
void module1_function() {
std::cout << "Module1 function executed!" << std::endl;
}
2.7. Example of module2/module2.cpp
:
#include "module2.h"
#include <iostream>
void module2_function() {
std::cout << "Module2 function executed!" << std::endl;
}
2.8. Build and Run
Follow the previous steps to create the build
directory, run cmake
and make
, and finally, you will see the following output:
Hello from Module1 and Module2!
Module1 function executed!
Module2 function executed!
This indicates that our multi-module project has been successfully built and the function calls between the modules can run smoothly.
3. Advanced CMake Techniques
-
You can use the if()
statement to decide whether to compile certain files or use specific libraries based on different operating systems, compilers, or other conditions.
if(WIN32)
message("Building on Windows")
elseif(UNIX)
message("Building on Unix-like OS")
endif()
-
You can define custom compile options in the CMakeLists.txt
to use different configurations in different environments.
option(USE_MY_LIBRARY "Use My Library" ON)
if(USE_MY_LIBRARY)
add_subdirectory(MyLibrary)
endif()
4. Conclusion
I hope that through today’s explanation, you can master the basic usage of CMake and adjust the build system according to different project needs.
In future projects, you will find CMake to be an indispensable build tool that allows you to develop cross-platform more efficiently.