一文学会CMakeLists.txt: CMake现代C++跨平台工程化实战

你能学到什么?

朋友们好久不见,我是alibli,好久没有更新博客了。今天本人将通过构造一个实际的虚拟小项目,来让你彻底掌握CMake跨平台工程构建,学会CMakeLists.txt语法。该项目实现了一个简单的平方、立方的计算程序,只为演示工程构建方法。

每一行代码都会有非常非常详细的解释!

你将掌握如下内容:

  1. 构建静态库
  2. 构建动态库
  3. 构建可执行程序
  4. 动态库如何调用静态库
  5. 可执行程序如何调用动态库

项目整体构建

项目目录:

bash 复制代码
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

bash 复制代码
# 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

bash 复制代码
# calculator_project/math_utils/CMakeLists.txt

# 1. 定义一个静态库目标,名为 "math_utils"
#    STATIC 表示生成静态库(.a 文件)
#    源文件列表:MathUtils.cpp
add_library(math_utils STATIC
    MathUtils.cpp
)

# 2. 设置目标 math_utils 的头文件搜索路径
#    PUBLIC 表示:
#      - math_utils 自己需要这些头文件(PRIVATE 部分)
#      - 链接 math_utils 的目标也能看到这些头文件(INTERFACE 部分)
#    ${CMAKE_CURRENT_SOURCE_DIR} 是当前目录(即 math_utils/)
#    所以其他代码可以 #include "MathUtils.h"
target_include_directories(math_utils
    PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
)

# 3. 关键:启用 PIC,以便被动态库链接
set_target_properties(math_utils PROPERTIES
    POSITION_INDEPENDENT_CODE ON
)

# 4. 为 math_utils 添加编译定义(可选)
#    这里定义一个宏 MATH_UTILS_VERSION="1.0"
#    在 MathUtils.cpp 中可以用 #ifdef MATH_UTILS_VERSION 条件编译
target_compile_definitions(math_utils
    PRIVATE MATH_UTILS_VERSION=\"1.0\"
)

# 5. 输出一条状态信息,帮助调试
#    构建时会显示:-- Building static library: math_utils
message(STATUS "Building static library: math_utils")

MathUtils.h

cpp 复制代码
// calculator_project/math_utils/MathUtils.h
  2
  3 #ifndef MATH_UTILS_H
  4 #define MATH_UTILS_H
  5
  6 // 简单的数学工具类(静态库提供)
  7 class MathUtils {
  8 public:
  9     // 计算平方
 10     static double square(double x);
 11
 12     // 计算立方
 13     static double cube(double x);
 14 };
 15
 16 #endif // MATH_UTILS_H

MathUtils.cpp

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

bash 复制代码
# calculator_project/calculator/CMakeLists.txt

# 1. 定义一个共享库(动态库),名为 "calculator"
#    SHARED 表示生成 .so 文件(Linux)或 .dll(Windows)
#    源文件:Calculator.cpp
add_library(calculator SHARED
    Calculator.cpp
)

# 2. 设置 calculator 的头文件路径
#    PUBLIC:自己用 + 暴露给使用者
target_include_directories(calculator
    PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
)

# 3. 链接依赖:calculator 依赖 math_utils 静态库
#    PRIVATE 表示 math_utils 仅 calculator 内部使用,不暴露给主程序
#    但主程序仍能通过 calculator 调用 MathUtils 的功能
target_link_libraries(calculator
    PRIVATE math_utils
)

# 4. 为动态库添加版本号
#    这会影响生成的文件名,如 libcalculator.so.1.0
set_target_properties(calculator PROPERTIES
    VERSION 1.0
    SOVERSION 1
)

# 5. 输出状态信息
message(STATUS "Building shared library: calculator (depends on math_utils)")

Caculator.h

cpp 复制代码
// 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

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

bash 复制代码
# calculator_project/app/CMakeLists.txt

# 1. 创建可执行文件 "app",源文件 main.cpp
add_executable(app
    main.cpp
)

# 2. 链接 app 与动态库 calculator
#    PRIVATE:仅 app 使用
target_link_libraries(app
    PRIVATE calculator
)

# 3. 确保 app 能找到 calculator 的头文件
#    因为 calculator 的 PUBLIC include 路径会被继承
target_include_directories(app
    PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
)

# 4. 添加一个测试(可选)
#    创建一个测试,运行 app
add_test(
    NAME test_app_run
    COMMAND app
)

main.cpp

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;   // 25
    std::cout << "Cube of 3: " << calc.cube(3) << std::endl;       // 27

    return 0;
}

构建&测试

进入根目录,即calculator_project目录

构建,为了保持工作目录干净新建build目录。

bash 复制代码
mkdir build
cd build
cmake ..
make

运行

bash 复制代码
./app/app

运行结果显示为

bash 复制代码
Square of 5: 25
Cube of 3: 27
相关推荐
MMjeaty2 小时前
map/multimap容器
数据结构·c++
Florence233 小时前
GPU硬件架构和配置的理解
开发语言
青草地溪水旁3 小时前
设计模式(C++)详解—原型模式(2)
c++·设计模式·原型模式
青草地溪水旁3 小时前
设计模式(C++)详解—原型模式(3)
c++·设计模式·原型模式
C++_girl3 小时前
缓存未命中
c++·缓存
李游Leo3 小时前
JavaScript事件机制与性能优化:防抖 / 节流 / 事件委托 / Passive Event Listeners 全解析
开发语言·javascript·性能优化
bikong73 小时前
桥接模式,打造灵活可扩展的日志系统C++
c++·桥接模式
艾莉丝努力练剑3 小时前
【C++】类和对象(下):初始化列表、类型转换、Static、友元、内部类、匿名对象/有名对象、优化
linux·运维·c++·经验分享
疋瓞3 小时前
C++_STL和数据结构《1》_STL、STL_迭代器、c++中的模版、STL_vecto、列表初始化、三个算法、链表
数据结构·c++·算法