搭建支援 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 原始碼映象,主要步驟如下:
- 在伺服器搭建 Git 託管伺服器
- 在客戶端安裝配置好 Repo
- 在客戶端建立 manifest/default.xml 並上傳到 Git 伺服器
- 將客戶端 Android 原始碼上傳到 Git 伺服器
- 在其它獲得 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.txt
,push_aosp.sh
放在和原始碼 aosp/
同一個目錄裡,然後執行:
cat repo_path.txt | ./push_aosp.sh
在我的例子裡,原始碼大小有 22G ,上傳花了 2 個多小時。
十一、其它客戶端使用 Repo 伺服器
原始碼成功上傳到伺服器 A 之後,其它客戶端就可以下載使用了。假設客戶端計算機 C 要使用,流程如下:
- 安裝 Repo ,具體參考上文第二節“安裝 Repo”
repo init -u [email protected]:aosp/manifest
repo sync