使用 CMake 自动管理 C/C++ 项目

news/2025/2/6 22:46:27 标签: c语言, c++, cmake

使用 CMake 自动管理 C/C++ 项目

1. 介绍

CMake 是一个强大的构建系统,可用于跨平台管理 C/C++ 项目的编译过程。本 CMakeLists.txt 文件提供了一种自动化的方式来管理 C/C++ 项目,包括创建代码目录、自动编译所有源文件、管理输出文件等。

2. CMake 最低版本要求 & 项目信息

cmake">cmake_minimum_required(VERSION 3.14)
project(TestDemo C CXX)

这段代码确保 CMake 版本不低于 3.14,并定义项目名称 TestDemo,支持 C 和 C++ 语言。

3. 主要功能

CMakeLists.txt 具备以下功能:

  • 自动创建 src 目录,存放源代码文件;
  • 自动遍历 src 目录,编译其中的所有 .c.cpp 文件;
  • 设定编译输出路径,避免污染项目根目录:
    • .runtime/ 目录存放可执行文件;
    • .library/ 目录存放静态/动态库;
    • .archive/ 目录存放编译中间文件;
  • 支持多个 main() 函数,可以直接运行单个源文件。

4. C/C++ 语言标准设定

cmake">set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 17)

此处指定 C 语言标准为 C99,C++ 语言标准为 C++17。

5. 编译输出目录设定

cmake">set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/.archive)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/.library)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/.runtime)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/.runtime)
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/.library)

这些配置确保编译后的文件不会污染项目根目录。

6. 自动创建 src 目录

cmake">set(SRC_DIR ${PROJECT_SOURCE_DIR}/src)
if (NOT EXISTS ${SRC_DIR})
    file(MAKE_DIRECTORY ${SRC_DIR})
endif ()
message(STATUS "src目录: ${SRC_DIR}")

src 目录不存在,则自动创建,并在 CMake 输出日志中打印 src 目录路径。

7. 头文件搜索路径

cmake">include_directories(${SRC_DIR})

这行代码告诉编译器在 src 目录中查找头文件。

8. 自动遍历 src 目录下的 C/C++ 文件

cmake">file(GLOB_RECURSE SOURCE_FILES "${SRC_DIR}/*.c" "${SRC_DIR}/*.cpp")

使用 file(GLOB_RECURSE ...) 指令递归查找 src 目录下的所有 .c.cpp 文件。

9. 生成唯一目标名并添加可执行文件

cmake">foreach (SOURCE_FILE ${SOURCE_FILES})
    get_filename_component(FILE_NAME ${SOURCE_FILE} NAME)
    string(REGEX REPLACE "^${SRC_DIR}[\\/]" "" RELATIVE_PATH ${SOURCE_FILE})
    string(REGEX REPLACE "[\\/]" "." TARGET_NAME ${RELATIVE_PATH})
    add_executable(${TARGET_NAME} ${SOURCE_FILE})
    message(STATUS "[Compile] ${SOURCE_FILE} --> ${TARGET_NAME}")
endforeach ()
  • 获取文件名
    cmake">get_filename_component(FILE_NAME ${SOURCE_FILE} NAME)
    
  • 计算相对路径(去掉 src/ 部分),并将 / 替换为 .,仿照 Java 包命名格式。
  • 添加可执行文件,确保每个 C/C++ 文件都能独立编译。

10. 编译警告设置

cmake">if (MSVC)
    add_compile_options(/W4)
else ()
    add_compile_options(-Wall -Wextra -Wpedantic)
endif ()
  • Windows(MSVC):开启最高级别 /W4 警告。
  • Linux/macOS(GCC/Clang):启用 -Wall -Wextra -Wpedantic,增强代码质量。

11. CMake 关键指令说明

11.1 获取文件名组件

cmake">get_filename_component(变量名 文件路径 组件类型)

示例:

cmake">get_filename_component(FILE_NAME "D:/test/hello.txt" NAME_WE)

11.2 递归扫描目录下的 C/C++ 文件

cmake">file(GLOB_RECURSE 变量名 "路径/*.c" "路径/*.cpp")

示例:

cmake">file(GLOB_RECURSE SOURCE_FILES "src/*.c" "src/*.cpp")

11.3 正则替换字符串

cmake">string(REGEX REPLACE "正则表达式" "替换字符串" 变量 输入变量)

示例:

cmake">string(REGEX REPLACE "\\/" "." TARGET_NAME "src/com/example/Main.cpp")

11.4 数学运算

cmake">math(EXPR 变量 "数学表达式")

示例:

cmake">math(EXPR RESULT "5 * 5")

11.5 编译可执行文件

cmake">add_executable(可执行文件 源文件)

示例:

cmake">add_executable(my_program main.cpp)

11.6 目标链接库

cmake">target_link_libraries(可执行文件 PRIVATE 库名1 库名2)

示例:

cmake">target_link_libraries(my_program PRIVATE jsoncpp_lib jsoncpp_object)

11.7 日志输出

cmake">message(STATUS "日志信息")

示例:

cmake">message(STATUS "编译目录: ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")

12. 结论

这个 CMakeLists.txt 通过自动化管理 C/C++ 源文件、输出目录和编译过程,使项目更加结构化。它可以让开发者专注于编写代码,而无需手动管理每个可执行文件的构建。

13. 我常用的CMakeLists.txt配置

cmake"># 设置 CMake 最小版本 & 项目名称, 编程语言C、C++
cmake_minimum_required(VERSION 3.14)
project(TestDemo C CXX)

# >------------------------------- CMake 功能说明 -------------------------------
# 本 CMakeLists.txt 具备以下功能:
# 1. 重新加载 CMake 项目时,自动创建 src 目录(存放代码文件)。
# 2. 自动遍历 src 目录下的所有 .c 和 .cpp 文件,并生成独立的可执行文件。
# 3. 设定编译输出路径,避免污染项目根目录:
#    - 可执行文件存放于 `.runtime/` 目录。(即编译结果)
#    - 静态/动态库存放于 `.library/` 目录。
#    - 编译时中间文件存放于 `.archive/` 目录。
# 4. 允许多个 main() 函数共存,可直接运行单个文件。
# >------------------------------- 使用说明 -------------------------------
# 使用此CMakeList时,若要新建C/C++语言文件,请按照以下步骤:
# 1. 右键src目录(如果没有src目录,请先创建) ——> 新建 ——> C/C++源文件
# 2. 在弹出的对话框中,输入文件名(仅允许英文小写及下划线,不要出现空格),
#    后缀为 ".c(C语言文件)"或".cpp(C++文件)",切记不要勾选“添加到目标”,点击确定。
# 3. 点击 左上角横线 ——> 文件 ——> 重新加载CMake项目。
# 注:未重载前,进入文件可能会有“不属于任何项目目标”的警告,重载后即会消失。
#    若此警告未消失,请检查文件是否在src目录下,以及文件名是否符合规范。
# >------------------------------- 运行说明 -------------------------------
# 使用此CMakeList时,若运行 main() 函数代码,请直接点击函数前的绿色三角形按钮。
# 右上角的运行按钮会自动运行最近一次运行的程序,因此可能不是你想要的结果。
# >------------------------------- 注意事项 -------------------------------
# 可执行文件名,模仿Java包名 + 文件名 格式
# 例如:
#      文件路径 src/com/example/testdemo/Main.cpp
#      可执行文件名 com.example.testdemo.Main.cpp
# ------------------------------------------------------------------------

# 按照书本要求设定C语言和C++版本
set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 17)

# 定义输出目录,避免污染根目录
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/.archive)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/.library)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/.runtime)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/.runtime)
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/.library)

# 自动创建存放代码文件夹 src (重新加载CMake项目后,会自动创建)
set(SRC_DIR ${PROJECT_SOURCE_DIR}/src)
if (NOT EXISTS ${SRC_DIR})
    file(MAKE_DIRECTORY ${SRC_DIR})
endif ()
message(STATUS "src目录: ${SRC_DIR}")

# 设定头文件搜索路径
include_directories(${SRC_DIR})

# 遍历 src 目录下所有 C/C++ 文件并自动添加
file(GLOB_RECURSE SOURCE_FILES "${SRC_DIR}/*.c" "${SRC_DIR}/*.cpp")

foreach (SOURCE_FILE ${SOURCE_FILES}) # 遍历所有发现的文件
    # 生成唯一的目标名(避免重名问题)
    # get_filename_component(FILE_NAME ${SOURCE_FILE} NAME_WE) # 获取文件名(去掉扩展名)【注意: 这样子 C、C++ 文件在同一个目录, 并且文件名相同时,会导致冲突】
    get_filename_component(FILE_NAME ${SOURCE_FILE} NAME) # 获取文件名
    # 计算相对路径(去掉 src/ 部分),并且将`/`替换成`.` 模仿成Java的包名,方便后续添加到编译命令中
    string(REGEX REPLACE "^${SRC_DIR}[\\/]" "" RELATIVE_PATH ${SOURCE_FILE})
    string(REGEX REPLACE "[\\/]" "." TARGET_NAME ${RELATIVE_PATH})  # 将`/`替换成`.` 模仿成Java的包名,避免重名问题

    # 添加可执行文件
    add_executable(${TARGET_NAME} ${SOURCE_FILE})
    message(STATUS "[Compile] ${SOURCE_FILE} --> ${TARGET_NAME}")
endforeach () # 遍历结束

# -------------------------- CMake 关键指令说明 --------------------------
# 1. 获取文件名组件:
#    get_filename_component(变量名 文件路径 组件类型)
#    例如: 获取文件名(无扩展名)
#    get_filename_component(FILE_NAME "D:/test/hello.txt" NAME_WE)
#
# 2. 递归扫描指定目录下的所有 C/C++ 文件:
#    file(GLOB_RECURSE 保存结果的变量名 "路径/*.c" "路径/*.cpp")
#    例如: 扫描 src 目录下的所有 C 和 C++ 文件
#    file(GLOB_RECURSE SOURCE_FILES "D:/test1/*.c" "D:/test2/*.cpp")
#
# 3. 字符串处理:
#    - 正则表达式替换:
#      string(REGEX REPLACE "正则表达式" "替换字符串" 存储结果变量 输入变量)
#      例如: 将路径中的 "/" 替换为 "."
#      string(REGEX REPLACE "/" "." TARGET_NAME "D:/test/hello.txt")
#
#    - 计算字符串长度:
#      string(LENGTH 字符串变量 存储结果变量)
#
#    - 字符串截取:
#      string(SUBSTRING 字符串变量 起始下标 截取元素个数 存储结果变量)
#      例如: 截取字符串的前 5 个字符
#      string(SUBSTRING "hello world" 0 5 SUB_STR)
#
# 4. 数学运算:
#    math(EXPR 存储结果变量 "数学表达式")
#    例如: 计算一个数值的平方
#    math(EXPR RESULT "5 * 5")
#
# 5. 编译可执行文件:
#    add_executable(可执行文件名称 源码文件)
#    例如: 生成一个名为 my_program 的可执行文件
#    add_executable(my_program main.cpp)
#
# 6. 目标链接库:
#    target_link_libraries(可执行文件名称 PRIVATE 库名1 库名2)
#    例如: 连接 jsoncpp 库
#    target_link_libraries(my_program PRIVATE jsoncpp_lib jsoncpp_object)
#
# 7. 日志输出:
#    message(STATUS "日志信息")
#    例如: 输出 CMake 变量值
#    message(STATUS "编译目录: ${OUTPUT_DIR_RUNTIME}")
# ----------------------------------------------------------------------

# *********************************************************************************


# 设置编译选项(开启警告)
if (MSVC)
    add_compile_options(/W4)
else ()
    add_compile_options(-Wall -Wextra -Wpedantic)
endif ()

效果图
在这里插入图片描述


http://www.niftyadmin.cn/n/5843367.html

相关文章

Web安全|渗透测试|网络安全

基础入门(P1-P5) p1概念名词 1.1域名 什么是域名? 域名:是由一串用点分隔的名字组成的Internet上某一台计算机或计算机组的名称,用于在数据传输时对计算机的定位标识(有时也指地理位置)。 什么是二级域名多级域名…

关于大数据

在大数据背景下存在的问题: 非结构化、半结构化数据:NoSQL数据库只负责存储;程序处理时涉及到数据移动,速度慢 是否存在一套整体解决方案? 可以存储并处理海量结构化、半结构化、非结构化数据 处理海量数据的速…

http cookie的作用学习

1.介绍 HTTP Cookie 是 服务器发送给客户端(浏览器)的一小段数据,它会被客户端存储,并在后续请求时自动携带,以便服务器识别用户、保持会话状态或存储用户偏好等信息。 流程: 服务器发送 Cookie 服务器…

C# 异常处理全解析

.NET学习资料 .NET学习资料 .NET学习资料 在 C# 编程中,异常处理是一项至关重要的技术,它能够确保程序在面对各种意外情况时仍能稳定运行。本文将深入探讨 C# 异常处理的相关知识。 一、什么是异常 异常是程序在运行过程中发生的错误或意外情况。当程…

AI绘画:解锁商业设计新宇宙(6/10)

1.AI 绘画:商业领域的潜力新星 近年来,AI 绘画技术以惊人的速度发展,从最初简单的图像生成,逐渐演变为能够创造出高度逼真、富有创意的艺术作品。随着深度学习算法的不断优化,AI 绘画工具如 Midjourney、Stable Diffu…

1 HBase 基础

1 HBase 基础 1. HBase 基本介绍2. HBase 与 Hadoop 的关系3. RDBMS 与 HBase 的对比4. HBase 特征简要 1. HBase 基本介绍 简介 HBase 是 BigTable 的开源 Java 版本。是建立在 HDFS 之上,提供高可靠性、高性能、列存储、可伸缩、实时读写 NoSql 的数据库系统。 它…

机器学习8-卷积和卷积核1

机器学习8-卷积和卷积核1 卷积与图像去噪卷积的定义与性质定义性质卷积的原理卷积步骤卷积的示例与应用卷积的优缺点优点缺点 总结 高斯卷积核卷积核尺寸的设置依据任务类型考虑数据特性实验与调优 高斯函数标准差的设置依据平滑需求结合卷积核尺寸实际应用场景 总结 图像噪声与…

《Kettle保姆级教学-界面介绍》

目录 一、Kettle介绍二、界面介绍1.界面构成2、菜单栏详细介绍2.1 【文件F】2.2 【编辑】2.3 【视图】2.4 【执行】2.5 【工具】2.6 【帮助】 3、转换界面介绍4、作业界面介绍5、执行结果 一、Kettle介绍 Kettle 是一个开源的 ETL(Extract, Transform, Load&#x…