搭建本地AOSP Gerrit Server完全指南 (二)

在这里插入图片描述

前言

本文在引导你如何将AOSP (Android Open Source Project)完整导入企业内部或个人Gerrit伺服器。AOSP包含近800个专案,要如何有效快速无误的将这些专案导入Gerrit对很多IT人员来说若不知方法会是很头痛的问题。

本文是系列文的第二篇,本系列在介绍如何建立本地自有(local host) Gerrit Server并且将AOSP (Android Open Source Project)的原始码完整导入此Gerrit Server。

系列分为三个部份,本文是第二部份,教导如何建立AOSP Git Mirror并且同步导入自建的Gerrit Server,如果你还不知如何架设一个Gerrit Server,可以回到这系列的

第一部份:架设Gerrit Server
第二部份:将AOSP完整导入Gerrit Server (本文)。
第三部份:如何从本地Gerrit Server建立一个AOSP Branch

进行此AOSP导入工作的主要目的在于本人专业工作上需要修改AOSP原始码以符合客制化的需求,但AOSP本身庞大的原始码及代码库(Repository)不适合再导入公司内部原有已存在的Gerrit Server。于是乎,将AOSP导入一个新建的Gerrit Server成为一个合理的选择。另外更重要的原因是,自身内部专案的AOSP原始码必须能够提供以下功能:

  • 进行Code Review,确保稳定性及纪录留存以保留技术知识。
  • 同步Merge Android官方原始码,确保官方修正的问题可以同步合入自身专案。

在完成本文中所述的任务之后,你应该可以:

  • 建立基本Gerrit Server的能力
  • 抓取AOSP并导入Gerrit Server的能力
  • 同步AOSP并且合入官方修改的原始码的能力
  • 建立自身AOSP Branch并且修改及进行Code Review

本文在Ubuntu 18.04进行,基本上我在Ubuntu 16.04也执行过同样任务,所以差别不大,所以你的系统是在Ubuntu 16.04,应该完全可以适用。

所需软体

  • Gerrit (version 2.16.4)
  • Git
  • Open JDK 8
  • Apache2 (非必要)
  • MySQL (非必要)
  • Gerrit Delete Project Plug-in (非必要,但很好用)

概念

AOSP的相关资讯都在Android的官方网站(https://source.android.com/),如果你还不曾完整Build过Android的Codebase (不论是AOSP,Qualcomm还是MTK),那么你必须从:

https://source.android.com/setup/build/requirements

这里开始把Build Code的环境设定好。

AOSP是由一大堆Projects组成(本文选写时,大约七八百个Project),利用repo这个命令管理。如果使用上一篇方法来的一个一个建立Projects,不仅旷日费时,也容易出错。

但如果把repo命令,git mirror概念,gerrit命令的基本功能都弄清楚,则之后的导入同步及错误修正就不会很难。

什么是Git Mirror?

用很简单的直接的讲法就是「完全复制一份出来」。以往利用git clone取得的Project,是针对单一branch取出原始码。而git clone --mirror则是把所有的branches/tags都取出来。就是AOSP上所有的原始码你可以一次性下载。那也就是我们的目的。

所以基本的做法很简单,用以下步骤来完成转移AOSP的工作。

  1. 取得一份完整的AOSP Git Repository (Git mirror)
  2. 把AOSP Git mirror中所有的Projects在Gerrit创建一次。
  3. 把所有的AOSP Git mirror中所有的branches/tags都push进Gerrit Server
  4. 留下AOSP Git Mirror做同步化,官方新的Patch可同步至本地Gerrit Server

概念很清楚简单,但执行起来却有很多细节处理。包含权限设定,错误处理,架构规划,都可能会让你卡住很久。所以我在做第一次转移AOSP的动作时,也花了三天以上的时间。所以本文就是让你能一次性完成,不必走一次一样的冤枉路。

执行AOSP移转汇入本地Gerrit Server

  • 取得AOSP Git Repository (Mirror)

首先取得完整的AOSP Git Repository。取得的位址及方法在以下官网描述

https://source.android.com/setup/build/downloading

在官网中告诉你下载的方式为:repo init -u https://android.googlesource.com/platform/manifest

但这个方式是取得master branch,或是在其后加入-b 以取得其他Branch。
本文要完整取出所有的branch及tags,就必须加入–mirror参数。在本文范例中,我们先在另一台主机Gerrit Client建立一个目录aosp_mirror,存放AOSP mirror

mkdir aosp_mirror
cd aosp_mirror
repo init -u https://android.googlesource.com/platform/manifest --mirror
repo sync

Copy

所以重点在–mirror这个参数,让我们可以完整取得AOSP所有Repository。注意,取得AOSP Repostory必须是在Gerrit Server有Administrator权限的人,在PART1中,此人为larson。所以不一定要在Gerrit Server同一台执行,可以在远端Client (以下称之为Gerrit Client)取回AOSP Repository后,透过git push将原码推至Gerrit Server。最后用repo sync取得原始码,这会花一段时间,我大概花了三个小时下载,因为大概有100 GBytes以上的资料量。

  • 在Gerrit Server建立所有Projects的前期工作

建立所有的Projects之前我们先建立一个Parent Repository–AOSP。(若你还没有Gerrit server及Gerrit Web UI,请先至PART1建立)。
在Gerrit Web UI中,点选Browse->Repositories,点击「CREATE NEW」

在这里插入图片描述

输入名称为AOSP,在「Only server as parent for other repositories」选择True。按下「CREATE」,完成建立。

AOSP只是个Parent Repository,主要是要把所有AOSP下的七百多个Projects在Gerrit上的权限在统一管理。之后加入的Projects将Parent设为AOSP,我们只要在AOSP下调整权限,就可以等同调整所有Projects的权限,同时也不影响其他已存在Gerrit Server的Projects的权限。

然后我们加入两个Group (成员群组),android-admin及android-coder,刚刚加入的Parent Project “AOSP”是把相关的Projects做一个群组,而成员群组也是同样的概念,把人加入某一个成员群组后,该成员的权限就能统一管控。

在Gerrit Web UI中,点选Browse->Groups,点击「CREATE NEW」,输入android-admin,按CREATE,建立群组。

在这里插入图片描述

再重复以上动作一次,加入android-coder。因为创立者是预设member,所以点选android-admin/android-coder后,再点选左侧的Memebers,就可以看到有哪些人在此群组。

android-admin顾名思义,就是管理AOSP Projects的人,包含建立/删除/Merge/Review等管理工作,而android-coder就是把修改的Code推上Gerrit等待Review,请android-admin进行Review后Commit或Abandon。

所以接下来就是设定AOSP的权限:
\1. 在Gerrit Web UI中,点选BROWSE->Repositories,选择或搜寻AOSP。点选进入。
\2. 在左侧栏,点选Access。
\3. 在中央主画面中,点击Edit。开始编辑权限
\4. 此时会有第一个Preference出现,把ref/for/改为ref/
\5. 下拉Add permission:,选择Push,点选右边的Add。此时有个Push权限加入。
\6. 在Push下方的"add group"文字栏中,输入android-admin,按Enter (或输入至一半时,若跳出选单,选android-admin即可)。
\7. 重覆步骤5和6,加入Forge Author Identity和Forge Committer Identity。
\8. 结果如下:

在这里插入图片描述

每个权限的意义可以至官网查询,这里不再重述。以上的权限其实还不足,也不够细致。但主要是在汇入AOSP所有Projects时,不会有权限问题而推入失败。

  • 在Gerrit Server汇入所有Projects

接下来是把七百多个Projects都建在Gerrit Server上。回到Gerrit Client主机上,进入刚刚建立的aosp_mirror目录中。你可以先输入:

repo forall -c 'echo $REPO_PROJECT'

Copy

来看所有的Projects,这个命令repo forall -c就是「在所有Projects执行命令」,而$REPO_PROJECT这变环境变数会随着当前的有不同的值,其值就是Project Name。
所以以上的命令就是把所有的Project Name列出来,而没有对Git/Gerrit做任何操作。

第一步,在Gerrit上新建所有Projects
理解了上述repo forall -c及$REPO_PROJECT变数后,以下的建立Projects的命令就很容易了解。

repo forall -c 'echo $REPO_PROJECT; ssh -p 29418 larson@192.168.100.56 gerrit create-project --owner android-admin $REPO_PROJECT;'

Copy

repo forall -c 'echo $REPO_PROJECT; ssh -p 29418 larson@192.168.100.56 gerrit set-project-parent --parent AOSP $REPO_PROJECT;'

Copy

第一个指令是建立所有Projects;第二个指令是把所有的Projects的Parent Project设为AOSP,所有的AOSP Projects的父专案就是AOSP,将来只要改变AOSP的权限就能设定所有Project存取权限。

repo forall -c是对每个Project做动作,echo是列出目前要重新建立在Gerrit Server的Project Name,如果发生了任何的Error Message,你也会比较容易知道是哪个Project出问题。真正执行建立Project的命令是:ssh -p 29418 larson@192.168.100.56 gerrit create-project --owner android-admin R E P O P R O J E C T ; s s h − p 29418 是用 S S H 的 29418 P o r t 通讯 ( G e r r i t S e r v e r 预设 P o r t ) , l a r s o n 是 A d m i n s t r a t o r u s e r n a m e , 192.168.100.56 是 S e r v e r I P 。 g e r r i t c r e a t e − p r o j e c t 是 G e r r i t 命令,用来建立新的 P r o j e c t , − − o w n e r 是 P r o j e c t 管理者,所以是 a n d r o i d − a d m i n 这个群组。最后 r e p o f o r a l l 替你把 REPO_PROJECT; ssh -p 29418是用SSH的29418 Port通讯(Gerrit Server预设Port),larson是Adminstrator username, 192.168.100.56是Server IP。gerrit create-project是Gerrit命令,用来建立新的Project,--owner是Project管理者,所以是android-admin这个群组。最后repo forall替你把 REPOPROJECT;sshp29418是用SSH29418Port通讯(GerritServer预设Port)larsonAdminstratorusername,192.168.100.56ServerIPgerritcreateprojectGerrit命令,用来建立新的ProjectownerProject管理者,所以是androidadmin这个群组。最后repoforall替你把REPO_PROJECT换成真正的Project Name。
以上的指令应该不会碰上什么困难,就是把700多个Projects整个Looping一次,建立好,设定Parent Project。
此时你可以回到Gerrit Web UI,看一下所有Projects是否成功建立。

在这里插入图片描述

可以看出已经有一大堆Projects列在Gerrit Web UI上了。

第二步,把所有的原始码汇入刚刚建立的Projects:
在执行全部专案原始码汇入之前,回到Gerrit Server主机,修改aosp_review_site/etc/gerrit.config并且重启gerrit service。修改如下:

[gerrit]
        basePath = /home/gerrit/aosp_git
        serverId = 03a2e828-86e2-47ac-abf2-facca5260695
        canonicalWebUrl = http://192.168.100.56:8080/
[database]
        type = mysql
        hostname = localhost
        database = reviewdb
        username = gerrit
[noteDb "changes"]
        disableReviewDb = true
        primaryStorage = note db
        read = true
        sequence = true
        write = true
[container]
        javaOptions = "-Dflogger.backend_factory=com.google.common.flogger.backend.log4j.Log4jBackendFactory#getInstance"
        javaOptions = "-Dflogger.logging_context=com.google.gerrit.server.logging.LoggingContext#getInstance"
        user = gerrit
        javaHome = /usr/lib/jvm/java-8-openjdk-amd64/jre
[index]
        type = LUCENE
[auth]
        type = OPENID
[receive]
        enableSignedPush = false
        maxBatchCommits = 1000000
        timeout = 120min
[sendemail]
        smtpServer = localhost
[sshd]
        listenAddress = *:29418
[httpd]
        listenUrl = http://*:8080/
[cache]
        directory = cache
[plugins]
        allowRemoteAdmin = true

加入第27,28行(Highlighten),maxBatchCommits=1000000是为了解决错误讯息为(more than 10000 commits, and skip-validation not set),而timeout=120m是修正在汇入时,由于Project过于庞大,造成过时问题。做一次汇入的时间很长,所以建议先改好gerrit.config,重启gerrit server (gerrit.sh restart)之后再进入汇入工作。如果不改gerrit.config,在执行汇入时,就会发生类似错误。

repo forall -c 'echo $REPO_PROJECT; git push ssh://larson@192.168.100.56:29418/$REPO_PROJECT +refs/heads/*'
platform/art
Counting objects: 302384, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (107448/107448), done.
Writing objects: 100% (302384/302384), 199.43 MiB | 16.72 MiB/s, done.
Total 302384 (delta 193646), reused 299963 (delta 191268)
remote: Resolving deltas: 100% (193646/193646)
remote: Counting objects: 302384, done
remote: Processing changes: refs: 140, done    
To ssh://192.168.100.56:29418/platform/art
 * [new branch]            idea133 -> idea133
 * [new branch]            idea133-weekly-release -> idea133-weekly-release
 * [new branch]            kitkat-cts-dev -> kitkat-cts-dev
 * [new branch]            kitkat-cts-release -> kitkat-cts-release
 * [new branch]            kitkat-dev -> kitkat-dev
 * [new branch]            kitkat-mr1-release -> kitkat-mr1-release
 * [new branch]            kitkat-mr1.1-release -> kitkat-mr1.1-release
 * [new branch]            kitkat-mr2-release -> kitkat-mr2-release
 * [new branch]            kitkat-mr2.1-release -> kitkat-mr2.1-release
 * [new branch]            kitkat-mr2.2-release -> kitkat-mr2.2-release
 * [new branch]            kitkat-release -> kitkat-release
 * [new branch]            kitkat-wear -> kitkat-wear
 ! [remote rejected]       l-preview -> l-preview (more than 10000 commits, and skip-validation not set)
 ! [remote rejected]       lollipop-cts-release -> lollipop-cts-release (more than 10000 commits, and skip-validation not set)
 ! [remote rejected]       lollipop-dev -> lollipop-dev (more than 10000 commits, and skip-validation not set)
 ! [remote rejected]       lollipop-mr1-cts-release -> lollipop-mr1-cts-release (more than 10000 commits, and skip-validation not set)

可以看出错误讯息为(more than 10000 commits, and skip-validation not set),而maxBatchCommits=1000000这个设定可以解决该问题。
接下来就真正进入Project汇入Gerrit Server的作业了。
对每一个AOSP Project执行git push的命令,将原始码汇入:

repo forall -c 'echo $REPO_PROJECT; git push ssh://larson@192.168.100.56:29418/$REPO_PROJECT +refs/heads/*'
repo forall -c 'echo $REPO_PROJECT; git push ssh://larson@192.168.100.56:29418/$REPO_PROJECT +refs/heads/* +refs/tags/*'

此时视你内部网路及主机的效能而定,但仍需要约30分钟至1小时以上的时间完成整个汇入的动作(花了两三小时才汇入也是正常)。

你可以看出我用了两个很相似的命令去汇入原始码,第一个是只推heads (branchs) 另一个加上tags。这看起来有点瞎,但在性能不太强大的Gerrit Server主机是必须的。否则会由于效能不足(RAM?)而发生internal server error!

下完命令后,大部份的Project都可以顺利被汇入gerrit server,最可能出错的Project是platform/manifest,如果你看到以下错误讯息(internal server error):

To ssh://larson@192.168.100.56:29418/platform/manifest
 ! [remote rejected] adt_23.0.3 -> adt_23.0.3 (internal server error)
 ! [remote rejected] afw-test-harness-1.5 -> afw-test-harness-1.5 (internal server error)
 ! [remote rejected] afw-test-harness-2.1 -> afw-test-harness-2.1 (internal server error)
 ! [remote rejected] afw-test-harness-marshmallow-dev -> afw-test-harness-marshmallow-dev (internal server error)
 ! [remote rejected] afw-test-harness-nougat-dev -> afw-test-harness-nougat-dev (internal server error)
 ! [remote rejected] android-1.6_r1 -> android-1.6_r1 (internal server error)
 ! [remote rejected] android-1.6_r1.1 -> android-1.6_r1.1 (internal server error)
 ! [remote rejected] android-1.6_r1.2 -> android-1.6_r1.2 (internal server error)
 ! [remote rejected] android-1.6_r1.3 -> android-1.6_r1.3 (internal server error)
 ! [remote rejected] android-1.6_r1.4 -> android-1.6_r1.4 (internal server error)
 ! [remote rejected] android-1.6_r1.5 -> android-1.6_r1.5 (internal server error)

以经验而言,Gerrit Server主机端发生问题,很多的状况是RAM不足造成的。但也不要傻傻的就跑去扩充RAM(我的Gerrit Server有8G RAM,但在push manifest时,还是出现internal server error!)。仔细检视platform/manifest这个Projects,很明显它的refs非常多,所以如果这是个造成问题的变数,那我们可以各别ref推入的方式来解决这个问题。
首先回到Gerrit Client,进入aosp_mirror/platform/manifest.git

for n in $(git for-each-ref --format='%(refname)' refs/heads); do git push ssh://larson@192.168.100.56:29418/platform/manifest $n; done
for n in $(git for-each-ref --format='%(refname)' refs/tags); do git push ssh://larson@192.168.100.56:29418/platform/manifest $n; done

一样分两个命令,一个把所有Branch推入,另一个把所有Tags推入。这两个命令就是先列出所有Branch/Tag后,逐个推入,这样就不会有因RAM不足而造成的错误。缺点就是比较花时间。(但一定比去扩充RAM省时省钱,至于RAM有扩到多大才够?我也没试过!当然也有可能是Gerrit的编程时,没考虑如此大量的refs时的状况,所以在gerrit server发生问题时,我们从error log也看不出什么问题,因为也没看见任何Exception!!这只有真的去钻Source Code才能得到答案了。)

  • 验证,取回原始码并且Build出Image在Emulator执行

目前我们取得的是AOSP Master branch,也就是最新的原始码,通常都可以Build过。但如果你就这么刚好在两个Commit中间取出原始码,Build Fail也不是不可能的事。

要验证我们的Gerrit是不是没有问题,第一步就是把AOSP整个取出,之后Build过一次:

repo init -u ssh://larson@192.168.100.56:29418/platform/manifest
repo sync
source ./build/envsetup.sh
lunch aosp_x86_64-eng #官方Build出X86 Emulator的方法
make -j16 #開始執行Build

Build好之后,执行Android Emulator,在Emulator中,Settings->System->About emulated device,检视Build Number,是你Build的日期及user name,也就确认此Emulator是由你自己Build出来的,也确认我们把AOSP汇入再取出后是完整的AOSP Codebase。

在这里插入图片描述

  • 同步官方AOSP

完成AOSP Mirror转移汇入本地Gerrit Server的工作后,不要就把Gerrit Client中的aosp_mirror干掉!因为将来和官方AOSP的同步还要靠它!例如AOSP又出了一个Android Q/R…,或是用到不同平台装置,例如IoT/Car,的Branch。这时如果你必须同步这些Code,就在aosp_mirror下,执行repo sync,把原始码同步后,再汇入你的gerrit server。先回到Gerrit Client的aosp_mirror目录,同步官方的AOSP方式:

repo sync -j8 #同步官方AOSP
repo forall -c 'echo $REPO_PROJECT; ssh -p 29418 larson@192.168.100.56 gerrit create-project --owner android-admin $REPO_PROJECT' #若有新的Project,就建立它,已經存在的Projects會回應Fatal error,這是正常的,無視
repo forall -c 'echo $REPO_PROJECT; ssh -p 29418 larson@192.168.100.56 gerrit set-project-parent --parent AOSP $REPO_PROJECT' #若有新的Project,將Parent設為AOSP
repo forall -c 'echo $REPO_PROJECT; git push ssh://larson@192.168.100.56:29418/$REPO_PROJECT +refs/heads/* +refs/tags/*' #匯入所有新增的Code,完成同步

建立自己的分支Branch

一般而言,在AOSP上工作的研发人员不会在Master branch上修改Code。因为此时的Code未必稳定,而且可能Build不出来,也可能Build出来了,但常发生Crash。Master branch是最经常改Code的地方,如果在这个分枝工作,势必常常面临Merge Code的工作。

所以一般来说我们会在相对稳定的Branch/Tag上,建立自有的分枝后,在自有分枝上工作。这也是我们需要一个自有Gerrit Server并把AOSP完整移转过来的最初原因。
如何建立分枝,可以看PART3,建立AOSP分枝并建立在Gerrit Server

虽然本文是要「完整的」移转汇入AOSP,但事实上我们现在做完的移转是不完整,而且很多Projects其实根本还没汇入。为什么?主要是因为AOSP Master branch并不是包含所有Projects,而是目前最新的Android版本中的Projects。例如,AOSP Master branch在本文发表时,是Android Q,而上个版本Android P中有个Project名称为framework/data-binding,但在Android Q因架构改变,不再纳入此Project。所以我们的Gerrit server没有这个Project。那么如果我要在Pixel 2上的Android P上修改Code,并且把这个修改纳入Gerrit review,成为自己的ROM,及内部知识保留(这也是本文一开始说的目的!)但我的Gerrit server没有framework/data-binding这个Projects,怎么办?那这就是PART3要讨论的部份了!

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值