news 2026/9/4 21:15:03

第一个 CMake 项目:最小 CMakeLists.txt 到底写了什么

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
第一个 CMake 项目:最小 CMakeLists.txt 到底写了什么

欢迎拜访:雾里看山-CSDN博客
本篇主题:第一个 CMake 项目:最小CMakeLists.txt到底写了什么
发布时间:2026.9.3
隶属专栏:CMake

目录

  • 这一篇的目标
  • 最小的工程长什么样
    • 文件结构
    • main.cpp
    • CMakeLists.txt
  • 逐行拆解
    • 第一行:cmake_minimum_required
    • 第二行:project
      • 关于语言的默认值
      • 关于 C 和 CXX
      • VERSION 字段
    • 第三行:add_executable
  • 把它跑起来
  • 容易踩的几个坑
    • 坑 1:文件名大小写
    • 坑 2:忘记写 `cmake_minimum_required`
    • 坑 3:target 名和源文件同名
    • 坑 4:相对路径的理解
    • 坑 5:源文件里有 `.c` 又想当 C++ 编
  • 加点东西看看效果
    • 1. 加多个源文件
    • 2. 加头文件目录
    • 3. 加 C++ 标准
    • 4. 加宏定义
  • target 是 CMake 的核心
  • 把最小工程再压缩到极致
  • 这一篇抓什么
  • 总结

这一篇的目标

上一篇跑通了命令行工作流。这一篇就把CMakeLists.txt真正打开看看:一个最小可运行的 CMake 工程到底由哪些行组成,每一行的作用是什么

虽然只有几行,但里面每一行都有自己的角色。把这几行吃透,后面再加 target、加依赖、装包都不会陌生。

最小的工程长什么样

文件结构

一个最小 CMake 工程通常长这样:

hello/ ├── CMakeLists.txt └── main.cpp

main.cpp

先写一个简单的 C++ 程序:

#include<iostream>intmain(){std::cout<<"Hello, CMake!"<<std::endl;return0;}

CMakeLists.txt

对应写一个最小版本:

cmake_minimum_required(VERSION 3.15) project(HelloCMake LANGUAGES CXX) add_executable(hello main.cpp)

只有三行命令,但它已经是一个完整的、可构建的工程。

逐行拆解

第一行:cmake_minimum_required

cmake_minimum_required(VERSION 3.15)

这一行的作用是:声明这个项目至少需要哪个版本的 CMake 才能正常构建

为什么要写这一行?

CMake 自己也在演进,每个版本会引入新的命令、新的策略(policy)、新的行为。如果项目用到了一些新特性,而对方的 CMake 版本太旧,就可能出问题。

所以:

  • 这是兼容性保护,不是“版本号越高越好”
  • 选择版本的原则是:用到你需要的特性的最低版本

比如:

  • 要用target_link_libraries(... PRIVATE/PUBLIC/INTERFACE)的现代语义,至少 3.x 任意版本都行
  • 要用FetchContent,需要 3.14+
  • 要用imported targets的标准改进,需要 3.15+
  • 要用CMakePresets.json,需要 3.19+

如果团队用的 CMake 都在 3.20 以上,那直接写 3.20 也没问题。

另外,cmake_minimum_required必须是CMakeLists.txt中的第一行有效命令(注释和空行不算),否则 CMake 会报错。

第二行:project

project(HelloCMake LANGUAGES CXX)

这一行做几件事:

  • 给项目取一个名字HelloCMake
  • 声明这个项目使用的语言:LANGUAGES CXX表示只用 C++
  • 同时,CMake 会自动给一些变量赋值:
    • PROJECT_NAMEHelloCMake
    • CMAKE_PROJECT_NAME→ 顶层项目名
    • PROJECT_SOURCE_DIRPROJECT_BINARY_DIR

关于语言的默认值

如果不写LANGUAGES,CMake 默认是,也就是说不会自动启用任何语言的检查。

但很多人会看到这种写法:

project(HelloCMake CXX)

这种是不带关键字的写法,靠位置参数识别。两种写法等价:

project(HelloCMake CXX) project(HelloCMake LANGUAGES CXX)

推荐带LANGUAGES关键字,更清楚。

关于 C 和 CXX

如果工程同时有 C 和 C++:

project(MyApp LANGUAGES C CXX)

如果只有 C:

project(MyApp LANGUAGES C)

VERSION 字段

project还可以接受一个版本号:

project(HelloCMake VERSION 1.0.0 LANGUAGES CXX)

它会顺带设置:

  • PROJECT_VERSION1.0.0
  • PROJECT_VERSION_MAJOR1
  • PROJECT_VERSION_MINOR0
  • PROJECT_VERSION_PATCH0

后面做打包、安装、版本头文件生成时经常会用到这些变量。

第三行:add_executable

add_executable(hello main.cpp)

这一行是 CMake 中最关键的一行之一,作用是:声明一个可执行文件目标

把它拆开看:

  • hello:target 名(目标名),也是最终生成的产物名(在 Linux 上是hello,Windows 上是hello.exe
  • main.cpp:构成这个可执行文件的源文件列表

执行完这一行之后,CMake 内部就建立了一个名为hello的 target,后续可以围绕它做很多事情:

  • 链接库
  • 加头文件目录
  • 加编译选项
  • 加宏定义

不过这些是后面的内容,这一篇先建立“target 是 CMake 里的第一公民”这个概念。

把它跑起来

假设工程根目录是hello/,执行:

cdhello cmake-S.-Bbuild-GNinja cmake--buildbuild

执行完后:

hello/ ├── CMakeLists.txt ├── main.cpp └── build/ ├── build.ninja └── hello (可执行文件)

运行:

./build/hello

输出:

Hello, CMake!

至此,一个最小可运行 CMake 工程就跑通了。

容易踩的几个坑

坑 1:文件名大小写

在 Linux/macOS 上:

  • CMakeLists.txt必须大小写完全一致
  • CMakeLists.txt不是cmakeLists.txt,也不是CMakelists.txt

写错大小写,CMake 直接报错:Could not find file: ... CMakeLists.txt

坑 2:忘记写cmake_minimum_required

没写时,CMake 会用一个非常老的默认行为,很多现代写法会出问题。

所以:任何CMakeLists.txt都应该先写这一行

坑 3:target 名和源文件同名

下面这种写法是错的:

add_executable(main.cpp main.cpp)

target 名和文件名重复会导致 CMake 把第一个参数当成 target,源文件列表变成(main.cpp)(一个空名字 + 一个 main.cpp),逻辑混乱。

正确写法:

add_executable(my_app main.cpp)

或者:

add_executable(${PROJECT_NAME} main.cpp)

坑 4:相对路径的理解

add_executable(hello main.cpp)中的main.cpp是相对于当前CMakeLists.txt所在目录的,而不是当前执行cmake命令时的目录。

所以无论你在哪里调用cmake -S,源文件路径都是从CMakeLists.txt自身算起的。这一点在后面多目录工程里尤其重要。

坑 5:源文件里有.c又想当 C++ 编

如果main.cpp实际写的是 C 代码,CMake 默认会按CXX(C++)处理,可能出现奇怪错误。这种情况要么:

  • 把扩展名改成.c,并在project里加LANGUAGES C
  • 或者明确加set_source_files_properties(main.cpp PROPERTIES LANGUAGE CXX)

加点东西看看效果

下面在最小工程基础上,做一些最常见的扩展,方便感受 CMake 描述能力。

1. 加多个源文件

cmake_minimum_required(VERSION 3.15) project(HelloCMake LANGUAGES CXX) add_executable(hello main.cpp greet.cpp )

CMake 会把它们一起编译进同一个可执行文件。

2. 加头文件目录

cmake_minimum_required(VERSION 3.15) project(HelloCMake LANGUAGES CXX) add_executable(hello main.cpp) target_include_directories(hello PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)

这时:

hello/ ├── CMakeLists.txt ├── main.cpp ├── greet.cpp └── include/ └── greet.h

greet.cpp就可以这样写:

#include"greet.h"#include<iostream>voidgreet(conststd::string&name){std::cout<<"Hello, "<<name<<"!"<<std::endl;}

3. 加 C++ 标准

cmake_minimum_required(VERSION 3.15) project(HelloCMake LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) add_executable(hello main.cpp)

或者更推荐的做法是写到 target 上:

cmake_minimum_required(VERSION 3.15) project(HelloCMake LANGUAGES CXX) add_executable(hello main.cpp) target_compile_features(hello PRIVATE cxx_std_17)

这两个写法效果类似,后者更符合 target-first 的思想。

4. 加宏定义

target_compile_definitions(hello PRIVATE USE_FANCY_LOG=1)

源码里就可以:

#ifdefUSE_FANCY_LOGstd::cout<<"[FANCY] Hello, CMake!"<<std::endl;#elsestd::cout<<"Hello, CMake!"<<std::endl;#endif

target 是 CMake 的核心

这一节很重要,因为它决定了你后面写 CMake 的风格。

add_executable创建了一个 targethello。从此以后:

  • 头文件目录挂到hello
  • 宏定义挂到hello
  • 编译选项挂到hello
  • 依赖的库挂到hello

所有内容都围绕 target 转,这是现代 CMake 的核心思维。

与之相对的是“旧式 CMake”:

# 旧式:到处用全局变量 include_directories(include) add_compile_options(-Wall) add_definitions(USE_FANCY_LOG=1) add_executable(hello main.cpp) target_link_libraries(hello some_lib)

这种写法会让所有 target 都共享这些设置,互相污染,所以现代 CMake 强烈不推荐。

记住一句话:target 是第一公民,全局命令少用

把最小工程再压缩到极致

如果只允许保留三行,下面这三行就是一个最小 CMake 工程:

cmake_minimum_required(VERSION 3.15) project(Min LANGUAGES CXX) add_executable(min main.cpp)

跑一下:

cmake-S.-Bbuild-GNinja cmake--buildbuild ./build/min

你会发现,CMake 工程其实没有想象中复杂。

这一篇抓什么

这一篇真正要带走的,是这几点:

  • CMakeLists.txt最小有三行:cmake_minimum_requiredprojectadd_executable
  • cmake_minimum_required是兼容性护栏,必须写在最前面
  • project命名项目,并设置语言和(可选)版本号
  • add_executable创建 target,是现代 CMake 的起点
  • target 是 CMake 的第一公民,所有构建属性最终挂到 target 上

只要这三行命令的作用清楚了,后面学target_include_directoriestarget_link_libraries就非常自然。

总结

第一个 CMake 项目这一篇真正要抓住的,是这一组主线:

  • cmake_minimum_required设定最低版本
  • project命名项目、声明语言
  • add_executable创建可执行文件 target
  • target 是 CMake 一切行为的起点
  • 最小工程只需要这三行

下一篇会讲源码目录、构建目录与 out-of-source build,把“源码”和“构建产物”为什么要分开这件事说清楚。

⚠️ 写在最后:以上内容是我在学习以后得一些总结和概括,如有错误或者需要补充的地方欢迎各位大佬评论或者私信我交流!!!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/4 21:14:10

Spring Boot实战:高校双创竞赛管理系统的架构设计与实现

简介&#xff1a;本资源是一套基于Spring Boot框架开发的大学生创新创业竞赛全流程管理平台源码&#xff0c;面向高校计算机专业师生、双创教育管理者及Java全栈学习者&#xff0c;解决竞赛项目申报、路演展示、专家评审与多角色协同管理等实际业务场景需求。压缩包共403个文件…

作者头像 李华
网站建设 2026/9/4 21:12:01

Agentic RL 后训练资源怎么分?港中文、恒生大学提出 Libra

语言模型正从“回答问题”迈向“完成任务”, 于RL后训练里, 模型不但生成文本, 还会调用搜索、代码执行等外部工具, 依据环境返回继续推理。如此交互令模型具备更强行动能力, 还使训练系统面临一种相对普通RLHF较不稳定的工作负载: 同一批请求能够产生长度相差数十倍的轨迹, 少…

作者头像 李华
网站建设 2026/9/4 21:08:10

基于树莓派Pico和E22-900M22S的串口转LoRa模块设计与实战

做物联网的朋友应该都对LoRa不陌生&#xff0c;但真要自己从零搭一个可以用的串口转LoRa模块单元&#xff0c;很多人会卡在选型、接线、配置和天线这几关上。我今天把基于E22-900M22S模组和树莓派Pico的整套设计思路整理出来&#xff0c;从硬件选型到代码实现&#xff0c;再到调…

作者头像 李华
网站建设 2026/9/4 21:08:05

从变量到流程控制:VtorShell-02如何让自动化脚本告别硬编码

1. 从裸脚本到“半个编程语言”&#xff1a;VtorShell 的第二次进化如果你写过运维脚本、CI 流水线或自动化任务&#xff0c;多半经历过同一个尴尬阶段&#xff1a;脚本一开始只是几条命令的堆叠&#xff0c;用来完成一个固定动作。可当需求开始变化&#xff0c;比如“这次上线…

作者头像 李华
网站建设 2026/9/4 21:07:48

长时间断食不是饿肚子,而是代谢模式切换

「长时间断食」这四个字一出现&#xff0c;很多人脑子里已经蹦出两个极端画面&#xff1a;一边是“饿得头晕眼花也硬扛”&#xff0c;另一边是“几天不吃饭的苦行僧”。如果只看这些表面印象&#xff0c;你很容易把长时间断食理解成“普通轻断食的加强版——忍得更久、吃得更少…

作者头像 李华
网站建设 2026/9/4 21:06:47

肺结节检测YOLO数据集:三格式标签+患者级划分+开箱训练

简介&#xff1a;本资源是面向医学影像AI初学者与计算机视觉实践者的肺结节目标检测专项数据集&#xff0c;专为YOLO系列模型训练定制&#xff0c;解决真实临床场景下小目标、低对比度结节检测的数据匮乏与标注格式适配难题。资源包含10000张高质量胸部CT切片图像及完整标注体系…

作者头像 李华