分享

搭建自己的Android源码仓库 | 飞熊在天

 写意人生 2014-04-20

Android是智能手机市场上炙手可热的开源操作系统,由Google推出后现在已经升级到了4.0版,受到了三星,HTC,摩托罗拉等大手机厂商的支持,最近一两年的增长速度超过了iPhone。

和苹果不一样,Android是开源系统,因此我们每个人都可以下载它的源码,修改,编译,生成自己的系统,然后刷到自己的手机上去。对于喜欢智能手机和DIY的程序员来说,这也是非常有趣的一件事。

Android源码的下载,编译的环境要求,编译步骤在上面的链接里都有,只要按步就班去做就没问题。不过DIYer们是不能满足于只用现成的,我们需要修改系统,而且最好有版本控制,就像Android源码的Repo工具一样。下面我们就来说说如何搭建自己的Repo版本控制服务器。

首先来说说Repo是干什么的。Android是一个非常庞大的项目,里面有很多相对独立的模块,例如Java虚拟机dalvik,例如libc实现bionic,例如浏览器引擎webkit,还有各个厂商的驱动与私有代码,等等。因此,Google将Android划分成多个子项目,每个子项目是一个独立的Git仓库,然后自己用python开发了一个Repo工具来对这几百个子项目(Git仓库)进行管理。

例如你可以通过下面的命令下载repo工具,然后初始化和下载Android的源代码(其中-j16表示使用16个链接同步下载,这段代码是从Android官网上摘录的,页面是Downloading the Source Tree):

1
2
3
4
5
6
7
mkdir ~/bin
PATH=~/bin:$PATH
curl https://dl-ssl.google.com/dl/googlesource/git-repo/repo > ~/bin/repo
chmod a+x ~/bin/repo
repo init -u https://android./platform/manifest
repo sync -j16

从Google下载的Repo的代码很有意思,我们忽略无关的注释,截取开头一段来看:

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/sh
REPO_URL='https://android./tools/repo'
REPO_REV='stable'
magic='--calling-python-from-/bin/sh--'
"""exec" python -E "$0" "$@" """#$magic"
if __name__ == '__main__':
  import sys
  if sys.argv[-1] == '#%s' % magic:
    del sys.argv[-1]
del magic

代码的开头是Shell脚本,但是到了第七行,就神奇滴通过这个命令exec python -E "$0" "$@"…变成python代码,覆盖自身执行了。它利用了Python代码的长字符串”"”标记,使得第7行只会在Shell下执行,在python里就是一个常量字符串,开始的几行代码在Python和shell下都是有效的表达式。

从Google下载的repo只是一个初始化代码,通过repo initrepo sync,它不仅能下载Android源码,也能下载自身的最新代码。

如果我们要搭建自己的源码服务器,首先当然考虑的是Repo,不然我们自己重新构思一个多Git仓库管理的工具,也是非常麻烦的一件事。

下面我们就来看看如何利用Google的repo来管理我们自己的Android源码仓库。

首先我们需要理解repo所管理的Android代码的结构。这些结构都是在.repo/manifest.xml文件中给出的,我们给出一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<manifest>
  <remote name="aosp" fetch="https://android./" />
  <remote name="github" fetch="git://github.com" review="review.cyanogenmod.com" />
  <default revision="refs/heads/ics" remote="github" sync-j="4" />
  <project path="build" name="CyanogenMod/android_build">
    <copyfile src="core/root.mk" dest="Makefile" />
  </project>
  <project path="abi/cpp" name="platform/abi/cpp" remote="aosp" revision="refs/tags/android-4.0.3_r1" />
  <project path="bionic" name="CyanogenMod/android_bionic" />
  ... ...
</manifest>

在这个文件里主要的节点是remote,default和project。其中remote表示的是代码服务器的地址(fetch)和名称(name),另外还可以设定对应的审核服务器(review),当你通过repo向服务器提交代码的时候,会首先提交到审核服务器,通过了之后才能入库。当然,对于我们自己的内部代码服务器来说,一般是没有审核服务器的。

default表示的是当一个project没有设定remote属性时(例如下面的name属性为CyanogenMod/android_bionic的project节点),这个project从什么地址获取和提交代码。例如对于CyanogenMod/android_bionic项目来说,它的remote属性没有标明,因此就直接使用default的remote,即从github(git://github.com)获取代码,对应的分支是ics分支(refs/heads/ics)。

而对于name属性为platform/abi/cpp的项目来说,它则从aosp(https://android./)获取代码,获取的只是android-4.0.3_r1这个标签的代码(refs/tags/android-4.0.3_r1)。另外,对于每个project来说,name对应的是远端服务器地址,path对应的是本地代码地址。例如CyanogenMod/android_bionic这个项目就是要将git://github.com/CyanogenMod/android_bionic的Git仓库的代码同步到本地./bionic目录。除此之外,还要注意的是,我们需要将服务器的Git仓库设置为裸仓库,这样才方便提交代码(git push)。

搞清楚了项目配置文件的结构以后,我们还需要知道如何让repo获取这个配置文件。实际上,repo init的-u参数对应的就是这个项目配置文件的Git仓库地址。这个Git仓库下只需要有一个名为default.xml的文件即可,文件内容即为上述内容。

假设服务器上存放源码的根目录是$REPOROOT,服务器地址是$REPOSVR。因为我们需要同时提供代码下载与提交功能,因此可以通过SSH协议来访问Git仓库。下面是搭建和使用自有Android源码服务器的步骤:

  1. 在$REPOROOT/myandroid目录下创建你需要自己修改的项目的Git仓库,例如假设你想修改recovery项目,则可以执行如下命令:
    1
    2
    cd $REPOROOT/myandroid
    git clone --bare https://android./platform/bootable/recovery android_bootable_recovery.git
  2. 在$REPOROOT/manifest目录下创建项目配置文件。
    1
    2
    3
    4
    5
    6
    cd $REPOROOT
    git init manifest
    cd manifest
    vi default.xml
    git add .
    git commit -m "initial version of manifest"

    由于除了recovery项目以外,其他项目仍然使用Android自己的源代码,因此可以这样写default.xml文件:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <?xml version="1.0" encoding="UTF-8"?>
    <manifest>
      <remote name="aosp" fetch="https://android./" />
      <remote name="myandroid" fetch="." />
      <default revision="master" remote="aosp" sync-j="4" />
      <project path="build" name="platform/build">
        <copyfile src="core/root.mk" dest="Makefile" />
      </project>
      <project path="abi/cpp" name="platform/abi/cpp" />
      <project path="bootable/recovery" name="myandroid/android_bootable_recovery" remote="myandroid" />
      ... ...
    </manifest>
  3. 配置好ssh和用户权限,保证每个用户得到授权以后才能访问源代码,而且不能直接登录服务器,例如可以在服务器上修改对应用户的.profile(debian)或者.bash_profile(ubuntu)文件,在最后加上exit命令(echo "exit" >> ~/.profile)
  4. 每个用户可以通过下面的命令来获取代码(其中$repouser_in_server对应的就是服务器上可以读写源代码目录,即$REPOROOT目录的用户名):
    1
    2
    3
    4
    5
    6
    7
    8
    #首先要设置好git用户与提交代码时的编辑器
    git config --global user.email "yourname@yourcompany.com"
    git config --global user.name "Your Name"
    git config --global core.editor "vi"
    #获取代码
    repo init -u ssh://$repouser_in_server@$REPOSVR:22/$REPOROOT/manifest
    repo sync
  5. 每个用户可以通过下面的命令来在本地创建myname/myfeature分支,修改与提交代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    cd bootable/recovery
    git checkout -b myname/myfeature
    #...
    #修改,构建并测试代码
    #中间可能有git add与git commit
    #但是这些都只会影响到你本地的myname/myfeature分支而已
    #...
    git branch master myandroid/master
    git checkout master
    git merge myname/myfeature
    cd ..
    make
    #...运行并测试通过...
    cd bootable/recovery
    git push myandroid master

为了方便起见,你可以在服务器上创建一个唯一的用户让大家来通过他读写代码,同时要向上面说的那样禁止任何人从远端通过这个用户登录服务器,以保证服务器的安全。你可以通过SSH的RSA证书避免每次登录SSH需要输入密码的麻烦,只需要运行ssh-keygen,然后将生成的~/.ssh/id_rsa.pub文件传给服务器管理员,让服务器管理员通过cat id_rsa.pub >> /home/$repouser_in_server/.ssh/authorized_keys命令添加证书即可。此外,你也需要手工ssh登录一下服务器,以保存服务器的指纹。就像这样,ssh $repouser_in_server@$REPOSVR

这篇文章大量参考了《Git权威指南》这本书,特别是其中的第25章,非常感谢这本书的作者蒋鑫。当然,还需要感谢Android的作者Google,这是必须的。

2012.4.25补充

如果需要禁止任何人向服务器提交新的分支,可以写一个pre-receive钩子脚本(同时设为可执行),这个文件可以放在Git仓库的.git/hooks目录下。在每次Git仓库接收远程git push包之前就会调用这个文件。在pre-receive里代码可以从标准输入读取oldrev newrev refname三个参数,然后根据这三个参数判断是否需要阻止这次推送。如果不阻止就返回0,阻止就返回非零,同时可以通过标准输出打印错误提示信息。

值得注意的是,由于参数里面并没有推动者的信息,因此无法根据推送者判断是否需要阻止推送。下面是一个pre-receive的例子:

1
2
3
4
5
6
7
8
while read oldrev newrev refname
do
  if [ $oldrev = "0000000000000000000000000000000000000000" ]
  then
    echo "You CANNOT push new remote branch"
    exit 1
  fi
done

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多