You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Makefile隐式规则引发链接错误,自定义规则未生效问题咨询

Makefile链接错误分析与解决

问题背景

编写的Makefile

SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
EXES = $(filter-out tap,$(basename $(SRCS)))

all: $(EXES)

%: %.o tap.o
        gcc -o $@ $^

%.o: %.c
        gcc -c $<

tap.o: tap.c tap.h
        gcc -c $<

clean:
        rm -f $(OBJS)

fclean: clean
        rm -f $(EXES)

当前文件结构

.
├── isalnum.c
├── isalpha.c
├── isascii.c
├── isdigit.c
├── isprint.c
├── Makefile
├── tap.c
└── tap.h

执行make时的错误信息

cc     isalnum.c   -o isalnum
/usr/bin/ld: /tmp/ccnuuKzZ.o: in function `main':
isalnum.c:(.text+0x14): undefined reference to `tap_plan'
/usr/bin/ld: isalnum.c:(.text+0x4a): undefined reference to `ok_at_loc'
/usr/bin/ld: isalnum.c:(.text+0x80): undefined reference to `ok_at_loc'
/usr/bin/ld: isalnum.c:(.text+0xb8): undefined reference to `ok_at_loc'
/usr/bin/ld: isalnum.c:(.text+0xf0): undefined reference to `ok_at_loc'
/usr/bin/ld: isalnum.c:(.text+0x128): undefined reference to `ok_at_loc'
/usr/bin/ld: /tmp/ccnuuKzZ.o:isalnum.c:(.text+0x160): more undefined references to `ok_at_loc' follow
/usr/bin/ld: /tmp/ccnuuKzZ.o: in function `main':
isalnum.c:(.text+0x3d9): undefined reference to `exit_status'
collect2: error: ld returned 1 exit status
make: *** [&lt;builtin&gt;: isalnum] Error 1

使用make -r后的正常执行日志

gcc -c tap.c
gcc -c isalnum.c
gcc -o isalnum isalnum.o tap.o
gcc -c isalpha.c
gcc -o isalpha isalpha.o tap.o
gcc -c isascii.c
gcc -o isascii isascii.o tap.o
gcc -c isdigit.c
gcc -o isdigit isdigit.o tap.o
gcc -c isprint.c
gcc -o isprint isprint.o tap.o
rm isascii.o isprint.o isalnum.o isdigit.o isalpha.o

用户疑问:自定义目标构建规则被绕过,执行的cc命令与Makefile定义不符,tap.c包含缺失符号,make -r正常,请问原因是什么?工作流程有哪些问题?

原因分析

问题核心是GNU Make的内置规则优先级高于自定义模式规则:

  • GNU Make默认自带一系列内置规则,其中包含%: %.c规则——直接将单个.c文件编译链接为同名可执行文件,命令为cc $< -o $@。
  • 你编写的%: %.o tap.o规则属于模式规则,逻辑是先编译.c为.o,再链接.o和tap.o。但内置规则的匹配更直接(一步从.c生成可执行),优先级更高,因此Make会优先选择内置规则,跳过你定义的需要链接tap.o的规则,导致测试程序未链接tap.c生成的目标文件,出现符号未定义错误。
  • 使用make -r参数时,会禁用所有内置规则,此时Make只能使用你定义的规则,因此会正确链接tap.o,执行正常。

工作流程的问题

  1. 对Makefile规则优先级理解不足:未考虑到内置规则的存在和更高优先级,导致自定义规则被覆盖。
  2. 原Makefile中SRCS包含了tap.c,虽然EXES排除了tap,但规则优先级问题导致链接步骤未按预期执行。

修正方法

方法1:明确指定目标依赖(推荐)

修改Makefile,让每个测试可执行文件的规则更明确,确保覆盖内置规则:

# 排除tap.c,仅保留测试主文件
TEST_SRCS = $(filter-out tap.c, $(wildcard *.c))
EXES = $(basename $(TEST_SRCS))
# 所有目标文件包括测试.o和tap.o
OBJS = $(TEST_SRCS:.c=.o) tap.o

all: $(EXES)

# 明确绑定每个测试可执行文件到对应的.o和tap.o
$(EXES): %: %.o tap.o
	gcc -o $@ $^

%.o: %.c
	gcc -c $<

tap.o: tap.c tap.h
	gcc -c $<

clean:
	rm -f $(OBJS)

fclean: clean
	rm -f $(EXES)

方法2:禁用内置规则(不推荐,除非必要)

在Makefile开头添加MAKEFLAGS += -r,全局禁用内置规则,强制Make使用你定义的所有规则:

MAKEFLAGS += -r

SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
EXES = $(filter-out tap,$(basename $(SRCS)))

all: $(EXES)

%: %.o tap.o
	gcc -o $@ $^

%.o: %.c
	gcc -c $<

tap.o: tap.c tap.h
	gcc -c $<

clean:
	rm -f $(OBJS)

fclean: clean
	rm -f $(EXES)

方法3:修改内置链接变量

通过修改Make的内置链接变量,让所有链接操作自动包含tap.o:

SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
EXES = $(filter-out tap,$(basename $(SRCS)))
# 让链接时自动加入tap.o
LDLIBS += tap.o

all: $(EXES)

%.o: %.c
	gcc -c $<

tap.o: tap.c tap.h
	gcc -c $<

clean:
	rm -f $(OBJS)

fclean: clean
	rm -f $(EXES)

内容的提问来源于stack exchange,提问作者ismbks

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 04:32:52