• 如何搭建android源代码repo仓库


    如何搭建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 <xxx@sina.com>
    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 root@192.168.1.1 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://root@192.168.1.1: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的小错误,百度搜索一下,很容易修复的。

  • 相关阅读:
    OpenCV从入门到精通实战(七)——探索图像处理:自定义滤波与OpenCV卷积核
    阿里饶子昊:Spring Cloud Alibaba发展和近期规划
    学习 Python 数据可视化,如何快速入门?
    SpringBoot SpringBoot 开发实用篇 5 整合第三方技术 5.4 手机验证码案例 - 验证码校验
    重新理解微服务之终究绕不过这4个坎?(观点探讨)
    实战攻防演练-Linux写入ssh密钥,利用密钥登录
    docker 入门指南
    Nmap渗透测试指南之探索网络
    【六祎 - Mac】Mac安装Nginx;Mac编译安装Nginx;
    态路小课堂丨400G QSFP112—助力IDC数据中心升级
  • 原文地址:https://www.cnblogs.com/aosp/p/16445394.html