C++之 CMake、CMakeLists.txt、Makefile

这两者的关系其实非常好理解,我们可以用一个**"盖房子"**的例子来打比方。

简单来说:CMake 是"设计师",Makefile 是"施工图纸",Make 是"施工队"。

🏠 直白的大白话解释

想象你要盖一栋房子(编译一个 C++ 项目):

  1. CMakeLists.txt(你的需求)

    这是你写给设计师的需求文档。你在上面写:"我要一个 3 室 2 厅的房子,要有落地窗,风格要现代。"(这对应你的项目配置:我要用 C++17,我有 main.cpp 和 utils.cpp 这些文件,我要链接 OpenGL 库)。

  2. CMake(设计师/翻译官)

    CMake 读取你的需求文档(CMakeLists.txt),然后根据你的环境(你是住在 Linux 还是 Windows?)画出了一套详细的施工图纸Makefile)。

    • 如果你在 Linux,它会画出适合 Linux 的图纸。
    • 如果你在 Windows,它会画出适合 Windows 的图纸(比如 Visual Studio 的项目文件)。
  3. Makefile(施工图纸/说明书)

    这是一份非常详细的操作指南 。上面写着:"第一步,先倒水泥;第二步,砌墙;第三步,刷漆。"(这对应具体的编译命令:先用 g++ 编译 main.cpp 生成 main.o,再链接...)。注意:Makefile 自己不会干活,它只是被阅读的。

  4. Make(施工队/工头)

    Make 是一个工具,它拿着 Makefile(施工图纸),指挥具体的工人(编译器 g++)去干活。它会检查:"墙是不是已经砌好了?没砌好就赶紧砌,砌好了就不用管了。"


🔗 它们的具体关系

  • CMake 生成 Makefile
    CMake 的工作就是 Makefile。当你运行 cmake . 时,它就在帮你生成这个文件。
  • Make 执行 Makefile
    Make 的工作是 Makefile。当你运行 make 时,它按照 Makefile 里的指令去调用编译器。

🆚 为什么要用 CMake,直接写 Makefile 不行吗?

行,但是太累了。

  • 手写 Makefile
    就像你自己画施工图纸。如果房子只有 10 平米(几个文件),你自己画很快。但如果要盖摩天大楼(几千个文件,复杂的依赖),或者你要在不同的地方盖(Linux、Windows、Mac),手写图纸会累死人,而且容易出错。
  • 用 CMake
    你只需要写一份需求文档(CMakeLists.txt)。不管你要盖什么房子,不管在哪里盖,CMake 都能自动帮你生成对应的图纸。

📌 总结

  • CMake :是跨平台的构建生成器。它负责把"人话"(配置)翻译成"机器话"(Makefile)。
  • Makefile :是构建脚本。它记录了具体的编译步骤。
  • Make :是执行工具。它负责执行 Makefile 里的命令。

你的工作流程通常是:

CMakeLists.txt -> 运行 cmake 生成 Makefile -> 运行 make 编译代码。

bash 复制代码
# 指定 CMake 的最低版本要求
cmake_minimum_required(VERSION 3.10)

# 定义项目名称和版本
project(main VERSION 1.0)

# 设置 C++ 标准为 C++17,并要求编译器必须支持
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 导出编译命令数据库,方便 VSCode 的 C/C++ 插件进行代码分析
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# 添加头文件搜索路径
include_directories(${PROJECT_SOURCE_DIR}/include)

# 收集 src 目录下的所有 .cpp 源文件
file(GLOB SOURCES "${PROJECT_SOURCE_DIR}/src/*.cpp")

# 定义要生成的可执行文件及其源文件
add_executable(${PROJECT_NAME} ${SOURCES})

# 设置输出路径
set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin)

这份 CMakeLists.txt 写得非常规范且现代,比上一版更健壮。特别是你使用了 set_target_properties 来指定输出路径,这是比旧版 EXECUTABLE_OUTPUT_PATH 更推荐的做法。

下面我为你逐行拆解每一行代码、关键字的含义,并结合你 E:/C++11 这个项目给出它们实际对应的值。


核心配置部分

cmake_minimum_required(VERSION 3.10)
  • 含义:声明运行此 CMake 脚本所需的最低版本。如果用户的 CMake 版本低于 3.10,CMake 会报错并停止运行。
  • 作用:确保脚本中使用的语法特性在当前环境中受支持。
  • 当前状态:你的电脑上安装的 CMake 版本必须 ≥ 3.10。
project(main VERSION 1.0)
  • 含义:定义项目名称和版本号。这是 CMake 脚本中最重要的指令之一,它会初始化一系列变量。
  • 关键字
    • main:这是你给项目起的名字。
    • VERSION 1.0:项目的版本号。
  • 生成的变量
    • PROJECT_NAME:值为 "main"
    • PROJECT_VERSION:值为 "1.0"
    • PROJECT_SOURCE_DIR:值为 "E:/C++11"(即包含此文件的根目录)。
set(CMAKE_CXX_STANDARD 17)
  • 含义:告诉 CMake 使用 C++17 标准来编译代码。
  • 作用 :相当于在 g++ 编译器后面加了 -std=c++17 参数。这让你可以使用现代 C++ 的特性(如 autofilesystem 等)。
set(CMAKE_CXX_STANDARD_REQUIRED ON)
  • 含义:强制要求编译器支持 C++17。
  • 作用:如果编译器太老(不支持 C++17),CMake 会直接报错,而不是悄悄降级使用旧标准(如 C++98),避免莫名其妙的编译错误。
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
  • 含义 :生成 compile_commands.json 文件。
  • 作用:这个文件包含完整的编译命令。VSCode 的 C/C++ 插件读取它,就能精准地知道头文件在哪里、宏定义是什么,从而提供完美的代码跳转和补全功能。

源文件与路径配置

include_directories(${PROJECT_SOURCE_DIR}/include)
  • 含义:添加头文件搜索路径。
  • 关键字
    • include_directories:指令,用于指定头文件目录。
    • ${PROJECT_SOURCE_DIR}:变量引用,取值为 "E:/C++11"
  • 实际路径E:/C++11/include
  • 作用 :当你在代码中写 #include <myheader.h> 时,编译器会去这个目录下查找。
file(GLOB SOURCES "${PROJECT_SOURCE_DIR}/src/*.cpp")
  • 含义:使用通配符查找文件。
  • 关键字
    • file(GLOB ...):文件操作指令,用于匹配文件路径。
    • SOURCES:你自定义的一个变量名,用来存放找到的文件列表。
    • "${PROJECT_SOURCE_DIR}/src/*.cpp":匹配规则。
  • 实际值 :假设你 src 文件夹里有 main.cpptest.cpp,那么 SOURCES 变量的值就是 "E:/C++11/src/main.cpp;E:/C++11/src/test.cpp"
  • 注意 :这种方式会自动把 src 下所有 .cpp 加入编译,不用每次新增文件都改 CMake 文件,很方便。
add_executable(${PROJECT_NAME} ${SOURCES})
  • 含义:定义一个可执行文件目标。
  • 关键字
    • add_executable:指令,生成 .exe(Windows)或无后缀可执行文件(Linux)。
    • ${PROJECT_NAME}:值为 "main",这是生成的程序的名字。
    • ${SOURCES}:值为上面 file(GLOB ...) 找到的所有 .cpp 文件。
  • 作用 :告诉 CMake:"请把 SOURCES 里的这些代码编译成一个叫 main 的程序"。

输出路径配置(关键点)

set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin)
  • 含义:设置特定目标的属性。这里专门设置"运行时输出目录"。
  • 关键字
    • set_target_properties:指令,用于精细化控制目标的属性。
    • ${PROJECT_NAME}:目标名,即 "main"
    • PROPERTIES:关键字,后面跟属性名和值。
    • RUNTIME_OUTPUT_DIRECTORY:属性名,专门控制可执行文件(.exe)的输出位置。
    • ${PROJECT_SOURCE_DIR}/bin:属性值,即 "E:/C++11/bin"
  • 为什么比旧版好?
    • 旧版(EXECUTABLE_OUTPUT_PATH)是全局变量,会影响项目里所有的可执行文件。
    • 新版(你用的这个) :只针对名为 main 的这个程序生效。如果你的项目将来变大了,包含多个程序,你可以分别为它们设置不同的输出目录,互不干扰。

总结

你的这份 CMake 脚本逻辑非常清晰:

  1. 定标准:要求 CMake 3.10+,使用 C++17。
  2. 找文件 :自动扫描 src 下的代码,指定 include 目录。
  3. 生程序 :编译生成名为 main 的程序。
  4. 定位置 :把生成的程序扔进 E:/C++11/bin 文件夹里。

这套配置在 Windows 和 Linux 下通用,直接运行 CMake: Build 即可。

相关推荐
研究点啥好呢6 小时前
Github热门项目推荐 | 创建你的像素风格!
c++·python·node.js·github·开源软件
_dindong6 小时前
cf1091div2 C.Grid Covering(数论)
c++·算法
lly2024066 小时前
C 标准库 - `<stdio.h>`
开发语言
沫璃染墨6 小时前
C++ string 从入门到精通:构造、迭代器、容量接口全解析
c语言·开发语言·c++
jwn9996 小时前
Laravel6.x核心特性全解析
开发语言·php·laravel
迷藏4946 小时前
**发散创新:基于Rust实现的开源合规权限管理框架设计与实践**在现代软件架构中,**权限控制(RBAC)** 已成为保障
java·开发语言·python·rust·开源
6Hzlia6 小时前
【Hot 100 刷题计划】 LeetCode 17. 电话号码的字母组合 | C++ 回溯算法经典模板
c++·算法·leetcode
功德+n6 小时前
Linux下安装与配置Docker完整详细步骤
linux·运维·服务器·开发语言·docker·centos
明日清晨6 小时前
python扫码登录dy
开发语言·python