刚毕业时在某手机厂商任职 Android Framework 研发工程师,工作了一段时间后,深感 Framework 层的编译调试太繁琐,每次都做重复劳动.
因此决定要做一个一揽子解决方案,将整个 编译-打包-安装至手机 的流程用一行命令搞定. 于是写了一年多,写完了这个最终近2000行的脚本.
这是题主从零开始学写 bash 的最初的原因.
现在不再做 Framework 开发了,也再也没哪个脚本写的比这个罗辑更多了.最近整理脚本,觉得不分(xian)享(bai)一下太对不起这脚本了.
需求:
做过 Framework 开发的同学都知道, Framework 层中基本是一个模块一个 Android.mk, 最终的编译生成物有一个的,也有多个的. 编译产物会被放在项目根目录下的固定目录,其路径就对应着这个编译生成物应该推送至手机内的那个路径.
一般的调试流程是:
修改代码 -> 找到模块根目录编译模块 -> 编译 -> 在编译日志中找到编译产物路径 -> 根据编译产物路径将生成物 adb push 到手机
有时一个模块生成多个lib (我记得 installd 模块会生成7个),少推一个手机就起不来了.
有时一个修改涉及多个模块,那上面的步骤就要重复多次.
我们厂当时代码都放在了服务器上,编译完成后,要把编译生成物放在一个中间服务器(便于审核),再从中间服务器下载到本机,再从本机 push 到手机.那这个流程就又延长且繁琐了,最终每一次编译调试都要五分钟左右,还经常性的出错.
另外系统签名肯定是要放在服务器的,要签名就得上传到签名服务器. 某些模块没有签名的话,直接推也会让手机起不来.
最终要实现的脚本要完成下面事情:
- 支持指定某个文件为参数,自动找到相应的 Android.mk 编译之
- 支持指定多个上述文件.多个文件对应一个 Android.mk 时, duplicate 之. 对应多个时,逐个编译之
- 分析编译日志,找到所有编译产物路径
- 分析编译产物路径,并用 adb push 逐个将编译产物推送至手机
由于我们厂又多了个中间服务器中转过程,在以上步骤3之后的需求改为如下:
- 将解析得到的信息汇总,生成 adbpush.sh 的脚本,内含各个编译产物要推送到手机内的路径
- 将编译产物及 adbpush.sh 打包,并上传至中间服务器
- 本机编写 adb_auto_push.sh 脚本,从中间服务器下载上一步的压缩包
- 解压并对需要签名的模块签名(到签名服务器)
- 执行压缩包内的 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水平依然有很多不足之处.
说人话就是: 学无止境,欢迎指教,和平讨论~.