# Introduction-to-Makefile **Repository Path**: changfengpro/Introduction-to-Makefile ## Basic Information - **Project Name**: Introduction-to-Makefile - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-02-07 - **Last Updated**: 2026-02-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # C++ 项目构建指南 (Makefile) 这份 Makefile 旨在自动化编译位于 `Src/` 目录下的 C++ 源代码,并将生成的对象文件与可执行文件存放在 `build/` 目录下。 ## 1. 快速入门 * **编译项目**:在终端输入 `make` 或 `make all`。 * **清理构建产物**:输入 `make clean`。 * **重新编译**:输入 `make rebuild`(默认开启 12 线程加速)。 * **查看调试信息**:输入 `make info` 查看当前的编译器参数、源文件列表和目标文件列表。 --- ## 2. Make 执行流程图解 当你在终端输入 `make` 时,程序的执行逻辑如下: 1. **解析变量**:读取 `TARGET`, `CXXFLAGS`, `SRC_DIR` 等定义的变量。 2. **搜索源文件**:通过 `wildcard` 扫描 `Src/` 下所有的 `.cpp` 文件。 3. **生成目标列表**:根据源文件列表,计算出对应的 `.o`(对象文件)路径。 4. **检查构建目录**:如果 `build/` 目录不存在,则创建它。 5. **增量编译**: * 检查每个 `.cpp` 是否比对应的 `.o` 文件新。 * 检查依赖文件 `.d`(头文件是否有变动)。 * 执行 `g++ -c` 生成对象文件。 6. **链接**:将所有 `.o` 文件组合,链接成最终的可执行文件 `build/coordin`。 --- ## 3. Makefile 逐行语法解析 ### 3.1 核心变量定义 ```makefile TARGET = coordin # 定义最终生成的可执行文件名 DEBUG = 1 # 调试开关,1开启,0关闭 OPT = -Og # 基础优化等级(-Og 对调试友好) BUILD_DIR = build # 编译产物存放路径 SRC_DIR = Src # 源代码存放路径 INC_DIR = Inc # 头文件存放路径 ``` ### 3.2 自动扫描与转换 ```makefile CXX_SOURCES = $(wildcard $(SRC_DIR)/*.cpp) ``` * **语法**:`$(wildcard pattern)` 是内置函数,用于匹配符合模式的文件名。这里它获取了 `Src` 下所有的 `.cpp` 文件。 ```makefile OBJECTS = $(CXX_SOURCES:$(SRC_DIR)/%.cpp=$(BUILD_DIR)/%.o) ``` * **语法**:这是**模式替换**。它将 `CXX_SOURCES` 变量中所有的 `Src/xxx.cpp` 替换为 `build/xxx.o`。 * 代码的执行过程如下: >它遍历 CXX_SOURCES中的每一个文件路径。\ 对于 **Src/main.cpp**:匹配模式 **Src/%.cpp**成功,**%**匹配到了 **main**。\ 将其替换为 build/%.o,即 build/main.o。\ 对于 `Src/utils/file1.cpp`:\ 匹配模式 Src/%.cpp失败,因为路径中包含了子目录 utils/。这个模式无法匹配到它,所以这个文件不会被转换,直接保留原值 Src/utils/file1.cpp。 如需处理子目录,可以使用**patsubst**函数 \ `OBJECTS = $(patsubst $(SRC_DIR)/%.cpp, $(BUILD_DIR)/%.o, $(CXX_SOURCES))` ### 3.3 编译选项 (CXXFLAGS) ```makefile CXXFLAGS = $(ARCH) -Wall -Wextra -std=c++17 CXXFLAGS += -MMD -MP ``` * `-Wall -Wextra`:开启几乎所有的警告,帮助发现潜在 Bug。 * `-std=c++17`:指定使用 C++17 标准。 * `-MMD -MP`:**关键语法**。告诉编译器生成 `.d` 依赖文件。这样当你修改了 `.h` 头文件时,Make 也能识别出哪些 `.cpp` 需要重新编译。 ### 3.4 核心规则 Makefile 的核心格式是: ``` 目标 (Target): 依赖 (Dependencies) 命令 (Command) ```` * **目标 (Target)**:你想得到什么?(通常是一个文件名,如 main.o 或 coordin.exe;也可以是一个动作名,如 clean)。 * **依赖 (Dependencies)**:你需要什么原材料?(通常是源代码 .cpp 或头文件 .h) * **命令 (Command)**:你怎么把原材料变成目标?(这是一条 Shell 命令,比如 g++ -c ...)。 * **注意**:命令前面必须是一个 Tab 制表符,不能是空格。 ```makefile $(BUILD_DIR)/%.o: %.cpp | $(BUILD_DIR) $(CXX) -c $(CXXFLAGS) $(CXX_INCLUDES) -o $@ $< ``` 上述规则最终转换为下述命令 ``` mkdir build g++ -c -Wall -Wextra -std=c++17 -g -O0 -MMD -MP -IInc -o build/file1.o Src/file1.cpp # 编译命令 g++ -c -Wall -Wextra -std=c++17 -g -O0 -MMD -MP -IInc -o build/file2.o Src/ file2.cpp # 编译命令 g++ build/file1.o build/file2.o -o build/coordin # 链接命令 ``` * `%.o: %.cpp`:模式规则,说明如何从 `.cpp` 生成 `.o`。 * `| $(BUILD_DIR)`:**Order-only 依赖**。确保 `build` 目录在编译前已创建,但目录的时间戳更新不会触发重新编译。 * `$@`:自动化变量,代表**目标文件**(即 `.o` 文件)。 * `$<`:自动化变量,代表**第一个依赖文件**(即 `.cpp` 文件)。 ### 3.5 链接规则 ```makefile $(BUILD_DIR)/$(TARGET): $(OBJECTS) $(CXX) $(OBJECTS) $(LDFLAGS) $(LIBS) -o $@ ``` * 将所有计算出的 `OBJECTS` 链接成最终的程序。 ### 3.6 依赖包含 ```makefile -include $(DEPS) ``` * **语法**:`include` 会将 `.d` 文件中的内容(头文件依赖关系)嵌入到 Makefile 中。前面的 `-` 表示如果文件不存在(第一次编译时)也不报错。 --- ## 4. 关键语法特性总结 | 语法 | 含义 | | --- | --- | | `:=` / `=` | 变量赋值(`:=` 是立即赋值,`=` 是延迟展开)。 | | `+=` | 追加字符串到变量末尾。 | | `.PHONY` | **伪目标**。防止目录下存在同名文件(如存在名为 `clean` 的文件)导致指令失效。 | | `@` (如 `@echo`) | 禁止在终端打印出该命令本身,只打印命令的输出结果。 | | `-` (如 `-rmdir`) | 忽略该命令执行时的错误,继续往下执行。 | --- ## 5. **附表**: ```makefile 通用模板 ################################################################################ # 通用 C++ Makefile 模板 ################################################################################ # ========================================== # 1. 用户配置区 (User Configuration) # ========================================== # 最终生成的可执行文件名 TARGET := # 构建目录与源码目录设置 BUILD_DIR := SRC_DIR := INC_DIR := # 编译器设置 CXX := g++ STD := -std=c++17 # 调试与优化 (0: 发布模式, 1: 调试模式) DEBUG := 1 # 额外链接库 (例如: -lpthread -lm) LIBS := LDFLAGS := # ========================================== # 2. 自动发现与路径处理 (Automatic Discovery) # ========================================== # 扫描 Src 目录下所有的 .cpp 文件 # 如果有多个源码目录,可以使用: $(shell find $(SRC_DIR) -name "*.cpp") [仅限Linux] SOURCES := $(wildcard $(SRC_DIR)/*.cpp) # 计算对象文件 (.o) 和依赖文件 (.d) 的路径 # 将 Src/main.cpp 映射为 build/main.o OBJECTS := $(SOURCES:$(SRC_DIR)/%.cpp=$(BUILD_DIR)/%.o) DEPS := $(OBJECTS:.o=.d) # 包含路径处理 (-IInc) INC_FLAGS := $(addprefix -I,$(INC_DIR)) # ========================================== # 3. 编译选项 (Flag Definitions) # ========================================== # 基础编译参数 CXXFLAGS := $(STD) -Wall -Wextra $(INC_FLAGS) # 自动生成依赖文件 (.d) 的关键参数 CXXFLAGS += -MMD -MP # 根据 DEBUG 变量调整选项 ifeq ($(DEBUG), 1) CXXFLAGS += -g -O0 else CXXFLAGS += -O2 endif # ========================================== # 4. 构建规则 (Build Rules) # ========================================== # 默认目标 all: $(BUILD_DIR)/$(TARGET) # 链接阶段:将所有 .o 链接成最终程序 $(BUILD_DIR)/$(TARGET): $(OBJECTS) @echo [LINK] $@ @$(CXX) $(OBJECTS) $(LDFLAGS) $(LIBS) -o $@ # 编译阶段:将 .cpp 编译为 .o # | $(BUILD_DIR) 确保目录先被创建 $(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp | $(BUILD_DIR) @echo [CXX] $< @$(CXX) -c $(CXXFLAGS) -o $@ $< # 创建构建目录 $(BUILD_DIR): @mkdir $@ # ========================================== # 5. 辅助功能 (Utility Targets) # ========================================== .PHONY: all clean info rebuild # 清理构建产物 (兼容 Windows/Linux) clean: ifeq ($(OS),Windows_NT) @if exist $(BUILD_DIR) rd /s /q $(BUILD_DIR) else @rm -rf $(BUILD_DIR) endif # 重新构建 rebuild: clean all # 查看项目配置信息 info: ifeq ($(OS),Windows_NT) @chcp 65001 >nul endif @echo ========================================== @echo Project: $(TARGET) @echo Sources: $(SOURCES) @echo Objects: $(OBJECTS) @echo CXXFLAGS: $(CXXFLAGS) @echo ========================================== # 引入生成的依赖文件,处理头文件更新 -include $(DEPS) ```