我有一个Makefile
,如果某些对象文件尚不存在,我将从中运行另一个Makefile
的命令。
规则如下:
$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/%.c $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
+$(MAKE) -C $(COMMONDIR)
其中的变量在同一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
,所以我尝试了:
$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
+$(MAKE) -C $(COMMONDIR)
但这不起作用,为什么呢?
为完整起见,这里是完整的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)
编辑
我得到的错误是这样的:
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
,这是错误的,但是我不知道哪个规则以及为什么会生成它。
答案 0 :(得分:2)
为什么您做错了
食谱 A :
$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/%.c $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
+$(MAKE) -C $(COMMONDIR)
和配方 B :
$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
+$(MAKE) -C $(COMMONDIR)
实质上具有不同的含义,并且肯定不会产生相同的行为。
食谱 A 说:
$(OBJDIRCOMMON)/file.o
不存在,则必须将其最新
或早于$(COMMONDIR)/file.c
或$(COMMONDIR)/Makefile
。$(OBJDIRCOMMON)/file.o
必须是最新的,则$(OBJDIRCOMMON)
必须首先更新。$(OBJDIRCOMMON)/file.o
为最新版本,请在shell中运行$(MAKE) -C $(COMMONDIR)
的扩展。食谱 B 说:
$(OBJDIRCOMMON)/file.o
不存在,则必须将其最新
或早于$(COMMONDIR)/Makefile
。$(OBJDIRCOMMON)/file.o
必须是最新的,则$(OBJDIRCOMMON)
必须首先更新。$(OBJDIRCOMMON)/file.o
为最新版本,请在shell中执行$(MAKE) -C $(COMMONDIR)
的扩展。请注意,标准 A.1 与标准 B.1 不同。配方 A 将执行
如果$(OBJDIRCOMMON)/file.o
早于$(OBJDIRCOMMON)/file.c
。配方 B 不会。
配方 B 放弃了对象文件对相应源文件的依赖关系,
并告诉Make $(OBJDIRCOMMON)/file.o
仅在被重造时
早于$(COMMONDIR)/Makefile
。
一天结束时,规则唯一需要的实际输入就是另一个Makefile
您在这里所说的“规则”实际上是$(MAKE) -C $(COMMONDIR)
的命令行。
该命令的输入是一回事。执行它的标准是另一个。
您的操作如何导致看到的错误。
这更棘手。让我们重现它。
这是一个围栏:
$ ls -R
.:
app common
./app:
foo.c main.c Makefile
./common:
bar.c Makefile
在这里,./app/Makefile
就是您的配方为 A 的Makefile。 ./common/Makefile
,
您没有发布的只是:
obj/bar.o: bar.c
gcc -MMD -MP -c -I. $< -o $@
因为这将起到说明作用。
我们内置./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 并重建。
$ 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
:
$ 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
。所以我就这样:
$ 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正在尝试通过运行以下命令来重新制作它:
gcc ../common/obj/bar.d.o -o ../common/obj/bar.d
为什么?为了回答这个问题,我们首先将./app/Makefile
改回使用配方 A
-考虑完成-然后执行:
$ make --print-data-base > out.txt
将{收集的所有信息转储到out.txt
中,
生成文件(Makefile
及其所有递归include
-s的生成文件,
在这种情况下,只是自动生成的.d
文件)。
让我们看看数据库对../common/obj/bar.d
的评价。它说:
# 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
必须对这些文件中的任何文件进行更新。好。
现在让我们再次回到./app/Makefile
中的食谱 B -认为已完成-
并再次执行:
$ make --print-data-base > out.txt
,然后再次浏览out.txt
中与../common/obj/bar.d
有关的内容。这次我们发现:
../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
!
制作方法如下:
$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
它当然会扩展为:
gcc ../common/obj/bar.d.o -o ../common/obj/bar.d
由于配方 B ,使Make如何解决该问题?
首先,它考虑了makefile文件中的任何规则还是任何
内置的规则使它可以直接从任何现有文件制作../common/obj/bar.d
,
并画了一个空白。
下一步它考虑了其中任何规则是否为
从intermediate file中制作../common/obj/bar.d
。 中间文件是不存在的文件,但其自身可以被 制成
从现有文件中读取任何规则或其内置规则。这个
时间到了。
Make的内置模式规则之一是:
%: %.o
# recipe to execute (built-in):
$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
您可以在out.txt
中找到它。您会看到这是模式规则
它与以下项匹配:
../common/obj/bar.d: ../common/obj/bar.d.o
该配方中有一个配方可以链接程序,名为../common/obj/bar.d
对象文件../common/obj/bar.d.o
。
没有目标文件../common/obj/bar.d.o
,但是它可以是中间文件吗?如果
Make可以找到规则,从存在 do 的文件中制作../common/obj/bar.d.o
,
那么它也可以使用此../common/obj/bar.d
规则制作%: %.o
。
它可以找到从现有文件中制作../common/obj/bar.d.o
的方法
因为我们只给了它一个! -食谱 B :
$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
+$(MAKE) -C $(COMMONDIR)
这告诉Make是否有任何匹配$(OBJDIRCOMMON)/%.o
的目标(例如../common/obj/bar.d.o
)
不存在,但是$(COMMONDIR)/Makefile
确实存在(确实存在),则该目标
通过运行以下命令来更新:
$(MAKE) -C $(COMMONDIR)
使我们相信。它运行了$(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
是最新的。因此它前进到:
../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
由于我们撒谎而失败了:
make -C ../common
根本不做../common/obj/bar.d.o
。
gcc: error: ../common/obj/bar.d.o: No such file or directory
这对于食谱 A 不会发生,因为
$(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
不是
一个目标。
粘贴食谱 A ,因为它是正确的,食谱 B 是错误的。同时查看
并修复makefile,以使make clean
始终删除所有可能已构建的非.PHONY
目标,而不会删除其他任何目标。最后,避免在配方未提及目标的情况下使用非.PHONY
目标编写配方。
答案 1 :(得分:0)
应该有&&而不是管道
$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/Makefile | (OBJDIRCOMMON)
+$(MAKE) -C $(COMMONDIR