posts - 79, comments - 75, trackbacks - 0, articles - 1
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

公告

APK编译时自动带上commit id版本标识

Posted on 2012-09-09 13:39 sinojelly 阅读(...) 评论(...) 编辑 收藏

带有commit id的版本描述

git describe可以显示用户友好的版本描述:
$ git describe --tags
0.1.34-3-gab9f2b0
 
它的含义是:
1、当前HEAD位置前面一个tag是0.1.34。
2、当前HEAD在0.1.34 tag之后3个commit的位置。
3、当前HEAD对应的commit id是ab9f2b0。(g应该表示global,全局唯一的SHA)
 
参见下面的分支图:
$ git lg -5
* ab9f2b0 - (HEAD, origin/master, origin/HEAD, master) exit with status 0 when t
*   c99a2a9 - Merge pull request #210 from egeland/master (9 months ago) <Gabrie
|\  
| * 20ebe7a - Added Norwegian strings (9 months ago) <Frode Egeland>
|/  
* 388aa1d - (0.1.34) release 0.1.34 (9 months ago) <Gabriel Falcao>
 
注:
如果HEAD位置刚好有tag,则只显示tag。
 

版本描述如何传递给应用代码

如果是C/C++,可以在Makefile中定义宏来传递。
那么Java编写的APK如何传递呢?
 
经过研究,较好的一种方式是通过AndroidManifest.xml中的meta-data。
 

在AndroidManifest.xml中增加hw_version的meta-data

    <application>
        <activity>
        </activity>
        <meta-data android:name="hw_version" android:value="0.1.34-3-gab9f2b0" />
    </application>

在Java文件中读取meta-data

import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test_app);

        try {
            ApplicationInfo appInfo = this.getPackageManager()
                                      .getApplicationInfo(getPackageName(), 
                              PackageManager.GET_META_DATA);
            String hwVersion=appInfo.metaData.getString("hw_version");
            ((TextView)findViewById(R.id.hw_version)).setText("Apk version is " + hwVersion);
            Log.i("MyCamera", "The version of this apk is " + hwVersion);
        } catch (Exception ex) {
            Log.i("MyCamera", "Get apk version failed.");
        }
    }

如何在Android编译系统中自动生成hw_version

用sed替换版本号

首先固定在AndroidManifest.xml中默认设置hw_version为no-version。
在编译的时候用sed命令替换成正确的版本描述。
$ sed -n 's/<meta-data android:name="hw_version" android:value=".*"/<meta-data android:name="hw_version" android:value="abcdef"/p' AndroidManifest.xml
        <meta-data android:name="hw_version" android:value="abcdef" />

修改Android.mk自动查询版本描述并更新AndroidManifest.xml

在Android.mk中 include $(BUILD_PACKAGE) 之前加上:
LOCAL_HW_VERSION := $(shell git --git-dir=$(LOCAL_PATH)/.git describe --tags)
$(shell sed -i 's/<meta-data android:name="hw_version" android:value=".*"/<meta-data android:name="hw_version" android:value="$(LOCAL_HW_VERSION)"/' $(LOCAL_PATH)/AndroidManifest.xml)
 

自动生成应用版本号

Android应用的版本号一般定义在AndroidManifest.xml中,以android:versionName="1.1"的形式标识。
修改版本号,一般要修改该xml文件并上库。
 
参见前面sed替换版本描述的方法,可以根据git仓最近的tag替换versionName。
比如,保留tag和相对tag的commit数量作为版本号:
$ git describe --tags
0.1.34-3-gab9f2b0
 
如果当前HEAD位置就有tag,则得到的结果类似下面这样:
$ git describe --tags
0.1.34
 
可以参考下面的sed正则表达式匹配只使用满足条件的tag:(数字.数字.数字,分别为V版本、R版本、C版本)
$ echo "0.1.34-3-gab9f2b0" | sed -n -e "/^[0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\(-[0-9]\{1,\}-g[0-9a-f]\{7,\}\)\{0,1\}$/p"
0.1.34-3-gab9f2b0
$ echo "0.1.34" | sed -n -e "/^[0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\(-[0-9]\{1,\}-g[0-9a-f]\{7,\}\)\{0,1\}$/p"
0.1.34
 
该正则表达式的说明如下:
^ 匹配字符串开始
[0-9]\{1,\}  匹配1个或多个数字
\.   匹配点。
\(-[0-9]\{1,\}-g[0-9a-f]\{7,\}\)\{0,1\}  commit相关信息出现0次或者1次。
$ 匹配字符串结束。
 
为了用户更容易理解,可去掉最后面的commit id,并把相对于tag位置的commit数量以点连接起来,为了避免没有commit id的时候末尾是个点,可总是补上一个0:
$ echo "0.1.34-3-gab9f2b0" | sed -n -e "s/^\([0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\)\(-\([0-9]\{1,\}\)-g[0-9a-f]\{7,\}\)\{0,1\}$/\1\.0\3/p"
0.1.34.03
 
当按照上述方式得到的结果不为空,才替换versionName.
 
把这个思路实现到Android.mk中:
 
LOCAL_HW_VERSION_NAME := $(shell echo $(LOCAL_HW_VERSION) | sed -n -e "s/^\([0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\)\(-\([0-9]\{1,\}\)-g[0-9a-f]\{7,\}\)\{0,1\}\$$/\1\.0\3/p")

ifneq (,$(strip $(LOCAL_HW_VERSION_NAME)))
$(shell sed -i 's/android:versionName=".*"/android:versionName="$(LOCAL_HW_VERSION_NAME)"/' $(LOCAL_PATH)/AndroidManifest.xml)
endif
 
说明:
Makefile中的$需要重复进行转义。
 

Makefile修改重构优化

 
这么复杂而且可复用的处理直接写在每个模块的Android.mk中显然是不好的,最好把它提取成函数,需要的地方调用一次即可。
于是,把它提取到 build/core/.mk 中,内容如下:
###########################################################
## Modify version info in AndroidManifest.xml
###########################################################

# Usage:
#   1. git tag n.m.x on the significant commit. (n, m, x are digitals)
#   2. call $(modify-hw-version) in Android.mk
#   3. make sure there is android:versionName or hw_version meta-data in AndroidManifest.xml

define modify-hw-version
    $(eval $(generate-hw-version))
endef

# 1. LOCAL_HW_VERSION is like 0.1.34-3-gab9f2b0, 
#             0.1.34  is tag name, 
#             3       is commit count from the tag, 
#             ab9f2b0 is the current commit id.
#
# 2. LOCAL_HW_VERSION_NAME is like 0.1.34.03
define generate-hw-version

$(eval LOCAL_HW_VERSION := $(shell git --git-dir=$(LOCAL_PATH)/.git describe --tags))
$(shell sed -i 's/<meta-data android:name="hw_version" android:value=".*"/<meta-data android:name="hw_version" android:value="$(LOCAL_HW_VERSION)"/' $(LOCAL_PATH)/AndroidManifest.xml)

$(eval LOCAL_HW_VERSION_NAME := $(shell echo $(LOCAL_HW_VERSION) | sed -n -e "s/^\([0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\)\(-\([0-9]\{1,\}\)-g[0-9a-f]\{7,\}\)\{0,1\}\$$/\1\.0\3/p"))
ifneq (,$(strip $(LOCAL_HW_VERSION_NAME)))
$(shell sed -i 's/android:versionName=".*"/android:versionName="$(LOCAL_HW_VERSION_NAME)"/' $(LOCAL_PATH)/AndroidManifest.xml)
endif

endef
 
此方案可圆满解决版本号自动生成的问题,但编译后AndroidManifest.xml被修改。这可能影响后续的git操作。
最好能有编译的后处理,把AndroidManifest.xml还原。