8

如何搭建android源代码repo仓库 - 河东西望

 1 year ago
source link: https://www.cnblogs.com/aosp/p/16445394.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

如何搭建android源代码repo仓库




.
版本: v0.3
作者:河东西望
日期:2022-7-5
.

如果你的开发是基于AOSP源码来建仓,那么搭建repo服务器和部署自己的repo仓库就是非常必要的工作了。

现实中很多公司都是直接把AOSP源代码放在一个git仓库中来管理,这样做没什么毛病。但是如果开发团队人数较多,会存在一些明显的弊端:

  1. AOSP源代码一般的大小是20GB,编译之后达到40GB以上。每次同步会很耗费时间。
  2. AOSP源代码文件数量将近70W个,单个仓库的文件索引会很庞大,每次检索非常耗时。
  3. AOSP和芯片平台厂商(例如qcom、mtk、rockchip等)的原始代码仓库,一般是以repo仓库结构提供的,而不是单个git仓库。改变仓库结构之后,人工维护的工作量比较大。

AOSP源代码结构复杂庞大,除了本身的代码模块之外,还集成了五六百个第三方仓库,这些仓库也会在不停的更新迭代。AOSP维护整个代码仓库框架就需要保持足够的灵活性,于是就引进了repo脚本来批量管理git仓库。AOSP的源代码切分的git仓库数量多达六七百多个。

如果我们想要跟上游平台仓库保持一致的仓库结构,很方便快捷的同步代码,而我们自己也能灵活的管理本地的扩展仓库,就需要搭建部署自己的repo仓库了。

在实际的团队开发实践中,代码管理框架一般是gerrit+repo的组合形式。gerrit的安装部署和权限管理等不是本文档的内容,请读者搜索网络材料来安装部署。本文档的主要内容包括:

  1. 说明repo仓库的管理策略。
  2. 如何建立清单仓库。
  3. 如何批量切分git仓库。
  4. repo仓库的操作使用。

本文档的服务器操作系统是ubuntu18.04。

1 repo是如何管理仓库的?

1.1 repo如何工作的?

新的操作系统安装包中已经包含了repo命令程序,可以直接通过下面的命令安装:

sudo apt-get install repo

# 查看repo版本
repo --version

<repo not installed>
repo launcher version 2.17
       (from /usr/local/bin/repo)
git 2.34.1
Python 3.10.4 (main, Apr  2 2022, 09:04:19) [GCC 11.2.0]
OS Linux 5.15.0-39-generic (#42-Ubuntu SMP Thu Jun 9 23:42:32 UTC 2022)
CPU x86_64 (x86_64)
Bug reports: https://bugs.chromium.org/p/gerrit/issues/entry?template=Repo+tool+issue

安装完毕,我们可以看到安装的实际上是一个repo的引导器。它还不是完整的管理工具。

通过cat命令查看一下repo文件,实际上是一个python3脚本.

which repo
/usr/local/bin/repo

cat /usr/local/bin/repo
........

客户端在同步仓库时一般用到的几个命令序列:

# 初始化仓库
repo init -u ....
# 检出代码
repo sync -j16
# 建立本地分支
repo start <BRANCH> --all
# 批量执行git命令
repo forall -c `GIT COMMAND`

我们以清华大学官网的AOSP代码为例,操作一下repo的仓库同步。

# 设置一下REPO_URL。
export REPO_URL='https://mirrors.tuna.tsinghua.edu.cn/git/git-repo/'

mkdir -p ~/aosp
cd aosp

# 初始化仓库
repo init -u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest
Downloading Repo source from https://mirrors.tuna.tsinghua.edu.cn/git/git-repo/
remote: Enumerating objects: 7372, done.
remote: Counting objects: 100% (7372/7372), done.
remote: Compressing objects: 100% (3935/3935), done.
remote: Total 7372 (delta 4765), reused 5577 (delta 3363)
接收对象中: 100% (7372/7372), 3.27 MiB | 3.58 MiB/s, 完成.
处理 delta 中: 100% (4765/4765), 完成.

... A new version of repo (2.21) is available.
... You should upgrade soon:
    cp /home/xxx/Documents/aosp/.repo/repo/repo /usr/local/bin/repo

Downloading manifest from https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest
remote: Enumerating objects: 98793, done.
remote: Counting objects: 100% (98793/98793), done.
remote: Compressing objects: 100% (39423/39423), done.
remote: Total 98793 (delta 38918), reused 98123 (delta 38532)
接收对象中: 100% (98793/98793), 25.23 MiB | 2.40 MiB/s, 完成.
处理 delta 中: 100% (38918/38918), 完成.

Your identity is: xxx <[email protected]>
If you want to change this, please re-run 'repo init' with --config-name

Testing colorized output (for 'repo diff', 'repo status'):
  black    red      green    yellow   blue     magenta   cyan     white 
  bold     dim      ul       reverse 
Enable color display in this user account (y/N)? y

repo has been initialized in /home/xxx/Documents/aosp/
If this is not the directory in which you want to initialize repo, please run:
   rm -r /home/yl/Documents/aosp//.repo
and try again.

repo init命令执行完毕,实际上就是下载了一个.repo目录下(repo命令版本过时,可以根据提示更新一下,也可以不用更新)。

我们看一下repo目录内容:

repo
├── manifests        // 清单仓库
│   ├── .git -> ../manifests.git/
│   ├── default.xml  // 真正的仓库清单文件
│   ├── GLOBAL-PREUPLOAD.cfg
│   └── OWNERS
├── manifests.git    // 清单仓库git配置信息
│   ├── branches
│   ├── config
│   ├── description
│   ├── FETCH_HEAD
│   ├── HEAD
│   ├── hooks
│   ├── index
│   ├── info
│   ├── logs
│   ├── objects
│   ├── packed-refs
│   └── refs
├── manifest.xml     // 清单文件,内容包含default.xml文件
└── repo             // repo工具脚本
    ├── color.py
    ├── command.py
    ├── git_trace2_event_log.py
    ├── hooks
    ├── hooks.py
    ├── repo
    ├── .........
    └── wrapper.py

repo init干了这几件事情:

  1. 下载repo工具仓库,其中包含了python脚本。在.repo/repo目录下。
  2. 下载了manifests清单仓库,在目录.repo/manifests下,软链接包含了.repo/manifests.git目录。
  3. 下载了一个仓库清单文件manifests.xml,其中包含default.xml。

repo批量管理仓库的内容就在default.xml中。我们看一下文件内容:

<?xml version="1.0" encoding="UTF-8"?>
<manifest>

  <remote  name="aosp"
           fetch=".."
           review="https://android-review.googlesource.com/" />
  <default revision="master"
           remote="aosp"
           sync-j="4" />

  <manifest-server url="http://android-smartsync.corp.google.com/android.googlesource.com/manifestserver" />

  <superproject name="platform/superproject" remote="aosp"/>
  <contactinfo bugurl="go/repo-bug" />

  <project path="build/make" name="platform/build" groups="pdk" >
    <linkfile src="CleanSpec.mk" dest="build/CleanSpec.mk" />
    <linkfile src="buildspec.mk.default" dest="build/buildspec.mk.default" />
    <linkfile src="core" dest="build/core" />
    <linkfile src="envsetup.sh" dest="build/envsetup.sh" />
    <linkfile src="target" dest="build/target" />
    <linkfile src="tools" dest="build/tools" />
  </project>
  <project path="build/bazel" name="platform/build/bazel" groups="pdk" >
    <linkfile src="bazel.WORKSPACE" dest="WORKSPACE" />
    <linkfile src="bazel.sh" dest="tools/bazel" />
    <linkfile src="bazel.BUILD" dest="BUILD" />
  </project>
  <project path="build/bazel_common_rules" name="platform/build/bazel_common_rules" groups="pdk" />
  <project path="build/blueprint" name="platform/build/blueprint" groups="pdk,tradefed" />
  <project path="build/pesto" name="platform/build/pesto" groups="pdk" />
  <project path="build/soong" name="platform/build/soong" groups="pdk,tradefed" >
    <linkfile src="root.bp" dest="Android.bp" />
    <linkfile src="bootstrap.bash" dest="bootstrap.bash" />
  </project>
  <project path="art" name="platform/art" groups="pdk" />
  <project path="bionic" name="platform/bionic" groups="pdk" />
  <project path="bootable/recovery" name="platform/bootable/recovery" groups="pdk" />
  <project path="bootable/libbootloader" name="platform/bootable/libbootloader" groups="vts,pdk" />
  <project path="device/common" name="device/common" groups="pdk-cw-fs,pdk" />
  <project path="device/generic/arm64" name="device/generic/arm64" groups="pdk" />
  <project path="device/generic/armv7-a-neon" name="device/generic/armv7-a-neon" groups="pdk" />
  <project path="device/generic/art" name="device/generic/art" groups="pdk" />
  ......
  <repo-hooks in-project="platform/tools/repohooks" enabled-list="pre-upload" />

  <!--
    Merge marker to make it easier for git to merge AOSP-only manifest
    changes to a custom manifest.  Please keep this marker here and
    refrain from making changes on or below it.
  -->
</manifest>  

这个XML文件的语法,不是本文档的内容。简单说明一下几个关键的:

remote: 远程服务端repo仓库名称
project: 每个项目的git仓库
path: 客户端git仓库路径
name: 服务端git仓库名称
revision: 分支名称或者commit id
linkfile: 软链接文件,src和dest表示源和目的。
copyfile: 文件拷贝,src和dest表示源和目的。
groups: 分组。

概括一下repo批量管理仓库的方式:

  1. repo init命令下载工具脚本仓库和清单仓库。
  2. repo sync命令根据清单文件中的配置,下载远程仓库二进制文件。
  3. repo start检出分支。

所以,repo仓库的管理核心就在manifest.git,而起关键作用的配置文件就是仓库清单文件xxx.xml,有多少个项目仓库,就有对应的xml清单文件。

1.2 搭建repo服务需要做哪些事情?

接着就是关键问题:搭建repo服务器我们要做哪些事情?

简而言之:

  1. 部署通用的工具仓库git-repo.git。
  2. 部署自己的清单仓库manifests.git。
  3. 编写清单文件manifests.xml
  4. 批量创建工程子仓库和上传源代码。

2 部署工具仓库git-repo.git

实际上,如果你的网络链接可以链接到外网的话,工具仓库git-repo.git是不需要搭建部署的。

只需要通过REPO_URL这个环境变量来指定URL就可以自动拉取git-repo.git仓库。

export REPO_URL=https://mirrors.tuna.tsinghua.edu.cn/git/git-repo/

常用的URL有这些:

## google地址,需要梯子。
https://gerrit.googlesource.com/git-repo

## 清华大学开源软件镜像站
https://mirrors.tuna.tsinghua.edu.cn/git/git-repo/

## 其他可以自己网上搜索...

但是现实中,很多公司的外网是受限的,无法访问。这个时候就需要考虑搭建自己的工具仓库git-repo.git了。

这个仓库的搭建很简单,就是一个普通的git仓库,但是涉及到GPG签名的问题就比较复杂。这个过程不是本文档的内容,可以搜索互联网上的文档资料。

3 部署清单仓库manifests.git

部署清单仓库的方式,跟普通的git仓库没有什么差别。关键问题在:

  1. 清单仓库如何管理多个项目仓库?
  2. 如何编写清单文件manifests.xml?

3.1 如何设计清单仓库及分支?

3.1.1 需要几个仓库?

从服务器的角度来看,你的服务器上肯定会管理多个项目仓库(例如qcom8953,qcom8916,mtk6219,mtk2226,rk3399,rk3358等等),那我们管理这些仓库的方式可能有:

  1. 整个服务器只建立一份manifests.git仓库,每个仓库下的不同的xml文件管理不同的项目仓库。例如qcom8953.xml,rk3399.xml等。
  2. 每个项目仓库建立一个对应的清单仓库,例如manifests-qcom8953.git、manifests-rk3399.git等,其中放置各项目仓库的清单文件xml。

这两种方式都可以,取决于你的代码管理策略。我一般在整个服务器上使用一个manifests.git仓库,通过不同的xml文件来管理所有的项目仓库。服务器端项目仓库的目录结构设计如下:
aosp
├── manifests.git // 清单仓库
│   ├── qcom8953.xml
│   ├── rk3399.xml
│   ├── rk3358.xml
│   └── mtk6226.xml
├── qcom/qcom8953 // 项目仓库
├── rockchip/rk3399 // 项目仓库
├── rockchip/rk3358 // 项目仓库
└── mtk/mtk6226 // 项目仓库

3.1.2 需要几个分支?

清单仓库本身就是一个普通的git仓库,他也是有分支的。同一个配置文件,例如qcom8953.xml,他也可以切分出不同的分支,来管理不同的项目仓库版本。

清单仓库的分支,跟项目仓库的分支没有直接的关联关系。但是在实践操作中,我们一般会在清单仓库中建立项目仓库对应的分支,方便管理映射。例如qcom8953项目仓库,它有4个分支master、develop、test和release,那么我们的manifests.git也可以建立四个分支,跟项目仓库对应起来。

我这里建立两个分支:master和release。后面会按照这两个分支来操作。

我们先看一下repo init命令的常用参数:

repo init --help
Usage: repo init [options] [-u] url

Options:

  Manifest options:
    -u URL, --manifest-url=URL
                        manifest repository location
    -b REVISION, --manifest-branch=REVISION
                        manifest branch or revision (use HEAD for default)
    -m NAME.xml, --manifest-name=NAME.xml
                        initial manifest file

  repo Version options:
    --repo-url=URL      repo repository location ($REPO_URL)
    --repo-rev=REV      repo branch or revision ($REPO_REV)
    --no-repo-verify    do not verify repo source code

  Other options:
    --config-name       Always prompt for name/e-mail

一般来说,-b的默认值是master,-m的默认值是manifests.xml。如果我们想要使用不同的默认的清单文件,就需要在编写服务端清单文件和repo init参数时需要配合使用。

我这里全部使用默认值。在aosp源码的mannifests.xml中则体现在这几个项上:

  <remote  name="aosp"
           fetch=".."
           review="https://android-review.googlesource.com/" />
  <default revision="master"     ## 远程默认分支
           remote="aosp"         ## 远程默认仓库,就是remote标签中的name="aosp"
           sync-j="4" />

3.2 如何编写清单文件?

编写清单文件,肯定不可能全部手写,需要拿到一份原始的manifests.xml文件。这份文件需要你从你的源头代码repo仓库中获取是最好的。

我这里就以qcom的AOSP源码为例,修改出我自己的仓库qcom8953.xml。

<?xml version="1.0" encoding="UTF-8"?>
<manifest>
    <!--
        remote: 服务器远程仓库的信息。
        name: 远程manifest仓库名称,一般默认为origin
        fetch: 仓库url。如果设置为"."(注意是一个点), 就表示在repo init -u url中配置。
        review: 评审系统网址。如果提交代码,就会进入这个评审系统。
    -->
    <remote name="origin"  fetch="." review="gerrit.your_domain.com" />
        <!--
          default: 这个repo仓库的默认属性。
          revision: 默认的工程代码仓库分支,可以修改这里来指向不同的分支。
          remote:   远程仓库的名称。这里跟<remote>标签是对应的,当然还可以指向其他仓库的remote。
        -->
        <default revision="master" remote="origin"/>

        <!--
            project: 每个仓库就是一个project
            path: 客户端同步到本地后的路径名称。
            name: 服务器端git仓库名称,注意带了路径。
        -->
        <project path="build" name="build"  />
        <project path="device" name="device"  />
        <project path="frameworks/base" name="frameworks/base" />
        <!--        
            重要说明:
            这里的build目录和其下的三个子目录:blueprint, kati, soong是嵌套的git仓库结构!
            这种嵌套的git仓库。有一个非常重要的控制文件:.gitignore.
            父级目录那种一定存在一个.gitignore文件,其中包含了嵌套的子级仓库路径。
        -->
        <project path="build" name="build">
            <!-- copyfile: 将build/core/root.mk拷贝为Makefile。其中带有相对路径 -->
            <copyfile src="core/root.mk" dst="Makefile" />
        </project>
        <project path="build/blueprint" name="build/blueprint" />
        <project path="build/kati" name="build/kati" />
        <project path="build/soong" name="build/soong">
            <!-- linkfile: 建立一个软链接Android.bp指向build/soong/root.bp -->
            <linkfile src="root.bp" dest="Android.bp" />
            <linkfile src="bootstrap.bash" dest="bootstrap.bash" />
         </project>
         <!-- 更多仓库信息 ...... -->
</manifest>

3.3 创建清单仓库manifests.git

创建清单仓库可以有两种方式:

  1. gerrit系统的web页面创建。这种方式适合创建少量的仓库,简单快捷。
  2. gerrit命令创建。这种方式适合批量创建大量的仓库,特别是AOSP仓库。

第一种方式比较简单,在WEB页面上操作就可以,没有什么特别的。

重点介绍第二种命令行方式。

ssh -p 29418 <USERNAME>@<GERRIT_IP_ADDRESS> gerrit create-project aosp/manifests.git

创建完毕,在gerrit页面上也可以看到这个manifests.git仓库。管理员可以在页面上创建两个分支:master,release。

客户端按照正常的git操作操作:

## 克隆代码
git clone .../manifests.git
## 提交清单文件qcom8953.xml
git add qcom8953.xml
git commit -m "add qcom8953.xml"
git push

这样我们就建立了一个manifests.git仓库,并上传了一个清单文件qcom8953.xml。

下一步就是如何创建项目仓库了。

4 批量创建工程子仓库

初始工程仓库的源代码,最好来自平台厂商提供的带了.repo和.git的完整repo仓库,然后我们看看如何将这个仓库移植到我们自己的内部仓库。

一般的操作步骤:

  1. gerrit命令批量创建服务端空仓库。
  2. 预处理本地repo仓库。
  3. 批量上传源代码。

4.1 批量创建服务端空仓库

批量创建子仓库只能通过gerrit命令行来进行。

ssh -p 29418 <USERNAME>@<GERRIT_IP_ADDRESS> gerrit create-project aosp/qcom8953/xxx.git

这里我们需要知道的qcom8953仓库的所有仓库名字,然后通过脚本来批量创建,有两种方式:

  1. 从xml脚本中提取仓库名称。
  2. 从原始源代码仓库的.repo/manifets.git中的project.list文件中直接获取。
    不管哪种方式,都需要人工核对一下最终的仓库数量和名称是一致的。

我们最终需要一个仓库列表文件:project.list。文件内容如下:

qcom/qcom8953/art
qcom/qcom8953/bionic
qcom/qcom8953/bootable/bootloader/edk2
qcom/qcom8953/bootable/recovery
qcom/qcom8953/build/blueprint
qcom/qcom8953/build/kati
qcom/qcom8953/build
qcom/qcom8953/build/soong
qcom/qcom8953/cts
qcom/qcom8953/dalvik
qcom/qcom8953/developers/build
qcom/qcom8953/developers/demos/JustForUs
qcom/qcom8953/developers/samples/android
qcom/qcom8953/development
qcom/qcom8953/device/common
qcom/qcom8953/device/generic/arm64
qcom/qcom8953/device/generic/armv7-a-neon
......

接下来我们可以使用下列命令来批量创建服务端空仓库:

# 参数说明
# --empty-commit: 强制参数, 必须添加; 
# -p YOUR-projects: 你的gerrit权限仓库。根据你的gerrit权限名称来确定。
# 其他参数就不要再添加了,画蛇添足以后,多出很多问题。
# 注意远程仓库名称前需要带path前缀(qcom/qcom8953),防止服务器端仓库路径混乱。

cat project.list | xargs -I {}  ssh -p 29418 [email protected] gerrit create-project "{}" --empty-commit -p YOUR-projects

创建完毕,在gerrit页面上就看到了几百个git仓库。

4.2 预处理本地repo代码

在上传本地repo仓库之前,需要做几个预处理动作:

  1. 删除本地工程中的.repo目录。
  2. 核对本地仓库.repo/manifests.git/xxxx.xml清单文件,根据文件内容删除掉本地代码中的copyfile和linkfile的源。
  3. 修改本地每个仓库的git remote url路径。这里你需要自己编写脚本来批量完成。
git remote set-url origin ssh://[email protected]:29418/xxx

4.3 上传分支和代码

仓库预处理完毕,你需要一一上传各仓库的指定的分支和代码到服务器仓库。
这里是通用的操作, 你也需要自己编写脚本进行上传同步:

git checkout master
git push origin master:master
git push -- tags

当然,上传之后,你最好在第三方测试验证一下,是不是有些.gitignore或者某些嵌套仓库存在问题,需要手工进行修复。

5 repo仓库使用简述

repo仓库搭建完毕,对客户端来说,使用操作并不麻烦,使用常用的repo命令和git命令即可完成代码提交。

# 初始化仓库
repo init -u <remote manifests url> -n <manifests branch> -m <manifest NAME.xml>
# 检出代码
repo sync -j16
# 建立本地分支
repo start <BRANCH> --all
# 批量执行git命令
repo forall -c `GIT COMMAND`

单个仓库的代码提交合并:

git add xxx
git commit -m "xxxx"
git push HEAD:refs/for/BRANCH

初次提交时可能会报告no change id的小错误,百度搜索一下,很容易修复的。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK