一,前言
如何能快速知道哪些c被编译到boot了。通过看编译信息,通过看编译完成后的ouput文件夹中的o文件,能否直接copy这些c文件到某个文件夹中,这样搜索看代码定位比较容易。若makefile自己有全部的c路径,那么我做个脚本后处理下,copy这些路径的c即可,另外,makefile居然有复制o文件,那么我只要找到它的位置,再加一句copy c文件即可,不过这是编译过程中copy会浪费点时间。
二,Makefile复习
- 常用关键字含义
$
- 关于origin语法,告诉变量从后面的参数来,也就是从command line过来
ifeq ("$(origin V)", "command line")
KBUILD_VERBOSE = $(V)
endif
用户输入为make V=1,那么$(V)的值就是1,也就是KBUILD_VERBOSE的值是1.
- 关于@make中@的作用就是禁止make命令输出。
如果 V=0 的话上述命令展开就是“@ make $(build)=tools”, make 在执行的时候默认会在终
端输出命令,但是在命令前面加上“@”就不会在终端输出命令了。当 V=1 的时候 Q 就为空,
上述命令就是“make $(build)=tools”,因此在 make 执行的过程,命令会被完整的输出在终端上。 - 如下什么含义
$(filter-out _all sub-make $(CURDIR)/Makefile, $(MAKECMDGOALS)) _all: sub-make
@:
filter-out是反过滤函数,语法是:(MAKECMDGOALS)中符合规则_all的所有字符串后,剩下的作为返回值。MAKECMDGOALS我打印出来是空,所以展开后等于,@:就是什么都不做的意思,因为_all是伪目标,所以不是真实存在,没有动作。但是过程中会去执行sub-make的依赖项。
_all: sub-make
@:
- 关于4个等号的作用
“=”是全部遍历完的值。
“:=”是向前看,不是遍历完的值。
“?=”之前已经赋值过,就忽略。
“+=”追加。 - 有个好玩的归递调用
sub-make: FORCE
$(Q)$(MAKE) -C $(KBUILD_OUTPUT) KBUILD_SRC=$(CURDIR)
-f $(CURDIR)/Makefile $(filter-out _all sub-make,$(MAKECMDGOALS))
# Leave processing to above invocation of make
skip-makefile := 1
$(info appletest1 is [${skip-makefile}])
endif # ifneq ($(KBUILD_OUTPUT),)
endif # ifeq ($(KBUILD_SRC),)
$(info appletest2 is [${skip-makefile}])
# We process the rest of the Makefile if this is the final invocation of make
ifeq ($(skip-makefile),)
$(info appletest3 is [${skip-makefile}])
我加了打印信息显示如下
make ARCH=arm CROSS_COMPILE=arm-none-eabi- O=stm -n |grep appletest
appletest1 is [1]
appletest2 is [1]
appletest2 is []
appletest3 is []
说明这个Makefile被调用了2次。关键就是在sub-make:中的-f后面跟的Makefile就包括了此文件。所以再次进行了归递调用,感觉这样设计是否很复杂。调用完还要继续调用下方的内容吗?再细看下ifeq ($(skip-makefile),)对的结尾endif # skip-makefile等于在最后一行,所以后面等于没了。也不算一个归档调用。我验证了下,内容修改如下,改成刚刚学习到的什么都不做。
sub-make: FORCE
@:
通过命令make ARCH=arm CROSS_COMPILE=arm-none-eabi- O=stm -n V=1 >1c.txt
打印的所有信息如下,等于这个Makefile直接完结了。因为第二次调用的时候KBUILD_SRC已经定义,所以skip-makefile就为空了,才导致了路径不同。
$MK_ARCH is ["arm"]
appletest1 is [1]
appletest2 is [1]
:
:
- 打印的make[1],make[2],make[3]是什么含义
变量MAKELEVEL的值在向下层传递时发生变化。该变量的值是字符型,它用十进制数表示层的深度。‘0’代表顶层make,‘1’代表子make,‘2’代表子-子-make,以此类推。 - 显示重定向
0、1、2指的是文件描述符。 0:stdin 1:stdout 2:stderr “&” &用来指明其后跟的是文件描述符 “>”则是重定向符号。 1>&2 意思是把标准输出重定向到标准错误. 2>&1 意思是把标准错误输出重定向到标准输出。直接echo xxx >&2就是把echo打印到标准输出的内容重定向到标准错误。 - 关于替换
INPUTS-y += $(CONFIG_BUILD_TARGET:”%”=%)
就是把CONFIG_BUILD_TARGET中格式为”任意字符”,替换为任意字符,简单来说就是删除双引号。 - MAKECMDGOALS是一个make的特殊变量,保存了指定的目标名。若make没有指定目标则此变量为空。
- Make命令不带目标的情况下,就执行第一个不带点的目标。一般makefile有很多代点的伪目标,用来执行文件操作等,因为它其实没有实际要编译的target。
- 我用的makefile调试命令,除了用$(info $(xxx))打印条件变量,还可以用如下命令。另外makefile是全部解析完成后,再进行命令执行的,这点很重要。
make ARCH=arm CROSS_COMPILE=arm-none-eabi- O=stm V=1 >1e.txt
make ARCH=arm CROSS_COMPILE=arm-none-eabi- O=stm -qp
make ARCH=arm CROSS_COMPILE=arm-none-eabi- O=stm -t
make ARCH=arm CROSS_COMPILE=arm-none-eabi- O=stm -n
make ARCH=arm CROSS_COMPILE=arm-none-eabi- O=stm -d
- 如何快速看uboot。
先自己从主Makefile开始看,找到首个目标,然后找到依赖关系,此时可能遇到很对变量,这些变量的值我期望直接告诉我,一个是遇到不清楚的自己打印出来,另外一个就是一下子全部打印出来,用-qp即可
make ARCH=arm CROSS_COMPILE=arm-none-eabi- O=stm -qp >A.txt
可以不执行用-n,可以打印全路径执行顺序用V=1
make ARCH=arm CROSS_COMPILE=arm-none-eabi- O=stm -n V=1 >B.txt
比如我从all的依赖项找到如下binman_stamp的依赖项,遇到一个INPUTS-y,我需要快速知道此变量的内容,就去A.txt搜索INPUTS-y即可,也可以搜索binman_stamp:关键字,因为-pq都展开了。是不是超级快呀!
.binman_stamp: $(INPUTS-y) FORCE
ifeq ($(CONFIG_BINMAN),y)
$(call if_changed,binman)
endif
@touch $@
A.txt中找到的信息
.binman_stamp: checkarmreloc u-boot.srec u-boot.bin u-boot.sym System.map binary_size_check FORCE
# Implicit rule search has not been done.
# Last modified 2023-10-31 09:15:34.966171096
# File has been updated.
# Needs to be updated (-q is set).
# variable set hash-table stats:
# Load=0/32=0%, Rehash=0, Collisions=0/36=0%
# recipe to execute (from '/work/uboot/u-boot-2023.10/Makefile', line 1123):
@touch $@
很明显出来完成”checkarmreloc u-boot.srec u-boot.bin u-boot.sym System.map binary_size_check FORCE”这些依赖项后,最后touch一个binmain_stamp的文件。还可以继续用make ARCH=arm CROSS_COMPILE=arm-none-eabi- O=stm -t >C.txt
来查看touch文件的顺序。先touch的是”include/config/uboot.release”最后是binmain_stamp文件。
- 我不准备按顺序全部看了,随便挑选一个,看看uboot.release是如何生成的。因为我的目的是快速看,快速看的目的是将来万一有问题可以便于定位,所以掌握方法最重要。
若要按顺序就是从第一个依赖项checkarmreloc开始继续用同样的套路来看即可。
先看主makekfile搜索关键自己uboot.release
# Store (new) UBOOTRELEASE string in include/config/uboot.release
include/config/uboot.release: include/config/auto.conf FORCE
$(call filechk,uboot.release)
然后到A.txt中
include/config/uboot.release: include/config/auto.conf FORCE
# Implicit rule search has not been done.
# Implicit/static pattern stem: ''
# Last modified 2023-10-31 09:10:57.346234441
# File has been updated.
# Needs to be updated (-q is set).
# automatic
# @ := include/config/uboot.release
# automatic
# % :=
# automatic
# * :=
# automatic
# + := include/config/auto.conf FORCE
# automatic
# | :=
# automatic
#
这里的call没有展开,那么继续在A.txt中找,里面变量没有展开
# makefile (from 'scripts/Kbuild.include', line 56)
filechk = $(Q)set -e; mkdir -p $(dir $@); $(filechk_$(1)) $@.tmp; if [ -r $@ ] && cmp -s $@ $@.tmp; then rm -f $@.tmp; else $(kecho) ' UPD $@'; mv -f $@.tmp $@; fi
在V=1的B.txt找到具体信息
set -e; mkdir -p include/config/; echo "2023.10$(/bin/bash ../scripts/setlocalversion ..)" include/config/uboot.release.tmp; if [ -r include/config/uboot.release ] && cmp -s include/config/uboot.release include/config/uboot.release.tmp; then rm -f include/config/uboot.release.tmp; else : ' UPD include/config/uboot.release'; mv -f include/config/uboot.release.tmp include/config/uboot.release; fi
应该先找依赖项哦
include/config/auto.conf: .config include/config/auto.conf.cmd ../tools/Kconfig ../test/overlay/Kconfig ...太多了../include/linux/byteorder/little_endian.h ../include/linux/byteorder/swab.h ../include/linux/byteorder/generic.h
# Implicit rule search has been done.
# Implicit/static pattern stem: 'auto'
# Last modified 2023-10-31 08:47:44.116596641
# File has been updated.
# Successfully updated.
# recipe to execute (from '/work/uboot/u-boot-2023.10/Makefile', line 613):
$(Q)$(MAKE) -f $(srctree)/Makefile syncconfig
@# If the following part fails, include/config/auto.conf should be
@# deleted so "make silentoldconfig" will be re-run on the next build.
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.autoconf ||
{ rm -f include/config/auto.conf; false; }
@# include/config.h has been updated after "make silentoldconfig".
@# We need to touch include/config/auto.conf so it gets newer
@# than include/config.h.
@# Otherwise, 'make silentoldconfig' would be invoked twice.
$(Q)touch include/config/auto.conf
这个太多了的内容也好分析,从第一个.config就是一个文件,include/config/auto.conf.cmd是一个脚本,此脚本包括了那么多内容
但是问题来了(MAKE) -f $(srctree)/Makefile syncconfig中syncconfig是什服务器托管网么,搜索不到,于是把执行动作的3个MAKE删除依然正常跑。再看看注释这应该是关系到config配置文件更新才用到的,于是我distclean不要加-n,其它参数正常加再来一遍。
make ARCH=arm CROSS_COMPILE=arm-none-eabi- O=stm V=1 >33.txt
然后再distclean不加-q,再来一遍,目的是真实的重新编译
make ARCH=arm CROSS_COMPILE=arm-none-eabi- O=stm -p >44.txt
此时再找依赖项,就没有那么多文件了。
# Not a target:
include/config/auto.conf:
# Implicit rule search has been done.
# Last modified 2023-10-31 10:52:31.084670879
# File has been updated.
# Successfully updated.
那么应该到了执行阶段了,搜索”syncconfig:”也正常能找到
syncconfig: scripts/kconfig/conf
# Phony target (prerequisite of .PHONY).
# Command line target.
# Implicit rule search has not been done.
# Implicit/static pattern stem: ''
# File is an intermediate prerequisite.
# File does not exist.
# File has been updated.
# Successfully updated.
# automatic
# @ := syncconfig
# automatic
# % :=
# automatic
# * :=
# automatic
# + := scripts/kconfig/conf
# automatic
# | :=
# automatic
#
接着继续找依赖项scripts/kconfig/conf
scripts/kconfig/conf: FORCE scripts/kconfig/conf.o scripts/kconfig/zconf.tab.o
# Implicit rule search has not been done.
# Implicit/static pattern stem: ''
# Last modified 2023-10-31 10:51:19.8446895
# File has been updated.
# Successfully updated.
# automatic
# @ := scripts/kconfig/conf
# automatic
# % :=
# automatic
# * :=
# automatic
# + := FORCE scripts/kconfig/conf.o scripts/kconfig/zconf.tab.o
# automatic
# | :=
# automatic
#
查看if_changed,在scripts/Kbuild.include文件中找到此函数功能就是发现target更新,就重新build,调用执行Kconfig脚本命令$@ $(Kconfig)
# makefile (from 'scripts/Kbuild.include', line 257)
if_changed = $(if $(strip $(any-prereq) $(arg-check)), @set -e; $(echo-cmd) $(cmd_$(1)); printf '%sn' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd, @:)
至于Kconfig的命令我暂时也不清楚,网上搜索主要功能是根据配置文件生成关联关系用的。
根据`recipe to execute (from ‘../scripts/kconfig/Makefile’, line 75)`可以找到没展开的源码。
simple-targets := oldconfig allnoconfig allyesconfig allmodconfig
alldefconfig randconfig listnewconfig olddefconfig syncconfig
PHONY += $(simple-targets)
$(simple-targets): $(obj)/conf
$
通过这个(MAKE) -f $(srctree)/Makefile syncconfig,也就是把目标从all改成了syncconfig来进行了Kconfig的配置关联。
- defconfig配置有更新比没更新V=1打印的多了前30多行,这块等于Kconfig配置文件处理,能生成关键文件touch include/config/auto.conf。它的执行东西是生成了tmp文件,最后好像把版本信息保存了。好了prepare3的依赖条件等于分析完成了。
prepare3: include/config/uboot.release
ifneq ($(KBUILD_SRC),)
@$(kecho) ' Using $(srctree) as source for U-Boot'
$(Q)if [ -f $(srctree)/.config -o -d $(srctree)/include/config ]; then
echo >&2 " $(srctree) is not clean, please run 'make mrproper'";
echo >&2 " in the '$(srctree)' directory.";
/bin/false;
fi;
endif
KBUILD_SRC不为空,所以会继续打印信息,然后执行脚本,通过V=1的信息可以看到
: ' Using .. as source for U-Boot'
if [ -f ../.config -o -d ../include/config ]; then
echo >&2 " .. is not clean, please run 'make mrproper'";
echo >&2 " in the '..' directory.";
/bin/false;
fi;
prepare3目标完成了,它是prepare2的依赖条件,所以继续做prepare2的依赖条件outputmakefile
prepare2: prepare3 outputmakefile cfg
outputmakefile:
ifneq ($(KBUILD_SRC),)
$(Q)ln -fsn $(srctree) source
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile $(srctree)
endif
其实就是调用mkmakefile来生成include/generated/下的文件。然后就是cfg目标,这样看来我反向看真的是不方便呢,还是正向看方便,这样是我不小心选了个不太容易看的目标对象。
三,Makefile修改
- 回到我的主要目的,makefile稍微复习了下,然后就要干活了,我希望在vscode中仅搜索这些被编译的c内容,代表我需要修改Makefile让它编译过程把c文件也copy到o文件一起。
- 开始按目的,来修改makefile
快速看关键字,很明显o文件在这个依赖项中
u-boot: $(u-boot-init) $(u-boot-main) $(u-boot-keep-syms-lto) u-boot.lds FORCE
+$(call if_changed,u-boot__)
ifeq ($(CONFIG_KALLSYMS),y)
$(call cmd,smap)
$(call cmd,u-boot__) common/system_map.o
endif
如下可以看出,每个文件夹下都有一个汇总的built-in.o,这是-p展开的内容
u-boot-init := arch/arm/cpu/armv7m/start.o
u-boot-main := arch/arm/cpu/built-in.o arch/arm/cpu/armv7m/built-in.o arch/arm/lib/built-in.o arch/arm/mach-stm32/built-in.o board/st/common/built-in.o board/st/stm32f429-discovery/built-in.o boot/built-in.o cmd/built-in.o common/built-in.o disk/built-in.o drivers/built-in.o drivers/usb/cdns3/built-in.o drivers/usb/common/built-in.o drivers/usb/dwc3/built-in.o drivers/usb/emul/built-in.o drivers/usb/eth/built-in.o drivers/usb/host/built-in.o drivers/usb/isp1760/built-in.o drivers/usb/mtu3/built-in.o drivers/usb/musb-new/built-in.o drivers/usb/musb/built-in.o drivers/usb/phy/built-in.o drivers/usb/ulpi/built-in.o dts/built-in.o env/built-in.o fs/built-in.o lib/built-in.o net/built-in.o
makefile源码的相关脚本在如下
libs-y := $(patsubst %/, %/built-in.o, $(libs-y))
u-boot-init := $(head-y)
u-boot-main := $(libs-y)
我知道每个文件夹的build-in必定是在这个文件夹中的o文件合成的。在-p生成的文件中搜索”built-in.o:”有很多,来自的路径都是
# recipe to execute (from '/work/uboot/u-boot-2023.10/Makefile', line 1858):
找到makefile脚本代码
$(sort $(u-boot-init) $(u-boot-main)): $(u-boot-dirs) ;
$(u-boot-dirs): prepare scripts
$(Q)$(MAKE) $(build)=$@
接着找到prepare再接着就找到了scripts/Makefile.build
里面感觉看到了关键信息
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
include $(kbuild-file)
然后看到-p生成的信息去找kbuild-dir好多呀,每个的路径都不同,然后o文件也不同
# makefile (from '../scripts/Makefile.build', line 55)
kbuild-dir := ../disk
# makefile (from '../disk/Makefile', line 19)
obj- = part.o part_mac.o part_dos.o part_iso.o part_amiga.o part_efi.o
# makefile (from 'scripts/Kbuild.include', line 110)
as-instr = $(call try-run, printf "%bn" "$(1)" | $(CC) $(KBUILD_AFLAGS) -c -x assembler -o "$$TMP" -,$(2),$(3))
......
# makefile (from 'scripts/Makefile.lib', line 254)
cmd_ld = $(LD) $(ld_flags) $(filter-out FORCE,$^) -o $@
搜索cmd_ld无法找到调用,搜索cmd_找到了一些信息,猜测.o.cmd中c文件的信息就是这样写入的
if_changed = $(if $(strip $(any-prereq) $(arg-check)),
@set -e;
$(echo-cmd) $(cmd_$(1));
printf '%sn' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd, @:)
我最后加了已经$(info applefile $(any-prereq))
打印的信息居然都是.o,不过基本接近了。
applefile lib/libfdt/fdt.o lib/libfdt/fdt_ro.o lib/libfdt/fdt_wip.o lib/libfdt/fdt_strerror.o lib/libfdt/fdt_sw.o lib/libfdt/fdt_rw.o lib/libfdt/fdt_empty_tree.o lib/libfdt/fdt_addresses.o lib/libfdt/fdt.o lib/libfdt/fdt_ro.o lib/libfdt/fdt_wip.o lib/libfdt/fdt_strerror.o lib/libfdt/fdt_sw.o lib/libfdt/fdt_rw.o lib/libfdt/fdt_empty_tree.o lib/libfdt/fdt_addresses.o
- 找到了脚本的位置,我自己尝试copy c文件了
现在自己做一个目标,小而美的尝试下功能。不知道为什么用if语句不行,只能用shell,如下不会报错,且功能正常
applecp:lib/crc8.o lib/crc16.o lib/crc16-ccitt.o lib/ldiv.o lib/net_utils.o
$(foreach name, $(filter-out %/built-in.o,$^), $(shell
cp -f $(KBUILD_SRC)/$(name:.o=.c) $(CURDIR)/temp
2>/dev/null
cp -f $(KBUILD_SRC)/$(name:.o=.S) $(CURDIR)/temp
2>/dev/null)
)
如下功能正常,但是会报错/bin/sh: 1: Syntax error: ";" unexpected
applecp:lib/crc8.o lib/crc16.o lib/crc16-ccitt.o lib/ldiv.o lib/net_utils.o
$(foreach name, $(filter-out %/built-in.o,$^),
if [ -e $(KBUILD_SRC)/$(name:.o=.S)]; then
$(shell cp $(KBUILD_SRC)/$(name:.o=.S) $(CURDIR)/temp 2>/dev/null);
else
if [ -e $(KBUILD_SRC)/$(name:.o=.c)]; then
$(shell cp $(KBUILD_SRC)/$(name:.o=.c) $(CURDIR)/temp 2>/dev/null);
/bin/false;
fi;
fi;
)
- 自己对小目标功能验证通过,那么把小功能移植到Makefile的位置即可。修改点对比,如下图
- 体验一下成果,开始试用。又开了一个vscode,仅导入stm文件夹,专门用来搜索看代码的。若要修改代码,不知道具体路径可以搜索xx.o.cmd里面有具体路径,然后到另外一个大而全的vscode中按此路径找到文件来修改即可完成整个流程。
{
// 在使用搜索功能时,将这些文件夹/文件排除在外
"search.exclude": {
"source/*": true,
},
// 这些文件将不会显示在工作空间中
服务器托管网 "files.exclude": {
"source/*": true,
},
}
四,小结
学以致用乐趣无穷。简单来说我不是为了学习uboot的Makefile而学习,我是为了解决问题而学习,另外目标并不是uboot而是Makefile复习,因为这个技能是通用的,又多掌握了Makefile的几种调试方法。
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net
静态路由 网络实验 拓扑图 初步配置 R1 ip 配置 R2 ip 配置 R3 ip 配置 查看当前的路由表信息 查看路由表信息 配置静态路由 测试 拓扑图 需求:实现 ip 192.168.1.1 到 192.168.2.1 的通信。 初步配置 R1 ip …