makefile优化

以前从未在意过makefile的执行效率,直到最近,由于需要将代码前前后后编译几十遍甚至上百遍,才发现原来的方式实在太慢。

以前的方式:

       ALL ?=
       default: END

       target1:
            cd . && make clean ready ....
       ALL += target1
       target2:
            cd . && make clean ready .....
       ALL += target2
      很多的target
       done:
            @echo done
       END: $(ALL) done
       

这样前前后后编译下来,天哪,竟然要2个小时,想想这样的操作需要3遍,基本上一天就没有了。这实在忍受不了。

make命令支持-j参数,可以并行执行,但上述模式是不可以的。由于调用make命令会将-j参数设置为默认的,因此在最外部调用-j参数无实质性作用

测试一下大致的目标,将所有生成的中间文件的命令完全的写入到一个临时文件中,并在原来的目标中生成临时文件,以便检查依赖性通过,然后在最后一步运行中间文件的命令时使用并行执行。这样时间大幅度的降低为40分钟左右。

但这样还是太慢,研究代码,发现时间在重新执行make命令时对makefile的解析上,那么,我们就要想办法让makefile解析的更快些,避免无意义的解析。这就用到了makefile中的eval函数。首先,定义一个模板:

       define make_target_one
       .PHONY $(1) $(1)_ready $(2) .o .s .c .cpp .obj
       $(1): $(1)_ready $(2)
       $(2): $(2).$(MODULE)
       $(2).$(MODULE): $(OUTDIR)/$(LOCALOBJS)
             生成$(2).$(MODULE)的指令
       $(OUTDIR)%.o: %.c
            $(CC) $(CFLAGS) $(OBJCMD) $$(@) $$<
       ALL += $(1)
       

这样,如果我们需要新的目标,只用加入:

       #设置好LOCALOBJS内容
       LOCALOBJS :=
       $(eval $(call make_target_one $(TARGET), $(DISTDIR)/$(TARGET)))
       

接下来测试,这下即使不使用并行执行也在40分钟以内,效果相比中间的临时测试也要好了,然后加上并行测试的选项,使用-j2,时间减半,只有20分钟了,对于并行执行的数量,超过CPU内核的话,意义就不明显了。

最明显的优化就是上面些了,我发现我原来的写法中有一个问题,经常使用find命令,大致如下:

       LIBRARY_LIST := $(shell cd lib_dir && find . -name *.a)
       LIBRARY_LIST :=$(foreach temp, $(LIBRARY_LIST), $(dirname $(temp)))
       LIBRARY_LIST := $(shell echo $(LIBRARY_LIST) | sed -e 's/.\///g')
       

逻辑很简单,查找后缀.a的文件,然后取每一个的目录名,然后去掉之前的./,我想,find命令总是执行慢的,sed的这一步我也不想要,我想起了echo的自动展开方式,于是,变成了下面的方式:

       LIBRARY_LIST := $(shell cd lib_dir && echo */*/*/*.a)
       LIBRARY_LIST :=$(foreach temp, $(LIBRARY_LIST), $(dirname $(temp)))
       

echo要比find更高效,而且,没有之前讨厌的./的问题。这样,基本上满意些了。

发布者

rix

如果连自己都不爱自己,哪还有谁来爱你