1. 程式人生 > >搭建支援 Repo 的 Android 原始碼映象(Repo 伺服器)

搭建支援 Repo 的 Android 原始碼映象(Repo 伺服器)

方案廠商給了一份 Android 原始碼,沒有 manifest.git 檔案,不支援 Repo。為了基於這份程式碼搭建支援 Repo 的映象伺服器,斷斷續續摸索了兩個星期,總算 hacking 成功。

本文用到的主要知識:

  • shell script
  • git 指令

一、關於 Repo

基於 Android 原始碼的開發工作大多要用到 Git 和 Repo。

Repo 是基於 Git 的倉庫管理工具,支援同時管理許多個 Git 倉庫。因為 Android 原始碼包含了許多個 Git 倉庫,使用 Repo 可以簡化許多工作。比如,使用一個 Repo 命令,就可以從多個不同的倉庫下載檔案,同步到你的計算機上。

搭建支援 Repo 的 Android 原始碼映象,主要步驟如下:

  1. 在伺服器搭建 Git 託管伺服器
  2. 在客戶端安裝配置好 Repo
  3. 在客戶端建立 manifest/default.xml 並上傳到 Git 伺服器
  4. 將客戶端 Android 原始碼上傳到 Git 伺服器
  5. 在其它獲得 git 許可權的客戶端使用:Repo init; Repo sync

二、搭建 Git 伺服器

搭建 Git 伺服器這部分的內容相對獨立,和 Repo 的關係不大,因此另外寫了一篇文章:

三、搭建 Repo 伺服器的裝置及要求

伺服器 A:

  • IP 地址:192.168.1.101
  • 安裝了 Gitolite
  • 將作為 Repo 伺服器(這樣稱呼比較直觀,但不知是否準備)

客戶端 B:

  • IP 地址:192.168.1.102
  • 取得 A 的 Gitolite 的管理員身份(可以修改 gitolite-admin)
  • 存放了廠商給的 Android 原始碼 asop/,沒有 manifest/default.xml 檔案

以下的操作,除非有特別說明,都在客戶端 B 執行。

四、安裝 Repo

mkdir ~/bin
PATH=~/bin:$PATH
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo

因為特殊的國情。。。上述操作後 Repo 還是很難直接使用的。比如在 repo init 時,即使是科學上網,也無法連線上

REPO_URL = 'https://gerrit.googlesource.com/git-repo'

一個替代的方案是,使用清華的映象。開啟 ~/bin/repo,把裡面的 REPO_URL 地址改為:

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

這樣就可以執行 repo 指令了。

五、認識 manifest/default.xml 檔案

Android 原始碼裡有上百個 git 專案,不同版本的原始碼專案各不相同,Repo 指令如何知道具體有哪些專案呢,答案就在 manifest/default.xml default.xml 記錄了這些 git 專案的名稱、路徑等資訊,通過它, Repo 就有跡可循。

瞭解 manifest/default.xml 最好的辦法,就是從零搭建一個 Repo 伺服器。

隨便找臺 Linux 作業系統的計算機,新建一個目錄模擬 Repo 伺服器:

mkdir /tmp/repo-server
cd /tmp/repo-server

/tmp/repo-server 目錄建立若干個 git 倉庫:

#建立 git 倉庫 project_1
mkdir project_1 && cd project_1
git init;
touch p1.txt;
git add .;
git commit –m "initial commit"

cd..

#建立一個 git 倉庫 project_2
mkdir project_2 && cd project_2
git init;
touch p2.txt;
git add .;
git commit –m "initial commit"

/tmp/repo-server 建立 manifest 倉庫:

#建立 manifest 倉庫
mkdir manifest && cd manifest
git init;
touch default.xml;

default.xml 加入以下內容:

<?xml version="1.0" encoding="UTF-8"?>
<manifest>
  <remote  name="test"
           fetch="." />
  <default revision="master"
           remote="test"
           sync-j="4" />
  <project path="project_1" name="project_1" />
  <project path="project_2" name="project_2" />
</manifest>

然後 commit 程式碼

git add .;
git commit –m "initial manifest"

這樣,模擬 Repo 伺服器就搭建完成了。

接著再建一個新的目錄/tmp/repo-client/,模擬 Repo 客戶端。在客戶端目錄裡:

repo init –u /tmp/repo-server/manifest

如果一切正常,將會收到成功提示資訊。這時候去檢視該目錄下的 .repo,就會發現有一個 manifest.xml,內容是克隆伺服器的 default.xml 檔案。

最後,使用 Repo 指令將多個 git 專案的程式碼一次性同步到客戶端上:

repo sync

仔細揣摩 default.xml 檔案的內容,你應該能理解這個 xml 檔案的作用。

注意:以上試驗,我在 centOS 測試成功,在 macOS 測試時,執行 repo init -u $LOCAL_ADDRESS 會報錯,會要求最後一個引數是 url

六、從原始碼 aosp/ 找出所有 git 倉庫

從廠商得到的 Android 原始碼 aosp/ 目錄大致如下,

aosp
|- art
|- /abi
  |- cpp
|- /developers
  |- build
  |- demos
  |- samples
    |- /android
...

因為廠商不用 Repo 管理,所以原始碼裡也沒有 manifest/default.xml 檔案。

幸好,仔細檢視 aosp/ 之後發現,裡面有些目錄下有 .git/ 目錄,說明它就是一個 git 專案。因此可以通過找出所有帶 .git/ 目錄的目錄,來確定 aosp/ 有哪些 git 專案。程式碼如下:

find aosp/ -type d -name '.git' > git_projects.txt`

最終在這份 aosp/ 共找到 531 個 git 專案。

七、建立 default.xml 檔案

得到 git_projects.txt 後,就可以據此建立 default.xml 檔案。

git_projects.txt 每一行的內容是這樣的:

aosp/prebuilts/devtools/.git

使用 bash 指令去掉最後開頭的 aosp/ 和末尾的 /.git

cat git_projects.txt | cut -c 6- | sed 's/.....$//' > path.txt

得到每一行內容的格式如下:

prebuilts/devtools

生成 xml 檔案的指令碼 gen_xml.sh :

#!/bin/bash

echo -e "
<?xml hhhversion=\"1.0\" encoding=\"UTF-8\"?>
<manifest>
  <remote  name=\"aosp\"
           fetch=\".\"/>
  <default revision=\"master\"
           remote=\"aosp\"
           sync-j=\"4\" />" >>$1

while read line; do
	echo "<project path=\"$line\" name=\"$line\" />" >>$1
done
echo -e "\n</manifest>" >>$1

bash 指令建立 default.xml

cat path.txt | ./gen_xml.sh default.xml

得到的 default.xml 裡,<project /> 示例如下:

<project path="prebuilts/devtools" name="prebuilts/devtools" />

八、在 Gitolite 初始化所有 asop/ 的 git 倉庫

前提:客戶端 B 可以修改伺服器 A 的 gitolite-admin 專案,即管理 Gitolite 的專案

這裡還要用到 git_projects.txt 檔案。這次只要去掉每行末尾的 /.git

cat git_projects.txt | sed 's/.....$//'  > repo_path.txt

得到的每一行的格式如下:

aosp/prebuilts/devtools

編寫 gen_server_repo.sh

#!/bin/bash

# 宣告群組 @aosp_dev , 成員: jack, tom
@aosp_dev    =    jack tom

# 加上 manifest 倉庫
echo -e "
repo aosp/manifest\n
     RW+    =    @aosp_dev\n" >>$1

while read line; do
	echo -e "repo $line\n     RW+    =    @aosp_dev\n" >>$1
done

bash 指令生成 aosp.conf 檔案:

cat repo_path | ./gen_server_repo.sh  aosp.conf

gitolite-admin/conf/gitolite.conf 開頭加入:

include "aosp.conf"

把更新的內容 push 到伺服器 A ,gitolite 就會在相應目錄(通常是 /home/git/repositories )初始化所有 aosp 的所有 git 倉庫。

至此,aosp 的所有倉庫已經在伺服器 A 生成了,下一步就是把 aosp 的原始碼上傳到伺服器。

九、上傳 manifest/default.xml 到伺服器

把之前建立好 default.xml 檔案上傳到伺服器的 aosp/manifest 倉庫:

git clone [email protected]:aosp/manifest
cd manifest
#
# 把default.xml 檔案放到 manifest/ 目錄
#
git add .
git commit -m 'add default.xml'
git push

十、上傳 aosp/ 原始碼到伺服器

上傳原始碼的 shell 指令碼 push_aosp.sh:

#!/bin/bash

work_dir=$1

pwd=${PWD}
count=0

while read line; do
    echo $line
    count=$((count+1))
    line1=${line%%/*}
    if [ -z "$line" ]; then
        echo $work_dir not exist !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 1>&2
        continue
    fi
    if [ $(ls -A $pwd/$line | wc -l) -eq 0 ]; then
        echo $work_dir empty !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 1>&2
        continue
    fi
    workdir=$pwd/$line
    cd $workdir
        rm -rf .git
        git init .  1>&2
        git add . -f 1>&2
        { git commit -m "Initial commit" &&
            git push -f --set-upstream [email protected]:$line.git master
        } || {
            touch empty_file
            git add .
            git commit -m "新增一個空檔案,消滅空倉庫"
            git push -f --set-upstream [email protected]:$line.git master
            echo number:$count should be empty $line >> $HOME/log_$(date +%Y_%m_%d)
        }
	echo -e "number:$count\n"
    cd -
done

注意 push_aosp.sh 裡的這段指令碼:

{ git commit -m "Initial commit" &&
    git push -f --set-upstream [email protected]:$line.git master
} || {
    touch empty_file
    git add .
    git commit -m "新增一個空檔案,消滅空倉庫"
    git push -f --set-upstream [email protected]:$line.git master
    echo number:$count should be empty $line >> $HOME/log_$(date +%Y_%m_%d)
}

這其實是個變通辦法。因為之前試過忽略空倉庫,不做 push 。但程式碼上傳到伺服器後,客戶端使用 repo sync 下載時,會出現 error: Exited sync due to fetch errors 錯誤,導致同步失敗。

所以只好消滅所有空倉庫。

這裡還把所有加工過的空倉庫記錄到 $HOME/log_$(date +%Y_%m_%d) 檔案裡,作為備忘。

最後,把 repo_path.txtpush_aosp.sh 放在和原始碼 aosp/ 同一個目錄裡,然後執行:

cat repo_path.txt | ./push_aosp.sh

在我的例子裡,原始碼大小有 22G ,上傳花了 2 個多小時。

十一、其它客戶端使用 Repo 伺服器

原始碼成功上傳到伺服器 A 之後,其它客戶端就可以下載使用了。假設客戶端計算機 C 要使用,流程如下:

  1. 安裝 Repo ,具體參考上文第二節“安裝 Repo”
  2. repo init -u [email protected]:aosp/manifest
  3. repo sync

參考資料