Version 和 Build 的概念
在應用 target 的 General 面板中有兩個設置項,分別為 Version 和 Build,它們實際對應了 Info.plist 文件中的兩項:
- Bundle versions string, short(CFBundleShortVersionString)
- Bundle version(CFBundleVersion)
這兩項都是用來標識應用的版本號。區別在于,Version 標識應用程序的發布版本號,格式應采用語義化的版本號,而 Build 則用來標識產品構建時的內部版本號(包括發布與未發布的),簡單的可使用遞增的整數表示。Version 是對外向用戶顯示說明的,而 Build 是開發內部用來唯一確定一次分發版本的。這里是官方介紹:Setting the Version Number and Build String。
Version 一般在應用發布或更新時由產品部門確定,在了解清楚兩者的不同后,我們主要關注開發中使用的構建號。Build 可以方便我們清晰的追蹤構建的版本,比如在發布前,隨著測試與修復 bug 的進行,我們需要不斷的給測試人員提供新的版本,這時,通過構建版本號就可以清晰的標識具體的測試版本。或者我們可以通過構建版本號來區分收集的 Crash 信息。
構建版本號的自動修改
通過上述對 Build 的介紹我們知道,構建版本號是隨著內部分發版本而需要不斷更新的。為了提高效率,我們可以讓構建版本號在產品構建過程中自動更新,方法是在應用 target 的 Build Phases 面板中添加用于更改構建版本號的自定義腳本。
需要將 Run Script 步驟拖放到 Copy Bundle Resources 步驟之前,這樣可以保證 CFBundleVersion 的值與最新一次的構建版本一致,否則 CFBundleVersion 的值為下一次的構建版本。
修改構建版本號的腳本
通過 shell 腳本,我們可以實現不同格式的構建版本號的自動修改。
遞增整數
最簡單的構建版本號的設置就是,隨著每一次的構建,整數版本號遞增1。第一種方式是使用蘋果公司提供的命令agvtool
,這里是該命令的介紹:Automating Version and Build Numbers Using agvtool。在腳本中只需要簡單的執行以下命令:
agvtool next-version -all
另一種方式就是獲取當前的構建版本號,加1后再重新設置構建版本號:
build_number=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" ${INFOPLIST_FILE})
build_number=$((${build_number} + 1))
/usr/libexec/PlistBuddy -c "Set CFBundleVersion ${build_number}" ${INFOPLIST_FILE}
構建號與版本號的拼接:
build_number=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" ${INFOPLIST_FILE})
version_number=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" ${INFOPLIST_FILE})
build_number=$(echo $build_number|sed 's/.*\./''/')
build_number=$((${build_number} + 1))
/usr/libexec/PlistBuddy -c "Set CFBundleVersion ${version_number}.${build_number}" ${INFOPLIST_FILE}
基于 Git
我們也可以通過 Git 的提交信息來標識構建版本,如以提交次數作為構建版本:
build_number=$(git rev-list HEAD | wc -l | awk '{print $1}')
/usr/libexec/PlistBuddy -c "Set CFBundleVersion ${build_number}" ${INFOPLIST_FILE}
或者以最新提交的 ID 作為構建版本號:
build_number=$(git rev-parse --short HEAD)
/usr/libexec/PlistBuddy -c "Set CFBundleVersion ${build_number}" ${INFOPLIST_FILE}
基于日期時間
還可以用不同格式化的當前時間作為構建版本號
build_number=$(date +%Y%m%d)
/usr/libexec/PlistBuddy -c "Set CFBundleVersion ${build_number}" ${INFOPLIST_FILE}
自定義腳本
上述各種格式的腳本都可以進行修改以符合自己的格式需求,而且可以對各種格式信息進行組合。最終,我選擇的格式為日期+當天構建次數+環境標識,如:201701051R。這種格式相比單純的構建次數遞增,除了達到唯一確定標識一次構建版本的作用外,還提供了更有意義的信息,比如測試人員可以很容易的根據環境標識選擇要測試環境對應的構建版本。以下是我使用的腳本:
if [ ${CONFIGURATION} != "Develop" ]
then
last_build_version=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" ${INFOPLIST_FILE})
last_build_date=${last_build_version:0:8}
#因為最后一位為環境標識位,所以構建次數通過截取第九位到倒數第二位的字符確定。
last_build_number_length=$((${#last_build_version} - 9))
last_build_number=${last_build_version:8:$last_build_number_length}
current_date=$(date +%Y%m%d)
if [ ${last_build_date} = ${current_date} ]; then
build_number=$(expr ${last_build_number} + 1)
else
build_number="1"
fi
build_number=$(printf "%02d" ${build_number})
if [ ${CONFIGURATION} = "Debug" ]; then
environment_flag="S"
elif [ ${CONFIGURATION} = "Release" ]; then
environment_flag="P"
fi
build_version=${current_date}${build_number}${environment_flag}
/usr/libexec/PlistBuddy -c "Set CFBundleVersion ${build_version}" ${INFOPLIST_FILE}
fi
腳本中最外層有一個判斷,作用是過濾所有開發調試中的構建過程。只有真正構建一個要分發出去的產品時,才應計入構建版本號中。而另一種方式則是勾選 Run Script 面板中的 Run Script only when installing 選項,勾選該選項后,腳本在平常調試構建過程中并不會執行,而只在 Archive 的構建過程時才會執行,這樣也就不需要添加外層的判斷了。
對于腳本中使用到的一些環境變量,如 CONFIGURATION,INFOPLIST_FILE 等,如果你勾選了 Run Script 面板中的 Show environment variables in build log 選項,則可以在構建過程的 log 中看到所有添加的環境變量,主要是當前 Build Settings 中的值。
2019.7.11 對腳本的更新
- 簡化變量名,在當前語境下,不需要寫 last,去掉后意義仍然明確。
- 日期過長,去掉前兩位的世紀標識,采用六位標識,如 190711,在可預見的應用生命周期內,不會有歧義。
- 根據文檔 CFBundleVersion 中的定義,構建號只允許由數字和
.
組成。通過字母標識服務器環境,在上傳應用商店時還需要手動修改構建號,所以改用數字標識。文檔中定義的格式及意義與版本號一致,所以不采用。 - 連續的數字閱讀起來不方便,用
.
分割不同的意義塊。 - Build Phases 中的默認腳本名 "Run Script" 可修改,設置為 "Update Build Version"。
最終確定的格式為:日期.當天的構建次數.服務器環境標識。
build_version=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" ${INFOPLIST_FILE})
# 數組用括號來表示,元素用"空格"符號分割開。所以操作字符串將點換成空格,使其中每一段可直接轉換為數組元素。
array=(${build_version//./ })
build_date=${array[0]}
build_number=${array[1]}
current_date=$(date +%y%m%d)
if [ ${build_date} = ${current_date} ]; then
build_number=$((${build_number} + 1))
else
build_number=1
fi
if [ ${CONFIGURATION} = "Debug" ]; then
environment_flag=0
elif [ ${CONFIGURATION} = "Release" ]; then
environment_flag=1
fi
build_version=${current_date}.${build_number}.${environment_flag}
/usr/libexec/PlistBuddy -c "Set CFBundleVersion ${build_version}" ${INFOPLIST_FILE}