转载请注明地址:http://blog.csdn.net/zsy2020314/article/details/9312147
/* 和分析makefile一样,分析mkconfig同样注重句法分析 */ ##################################################################### #!/bin/sh -e # 上面这句指定执行该脚本所使用的解释器, -e相当于使用/bin/bash # Script to create header files and links to configure # U-Boot for a specific board. # Parameters: Target Architecture CPU Board [VENDOR] [SOC] # (C) 2002-2010 DENX Software Engineering, Wolfgang Denk <[email protected]> # ##################################################################### # mkconfig脚本文件存在的理由:因为uboot是一个多CPU多架构的统一bootloader,为了完成针 # 对特定目标单板,目标架构的编译,需要给Makefile指明,哪些文件需要编译,相当与从整体上控制 # 需要编译的cpu,单板等。 # 当执行make xxx_config U-boot时就会调用这个mkconfig文件。mkconfig会处理单板信息 # (在boards.cfg文件中定义有各种单板信息,smdkc100_config只是其中一个)并生成单板信息到 # U-boot源码顶层目录/include/config.mk这个文件中(会自动创建)。 # 以make smdkc100_config为例,执行成功后,mkconfig会产生5个变量分别为: # ARCH CPU BOARD VENDOR SOC # arm armv7 smdkc100 samsung s5pc1xx # (ARCH=目标板的CPU架构 CPU=具体使用的CPU型号 BOARD = 目标板名称 SOC = 芯片名称) APPEND=no # 默认创建一个新的配置文件 BOARD_NAME="" # make 执行xxx_config的时候打印输出单板名 TARGETS="" arch="" cpu="" board="" vendor="" soc="" options="" ##################################################################### # SHELL常用内部参数: # $# ----传递给程序的总的参数数目 # $? ----上一个代码或者shell程序在shell中退出的情况,如果正常退出则返回0,反之为非0值。 # $* ----传递给程序的所有参数组成的字符串。 # $n ----表示第几个参数,$1 表示第一个参数,$2 表示第二个参数 ... # $0 ----当前程序的名称 # $@ ----以"参数1" "参数2" ... 形式保存所有参数 # $$ ----本程序的(进程ID号)PID # $! ----上一个命令的PID # 下面对$?多做些说明,当补充shell知识吧 # 切记:$?永远表示shell命令最后一次执行后的退出状态,当函数执行完毕后,如果又执行了其它命 # 令,则$?不再表示函数执行后的状态,而表示其它命令的退出状态. #-a 表示(and)两个条件同时成立 # -eq 表示两数值相等 # -gt 表示n1大于n2,即前面大于后面 # -lt 表示n1小于n2,即前面小于后面 # "\(" "\)"是对圆括号的转义,转成普通圆括号"()"来包裹条件表达式 # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # !!!!注意!!!:从下面开始我们做个约定$0,$1,$2,...我们称$0为第0个变量,$1为第1个变量!!!! # !!!!!这样排号从描述上比较便!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # if[...]中的表达式意思为:如果传递给mkconfig的参数个数($#)等于2个并且第1个变量为 # "-A"($1),则执行出错提示.那么$0,$1,$2,...是什么呢?我们看Makefile中是怎么执行make # xxx_config这个伪目标的. # Makefile中是这么写的: # %_config:: unconfig # @$(MKCONFIG) -A $(@:_config=) # MKCONFIG的值为mkconfig(这是在Makefile里有定义的,自己搜索一下就可以到),$(MKCONFIG) # 就是执行mkconfig脚本,则传入的参数 $0=U-boot源码顶层目录/mkconfig $1 = -A $2 = # $(@:_config),当我们输入的是make smdkc100_config时$(@:_config) = smdkc100,所以# 这个表达式意思是把输入的参数中的字符串"_config"去掉然后返回剩下的部分, # smdkc100_config去掉"_config"后当然就剩下"smdkc100",所以$(@:_config) = smdkc100, # 进而$2 = $(@:_config) = smdkc100。所以很明显,执行@$(MKCONFIG) -A $(@:_config=) # 后,传入给mkconfig的参数的确是有两个并且第1个变量$1=-A,所以if表达式为真然后会执行then # 后面的脚本语句!! if [ \( $# -eq 2 \) -a \( "$1" = "-A" \) ] ; then ######################################################################## # egrep是grep的扩展,egrep支持扩展型的正则表达式。-i参数表示不区分大小写。 # "^[[:space:]]*${2}[[:space:]]"。'^'表示反向选择,'*'是通配符,代表任意(0个或 # 多个)字符*${2}则表示1个以上的任意多个${2}。所以这个表达式的意思是:从boards.cfg文 # 件中搜索符合条件:非空格开头但以空格结尾的包含至少1个"smdkc100"字符串的行.如果搜索 # 不到,则说明不存在对应的单板信息,则会报错说找不到创建目标的规则并退出不再执行脚本。 # !!!说了这么多,总结起来这个if判断表达式所做的工作就是从boards.cfg文件中找到执行 # xxx_config的单板信息,找不到就无法完成单板配置,U-boot编译自然也无法完成!!!! line=`egrep -i "^[[:space:]]*${2}[[:space:]]" boards.cfg` || { echo "make: *** No rule to make target \`$2_config'. Stop." >&2 exit 1 } ######################################################################## # 了解这句的意思前先做些功课吧,看完不用我解释相信你也清楚是干嘛用的了 # 扩展阅读:set,env和export这三个命令都可以用来显示shell变量,其区别是什么? # set 用来显示本地变量 # env 用来显示环境变量 # export 用来显示和设置环境变量 # set 显示当前shell的变量,包括当前用户的变量 # env 显示当前用户的变量 # export 显示当前导出成用户变量的shell变量 # 每个shell有自己特有的变量(set)显示的变量,这个和用户变量是不同的,当前用户变量和你 # 用什么shell无关,不管你用什么shell都在,比如HOME,SHELL等这些变量,但shell自己的 # 变量不同,shell是不同的,比如BASH_ARGC, BASH等,这些变量只有set才会显示,是bash # 特有的,export不加参数的时候,显示哪些变量被导出成了用户变量,因为一个shell自己的变 # 量可以通export “导出”变成一个用户变量。set ${line}意思是设置line中的变量为本地环 # 境变量,变量的集合为line,你可以在这句的下面加上显示本地环境变量的语句echo `set`, # 终端会打印出所有本地变量,找一下就可以发现其中有一句内容是:line = 'smdkc100 arm # # armv7 smdkc100 sansung s5pc1xx'。要是缺少了set ${line}就等着出错吧!(上述结果 # 是基于make smdkc100_config而言的) set ${line} #echo `set` # 删掉echo前面的'#',该语句将会被执行。2012-12-25圣诞节^_^ ####################################################################### # 如果boards.cfg搜索到的单板信息行的变量只有3个,如果有需要的话则把变量${1}作为默认 # 单板名称 [ $# = 3 ] && set ${line} ${1} ########################################################################### # 接上面的if表达式的分支,即如果make smdkc100_config时,即执行 # %_config:: unconfig # @$(MKCONFIG) -A $(@:_config=) # 后传入的参数不是2个或者第1个变量不是-A则执行elif下面的判断 # 如果${MAKEFLAGS+set}${MAKELEVEL+set} = setset则向终端打印警告信息并使用sleep 5 # 来做延时提示,延时一下信息会停留在终端一段时间然后继续往下执行,这样做只是为了让程序猿更 # 容易注意到这个这个警告条目吧,我的想法是这样的,有更好解释的可以告知我,谢谢!"cat << -EOF # 信息 EOF" 是用来显示文本内容的,EOF就是"end of file"的意思实际上正常情况下 # "${MAKEFLAGS+set}${MAKELEVEL+set}" = "setset"是成立的,只是执行if了。所以轮不到 # elif执行! elif [ "${MAKEFLAGS+set}${MAKELEVEL+set}" = "setset" ] ; then # only warn when using a config target in the Makefile cat <<-EOF warning: Please migrate to boards.cfg. Failure to do so will mean removal of your board in the next release. EOF sleep 5 fi ########################################################################### # 如果上面一切正常的话,执行了make smdkc100_config后就会有6个变量传进来,分别是: # smdkc100 arm armv7 smdkc100 sansung s5pc1xx # 此时$# = 6,while [ $# -gt 0 ]的意思就是当S#不为0时循环执行do ... done之间的语句 # shift的作用是使$1=$2,$2=$3,$3=$4….,而原来的$1将丢失。因此while循环的作用是,依次# 处理传递给mkconfig脚本的选项(--,-a,-n,-t,*)。由于我们并没有传递给mkconfig任何的选 # 项,因此while循环中的代码不起作用。具体的处理是比较简单了,既然没用到也就不多废话了。 while [ $# -gt 0 ] ; do case "$1" in --) shift ; break ;; -a) shift ; APPEND=yes ;; -n) shift ; BOARD_NAME="${1%_config}" ; shift ;; -t) shift ; TARGETS="`echo $1 | sed 's:_: :g'` ${TARGETS}" ; shift ;; *) break ;; esac done ########################################################################### # 检查如果传进来的变量个数,如果小于4个则退出,若大于7个也要退出 [ $# -lt 4 ] && exit 1 [ $# -gt 7 ] && exit 1 ########################################################################### # 变量赋值,CONFIG_NAME = smdkc100,${1%_config}意思是将$1的字符串右边拿掉"_config"(如果有的话) CONFIG_NAME="${1%_config}" ########################################################################### # 如果BOARD_NAME(单板名称)不为空则什么都不做,如果为空则BOARD_NAME="${1%_config} # ${1%_config}意思是将$1的字符串右边拿掉"_config"(如果有的话) # 脚本文件开头BOARD_NAME = "",所以执行下句后BOARD_NAME = smdkc100 [ "${BOARD_NAME}" ] || BOARD_NAME="${1%_config}" ########################################################################### # arch = arm # 处理cpu赋值时,先用echo $3读入要处理的信息行,即armv7,armv7会被赋值到$1中,即$1=armv7 # 而$2 = 空,然后awk 'BEGIN {FS = ":"}; {print $1}'意思是以冒号为分隔符,提取出输入 # 行的第一个变量,结果是cpu = armv7。同理spl_cpu = $2,而$2为空,所以spl_cpu为空 arch="$2" cpu=`echo $3 | awk 'BEGIN {FS = ":"} ; {print $1}'` spl_cpu=`echo $3 | awk 'BEGIN {FS = ":"} ; {print $2}'` ########################################################################### # 如果$4的值为'-',则board = ${BOARD_NAME} = "smdkc100"(正常情况下), # 否则board="$4"="smdkc100",为什么这么做呢,其实是为了防止boards.cfg中单板信息不足的 # 情况下还能从ARCH信息中把单板名字提取出来然后复制给board变量。(PS:开发者想得真周到) if [ "$4" = "-" ] ; then board=${BOARD_NAME} else board="$4" fi ########################################################################### # 当传进来的变量数目大于4的时候而且第5个变量不等于"-"时vendor="$5"="samsung" # 当传进来的变量数目大于5的时候而且第6个变量不等于"-"时soc="$6"="s5pc1xx" # 当传进来的变量数目大于6的时候而且第7个变量不等于"-"时执行{..}中的处理 [ $# -gt 4 ] && [ "$5" != "-" ] && vendor="$5" [ $# -gt 5 ] && [ "$6" != "-" ] && soc="$6" [ $# -gt 6 ] && [ "$7" != "-" ] && { # check if we have a board config name in the options field # the options field mave have a board config name and a list # of options, both separated by a colon (':'); the options are # separated by commas (','). # # Check for board name ############################################################# # 检查在选项区域(Options)中是否存在一个单板配置选项,该选项区域可能含有单板名称和选项 # 表。单板名称和选项之间使用':'做分隔符,选项表成员之间则用','做分隔符 # 举例:对于mx51_efikamx单板来说,它的Options为: # mx51_efikamx:MACH_TYPE=MACH_TYPE_MX51_EFIKAMX,IMX_CONFIG=board/genesi/m # x51_efikamx/imximage_mx.cfg即 #"$7"="mx51_efikamx:MACH_TYPE=MACH_TYPE_MX51_EFIKAMX,IMX_CONFIG=board/g # enesi/mx51_efikamx/imximage_mx.cfg" # ${7%:*}:从字符串"$7"右边开始删掉字符,直到遇到(从右部数起的)第一个':' # temp = mx51_efikamx tmp="${7%:*}" # 如果tmp不是空的则配置名称CONFIG_NAME = "$tmp" = "mx51_efikamx" if [ "$tmp" ] ; then CONFIG_NAME="$tmp" fi # 注意!确保Options域中的内容中只有一个':'号 # "${tmp}" != "$7"用这句来做比较,确认上一步处理是否成功 # ${7#*:},这个表达式意思是从字符串"$7"左边开始删掉直到遇到第一个':'为止 # options=${7#*:}=MACH_TYPE=MACH_TYPE_MX51_EFIKAMX,IMX_CONFIG=board/genes # i/mx51_efikamx/imximage_mx.cfg # echo ${options}这句是将options作为sed的处理源 # sed 's:,: :g',开头的's'代表搜索,搜索','然后把搜索到的','替换成空格' ' # TARGETS = MACH_TYPE=MACH_TYPE_MX51_EFIKAMX # IMX_CONFIG=board/genesi/mx51_efikamx/imximage_mx.cfg if [ "${tmp}" != "$7" ] ; then options=${7#*:} TARGETS="`echo ${options} | sed 's:,: :g'` ${TARGETS}" fi } ########################################################################### # 如果变量ARCH的值存在而且变量ARCH的值与变量arch的值不相等的话则配置失败,打印错误信息 # ARCH和arch必须相等! if [ "${ARCH}" -a "${ARCH}" != "${arch}" ]; then echo "Failed: \$ARCH=${ARCH}, should be '${arch}' for ${BOARD_NAME}" 1>&2 exit 1 fi ########################################################################### # 如果options存在则把options信息一并打印出来,否则的话就没必要打印! # 这里就是我们执行make xxx_config后会打印到终端上的信息! if [ "$options" ] ; then echo "Configuring for ${BOARD_NAME} - Board: ${CONFIG_NAME}, Options: ${options}" else echo "Configuring for ${BOARD_NAME} board..." fi ########################################################################### # 如果源码顶层目录(SRCTREE)和存放编译生成文件的目录(OBJTREE)不一致的话就在 # 存放编译生成文件的目录(OBJTREE)建立两个文件include和include2 # 然后进入include2目录中,删除asm文件夹,建立一个 软链接asm,链接指向目录 # ${SRCTREE}/arch/${arch}/include/asm # LNPREFIX多加了一个'/'作为路径,然后退到上层目录进入include文件夹,创建一个名为asm的文# 件夹 if [ "$SRCTREE" != "$OBJTREE" ] ; then mkdir -p ${OBJTREE}/include mkdir -p ${OBJTREE}/include2 cd ${OBJTREE}/include2 rm -f asm ln -s ${SRCTREE}/arch/${arch}/include/asm asm LNPREFIX=${SRCTREE}/arch/${arch}/include/asm/ cd ../include mkdir -p asm else ######################################################################## # 如果二者目录相同的话就直接进入当前目录(此时当前目录就是U-boot源码的顶层目录)的 # include文件夹中删除 asm文件夹,直接建立一个软链接asm,链接指向目 # 录../arch/${arch}/include/asm # 一般我们采用这个分支的方法来编译U-boot,OBJTREE目录就是U-boot源码顶层目录相当于 # ln -s ../arch/arm/include/asm asm cd ./include rm -f asm ln -s ../arch/${arch}/include/asm asm fi ########################################################################### # 删除 asm/arch,这个时候的asm是个软连接,实际上是对链接的目标进行操作,建立软件接的目的就# 是方便程序的编写和编译,提高效率,你写程序的时候总不想常常打一长串的路径吧 rm -f asm/arch ########################################################################### # 如果soc不为空则建立软连接asm/arch,指向${LNPREFIX}arch-${cpu},由于OBJTREE目录就是 # U-boot源码顶层目录,LNPREFIX为空 # 一般soc也不为空,则相当于执行: ln -s arch-armv7 asm/arch if [ -z "${soc}" ] ; then ln -s ${LNPREFIX}arch-${cpu} asm/arch else ln -s ${LNPREFIX}arch-${soc} asm/arch fi ########################################################################### # 如果架构是arm架构,则删除当前目录(此时当前目录是U-boot源码顶层目录/include)下的 # asm/proc目录建立软链接ln -s proc-armv asm/proc if [ "${arch}" = "arm" ] ; then rm -f asm/proc ln -s ${LNPREFIX}proc-armv asm/proc fi ########################################################################### # 生成make的头文件config.mk # ARCH=arm CPU=armv7 BOARD=smdkc100 VENDOR=samsung SOC=s5pc1xx # 加一个exit0(返回成功值),然后这些统统写入config.mk文件中, > config.mk是强行覆盖创建# config.mk然后把数据导入config.mk中 ( echo "ARCH = ${arch}" if [ ! -z "$spl_cpu" ] ; then echo 'ifeq ($(CONFIG_SPL_BUILD),y)' echo "CPU = ${spl_cpu}" echo "else" echo "CPU = ${cpu}" echo "endif" else echo "CPU = ${cpu}" fi echo "BOARD = ${board}" [ "${vendor}" ] && echo "VENDOR = ${vendor}" [ "${soc}" ] && echo "SOC = ${soc}" exit 0 ) > config.mk ########################################################################### # 如果厂商名不为空则BOARDDIR = ${vendor}/${board},vendor=samsung,所以 # BOARDDIR = samsung/smdkc100 if [ -z "${vendor}" ] ; then BOARDDIR=${board} else BOARDDIR=${vendor}/${board} fi ########################################################################### # 创建指定的配置的头文件,默认APPEND=no(本文件开头有赋值),所以执行else分支,创建 # config.h文件 if [ "$APPEND" = "yes" ] # Append to existing config file then echo >> config.h else > config.h # Create new config file fi ########################################################################### # 向config.h中写入信息 echo "/* Automatically generated - do not edit */" >>config.h ########################################################################### # 如果没有Options选项的话,TARGETS为空,for不执行,如果有Options选项,则i=TARGETS中变# 量的个数变量之前已经被处理成以空格符' '分隔,例如:TARGETS = # MACH_TYPE=MACH_TYPE_MX51_EFIKAMX # IMX_CONFIG=board/genesi/mx51_efikamx/imximage_mx.cfg,然后用echo ${i}逐条导入 # sed进行处理,sed '/=/ {s/=/ /;q; } ; { s/$/ 1/; }'的作用是搜索等号'=',然后把 # 等号替换成TAB符' ',接着参数q意思是退出当前动作执行下一个动作,下一个动作用分号';'分隔, # 最后加上#define CONFIG_${i}后写入config.h中。 # "MACH_TYPE=MACH_TYPE_MX51_EFIKAMX"处理成: # "#define CONFIG_MACH_TYPE MACH_TYPE_MX51_EFIKAMX" # "IMX_CONFIG=board/genesi/mx51_efikamx/imximage_mx.cfg"处理成: # "#define CONFIG_IMX_CONFIG board/genesi/mx51_efikamx/imximage_mx.cfg" for i in ${TARGETS} ; do i="`echo ${i} | sed '/=/ {s/=/ /;q; } ; { s/$/ 1/; }'`" echo "#define CONFIG_${i}" >>config.h ; done ########################################################################### # 如果是smdkc100,config.h中会多出以下头文件 # #define CONFIG_SYS_ARCH "arm" # #define CONFIG_SYS_CPU "armv7" # #define CONFIG_SYS_BOARD "smdkc100" # #define CONFIG_SYS_VENDOR "samsung" # #define CONFIG_SYS_SOC "s5pc1xx" echo "#define CONFIG_SYS_ARCH \"${arch}\"" >> config.h echo "#define CONFIG_SYS_CPU \"${cpu}\"" >> config.h echo "#define CONFIG_SYS_BOARD \"${board}\"" >> config.h [ "${vendor}" ] && echo "#define CONFIG_SYS_VENDOR \"${vendor}\"" >> config.h [ "${soc}" ] && echo "#define CONFIG_SYS_SOC \"${soc}\"" >> config.h ########################################################################### # 添加别的头文件,利用cat << EOF >> config.h ...内容... EOF把信息写入config.h中 cat << EOF >> config.h #define CONFIG_BOARDDIR board/$BOARDDIR #include <config_cmd_defaults.h> #include <config_defaults.h> #include <configs/${CONFIG_NAME}.h> #include <asm/config.h> #include <config_fallbacks.h> #include <config_uncmd_spl.h> EOF # 返回成功状态 exit 0