msdos系统源码编译-Android编译系统解读(一)

Android的优势在于它是开源的。 手机、平板厂商可以根据自己的硬件定制自己的手机产品,比如魅族、乐Phone、M9等。因此,我们在定制Android源码时,有必要了解Android的编译过程。

如果你从来没有编译过Android代码,最官方的编译过程是查看Android官网:

不过,这里只是告诉你如何编译一个通用的系统,并没有告诉你详细的细节msdos系统源码编译,让我们按照编译过程来理解。

±---------------------------------------------------------------- -------------------------------------------------- ----------------+

本文使用Android 2.1版本,开发板为华清视通开发的FS_S5PC100 A8开发板。

±---------------------------------------------------------------- -------------------------------------------------- ----------------+

按照Google给出的编译步骤如下:

1> source build/envsetup.sh:加载命令

2>lunch:选择平台编译选项

3> make:执行编译

我们按照编译步骤来分析编译过程的细节,最后添加我们自己平台产品的编译选项。

1. 源构建/envsetup.sh

该命令用于将envsetup.sh中使用的所有命令加载到环境变量中,我们来分析一下。

envsetup.sh中主要命令如下:

msdos系统源码编译_源码编译器_源码编译安装的步骤

这个命令是用来将envsetup.sh里的所有用到的命令加载到环境变量里去,我们来分析下它。
envsetup.sh里的主要命令如下:
function help()                  # 显示帮助信息
function get_abs_build_var()           # 获取绝对变量
function get_build_var()             # 获取绝对变量
function check_product()             # 检查product
function check_variant()             # 检查变量
function setpaths()                # 设置文件路径
function printconfig()              # 打印配置
function set_stuff_for_environment()        # 设置环境变量
function set_sequence_number()            # 设置序号
function settitle()                # 设置标题
function choosetype()               # 设置type
function chooseproduct()              # 设置product
function choosevariant()              # 设置variant
function tapas()                  # 功能同choosecombo
function choosecombo()               # 设置编译参数
function add_lunch_combo()             # 添加lunch项目
function print_lunch_menu()            # 打印lunch列表
function lunch()                 # 配置lunch
function m()                   # make from top
function findmakefile()              # 查找makefile
function mm()                   # make from current directory
function mmm()                   # make the supplied directories
function croot()                 # 回到根目录
function cproj()
function pid()
function systemstack()
function gdbclient()
function jgrep()                 # 查找java文件
function cgrep()                  # 查找c/cpp文件
function resgrep()
function tracedmdump()
function runhat()
function getbugreports()
function startviewserver()
function stopviewserver()
function isviewserverstarted()
function smoketest()
function runtest()
function godir ()                 # 跳到指定目录 405
 # add_lunch_combo函数被多次调用,就是它来添加Android编译选项 
# Clear this variable.  It will be built up again when the vendorsetup.sh
 406 # files are included at the end of this file.
 # 清空LUNCH_MENU_CHOICES变量,用来保存编译选项 
 407 unset LUNCH_MENU_CHOICES
 408 function add_lunch_combo()   
 409 {
 410     local new_combo=$1         # 获得add_lunch_combo被调用时的参数
 411     local c
     # 依次遍历LUNCH_MENU_CHOICES里的值,其实该函数第一次调用时,该值为空
 412     for c in ${LUNCH_MENU_CHOICES[@]} ; do 
 413         if [ "$new_combo" = "$c" ] ; then    # 如果参数里的值已经存在于LUNCH_MENU_CHOICES变量里,则返回
 414             return
 415         fi
 416     done
     # 如果参数的值不存在,则添加到LUNCH_MENU_CHOICES变量里
 417     LUNCH_MENU_CHOICES=(${LUNCH_MENU_CHOICES[@]} $new_combo)
 418 }
# 这是系统自动增加了一个默认的编译项 generic-eng
 420 # add the default one here
 421 add_lunch_combo generic-eng    # 调用上面的add_lunch_combo函数,将generic-eng作为参数传递过去
 422 
 423 # if we're on linux, add the simulator.  There is a special case
 424 # in lunch to deal with the simulator
 425 if [ "$(uname)" = "Linux" ] ; then
 426     add_lunch_combo simulator
 427 fi
# 下面的代码很重要,它要从vendor目录下查找vendorsetup.sh文件,如果查到了,就加载它
1037 # Execute the contents of any vendorsetup.sh files we can find.
1038 for f in `/bin/ls vendor/*/vendorsetup.sh vendor/*/build/vendorsetup.sh 2> /dev/null`
1039 do
1040     echo "including $f"
1041    . $f       # 执行找到的脚本,其实里面就是厂商自己定义的编译选项
1042 done
1043 unset f

envsetup.sh的主要功能如下:

1、加载编译时用到的功能命令,如:help、lunch、m、mm、mmm等。

2.增加了两个编译选项:generic-eng和simulator,这两个选项是系统默认选项

3. 在vendor//和vendor//build/目录下找到vendorsetup.sh。 如果存在则加载并执行,并添加厂商自己定义的产品的编译选项。

其实上面的第三项就是将厂商自己定义的产品的编译选项添加到编译系统中,里面的代码是:add_lunch_combo xxx-xxx。

根据前面的内容可以推断,如果想要定义自己的平台产品编译项,简单的方法就是直接在envsetup.sh末尾添加add_lunch_combo myProduct-eng。 当然,这样做并不符合里面代码的初衷。 我们还是老老实实地在vendor目录下创建自己的公司名称,然后在公司目录下新建一个vendorsetup.sh,在上面添加我们自己的产品编译项。

msdos系统源码编译_源码编译安装的步骤_源码编译器

#mkdir vendor/farsight/
#touch vendor/farsight/vendorsetup.sh
#echo "add_lunch_combo fs100-eng" > vendor/farsight/vendorsetup.sh

这样,当我们执行source build/envsetup.sh命令时,我们可以在shell上看到如下信息:

including vendor/farsight/vendorsetup.sh

2、按照android官网的步骤开始执行lunch full-eng

当然,如果执行上面的命令,它仍然会编译通用的eng版本系统,而不是我们定制的系统。 我们可以执行lunch命令,它会复制一个选择菜单并列出可用的编译选项。

如果您按照第一步添加了vendorsetup.sh,您的选项将出现:

You're building on Linux
generic-eng simulator fs100-eng
Lunch menu... pick a combo:
     1. generic-eng
     2. simulator
     3. fs100-eng

其中第三项是我们自己添加的编译项。

Lunch命令是envsetup.sh中定义的命令,用于允许用户选择编译项来定义编译过程中使用的Product和全局变量。

我们还没有解释下面的 fs100-eng 是什么意思。 现在我们来解释一下。 fs100是我定义的产品名称,eng是产品的编译类型。 除了eng之外,还有user和userdebug,分别代表:

eng: 工程机械,

用户:最终用户机器

userdebug:调试测试机

测试:测试机

源码编译安装的步骤_msdos系统源码编译_源码编译器

可见,除了eng和user之外,另外两个通常不能交给最终用户。 记得m8下来的时候,先放出了一些eng工程机msdos系统源码编译,然后用户机拿下来之后,就可以换成工程机了。

那么这四种类型是用来做什么的呢? 其实main.mk中是有说明的,在Android源码中,每个target(也可以看成是一个项目)目录下都有一个Android.mk makefile,并且每个target的Android.mk都有一个类型声明: LOCAL_MODULE_TAGS ,这个TAGS用于指定,编译后当前目标属于该类别。

PS:Android.mk 与 Linux 中的 makefile 不同。 它是Android编译系统定义的makefile,可以轻松编译成:c、c++动态或静态库或可执行程序,或java库或android程序。

好吧,我们来分析一下lunch命令做了什么?

function lunch()
{
    local answer
    # lunch后面直接带参数的情况,则编译项为指定的参数: $1
    if [ "$1" ] ; then
      
        answer=$1
    else
       # lunch后面不带参数的情况,则调用print_lunch_menu函数,打印所有的target product和variant菜单提供用户选择
        print_lunch_menu   
        echo -n "Which would you like? [generic-eng] "
        read answer   # 读取用户选择的结果,注意,这儿可以是数字,也可以是字符串的选项内容
    fi
    local selection=
    # 如果用户在菜单中没有输入任何内容(直接回车),则为系统缺省的generic-eng编译项
    if [ -z "$answer" ]
    then
                   selection=generic-eng
    elif [ "$answer" = "simulator" ]
    then
        # 如果是用户输入的是模拟器:simulator
        selection=simulator
    # 如果answer是菜单中的数字,则获取该数字
    elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")
    then
        if [ $answer -le ${#LUNCH_MENU_CHOICES[@]} ]       # 如果用户输入的菜单数字不合法(超过全部选项数组的元素个数)
        # ${#LUNCH_MENU_CHOICES[@]}是指,取得LUNCH_MENU_CHOICES这个数组的元素个数,LUNCH_MENU_CHOICES[@]指引用数组里的全部元素,以列表返回
        then
            selection=${LUNCH_MENU_CHOICES[$(($answer-$_arrayoffset))]}    #这儿通过$answer - $_arrayoffset来引用用户输入的数字对应的数组LUNCH_MENU_CHOICES中的编译项,其中_arrayoffset这个变量表示,是否是数组下标从0开始,这儿其值为:1
        fi
        # 如果 answer字符串匹配 *-*模式(开头结尾不能为-)
    elif (echo -n $answer | grep -q -e "^[^-][^-]*-[^-][^-]*$")
    then
        selection=$answer
    fi
    #如果selection为空,出错退出
    if [ -z "$selection" ]
    then
        echo
        echo "Invalid lunch combo: $answer"
        return 1
    fi
    # special case the simulator
    if [ "$selection" = "simulator" ]      #如果用户选项为使用模拟器
    then
        # 导出4个环境变量,这4个环境变量将指定编译系统编译为模拟器
        export TARGET_PRODUCT=sim                    #产品变量
        export TARGET_BUILD_VARIANT=eng         #版本型号变量
        export TARGET_SIMULATOR=true                 #编译在模拟器中
        export TARGET_BUILD_TYPE=debug           #类型变量
    else
        # 将 product-variant模式中的product分离出来,sed命令是将-后面的字符串替换为空串,也就是只保留-前面的内容
        local product=$(echo -n $selection | sed -e "s/-.*$//")
        # 检查之,调用关系 check_product()->get_build_var()->build/core/config.mk比较罗嗦,不展开了
        check_product $product
        if [ $? -ne 0 ]
        then
            echo
            echo "** Don't have a product spec for: '$product'"
            echo "** Do you have the right repo manifest?"
            product=
        fi
        # 将 product-variant模式中的variant分离出来,sed命令将-前面的内容替换为空串
        local variant=$(echo -n $selection | sed -e "s/^[^-]*-//")
        # 检查之,看看是否在 (user userdebug eng) 范围内
        check_variant $variant
        if [ $? -ne 0 ]
        then
            echo
            echo "** Invalid variant: '$variant'"
            echo "** Must be one of ${VARIANT_CHOICES[@]}"
            variant=
        fi
        if [ -z "$product" -o -z "$variant" ]       #再次检查两个变量是否为空
        then
            echo
            return 1
        fi
 #  导出4个环境变量(和上面模拟器的对比着看),这里很重要,因为后面的编译系统都是依赖于这里定义的几个变量的
        export TARGET_PRODUCT=$product
        export TARGET_BUILD_VARIANT=$variant
        export TARGET_SIMULATOR=false
        export TARGET_BUILD_TYPE=release
    fi # !simulator
    echo
    # 设置到环境变量,比较多,不再一一列出,最简单的方法 set >env.txt 可获得
    set_stuff_for_environment
    # 打印一些主要的变量, 调用关系 printconfig()->get_build_var()->build/core/config.mk->build/core/envsetup.mk 比较罗嗦,不展开了
    printconfig
}
 

从前面的分析可以看出,lunch命令可以带参数也可以不带参数,最终会导入一些重要的环境变量,从而影响编译系统的编译结果。 导出的变量如下(以实际运行情况为例)

   TARGET_PRODUCT=fs100
        TARGET_BUILD_VARIANT=eng
        TARGET_SIMULATOR=false
        TARGET_BUILD_TYPE=release

执行完上面两步之后,就该执行make命令了。 当然,如果完成了以上两步,执行make后肯定会出现问题。 我们还需要做一些其他的操作,这些操作将在下一篇文章中进行分析。