1. What is CMake?
In short, CMake is a top-notch build configuration tool, renowned for its expertise in automatically generating Makefiles. When multiple developers collaborate on large projects, especially in complex environments that span various languages such as C/C++/Java or compilers, it acts like a conductor, orderly guiding the compilation process until it elegantly produces executable files (e.g., .exe) or shared libraries (such as .dll, .so). This magic is all derived from the meticulously crafted CMakeLists.txt file. To explore its deeper mysteries, feel free to visit the official website: www.cmake.org.
2. Installing CMake
• Linux Systems: Most come pre-installed; simply use the commandcmake –version to check the version.
• Windows/Uninstalled Linux: Go to the download pagehttp://www.cmake.org/HTML/Download.html to obtain the installation package.
## Step 1: Install the cmake script tool in the MSYS2 MinGW x64 terminal
pacman -S mingw-w64-x86_64-cmake
### Check in CMD or PowerShell
cmake --version
gcc --version
# Step 2: Write the CMake script file
### Create CMakeLists.txt
# Step 3: Open CMD or PowerShell, navigate to the source directory
// Generate the build system
cmake -G "MinGW Makefiles" .
// Compile the project to generate the executable file
mingw32-make
3. Your First CMake Project (Hello World)
1. Write the Source Code
Create a new filetest.c with the following content:
#include <stdio.h>
int main() {
printf("Hello, World! 你好.....\n");
return 0;
}
2. Write CMakeLists.txt
# Specify version
CMAKE_MINIMUM_REQUIRED(3.26)
# Create a new CMakeLists.txt in the same directory (core configuration file), content:
PROJECT(HELLO) # Define project name (HELLO), supports all languages by default
SET(SRC_LIST test.c) # Define variable SRC_LIST to store the list of source files
MESSAGE(STATUS "Current binary directory: ${HELLO_BINARY_DIR}") # Output prompt information (STATUS type)
MESSAGE(STATUS "Current source file directory: ${HELLO_SOURCE_DIR}")
ADD_EXECUTABLE(hello ${SRC_LIST}) # Generate executable file hello using source files in SRC_LIST
3. Generate Makefile and Compile
1. Executecmake . (Generate Makefile and other files in the current directory)
2. Executemake (Compile to generate the executable file)
4. Simplified Writing
The aboveCMakeLists.txt can be simplified to:
PROJECT(HELLO)
ADD_EXECUTABLE(hello test.c)
SinceSET and MESSAGE are not mandatory, beginners can ignore them for now.
4. Introduction to CMake Main Syntax
1. Common Keywords
• PROJECT(ProjectName [Language])
Defines the project name and supported languages (e.g.,PROJECT(HELLO CXX) indicates support for C++).
Two variables are automatically generated:
– <ProjectName>_BINARY_DIR (binary file directory, e.g., HELLO_BINARY_DIR)
– <ProjectName>_SOURCE_DIR (source file directory)
However, it is recommended to use PROJECT_BINARY_DIR and PROJECT_SOURCE_DIR (the variable names remain unchanged when the project name is modified).
• SET(VariableName Content)
Explicitly defines a variable. For example,SET(SRC_LIST test.c t1.c), the variable value can be accessed using${VariableName} (directly use the variable name inIF statements).
• MESSAGE(Type Information)
Outputs information to the terminal, types include:
– STATUS (normal prompt, output prefix is --)
– SEND_ERROR (report error but continue)
– FATAL_ERROR (report error and terminate)
• ADD_EXECUTABLE(ExecutableName SourceFileList)
Generates an executable file. The source file list can be directly written file names (e.g., test.c), or use variables (e.g.,${SRC_LIST}).
2. Syntax Notes
• Variable values are accessed using${VariableName}, but directly use the variable name inIF conditional statements.
• Commands are case-insensitive (recommended to use all uppercase), while parameters and variables are case-sensitive.
• If the source file name contains spaces, it must be enclosed in double quotes (e.g.,SET(SRC_LIST “test.c”)).
5. Code Build Methods
1. Internal Build
Directly executecmake . in the source file directory, the generated temporary files (e.g.,CMakeFiles, Makefile) will be mixed with the source files, making cleanup troublesome.
2. External Build
1. Create a folder named `build` in the project root directory, specifically for containing temporary files generated during the build process.
2. Enter the `build` folder and execute the `cmake ..` command (where `..` cleverly points to the parent directory containing `CMakeLists.txt`) to start the CMake configuration process.
3. This way, all temporary files generated during the build will be neatly organized in the `build` directory, ensuring that the project’s source files remain clean and undisturbed.
6. Project Development Structure
Assuming we want to optimize the project structure to:
Project Root Directory
├── build # External build directory (temporary files)
├── CMakeLists.txt # Root directory configuration file
├── src # Source code directory
│ ├── CMakeLists.txt
│ └── test.c
├── doc # Documentation directory (stores hello.txt)
├── COPYRIGHT # Copyright statement
├── README # Project description
└── runhello.sh # Run script
1. Root Directory CMakeLists.txt
UseADD_SUBDIRECTORY(src bin) to specify:
– Add thesrc directory to the project.
– The compilation results (e.g., executable files) are stored in thebin directory (build/bin).
2. src Directory CMakeLists.txt
Directly writeADD_EXECUTABLE(hello test.c) to generate the executable file.
3. Adjust the Executable File Output Path
Add the following insrc/CMakeLists.txt:
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) # Output executable files to the bin directory
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib) # Output library files to the lib directory
7. Installing the Project (make install)
1. Installation Targets
• Executable files, library files → /usr/local/bin or /usr/local/lib.
• Documentation, copyright files → /usr/local/share/doc/cmake.
2. Key Command: INSTALL
Add installation rules inCMakeLists.txt:
# Install files (COPYRIGHT, README) to /usr/local/share/doc/cmake
INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake)
# Install script (runhello.sh) to /usr/local/bin
INSTALL(PROGRAMS runhello.sh DESTINATION bin)
# Install the entire doc directory to /usr/local/share/doc/cmake (preserving directory structure)
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)
3. Execute Installation
1. Enter thebuild directory and executecmake .. (you can specify the installation path:cmake -DCMAKE_INSTALL_PREFIX=/usr ..).
2. Executemake to compile.
3. Executemake install to complete the installation.
8. Building Static and Dynamic Libraries
1. Static Library vs Dynamic Library
|
Type |
Extension |
Characteristics |
|
Static Library |
.a/.lib |
Integrated into the executable file at compile time, can run independently |
|
Dynamic Library |
.so/.dll |
Not integrated at compile time, executable files depend on dynamic libraries |
2. Build Example (Generate Static and Dynamic Libraries)
Assuming the project structure:
Project Root Directory
├── build
├── CMakeLists.txt
└── lib
├── CMakeLists.txt
├── library.h # Header file
└── library.c # Library source file
Step 1: Write the Header File and Source File
- library.h:
#ifndef PERRY_STATIC_LIBRARY_H
#define PERRY_STATIC_LIBRARY_H
void hello(void);
#endif//PERRY_STATIC_LIBRARY_H
- library.c::
#include “library.h”
#include <stdio.h>
void hello(void) {
printf(“Hello, World! I’m static library… \n”);
}
Step 2: Write the CMakeLists.txt for the lib directory
SET(LIBHELLO_SRC hello.c) # Define library source file variable
# Generate dynamic library (libhello.so)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})# Generate static library (libhello.a)
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})S
ET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME “hello”) # Rename static library to hello
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1) # Avoid name conflicts
Step 3: Install the Library and Header Files
Add the following inlib/CMakeLists.txt:
# Install header files to /usr/local/include/hello
INSTALL(FILES hello.h DESTINATION include/hello)
# Install dynamic and static libraries to /usr/local/lib
INSTALL(TARGETS hello hello_static LIBRARY DESTINATION lib # Dynamic library installation path
ARCHIVE DESTINATION lib) # Static library installation path
9. Using External Libraries
For example, there is a new project that needs to call the generatedlibhello.so library:
1. Project Structure
Project Root Directory
├── build
├── CMakeLists.txt
└── src
├── CMakeLists.txt
└── test.c # Call HelloFunc()
2. Content of test.c
#include <stdio.h>
#include "test.h"
#include "library.h"
int main() {
printf("Hello, World! 你好.....\n");
hello();
return 0;
}
3. Key Configuration (CMakeLists.txt)
1. Specify Header File Search Path
Add the following insrc/CMakeLists.txt:
INCLUDE_DIRECTORIES(/usr/include/hello)
# Header files are in the /usr/include/hello directory
2. Specify Library File Search Path and Link
Add the following insrc/CMakeLists.txt (afterADD_EXECUTABLE):
10. Other Useful Tips
• Generate Debug Version: During the compilation process, execute the command `cmake .. -DCMAKE_BUILD_TYPE=debug` to generate the Debug version.
• Environment Variable Configuration:
– `CMAKE_INCLUDE_PATH`: Used to set the default search path for header files, for example, set it using the command `export CMAKE_INCLUDE_PATH=/usr/include/hello`.
– `CMAKE_LIBRARY_PATH`: Used to set the default search path for library files. Through the above steps, you have mastered the basic applications and common operations of CMake. In actual project development, you can adjust the configuration according to specific needs and gradually learn more advanced commands, such as conditional compilation, cross-platform configuration, etc.