1. 程式人生 > >安卓開發的工作流程

安卓開發的工作流程

Table of Contents

關於本文件庫的說明,包括如何編輯、更新本文件,如何訪問本文件庫其他頁面,請參考 文件專案說明

1 工作環境的安裝與配置

我們的安卓開發基本上都在 Linux 系統下進行,發行版是 Ubuntu,版本建議 14.04(12.04 和 16.04 在某些安卓版本上都會出編譯錯誤,參考 faq: 安裝使用 ubuntu-16.04 注意事項)。

裝好 Linux 系統後,用這條命令配置基於 system-config 的開發環境:

bash -c "$(curl -s http://172.16.0.9/setup-system-config.sh)" setup-system-config.sh

對於開發環境有任何問題,比如 Java 版本該如何切換 Java6 和 Java7 的問題,請參考 開發環境常見問題解答

1.1 升級 system-config

執行

system-config-update

或者重新執行上面的配置命令,都可以升級 system-config。

如果你有自己對 system-config 的定製,可以提交到伺服器:

cd ~/system-config; git push origin HEAD:refs/heads/sandbox/+你的公司郵箱字首

如果你有自己的改動,那麼在升級的過程中,有可能會發生衝突,這種情況下需要你自己解一下衝突。

另外,如果你覺得你的改動對其他人也有幫助,那你可以把程式碼提給我 review,然後我會考慮要不要把你的改動合入到我的 system-config 專案中。

在使用過程中遇到什麼問題的話,請參考 開發環境常見問題解答

1.1.1 自己定製、重置 system-config 的配置

system-config 本身是一個開源的專案,它的 github 網址:我的 system-config 專案

在我們公司裡使用的 system-config 是多個子專案的合集(其中包括一個不開源的錘子內部專案:smartcm)。

System-config 自帶了一個非常強大的搜尋原始碼的功能,beagrep,各位如果對原始碼有疑問,可以考慮使用這個工具(參考 beagrep 的原始碼和這篇部落格:

http://baohaojun.github.io/beagrep-cn.html):

cd ~/system-config # 到你想閱讀的程式碼目錄下

for-code-reading # 建立程式碼索引。這個命令會執行比較長的時間,取決於代
                 # 碼量,在 system-config 下要幾分鐘,如果是安卓那個量
                 # 級的程式碼,基本上跟做一次全編的時間差不多。


beagrep -e "hello world" # 搜尋原始碼

在 system-config 的環境裡,我們建議如下 3 種自定義開發環境的策略,供參考:

  1. 直接修改 system-config 的原始碼,並考慮提交給包昊軍 Review、合入(如果你認為對其他同事也有用的話)

    如果不提交給我 Review 併合入的話,後續你再升級 system-config 的時候,你的改動就可能跟我的改動發生衝突,需要你花費一些精力去解決。

  2. 修改自己的定製檔案,比如自己的 ~/.bashrc

    但是請注意,最好是要在 ~/.bashrc 檔案的末尾加入你的改動,而不是在這個檔案開頭的部分。因為有些環境變數比如 PATH 在 system-config 裡是會強行重置的,如果你在 system-config 之前改了 PATH,那被 system-config 一重置這個改動就無效了。

    另外注意確保自己的修改不會影響 system-config 的功能。

  3. 通過 system-config 提供的 sc 命令直接關閉 system-config,需要時再開啟

    sc 命令還提供更多方便的功能,詳情請開啟原始碼檔案檢視。

一般情況下你很少需要修改 system-config,因為如果有個命令你覺得不好用,你別用它,當它不存在就可以了。但 system-config 通過重定義覆蓋了一些原有的命令,比如 adb,我對它進行了重度的封裝,有時候你可能會覺得有些提示資訊太煩瑣,希望直接使用未修改的 adb 版本,這裡簡單寫一下如何操作(詳情請參考 system-config 原理與使用的簡要說明)。

  1. 用 type 命令看一下你希望重定義的命令,比如 type adb,你會看到輸出是 adb is aliased to `my-adb',也就是說,它是一個 bash 的別名(請執行 info bash 來閱讀 bash 的 info 手冊)
  2. 如果你看到的命令是一個別名,請在 ~/.bashrc 的末尾加入 unalias XXX 取消這個別名的定義(請在 bash info 手冊中搜索 unalias)
  3. 如果你看到的命令是一個函式,請在 ~/.bashrc 的末尾加入 unset -f XXX 取消這個函式的定義(請在 info 中搜索 unset)
  4. 如果你看到的命令是一個檔案,請執行 type -a XXX 檢視 XXX 的所有版本,確認一下它排在第一行的檔案是由 system-config 提供的,然後考慮一下是否可以直接刪除這個檔案

2 下載程式碼、編譯、提交

2.1 下載程式碼

我們使用谷歌官方提供的 repo/gerrit 來管理程式碼。但下載程式碼時,我們封裝了一個非常簡單的方式,就是在 system-config 下,使用 sse 命令(sse 代表 smartisan software engineers,它支援許多子命令,所有的子命令指令碼都在 ~/src/github/smartcm/sse-helpers 目錄下,如有需要可自己開啟檢視),選擇 get-source-code 子命令。

通過這個方式下載程式碼,sse 最後會告訴你如果自己用 repo init/repo sync 的話,應該使用什麼樣的命令,你可以記一下,下次有可能會用上。

上面的方式下載到的程式碼,都是各個產品分支的最新程式碼。但有時候你會需要同步到某一天的正式版本的程式碼(一般有兩種情況下需要這樣做:你要查那天的版本的問題;你當前的程式碼有別人引入的編譯錯誤,導致你編譯出錯),這種情況下,你可以在一個已經下載過程式碼的倉庫目錄下,使用 repo sync --help 檢視一下幫助,裡面有個選項允許你通過一個臨時的 manifest.xml 檔案來同步程式碼,而我們的每個正式版本,在刷機包目錄下都有一個 manifest.xml 和 oem-manifest.xml,裡面儲存著編譯時的程式碼快照。

如果剛入職時發現不能同步安卓程式碼的話,請參考 5.51.1

2.1.1 程式碼分倉許可權控制說明

由於公司資訊保安管控要求,需要對一些關鍵倉儲管控許可權,這些倉儲的原始碼原則上只對模組維護組開放,不對其他開發組開放。

涉及的倉儲有:AppStoreSmartisan(APP3)、CloudServiceSmartisan(APP3)、GameStoreSmartisan(APP3)、LaucherSmartisanNew(Framework)、Tpassword(APP2)、OEM(BSP)、Brower(APP1)

其中 OEM 和 browser 程式碼通過另外的 manifest 管控,不影響大家的下載習慣。其他幾個管控倉儲下載時(有許可權的前提下)需要注意:

repo init 時需要新增:-g default,$group-name。 $group-name 的值有:sos-app3、sos-fw、sos-app2。如果需要多個 group,$group-name 之間用逗號分隔。示例:

repo init -g default,sos-fw -u ssh://gerrit.smartisan.cn/qualcomm/platform/manifest.git -b sanfrancisco -m osborn-rom.xml --repo-url smartisan:googlesource/git-repo --reference ~/src/android-mirror

如果你是使用 sse 下載程式碼,sse 會提示你是否需要下載額外的 repo group 的倉庫。如果嘗試下載自己沒有許可權的倉庫,repo sync 最終會出錯退出。sse 下載時文字提示介面如下:

1) none 只下載普通許可權倉庫
2) all 下載所有倉庫
3) sos-app2 額外下載一些屬於 app2 組的保密倉庫,比如 Tpassword
4) sos-app3 額外下載一些屬於 app3 組的保密倉庫,比如 AppStoreCommon
5) sos-fw 額外下載一些屬於 frameworks 組的保密倉庫,比如 LauncherSmartisanNew
6) done 選擇完畢,退出此選單
7) help 顯示關於此選項的幫助
請選擇哪些額外的需要特殊許可權的 repo group 是你需要下載的(如有不明,請選 help)。當前已選擇: '' (Type ? for help)>

如果確認自己屬於某個專案組,但卻沒有相關專案的程式碼下載許可權,請給自己的 Leader 發郵件並抄送給 [email protected] 申請相關許可權,Leader 回覆郵件批准後,cm 會負責更改相關許可權。

2.2 搜尋程式碼

公司的各個產品的主線程式碼,均提供了每天更新的搜尋引擎索引,以加快各位同事全域性搜尋程式碼的速度。在解問題的時候,比如想查詢 logcat 中 Checking URI perm 輸出的程式碼位置,可以在自己的安卓原始碼目錄下執行如下命令:

abc-x grep "Checking URI perm"

一般情況下 0.5 ~ 5 秒之內就可以完成搜尋。

如果想要自己在本地搜尋程式碼,比如非主線程式碼、或專案初期還沒有拉出主線,只有安卓原生線時;或想要搜尋其他任意開源專案的程式碼的時候——可以自己執行一下 for-code-reading 命令在本地建立一個程式碼索引,然後用 beagrep -e 'hello world' 命令搜尋“hello world”。

關於它的更多用法請參考 5.23

2.3 編譯

使用 sse 命令,選擇 build 子命令。裡面會問你各種詳細的編譯選項,最後再告訴你下次你自己想不通過 sse 選擇,直接啟動編譯的話,該使用什麼簡單的命令。

2.3.1 system-config 下編譯詳解

一般來講,安卓的開發、除錯過程中,有 3 種編譯方法,分別是:全編、只編某個目錄、只編某個目錄及其依賴。對應的命令分別是 makemmmma

但上面的 3 個命令使用起來比較麻煩(有坑,參考 5.44 和 5.44.1),所以在 system-config 下,提供了對應的 3 個命令分別是 android-makemmmma

安卓官方命令 system-config 封裝
. build/envsetup.sh N/A(system-config 下強烈建議不要使用,各個編譯命令應該自動呼叫此指令碼)
lunch lunch(但提供了更友好的選擇介面)
make(全編,必須先 lunch) android-make(全編,如果沒有 lunch 過,自動幫你 lunch)
make systemimage android-make systemimage(引數一致)
make showcommands(編譯時列印呼叫的命令) android-make -v
mm(只編當前目錄) mm(是一樣的,但需要的話會自動 lunch)或 mm -q
mm showcommands MODULE (只編某模組,並顯示命令) mm -v MODULE
mma(只編當前目錄及其依賴) mma(自動 lunch)
沒有這樣的功能 mm-adb(用 mm 編譯,並把有更新的 /system 下檔案用 adb push 到手機上)

針對上面這 3 種編譯方法,建議平時儘量少用全編(因為實在太浪費時間),除非你除錯問題確實有需要的話,再考慮用全編。

同時第一次編譯程式碼時,一般必須要用 mma,因為依賴還沒有編出來。

後續在除錯自己相關的模組時,儘量用 mm,不停的快速迭代。注意 system-config 的 mm 有個 -q 引數,可以讓它的編譯變得更快。

除此之外,如果有其他的編譯需求,比如編譯 ota 包、編譯刷機包,等等,請參考 sse build 裡使用的編譯命令。

專案後期除錯的過程中,可能還會碰到各種簽名相關的問題,請參考 5.12

2.4 編譯出錯資訊快速定位

sse build 的終端列印輸出同時還會儲存在 .repo/build.log 檔案下,可以使用 grep-errors .repo/build.log 命令快速定位。

如果你有一個自己的出錯編譯輸出檔案 BUILD-OUTPUT.TXT ,也可以試試用 grep-errors BUILD-OUTPUT.TXT 看看能不能快速定位。

如果發生了編譯錯誤,認為不是自己的程式碼的改動引起的,可以參考 faq:5.15.

很多同事編譯安卓的時候,喜歡上來就用 make 命令,但這樣的話出錯之後查詢起真正出錯的那一行就非常麻煩,只能用滑鼠回滾終端文字,然後用肉眼檢視;或者再單獨把這些輸出從終端上拷貝下來,再儲存到一個檔案裡,然後再自己用編輯器或 grep 等文字處理工具檢視。如果用 sse build 或 android-make 命令編譯的話,就完全不用操心這些問題了。

這個命令的工作原理很簡單:

  1. 用常見的出錯關鍵字過濾一遍 build.log
  2. 用常見的“非出錯關鍵字”再過濾一遍步驟 1 的結果,儘可能的消除“噪聲”

目前公司的持續整合就使用了這個指令碼,可以幫大家快速的定位是哪個模組出現了編譯錯誤。

2.5 提交程式碼 review

執行 gerrit-push-review 命令。加 --help 引數可以顯示幫助:

使用方法:gerrit-push-review [OPTIONS]... REVIEWERS...

這是一個方便往 Gerrit 上 push review 的指令碼。功能:
    - 預設自動計算該往哪條分支上提交;也可自己用 -b 引數指定
    - 允許全域性 push(當前目錄下有 .repo 目錄) 或單個 git 倉庫 push(當前目錄下有 .git)
    - push 的同時允許指定 Reviewers(後面跟一個或多個同事的賬號作為引數)

所有功能最後都是呼叫 git 命令實現,請閱讀指令碼原始碼並參考 git 官方文件,比如 git push --help(git push 最簡單的呼叫方法是不帶任何引數,git 會自動幫你計算往哪個 remote 伺服器、哪條分支上 push 哪個 commit,這是最方便的用法,此指令碼就是受此啟發而寫出來的對 gerrit push review 操作的封裝)。

Options and arguments:
      --[no-]do-rebase        要不要先 rebase 到遠端伺服器分支,預設是要 rebase
      --[no-]draft            提一個 draft review,打個草稿,繼續完善,暫時不開放給其他同事
  -t, --patch-topic=PATCH_TOPIC
                              要全域性多個 repo push 或一個 repo 下 push 多個 patch 的時候,加個 topic,方便 review
  -r, --push-rev=PUSH_REV     要 push 哪個 revision,預設是 HEAD
      --repo-remote=REPO_REMOTE
                              提給哪個 repo remote,預設是 REPO_REMOTE 變數或 git remote 或 repo manifest 中設定的 remote(依此順序)
      --review-push-url=REVIEW_PUSH_URL
                              使用自己制定的 push url,不要讓指令碼自動計算(適於用:自動計算結果有誤;測試等)
      --[no-]skip-gerrit      不要提到 gerrit 上 review,直接往 refs/heads/ 上 push
  -R, --[no-]skip-local-review提交前不要做 local review(鼓勵做 local review!)
  -b, --to-branch=TO_BRANCH   提到哪個分支上,若不指定,預設是 git 當前在跟蹤的分支或 repo manifest 中設定的分支

2.6 獲取 CM 每天編譯的刷機包

每天主線編譯的刷機包放在共享目錄下 \\172.16.2.240\flash\daily (Linux 下路徑:\\172.16.2.240\flash\daily 參考:版本共享目錄變更說明),各產品按產品名劃分。

每個刷機包內有關於該版本的如下資訊:

  1. manifest.xml 和 oem-manifest.xml,這個是 repo manifest 命令 export 出來的 manifest 快照,裡面有每個專案的 git 版本號。
  2. change.log 和 oem-change.log,與上一個版本的 build 相比,都有哪些改動。
  3. repo-init.txt,該 build 是怎麼 repo init 的,裡面包含了完整的 repo init 命令,你可以拿來參考,比如應該用哪個 manifest.xml 去做 repo init。

共享資料夾的訪問方法(域名、使用者名稱、密碼等資訊)參考 本條常見問答

2.6.1 版本共享目錄變更說明

原版本共享伺服器 2.225 成為了大家工作效率的嚴重瓶頸,測試同事想刷個機結果出現了一早上都沒有刷完的現象,並導致 CM 這邊無法按正常速度編譯出版本(編譯完成後把版本拷貝到共享目錄的操作一早上也沒有完成)。

因此,將版本共享伺服器更換成 2.240,繼續對所有同事開放。

同時,每個 team 可以提供 1~3 臺固定 IP 的刷機專用 PC,通過 2.18 上開放白名單允許這些專用 PC 訪問。在刷機專用 PC 上獲取版本要注意:一定要將版本拷到本地!不要線上刷機!這樣,每個版本一般只要拷貝一次就可以,後續有同事要刷這個版本(或者通過網路從專用 PC 上下載這個版本),都不需要再佔用伺服器資源。如發現在專用 PC 上線上刷機的情況(比如一天內多次下載同一版本),CM 會考慮給各 Leader 發郵件通報“批評”、下調該 PC 服務優先順序等措施。

各 Team 的刷機專用 PC 申請記錄如下(如需知道機器物理位置,請諮詢申請人):

申請人 部門 ip
門善良 3 樓測試部 172.16.5.104
張莉婷 效能測試組 172.16.5.20、172.16.5.50
羅晨 封閉開發 192.168.50.254
武照東 成都測試 172.19.4.167
張保永 Trident 產品測試 172.16.6.66、172.16.6.67
唐瑜 測試 172.16.5.24
李妍 使用者反饋組 172.16.5.136
王洋 BSP 測試組 172.16.7.48、172.16.6.126
閆慧 BSP 172.16.15.97 172.16.15.104 172.16.26.22
米曉川 Frameworks 172.16.15.131

2.7 刷機

在 Windows 下刷機需要使用工具組提供的刷機工具,在 \\share.smartisan.cn\share\Tools\DownloadTool 下,具體需要怎麼使用、該用哪個版本請諮詢測試組會使用刷機工具的同事。

BSP 組的同事針對較新的高通平臺產品,提供了 Linux 下的刷機方法,如果你發現在刷機包目錄下有 edl-flash.sh 檔案的話,你可以在刷機包共享目錄下直接使用 sse .edl 命令進行刷機。

較老的高通平臺產品,比如 T1、T2 和 U1,工程師在 Linux 下刷機可以使用手機版 Debian 系統,具體請參考 這篇部落格(注意:此方法已很久沒有維護)。

2.8 獲取 Build 符號資訊

CM 每天的 Build,都會帶相應的符號資訊,主要分為 3 類:

  1. 高通 oem 符號資訊
  2. Kernel 符號資訊(也即 vmlinux 檔案)
  3. System 符號資訊

其中,1 和 2 的資訊對 Bsp 的同事解 Ramdump 問題比較有用,3 的資訊對做上層開發的同事比較有用。

在這個頁面上可以通過輸入 3 種資訊(Linux 版本字串,刷機包目錄名,系統編譯時間)中的任意一種,然後通過資料庫查詢,查到對應的 symbol 版本。

其中 Linux 版本字串,可以通過以下命令從 ramdump 檔案中獲取,這在有時 Tester 給出的版本不正確時非常有用:

strings DDRCS1.BIN |grep 'Linux version'

2.9 獲取 OTA 版本資訊

日常和 Daily 編譯的 OTA 版本的連結,可以通過以下頁面獲取。

3 工作流程的詳解版

3.1 git 開發環境的配置詳解

請參考 這篇部落格

  1. 需要配置 ~/.gitconfig ,加入 user.name 和 user.email 的配置
  2. 生成 ssh rsa 公私鑰,登入 Gerrit 伺服器(有兩臺,一臺是 公司內部的,另一臺是 對 ODM 廠商開放的 ),加入你的公鑰。
  3. 安裝 Oracle 提供的 jdk6。這個已經放到 Gerrit 程式碼伺服器上,直接下載即可,免安裝。
  4. 用 apt-get install 安裝一些需要的系統程式(如果某程式安裝不上的話,請忽略,隨著 ubuntu 版本升級,有可能會出現這種情況,system-config 的安裝指令碼會自動處理這種錯誤)。

    ant libc6-dev-i386 python-crypto
    bc libc6-i386 python-mysqldb
    bison libdatetime-perl python3
    build-essential liblua5.2-dev qt5-default
    ccache libssl-dev qtchooser
    command-not-found libstring-approx-perl rsync
    curl libstring-shellquote-perl schedtool
    daemon libswitch-perl strace
    expect libtext-csv-perl subversion
    file libtext-glob-perl time
    flex libtext-levenshtein-perl unzip
    genisoimage libxml-parser-perl wget
    gitk libxml-simple-perl wmctrl
    gperf libxml2-utils xbindkeys
    htop lua5.2 xclip
    iputils-ping mysql-client xmlstarlet
    jq net-tools xorriso
    less openssh-client zenity
    lftp openssh-server zip
    lib32stdc++6 pkg-config zlib1g-dev
    lib32z1 policycoreutils

3.1.1 如何在別的系統上配置程式碼下載環境

注意:本人自己目前並不使用 Windows 或 Mac 系統,因此本條目建議僅供參考。如果有問題的話建議自己多折騰,本人無法提供技術支援。

如果自己沒有裝過 Linux 環境的話,建議考慮用虛擬機器裝個 Linux 作為參考系統。

在 Windows 系統下,Win10 的話建議裝個 Ubuntu,然後一切流程就跟這篇文件裡提供的一樣了。Win10 以前的系統建議裝個 cygwin,裝完以後你可以得到一個 Unix 的環境。

Mac 系統的話開發環境是比較像 Linux 的。

要在這些系統上用 git 連線公司的伺服器的話,請參考配置好的 Linux 環境下的 ~/.gitconfig 檔案和 ~/.ssh 目錄(尤其是 ~/.ssh/config 檔案)。建議可以直接把這些檔案拷貝過去試試。另外注意 ~/.ssh 目錄下的檔案有特殊許可權要求,請用 ls -l 檢視,並在需要的時候用 chmod 修改。

如需使用 repo 命令,可以在配好 git 連線 gerrit 之後,從 ssh://gerrit.smartisan.cn/googlesource/git-repo clone 下來後把裡面的 repo 檔案拷貝到自己的 PATH 路徑裡,然後就可以開始使用了。

3.2 獲取程式碼詳解

我們的程式碼管理工具主要有這 3 個:

git

最基本的程式碼版本管理工具,參考 git 簡介

repo

把 N 個 git repo 集中管理的工具,參考 repo 簡介

gerrit

提供網頁版和命令列版本的程式碼 review 功能,參考 gerrit 簡介

3.2.1 下載各產品主線安卓程式碼

我們下載安卓專案程式碼,用的是谷歌提供的 repo 命令,裡面最關鍵的三個引數分別是:manifest 倉儲的地址(-u URL),manifest 倉儲的分支(-b BRANCH),manifest.xml 檔案的名字(-m MANIFEST.XML)。

以 T1 專案的主線為例,這三個引數分別是 ssh://gerrit.smartisan.cn/qualcomm/platform/manifest.gitsanfranciscosfo-rom.xml ,所以下載程式碼的命令是:

repo init -u ssh://gerrit.smartisan.cn/qualcomm/platform/manifest.git \
     -b sanfrancisco \
     -m sfo-rom.xml \
     --repo-url ssh://gerrit.smartisan.cn/googlesource/git-repo \
     --reference ~/src/android-mirror/ \
     --depth 1

repo sync -j4 -c -d
  • 注意上面的 --depth 1 引數,這個引數允許你只獲取伺服器上最新的一個提交,這樣是最節省伺服器頻寬、本地儲存空間的。如果不加這個引數的話,repo sync 的時候會把所有的歷史紀錄全部下載下來,下載一份程式碼的時候可能從 --depth 1 的 10 分鐘左右變成 1 小時左右。

    如果某些倉儲需要檢視其完整歷史紀錄的話,可以在該倉儲下使用 sse unshallow-source-code 命令把“淺”的歷史紀錄全部補齊。

  • 注意上面的 --reference 引數,這裡指向了之前建立的 repo 映象。

    以前我們的流程裡鼓勵大家使用 create-repo-mirror 命令自己建立一個所有倉庫的映象,之後下載程式碼時可以重用這份映象,但現在發現這個映象佔用的空間太大了,已經達到 200~300 G 之巨,並且使用了 --depth 1 引數後,映象帶來的效果也大大降低了,因此現在已經不鼓勵大家使用 create-repo-mirror 建立完整映象了。

    但是,上面 --depth 1 裡提到的 sse unshallow-source-code 這個機制,如果能重用映象資料的話,也是非常有幫助的,所以繼續保留這個 --reference ~/src/android-mirror/ 引數。

  • 注意上面 repo sync 命令的 -j4 選項,意思是以 4 個程序(jobs)同步獲取程式碼,能加快速度。這裡要提醒大家,千萬不要設一個巨大的值,會害人害己,使自己的機器變得劇慢,同時 gerrit 伺服器也變得劇慢,其他同事提個 review 都大受影響。尤其是有些同事連線到遠端 Linux 上工作,直接指定 -j64,但他自己是感覺不到本地機器的桌面變慢的,因為 -j64 是在遠端執行的!對於這樣的濫用伺服器行為,一經發現,將自動封禁其賬號 2 小時並通知其主管。
  • 關於上面的 –repo-url 引數,repo 命令本身只是一個簡單的初始指令碼,它後臺需要一整套 python 指令碼的支援。repo init 預設會把這套 python 指令碼從谷歌的官網上下載回來,但因為 GFW 的關係,谷歌官網無法訪問,所以我們在本地做了個映象,並且大家需要指定 --repo-url ssh://gerrit.smartisan.cn/googlesource/git-repo

其他不同產品、不同分支安卓程式碼的 MANIFEST.XML,參考 安卓 MANIFESTS 列表

3.2.2 下載各產品主線 OEM 程式碼

一般只有 BSP 的同事需要下載 OEM 程式碼,其他同事可以跳過此節。

高通 OEM 程式碼(如果有的話),我們也是用 repo 管理的,所以下載命令只有 -m MANIFEST.XML 引數不同,以 T1 主線 OEM 程式碼為例:

repo init -u ssh://gerrit.smartisan.cn/qualcomm/platform/manifest.git \
     -b sanfrancisco \
     -m sfo-rom-oem.xml \
     --repo-url ssh://gerrit.smartisan.cn/googlesource/git-repo \
     --reference ~/src/android-mirror/

repo sync -j4 -c -d

同樣,其他不同產品、不同分支 OEM 程式碼的 MANIFEST.XML,參考 OEM MANIFESTS 列表

3.2.3 安卓 MANIFESTS 列表

公司的各個產品的主線、MOL、高通線程式碼 manifest 資訊,可以通過以下頁面獲取:

內容如下表,各行的格式是 android_manifests[產品名-分支名]=manifest.xml 檔名

其中產品名顧名思義,如 t1、u1 等。分支名大家最需要關注的是 dev ,這個即代表主線開發分支。

再次強烈建議使用 sse 命令,它會自動幫你確定該使用哪個 manifest。

old_android_manifests[aosp-$aosp_branch]=aosp-$aosp_branch.xml
android_manifests[aries-dev]=aries-rom.xml
android_manifests[aries-smandroid]=smandroid/sdm710-r2.0_010.xml
android_manifests[ocean-6.2-mol]=mol/ocean-rom-6.2.0.xml
android_manifests[ocean-6.6-mol]=mol/ocean-rom-6.6.0.xml
android_manifests[ocean-6.6-rls]=rls/ocean-mol-6.6.0.xml
android_manifests[ocean-cta]=cta/ocean-rom-20180607.xml
android_manifests[ocean-cts]=cts/ocean-mol-6.2.0-20180702.xml
android_manifests[ocean-dc]=dc/ocean-mol-6.2-20180719.xml
android_manifests[ocean-dev]=ocean-rom.xml
android_manifests[ocean-factory]=factory/ocean-rom-20180530.xml
android_manifests[ocean-tiyan]=sandbox/ocean-rom-6.6.0-mol-tiyan.xml
android_manifests[ocean-trinity-6.6-mol]=mol/ocean-trinity-6.6.0.xml
android_manifests[ocean-trinity]=ocean-trinity.xml
android_manifests[odin-4.4-mol]=mol/odin-rom-4.4.0.xml
android_manifests[odin-mol-4.1]=mol/odin-rom-4.1.0.xml
android_manifests[osborn-4.4-mol]=mol/osborn-rom-4.4.0.xml
android_manifests[osborn-mol-4.1]=mol/osborn-rom-4.1.0.xml
android_manifests[osborn-trinity]=feature/osborn-trinity.xml
android_manifests[oscar-4.4-mol]=ontim-oscar/mol/oscar-rom-4.4.0.xml
android_manifests[oscar-mol-4.1]=ontim-oscar/mol/oscar-rom-4.1.0.xml
android_manifests[oscar-oversea]=ontim-oscar/oversea/oscar-mol-4.1.0-20180409.xml
android_manifests[oxford-4.4-mol]=mol/osborn-rom-4.4.0-oxford.xml
android_manifests[playground-dev]=playground/smartcm-test.xml
android_manifests[surabaya-4.4-mol]=mol/surabaya-rom-4.4.0.xml
android_manifests[t1-dev]=sfo-rom.xml
android_manifests[t2-4.4-mol]=mol/icesky-rom-4.4.0.xml
android_manifests[t2-mol-4.1]=mol/icesky-rom-4.1.0.xml
android_manifests[trident-4.4-mol]=mol/trident-rom-4.4.0.xml
android_manifests[trident-6.1-mol]=mol/trident-rom-6.1.0.xml
android_manifests[trident-6.6-tiyan]=sandbox/trident-rom-6.6-mol-tiyan.xml
android_manifests[trident-cta]=cta/trident-rom-20180129.xml
android_manifests[trident-dc]=dc/trident-rom-mol-4.4.0.xml
android_manifests[trident-dev]=trident-rom.xml
android_manifests[trident-factory]=factory/trident-rom-20180404.xml
android_manifests[trident-trinity-6.6-mol]=mol/trident-trinity-6.6.0.xml
android_manifests[trident-trinity-factory]=factory/trident-trinity-20180827.xml
android_manifests[trident-trinity]=trident-trinity.xml
android_manifests[u1-4.4-mol]=mol/u1-l-rom-4.4.0.xml
android_manifests[u1-mol-4.1]=mol/u1-l-rom-4.1.0.xml

3.2.4 OEM MANIFESTS 列表

下表中,各行的格式是 oem_manifests[產品名-分支名]=manifest.xml 檔名

其中產品名顧名思義,如 t1、u1 等。分支名大家最需要關注的是 dev ,這個即代表主線開發分支。

再次強烈建議使用 sse 命令,它會自動幫你確定該使用哪個 manifest。

oem_manifests[product−smandroid]=
{oem_manifests[$key]}
oem_manifests[aries-dev]=smandroid/oem-sdm710-r2.0_010.xml
oem_manifests[aries-smandroid]=smandroid/oem-sdm710-r2.0_010.xml
oem_manifests[ocean-6.2-mol]=mol/oem-ocean-rom-6.2.0.xml
oem_manifests[ocean-6.6-mol]=mol/oem-ocean-rom-6.6.0.xml
oem_manifests[ocean-6.6-rls]=rls/oem-ocean-mol-6.6.0.xml
oem_manifests[ocean-cta]=cta/oem-ocean-rom-20180607.xml
oem_manifests[ocean-cts]=cts/oem-ocean-mol-6.2.0-20180702.xml
oem_manifests[ocean-dc]=dc/oem-ocean-mol-6.2-20180719.xml
oem_manifests[ocean-dev]=smandroid/oem-ocean-sdm710-r007.0.xml
oem_manifests[ocean-factory]=factory/oem-ocean-rom-20180530.xml
oem_manifests[ocean-trinity-6.6-mol]=mol/oem-ocean-trinity-6.6.0.xml
oem_manifests[odin-4.4-mol]=mol/oem-odin-rom-4.1.0.xml
oem_manifests[odin-mol-4.1]=mol/oem-odin-rom-4.1.0.xml
oem_manifests[osborn-4.4-mol]=mol/oem-osborn-rom-4.4.0.xml
oem_manifests[osborn-mol-4.1]=mol/oem-osborn-rom-4.1.0.xml
oem_manifests[oscar-4.4-mol]=ontim-oscar/mol/oem-oscar-rom-4.4.0.xml
oem_manifests[oscar-mol-4.1]=odm/ontim/oscar/mol/oem-oscar-rom-4.1.0.xml
oem_manifests[oscar-oversea]=ontim-oscar/oversea/oem-oscar-mol-4.1.0-20180409.xml
oem_manifests[oxford-4.4-mol]=mol/oem-osborn-rom-4.4.0-oxford.xml
oem_manifests[playground-dev]=playground/oem-smartcm-test.xml
oem_manifests[surabaya-4.4-mol]=mol/oem-surabaya-rom-3.6.0.xml
oem_manifests[t1-dev]=sfo-rom-oem.xml
oem_manifests[t2-4.4-mol]=mol/oem-icesky-rom-3.7.0.xml
oem_manifests[t2-mol-4.1]=mol/oem-icesky-rom-3.7.0.xml
oem_manifests[trident-4.4-mol]=mol/oem-trident-rom-4.4.0.xml
oem_manifests[trident-6.1-mol]=mol/oem-trident-rom-6.1.0.xml
oem_manifests[trident-cta]=cta/oem-trident-rom-20180129.xml
oem_manifests[trident-dc]=dc/oem-trident-rom-mol-4.4.0.xml
oem_manifests[trident-dev]=smandroid/oem-trident-sdm845-r00058.1.xml
oem_manifests[trident-factory]=factory/oem-trident-rom-20180404.xml
oem_manifests[trident-trinity-6.6-mol]=mol/oem-trident-trinity-6.6.0.xml
oem_manifests[trident-trinity-factory]=factory/oem-trident-trinity-20180827.xml
oem_manifests[u1-4.4-mol]=mol/oem-u1-l-rom-3.6.0.xml
oem_manifests[u1-mol-4.1]=mol/oem-u1-l-rom-3.6.0.xml
oem_manifests[如果沒有找到你想要的產品、配置,你可能需要升級system-config了]=sfo-rom-oem.xml

3.3 編譯詳解

3.3.1 安卓官方編譯方法

注意: 這裡記錄一下安卓官方編譯方法,只是為了完整起見。在 system-config 環境下,有更簡潔、更不容易出錯的方法,強烈建議不要使用安卓官方的編譯命令和流程(如何使用 system-config 的編譯方法請參考 2.3.1。安卓官方流程的缺點請參考 5.44 和 5.44.1)。

安卓官方提供的三步編譯方法如下:

  1. 引入安卓 shell 環境配置: . build/envsetup.sh (system-config 環境下完全不需要這一步)
  2. 設定編譯選項,以 T1 專案為例, lunch msm8974sfo_lte-userdebug 或 lunch msm8974sfo-userdebug (system-config 環境下,也幾乎完全不需要這一步)
  3. 編譯: make、mm、mma(system-config 環境下,對應的命令分別是 make: android-makemm: mmmma: mma,其中 make 是一個系統命令,所以必須提供一個不一樣的名字,另外兩個是安卓自己定義的,所以 system-config 直接覆蓋了)。其中:
    • make 做安卓全域性編譯,它會掃描並讀取所有 Android.mk,確定所有依賴關係,最後編譯出 boot.img、system.img 等安卓標準檔案。

      make 可以加一些引數,比如 -jN,以 N 個執行緒進行並行編譯,以加快編譯速度。你可以試試不同的 N 值,一般以你的 CPU 核數*1~2 是比較合理的。取太小的值會使編譯變慢,取太大的值會使系統變得劇慢,並且也會使編譯變慢(系統大部分時間用於程序切換,而不是真正的工作進展)。

      還可以給 make 指定其它編譯目標(make targets),比如 bootimage,這樣的話就可以只編譯 boot.img。

    • mm 做當前目錄編譯,並且不讀取所有 Android.mk,所以其編譯速度是非常快的。但前提是你已經做過完整編譯(或者至少當前目錄的所有依賴都已經編譯過——見下一條 mma 命令)。
    • mma 做當前目錄編譯,並讀取所有 Android.mk,並且編譯當前目錄下的 make targets 的所有依賴。

大部分時候,建議使用 mm。第一次編譯,推薦使用 mma。實在沒辦法的情況下,才使用 make 做全域性編譯。因為安卓全域性編譯實在是太費時間了。

mma 的工作原理是解析出當前目錄下的 make targets,然後呼叫 make 並傳入相應的 make targets。

mm 的工作原理是解析出當前目錄下的 make targets,然後呼叫 make 並傳入相應的 make targets,並且設定 ONE_SHOT_MAKEFILE=$M 環境變數,其中 $M 是當前目錄下的 Android.mk 在程式碼樹中的路徑(就是因為這個變數的存在,使安卓編譯系統不再查詢並讀取其他 Android.mk)。

3.3.2 system-config 提供的更佳編譯方法

接下來要講一下安卓官方的編譯方法存在的嚴重問題及 system-config 提供的解決方案。

  1. 不支援刷機包、ota 包編譯。這是因為我們的程式碼並不是直接來自谷歌,而是來自晶片廠商高通。高通的刷機包對 system.img、userdata.img 有自己額外的定製(拆分成多個檔案);並且還有其他額外的分割槽對應的刷機檔案。要編出這些檔案,唯一的途徑是通過 system-config。可以使用介面友好的 sse 命令(一步一步有引導,不需要文件)。也可以使用更直接的 smartcm 命令,見 這篇部落格
  2. 三步編譯方法使用比較繁瑣

    每次開啟新的視窗都要重新設一遍,每次換到不同的產品目錄下工作也要重新設一遍。

為此,我在 system-config 裡封裝了一個 android-make 指令碼,避免了以上問題。以 T1 為例,用法如下:

android-make -c msm8974sfo_lte-userdebug
  • 用過一次 -c 選項之後,system-config 會幫你把配置儲存在 buildspec.mk 裡(注意這是谷歌官方支援的機制),以後就可以不必每次都加這個 -c 引數,除非你想換一個產品配置。
  • 另外, android-make -c 之後直接輸入 lte 並按 Tab 鍵,是支援智慧補齊的。
  • 如果你之前沒有用過 -c 引數指定過,直接執行 android-make 也是可以的,它會提示你選哪個產品配置。注意它的提示選項比安卓官方的要友好很多!你可以直接輸入數字(像官方的 lunch 那樣),也可以輸入 lte 回車,縮小選擇範圍之後再輸入數字回車或直接回車(如果要選第一個選項的話)。
  • mm 和 mma 命令被單獨封裝成指令碼,可以直接呼叫。
  • 提供 mm-adb 命令,允許把 mm 編譯命令更新了的 system 目錄下的檔案,直接 push 到手機上。

3.3.3 BSP 同事需要掌握的編譯方法

android-make bootimage

只編譯 boot.img

android-make kernel

直接用 mm 的方式編譯 kernel,省去讀取所有 Android.mk 的時間

注意在第一次使用此命令之前,需要保證之前已經編譯過 bootimage

android-make lk

直接用 mm 的方式編譯 lk bootloader

android-make selinux

直接用 mm 的方式編譯 sepolicy

以上三個命令,僅限於高通平臺,如有無法使用的情況,請諮詢自己的 Team Leader 更常規的做法。歡迎 BSP 工程師協助維護此指令碼的這些用法。

sse

允許編譯 oem 包

NON-HLOS.bin 的編譯方法

1.同步 oem-release 倉庫,並在該目錄下修改程式碼

2.執行 oem-release/common/build 下的 python 指令碼生成 NON-HLOS.bin , 以 T3 專案為例,操作如下:

cd common/build
python build.py --nonhlos

3.驗證成功後在 oem 的程式碼目錄下(與 oem-release 目錄在同一層的 modem_proc、common、others 等倉庫)修改程式碼,並提交 review。

3.3.4 如何編譯高通 oem

最簡單的辦法是通過 sse 的 build 子命令,裡面有關於 oem 編譯的引導。

以下是自己手動執行的步驟:

  1. 安裝、升級 system-config
  2. 執行 setup-oem-compiler
  3. 執行 oem 程式碼目錄下的 oem-cm-scripts/build-all.sh 檔案

    上面這個命令會編譯所有子系統。如果只想編譯某個子系統,比如 rpm,可以開啟 oem-cm-scripts/build-all.sh 檔案搜一下相關子系統的編譯命令。

    2017-04-25 更新 如果需要只編譯某個子系統,也可以試試 sse 的 build-oem 子命令。

在開發 oem 的過程中,有一些常見問題,在這裡說明一下。

高通的程式碼分為兩部分發布,一部分是開源的安卓程式碼,另一部分是不開源的,一般稱其為 oem 程式碼。在 oem 程式碼裡,又分為兩部分,有一部分是要跟安卓合併在一起的,即 vendor/qcom/proprietary 程式碼;另一部分則是各種子系統的程式碼,比如 boot_imagesmodem_proc,等等。

子系統應該如何編譯,高通有專門的 release notes 文件,那是最權威的參考資料,如果各位對 oem-cm-scripts/build-all.sh 裡的編譯步驟還有疑問的話,請參考高通的文件。工程師可以找自己的 Leader 問一下,Team 內部有沒有一個專門的分享這些文件的地方。

這些子系統的程式碼編譯之後,其輸出檔案最後會出現在手機上,一般可能有以下幾種方式,請大家自己研究一下自己的模組是如何釋出到手機裡的:

  1. 出現在手機上的某個分割槽中,比如 boot_images 下最後編譯出一個 bootloader,直接刷到 sbl 或 xbl 分割槽上。
  2. 拼到 NON-HLOS.bin 檔案中,最後刷到手機的對應分割槽上(一般叫 modem 分割槽,大家可以自己在刷機包目錄下執行 rgrep NON-HLOS.bin *.xml 命令檢視對應的檔案刷到了哪個分割槽)。

    NON-HLOS.bin 其實是一個 FAT 檔案系統,大家可以自己在 Linux PC 上 mount、修改它裡面的內容。

    NON-HLOS.bin 裡最後會打包進去什麼檔案,都是通過 oem 程式碼下的 contents.xml(在新的專案下一般其位置在 others/contents.xml)來管理的,大家如果需要往裡面打包新的檔案,請自己修改這個.xml 檔案。

    打包 NON-HLOS.bin 的命令一般在 common/build/build.py(以前的專案下的名字叫 update_common_info.py),具體的用法請參考對應的安卓程式碼專案下的 flashing-files/.scripts/update-common-info.sh 檔案(如果沒有這個檔案的話,那該產品使用的就是 ~/system-config/src/github/smartcm/update-common-info.sh 檔案)。因為我們 CM 打包 NON-HLOS.bin 都是在編譯安卓之後,所以這個指令碼跟安卓程式碼放在一起,各位工程師使用這個指令碼,應該是在 oem 程式碼下開發的時候,這時候執行這個指令碼,最後 system.img 和 userdata.img 檔案肯定打不出來,因此是會出錯的,但應該不影響 NON-HLOS.bin 被正確打包出來,這一點請注意。

  3. 通過安卓的 Android.mk 檔案裡的配置,把某些韌體最後拷貝到對應的 /system 或 /vendor 目錄下。

另外,根據自己的研發過程中,要更新的手機上的檔案及其方法的不同,可能你編譯 oem 的方法也不同。如果是一個獨立的模組,比如 boot_images,那可能單獨編譯它一個就可以了;如果是 NON-HLOS.bin 這樣由 N 個模組打包在一起,那你在第一次打包之前,最好是把整個 oem 程式碼都編一遍,不然打包出來的 NON-HLOS.bin 裡可能會少檔案或者用到高通 prebuild 的版本,最後都會引起不必要的問題。

最後在這裡專門講一下高通的 oem 子系統程式碼和安卓程式碼最後是怎麼拼在一起,出刷機包的。

  1. oem-release 的工作原理

    因為 oem 程式碼及其編譯結果及其龐大,非常不利於版本管理和釋出追蹤,所以 CM 使用了一個 oem-release 的目錄,專門用來存放最後拼在一起出刷機包的時候要用到的 oem 檔案。

    其具體的工作原理是這樣的:

    1. 在專案開始的時候,CM 會拿高通的基線程式碼跑一遍生成刷機包的流程
    2. 在這個流程中,生成刷機包的時候被用到的檔案(根據檔案訪問、修改的時間戳判斷),CM 會把它們拷貝到 oem-release 目錄,拷貝的過程保持原有的目錄結構不變,整個平移過來

      因為這個檔案集合比整個 oem 的編譯結果要小很多,只有幾⼗兆,所以是可以用版本管理的。整個 oem 程式碼+編譯結果有幾十個 G,根本無法進行長期的版本管理。

    3. 把 oem-release 目錄用 git 管理起來,每次 oem 編譯完,都要從 oem 程式碼目錄往 oem-release 目錄下重新拷貝更新其所有檔案。

    結合以上關於 oem release 流程的說明,你在開發的過程中,如果發現:

    • 需要往手機上增加一個新的檔案(或者往 NON-HLOS.bin 裡增加一個新的檔案),請注意一定要修改以下兩個檔案之一:

      • 修改 contents.xml 檔案,把你要打包到 NON-HLOS.bin 裡的檔案參考其已有其他檔案的格式修改一下。

        這種改法適用於要打包到 NON-HLOS.bin 裡的檔案。

        (最近 CM 剛剛更新了一下編譯指令碼,把 contents.xml 裡列的檔案也都會自動釋出到 oem-release 裡去。)

      • 修改 oem-cm-scripts/oem-release-copy-files.txt 檔案,把你要加的檔案在 oem 程式碼下的相對路徑(可以使用 system-config 下的 ap FILE 命令獲取 FILE 在 gerrit/repo 管理的程式碼目錄下的相對路徑並自動放到剪貼簿裡,ap = Android Path,類似的命令還有很多,參考 ~/system-config/bin/ap)填進去。

        這種改法適用於最後要通過其他方法打包到手機上的檔案

      只有做了這些修改,你的新增檔案才會被髮布到 oem-release 倉庫裡去,最後安卓編譯的時候才能看到這個新檔案。

    • 需要在 oem 程式碼裡刪除一個已經被髮布到 oem-release 目錄下檔案

      目前為止(2017-12-19),我們在專案中還從未遇到過這樣的需求,但以後一旦遇到的話,請一定記住要同時刪除 oem-release 目錄下的對應的檔案。

      否則的話,oem 的編譯會出錯。最後在 release 的時候發現有一個需要 release 的檔案不存在。

3.4 提交程式碼規範詳解

大家改完程式碼之後,用 git add、git commit -s 命令提交到本地 git 歷史中。

提交的時候,注意我們的 commit message 格式要求:

  1. 全部英文
  2. 第一行為對改動的簡要總結,長度不超過 50。(某些編輯器會對超出的文字用不一樣的顏色顯示)
  3. 第二行為空行
  4. 第三行開始,是對改動的詳細介紹,可以是多行內容,每行長度不超過 72,中間也可以有空行,用於分段。
  5. 空行
  6. Ticket: Mantis ticket number,每個修改需要在 Mantis 系統 中存在相應的 Bug Ticket,在這裡記錄 Ticket number。

    每個改動都有對應的 Ticket,對測試 Team 的意義十分重大,可以允許他們檢查每天的版本都有哪些 Bug 被 Fix 了,然後更有針對性、更有效的進行測試

  7. Change-Id:, 系統自動生成1

    Change-Id: 欄位十分重要,是 Gerrit 系統判斷兩個 git commit 邏輯上是否屬於同一個 Patch 的依據。在一條 git 分支上,一個 Change-Id 只能出現一次。Change-Id 都是 gerrit 定製的 git commit hook 指令碼自動生成的,如果你發現跟 Change-Id 相關的錯誤,請參考 這條 faq 或諮詢自己的 Leader。

  8. Signed-off-by:,由 git commit 的 -s 引數自動新增

3.4.1 Commit message 示範

commit 1db22817cf935fb4d4ca1065c06a91dab4e416c7
Author: lixin <[email protected]>
Date:   Mon Nov 4 14:44:04 2013 +0800

    Add README file

    This file is used to define the environment variable, include repo
    url and git url

    Ticket: 0005001
    Change-Id: I8b5df9429fd2841d078eefcef354c3f8729fffbb
    Signed-off-by: lixin <[email protected]>

3.4.2 將程式碼 push 到 Gerrit 上進行 Review

注意以下的大部分步驟,使用 system-config 的 gerrit-push-review,都是可以自動處理的。

  1. 你需要確定當前程式碼倉儲對應的兩個引數:git remote 名,git branch 名。方法是通過開啟 .repo/manifest.xml 檔案進行察看,參考 manifest.xml 檔案格式

    以 T1 主線程式碼的 system/core 倉儲為例,git remote 名是 smartisan,git branch 名是 sfo-rom,以下示例命令均基於這兩個變例,實際使用時需自行修改,比如 T2 專案主線的 system/core 倉儲,git remote 不變,但需要將 git branch 名改為 icesky-rom;而 T2 專案主線的 packages/apps/EmailSmartisan 倉儲,則需要保持 git branch 名為 sfo-rom 不變(因為與 T1 共線),但 git remote 名要改為 smartisanos 。這麼複雜的規則,是我一再強調要用 gerrit-push-review 命令的原因。

  2. 確保你的程式碼是基於伺服器上最新版本

    git fetch -v smartisan sfo-rom # 或者用 repo sync -n -c .
    
    git rebase smartisan/sfo-rom
    

    如果 rebase 時有衝突的話,說明其他人已經進了新的程式碼,並且與你的改動衝突,你需要自己解衝突,有必要的話,需要與相關 patch 作者進行溝通。

  3. 用 git diff smartisan/sfo-rom 先自己在本地做一下 review
  4. push for review (注意此命令已經不能直接使用,必須先配置一下,請參考 為什麼不能直接 git push review 了

    git push smartisan HEAD:refs/for/sfo-rom
    

    在這個 push 命令的終端輸出中,會顯示該 Patch 相應的 Gerrit Review 網址

  5. 開啟相應的 Gerrit Review 網頁,新增你的 Team Leader 或相關工程師作為 Reviewers。

    如果你的 Reviewers Review 通過,那麼恭喜,流程到此結束,你的程式碼就進了伺服器了,可以去開發下一個功能、解下一個 Bug 了。

    如果你的 Reviewer 們提出問題,需要你進行修改的話,進入下一個步驟:

  6. 在原來的提交的基礎上,進行相應的改正,然後用 git commit --amend 命令再次提交。因為 --amend 引數的關係,原來的 commit message 會顯示出來,你可以編輯,但是!絕對不要改 Change-Id!因為在邏輯上,你還是在解決同一個問題。

    回到上面的第 2 步,重新來一遍(所以提升自己的水平,爭取一遍就把 Patch 通過,格外重要;使用 gerrit-push-review 命令, 簡化以上步驟,格外重要;同時,一次 Review 不過,多次 Review,格外重要——不管不顧程式碼質量,提上來就保證通過 Review,是給大家挖坑作死的節奏)。

    這時候在第 4 步時,因為你的 Change-Id 沒變,Gerrit Review 的網址也不會變,只是會生成一個新的 Patch Set。

    第 5 步新增 Reviewer 可以省略,Reviewer 們會自動收到郵件通知有新的 patch set 生成。

以下為一些注意事項、你可能需要知道的技巧:

  • 邏輯上獨立的改動,儘量用一個 Patch 解決。
  • 一個 Patch 裡面,一定不要包含多個問題的修改。
  • Gerrit 上的 Review,如果程式碼質量問題太多的話,可以考慮 abort。

3.4.3 確保所有倉儲均已提交

如果你確信自己只改了一個倉庫的話,在該倉庫下用上面的步驟就可以了。

如果你改過多個倉庫,並且有點不能確定到底哪些倉庫改了哪些沒改的話,system-config 下有個 repo-changes? 命令可以幫你,在安卓程式碼頂層目錄下執行,它會列出所有有改動的程式碼。

注意如果自己的程式碼倉庫有改動尚未提交至伺服器的情況下,也可以用 repo sync 同步程式碼,但是記住千萬不能使用 -d 引數,用過之後你的程式碼歷史就與伺服器取齊了。

(你可以用 git reflog 找回你最近的本地提交。但如果時間久遠了、或者你自己忘了哪些倉庫下可能有本地提交的話,就麻煩了。)

4 版本管理工具介紹

在安卓開發的過程中,主要要用到兩種版本管理工具,側重點各有不同。第一種叫 git。第二種叫 repo,是在 git 之上的一層封裝,可以把多個 git 倉儲(也叫 git repository,簡稱 git repo —— 這些術語容易混淆)放在一起來管理。

4.0.1 git 簡介

在安卓開發的過程中,我們這兩個工具都需要掌握,並且對其熟練程度要求是比較高的,這裡結合我們的工作流,講一下這兩個工具的常見用法。注意 git 和 repo 命令及其子命令都自帶幫助文件,比如下面馬上提到的 git init ,你可以用 git init --help 開啟其幫助慢慢的看,是英文的,如果有困難的話可以多看幾遍。強烈建議對下面提到的子命令,有任何疑問,立刻先開啟幫助文件看一下。

網上也有很多相關的資料,比如 git 官方網站上有本名為“Pro Git”的書,免費,並且有 中文版 的。也有些網頁,用示意圖的形示講解 git,比較方便理解,比如 這個中文網頁,感覺還不錯。當然,網上的資料質量參差不齊,請一定以官方的手冊為準。實在理解有困難的話,可以自己在本地做幾個實驗,說不定就搞清楚了。

在理想的情況下,我們只做一個專案,並且只有一條開發分支,並且只有一個人在做這個專案的開發,並且不需要做各種試錯。這種情況下,我們只需要掌握 3、4 條 git 命令就夠了:

git init

建立一個專案

git add

把改動過的檔案放到 git index 裡。git index 是個有點繞的術語,你可以把它想像成預備提交的區域,你執行 git commit 的話,預設是把你用 git add 放到 git index 裡的改動,提交到 git 歷史記錄中。你可以把它理解為索引,一個檔案被索引到了,意思就是 git 已經知道這個檔案,準備要收錄了。還有很多種別的說法,比如 stage,上臺準備試演了,沒問題的話也可以正式提交進入 git 歷史。相應的從 git index 裡拿掉,也叫 unstage。

git commit

把改動提交到 git 歷史中。恭喜你,你的專案有了一個新的版本,快點發布然後通知大家下載嚐鮮吧!等一下!說不定你只是引入了一個新的 Bug 而已。

但事實上,理想歸理想,現實歸現實。我們幾乎永遠不可能一個人工作,總是在別人的基礎上,所以你還要再掌握這些 git 命令:

git clone

從別的地方(別人已經建立好的 git 倉庫、或者你自己以前建立、儲存在伺服器上的倉庫)克隆一個專案,在此之上開始工作。

git push

把你的工作成果 push 到遠端伺服器上,用於儲存(萬一自己的機器硬碟壞掉)、分享(讓別人基於你的工作成果繼續開發)。

注意 git push 的時候你可能需要指定 git remote 的名字或 url,以及目標 git branch 的名字,比如這樣: git push origin HEAD:refs/heads/master ,這裡 origin 是 remote 的名字,master 是目標分支的名字。

但在有種情況下,你不需要這樣做,直接 git push 就可以,git 自動幫你補上 remote 和 branch 的資訊。那就是你的本地分支設定了跟蹤上游遠端分支的時候,參考 git branch 的 --set-upstream--