从Makefile内部调用另一个Makefile的规则 [英] rule to call another Makefile from inside a Makefile
问题描述
我有一个Makefile
,如果某些对象文件尚不存在,我可以从中运行另一个Makefile
的命令.
I have a Makefile
from which I run the command of another Makefile
if some objects file do not exist already.
规则如下:
$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/%.c $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
+$(MAKE) -C $(COMMONDIR)
其中,变量在同一Makefile
中的定义如下:
where the variables are defined as follows in the same Makefile
:
COMMONDIR := ../common
SOURCESCOMMON := $(wildcard $(COMMONDIR)/*.c)
OBJDIRCOMMON := $(COMMONDIR)/obj
OBJECTSCOMMON := $(patsubst $(COMMONDIR)/%.c,$(OBJDIRCOMMON)/%.o, $(SOURCESCOMMON))
DEPENDSCOMMON := $(patsubst $(COMMONDIR)/%.c,$(OBJDIRCOMMON)/%.d, $(SOURCESCOMMON))
此规则可以正常工作,但最终,该规则所需的唯一实际输入是其他Makefile
,所以我尝试了:
This rule works fine but at the end of the day the only real input required by the rule is the other Makefile
so I tried:
$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
+$(MAKE) -C $(COMMONDIR)
但这不起作用,为什么呢?
but this does not work, why is that?
为完整起见,这里是完整的Makefile
For completeness here is the complete Makefile
CC = gcc
INC_PATH = -I../common/
SOURCEDIR := ./
SOURCES := $(wildcard $(SOURCEDIR)/*.c)
OBJDIR :=./obj
OBJECTS := $(patsubst $(SOURCEDIR)/%.c,$(OBJDIR)/%.o, $(SOURCES))
DEPENDS := $(patsubst $(SOURCEDIR)/%.c,$(OBJDIR)/%.d, $(SOURCES))
COMMONDIR := ../common
SOURCESCOMMON := $(wildcard $(COMMONDIR)/*.c)
OBJDIRCOMMON := $(COMMONDIR)/obj
OBJECTSCOMMON := $(patsubst $(COMMONDIR)/%.c,$(OBJDIRCOMMON)/%.o, $(SOURCESCOMMON))
DEPENDSCOMMON := $(patsubst $(COMMONDIR)/%.c,$(OBJDIRCOMMON)/%.d, $(SOURCESCOMMON))
# ADD MORE WARNINGS!
WARNING := -Wall -Wextra
# OBJS_LOC is in current working directory,
EXECUTABLE := ../server
# .PHONY means these rules get executed even if
# files of those names exist.
.PHONY: all clean
# The first rule is the default, ie. "make",
# "make all" and "make parking" mean the same
all: $(EXECUTABLE)
clean:
$(RM) $(OBJECTS) $(DEPENDS) $(EXECUTABLE)
# Linking the executable from the object files
# $^ # "src.c src.h" (all prerequisites)
$(EXECUTABLE): $(OBJECTS) $(OBJECTSCOMMON)
$(CC) $(WARNING) $^ -o $@
-include $(DEPENDS) $(DEPENDSCOMMON)
$(OBJDIR):
mkdir -p $(OBJDIR)
$(OBJDIR)/%.o: $(SOURCEDIR)/%.c Makefile | $(OBJDIR)
$(CC) $(WARNING) -MMD -MP -c $(INC_PATH) $< -o $@
$(OBJDIRCOMMON):
mkdir -p $(OBJDIRCOMMON)
$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/%.c $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
+$(MAKE) -C $(COMMONDIR)
编辑
我得到的错误是这样的:
The error I get is like this:
Entering directory '/home/user/Documents/UnixSystem/network/common'
gcc -Wall -Wextra -MMD -MP -c utilities.c -o obj/utilities.o
gcc -Wall -Wextra -MMD -MP -c error.c -o obj/error.o
make[2]: Leaving directory '/home/user/Documents/UnixSystem/network/common'
gcc ../common/obj/error.d.o -o ../common/obj/error.d
gcc: error: ../common/obj/error.d.o: No such file or directory
gcc: fatal error: no input files
compilation terminated.
据我了解,另一个Makefile
的执行成功.但是,此后它正在尝试执行此命令gcc ../common/obj/error.d.o -o ../common/obj/error.d
,这是错误的,但是我不知道哪个规则以及为什么会生成它.
From which I understand the execution of the other Makefile
was successful. However after that it is trying to execute this command gcc ../common/obj/error.d.o -o ../common/obj/error.d
which is wrong but I do not know which rule and why it's generating it.
推荐答案
为什么您做错了
食谱 A :
$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/%.c $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
+$(MAKE) -C $(COMMONDIR)
和配方 B :
$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
+$(MAKE) -C $(COMMONDIR)
实质上具有不同的含义,并且肯定不会产生相同的行为.
have essentially different meanings and will certainly not yield the same behaviour.
食谱 A 说:
- 如果目标
$(OBJDIRCOMMON)/file.o
不存在,则必须将其最新 或早于$(COMMONDIR)/file.c
或$(COMMONDIR)/Makefile
. - 如果必须更新目标
$(OBJDIRCOMMON)/file.o
,则$(OBJDIRCOMMON)
必须首先更新. - 要使目标
$(OBJDIRCOMMON)/file.o
为最新,请在外壳中运行$(MAKE) -C $(COMMONDIR)
的扩展.
- Any target
$(OBJDIRCOMMON)/file.o
must be made up-to-date if does not exist or is older than either$(COMMONDIR)/file.c
or$(COMMONDIR)/Makefile
. - If a target
$(OBJDIRCOMMON)/file.o
must be made up-to-date, then$(OBJDIRCOMMON)
must be made up-to-date first. - To make a target
$(OBJDIRCOMMON)/file.o
up-to-date, run the expansion of$(MAKE) -C $(COMMONDIR)
in a shell.
食谱 B 说:
- 如果目标
$(OBJDIRCOMMON)/file.o
不存在,则必须将其最新 或早于$(COMMONDIR)/Makefile
. - 如果必须更新目标
$(OBJDIRCOMMON)/file.o
,则$(OBJDIRCOMMON)
必须首先更新. - 要使目标
$(OBJDIRCOMMON)/file.o
为最新,请在外壳中执行$(MAKE) -C $(COMMONDIR)
的扩展.
- Any target
$(OBJDIRCOMMON)/file.o
must be made up-to-date if does not exist or is older than$(COMMONDIR)/Makefile
. - If a target
$(OBJDIRCOMMON)/file.o
must be made up-to-date, then$(OBJDIRCOMMON)
must be made up-to-date first. - To make a target
$(OBJDIRCOMMON)/file.o
up-to-date, execute the expansion of$(MAKE) -C $(COMMONDIR)
in a shell.
请注意,标准 A.1 与标准 B.1 不同.配方 A 将执行
如果$(OBJDIRCOMMON)/file.o
早于$(OBJDIRCOMMON)/file.c
.食谱 B 不会.
配方 B 放弃了对象文件对相应源文件的依赖关系,
并告诉Make $(OBJDIRCOMMON)/file.o
仅在被重制时才可以重制
早于$(COMMONDIR)/Makefile
.
Notice that criterion A.1 is different from criterion B.1. Recipe A will execute
if $(OBJDIRCOMMON)/file.o
is older than $(OBJDIRCOMMON)/file.c
. Recipe B will not.
Recipe B discards the dependency of the object files on the corresponding source files,
and tells Make that $(OBJDIRCOMMON)/file.o
is only ever to be remade if it
is older than $(COMMONDIR)/Makefile
.
一天结束时,规则唯一需要的实际输入就是另一个Makefile
at the end of the day the only real input required by the rule is the other Makefile
您在这里所说的规则"实际上是($(MAKE) -C $(COMMONDIR)
的)命令行.
该命令的输入是一回事.执行它的标准是另一个.
What you mean by "the rule" here is actually the commandline (expanded from) $(MAKE) -C $(COMMONDIR)
.
The inputs of this command are one thing; the criteria for executing it are another.
您的操作如何导致看到的错误.
这更棘手.让我们重现它.
This is thornier. Let's reproduce it.
这是一个围栏:
$ ls -R
.:
app common
./app:
foo.c main.c Makefile
./common:
bar.c Makefile
在这里,./app/Makefile
就是您的配方为 A 的Makefile. ./common/Makefile
,
您没有发布的只是:
Here, ./app/Makefile
is exactly your Makefile with recipe A. ./common/Makefile
,
which you didn't post, is just:
obj/bar.o: bar.c
gcc -MMD -MP -c -I. $< -o $@
因为这将起到说明作用.
because that will do for illustration.
我们内置于./app
:
$ cd app
$ make
mkdir -p ./obj
gcc -Wall -Wextra -MMD -MP -c -I../common/ foo.c -o obj/foo.o
gcc -Wall -Wextra -MMD -MP -c -I../common/ main.c -o obj/main.o
mkdir -p ../common/obj
make -C ../common
make[1]: Entering directory '/home/imk/develop/so/make_prob/common'
gcc -MMD -MP -c -I. bar.c -o obj/bar.o
make[1]: Leaving directory '/home/imk/develop/so/make_prob/common'
gcc -Wall -Wextra obj/foo.o obj/main.o ../common/obj/bar.o -o ../server
很好.
现在,我像您一样更改./app/Makefile
,以使用配方 B 并进行重建.
Now I change ./app/Makefile
as you did, to use recipe B, and rebuild.
$ make
gcc -Wall -Wextra -MMD -MP -c -I../common/ foo.c -o obj/foo.o
gcc -Wall -Wextra -MMD -MP -c -I../common/ main.c -o obj/main.o
gcc -Wall -Wextra obj/foo.o obj/main.o ../common/obj/bar.o -o ../server
仍然可以...但是请稍等!那个没有调用./common
make
根本就是变更可能会影响的那个.更好的clean
:
Still fine... But wait a minute! That one didn't invoke the ./common
make
at all, which is the one that the change might affect. Better clean
:
$ make clean
rm -f ./obj/foo.o ./obj/main.o ./obj/foo.d ./obj/main.d ../server
,然后重试:
$ make
gcc -Wall -Wextra -MMD -MP -c -I../common/ foo.c -o obj/foo.o
gcc -Wall -Wextra -MMD -MP -c -I../common/ main.c -o obj/main.o
gcc -Wall -Wextra obj/foo.o obj/main.o ../common/obj/bar.o -o ../server
没有区别吗?嗯,这是因为此Makefile的clean
无法删除所有
make
构建的文件:省略了../common/obj/bar.o
.所以我就这样:
No difference? Ah, that's because this Makefile's clean
fails to delete all
the files that make
builds: it leaves out ../common/obj/bar.o
. So I'll just:
$ rm ../common/obj/*
再去一遍:
$ make
make -C ../common
make[1]: Entering directory '/home/imk/develop/so/make_prob/common'
gcc -MMD -MP -c -I. bar.c -o obj/bar.o
make[1]: Leaving directory '/home/imk/develop/so/make_prob/common'
gcc ../common/obj/bar.d.o -o ../common/obj/bar.d
gcc: error: ../common/obj/bar.d.o: No such file or directory
gcc: fatal error: no input files
compilation terminated.
这是你的奥秘.
当我压缩../common/obj
文件时,不仅删除了其中的所有 object 文件,
而且还有依赖文件../common/obj/bar.d
.现在,Make正在尝试通过运行以下命令来重新制作它:
When I zapped the ../common/obj
files, I deleted not only all the object files therein
but also the dependency file ../common/obj/bar.d
. And now Make is trying to remake it by running:
gcc ../common/obj/bar.d.o -o ../common/obj/bar.d
为什么?为了回答这个问题,我们首先将./app/Makefile
改回使用配方 A
-考虑完成-然后执行:
How come? To answer that, we'll first change ./app/Makefile
back to use recipe A
- consider it done - and then do:
$ make --print-data-base > out.txt
将out.txt
中所有收集到的信息转储到out.txt
中
生成文件(Makefile
以及它递归include
-s的所有生成文件,
在这种情况下,只是自动生成的.d
文件.
which dumps in out.txt
all the information that Make gleans from reading all
the makefiles (Makefile
and all the makefiles that it recursively include
-s,
in this case just the auto-generated .d
files).
让我们看看数据库对../common/obj/bar.d
的评价.它说:
Let's see what the database has to say about ../common/obj/bar.d
. It says:
# Not a target:
../common/obj/bar.d:
# Implicit rule search has been done.
# Last modified 2019-01-11 16:01:33.199263608
# File has been updated.
# Successfully updated.
肯定我们不想要 ../common/obj/bar.d
成为目标,并且不是一个
定位是因为,在读取了所有makefile并考虑了其所有内置规则之后,
以及它实际上可以找到的所有文件,Make无法以任何方式查看../common/obj/bar.d
必须对这些文件中的任何文件进行更新.好.
Certainly we don't want ../common/obj/bar.d
to be a target, and it isn't a
target because, having read all the makefiles, and considered all its builtin rules,
and all of the files it can actually find, Make can't see any way in which ../common/obj/bar.d
has to be made up-to-date with respect to any of those files. Good.
现在,让我们再次恢复./app/Makefile
中的配方 B -认为已完成-
并再次执行:
Now let's revert to recipe B in ./app/Makefile
again - consider it done -
and again do:
$ make --print-data-base > out.txt
,然后再次查看out.txt
中与../common/obj/bar.d
有关的内容.这次我们发现:
and again look in out.txt
concerning ../common/obj/bar.d
. This time we find:
../common/obj/bar.d: ../common/obj/bar.d.o
# Implicit rule search has been done.
# Implicit/static pattern stem: '../common/obj/bar.d'
# Last modified 2019-01-11 16:01:33.199263608
# File has been updated.
# Successfully updated.
# recipe to execute (built-in):
$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
因此这次../common/obj/bar.d
是一个目标!这取决于../common/obj/bar.d.o
!
制作方法如下:
So this time ../common/obj/bar.d
is a target! And it depends on ../common/obj/bar.d.o
!
And the recipe to make it is:
$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
它当然会扩展为:
gcc ../common/obj/bar.d.o -o ../common/obj/bar.d
借助食谱 B ,使Make如何解决该问题?
How was Make able to work that out, thanks to recipe B?
首先,它考虑了makefile文件中的任何规则还是任何
内置的规则使它可以直接从任何现有文件中制作../common/obj/bar.d
,
并画了一个空白.
Well first it considered whether any of the rules in the makefiles or any of the
builtin rules gave it direct way to make ../common/obj/bar.d
from any existing files,
and drew a blank.
下一步它考虑了其中任何规则是否为 从 中间文件 . 中间文件是不存在的文件,但其自身可以被 制成 从现有文件中读取的任何规则或内置规则中的任何内容.这 时间到了.
Next it considered whether any of those rules gave it a way to
make ../common/obj/bar.d
from an intermediate file. An intermediate file being a file that doesn't exist but itself can be made
from existing files, by any of the rules it has read or its builtin-rules. This
time it saw a way.
Make的内置模式规则之一是:
One of Make's builtin pattern rules is:
%: %.o
# recipe to execute (built-in):
$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
您可以在out.txt
中找到它.您会看到这是模式规则
它与以下项匹配:
You can find it in right there in out.txt
. And you can see this is the pattern rule that
it matches with:
../common/obj/bar.d: ../common/obj/bar.d.o
配方中有一个配方可以链接程序,名为../common/obj/bar.d
对象文件../common/obj/bar.d.o
.
The recipe there is a recipe to link a program called ../common/obj/bar.d
given
an object file ../common/obj/bar.d.o
.
没有目标文件../common/obj/bar.d.o
,但是它可以是中间文件吗?如果
Make可以找到从 do 存在的文件中制作../common/obj/bar.d.o
的规则,
那么它也可以使用%: %.o
规则制作../common/obj/bar.d
.
There is no object file ../common/obj/bar.d.o
, but can it be an intermediate file? If
Make can find a rule for making ../common/obj/bar.d.o
from files that do exist,
then it can also make ../common/obj/bar.d
with this %: %.o
rule.
它可以从现有文件中找到制作../common/obj/bar.d.o
的方法
因为我们只给了它一个! -食谱 B :
And it can find a recipe for making ../common/obj/bar.d.o
from existing files
because we just gave it one! - recipe B:
$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
+$(MAKE) -C $(COMMONDIR)
这告诉Make是否有任何目标匹配$(OBJDIRCOMMON)/%.o
(如../common/obj/bar.d.o
)
不存在,但$(COMMONDIR)/Makefile
确实存在(确实存在),则该目标
通过运行以下命令来更新:
That tells Make that if any target matching $(OBJDIRCOMMON)/%.o
(like ../common/obj/bar.d.o
)
does not exist, but $(COMMONDIR)/Makefile
does exist (which it does), then that target
is made up-to-date by running:
$(MAKE) -C $(COMMONDIR)
使我们相信.它运行了$(MAKE) -C $(COMMONDIR)
:
Make believed us. It ran $(MAKE) -C $(COMMONDIR)
:
make -C ../common
make[1]: Entering directory '/home/imk/develop/so/make_prob/common'
gcc -MMD -MP -c -I. bar.c -o obj/bar.o
make[1]: Leaving directory '/home/imk/develop/so/make_prob/common'
,然后将其视为最新的../common/obj/bar.d.o
.因此,它转到:
and then considered ../common/obj/bar.d.o
up-to-date. So it moved on to:
../common/obj/bar.d: ../common/obj/bar.d.o
$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
并运行:
gcc ../common/obj/bar.d.o -o ../common/obj/bar.d
由于我们撒谎而失败了:
which failed because we lied:
make -C ../common
根本不做../common/obj/bar.d.o
.
gcc: error: ../common/obj/bar.d.o: No such file or directory
这对于配方 A 不会发生,因为
This does not arise with recipe A, because
$(OBJDIRCOMMON)/bar.d.o: $(OBJDIRCOMMON)/bar.d.c $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
+$(MAKE) -C $(COMMONDIR)
不提供不提供提供一种方法来从现有文件中创建$(OBJDIRCOMMON)/bar.d.o
,
因为$(OBJDIRCOMMON)/bar.d.c
不存在.所以../common/obj/bar.d
不是
一个目标.
does not offer Make a way to make $(OBJDIRCOMMON)/bar.d.o
from existing files,
because $(OBJDIRCOMMON)/bar.d.c
does not exist. So ../common/obj/bar.d
is not
a target.
粘贴食谱 A ,因为它是正确的,食谱 B 是错误的.同时查看
并修复makefile,以便make clean
始终删除可能已经构建的所有非.PHONY
目标,而不会删除其他任何目标.最后,避免在配方未提及目标的情况下使用非.PHONY
目标编写配方.
Stick with recipe A, because it's right, and recipe B is wrong. Also review
and fix the makefiles so that make clean
always deletes all the non-.PHONY
targets that might have been built, and nothing else. Lastly avoid writing recipes with non-.PHONY
targets where the recipe does not mention the target.
这篇关于从Makefile内部调用另一个Makefile的规则的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!