首页 > 代码库 > 使用crontab进行Android代码的自动更新和构建
使用crontab进行Android代码的自动更新和构建
引子
最近的工作是一个在Android平台上进行开发的项目,我个人基本是不改动Android部分的代码,但是我所在的项目需要使用到Android编译出来的很多目标文件。另一方面,我又不是开发apk等基于通用Android平台的项目,即Android部分的代码是有其他同事在进行维护。那么就会有这样的场景:我需要保持Android部分代码的更新和并构建出来。
编译过整个Android工程的人都知道编译一次的时间大概要30分钟以上(如果你是独占服务器且内存超大,那么请默默走开~),要是整个工程全部进行重编(加上-B参数),则耗时至少在2个小时以上。假设你想要加速编译的过程而使用 -jN参数时,那你就等着被其他同事骂吧(因为会拖的别人完全无法进行任何操作)。
对于我的工作来讲,要求保持Android部分代码的更新,但是时效性并没有太强,即只要Android部分代码有改动了,我这边抽空的时候更新下载并编译出来即可。是的,只需要抽空完成这个操作即可,但是作为一个程序员一定知道的,如果要抽空完成一件事,那么这件事就基本上遥遥无期了。
因此,我就想何不搞一个自动更新并构建的脚本,让这个工作在晚上没人的时候静悄悄的完成,这就有了下面的脚本和这篇文章。
crontab_android-4.2.1_r1.sh
先看一下这个脚本的具体内容:
下面就对该脚本进行分解。
第一部分
#! /bin/bash
之所以把这一行单独拿出来讲解,是因为在Ubuntu下,默认的sh已经被替换成了dash,而dash是一种非常符合POSIX标准的Unix Shell,其带来的影响就是"原先在bash shell 下可以运行的shell script ,会出现一些意想不到的问题,不是100%的兼用。"。而Bash是我们比较熟悉的,其语法相对也比较宽泛。
而这里使用bash的根本原因则是,在编译Android之前,需要使用下述命令来配置环境:
source build/envsetup.sh
而我发现,如果使用默认的sh来解析和执行脚本,那么这一行命令放到脚本中竟然无法执行,错误提示如下:
./crontab_android-4.2.1_r1.sh: 20: ./crontab_android-4.2.1_r1.sh: source: not found
修改成#! /bin/bash后,就可以顺利执行。至于具体bash和dash的区别可以参考《bash与dash的差别》
第二部分
ANDROID_4_2=~/android-4.2.1_r1 cd $ANDROID_4_2 HEAD_VER=`svn log -r HEAD | awk '{if(NR==2)print $1}' | cut -b 2-` BASE_VER=`svn log -r BASE | awk '{if(NR==2)print $1}' | cut -b 2-`
首先需要进入到Android的源码目录,并获取到服务器上的最新版本号和本地的版本号。注意,这里分别是用了HEAD和BASE来表示服务器上的最新版本和当前工作副本的版本。关于HEAD和BASE的解释具体见svn log --help:
-r [--revision] ARG : ARG (some commands also take ARG1:ARG2 range)
A revision argument can be one of:
‘HEAD‘ latest in repository
‘BASE‘ base rev of item‘s working copy
而通过svn log -r HEAD或svn log -r BASE获取的版本信息格式如下:
------------------------------------------------------------------------ r100 | nferzhuang | 2014-12-26 15:06:44 +0800 (Fri, 26 Dec 2014) | 2 lines This is a test version ------------------------------------------------------------------------
因此,需要通过awk读取第二行的第一个字段,并且使用cut截取第2个字符到结尾的部分。(PS:这一小块的处理不是很好,后续需要优化一下)
第三部分
if [ $HEAD_VER -eq $BASE_VER ]; then echo "$DATE: current version($BASE_VER) is the same with svn server($HEAD_VER), no need to build system" >> $CRONTAB_LOG_FOLDER/crontab_log.txt else echo "$DATE: current version($BASE_VER) is different with svn server($HEAD_VER), need to build system" >> $CRONTAB_LOG_FOLDER/crontab_log.txt BUILD_LOG=$CRONTAB_LOG_FOLDER/${ANDROID_4_2##*/}_build_$DATE.log svn up --force > $BUILD_LOG svn st -q | grep ^M | grep -v $NONEED_TO_REVERT | awk '{print $2}' | xargs -i svn revert {} >> $BUILD_LOG source build/envsetup.sh >> $BUILD_LOG make >> $BUILD_LOG 2>& 1 fi
再得到服务器上的最新版本号和本地的版本号后,通过比较它们就知道是否需要进行代码更新,如果不需要则直接记录一下log到指定文件中即可。否则就需要更新代码并进行编译。
这里先讲一下BUILD_LOG,即Log文件的名称是如何组成的。
ANDROID_4_2=~/android-4.2.1_r1 DATE=`date "+%Y-%m-%d"` BUILD_LOG=$CRONTAB_LOG_FOLDER/${ANDROID_4_2##*/}_build_$DATE.log
DATE的获取可以参考date命令的man手册,这里主要描述一下linux中shell截取字符串的方法,具体请参考文章《linux中shell截取字符串方法总结》
使用 ## 号操作符。用途是从左边开始删除最后一次出现子字符串即其左边字符,保留右边字符。用法为##*substr,例如:
str=‘http://www.baidu.com/cut-string.html‘
echo ${str##*/}
得到的结果为cut-string.html,即删除最后出现的"/"及其左边所有字符
注:如果是echo ${str##*//},输出结果与echo ${str#*//}一样,因为str中只有一个"//"字符串
我咋这里主要的作用是截取$ANDROID_4_2字符串的最后一个字段,然后和_build_$DATE.log组装成一个文件名。
下面的一个命令也单独需要讲一下:
svn st -q | grep ^M | grep -v $NONEED_TO_REVERT | awk '{print $2}' | xargs -i svn revert {} >> $BUILD_LOG
先说一下这行代码的作用:讲当前文件夹下所有改动的文件(除$NONEED_TO_REVERT文件)还原。分解一下就是:
- svn st -q:获取一下当前的svn改动的摘要信息,注意此处使用-q参数的作用是不显示不在版本管理中的文件
- grep ^M:过滤出所有M开头的行,即有改动的文件
- grep -v $NONEED_TO_REVERT:在过滤出来的文件中不包括指定的$NONEED_TO_REVERT文件
- awk ‘{print $2}‘:对每一行打印出第二个字段
- xargs -i svn revert {}:对每一行的结果执行svn revert操作,关于xargs -i参数的使用请参考文章《xargs的i参数》
最后一个需要讲解的代码是:
make >> $BUILD_LOG 2>& 1
这里使用2>& 1的作用是:不仅把标准输出的结果重定向到文件中,也把错误输出的结果重定向到文件。具体请参考文章:《2>&1使用》
crontab简述
介绍完了crontab_android-4.2.1_r1.sh脚本的内容,如果让这个脚本按照计划的时间运行起来就是crontab的工作了。先看一下百度百科中对于crontab的定义:
crontab命令常见于Unix和类Unix的操作系统之中,用于设置周期性被执行的指令。该命令从标准输入设备读取指令,并将其存放于"crontab"文件中,以供之后读取和执行。该词来源于希腊语chronos(χρνο),原意是时间。通常,crontab储存的指令被守护进程激活,在后台运行,每分钟检查一次是否有预定的作业需要执行。这类作业一般称为cron jobs。
crontab作业的格式如下:
* * * * * command
分 时 日 月 周 命令
第1列表示分钟1~59 每分钟用*或者 */1表示
第2列表示小时1~23(0表示0点)
第3列表示日期1~31
第4列表示月份1~12
第5列标识号星期0~6(0表示星期天)
第6列要运行的命令
crontab命令的使用方法是:
usage: crontab [-u user] file
-e (edit user‘s crontab)
-l (list user‘s crontab)
简单来讲就是通过-e参数进行编辑,通过-l参数进行查看。具体编辑的方法不讲,下面看一下我的crontab作业表:
#00 00 * * * date >> ~/bak/date.txt 00 00 * * * sh ~/bin/crontab_android-4.2.1_r1.sh &
上面一行是用来进行测试,确保每天凌晨的时候可以开始执行任务,下面的一行就是添加了每天00:00的时候执行crontab_android-4.2.1_r1.sh脚本。
这里为什么使用&来强制进行后台运行,主要的原因是因为该脚本中是一个阻塞的工作,不应该独占crontab任务表的执行。
总结
这一篇博客实际上并不太好,涉及到了crontab、svn、shell等好几个方面,每一个单独拿出来都是厚厚的一本书,在这里我是使用他们组合完成一个自动化的动作。
最后要收的一句就是:能够制造和使用工具是人和动物的本质区别。
使用crontab进行Android代码的自动更新和构建