注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

configure脚本执行及常识  

2011-09-25 16:23:32|  分类: 脚本语言 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

一、bash基本语法

在bash中,引号绝对是一大特色,很多的引号经常搞得大家晕头转向的,所以对于引号的理解是理解bash脚本的一个重要的入口文件和基础。在整个bash文件中。

Bash手册中关于转义的说明文档

1、转义符

首先说明的是转义一个字符的反斜杠,这里的说明是在没有更外层引号的情况下的一个说明,如果有更外层的引号,那么整个意义将会在对应的引号情况下进行说明。这个转义最为强烈,它除了所有UNIX类的逻辑换行功能外,其它的情况下这个字符后面的字符将会被作为字面量使用。也就是转义该字符的特殊功能,例如$的元字符功能,空格的单词分割功能等。它的转义功能最为强烈,但是一次只能覆盖一个字符

3.1.2.1 Escape Character
A non-quoted backslash ‘\’ is the Bash escape character. It preserves the literal value ofthe next character that follows, with the exception of newline. If a \newline pair appears,and the backslash itself is not quoted, the \newline is treated as a line continuation (thatis, it is removed from the input stream and effectively ignored).

2、单引号

单引号,这个转义也非常强烈,相对于反斜杠,它的覆盖范围更加的广泛。和单引号的单字符相比,它可以覆盖到另一个单引号匹配的位置。在这个引号内,反斜杠也无法转义。可以说,在这个引号内,它的引用最为强烈,是转义符的扩展,包括反斜杠和换行都是字面值,其中不允许任何类型的嵌套和转义
3.1.2.2 Single Quotes
Enclosing characters in single quotes (‘’’) preserves the literal value of each character withinthe quotes. A single quote may not occur between single quotes, even when preceded by abackslash.

3、双引号

这个是一个相对温和的转义,它对立面的空格进行了转义,但是同时还可以识别另外的一些基本元字符。例如美元符号的变量引用,反引号的命令结果展开,历史命令感叹号的展开,还有就是反引号在内部的某些特殊情况下也可以作为转义。注意:在双引号中,单引号并没有特殊意义,它和普通字面一样,不能使用单引号在双引号内禁用上面的展开;反过来,同样不能再单引号内使用双引号来使能展开
3.1.2.3 Double Quotes
Enclosing characters in double quotes (‘"’) preserves the literal value of all characters withinthe quotes, with the exception of ‘$’, ‘‘’, ‘\’, and, when history expansion is enabled, ‘!’.The characters ‘$’ and ‘‘’ retain their special meaning within double quotes (see Section 3.5[Shell Expansions], page 16). The backslash retains its special meaning only when followedby one of the following characters: ‘$’, ‘‘’, ‘"’, ‘\’, or newline. Within double quotes,backslashes that are followed by one of these characters are removed. Backslashes precedingcharacters without a special meaning are left unmodified. A double quote may be quotedwithin double quotes by preceding it with a backslash. If enabled, history expansion willbe performed unless an ‘!’ appearing in double quotes is escaped using a backslash. Thebackslash preceding the ‘!’ is not removed.The special parameters ‘*’ and ‘@’ have special meaning when in double quotes (seeSection 3.5.3 [Shell Parameter Expansion], page 18).

4、反引号

反引号的功能和双引号类似。在其中,单引号同样是字面值,但是它可以嵌套,并且可以展开变量

When the old-style backquote form of substitution is used, backslash retains its literalmeaning except when followed by ‘$’, ‘‘’, or ‘\’. The first backquote not preceded by abackslash terminates the command substitution. When using the $(command) form, allcharacters between the parentheses make up the command; none are treated specially.Command substitutions may be nested. To nest when using the backquoted form, escapethe inner backquotes with backslashes.If the substitution appears within double quotes, word splitting and filename expansionare not performed on the results.

5、变量中使用单引号

单引号的如此的六亲不认,那么如果我们希望在一个变量的定义中使用单引号的话该怎么办呢(因为我们不能再单引号内转义单引号)?
通常的办法是通过字符串的连接。例如 想定义一个变量,它的内容为 That's funny,那么可以通过连接之后转义
Var=That\'s\ funny
如果想使用变量,那么可以定义为
Var='$WHO'\''s $WHAT'。
这一点在configure中会使用这个格式的定义,里面很多的转义都是基于这个基本原理进行的。例如,在最后生成的config.log文件中包含的所有变量定义都会由configure在后面加上一个变量定义的引号,例如 SHELL='/bin/sh',如果一个变量本身就包含了单引号,那么就需要特殊处理了

6、环境变量的导出

默认情况下,在shell中定义的变量时不会自动导出的,如果希望导出则需要添加export关键字,这一点和make中变量的使用方法类似。但是当我们通过.或者说source命令来执行一个脚本的时候,此时同样是在同一个shell中执行的,也就是并没有派生子进程,所以该shell中的所有变量(不需要导出)对它调用的子脚本都是可见的,这一点在调用子configure脚本的时候将会用到这个属性。

下面是测试代码

[tsecer@Harry TsecerBuildTest]$ FOO=BAR
[tsecer@Harry TsecerBuildTest]$ . ./MySh.sh
BAR
[tsecer@Harry TsecerBuildTest]$ sh MySh.sh

[tsecer@Harry TsecerBuildTest]$ cat MySh.sh
echo $FOO

7、注释

在bash中,一个以 # 开始的地方表示一个注释的开始,但是要注意的是,这个字符之前必须是一个空白,(空格,制表符,换行符),否则它之后的也并不是注释

[tsecer@Harry TsecerBuildTest]$ echo hehe#haha
hehe#haha
[tsecer@Harry TsecerBuildTest]$ echo hehe # haha
hehe

二、configure中常用的一些工具

1、sed、awk等

这些是都是一些常用工具,有专门的说明手册,在这里不能一一展开,重要的就是正则表达式。

对于awk在生成的config.status中有使用到。这个工具的开始可以定义一个

BEGIN {statement}

也就是在awk执行的时候将会首先执行这个block中的命令。然后它和perl一样,里面内置了一些数组,这些数组的定义方式和perl中变量的定义方式相同,就是可以通过

ARRAY["VAR"]=VAL

的方式不断的在其中定义数组变量,并且数组的下标是变量,这一点在C语言中是没有的。

2、expr
`STRING : REGEX'
     Perform pattern matching.  The arguments are coerced to strings and the second is considered to be a (basic, a la GNU `grep')  regular expression, with a `^' implicitly prepended. The first argument is then matched against this regular expression.
     If the match succeeds and REGEX uses `\(' and `\)', the `:'expression returns the part of STRING that matched the subexpression; otherwise, it returns the number of characters  matched.如果模式匹配成功,则返回正则表达式中\(和\)中匹配的字符串内容,否则返回为空
     If the match fails, the `:' operator returns the null string if `\(' and `\)' are used in REGEX, otherwise 0.
     Only the first `\( ... \)' pair is relevant to the return value;additional pairs are meaningful only for grouping the regular     expression operators.只有第一个锚点有效,其他的锚点只是为了将操作符进行优先级调整
     In the regular expression, `\+', `\?', and `\|' are operators which respectively match one or more, zero or one, or separate alternatives.  SunOS and other `expr''s treat these as regular characters.  (POSIX allows either behavior.)

下面是一个configure参数解析中经常要使用的一种提取变量值的方法
  case $ac_option in
  *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;这是在configure开始的时候提取参数的一个操作,它就是将第一个等号之后的内容赋值给ac_optarg变量中,注意,是第一个等号,这里不是默认的贪婪匹配
  *) ac_optarg=yes ;;
  esac
3、dirname

将一个路径转换为文件夹路径的形式,这个可以通过文件名获得一个文件夹的位置。这个在推算source和build文件夹位置时使用

 Print  NAME  with its trailing /component removed; if NAME contains no /’s, output ‘.’ (meaning the current directo)

4、set

set最后指定的参数作为和命令行中指定的变量形式相同,这样便于通过shift、$#之类的内置命令进行字符串的处理

The remaining N arguments are positional parameters and are assigned, in order, to $1, $2, . . . $N. The special parameter # is set to N. The return status is always zero unless an invalid option is supplied.

如果没有指定参数,则会显示出所有已定义的变量,并且按照本地字典序排列

If no options or arguments are supplied, set displays the names and values of allshell variables and functions, sorted according to the current locale, in a formatthat may be reused as input for setting or resetting the currently-set variables.

5、trap

当程序退出时,系统的变量此时都已经确定,所以就可以通过这种方式向系统做一些善后总结工作,例如config.log的写入工作。

trap [-lp] [arg] [sigspec ...]
The commands in arg are to be read and executed when the shell receivessignal sigspec. If arg is absent (and there is a single sigspec) or equal to ‘-’,each specified signal’s disposition is reset to the value it had when the shell wasstarted. If arg is the null string, then the signal specified by each sigspec isignored by the shell and commands it invokes. If arg is not present and ‘-p’has been supplied, the shell displays the trap commands associated with eachsigspec. If no arguments are supplied, or only ‘-p’ is given, trap prints the listof commands associated with each signal number in a form that may be reusedas shell input. The ‘-l’ option causes the shell to print a list of signal names andtheir corresponding numbers. Each sigspec is either a signal name or a signalnumber. Signal names are case insensitive and the SIG prefix is optional. If asigspec is 0 or EXIT, arg is executed when the shell exits. If a sigspec is DEBUG,the command arg is executed before every simple command, for command,case command, select command, every arithmetic for command, and beforethe first command executes in a shell function. Refer to the description of theextglob option to the shopt builtin (see Section 4.2 [Bash Builtins], page 39)for details of its effect on the DEBUG trap. If a sigspec is ERR, the command argis executed whenever a simple command has a non-zero exit status, subject tothe following conditions. The ERR trap is not executed if the failed commandis part of the command list immediately following an until or while keyword,part of the test in an if statement, part of a && or || list, or if the command’sreturn status is being inverted using !. These are the same conditions obeyedby the errexit option. If a sigspec is RETURN, the command arg is executedeach time a shell function or a script executed with the . or source builtinsfinishes executing.

三、configure的常用参数及命令行处理方式

1、configure命令行带参参数处理

ac_prev=
for ac_option 由于之后没有指定参数,所以遍历的就是所有的命令行参数
do

  # If the previous option needs an argument, assign it.如果说前一个选项需要参数,那么在这个地方开始进行赋值
  if test -n "$ac_prev"; then
    eval "$ac_prev=\$ac_option" 这里是ac_option,也就是整个命令行参数直接赋值给ac_prev变量表示的值
    ac_prev=
    continue
  fi

  case "$ac_option" in
  -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;通过expr来讲命令行中xxx=yyy中的yyy放入变量ac_optarg变量中,供之后变量赋值时使用
  *) ac_optarg= ;;
  esac

对于enable类型参数的处理

-enable-* | --enable-*)参数可以以一个或者两个横线开始,但是不能没有
    ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`取得enable之后,=号之前的字符串
    # Reject names that are not valid shell variable names.
    if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
      { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
    fi
    ac_feature=`echo $ac_feature| sed 's/-/_/g'`将所有的中划线转换为下划线
    case "$ac_option" in
      *=*) ;;
      *) ac_optarg=yes ;;
    esac
    eval "enable_${ac_feature}='$ac_optarg'" ;;注意,这里的ac_optarg是在循环的最开始赋值的,也就是第一个等号之后的内容,with和without也是如此处理,它们都可以带参数,对于without,它的enable_$feature则是确定值no。也就是with可以带参数,而without不行

2、直接赋值变量处理

 *=*)     对于直接在命令行中写入的变量赋值类型处理
    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`同样是将等号之前内容放入ac_envvar变量中
    # Reject names that are not valid shell variable names.
    expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
      { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
   { (exit 1); exit 1; }; }
    eval $ac_envvar=\$ac_optarg将之前解析出得变量值赋值给envvar
    export $ac_envvar ;;导出该变量,和make一样,命令行中的变量可以导出

3、常用变量

①、--help

这个变量之所以重要,是他可以列出那些可以通过enabel进行修改的,并且子configure的配置也需要通过这里传递下去,所以即使在主configure中没有使用,但是下层的configure也需要使用这些变量,所以我们应该同时查看子configure的help,这里就有一个help的选项值,就是recursive。使用的方法为

configure --help=re

从而将子configure中需要的配置变量打印处理。

  -help | --help | --hel | --he | -h)
    ac_init_help=long ;;
  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
    ac_init_help=recursive ;;
  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
    ac_init_help=short ;;

②目录绝对路径

# Be sure to have absolute directory names.通过命令行指定的目录必须为绝对路径,所以一般执行的命令为

configure --bindir=`pwd`
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
  datadir sysconfdir sharedstatedir localstatedir includedir \
  oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
  libdir localedir mandir
do
  eval ac_val=\$$ac_var
  case $ac_val in
    [\\/$]* | ?:[\\/]* )  continue;;
    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
  esac
  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
   { (exit 1); exit 1; }; }
done

③、优化的处理

该选项在很多的开源工程中没有,但是有些有这个功能,例如oprofile中已经有该功能

# Check whether --enable-optimization was given.
if test "${enable_optimization+set}" = set; then :
  enableval=$enable_optimization; enable_optimization=$enableval
else
  enable_optimisation=yes默认打开,可以通过disable_optimazition来禁用优化,可以看到实现也是比较简单的
fi

if test "$enable_optimization" = "no"; then
 CFLAGS=`echo $CFLAGS | sed 's/-O2//g'`
 CXXFLAGS=`echo $CXXFLAGS | sed 's/-O2//g'`
fi

4、enable-XXX及disable-XXX的区别及处理流程
 -enable-* | --enable-*)
    ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
    # Reject names that are not valid shell variable names.
    if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
      { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
    fi
    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
    case "$ac_option" in
      *=*) ;;这个地方比较有意思。它可以指定enable-xxx或者enable-xxx=no,或者是enable-xxx=yes、或者其它。所以,如果我们要禁止一个功能,也可以用--enable-feature-XX=no来禁止,当然它的另一个特点就是可以带其他类型的参数(例如gcc编译的时候使用的--enable-languages=c,c++等),这一点是disable-feature不能做到的,并且使用disable-xxx输入量比较少,输入量少得话错误就少。
      *) ac_optarg=yes ;;如果没有指定,那么就是yes,这个也是比较重要的一项。
    esac
    eval "enable_${ac_feature}='$ac_optarg'" ;;

 

四、内部变量及流程

1、对于源代码位置的处理

# Find the source files, if location was not specified.
if test -z "$srcdir"; then
  ac_srcdir_defaulted=yes
  # Try the directory containing this script, then its parent.
  ac_prog=$0
  ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`相当于dirname的功能,就是删除最后一个路径分隔符及之后的内容。输入变量时命令行的第一个参数,我们一般通过 ../../configure 来执行,所以就获得了../..这个作为源代码的位置
  test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
  srcdir=$ac_confdir
  if test ! -r $srcdir/$ac_unique_file; then
    srcdir=..
  fi
else
  ac_srcdir_defaulted=no
fi

2、递归help的处理

gcc中的处理

ac_subdirs_all=`cd $srcdir && echo */configure | sed 's,/configure,,g'`精华就在这里,通过通配符列出了这个文件夹下所有直接下层文件夹中包含configure文件的那些文件夹,之后用这个变量来执行其中的configure,更下层的同样使用这种方法来递归

……

    elif test -f "$ac_srcdir/configure"; then
      echo &&
      $SHELL "$ac_srcdir/configure" --help=recursive

3、glibc中对子文件夹的迭代处理

# Iterate over all the sysdep directories we will use, running their
# configure fragments, and looking for a uname implementation.
uname=
for dir in $sysnames; do
  case $dir in
    /*) dest=$dir ;;
    *)  dest=$srcdir/$dir ;;
  esac
  if test -r $dest/configure; then
    { echo "$as_me:$LINENO: result: running configure fragment for $dir" >&5
echo "${ECHO_T}running configure fragment for $dir" >&6; }
    . $dest/configure
  fi

gcc中没有找到,可能更多的shift放在了Makefile中来完成这个递归工作,这样依赖的控制和计算可以更加的准确。

4、module的特殊处理

在gcc的文件夹中可以编译时放置一些gcc需要的文件,从而它可以在运行的时候按需进行编译,此时就需要对Makefile.in进行动态的修改。在gcc的Makefile中,里面定义了一种新的语法,就是 @if   lable @endif label,从而利用sed的区间匹配来进行文件的删除

@if target-libstdc++-v3-bootstrap
# Override the above if we're bootstrapping C++.
POSTSTAGE1_CXX_EXPORT = \
 CXX="$(STAGE_CC_WRAPPER) $$r/$(HOST_SUBDIR)/prev-gcc/g++$(exeext) \
   -B$$r/$(HOST_SUBDIR)/prev-gcc/ -B$(build_tooldir)/bin/ -nostdinc++ \
   -I$$r/prev-$(TARGET_SUBDIR)/libstdc++-v3/include/$(TARGET_SUBDIR) \
   -I$$r/prev-$(TARGET_SUBDIR)/libstdc++-v3/include \
   -I$$s/libstdc++-v3/libsupc++ \
   -L$$r/prev-$(TARGET_SUBDIR)/libstdc++-v3/src/.libs"; export CXX; \
 CXX_FOR_BUILD="$$CXX"; export CXX_FOR_BUILD;
@endif target-libstdc++-v3-bootstrap

在gcc的configure文件中

host_libs="intl mmalloc libiberty opcodes bfd readline tcl tk itcl libgui zlib libcpp libdecnumber gmp mpfr mpc ppl cloog libelf libiconv"

gcc将尝试在代码中搜索这些文件夹,它们可能都是gcc编译时需要的文件。

  extrasub_target="$extrasub_target
/^@if target-$module\$/d 如果文件夹下存在一些特定的文件夹,例如make,bash,以及gcc编译时必须的GMP等,则删除这个@if 和 @endif,从而避免在接下来的区间删除中搜索到这个内容,从而也相当于保留了这一段区间中的内容
/^@endif target-$module\$/d
/^@if target-$module-$target_bootstrap_suffix\$/d
/^@endif target-$module-$target_bootstrap_suffix\$/d"
done

# Do the final fixup along with target modules.
extrasub_target="$extrasub_target
/^@if /,/^@endif /d"如果上面没有删除这个label,那么这个@if 和 @endif之间的内容要被全部删除

五、生成文件

1、config.log

这个文件大部分就是通过刚才说的trap来生成的

# When interrupted or exit'd, cleanup temporary files, and complete
# config.log.  We remove comments because anyway the quotes in there
# would cause problems or look ugly.
# WARNING: Use '\'' to represent an apostrophe within the trap.
# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
trap 'exit_status=$?
     cat <<\_ASBOX
## ----------------- ##
## Output variables. ##
## ----------------- ##
_ASBOX
    echo
    for ac_var in $ac_subst_vars
    do
      eval ac_val=\$$ac_var
      case $ac_val in
      *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; 这里华丽的转义就是为了转义变量定义中的单引号。
      esac
      echo "$ac_var='\''$ac_val'\''"
    done | sort
    echo

    if test -n "$ac_subst_files"; then
      cat <<\_ASBOX
' 0 注册的是0号信号,也就是当程序退出的时候执行的命令
2、config.status

这个是一个脚本文件,运行这个脚本将会对一个文件进行配置。这样的优点就是下次执行的时候会比较快。

在这个文件中使用了一个awk的语法,也就是对于宏定义在 D 数组中,而对于文件定义于F中,头文件定义于H中,从而在config.status进行特殊处理。这个地方就体现出了。只有ac_subst_vars中列出的变量才会被放置在D中,否则即使通过命令行传入也对系统不会有任何影响

# configure is writing to config.log, and then calls config.status.
# config.status does its own redirection, appending to config.log.
# Unfortunately, on DOS this fails, as config.log is still kept open
# by configure, so config.status won't be able to write to it; its
# output is simply discarded.  So we exec the FD to /dev/null,
# effectively closing config.log, so it can be properly (re)opened and
# appended to by config.status.  When coming back to configure, we
# need to make the FD available again.
if test "$no_create" != yes; then
  ac_cs_success=:
  ac_config_status_args=
  test "$silent" = yes &&
    ac_config_status_args="$ac_config_status_args --quiet"
  exec 5>/dev/null
  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false 在configure文件的最后同样是调用config.status进行文件的操作。
  exec 5>>config.log
  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
  # would make configure fail if this is the last instruction.
  $ac_cs_success || as_fn_exit $?
fi
我们看一个config.status中的片段

535 echo 'BEGIN {' >"$tmp/subs1.awk" &&
 536 cat >>"$tmp/subs1.awk" <<\_ACAWK &&
 537 S["LTLIBOBJS"]=""
 538 S["LIBOBJS"]=""
 539 S["RELEASE"]="stable"
 540 S["VERSION"]="2.11.2"
 541 S["mach_interface_list"]=""
 542 S["DEFINES"]=" -D_LIBC_REENTRANT"
 543 S["nopic_initfini"]=""
 544 S["static_nss"]="no"
 545 S["bounded"]="no"
 546 S["omitfp"]="no"
       

  评论这张
 
阅读(2443)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017