Makefile
Makefile 适合小型 C/C++ 项目,也适合学习构建规则。它的核心是:文件之间有依赖,依赖变了才重新执行命令。
核心规则
target: dependency
commandtarget:要生成的目标,可以是app、main.o,也可以是clean这种伪目标。dependency:目标依赖的文件。recipe:真正执行的命令,必须用 Tab 缩进。- 变量:把重复的编译器、参数、路径抽出来。
- 伪目标:不是文件名,只是命令入口,例如
clean、run。 - 增量构建:依赖文件时间更新后,才重新构建相关目标。
可直接用的小项目
目录结构:
hello-make/
├── Makefile
├── include/
│ └── hello.h
└── src/
├── hello.cpp
└── main.cppinclude/hello.h:
#pragma once
void say_hello();src/hello.cpp:
#include "hello.h"
#include <iostream>
void say_hello()
{
std::cout << "Hello Makefile" << std::endl;
}src/main.cpp:
#include "hello.h"
int main()
{
say_hello();
return 0;
}Makefile:
CXX := g++
CXXFLAGS := -std=c++17 -Wall -Wextra -Iinclude
LDFLAGS :=
BUILD_DIR := build
TARGET := $(BUILD_DIR)/hello
SRCS := src/main.cpp src/hello.cpp
OBJS := $(SRCS:src/%.cpp=$(BUILD_DIR)/%.o)
DEPS := $(OBJS:.o=.d)
.PHONY: all run clean
all: $(TARGET)
$(TARGET): $(OBJS)
$(CXX) $(OBJS) -o $@ $(LDFLAGS)
$(BUILD_DIR)/%.o: src/%.cpp
@if not exist "$(BUILD_DIR)" mkdir "$(BUILD_DIR)"
$(CXX) $(CXXFLAGS) -MMD -MP -c $< -o $@
run: $(TARGET)
$(TARGET)
clean:
@if exist "$(BUILD_DIR)" rmdir /s /q "$(BUILD_DIR)"
-include $(DEPS)Linux/macOS 把创建和删除目录两行换成:
@mkdir -p "$(BUILD_DIR)"
@rm -rf "$(BUILD_DIR)"核心命令自查表
| 命令 | 作用 |
|---|---|
make | 构建第一个目标,通常是 all |
make all | 构建完整项目 |
make run | 构建后运行 |
make clean | 删除构建产物 |
make -j4 | 4 线程并行构建 |
make -n | 只打印命令,不真正执行 |
make VERBOSE=1 | 常见项目中用于显示详细命令,是否有效取决于 Makefile |
常见错误
| 现象 | 原因 | 处理 |
|---|---|---|
missing separator | recipe 没用 Tab | 把命令前缩进改成 Tab |
| 改了头文件没重新编译 | 没追踪头文件依赖 | 使用 -MMD -MP 并 -include $(DEPS) |
clean 不执行 | 当前目录有同名文件 clean | 加 .PHONY: clean |
| 找不到头文件 | include path 没配 | 加 -Iinclude |
链接失败 undefined reference | 少了源文件或库 | 检查 SRCS、LDFLAGS |
注意事项
- Makefile 更适合规则明确的小项目;跨平台大型项目通常用 CMake 生成 Makefile。
- recipe 的命令就是 shell 命令,不同系统写法可能不同。
- Make 根据文件时间戳判断是否需要重建。