linux configure文件或目录,浅析linux1.0的Configure文件

最近想读一下linux1.0版中关于TCP/IP实现的代码,发现需要看一下其中的Makefile,于是一鼓作气看完了找到的Makefile教程,又发现其中有个配置步骤make config,于是深入看了配置过程中调用的Configure脚本,由于对shell不甚了解,看了两天才把整个脚步的设计思路大致搞清楚了。

Configure脚本供make调用(也可以直接调用该脚本),用来配置内核,它的标准输入是config.in文件,而不是终端,从config.in中读取信息进行配置。而脚本的输出有两个文件,一是include/linux/autoconf.h,二是在当前目录的.config隐藏文件。其实由于配置过程中用户有不同的偏好导致有不同的配置文件,Configure脚本可以根据用户本次的设置生成config.new文件,保存用户最新的设置,原来的config.in被重命名为config.old,config.new被重命名为config.in,下次执行Configure脚本时用户想保持和上次一样的配置,那就一路回车就可以了。不用重新设置。

Configure是从config.in中按行读取输入的,config.in文件中的行有不同的类型,Configure需要分别处理。有以下几种类型(//符号后面的部分,包括这两个符号,是我的注释):

1、 # internal comment

//以#开头的是内部注释,被Configure忽略掉,不处理

2、 : message

//以:开头的是要显示的消息,Configure脚本在处理过程中需要将

//message显示到标准输出

3、 * external comment

//以*开头的是外部注释,这个注释同时会保存到上面提到的两个输出

//文件中

4、 if condition

... commands ...

else

... commands ...

fi

//这个结构说成是条件配置吧,else语句是可选的,这个结构可以嵌套使

//用,condition可以是任何合法的bash表达式,注意,没有then这

//个关键字

5、 bool 'prompt' CONFIG_VARIABLE default

//这种类型的行会提示用户输入一个布尔值,默认的值是defaul(y或

//n),用户的输入被记录在下面

//四个地方:

#  In .config, if `y'

#   CONFIG_VARIABLE = CONFIG_VARIABLE

#  In .config, if `n'

#   # CONFIG_VARIABLE is not set

#

#  In autoconf.h, if `y'

#   #define CONFIG_VARIABLE 1

#  In autoconf.h, if `n'

#   #undef CONFIG_VARIABLE

#

#  In config.in, if `y'

#   bool 'prompt' CONFIG_VARIABLE y

#  In config.in, if `n'

#   bool 'prompt' CONFIG_VARIABLE n

#

#  In the environment of the Configure script, if `y'

#   CONFIG_VARIABLE = y

#  In the environment of the Configure script, if `n'

#   CONFIG_VARIABLE = n

//最后一个地方是当然Configure脚本的执行环境中,设置了这个变量,这会在

//条件配置中需要使用,用来测试CONFIG_VARIABLE变量的值是y还是n。

6、 int 'prompt' CONFIG_VARIABLE default

//这种类型的行会提示用户输入一个数字,默认的值是defaul,用户的输

//入也被记录在下面

//四个地方:

#  In .config

#   CONFIG_VARIABLE = response

#  In autoconf.h

#   #define CONFIG_VARIABLE (response)

#  In config.in

#   int 'prompt' CONFIG_VARIABLE response

#  In the environment of the Configure script

#   CONFIG_VARIABLE = response

7、 exec cmd

//Configure会让shell执行cmd命令的

//一共是七种类型的行。

//下面就是Configure的核心代码了:

//这个函数将读取的输入放到ans变量中,接收两个参数,第一个参数被显示到屏

//幕上.如果用户没有输入,则将第二个参数作为默认输入赋给ans变量。

function readln () {

echo -n "$1"read ans  [ -z "$ans" ] && ans=$2

}

//这个函数处理上面第5种类型的行,接受三个参数:'prompt'和

//CONFIG_VARIABLE和 default

function bool () {

# Slimier hack to get bash to rescan a line.

eval "set -- $1"

ans=""

while [ "$ans" != "y" -a "$ans" != "n" ]; do

readln "$1 ($2) [$3] " "$3"

//$1 ($2) [$3] 是合并的字符串,作为readln的第1个参数

//$3就是default,作为readln的第2个参数 done

//调用readln后,返回值就放在ans中,开始对ans处理

if [ "$ans" = "y" ]; then

echo "$2 = $2" >>$CONFIG

echo "#define $2 1" >>$CONFIG_H

//CONFIG和CONFIG_H两个变量见下文的定义

//上面提到用户输入被放到四个地方,这里只有两个,其余两个

//呢?在下面的代码中会见到

else

echo "# $2 is not set" >>$CONFIG

echo "#undef $2" >>$CONFIG_H

fi

raw_input_line="bool '$1' $2 $ans"

//raw_input_line就保存着用户的偏好配置值(ans),这个

//字符串将会被保持到config.new文件中,

//然后config.new被重命名为config.in,也就是上面在

//第5中类型的行中提到的第3个地方,这里还未真正输出到

//config.new文件。

eval "$2=$ans"

//第5中类型的行中提到的第4个地方,这个语句很重要的,下面

//的代码中有个case语句,其中要对遇到的if后面的表达式进

//行测试,因为我之前把这个语句注释掉了,所以那个表达式一

//直验证是假。

}

//这个函数和bool函数类似,不说了

function int () {

# Slimier hack to get bash to rescan a line.

eval "set -- $1"

ans="x"

while [ $[$ans+0] != "$ans" ]; do

readln "$1 ($2) [$3] " "$3"

done

echo "$2 = $ans" >>$CONFIG

echo "#define $2 ($ans)" >>$CONFIG_H

raw_input_line="int '$1' $2 $ans"

eval "$2=$ans"

}

CONFIG=.tmpconfig

CONFIG_H=include/linux/autoconf.h

//上文见到的两个变量的定义

trap "rm -f $CONFIG $CONFIG_H config.new ; exit 1" 1 2

#

# Make sure we start out with a clean slate.

#

> config.new

echo "#" > $CONFIG

echo "# Automatically generated make config: don't edit" >> $CONFIG

echo "#" >> $CONFIG

echo "/*" > $CONFIG_H

echo " * Automatically generated C config: don't edit" >> $CONFIG_H

echo " */" >> $CONFIG_H

stack=''

branch='t'

//这两个变量用来处理if。。。else。。。fi结构

//下面开始关键的处理代码了

whileread raw_input_line

//从config.in中读取一行到raw_input_line变量中do

# Slimy hack to get bash to rescan a line.

read cmd rest

END_OF_COMMAND

//从raw_input_line变量中取出第一个字符串,赋给cmd,剩

//下的赋给rest.根据前面说的6种不同的行,cmd可能

//是*或:或int或bool或exec或if或else或fi或#,#就不

//处理了

if [ "$cmd" = "*" ]; then

if [ "$branch" = "t" ]; then

//把处在if或else结构中的行称为在if或else子分支中。if

//或else外层的称为父分支

//如果一个分支内的行需要进行处理,前提标志是否进行处理的

//   变量branch=t (true)

//如果该分支的branch=f,则处于该分支的行都不需要处理

//   了,在没有遇到任何if或else结构之前的行都是需要处理

//   的,因此最外层的分支的branch=t         echo "$raw_input_line"

//如果该分支的branch=t,则分支内的行是需要处理的,下面

//几行代码就是按第3中类型的行进行处理的         echo "# $rest" >>$CONFIG

if [ "$prevcmd" != "*" ]; then

echo >>$CONFIG_H

echo "/* $rest" >>$CONFIG_H

//在autoconf.h中的注释是/* */,因此要进行小小的变换,

//对于遇到的第一行注释(prevcmd!=*)要加上/*   else

echo " * $rest" >>$CONFIG_H

//不是第一行注释的话(prevcmd=*)

fi

prevcmd="*" //作为下一个cmd的prevcmd

fi

else       //cmd!=*

[ "$prevcmd" = "*" ] && echo " */" >>$CONFIG_H

//本次处理的cmd不再是*,但刚处理过的那一行有*,说明注释

//结束,因此,要在此处理autoconf.h文件,在最后加上*/  prevcmd=""

case "$cmd" in

//在这里根据不同的cmd用case结构进行处理。

:) [ "$branch" = "t" ] && echo "$raw_input_line" ;;

//这行所在的分支内,如果该分支的branch=t说明该分支内

//的行是需要处理的,因此进行处理

int)  [ "$branch" = "t" ] && int "$rest" ;;

bool) [ "$branch" = "t" ] && bool "$rest" ;;

exec) [ "$branch" = "t" ] && ( sh -c "$rest" ) ;;

if) stack="$branch $stack"       //遇到当前分支(父分支)的

//一个子分支了,

//将当前的分支branch值压栈   if [ "$branch" = "t" ] && eval "$rest"; then

//当前分支(父分支)是t,且子分支if的表达

//式为真则if子分支的branch=t,         branch=t

else                 //当前分支(父分支)是f,或者表达式是f,

//if分支就是f了,不再执行if分支内的那些行

branch=f

fi ;;

else) if [ "$branch" = "t" ]; then

//此时开始处理含有else的行,那么上面的行就是if分支所

//在的行了,条件中的branch还是if属于分支的,上个if分

//支的branch=t,表示if分支执行了,因此这个else分支就

//是f了,不执行

branch=f

else     //之前的if分支内的行没有被处理,两种情况:

//1、if分支的父分支branch=t,但表达式是f,不执行

//   该if分支。暗示会执行else分支

//2、即使表达式是t,有可能是那个if的父分支是f,if

//   分支也不执行,else也不会被处理

//此时这个esle分支是否执行要看父分支是否是t(父

//分支是否要执行),

//如果父分支是t,因为if分支没执行,所以执行

//   else分支(else分支的branch=t)

//如果父分支是f,则else分支肯定不执行(因此else

//   分支的branch=f)

//则在两种情况下,else分支的branch的值和父分支的

//branch的值是相同的,因此对else分支的branch的

//赋值就是下面这条代码:

read branch rest

//如果父分支是f,那么else分支branch=f,

//else语句不执行,如果父分支是t,那么else

//分支的branch=t,就是说if分支因为条件不

//成立,那么else就开始执行了。                 END_OF_STACK

fi ;;

fi) [ -z "$stack" ] && echo "Error!  Extra fi." 1>&2

read branch stack

//弹出栈

END_OF_STACK

;;

esac       //此时才处理完不同的cmd fi

echo "$raw_input_line" >>config.new

//此时的raw_input_line已经在调用bool和int函数时被

//重新赋值了,此时才真正的将这个变量保存到用户的偏好配

//置文件config.new文件中,

done       //到这里,config.in文件中的行已经被处理完,下面开始收

//尾工作了[ "$prevcmd" = "*" ] && echo " */" >>$CONFIG_H

[ -z "$stack" ] || echo "Error!  Untermiated if." 1>&2

mv config.in config.old

mv config.new config.in      //把用户此次配置的选项重新保存到

//config.in中,原来的config.in

//保存到config.old中。下次make

//config时,如果不需要重新配置,一

//路回车就可以了

总结:

关键是对if。。。else。。。fi结构的处理,使用了堆栈,因为不像C里面处理if。。。else结构那样,在处理config.in文件时,即使if分支不成立,还是需要决定处理if分支下面的每一行,因此就需要当前分支的branch变量做标志了,根据这个变量的值决定是否需要处理这些行的具体内容。关键就是如何决定是否处理这个分支,然后对这个分支的branch变量赋值。

另外,在int函数和bool函数的最后eval的使用也是很重要的,处理if分支时,那个if表达式就是需要测试eval的执行结果的。

原始的Configure文件和config.in文件见附件。

rar.gif

文件:

linux1.0.rar

大小:

4KB

下载:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值