makefile 分析 -- 内置变量及自动变量
2026/6/11 9:23:50 网站建设 项目流程

makefile 分析1

-p 选项,可以打印出make过程中的数据库, 下面研究一下内置的变量和规则。
-n 选项, 只运行,不执行,
-d 选项,相当于--debug=a, b(basic),v(verbose),i(implicity),j(innvocation of command),m(remake files)

这里着重解释一下 -p
make -p -f /dev/null 可以打印出内置变量和内置规则
变量可以分为4类,
第一类: 环境变量, 比较重要的是PATH, PWD 就不一一列举了。
第二类: 内置变量或叫默认变量(default), 比较重要的是cc, CXX, .INCLUDE_DIRS, .DEFAULT_GOAL等

为完整起见,贴出我机器上的内置变量。大可不必死记硬背,掌握重要的,领会其含义即可。

[hjj@hjj ~]$ cat 2.txt .FEATURES := target-specific order-only second-expansion else-if archives jobserver check-symlink .INCLUDE_DIRS = /usr/include /usr/local/include /usr/include .LIBPATTERNS = lib%.so lib%.a .VARIABLES := AR = ar ARFLAGS = rv AS = as CC = cc CHECKOUT,v = +$(if $(wildcard $@),,$(CO) $(COFLAGS) $< $@) CO = co COFLAGS = COMPILE.C = $(COMPILE.cc) COMPILE.F = $(FC) $(FFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c COMPILE.S = $(CC) $(ASFLAGS) $(CPPFLAGS) $(TARGET_MACH) -c COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c COMPILE.cc = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c COMPILE.cpp = $(COMPILE.cc) COMPILE.def = $(M2C) $(M2FLAGS) $(DEFFLAGS) $(TARGET_ARCH) COMPILE.f = $(FC) $(FFLAGS) $(TARGET_ARCH) -c COMPILE.mod = $(M2C) $(M2FLAGS) $(MODFLAGS) $(TARGET_ARCH) COMPILE.p = $(PC) $(PFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c COMPILE.r = $(FC) $(FFLAGS) $(RFLAGS) $(TARGET_ARCH) -c COMPILE.s = $(AS) $(ASFLAGS) $(TARGET_MACH) CPP = $(CC) -E CTANGLE = ctangle CWEAVE = cweave CXX = g++ F77 = $(FC) F77FLAGS = $(FFLAGS) FC = f77 GET = get LD = ld LEX = lex LEX.l = $(LEX) $(LFLAGS) -t LINK.C = $(LINK.cc) LINK.F = $(FC) $(FFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) LINK.S = $(CC) $(ASFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_MACH) LINK.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) LINK.cc = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) LINK.cpp = $(LINK.cc) LINK.f = $(FC) $(FFLAGS) $(LDFLAGS) $(TARGET_ARCH) LINK.o = $(CC) $(LDFLAGS) $(TARGET_ARCH) LINK.p = $(PC) $(PFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) LINK.r = $(FC) $(FFLAGS) $(RFLAGS) $(LDFLAGS) $(TARGET_ARCH) LINK.s = $(CC) $(ASFLAGS) $(LDFLAGS) $(TARGET_MACH) LINT = lint LINT.c = $(LINT) $(LINTFLAGS) $(CPPFLAGS) $(TARGET_ARCH) M2C = m2c MAKE = $(MAKE_COMMAND) MAKEFILES := MAKEINFO = makeinfo MAKE_COMMAND := make MAKE_VERSION := 3.81 OUTPUT_OPTION = -o $@ PC = pc PREPROCESS.F = $(FC) $(FFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -F PREPROCESS.S = $(CC) -E $(CPPFLAGS) PREPROCESS.r = $(FC) $(FFLAGS) $(RFLAGS) $(TARGET_ARCH) -F RM = rm -f SUFFIXES := .out .a .ln .o .c .cc .C .cpp .p .f .F .r .y .l .s .S .mod .sym .def .h .info .dvi .tex .texinfo .texi .txinfo .w .ch .web .sh .elc .el TANGLE = tangle TEX = tex TEXI2DVI = texi2dvi WEAVE = weave YACC = yacc YACC.y = $(YACC) $(YFLAGS)

第三类: 自动变量

我的机器是这样的。

[hjj@hjj ~]$ cat 3.txt %D = $(patsubst %/,%,$(dir $%)) *D = $(patsubst %/,%,$(dir $*)) +D = $(patsubst %/,%,$(dir $+)) ?D = $(patsubst %/,%,$(dir $?)) @D = $(patsubst %/,%,$(dir $@)) ^D = $(patsubst %/,%,$(dir $^)) %F = $(notdir $%) *F = $(notdir $*) +F = $(notdir $+) <F = $(notdir $<) ?F = $(notdir $?) @F = $(notdir $@) ^F = $(notdir $^) 文件(4个) $@--目标文件, $<--第一个依赖文件。 $*--代表"茎",例如: dir/a.foo.o:bar.c echo $* 此处的$* 将会是dir/a.foo, 去掉已知后缀部分 $%--当规则的目标文件是一个静态库文件时,代表静态库的一个成员名 文件列表(3个) $^--所有的依赖文件, $?--所有比目标文件更新的依赖文件列表 $+--类似“$^”,但是它保留了依赖文件中重复出现的文件 $(@D) -- 目标的目录部分,文件名部分 $(@F) $(*D) -- 代表"茎"的目录部分,文件名部分 $(*F) $(<D) -- 第一个依赖文件目录部分,文件名部分 $(<F) $(?D) -- 被更新的依赖文件目录部分,文件名部分 $(?F) $(^D) -- 所有依赖文件目录部分,文件名部分 $(^F) $(%D) -- 库文件成员目录部分,文件名部分 $(%F) $(+D) -- 所有依赖的目录部分,文件名部分(可存在重复文件) $(+F)

\$@ 当前完整目标名 test.o 通用,所有规则都能用,指定输出文件
\$< 规则第一个依赖文件 test.c 单依赖 / 模式编译首选
\$^ 全部依赖文件(自动去重) test.c 链接阶段,汇总所有目标文件
\$+ 全部依赖文件(保留重复项) test.c 特殊链接脚本、需保留依赖顺序场景
\$? 比目标更新的依赖文件 test.c(文件修改后生效) 增量编译,只处理改动文件
\$% 静态库(.a)内部成员名 普通文件规则中无效 仅用于 libxxx.a(member.o) 归档规则
\$* 模式规则中 % 匹配的主干;普通规则去后缀文件名 test 批量后缀转换


给一个简单的测试例. main.c func.c test.c 可以给简单的空文件或壳文件

$ cat Makefile # 七个 Make 自动变量综合测试文件 # 执行方式: # 1. make all 普通规则测试 # 2. make libtest.a $* 测试 (模式匹配测试) # 3. make test-lib 测试静态库 $% 变量(专用,也很少用) # 4. make clean 清理产物 # 伪目标 .PHONY: all clean # ====================== 1. 普通规则:测试 $@ $< $^ $+ $? ====================== all: main.out # 故意写重复依赖,区分 $^(去重) 和 $+(不去重) main.out: main.c func.c main.c @echo "========== 普通规则变量测试 ==========" @echo "1. \$$@ 当前目标: $@" @echo "2. \$$< 第一个依赖: $<" @echo "3. \$$^ 所有依赖(去重): $^" @echo "4. \$$+ 所有依赖(保留重复): $+" @echo "5. \$$? 被更新的依赖: $?" @echo "" gcc $^ -o $@ # ====================== 2. 模式规则:测试 $* ====================== %.o: %.c @echo "========== 模式规则 \$$* 测试 ==========" @echo "目标文件(\$$@): $@" @echo "文件主干(\$$*): $*" @echo "依赖文件(\$$<): $<" @echo "" gcc -c $< -o $@ libtest.a: test.o ar rcs $@ $^ # ====================== 3. 静态库规则:测试 $% (仅归档库生效) ====================== # 生成静态库 libtest.a,专门测试 $% # 库成员规则,$% 代表库内成员名 .PHONY: test-lib test-lib: libtest.a(test.o) libtest.a(test.o): test.o @echo "========== 静态库 \$$% 测试 ==========" @echo "完整归档目标(\$$@): $@" @echo "库内成员名(\$$%): $%" @echo "依赖文件(\$$<): $<" @echo "" # 清理所有编译产物 clean: rm -f *.o main.out libtest.a

第四类: makefile 中定义的变量.
例如:
cc 是 /usr/bin/cc -> /usr/bin/gcc
CXX 是 g++
CURDIR 是你当前的工作目录.

.DEFAULT_GOAL 默认的维护目标,也是在makefile 中定义的变量

查找makefile 维护的目标
.DEFAULT_GOAL 默认的维护的目标(命令行未指定目标)
MAKECMDGOALS 命令行指定的维护目标。


补充make 的其它几个有用的选项.

make -r 取消内置的隐含规则
make -d 输出调试信息, 我们看到有大量的信息输出,其中就有查找是否需要更新Makefile 自身的
make -r -d 调试信息一下就少多了,因为不包括那些隐含规则后, 就不用考虑是否生成Makefile 了.

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询