C++ 请解释此Makefile-似乎缺少可执行规则
我有一个我不懂的makefileC++ 请解释此Makefile-似乎缺少可执行规则,c++,makefile,C++,Makefile,我有一个我不懂的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 de
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)
太好了。这一定是被呼叫的规则。但这条规则从何而来?如何知道存在哪些默认规则?您的make包包含大量默认规则,此Makefile依赖于这些规则。尝试使用-d(调试信息)运行make。我相信这将向您展示所有正在发挥作用的内容。理解此
Makefile
可能会错过的是和的概念
此部分查找.cpp文件:
build_sources:=$(wildcard *.cpp)
这一部分创建具有相同名称的目标(使用上面定义的变量build\u sources
),但扩展名替换为.d:
depends:=$(build_sources:.cpp=.d)
相同类型的构造定义了生成目标(相同的文件名,扩展名已删除):
然后,默认目标被定义为需要构建\u目标
,即对应于.cpp的可执行文件`
all: $(build_targets)
此规则定义如何从.cpp
生成.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:;" ; } > $@
.d将包含每个cpp文件的依赖项,并将创建一个规则来构建.o
文件和每个cpp的可执行文件$@
是规则(.d
文件)的目标,除非我弄错了,否则它将包含如下规则(由sed表达式编写):
最后,链接单个.o
文件的方法开始生效:
链接单个对象文件
n是通过C编译器运行链接器(通常称为ld)从n.o自动生成的。使用的精确配方是“$(CC)$(LDFLAGS)n.o$(LOADLIBES)$(LDLIBS)”
编辑:要在子目录obj
中构建对象,必须修改每个文件名:
depends:=$(foreach file,$(build_sources:.cpp=.d),"obj/$(file)")
要在单独的子目录bin
中生成二进制文件,您需要对生成\u目标执行相同的操作:
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
make -f /dev/null -p
这可以通过正确修改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" ; } > $@
$(CXX)-MM$(CPPFLAGS)$<{sed's#\($*\)\.o[:]*.\1.o$@:\'g';echo“%.h:;“obj='echo$>$@
生成文件本身不完整。它会自动创建文件.d
,这些文件是最后包含在makefile中的片段。这些文件是使用%.d
-规则创建的
因此,请查看生成的.d
文件,以查看每个对象文件的规则。规则生成脚本是用sed编写的,有点难读,但实际上非常简单。首先,使用-MM
标志在.cpp
文件上调用编译器,它将输出如下内容
foo.o: foo.cpp foo.h bar.h
例如,如果foo.cpp
包括其自己的头文件foo.h
以及bar.h
,则(直接或间接)
sed
regex replacement命令现在只需将生成的规则即将写入的文件名(即.d
文件)添加到上述规则中的.o
之后,因此它也被标记为取决于源,就像对象文件一样。当其中一个源中的include稍后更改时,这一点很重要。sedsed
命令还为所有头文件添加了一条不执行任何操作的规则
foo.o foo.d: foo.cpp foo.h bar.h
%.h:;
生成的对象文件.o
然后使用以下选项之一链接:
链接单个对象文件
n由n.o自动生成
通过C编译器运行链接器(通常称为ld
)。精确的
使用的配方是$(CC)$(LDFLAGS)n.o$(LOADLIBES)$(LDLIBS)
[……]
许多规则都是为了制定的。如果您使用的是GNU Make,您可以让它打印其内置规则,如下所示:
bin/foo: obj/foo.o
$(CC) $(LDFLAGS) obj/foo.o $(LOADLIBES) $(LDLIBS) -o bin/foo
make -f /dev/null -p
您可以使用
-r
使其忽略内置规则;如果您对Makefile执行此操作,您会发现它会抱怨它不知道如何创建目标。对。但是构建.d
的规则是不够的,因为它生成Foo.d
,而不是实际生成的Foo
通过C编译器。使用的精确公式是“$(CC)$(LDFLAGS)n.o$(LOADLIBES)$(LDLIBS)”。对于只有一个源文件的简单程序,此规则是正确的。“是这样吗?我正在更新更多详细信息。事实上,.d将包含在makefile中,并包含构建文件的规则,现在就完成了!很抱歉出现了问题:-)作为后续问题-如何更改生成文件以使输出在单独的生成目录中生成?几乎正确-sed命令使$@
的扩展依赖于与对象文件相同的内容(在本例中,写入的行是foo.o foo.d:foo.cpp foo.h bar.h
)因此,如果任何相关源发生更改,依赖项文件将重新生成。@TobySpeight感谢您的解释。我编辑了我的答案。
foo.o: foo.cpp foo.h bar.h
foo.o foo.d: foo.cpp foo.h bar.h
%.h:;
make -f /dev/null -p