若是你曾經試過作多 target 的項目,到了測試人員要測試包的時候,你就會明白什麼叫「生不如死」。雖然 Xcode 打包很方便,可是當你機械重複打 N 次包的時候,就會以爲這純粹是浪費時間的工做。因此這時候自動化打包就顯得尤其重要(其實就算只有一個 target,就算使用 Xcode 打包很方便,也應該構建自動化打包,由於你能夠節省大量時間)。html
構建自動化打包腳本
xcodebuild
使用 xcodebuild -h
來看看 xcodebuild 究竟是幹啥的ios
1
2
3
4
5
6
7
8
9
|
Usage: xcodebuild [-project ] [[-target ]...|-alltargets] [-configuration ] [-arch ]... [-sdk [|]] [-showBuildSettings] [=]... []...
xcodebuild [-project ] -scheme [-destination ]... [-configuration ] [-arch ]... [-sdk [|]] [-showBuildSettings] [=]... []...
xcodebuild -workspace -scheme [-destination ]... [-configuration ] [-arch ]... [-sdk [|]] [-showBuildSettings] [=]... []...
xcodebuild -version [-sdk [|] [] ]
xcodebuild -list [[-project ]|[-workspace ]] [-json]
xcodebuild -showsdks
xcodebuild -exportArchive -archivePath -exportPath -exportOptionsPlist
xcodebuild -exportLocalizations -localizationPath -project [-exportLanguage ...]
xcodebuild -importLocalizations -localizationPath -project
|
這裏我只截取了 usage 部分,option 部分太多沒有截取。git
這裏介紹幾條畢竟經常使用的命令github
1. xcodebuild -list …
xcodebuild -list [[-project ]|[-workspace ]] [-json]
macos
usage: 輸出 project 中的 targets 和 configurations,或者 workspace 中 schemes。-project
和 -workspace
是輸出指定內容,不輸入默認輸出當前目錄下。-json
是以 json 格式輸出。json
example:api
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
$ xcodebuild -list
Information about project "XX":
Targets:
XX
XXTests
Build Configurations:
Debug
Release
If no build configuration is specified and -scheme is not passed then "Release" is used.
Schemes:
XX
|
2. xcodebuild -project …
xcodebuild [-project ] [[-target ]...|-alltargets] [-configuration ] [-arch ]... [-sdk [|]] [-showBuildSettings] [=]... []...
數組
usage:xcode
-project
: 指定 project 名字,默認首個 project。緩存
-target
: 指定對應的 target ,默認首個 target。
-configuration
: 選擇Debug 或 Release,默認 Release,固然若是你有自定義的配置的,就應該選你配置的,上面 -list
中有輸出。
-showBuildSettings
: 顯示工程的配置。
=
: 修改工程的配置文件。
buildaction ...
: 以下,默認爲 build
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
Specify a build action (or actions) to perform on the target.
Available build actions are:
build Build the target in the build root (SYMROOT). This is
the default build action.
installsrc Copy the source of the project to the source root
(SRCROOT).
install Build the target and install it into the target's
installation directory in the distribution root
(DSTROOT).
clean Remove build products and intermediate files from the
build root (SYMROOT).
|
example:
$ xcodebuild -project 你的項目名字.xcodeproj -target 你的 target 名字 -configuration release
這行命令表示編譯 xx.xcodeproj 的 xx target。在 terminal 中會看到編譯過程,若是成功最後會輸出 ** BUILD SUCCEEDED **
。最後會在當前目錄下生成 build/Release-iphoneos/xx.app
$ xcodebuild -project 你的項目名字.xcodeproj -target 你的 target 名字 -configuration release -showBuildSettings
這行命令使用 -showBuildSettings
是不會 build 項目的,只是輸出工程的配置。這裏輸出的的內容有(內容過多,只截取部分)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
Build settings for action build and target XX:
ACTION = build
AD_HOC_CODE_SIGNING_ALLOWED = NO
ALTERNATE_GROUP = staff
ALTERNATE_MODE = u+w,go-w,a+rX
ALTERNATE_OWNER = TsuiYuenHong
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO
ALWAYS_SEARCH_USER_PATHS = NO
ALWAYS_USE_SEPARATE_HEADERMAPS = NO
APPLE_INTERNAL_DEVELOPER_DIR = /AppleInternal/Developer
APPLE_INTERNAL_DIR = /AppleInternal
APPLE_INTERNAL_DOCUMENTATION_DIR = /AppleInternal/Documentation
APPLE_INTERNAL_LIBRARY_DIR = /AppleInternal/Library
APPLE_INTERNAL_TOOLS = /AppleInternal/Developer/Tools
APPLICATION_EXTENSION_API_ONLY = NO
APPLY_RULES_IN_COPY_FILES = NO
ARCHS = armv7 arm64
...
|
若是要修改配置文件,就直接最命令最後加上你要修改的內容。
例如在這行命令最後加上指定證書
$ xcodebuild -project 你的項目名字.xcodeproj -target 你的 target 名字 -configuration release PROVISIONING_PROFILE="你證書的id"
其中的字段是上面 -showBuildSettings
顯示的字段,也能夠看官網介紹
3. xcodebuild -workspace …
xcodebuild -workspace -scheme [-destination ]... [-configuration ] [-arch ]... [-sdk [|]] [-showBuildSettings] [=]... []...
除了 workspace 和 scheme 以外其他選項都和上條命令相同。
-workspace
: 指定 workspace 名字,默認首個 workspace
-scheme
: 指定對應的 scheme ,默認首個 scheme
4 . xcodebuild -exportArchive …
這裏順便介紹一下 archive 命令,由於在下面使用 PackageApplication 會出一個警告說推薦使用 -exportArchive
。因此咱們就來嘗試一下使用 archive 來生成 app。
首先使用一下命令來生成 .xcarchive 文件xcodebuild archive -workspace xx.xcworkspace -scheme xx -archivePath xx.xcarchive
能夠看出添加上 archive 命令和最後加入 -archivePath
生成archivePath的路徑便可。
而後該路徑下會生成一個 xx.archivePath,裏面包括三個文件,xx.app.dsym文件(可用於bugly等監控bug的平臺),info.plist(保存打包的一些信息),還有咱們的 xx.app 文件。
其次使用 -exportArchive 生成 ipa 包
xcodebuild -exportArchive -archivePath xx.xcarchive -exportPath xx -exportFormat ipa
-archivePath
: xx.archivePath 的路徑
-exportPath
: 輸出路徑
-exportFormat
: 生成類型,這裏選擇咱們須要的 ipa
這樣就利用咱們的 xcodebuild 命令來生成 ipa 包
xcrun
這裏也使用 xcrun 來生成 ipa 包便可
xcrun -sdk iphoneos PackageApplication build/Release-iphoneos/xx.app -o ~/Desktop/xx.ipa
可是,在 macos10.12 和 Xcode8 的環境下會出現一個警告
warning: PackageApplication is deprecated, use xcodebuild -exportArchive instead.
說明 PackageApplication 已經被棄用了。
不過其實這一步能夠幾乎等價於將 xx.app 放入一個 payload 的文件夾下而後壓縮文件夾爲 xx.ipa,固然這樣作缺失一些信息,不過並不影響程序的運行。
初步小結
綜上,咱們有兩種方法來生成咱們須要的 ipa 包。
- 使用 xcodebuild 命令來編譯咱們的項目生成 app,而後再用 xcrun 將 app 轉 ipa。
- 使用 xcodebuild archive 命令來直接生成咱們須要的 ipa。
雖然如今網上幾乎都是使用 xcodebuild + xcrun 來來生成 ipa 包,不過既然官方說 PackageApplication is deprecated
,那仍是推薦使用第二種方法,一步到位。
自動化打包正式開始
這裏從我工做室的一個項目切入,這個項目須要最終生成 18 個 ipa 包,可是他們幾乎是共用一套代碼的,不一樣的地方在於bundleName/bundleDisplayName/bundleid 等,以及一些資源文件的不一樣,例如 icon 等。因此可想而知若是選擇手動打包的痛苦,而且當你打包到一半發現某個地方錯了要從新打包 ……
這裏說一下自動化打包1.0解決思路:
- 使用命令
defaults write
來修改項目中的 plist 文件,來達到修改 bundleName/bundleDisplayName/bundleid… 的目的。 - 使用命令
cp
來替換資源文件。 - 使用
xcodebuild -workspace ..
編譯出 app 包。 - 使用
xcrun ...
生成 ipa 。
這是我最開始想到的思路,最終運行時間大概爲每一個包2.5m(時間主要浪費在編譯),而後一套下來也要半個多小時。雖然比起手動打快了很多,但仍是太慢了。畢竟自動化的目的不只僅是自動,還要速度。
既然問題出在編譯上,那個人思路就往編譯一次屢次使用這個方向上面思考。而後想到了既然只是資源文件和plist的不一樣,沒有涉及到代碼的更換(不過這個項目後期不一樣 app 會執行不一樣一套代碼,不過也有解決辦法),這裏就出現了自動化打包2.0的版本。
- 使用
xcodebuild -workspace ..
編譯出 app 包。 - 使用命令
defaults write
來修改項目中的 plist 文件,來達到修改 bundleName/bundleDisplayName/bundleid… 的目的。 - 使用命令
cp
來替換資源文件。 - 重簽名
codesign -f -s "iPhone Distribution: xx co., LTD" --entitlements $Entitlements $ipaPath/Payload/YouXiaoYun.app
- 使用
xcrun ...
生成 ipa 。
和1.0大體類似,不過並非每次生成 ipa 都須要編譯一次。而是編譯一次,而後直接修改 app 下內容,不過這裏會出現簽名錯誤的問題,由於在編譯的最後會用證書幫 app 簽名,若是你直接替換資源而後就生成 ipa 的話會致使 ipa 沒法安裝。
那這時候神奇的重簽名技術就出來(重簽名用在正途上的真少見…hhhh,關於重簽名的文章 google 一下就會不少),使用 codesign 命令就能夠幫修改過資源的 app 重簽名。
最終使用2.0的時間基本是在5-6分鐘左右。果真能機器完成的工做絕對不要手動完成,從半天到30分鐘到最後的6分鐘,節省下來的時間可讓你學習到更多。
上面說到若是不一樣 app 間會用到不一樣的代碼。例如 app A 裏面的 title 叫 A 部門,app B 裏面 title 又叫 B 部門,這樣就不會經過命令行直接修改到代碼,不過我想到的是維護一個 plist 文件,plist 文件能夠這樣設計的,每一個不一樣 app 的 bundleName 都設置字典的鍵,而後字典下就能夠是你自定義的內容。而後每次啓動 app 就根據 bundleName 來尋找對應的字典,而後 title 就賦值爲 plist 下 title 的值。若是不一樣代碼就根據 code1 裏面的值來 switch 不一樣的代碼。
最終代碼
如下是完整的腳本文件,部分信息須要本身替換。
如下腳本適用於一次打 N 個包,適用狀況:
- 能夠替換 bundle 信息
- 替換音頻圖片資源
- 能夠執行不一樣代碼
- 生成相應的plist文件
- 上傳到蒲公英分發平臺
固然也能夠打一個包,適當刪除某些代碼便可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
# 1.Configuration Info
# 項目路徑 需修改
projectDir="你的項目路徑"
# 打包生成路徑 需修改
ipaPath="ipa生成路徑"
# 圖標路徑 需修改
iconPath="~/Desktop/icon"
# Provisioning Profile 需修改 查看本地配置文件
PROVISIONING_PROFILE="xxxxxxx-xxxx-4bfa-a696-0ec7391b24d8"
############# 重簽名須要文件
# 如下文件需放在 ipaPath 路徑下
Entitlements=$ipaPath/entitlements.plist
#############
# 版本號
bundleVersion="2.0.0"
# 選擇打包序號 多選則以空格隔開 如("1" "2" "3")
appPackNum=("1 2")
# 蒲公英分發參數 不分發可忽略 默認不分發 下面的兩個KEY是默認測試的網址對應KEY
ISUPLOAD=0
USERKEY="xxx"
APIKEY="xxx"
# ---------------------------可選 若是須要替換 app 的 icon --------------------------------- #
# 配置App信息數組 格式:"AppName(和工程中appInfo.Plist對應)" "icon"
#Schemes:
# 1.app1 app1Icon
# 2.app2 app2Icon
# 3.app3 app3Icon
# --------------------------------------------------------------------------------------- #
# 打包個數
appPackNumLength=${#appPackNum[*]}
appInfos=(
"app1" "app1Icon" "xxxx"
"app2" "app2Icon" "xxxx"
"app3" "app3Icon" "xxxx"
)
appInfosLength=${#appInfos[*]}
# Scheme Name
schemeName="xx"
# Code Sign ID
CODE_SIGN_IDENTITY="xx co., LTD"
# 生成 APP 路徑
buildDir="build/Release-iphoneos"
# 開始時間
beginTime=`date +%s`
# 建立打包目錄
mkdir ${ipaPath}/AllPack
# 本地存放所有 IPA 的路徑
allIPAPackPath="${ipaPath}/allPack"
# 清除緩存
rm -rf $projectDir/$buildDir
# Build 生成 APP
xcodebuild -workspace ${projectDir}/xx.xcworkspace -scheme ${schemeName} -configuration Release clean -sdk iphoneos build CODE_SIGN_IDENTITY="${CODE_SIGN_IDENTITY}" PROVISIONING_PROFILE="${PROVISIONING_PROFILE}" SYMROOT="${projectDir}/build"
if [[ $? = 0 ]]; then
echo "\033[31m 編譯成功\n \033[0m"
else
echo "\033[31m 編譯失敗\n \033[0m"
fi
# 先建立 payload 文件夾
mkdir ~/Desktop/Payload
# 移動編譯生成的 app 到桌面的 Payload 文件夾下
cp -Rf ${projectDir}/${buildDir}/${schemeName}.app $ipaPath/Payload
# 如下二選一
# 1.----所有打包----
#for (( i=0; i $plist_path
itemsassetskindsoftware-packageurlhttps://xxxxxxxxxxxx/$appDownloadName.ipakinddisplay-imageurlhttps://xxxxxxxxxxxx/${appIconName}.pngkindfull-size-imageurlhttps://xxxxxxxxxxxx/${appIconName}.pngmetadatabundle-identifier你的bundidbundle-version$bundleVersionkindsoftwaretitle$appDownloadName
EOF
# 移動
mv ${ipaPath}/$appDownloadName.ipa ${allIPAPackPath}/$appName
# 6.上傳蒲公英分發平臺
if [[ $ISUPLOAD = 1 ]]; then
echo "正在上傳蒲公英..."
curl -F "file=@$allIPAPackPath/$appName/$appDownloadName.ipa" -F "uKey=$USERKEY" -F "_api_key=$APIKEY" http://www.pgyer.com/apiv1/app/upload
fi
done
# 清除無關文件
rm -rf $ipaPath/Payload
# 結束時間
endTime=`date +%s`
echo -e "打包時間$[ endTime - beginTime ]秒"
|