`make` 什么时候应用隐式规则 `Makefile: Makefile.o`?
When does `make` apply the implicit rule `Makefile: Makefile.o`?
在我的工作目录中,有一个 makefile
、一个空的 obj
文件夹和一个包含 foo.c
的 src
文件夹。我有一个简单的规则,它接受一个目标文件,用 src 替换第一个斜杠之前的所有内容,并用 替换结尾的 .o .c.
示例: obj/foo.o --> src/foo.c
在 MadScientist 的帮助下(感谢他)我获得了以下模式规则(请不要提出更简单的规则 --> 我知道它们的存在,但仅这条规则说明了我的问题):
OBJFILES=obj/foo.o
all: $(OBJFILES)
@echo "all executed"
Y = $(shell echo | sed -e 's,^[^\/]*,src,' -e 's,\.o$$,.c,')
.SECONDEXPANSION:
%.o: $$(call Y,$$@)
@echo "OBJECTS executed for $@ - [$^]"
src/foo.c:
@echo "Congrats"
一旦我运行上面的makefile,我得到一个循环依赖:
xxxx@null:~/Desktop/experiment$ make
make: Circular src.o <- src dependency dropped.
OBJECTS executed for obj/foo.o - [src/foo.c]
all executed
很快就找到了原因:Make
创建了隐式规则Makefile: Makefile.o
,它触发了我的模式规则。由于 Makefile.o
不包含斜杠,因此 sed
的计算结果为 src
。 Make
再次应用最终导致循环依赖的隐式规则 src:src.o
。这可以在 make 输出中看到:
make --print-data-base | grep src
make: Circular src.o <- src dependency dropped.
OBJECTS executed for obj/foo.o - [src/foo.c]
Y = $(shell echo | sed -e 's,^[^\/]*,src,' -e 's,\.o$$,.c,')
# src (device 64769, inode 14031369): No files, no impossibilities so far.
Makefile.o: src
src: src.o
# Implicit/static pattern stem: 'src'
src.o:
# Implicit/static pattern stem: 'src'
# @ := src.o
# * := src
# < := src.o
obj/foo.o: src/foo.c
# + := src/foo.c
# < := src/foo.c
# ^ := src/foo.c
# ? := src/foo.c
src/foo.c:
如果我们现在以这种方式重新定义 Y
:Y = $(patsubst obj/%.o,src/%.c,)
,则不会发生循环依赖,因为 make
甚至不会尝试应用隐式规则 Makefile:Makefile.o
make --print-data-base | grep src
OBJECTS executed for obj/foo.o - [src/foo.c]
Y = $(patsubst obj/%.o,src/%.c,)
# src (device 64769, inode 14031369): No files, no impossibilities so far.
obj/foo.o: src/foo.c
# + := src/foo.c
# < := src/foo.c
# ^ := src/foo.c
# ? := src/foo.c
src/foo.c:
make
何时创建隐式规则 Makefile: Makefile.o
? Y
的两种不同定义有什么区别?
首先,如果您要使用二次展开,您有责任确保结果正确。在模式规则中,这意味着您的扩展必须处理 %
的所有不同值。在这里,您的问题基本上是您没有正确处理 %
不包含斜杠的值,但是您的目标 %.o
将应用于所有目标,而不仅仅是子目录中的目标。您需要修复 sed
调用。一种选择是:
Y = $(shell echo | sed -e 's,^[^\/]*/,src/,' -e 's,\.o$$,.c,')
通过添加斜杠,替换不会匹配简单的文件名。 .o
替换为 .c
仍然会发生。
您看到此行为的原因是 tries to remake makefiles。所以它想建立一个目标Makefile
。有一个内置的模式规则知道如何从同名目标文件构建可执行文件:% : %.o
Makefile
匹配 %
所以 make 尝试构建 Makefile.o
.它会看到您的模式规则 %.o
并尝试应用它,并且在第二次扩展中它将 Makefile.o
转换为 src
如上所述。
因此,除了上述之外,您还可以考虑其他事项:
如果您有足够新的 make 版本(并且您定义了所有自己的规则),您可以通过添加禁用所有内置规则:
MAKEFLAGS += -r
无论如何,这是一个好主意,因为它允许通过删除所有内置规则使 运行 更快。
其次,您可以将模式更改为更具体,以便它只匹配目标文件,如下所示:
obj/%.o: $$(call Y,$$@)
...
现在,此模式规则将仅匹配该子目录中的.o
个文件。
在我的工作目录中,有一个 makefile
、一个空的 obj
文件夹和一个包含 foo.c
的 src
文件夹。我有一个简单的规则,它接受一个目标文件,用 src 替换第一个斜杠之前的所有内容,并用 替换结尾的 .o .c.
示例: obj/foo.o --> src/foo.c
在 MadScientist 的帮助下(感谢他)我获得了以下模式规则(请不要提出更简单的规则 --> 我知道它们的存在,但仅这条规则说明了我的问题):
OBJFILES=obj/foo.o
all: $(OBJFILES)
@echo "all executed"
Y = $(shell echo | sed -e 's,^[^\/]*,src,' -e 's,\.o$$,.c,')
.SECONDEXPANSION:
%.o: $$(call Y,$$@)
@echo "OBJECTS executed for $@ - [$^]"
src/foo.c:
@echo "Congrats"
一旦我运行上面的makefile,我得到一个循环依赖:
xxxx@null:~/Desktop/experiment$ make
make: Circular src.o <- src dependency dropped.
OBJECTS executed for obj/foo.o - [src/foo.c]
all executed
很快就找到了原因:Make
创建了隐式规则Makefile: Makefile.o
,它触发了我的模式规则。由于 Makefile.o
不包含斜杠,因此 sed
的计算结果为 src
。 Make
再次应用最终导致循环依赖的隐式规则 src:src.o
。这可以在 make 输出中看到:
make --print-data-base | grep src
make: Circular src.o <- src dependency dropped.
OBJECTS executed for obj/foo.o - [src/foo.c]
Y = $(shell echo | sed -e 's,^[^\/]*,src,' -e 's,\.o$$,.c,')
# src (device 64769, inode 14031369): No files, no impossibilities so far.
Makefile.o: src
src: src.o
# Implicit/static pattern stem: 'src'
src.o:
# Implicit/static pattern stem: 'src'
# @ := src.o
# * := src
# < := src.o
obj/foo.o: src/foo.c
# + := src/foo.c
# < := src/foo.c
# ^ := src/foo.c
# ? := src/foo.c
src/foo.c:
如果我们现在以这种方式重新定义 Y
:Y = $(patsubst obj/%.o,src/%.c,)
,则不会发生循环依赖,因为 make
甚至不会尝试应用隐式规则 Makefile:Makefile.o
make --print-data-base | grep src
OBJECTS executed for obj/foo.o - [src/foo.c]
Y = $(patsubst obj/%.o,src/%.c,)
# src (device 64769, inode 14031369): No files, no impossibilities so far.
obj/foo.o: src/foo.c
# + := src/foo.c
# < := src/foo.c
# ^ := src/foo.c
# ? := src/foo.c
src/foo.c:
make
何时创建隐式规则 Makefile: Makefile.o
? Y
的两种不同定义有什么区别?
首先,如果您要使用二次展开,您有责任确保结果正确。在模式规则中,这意味着您的扩展必须处理 %
的所有不同值。在这里,您的问题基本上是您没有正确处理 %
不包含斜杠的值,但是您的目标 %.o
将应用于所有目标,而不仅仅是子目录中的目标。您需要修复 sed
调用。一种选择是:
Y = $(shell echo | sed -e 's,^[^\/]*/,src/,' -e 's,\.o$$,.c,')
通过添加斜杠,替换不会匹配简单的文件名。 .o
替换为 .c
仍然会发生。
您看到此行为的原因是 tries to remake makefiles。所以它想建立一个目标Makefile
。有一个内置的模式规则知道如何从同名目标文件构建可执行文件:% : %.o
Makefile
匹配 %
所以 make 尝试构建 Makefile.o
.它会看到您的模式规则 %.o
并尝试应用它,并且在第二次扩展中它将 Makefile.o
转换为 src
如上所述。
因此,除了上述之外,您还可以考虑其他事项:
如果您有足够新的 make 版本(并且您定义了所有自己的规则),您可以通过添加禁用所有内置规则:
MAKEFLAGS += -r
无论如何,这是一个好主意,因为它允许通过删除所有内置规则使 运行 更快。
其次,您可以将模式更改为更具体,以便它只匹配目标文件,如下所示:
obj/%.o: $$(call Y,$$@)
...
现在,此模式规则将仅匹配该子目录中的.o
个文件。