FFmpeg源代码简单分析:configure

本文记录FFmpegConfigure脚本的源代码。Configure一方面用于检测FFmpeg的编译环境,另一方面根据用户配置的选项生成config.makconfig.h文件(可能还有config.asm),提供给Makefile使用。由于FFmpegconfigure脚本很复杂(一个4000-5000行的Shell脚本),难以逐行细致的分析,因此本文简单梳理一下它的结构。

PS1:Configure的日志位于config.log文件中。查看该文件有助于分析Configure的过程。
PS2:使用“sh -x script_name.sh”可以调试Shell脚本。

Configure文件的整体流程

Configure文件的整体流程如下所示。
FFmpeg源代码简单分析:configure_第1张图片

Configure的整体流程可以分成以下几步:
Set Default Value:设置各个变量默认值;
Parse Options:解析输入的选项;
Check Compiler:检查编译器;
die_license_disabled():检查GPL等协议的设置情况;
Check:检查编译环境(数学函数,第三方类库等);
Echo info:控制台上打印配置信息;
Write basic info:向config.mak中写入一些基本信息;
print_config():向config.hconfig.makconfig.asm中写入所有配置信息;
print_enabled():向config.mak写入所有enabled的组件信息;
pkgconfig_generate():向libavXXX/libavXXX.pc中写入pkgconfig信息(XXX代表avcodecavformat等);

下文简单梳理一下这些步骤。

Set Default Value

Set Default Value部分设置一些Configure的默认值。例如下面的代码。

# 默认参数 default parameters
# 日志
logfile="config.log"
 
# 安装路径 installation paths
prefix_default="/usr/local"
bindir_default='${prefix}/bin'
datadir_default='${prefix}/share/ffmpeg'
incdir_default='${prefix}/include'
libdir_default='${prefix}/lib'
mandir_default='${prefix}/share/man'
shlibdir_default="$libdir_default"
postproc_version_default="current"
 
# 工具链 toolchain
ar_default="ar"
cc_default="gcc"
cxx_default="g++"
cc_version=\"unknown\"
host_cc_default="gcc"
install="install"
ln_s="ln -sf"
nm_default="nm"
objformat="elf"
pkg_config_default=pkg-config
ranlib="ranlib"
strip_default="strip"
yasmexe_default="yasm"
nm_opts='-g'
nogas=":"
# 机器 machine
arch_default=$(uname -m)
cpu="generic"
# 操作系统 OS
target_os_default=$(tolower $(uname -s))
host_os=$target_os_default
# alternative libpostproc version
ALT_PP_VER_MAJOR=51
ALT_PP_VER_MINOR=2
ALT_PP_VER_MICRO=101
ALT_PP_VER=$ALT_PP_VER_MAJOR.$ALT_PP_VER_MINOR.$ALT_PP_VER_MICRO
# 选项 configurable options
# PROGRAM_LIST内容是 ffplay ffprobe ffserver ffmpeg
enable $PROGRAM_LIST
enable avcodec
enable avdevice
enable avfilter
enable avformat
enable avutil
enable postproc
enable stripping
enable swresample
enable swscale
enable asm
enable debug
enable doc
enable fastdiv
enable network
enable optimizations
enable safe_bitstream_reader
enable static
enable swscale_alpha
# 编译选项 build settings
SHFLAGS='-shared -Wl,-soname,$$(@F)'
FFSERVERLDFLAGS=-Wl,-E
# 前缀后缀
LIBPREF="lib"
LIBSUF=".a"
FULLNAME='$(NAME)$(BUILDSUF)'
# 名称
LIBNAME='$(LIBPREF)$(FULLNAME)$(LIBSUF)'
# 动态库前缀后缀
SLIBPREF="lib"
SLIBSUF=".so"
# 名称
SLIBNAME='$(SLIBPREF)$(FULLNAME)$(SLIBSUF)'
SLIBNAME_WITH_VERSION='$(SLIBNAME).$(LIBVERSION)'
SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'
SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR) $(SLIBNAME)'
AS_O='-o $@'
CC_O='-o $@'
CXX_O='-o $@'
host_cflags='-D_ISOC99_SOURCE -O3 -g'
host_libs='-lm'
target_path='$(CURDIR)'

需要注意的是,“enable avcodec”,“enable avformat”,“enable avutil”等中的enable()本身是一个函数。enable()的定义如下。

#把所有输入参数的值设置为"yes"
enable(){
    set_all yes $*
}

可以看出enable()调用了set_all()函数。并且将第1个参数设置为“yes”,并且将调用enable()时候的参数传递给set_all()set_all()函数的定义如下所示。

#第一个参数为值,后面的参数为变量
set_all(){
    value=$1
    shift
    for var in $*; do
        eval $var=$value
    done
}

可以看出set_all()将传入的参数全部进行赋值。特定于enable()函数来说,就是将所有的输入变量赋值为“yes”。由此可见,“enable avcodec”实际上相当于执行了:

avcodec=”yes”

Parse Options

Parse Options部分用于解析Configure的附加参数。该部分的代码如下所示。

#注意:opt不是参数列表(实际上也没有看见opt变量的定义)
#原因是处在for循环中,当你没有为in指定列表时,for会默认取命令行参数列表。
#因此“opt”这个名字实际上是可以随便取的
for opt do
# "#"用于去除特定字符前面的字符串
# optval内容为opt去掉"="以及其前面字符串之后的内容
    optval="${opt#*=}"
    case "$opt" in
    # 不同的选项
    --extra-ldflags=*) add_ldflags $optval
    ;;
    --extra-libs=*) add_extralibs $optval
    ;;
    --disable-devices) disable $INDEV_LIST $OUTDEV_LIST
    ;;
    --enable-debug=*) debuglevel="$optval"
    ;;
    --disable-everything)
    map 'eval unset \${$(toupper ${v%s})_LIST}' $COMPONENT_LIST
    ;;
    --enable-*=*|--disable-*=*)
    eval $(echo "${opt%%=*}" | sed 's/--/action=/;s/-/ thing=/')
    is_in "${thing}s" $COMPONENT_LIST || die_unknown "$opt"
    eval list=\$$(toupper $thing)_LIST
    name=$(echo "${optval}" | sed "s/,/_${thing}|/g")_${thing}
    $action $(filter "$name" $list)
    ;;
    --enable-?*|--disable-?*)
    eval $(echo "$opt" | sed 's/--/action=/;s/-/ option=/;s/-/_/g')
    if is_in $option $COMPONENT_LIST; then
        test $action = disable && action=unset
        eval $action \$$(toupper ${option%s})_LIST
    elif is_in $option $CMDLINE_SELECT; then
        $action $option
    else
        die_unknown $opt
    fi
    ;;
    --list-*)
        NAME="${opt#--list-}"
        is_in $NAME $COMPONENT_LIST || die_unknown $opt
        NAME=${NAME%s}
        eval show_list $NAME \$$(toupper $NAME)_LIST
    ;;
    --help|-h) show_help
    ;;
    *)
    #% 就是从右边开始删除符合条件的字符串(符合条件的最短字符串)
    #%%是删除符合条件的最长的字符串
 
    #删除“=”右边的内容
    optname="${opt%%=*}"
    #删除左边的“--”
    optname="${optname#--}"
    optname=$(echo "$optname" | sed 's/-/_/g')
    #看看是否在opt列表中,不在的话就会返回错误
    if is_in $optname $CMDLINE_SET; then
        eval $optname='$optval'
    elif is_in $optname $CMDLINE_APPEND; then
        append $optname "$optval"
    else
         die_unknown $opt
    fi
    ;;
    esac
done

在这里需要注意,取出opt的值一般都是“--extra-ldflags=XXX”的形式,通过“${opt#*=}”截取获得“=”号后面的内容作为optval,对于“--extra-ldflags=XXX”来说,optval取值为“XXX”。

然后根据opt种类的不同,以及optval取值的不同,分别作不同的处理。

你可能感兴趣的:(ffmpeg)