努力理解Makefile隐式规则

问题描述 投票:0回答:1

这是我的 Makefile,目标只是编译一堆测试文件并将它们与另一个名为

tap.c
的文件链接,其中包含我的测试函数。

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

1 directory, 8 files

问题是当我尝试运行

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: *** [<builtin>: isalnum] Error 1

我的

tap.c
文件包含所有这些符号,并且构建目标的规则似乎被绕过,因为执行的
cc
命令与我在 Makefile 中定义的规则不同。

果然,经过一番研究,我发现当我使用

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

这里发生了什么?请告诉我我的工作流程出了什么问题,我肯定遗漏了一些关于 Makefile、链接和编译的重要内容。我希望我的初始 Makefile 能够毫无问题地工作。

c unit-testing makefile compilation linker
1个回答
0
投票

GNU Make 有许多内置规则。这些规则之一知道如何直接从源文件构建可执行文件。

很常见的是,有许多不同的隐式规则可用于创建一个目标:例如,可以从 C 文件、C++ 文件等编译

%.o
。因此,GNU Make 也有一套规则来决定检查和应用隐式规则的顺序。

其中一条规则规定,如果有两种方法链接规则来设定目标,则较短的链获胜。因此,如果 make 可以使用

foo.c
->
foo.o
->
foo
构建程序,并且也可以使用
foo.c
->
foo
构建程序,那么将使用后一个链。

这就是您在这里看到的:make 使用内置的隐式规则,而不是隐式规则提供的较长规则集。

你有很多选择。最简单的一种是禁用内置的隐式规则;如果您有 GNU Make 4.0 或更高版本,这将起作用:

MAKEFLAGS += -r

如果你不这样做,这也可以工作,尽管它不是那么好:

.SUFFIXES:

(您可以在 GNU Make 手册中找到有关

.SUFFIXES
及其作用的信息)。

但是,一般来说,向模式规则添加“额外”先决条件是一个坏主意,如下所示:

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

模式规则是一个模板而不是承诺。因此,如果由于某种原因未选择该规则,则先决条件

tap.o
将不适用。

更好的是明确添加先决条件,如下所示:

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

$(EXES): tap.o

现在您知道那些可执行文件都必须有

tap.o

另一种选择是使用静态模式规则。一般来说,“匹配任何内容”规则(目标为

%
的模式规则)对 makefile 的性能不利。因此,最好声明一个静态模式规则,如下所示:

$(EXES): %: %.o tap.o
         gcc -o $@ $^

虽然名称令人困惑,但静态模式规则根本不是隐式规则:这是为

$(EXES)
中的每个目标创建显式规则的简写。

© www.soinside.com 2019 - 2024. All rights reserved.