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

本文分享了一位Android Framework研发工程师为解决编译调试繁琐问题,编写的一个近2000行的bash脚本,实现了从编译到安装手机的自动化流程。脚本包括自动编译指定模块、解析编译产物、推送至手机等功能,并针对有中间服务器的情况进行了优化,极大地提高了工作效率和准确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

刚毕业时在某手机厂商任职 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水平依然有很多不足之处.

说人话就是: 学无止境,欢迎指教,和平讨论~.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值