从Makefile内部调用另一个Makefile的规则 [英] rule to call another Makefile from inside a Makefile

查看:184
本文介绍了从Makefile内部调用另一个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 说:

  1. 如果目标$(OBJDIRCOMMON)/file.o不存在,则必须将其最新 或早于$(COMMONDIR)/file.c$(COMMONDIR)/Makefile.
  2. 如果必须更新目标$(OBJDIRCOMMON)/file.o,则$(OBJDIRCOMMON) 必须首先更新.
  3. 要使目标$(OBJDIRCOMMON)/file.o为最新,请在外壳中运行$(MAKE) -C $(COMMONDIR)的扩展.
  1. 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.
  2. If a target $(OBJDIRCOMMON)/file.o must be made up-to-date, then $(OBJDIRCOMMON) must be made up-to-date first.
  3. To make a target $(OBJDIRCOMMON)/file.o up-to-date, run the expansion of $(MAKE) -C $(COMMONDIR) in a shell.

食谱 B 说:

  1. 如果目标$(OBJDIRCOMMON)/file.o不存在,则必须将其最新 或早于$(COMMONDIR)/Makefile.
  2. 如果必须更新目标$(OBJDIRCOMMON)/file.o,则$(OBJDIRCOMMON) 必须首先更新.
  3. 要使目标$(OBJDIRCOMMON)/file.o为最新,请在外壳中执行$(MAKE) -C $(COMMONDIR)的扩展.
  1. Any target $(OBJDIRCOMMON)/file.o must be made up-to-date if does not exist or is older than $(COMMONDIR)/Makefile.
  2. If a target $(OBJDIRCOMMON)/file.o must be made up-to-date, then $(OBJDIRCOMMON) must be made up-to-date first.
  3. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆