Makefile GNU make:使用生成的头文件生成自动依赖项
所以我跟着报纸走-- 生成文件:Makefile GNU make:使用生成的头文件生成自动依赖项,makefile,auto-generate,gnu-make,Makefile,Auto Generate,Gnu Make,所以我跟着报纸走-- 生成文件: ... HDRS := foo.h $(HDRS): mk_header.sh $* clean:: -rm $(HDRS) ... foo.h: --它就像一个符咒 但是当foo.h成为生成的文件时-- 生成文件: ... HDRS := foo.h $(HDRS): mk_header.sh $* clean:: -rm $(HDRS) ... mk_header.sh: #!/bin/bash UP=$(t
...
HDRS := foo.h
$(HDRS):
mk_header.sh $*
clean::
-rm $(HDRS)
...
foo.h:
--它就像一个符咒
但是当
foo.h
成为生成的文件时--
生成文件:
...
HDRS := foo.h
$(HDRS):
mk_header.sh $*
clean::
-rm $(HDRS)
...
mk_header.sh:
#!/bin/bash
UP=$(tr "[:lower:]" "[:upper:]" <<< $1)
cat <<EOF > $1.h
#ifndef __${UP}_H__
#define __${UP}_H__
void $1() ;
#endif
EOF
只有在第二次调用make
时,才会生成foo.h
,从而生成另一个构建级联
$ make
./mk_header.sh foo
cc -MMD -MG -MT 'main.o main.d' -c main.c -o main.o
cp main.d main.tmp
sed -e 's;#.*;;' -e 's;^[^:]*: *;;' -e 's; *\\$;;' \
-e '/^$/d' -e 's;$; :;' < main.tmp >> main.d
rm main.tmp
cc main.o foo.o -o main
$ ls
foo.c foo.d foo.h foo.o
main* main.c main.d main.o
Makefile mk_header.sh*
因此,我的问题是:有没有一种方法可以扩展上述论文建议的方法,以允许生成头文件,如果不消除在包含
*.d
片段时不必重新评估整个生成树所实现的性能增益,您可以为生成的头创建一个明确的依赖规则:
main.o: foo.h
如果生成的标题直接包含在少量文件中,这可能是一种可行的方法。简短回答:不。论文中描述的方法非常聪明,是我最喜欢的方法之一,但它是一种复杂的原始工具的使用。它利用了通常的方案,其中所有需要的头都存在;它试图解决的问题是确定哪些头(如果最近修改过)需要重建给定的对象文件。特别是,如果对象文件不存在,则必须重建它——在这种情况下,没有理由担心头文件,因为编译器肯定会找到它们 现在生成了头文件。因此,
foo.h
可能不存在,因此必须有人运行脚本来生成它,并且只有Make可以这样做。但是,如果不对main.c
进行一些分析,Make就无法知道foo.h
是必要的。但是,直到Make开始执行与main
相关的规则(例如main.o
或main.o.d
),Make才真正能够执行这些规则,直到它决定要构建哪些目标
所以我们必须使用。。。递归生成![Dun-dunnn!]
我们无法实现论文中避免重新使用Make的目标,但我们至少可以避免(一些)不必要的重建。你可以做一些像论文中描述的“基本的自相关性”;本文描述了这种方法的问题。或者您可以使用类似“高级”配方中的命令来生成标题列表,然后将其传递给
$(MAKE)
;这种方法是整洁的,但可能会在同一个头上多次调用Make,这取决于代码树的外观。问题是,*.d
生成文件片段必须在所有头生成完成后执行。通过这种方式,可以使用make依赖项强制执行正确的顺序:
SRCS := main.c foo.c
HDRS := foo.h
main: main.o foo.o
%.o: %.c | generated_headers
$(CC) -MMD -MG -MT '$@ $*.d' -c $< -o $@
cp $*.d $*.tmp
sed -e 's;#.*;;' -e 's;^[^:]*: *;;' -e 's; *\\$$;;' \
-e '/^$$/d' -e 's;$$; :;' < $*.tmp >> $*.d
rm $*.tmp
-include $(SRCS:.c=.d)
$(HDRS):
mk_header.sh $*
generated_headers: $(HDRS)
clean:
-rm $(HDRS) *.o *.d main
.PHONY: clean generated_headers
因此,开发商可以发布:
make FAST_AND_LOOSE=1 main.o
原始问题中的makefile不适用于gcc 4.8.2:
cc -MMD -MG -MT main.d -c main.c -o main.o
cc1: error: -MG may only be used with -M or -MM
我猜gcc在过去4年的某个时候改变了-MG
的行为
似乎如果您想支持生成的头文件,就不再需要
任何同时生成“.d”文件和“.o”文件的方法,无需
调用C预处理器两次
因此,我已将配方更新为:
%.o: %.c
$(CC) -MM -MG -MP -MT $*.o -MF $*.d $<
$(CC) -c $< -o $@
再次运行它可以工作:
$ make
./mk_header.sh foo
cc -MM -MG -MP -MT main.o -MF main.d main.c
cc -c main.c -o main.o
cc main.o -o main
因为我们必须运行两次C预处理器,所以让我们生成.d
在单独的规则中创建文件:
%.d: %.c
$(CC) -MM -MG -MP -MT $*.o -MF $@ $<
%.o: %.c
$(CC) -c $< -o $@
这是否会受到原始问题的性能问题的影响
试图避免?这本质上就是“基本的自相关性”解决方案
本文对此进行了描述
该文件声称该解决方案存在3个问题:
make
-include
而不是include
来解决。尽我所能
这与论文避免重复执行的技巧是正交的
make
。至少我使用-include
而不是包括
问题3由GCC的-MP
(或等效的sed脚本)解决——这是
也与避免重新执行make
的技术正交
问题1可能可以通过以下方式有所改善:
%.d: %.c
$(CC) -MM -MG -MP -MT $*.o -MF $@.new $<
cmp $@.new $@ 2>/dev/null || mv $@.new $@; rm -f $@.new
在这一变化之后:
$ make clean
rm -f *.o *.d main foo.h
$ make -d 2>&1 | grep Re-executing
Re-executing[1]: make -d
$ make -d 2>&1 | grep Re-executing
$ touch main.c; make -d 2>&1 | grep Re-executing
Re-executing[1]: make -d
$ make clean
rm -f *.o *.d main foo.h
$ make -d 2>&1 | grep Re-executing
Re-executing[1]: make -d
$ make -d 2>&1 | grep Re-executing
$ touch main.c; make -d 2>&1 | grep Re-executing
稍微好一点。当然,如果引入了新的依赖项,make
仍将
需要重新执行。也许没有什么可以改善这一点;这似乎是正确性和速度之间的折衷
以上所有内容都用make 3.81进行了测试。我已经意识到,这将强制实现正确的依赖关系,但是这不会扩展,并且与整个努力试图解决的问题背道而驰。
$ make
cc -MM -MG -MP -MT main.o -MF main.d main.c
cc -c main.c -o main.o
main.c:1:17: fatal error: foo.h: No such file or directory
#include "foo.h"
^
compilation terminated.
Makefile:7: recipe for target 'main.o' failed
make: *** [main.o] Error 1
$ make
./mk_header.sh foo
cc -MM -MG -MP -MT main.o -MF main.d main.c
cc -c main.c -o main.o
cc main.o -o main
%.d: %.c
$(CC) -MM -MG -MP -MT $*.o -MF $@ $<
%.o: %.c
$(CC) -c $< -o $@
$ make clean
rm -f *.o *.d main foo.h
$ make
cc -MM -MG -MP -MT main.o -MF main.d main.c
./mk_header.sh foo
cc -c main.c -o main.o
cc main.o -o main
%.d: %.c
$(CC) -MM -MG -MP -MT $*.o -MF $@.new $<
cmp $@.new $@ 2>/dev/null || mv $@.new $@; rm -f $@.new
$ make clean
rm -f *.o *.d main foo.h
$ make -d 2>&1 | grep Re-executing
Re-executing[1]: make -d
$ make -d 2>&1 | grep Re-executing
$ touch main.c; make -d 2>&1 | grep Re-executing
Re-executing[1]: make -d
$ make clean
rm -f *.o *.d main foo.h
$ make -d 2>&1 | grep Re-executing
Re-executing[1]: make -d
$ make -d 2>&1 | grep Re-executing
$ touch main.c; make -d 2>&1 | grep Re-executing