/* 和分析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 <wd@denx.de>
#
#####################################################################
# 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