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之後的需求改爲如下:

4. 將解析得到的信息彙總,生成 adbpush.sh 的腳本,內含各個編譯產物要推送到手機內的路徑

5. 將編譯產物及 adbpush.sh 打包,並上傳至中間服務器

6. 本機編寫 adb_auto_push.sh 腳本,從中間服務器下載上一步的壓縮包

7. 解壓並對需要簽名的模塊簽名(到簽名服務器)

8. 執行壓縮包內的 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 <<EOF

--------------------------------------------------------------------------------
USAGE:
compile_and_push.sh [-t] [-x] [-d] [-u] [-l lunch_number] PATH

OPTIONS:
-t : touch the the newest or the given file before compile
-l : use the given number as 'lunch' arg
-d : enable DEBUG, the debug logs will be print
-x : do not push libs to phone after compile
-u : make update-api after compile

DESCRIPTION:
Compile a module based on the given files and dirs, and then push the module(s) to the phone.
By default, the compiled modules will be pushed to the phone.
This shell only compile files or dirs in the same project path.
Note that the options should always come before params.

RETURN CODE:
When things' not all right, this sh will return ERROR CODE as below:
  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
  6:     input file in different project dir
--------------------------------------------------------------------------------

EOF
}

#see if $1 is interger or not
#if $2, $3 is presented, see if $1 is inside [$2, $3]
#yield true or false
#if present, $2 and $3 should be interger
function IsInterger() {
    local ret       #return value

    if [[ $1 =~ [0-9]+ ]]; then     #make sure input is interger
        ret="true"
    else
        ret="false"
    fi

    if [ "$ret" == "false" -o $# -eq 1 ]; then
        echo $ret
        return
    fi

    if [[ ( $1 -ge $2 ) && ( $1 -le $3 ) ]]; then      #make sure $n is inside the range
        ret="true"
    else
        ret="false"
    fi

    echo $ret
}

#see if $1 is one of the files below:
# c, cpp, h, java, xml, rc
#yield true or false
#$1 should be a full path of a file
function IsAndroidSrcFile() {
    local ret="false"
    if [ ! -f $1 ]; then
        echo "false"
        return
    fi

    local bn=$(basename $1)
    local suffix=${bn#*.}

    if [ "$suffix" == "c" -o \
         "$suffix" == "cpp" -o \
         "$suffix" == "h" -o \
         "$suffix" == "java" -o \
         "$suffix" == "xml" -o \
         "$suffix" == "rc" ]; then
        ret="true"
    else
        ret="false"
    fi

    echo $ret
}

#locate the nearest Android.mk file in the upper dirs
#$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 LocateAndroidmk() {
    local path
    local cur_path=$(pwd)
    local ret_path

    if [ $# -eq 0 ]; then
        path=$(pwd)
    elif [ -f $1 ]; then
        path=$(dirname $1 | xargs readlink -f)
    else
        path=$(readlink -f $1)
    fi

    ret_path=$path
    cd $ret_path > /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水平依然有很多不足之處.

說人話就是: 學無止境,歡迎指教,和平討論~.


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章