在ubuntu 20上编译busybox的时候,编译过一次后,再次编译仍然会重新编译。
于是开始debug,首先查看Makefile,可以单独编译一个目录:
$ make applets
其recipe是:make -f /work/staging/source/busybox-1.31.1/scripts/Makefile.build obj=applets
查看scripts/Makefile.build,发现其和内核是一套东西,里面有一个编译.o的规则:
1 2 3 | %.o: %.c FORCE $(call cmd,force_checksrc) $(call if_changed_rule,cc_o_c) |
if_changed_rule,定义在scripts/Kbuild.include:
1 2 3 4 | if_changed_rule = $(if $(strip $(filter-out $(PHONY),$?) \ $(call arg-check, $(cmd_$(1)), $(cmd_$@)) ),\ @set -e; \ $(rule_$(1))) |
$? 是所有比目标新的依赖。如果有就执行rule_cc_o_c,还有一个检查arg-check,$@是目标的名字,它比较两个变量是否相同,cmd_cc_o_c,cmd_applets/applets.o。
rule_cc_o_c的定义如下:
1 2 3 4 5 6 7 8 | define rule_cc_o_c $(call echo-cmd,checksrc) $(cmd_checksrc) \ $(call echo-cmd,cc_o_c) $(cmd_cc_o_c); \ $(cmd_modversions) \ scripts/basic/fixdep $(depfile) $@ '$(call make-cmd,cc_o_c)' > $(@D)/.$(@F).tmp; \ rm -f $(depfile); \ mv -f $(@D)/.$(@F).tmp $(@D)/.$(@F).cmd endef |
这个定义不仅生成依赖文件,还生成.applets.o.cmd,看一下这个文件的内容:
1 2 3 4 5 6 7 8 9 10 11 | cmd_applets/applets.o := /home/yuan/toolchain/crosstool-ipq4018/bin/arm-linux-gnueabihf-gcc -Wp,-MD,applets/.applets.o.d -std=gnu99 -Iinclude -Ilibbb -Iinclude2 -I/work/staging/source/busybox-1.31.1/include -I/work/staging/source/busybox-1.31.1/libbb -include include/autoconf.h -D_GNU_SOURCE -DNDEBUG -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D"BB_VER=KBUILD_STR(1.31.1)" -I/work/staging/source/busybox-1.31.1/applets -Iapplets -Wall -Wshadow -Wwrite-strings -Wundef -Wstrict-prototypes -Wunused -Wunused-parameter -Wunused-function -Wunused-value -Wmissing-prototypes -Wmissing-declarations -Wno-format-security -Wdeclaration-after-statement -Wold-style-definition -fno-builtin-strlen -finline-limit=0 -fomit-frame-pointer -ffunction-sections -fdata-sections -fno-guess-branch-probability -funsigned-char -static-libgcc -falign-functions=1 -falign-jumps=1 -falign-labels=1 -falign-loops=1 -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-builtin-printf -Os -D"KBUILD_STR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(applets)" -D"KBUILD_MODNAME=KBUILD_STR(applets)" -c -o applets/applets.o /work/staging/source/busybox-1.31.1/applets/applets.c deps_applets/applets.o := \ /work/staging/source/busybox-1.31.1/applets/applets.c \ $(wildcard include/config/build/libbusybox.h) \ /home/yuan/toolchain/crosstool-ipq4018/arm-linux-gnueabihf/sysroot/usr/include/stdc-predef.h \ /work/staging/source/busybox-1.31.1/include/busybox.h \ $(wildcard include/config/feature/prefer/applets.h) \ $(wildcard include/config/feature/sh/standalone.h) \ $(wildcard include/config/feature/sh/nofork.h) \ $(wildcard include/config/feature/suid.h) \ |
可以看到不仅依赖变了,如果编译.o的命令变了,比如CFLAGS变了,那么也会重新编译.o,这是内核编译的逻辑。
那么打印出cmd_cc_o_c,cmd_applets/applets.o两个变量,发现后者少了#之后的内容。这样就明朗了,在.cmd文件中,内容是完整的,但是在老的ubuntu上是可以编译的,其Make版本是4.1,而ubuntu 20上的版本是4.3
$ make --version
GNU Make 4.3
Built for x86_64-pc-linux-gnu
Copyright (C) 1988-2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
rule_cc_o_c,调用scripts/Kbuild.include中的make-cmd来转义cc_o_c,其定义如下:
make-cmd = $(subst \#,\\\#,$(subst $$,$$$$,$(call escsq,$(cmd_$(1)))))
核心就是替换#为\#,在变量的定义中,是需要转义的。在老的Make中,函数中的#需要转移,但是新的不需要转义。写一个测试Makefile:
1 2 3 4 5 6 | cmd_a := "12\#34" make-cmd = $(subst \#,\\\#,$(cmd_a)) $(info $(value make-cmd)) all: ; |
在Make 4.3中输出为:
$(subst \#,\\\#,$(cmd_a))
可以看到是把\#,替换为\\\#,就出问题了。
在Make 4.1中的输出为:
$(subst #,\#,$(cmd_a))
网上有类似的问题:
http://savannah.gnu.org/bugs/?20513