Android Framework 批量编译-批量推送 脚本

刚毕业时在某手机厂商任职 Android Framework 研发工程师,工作了一段时间后,深感 Framework 层的编译调试太繁琐,每次都做重复劳动.
因此决定要做一个一揽子解决方案,将整个 编译-打包-安装至手机 的流程用一行命令搞定. 于是写了一年多,写完了这个最终近2000行的脚本.
这是题主从零开始学写 bash 的最初的原因.
现在不再做 Framework 开发了,也再也没哪个脚本写的比这个罗辑更多了.最近整理脚本,觉得不分(xian)享(bai)一下太对不起这脚本了.

需求:

做过 Framework 开发的同学都知道, Framework 层中基本是一个模块一个 Android.mk, 最终的编译生成物有一个的,也有多个的. 编译产物会被放在项目根目录下的固定目录,其路径就对应着这个编译生成物应该推送至手机内的那个路径.
一般的调试流程是:
修改代码 -> 找到模块根目录编译模块 -> 编译 -> 在编译日志中找到编译产物路径 -> 根据编译产物路径将生成物 adb push 到手机
有时一个模块生成多个lib (我记得 installd 模块会生成7个),少推一个手机就起不来了.
有时一个修改涉及多个模块,那上面的步骤就要重复多次.
我们厂当时代码都放在了服务器上,编译完成后,要把编译生成物放在一个中间服务器(便于审核),再从中间服务器下载到本机,再从本机 push 到手机.那这个流程就又延长且繁琐了,最终每一次编译调试都要五分钟左右,还经常性的出错.
另外系统签名肯定是要放在服务器的,要签名就得上传到签名服务器. 某些模块没有签名的话,直接推也会让手机起不来.

最终要实现的脚本要完成下面事情:

  1. 支持指定某个文件为参数,自动找到相应的 Android.mk 编译之
  2. 支持指定多个上述文件.多个文件对应一个 Android.mk 时, duplicate 之. 对应多个时,逐个编译之
  3. 分析编译日志,找到所有编译产物路径
  4. 分析编译产物路径,并用 adb push 逐个将编译产物推送至手机

由于我们厂又多了个中间服务器中转过程,在以上步骤3之后的需求改为如下:

  1. 将解析得到的信息汇总,生成 adbpush.sh 的脚本,内含各个编译产物要推送到手机内的路径
  2. 将编译产物及 adbpush.sh 打包,并上传至中间服务器
  3. 本机编写 adb_auto_push.sh 脚本,从中间服务器下载上一步的压缩包
  4. 解压并对需要签名的模块签名(到签名服务器)
  5. 执行压缩包内的 adbpush.sh 脚本,将编译产物推送至手机

基于中转服务器,又写了一个脚本,实现轮询中转服务器(每5秒),有新的符合规则的压缩包则下载并推送.这样看起来在服务器转一手看起来就和在本机编译体验一样了.
这个脚本最终完成后,极大的提高了相关工作效率和准确度.

代码:

公司中转服务器的那个脚本就不贴了,这里只贴在本机编译 Framework 并推送的那个脚本.
已经离开Framework相关工作太久,逐一分析太费时间了,这里就偷懒只贴代码了.
这个脚本其实是不限工作环境的,对于现在的 Android 版本,应该稍加修改也还可以用.

#!/bin/bash

#       AUTHOR : liuxu
#       THIS SHELL IS ENVIRONMENT INDEPENDENT

#v1.0   compile and push libs to phone
#       $1 should be file or dir you wish to compile
#       without $1 this sh will compile the current dir
#v1.1   add error code for exit
#       1:     pre-compile error
#       2:     adb error
#       3:     post-compile error, most commonly caused by various compile error
#       4:     adb errors, may be caused by "device not found", not critical
#       5:     compile error
#v1.2   add -t option: touch file before compile. the sh will decide which file to touch, usually the last modified file.
#v2.0   2012-03-28
#       add support for multi-dir/file param
#       add -l option: use the given num as "lunch" command param
#v2.1   2012-04-12
#       add support for "choosecombo" when setting up compile environment
#v2.2   2012-04-19
#       add -d option: enable DEBUG
#v2.3   2012-07-03
#       debug: when LocateAndroidmk() return $PROJECT_PATH, exit sh, or it will run "make" in project root dir.
#v2.4   2012-11-28
#       1. another way to locate $PROJECT_PATH:
#          run "source build/envsetup.sh", if the return value is 0, than the $PROJECT_ROOT dir is located.
#v3.0   2013-01-17
#       this sh is now independent to environment. changes:
#       1. another way to locate $PROJECT_PATH:
#          just follow "gettop": see if "build/core/envsetup.mk" and "Makefile" exists.
#       2. use getopt to process params.
#       3. change the way of process params to let this shell be less dependent to environment.
#       4. when compile many files, print info after compile even if one of the files failed to compile.
#          this is to ensure that we know which modules has already been compiled.
#v3.1   2013-02-01
#       debug: locate wrong project root dir sometimes
#v3.2   2013-02-05
#       add -u option:
#       make update-api after compile
#v3.3   2013-04-15
#       print current time before exit

#====================================
#global variables

#I've exported this var through .bashrc. so just commit the code here. 
#DEFAULT_CONFIG_TAG="Config"

CURRENT_PATH=`pwd`                  #
PROJECT_NAME=                       #
PROJECT_PATH=                       #
LUNCH_NUM=
ERROR_NUM=0                         #mark for error number if an error occure, also can be used as "exit" return number
B_TOUCH_ENABLED="false"             #mark for use touch command before compile
B_ADB_REMOUNT="false"
B_COMPILE_SUCCEED="true"
B_PUSH_TO_PHONE="true"
B_UPDATE_API="false"
DEBUG="false"

SH_DOC=$(dirname $0)"/sh_document"
SH_DOCUMENT=$SH_DOC/document$(date +%m%d)
COM_LIBS_STORE=$SH_DOCUMENT/compiled_store  #used to store compiled file path
TMP_DIR=/tmp/compile_and_push_info          #temp file to store mmm output info

#this array should follow the below format:
#src_file:::compiled_lib:::phone_dir
declare -a MODULE_ARR               #modules generated by compile
MODULE_ARR_LENGTH=${#MODULE_ARR[*]}

#this array should follow the below format:
#src_file:::mk_path
declare -a SRC_ARR                  #src file or dir
SRC_ARR_LENGTH=${#SRC_ARR[*]}

#this array is used to cache duplicated src file
#if two src_file have the same mk_path, one of them is marked as duplicated
#element should be path based on PROJECT_PATH
declare -a DUPLICATED_SRC_ARR
DUPLICATED_SRC_ARR_LENGTH=${#DUPLICATED_SRC_ARR[*]}

#====================================
#util functions

trap "CLEAR_WORK" EXIT

DEBUG() {
    if [ "$DEBUG" == "true" ]; then
        $@
    fi
}

CLEAR_WORK() {
    if [ -e $TMP_DIR ]; then
        sudo rm -rf $TMP_DIR
    fi

    local current_time="`date +%x`  `date +%T`"
    echo "* Time on exit:  $current_time"
    echo
}

function ShellHelp() {
cat < /dev/null
    while true; do
        #DEBUG echo "LocateAndroidmk, path: $path; ret path: $ret_path"
        if [ -f $ret_path/Android.mk ]; then
            break
        elif [ "$ret_path" == "/" ]; then
            echo "/"
            return
        fi
        cd ..
        ret_path=$(pwd)
    done

    cd $cur_path > /dev/null
    echo $ret_path
}

#locate project root dir
#$1 should be a full path of a file or dir
#without $1 this function will check current path
#yield the path if found, or "/" if not found
function LocateProjectRoot() {
    local path
    local cur_path=$(pwd)
    local tmp_path
    local prj_path
    
    if [ $# -eq 0 ]; then
        path=$(pwd)
    elif [ -f $1 ]; then
        path=$(dirname $1 | xargs readlink -f)
    else
        path=$(readlink -f $1)
    fi

    tmp_path=$path
    cd $tmp_path > /dev/null
    while true; do
        if [ -f build/core/envsetup.mk -a -f Makefile ]; then
            break
        elif [ "$tmp_path" == "/" ]; then
            echo "/"
            return
        fi
        cd ..
        tmp_path=$(pwd)
    done

    cd $cur_path > /dev/null
    prj_path=$tmp_path
    if [ $? -eq 0 ]; then
        echo $prj_path
    else
        echo "/"
    fi
}

function PrintVariableInfo() {
    [ "$DEBUG" == "false" ] && return

    echo "===================================="
    echo "PROJECT_NAME=$PROJECT_NAME"
    echo "PROJECT_PATH=$PROJECT_PATH"
    echo "LUNCH_NUM=$LUNCH_NUM"
    echo "B_TOUCH_ENABLED=$B_TOUCH_ENABLED"
    echo "B_PUSH_TO_PHONE=$B_PUSH_TO_PHONE"
    echo "B_ADB_REMOUNT=$B_ADB_REMOUNT"

    for src in ${SRC_ARR[*]}; do
        echo "SRC_ARR : $src"
    done

    for module in ${MODULE_ARR[*]}; do
        echo "MODULE_ARR : $module"
    done
    echo "===================================="
}

#====================================
#env related functions

#get touch file between two dirs
#only find files in $1 and superior dir of $1, until we reach $2
#$1 and $2 should be full path of a dir
#$1 should be subdir of $2
function PickTouchFile() {
    local ret=""
    if [ ! -d $1 -o ! -d $2 ]; then
        echo $ret
        return
    fi

    local begin_file=$(readlink -f $1)
    local end_file=$(readlink -f $2)

    if [[ "$begin_file" != "$end_file"* && ! $begin_file -ef $end_file ]]; then
        echo $ret
        return
    fi

    local bn; local suffix
    local current_dir=$begin_file
    local end_dir=$(cd $end_file/.. ; pwd)

    while [ ! $end_dir -ef $current_dir ]; do
        DEBUG echo "DEBUG, search in $current_dir"
        cd $current_dir
        local tmp_arr=$(find $current_dir -maxdepth 1 -type f)
        if [ ${#tmp_arr[*]} -eq 0 ]; then
            #no file type in current dir, continue
            continue
        fi
        touch_file_arr=($(find $current_dir -maxdepth 1 -type f | xargs ls -1t | sed '/\/.git\//d; /Android.mk/d' | sed -n '1,10'p))

        if [ "$DEBUG" == "true" ]; then
            for tfa in ${touch_file_arr[*]}; do
                echo "DEBUG, SrcArrAdd, touch_file_arr : $tfa"
            done
        fi

        for fff in ${touch_file_arr[*]}; do
            bn=$(basename $fff)
            suffix=${bn#*.}
            if [ "$suffix" == "c" -o \
                 "$suffix" == "cpp" -o \
                 "$suffix" == "h" -o \
                 "$suffix" == "java" -o \
                 "$suffix" == "xml" -o \
                 "$suffix" == "rc" ]; then
                ret=$fff
                echo $ret
                return
            fi
        done

        current_dir=$(cd .. ; pwd)
    done

    unset touch_file_arr
    echo $ret
    return
}

#add element to SRC_ARR
#src_file:::mk_path
#src_file & mk_path are paths based on $PROJECT_PATH
#$1 should be full path of src_file
function SrcArrAdd() {
    DEBUG echo "+++++++++++++++++++++++++++"
    DEBUG echo "DEBUG, SrcArrAdd, \$1=$1"
    if [ ! -e $1 ]; then
        echo
        echo "* SrcArrAdd, $1 does not exist. should be full path of a dir or file."
        return 1
    fi

    local mk_path=$(LocateAndroidmk $1)
    DEBUG echo "DEBUG, SrcArrAdd, mk_path=$mk_path"
    
    if [ "$mk_path" == "/" ]; then
        echo
        echo "* SrcArrAdd, could not locate a validate Android.mk for the below path, pls check."
        echo $1
        echo
        ERROR_NUM=1
        exit $ERROR_NUM
    fi
    
    local touch_file
    #touch the newest file in $1
    #if no file exist, touch the newest file in mk_path
    if [ "$B_TOUCH_ENABLED" == "true" ]; then
        if [ -f $1 ]; then
            touch_file=$1
        else
            #cache an arr of the 10 newest files
            #so that if the first newest file could not be used as touch file, use the second, etc.
            touch_file=$(PickTouchFile $1 $mk_path)
            #exit the shell here if DEBUG and TOUCH is enabled, or else it will bring more unnecessary errors
            DEBUG exit $ERROR_NUM
        fi

        DEBUG echo "DEBUG, SrcArrAdd, for $1, touch_file=$touch_file"
        if [ -f $touch_file ]; then
            touch $touch_file
        else
            echo "* fail to locate a proper file to touch."
            echo "* possibly cd to a more specified dir (sub dir of current dir) will solve the problem."
            echo "* it is best if you would use a file type as param."
            ERROR_NUM=1
            exit $ERROR_NUM
        fi
    fi

    local param_full_path=$(readlink -f $1)
    local pj_mk_path=$(echo $mk_path | awk -F "$PROJECT_PATH/" '{print $2}')
    local pj_src_file=$(echo $param_full_path | awk -F "$PROJECT_PATH/" '{print $2}')

    SRC_ARR[$SRC_ARR_LENGTH]="$pj_src_file:::$pj_mk_path"
    SRC_ARR_LENGTH=${#SRC_ARR[*]}
    DEBUG echo "+++++++++++++++++++++++++++"
}

#====================================
#compile related functions

#setup compile environment
function SetupEnv() {
    if [ ! -f $PROJECT_PATH/$PROJECT_NAME.$DEFAULT_CONFIG_TAG ]; then
        echo
        echo "* cannot find .config file !!!"
        ERROR_NUM=1
        return $ERROR_NUM
    fi

    cd $PROJECT_PATH > /dev/null
    . $PROJECT_PATH/build/envsetup.sh

    if [ "$LUNCH_NUM" == "" ]; then
        LUNCH_NUM=$(sed -n '/^DEFAULT_LUNCH_NUM/'p $PROJECT_PATH/$PROJECT_NAME.$DEFAULT_CONFIG_TAG | awk -F "=" '{print $2}')
        COMBO=$(sed -n '/^DEFAULT_COMBO/'p $PROJECT_PATH/$PROJECT_NAME.$DEFAULT_CONFIG_TAG | awk -F "=" '{print $2}')
        if [ ! "$LUNCH_NUM" == "" ]; then
            lunch $LUNCH_NUM
        elif [ ! "$COMBO" == "" ]; then
            choosecombo $COMBO
        else
            echo
            echo "* no validate option for 'lunch' command. use -l to specify a lunch number."
            ShellHelp
            ERROR_NUM=1
            return $ERROR_NUM
        fi
    fi
    
    cd - > /dev/null
}

#add element to MODULE_ARR
#src_file:::compiled_lib:::phone_dir
#src_file & compiled_lib are paths based on $PROJECT_PATH
#$1 should be full path of src_file
#$2 should be full path of compiled_lib
function ModuleArrAdd() {
    if [ ! -e $1 ]; then
        echo
        echo "* ModuleArrAdd, $1 does not exist. should be full path a file"
        return 1
    fi

    local pj_src_file=$(echo $1 | awk -F "$PROJECT_PATH/" '{print $2}')
    local pj_compiled_lib=$(echo $2 | awk -F "$PROJECT_PATH/" '{print $2}')

    local product_name=$(echo $pj_compiled_lib | awk -F "/" '{print $4}')
    local file_basename=$(basename $2)
    local phone_dir=$(echo $2 | awk -F "out/target/product/$product_name" '{print $2}' | awk -F "$file_basename" '{print $1}')

    MODULE_ARR[$MODULE_ARR_LENGTH]="$pj_src_file:::$pj_compiled_lib:::$phone_dir"
    MODULE_ARR_LENGTH=${#MODULE_ARR[*]}
}

#compile all elements in SRC_ARR and decipher compile info
#src_file:::compiled_lib:::phone_dir
function DecipherCompileInfo() {
    local mk_dir; local pj_file ;local tmp_file
    [ -d $TMP_DIR ] || mkdir $TMP_DIR
    local compiled_lib_arr
    local uniq_mk_arr
    local uniq_mk_arr_length
    local is_mk_uniq="true"

    for n in ${SRC_ARR[*]}; do
        mk_dir=$(echo $n | awk -F ":::" '{print $2}')
        pj_file=$(echo $n | awk -F ":::" '{print $1}')
        tmp_file=$(basename $PROJECT_PATH/$pj_file)"_compile_info_"$(date +%m%d%H%M%S)

        #duplicate src_file with the same mk_path
        DEBUG echo "DEBUG, mk_dir : $mk_dir ; uniq_mk_arr : ${uniq_mk_arr[*]}"
        for uma in ${uniq_mk_arr[*]}; do
            if [ $PROJECT_PATH/$uma -ef $PROJECT_PATH/$mk_dir ]; then
                is_mk_uniq="false"
                DUPLICATED_SRC_ARR[$DUPLICATED_SRC_ARR_LENGTH]=$pj_file
                DUPLICATED_SRC_ARR_LENGTH=${#DUPLICATED_SRC_ARR[*]}
                break
            fi
        done

        DEBUG echo "DEBUG, DUPLICATED_SRC_ARR : ${DUPLICATED_SRC_ARR[*]}"
        DEBUG echo "is_mk_uniq : $is_mk_uniq"
        if [ "$is_mk_uniq" == "false" ]; then
            is_mk_uniq="true"
            continue
        fi

        #compile
        if [ "$mk_dir" == "" -o "$mk_dir" == "/" ]; then
            echo "* Should not compile in root dir of a project !"
            ERROR_NUM=1
            exit $ERROR_NUM
        fi
        echo "--------------------------------------------------------------------------------"
        echo "compile ....."
        echo "src file  : $pj_file"
        echo "mk path   : $mk_dir"
        echo "--------------------------------------------------------------------------------"
        echo
        cd $PROJECT_PATH/$mk_dir > /dev/null
        #TODO: perhaps we should use "make" instead of such command as "mm"
        mm | tee -a $TMP_DIR/$tmp_file
        if [ "$B_UPDATE_API" == "true" ]; then
            echo
            echo "make update-api"
            echo
            cd $PROJECT_PATH
            make update-api
        fi
        cd - > /dev/null
        echo

        #duplicate src_file with the same mk_path
        uniq_mk_arr[$uniq_mk_arr_length]=$mk_dir
        uniq_mk_arr_length=${#uniq_mk_arr[*]}

        #decipher
        compiled_lib_arr=($(cat $TMP_DIR/$tmp_file | sed -n '/^Install: /p' | awk -F ": " '{print $2}'))

        if [ "$DEBUG" == "true" ]; then
            for cla in ${compiled_lib_arr[*]}; do
                DEBUG echo "DEBUG, decipher libs from compile info: $cla"
            done
        fi

        if [ ${#compiled_lib_arr[*]} -eq 0 ]; then
            B_COMPILE_SUCCEED="false"
            echo "--------------------------------------------------------------------------------"
            echo "* Error occur while compile below file or dir. pls check it."
            echo "$PROJECT_PATH/$pj_file"
            echo
            JustShowInfo
            ERROR_NUM=5
            exit $ERROR_NUM
        fi

        if [ ! -d $SH_DOC ]; then
            mkdir $SH_DOC
        fi

        if [ ! -d $SH_DOCUMENT ]; then
            mkdir $SH_DOCUMENT
        fi

        for m in ${compiled_lib_arr[*]}; do
            DEBUG echo "DEBUG, PROJECT_PATH/pj_file : $PROJECT_PATH/$pj_file"
            DEBUG echo "DEBUG, PROJECT_PATH/m       : $PROJECT_PATH/$m"
            ModuleArrAdd $PROJECT_PATH/$pj_file $PROJECT_PATH/$m
            echo $PROJECT_PATH/$m >> $COM_LIBS_STORE
        done
    done
}

#====================================
#adb related functions

function ReadyADBRemount() {
    [ "$UID" = "0" ] && SUDO= || SUDO=sudo
#    if [ -f $PROJECT_PATH/out/host/linux-x86/bin/adb ]; then
#        ADB="$SUDO $PROJECT_PATH/out/host/linux-x86/bin/adb"
#    else
#        ADB="$SUDO /usr/local/bin/adb"
#    fi
    ADB="$SUDO /usr/local/bin/adb"
    DEBUG echo "ADB: $ADB"

    local adb_info1=$($ADB remount | sed -n '$'p)
    local retry_count=1     #just retry once
    local i=0
    while [ $retry_count -ne $i ]; do
        echo "$adb_info1"
        if [ "$adb_info1" == "remount succeeded" ]; then
            B_ADB_REMOUNT="true"
            return
        else
            echo "* retry remount."
            $ADB kill-server
            $ADB root
            adb_info1=$($ADB remount | sed -n '$'p)
        fi
        let i++
    done

    echo "* cannot remount, please push libs manually."
    ERROR_NUM=4
    return
}

function JustShowInfo() {
    local src_file
    local phone_dir                        #to where we will put the compiled file
    local compiled_lib

    if [ $ERROR_NUM -ne 0 -a $ERROR_NUM -ne 4 ]; then
        echo
        echo "* some problem happened while running this sh. better not push any libs to the phone. pls check. ERROR_NUM=$ERROR_NUM"
        echo
        exit $ERROR_NUM
    fi

    if [ $MODULE_ARR_LENGTH -eq 0 ]; then
        echo
        echo "* No module generated, exit."
        echo
        ERROR_NUM=3
        exit $ERROR_NUM
    fi

    #display duplicated src arr
    echo "--------------------------------------------------------------------------------"
    
    if [ $DUPLICATED_SRC_ARR_LENGTH -ne 0 ]; then
        echo
        for ddd in ${DUPLICATED_SRC_ARR[*]}; do
            echo "* Duplicate Src File : $ddd"
        done
    fi

    for n in ${MODULE_ARR[*]}; do
        src_file=$(echo $n | awk -F ":::" '{print $1}')
        compiled_lib=$(echo $n | awk -F ":::" '{print $2}')
        phone_dir=$(echo $n | awk -F ":::" '{print $3}')

        if [ "$compiled_lib" == "" ]; then
            echo "* no .so or .jar etc. was generated from below file or dir. pls check it."
            echo "$src_file"
            continue
        fi

        echo
        echo "* Project name           : $PROJECT_NAME"
        echo "* Src File or Dir        : $src_file"
        echo "* File Got From Compile  : $compiled_lib"
        echo "* using below command to push to phone: "
        echo "sudo adb push $PROJECT_PATH/$compiled_lib $phone_dir"
        echo
        echo "--------------------------------------------------------------------------------"
    done
}

function PushToPhone() {
    local src_file
    local phone_dir                        #to where we will put the compiled file
    local compiled_lib

    if [ $ERROR_NUM -ne 0 -a $ERROR_NUM -ne 4 ]; then
        echo
        echo "some problem happened while running this sh. better not push any libs to the phone. pls check. ERROR_NUM=$ERROR_NUM"
        echo
        exit $ERROR_NUM
    fi

    if [ $MODULE_ARR_LENGTH -eq 0 ]; then
        echo
        echo "* No module to be pushed, exit."
        echo
        ERROR_NUM=3
        exit $ERROR_NUM
    fi

    #display duplicated src arr
    if [ $DUPLICATED_SRC_ARR_LENGTH -ne 0 ]; then
        echo
        for ddd in ${DUPLICATED_SRC_ARR[*]}; do
            echo "* Duplicate Src File : $ddd"
        done
    fi

    for n in ${MODULE_ARR[*]}; do
        src_file=$(echo $n | awk -F ":::" '{print $1}')
        compiled_lib=$(echo $n | awk -F ":::" '{print $2}')
        phone_dir=$(echo $n | awk -F ":::" '{print $3}')

        if [ "$compiled_lib" == "" ]; then
            echo "* no .so or .jar etc. was generated from below file or dir. pls check it."
            echo "$src_file"
            continue
        fi

        echo
        echo "* Project name           : $PROJECT_NAME"
        echo "* Src File or Dir        : $src_file"
        echo "* File Got From Compile  : $compiled_lib"

        if [ "$B_ADB_REMOUNT" == "false" ]; then
            echo "* adb remount fail. pls push libs manually. using below command: "
            echo "sudo adb push $PROJECT_PATH/$compiled_lib $phone_dir"
            echo
            continue
        else
            echo
            echo "pushing ..."
            echo "$PROJECT_PATH/$compiled_lib  -->  $phone_dir"
            echo
        fi

        if [ -n "$compiled_lib" -a -n "$phone_dir" ]; then
            $ADB push $PROJECT_PATH/$compiled_lib $phone_dir
        else
            echo "* empty string caused by unknown error."
            echo
            ERROR_NUM=3
            return
        fi
    done
}

#====================================
#process args and opts

#process options
function ProcessOptions() {
    while getopts ":txdul:" opt; do
        DEBUG echo "opt: $opt"
        case "$opt" in
            "t")
                B_TOUCH_ENABLED="true"
                ;;
            "x")
                B_PUSH_TO_PHONE="false"
                ;;
            "u")
                B_UPDATE_API="true"
                ;;
            "d")
                DEBUG="true"
                ;;
            "l")
                DEBUG echo "DEBUG, ProcessParam, LUNCH_NUM=$LUNCH_NUM"
                if [ $(IsInterger $OPTARG) == "true" ]; then
                    LUNCH_NUM=$OPTARG
                else
                    echo "* you need to specify a digit value for option -l"
                    ShellHelp
                    ERROR_NUM=1
                    exit $ERROR_NUM
                fi
                ;;
            "?")
                #Unknown option
                echo "* unknown option: $opt"
                ShellHelp
                ERROR_NUM=1
                exit $ERROR_NUM
                ;;
            ":")
                #an option needs a value, which, however, is not presented
                echo "* option $OPTARG needs a value, but it is not presented"
                ShellHelp
                ERROR_NUM=1
                exit $ERROR_NUM
                ;;
            *)
                #unknown error, should not occur
                echo "* unknown error while processing options and params"
                ShellHelp
                ERROR_NUM=1
                exit $ERROR_NUM
                ;;
        esac
    done
    return $OPTIND
}

#process params
function ProcessParams() {
    DEBUG echo "params: $@"
    if [ $# -eq 0 ]; then
        #no params, just print help
        ShellHelp
        #return 1 here to avoid father operation such as "adb reboot"
        exit 1
    fi

    local pj_root
    local pj_name
    for param in $@; do
        #process and store params into array
        if [ -e $param ]; then
            local full_path=$(readlink -f $param)
            pj_root=$(LocateProjectRoot $full_path)
            DEBUG echo "+++++++++++++++++++++++++++"
            DEBUG echo "ProcessParams, param     : $param"
            DEBUG echo "ProcessParams, pj_root   : $pj_root"
            DEBUG echo "+++++++++++++++++++++++++++"
            
            if [ "$pj_root" == "/" ]; then
                echo
                echo "* target dir is not in any project!"
                ShellHelp
                ERROR_NUM=1
                exit $ERROR_NUM
            fi

            if [ "$PROJECT_NAME" == "" ]; then
                PROJECT_NAME=$(basename $pj_root)
                PROJECT_PATH=$pj_root
            else
                if [ ! "$pj_root" == "$PROJECT_PATH" ]; then
                    echo
                    echo "* for now we do not support compile in different projects. pls compile them one by one."
                    ERROR_NUM=6
                    exit $ERROR_NUM
                fi
            fi
            SrcArrAdd $param

        else
            echo "* param does not exist! $param"
            ShellHelp
            ERROR_NUM=1
            exit $ERROR_NUM
        fi
    done
}

#====================================
#main

ProcessOptions "$@"
param_start=$?
ProcessParams "${@:$param_start}"
unset param_start
    
SetupEnv
DEBUG PrintVariableInfo

DecipherCompileInfo

echo
echo "--------------------------------------------------------------------------------"
echo

if [ "$B_PUSH_TO_PHONE" == "true" ]; then
    ReadyADBRemount
    PushToPhone
else
    JustShowInfo
fi

exit $ERROR_NUM

另: 这个脚本成型于4年前,很多地方处理的并不成熟,
比如找 git 项目根目录的办法
比如 sed 命令的使用
比如 true false 变量的处理
等等等等. 即使是截止到写这篇文章的时间点, 题主的bash水平依然有很多不足之处.
说人话就是: 学无止境,欢迎指教,和平讨论~.

你可能感兴趣的:(Android Framework 批量编译-批量推送 脚本)