C++ 怎么写Makefile C++ Linux项目编译构建入门【构建】

2次阅读

Makefile需显式定义c++编译规则:用.CPP.o或更可靠的显式规则(如main.o: main.cpp),设置CXXFLAGS含-std=c++17等,自动生成依赖文件%.d并-include,链接时用g++且显式列出所有.o目标。

C++ 怎么写Makefile C++ Linux项目编译构建入门【构建】

Makefile 里怎么写 C++ 编译规则

默认 make 不认识 .cpp 文件,也不会自动调用 g++。必须显式声明编译器、后缀规则和依赖关系。

最简可用的规则是:

.CPP.o:     g++ -c $(CXXFLAGS) $< -o $@

其中 $ 是第一个依赖(如 main.cpp),$@ 是目标(如 main.o)。注意:.CPP.ognu make 的隐含规则写法,大小写敏感,.cpp.o 在旧版 make 中可能不生效。

  • 推荐用更明确的显式规则,避免隐含规则干扰:main.o: main.cpp + 缩进命令
  • CXXFLAGS 应包含 -std=c++17 -Wall -Wextra 等,别漏掉 -I 头文件路径
  • 不要写 .cpp.o: 后直接跟空格或 tab 混用——tab 字符必须严格,且只能是第一个字符

怎么组织多文件项目的依赖和链接

目标文件(.o)之间没有隐式依赖,main.o 用了 utils.h,但 make 不知道它该重编译——除非你告诉它。

立即学习C++免费学习笔记(深入)”;

手动写依赖容易出错,推荐用编译器自动生成:

%.d: %.cpp     g++ -MM $(CXXFLAGS) $< | sed 's,(.*).o[ :]*,1.o 1.d : ,g' > $@

再在 Makefile 开头用 -include 加载所有 .d 文件:

-include $(SRCS:.cpp=.d)
  • SRCS := main.cpp utils.cpp 这种变量定义要放在 -include 前面,否则 $(SRCS:.cpp=.d) 展开为空
  • 链接阶段用 g++ 而不是 gcc,否则 C++ 标准库(如 std::String)可能链接失败
  • 可执行文件目标要显式写依赖所有 .omyapp: main.o utils.o,不能只靠后缀规则

为什么改了头文件,make 却不重新编译

这是新手最常遇到的问题:修改了 utils.h,运行 make 却说“up to date”。根本原因是 Makefile 里没声明 .o 文件依赖对应头文件。

上面用 -MM 生成的 .d 文件内容类似:

main.o main.d: main.cpp utils.h

这样当 utils.h 时间戳更新,main.o 就会被判定为过期。

  • 如果不用自动生成,手写依赖必须完整列出所有 #include 的头文件,包括系统头(如 )通常不用列,但项目内头文件一个都不能少
  • sed 命令里的正则用于把 main.o: main.cpp utils.h 改成 main.o main.d: main.cpp utils.h,确保 .d 文件自身也被追踪
  • 第一次运行 make.d 文件不存在,-include 会静默忽略,所以首次构建不会失败

要不要用 CMake 或 Meson 替代 Makefile

纯手工写 Makefile 在中大型项目里很快失控:交叉编译、测试目标、安装规则、第三方库查找(pkg-config)、宏开关(-DDEBUG)都会让 Makefile 变得难读难维护。

如果你只是练手、写工具脚本、或嵌入式受限环境,一个 20 行以内的 Makefile 完全够用;但只要项目超过 3 个源文件、有子目录、需要跨平台,就该考虑 CMake。

  • CMakeLists.txt 里 add_executable(myapp main.cpp utils.cpp) 会自动处理依赖和链接,比手写 Makefile 稳定得多
  • Makefile 里写错一个 tab 或漏一个反斜杠,错误信息往往是 *** missing separator,而 CMake 报错通常指向具体行
  • 别为了“学 Makefile”硬扛——现代 C++ 项目几乎都用 CMake,Makefile 更多是理解构建原理的入口,不是日常主力

真正容易被忽略的是:哪怕只用 Makefile,也该把 CXXCXXFLAGSSRCSTARGET 提取成变量,而不是在命令里硬编码路径和参数。否则改一个编译选项就得全局搜索替换。

text=ZqhQzanResources