我有这个我不明白的makefile。
build_sources:=$(wildcard *.cpp)
depends:=$(build_sources:.cpp=.d)
build_targets:=$(build_sources:.cpp=)
.PHONY: all
all: $(build_targets)
.PHONY: clean
clean:
rm -f $(build_targets) *.{a,o,d}
#build the list of header file dependencies automatically
%.d: %.cpp
@echo building include dependencies for $(*F)
@$(CXX) -MM $(CPPFLAGS) $< | { sed 's#\($*\)\.o[ :]*#\1.o $@ : #g' ; echo "%.h:;" ; } > $@
-include $(depends)
我了解创建的可执行文件是build_target
。因此,如果我有Foo.cpp
和Bar.cpp
,则创建的可执行文件将为Foo
和Bar
。
但它是如何做到的?我只看到1条规则,它是%.d: %.cpp
。所以它说Foo.d
文件取决于Foo.cpp
。但是实际编译Foo
的规则呢?
这个makefile可以工作,所以它实际上并没有遗漏任何东西。但是为什么我看不到规则呢?有一些隐含的规则吗?
编辑 - 我确实进行了调试,并看到了以下内容
No need to remake target `foo.d'.
Considering target file `foo'.
File `foo' does not exist.
Looking for an implicit rule for `foo'.
Trying pattern rule with stem `foo'.
Trying implicit prerequisite `foo.o'.
Found an implicit rule for `foo'. ## WHAT EXACTLY IS THIS?
Considering target file `foo.o'.
File `foo.o' does not exist.
Looking for an implicit rule for `foo.o'.
Trying pattern rule with stem `foo'.
Trying implicit prerequisite `foo.c'.
Trying pattern rule with stem `foo'.
Trying implicit prerequisite `foo.cc'.
Trying pattern rule with stem `foo'.
Trying implicit prerequisite `foo.C'.
Trying pattern rule with stem `foo'.
Trying implicit prerequisite `foo.cpp'.
Found an implicit rule for `foo.o'.
Pruning file `foo.cpp'.
Pruning file `foo.cpp'.
Finished prerequisites of target file `foo.o'.
Must remake target `foo.o'.
g++ -I../../include -Wall -std=c++11 -O3 -Wsign-compare -Wno-strict-aliasing -s -c -o foo.o foo.cpp
那很好。这必须是被调用的规则。但这条规则从何而来?我如何知道存在哪些默认规则?
答案 0 :(得分:2)
您可能会错过了解Makefile
的概念pattern rule和automatic variables。
这部分找到.cpp文件:
build_sources:=$(wildcard *.cpp)
此部分创建具有相同名称的目标(使用上面定义的变量build_sources
),但扩展名替换为.d:
depends:=$(build_sources:.cpp=.d)
相同类型的构造定义了构建目标(删除了扩展名的相同文件名):
build_targets:=$(build_sources:.cpp=)
然后将默认目标定义为需要build_targets
,即对应于.cpp`的可执行文件
all: $(build_targets)
此规则定义了如何从.d
:
.cpp
#build the list of header file dependencies automatically
%.d: %.cpp
@echo building include dependencies for $(*F)
@$(CXX) -MM $(CPPFLAGS) $< | { sed 's#\($*\)\.o[ :]*#\1.o $@ : #g' ; echo "%.h:;" ; } > $@
gcc -MM
生成规则以了解.c
或.cpp
文件所依赖的标头。通常,如果test.cpp
包含test1.h
和test2.h
,则输出将为:
test.o: test.cpp test1.h test2.h
.d将包含每个cpp文件的依赖项,并将创建一个规则来构建.o
文件和每个cpp的可执行文件。 $@
是规则的目标(.d
文件),除非我误解,否则将包含一个看起来像这样的规则(由sed表达式编写):
filename.o filename.d : filename.cpp <list of headers>
%.h:;
第一条规则赋予.o
文件的依赖关系。它没有配方,这意味着它只是将依赖项添加到任何现有规则。隐式规则将用于构建它们。
第二个是这里,以防你压缩标题。在这种情况下,make
将使用此规则,该规则表明根本没有任何事情要做(它有一个空配方)。
然后所有.d
文件都包含在makefile的一部分中:
-include $(depends)
最后,链接单个.o
文件的implicit rule会启动:
链接单个目标文件
n是通过C编译器运行链接器(通常称为ld)从n.o自动生成的。使用的精确配方是'$(CC)$(LDFLAGS)n.o $(LOADLIBES)$(LDLIBS)'。
编辑:要在子目录obj
中构建对象,您必须修改每个文件名:
depends:=$(foreach file,$(build_sources:.cpp=.d),"obj/$(file)")
要在单独的子目录bin
中构建二进制文件,您需要对build_targets执行相同的操作:
build_targets:=$(foreach file,$(build_sources:.cpp=), "bin/$(file)")
然后你需要编写规则来构建它,因为默认规则不再适用(.o
不在同一目录中)。你想要的是添加这样的规则:
bin/foo: obj/foo.o
$(CC) $(LDFLAGS) obj/foo.o $(LOADLIBES) $(LDLIBS) -o bin/foo
可以通过对long shell命令的正确修改来完成:
@$(CXX) -MM $(CPPFLAGS) $< | { sed 's#\($*\)\.o[ :]*#\1.o $@ : #g' ; echo "%.h:;" ; obj=`echo $< | sed 's/.cpp/.o/'` ; bin=`echo $< | sed 's/.cpp//'` ; echo "$bin: $obj" ; echo -e "\t\$(CC) \$(LDFLAGS) $obj \$(LOADLIBES) \$(LDLIBS) -o $bin" ; } > $@
答案 1 :(得分:0)
您的make包中包含大量默认规则,这些Makefile依赖于此规则。尝试使用-d(调试信息)运行make。我相信这会向你展示一切正在发挥的作用。
答案 2 :(得分:0)
makefile本身并不完整。它会自动创建文件.d
,这些文件是最后包含在该makefile中的片段。 这些文件是使用%.d
- 规则创建的。
因此,请查看生成的.d
文件,以查看每个单独对象文件的规则。规则生成脚本使用sed
编写,有点难以阅读,但实际上非常简单。首先,使用.cpp
标志在-MM
文件上调用编译器,该标志将输出类似
foo.o: foo.cpp foo.h bar.h
如果foo.cpp
包含自己的头文件foo.h
以及bar.h
,例如(直接或间接)。
sed
regex replacement命令现在只需添加文件名,即在.d
之后生成的规则即将被写入(.o
文件)以上规则,因此它也被标记为依赖于源,就像目标文件一样。当其中一个来源中包含的内容稍后更改时,这一点非常重要。 sed
命令还为所有头文件添加了一条不执行任何操作的规则。
foo.o foo.d: foo.cpp foo.h bar.h
%.h:;
然后使用implicit rules:
之一链接生成的目标文件.o
链接单个目标文件
n是从n.o自动生成的 通过C编译器运行链接器(通常称为
ld
)。准确的 使用的食谱是$(CC) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)
。[...]
答案 3 :(得分:0)
许多规则内置于中。如果您正在使用GNU Make,您可以使用GNU Make来打印它的内置规则:
make -f /dev/null -p
您可以使用-r
忽略内置规则;如果您使用Makefile执行此操作,您会发现它抱怨它不知道如何制作您的目标。