一文学会CMakeLists.txt: CMake现代C++跨平台工程化实战
你能学到什么?
朋友们好久不见,我是alibli,好久没有更新博客了。今天本人将通过构造一个实际的虚拟小项目,来让你彻底掌握CMake跨平台工程构建,学会CMakeLists.txt语法。该项目实现了一个简单的平方、立方的计算程序,只为演示工程构建方法。
每一行代码都会有非常非常详细的解释!
你将掌握如下内容:
- 构建静态库
- 构建动态库
- 构建可执行程序
- 动态库如何调用静态库
- 可执行程序如何调用动态库
项目整体构建
项目目录:
calculator_project/
├── CMakeLists.txt # 外层主 CMake 文件
├── math_utils/ # 静态库源码
│ ├── CMakeLists.txt
│ ├── MathUtils.h
│ └── MathUtils.cpp
├── calculator/ # 动态库(依赖静态库)
│ ├── CMakeLists.txt
│ ├── Calculator.h
│ └── Calculator.cpp
└── app/ # 主程序├── CMakeLists.txt└── main.cpp
外层CMakeLists.txt
# calculator_project/CMakeLists.txt# 1. 指定项目所需的最低 CMake 版本
# 如果用户系统中的 CMake 版本低于 3.10,会报错
cmake_minimum_required(VERSION 3.10)# 2. 定义项目名称为 "CalculatorProject"
# LANGUAGES CXX 表示这是一个 C++ 项目
# CMake 会自动查找 C++ 编译器(如 g++)
project(CalculatorProject LANGUAGES CXX)# 3. 设置 C++ 标准为 C++17
# 要求编译器必须支持 C++17
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)# 4. 禁用编译器特定的扩展(如 GNU 扩展)
# 强制使用标准 C++,提高可移植性
set(CMAKE_CXX_EXTENSIONS OFF)# 5. 启用测试支持(即使现在不用,也先打开)
# 后续可以用 ctest 命令运行测试
enable_testing()# 6. 添加子目录 math_utils
# CMake 会进入 math_utils/ 目录并读取其 CMakeLists.txt
# 该目录将构建一个静态库
add_subdirectory(math_utils)# 7. 添加子目录 calculator
# 该目录将构建一个动态库,依赖上面的静态库
add_subdirectory(calculator)# 8. 添加子目录 app
# 构建主程序,链接动态库
add_subdirectory(app)
静态库构建
进入math_utils目录
CMakeLists.txt
# calculator_project/math_utils/CMakeLists.txt# 1. 定义一个静态库目标,名为 "math_utils"
# STATIC 表示生成静态库(.a 文件)
# 源文件列表:MathUtils.cpp
add_library(math_utils STATICMathUtils.cpp
)# 2. 设置目标 math_utils 的头文件搜索路径
# PUBLIC 表示:
# - math_utils 自己需要这些头文件(PRIVATE 部分)
# - 链接 math_utils 的目标也能看到这些头文件(INTERFACE 部分)
# ${CMAKE_CURRENT_SOURCE_DIR} 是当前目录(即 math_utils/)
# 所以其他代码可以 #include "MathUtils.h"
target_include_directories(math_utilsPUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
)# 3. 关键:启用 PIC,以便被动态库链接
set_target_properties(math_utils PROPERTIESPOSITION_INDEPENDENT_CODE ON
)# 4. 为 math_utils 添加编译定义(可选)
# 这里定义一个宏 MATH_UTILS_VERSION="1.0"
# 在 MathUtils.cpp 中可以用 #ifdef MATH_UTILS_VERSION 条件编译
target_compile_definitions(math_utilsPRIVATE MATH_UTILS_VERSION=\"1.0\"
)# 5. 输出一条状态信息,帮助调试
# 构建时会显示:-- Building static library: math_utils
message(STATUS "Building static library: math_utils")
MathUtils.h
// calculator_project/math_utils/MathUtils.h23 #ifndef MATH_UTILS_H4 #define MATH_UTILS_H56 // 简单的数学工具类(静态库提供)7 class MathUtils {8 public:9 // 计算平方10 static double square(double x);1112 // 计算立方13 static double cube(double x);14 };1516 #endif // MATH_UTILS_H
MathUtils.cpp
// calculator_project/math_utils/MathUtils.cpp#include "MathUtils.h"
#include <iostream>// 实现平方函数
double MathUtils::square(double x) {return x * x;
}// 实现立方函数
double MathUtils::cube(double x) {return x * x * x;
}// 如果定义了版本宏,打印版本(用于验证独立编译)
#ifdef MATH_UTILS_VERSION
#include <iostream>
void print_version() {std::cout << "MathUtils Version: " << MATH_UTILS_VERSION << std::endl;
}
#endif
动态库构建
进入calculator目录
CMakeLists.txt
# calculator_project/calculator/CMakeLists.txt# 1. 定义一个共享库(动态库),名为 "calculator"
# SHARED 表示生成 .so 文件(Linux)或 .dll(Windows)
# 源文件:Calculator.cpp
add_library(calculator SHAREDCalculator.cpp
)# 2. 设置 calculator 的头文件路径
# PUBLIC:自己用 + 暴露给使用者
target_include_directories(calculatorPUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
)# 3. 链接依赖:calculator 依赖 math_utils 静态库
# PRIVATE 表示 math_utils 仅 calculator 内部使用,不暴露给主程序
# 但主程序仍能通过 calculator 调用 MathUtils 的功能
target_link_libraries(calculatorPRIVATE math_utils
)# 4. 为动态库添加版本号
# 这会影响生成的文件名,如 libcalculator.so.1.0
set_target_properties(calculator PROPERTIESVERSION 1.0SOVERSION 1
)# 5. 输出状态信息
message(STATUS "Building shared library: calculator (depends on math_utils)")
Caculator.h
// calculator_project/calculator/Calculator.h#ifndef CALCULATOR_H
#define CALCULATOR_H// 计算器类,使用 MathUtils 提供的功能
class Calculator {
public:// 计算一个数的平方(调用静态库)double square(double x);// 计算一个数的立方(调用静态库)double cube(double x);
};#endif // CALCULATOR_H
Caculator.cpp
// calculator_project/calculator/Calculator.cpp#include "Calculator.h"
#include "MathUtils.h" // 包含静态库头文件// 使用 MathUtils::square
double Calculator::square(double x) {return MathUtils::square(x);
}// 使用 MathUtils::cube
double Calculator::cube(double x) {return MathUtils::cube(x);
}
可执行程序构建
进入app
CMakeLists.txt
# calculator_project/app/CMakeLists.txt# 1. 创建可执行文件 "app",源文件 main.cpp
add_executable(appmain.cpp
)# 2. 链接 app 与动态库 calculator
# PRIVATE:仅 app 使用
target_link_libraries(appPRIVATE calculator
)# 3. 确保 app 能找到 calculator 的头文件
# 因为 calculator 的 PUBLIC include 路径会被继承
target_include_directories(appPRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
)# 4. 添加一个测试(可选)
# 创建一个测试,运行 app
add_test(NAME test_app_runCOMMAND app
)
main.cpp
// calculator_project/app/main.cpp#include "Calculator.h"
#include <iostream>int main() {Calculator calc;std::cout << "Square of 5: " << calc.square(5) << std::endl; // 25std::cout << "Cube of 3: " << calc.cube(3) << std::endl; // 27return 0;
}
构建&测试
进入根目录,即calculator_project目录
构建,为了保持工作目录干净新建build目录。
mkdir build
cd build
cmake ..
make
运行
./app/app
运行结果显示为
Square of 5: 25
Cube of 3: 27