從零開始寫個自動打包IPA腳本

背景

新項目這邊每次版本移交,

給測試都是直接xcode挨個拿手機安裝的流程.

一次兩次還好,天天這么搞,而且每次一大波手機扔過來,瞬間覺得,自己好像是個疫苗注射員似的,順次給每個手機打一針.

biu!

NEXT...

biu!

NEXT...

biu!

NEXT...

...

??

...

于是..我崩潰了..

好吧,

是在是看不下去了,于是準(zhǔn)備寫個腳本以后自己打包,測試直接下載就好了.

思路

那么怎么實現(xiàn)這么一個事情呢?

其實上一家公司,做個類似的東西,雖不是我做的,但是大體流程還是知道的:

寫個bash腳本,執(zhí)行自動打包iOS版本,到指定的目錄 (有條件的公司,可以自己搭個小服務(wù)器,這樣誰都可以隨時隨地的打包)

將打包好的文件上傳到fir.im (當(dāng)然上傳到自己公司的服務(wù)器或者任何地方都行,只是fir.im我一直用,覺得比較方便)

開發(fā)一個內(nèi)部使用的類似APPStore,上面放著自己公司的所有APP,每次有更新的時候,測試童鞋直接通過這個自己下載新APP就可以了

開始做

1. 準(zhǔn)備背景知識

其實當(dāng)我們Xcode點擊了build或者運行comand+R之后,Xcode自己執(zhí)行的命令是xcodebuild這條命令.

然后,編譯好之后,怎么生產(chǎn)ipa包?

用xcrun命令

話不多說,先上手:

打開終端,cd到你的工程位置,然后先試一下xcodebuild命令,

//xcrunchengpoleness@polenxcodebuild_iPA $ xcrun --versionxcrunversion29.//xcodebuildchengpoleness@polenios (develop) $ xcodebuild -versionXcode7.3Buildversion7D175

看上去很簡單,大概了解了一下,就開始用:

chengpoleness@polenios (develop) $ xcodebuild2016-05-0213:05:04.623xcodebuild[1015:16272] [MT]PluginLoading:Required plug-incompatibility UUID ACA8656B-FEA8-4B6D-8E4A-93F4C95C362Cforplug-inat path'~/Library/Application Support/Developer/Shared/Xcode/Plug-ins/XcodeColors.xcplugin'not presentinDVTPlugInCompatibilityUUIDs2016-05-0213:05:04.625xcodebuild[1015:16272] [MT]PluginLoading:Required plug-incompatibility UUID ACA8656B-FEA8-4B6D-8E4A-93F4C95C362Cforplug-inat path'~/Library/Application Support/Developer/Shared/Xcode/Plug-ins/OMColorSense.xcplugin'not presentinDVTPlugInCompatibilityUUIDs=== BUILD TARGET xxx OF PROJECT xxx WITH THE DEFAULT CONFIGURATION (Release) ===Check dependenciesWrite auxiliary fileswrite-file/Users/chengpoleness/Documents/code/xxx/ios/build/xxx.build/Release-iphoneos/xxx.build/xxx.hmapwrite-file/Users/chengpoleness/Documents/code/xxx/ios/build/xxx.build/Release-iphoneos/xxx.build/xxx-own-target-headers.hmapwrite-file/Users/chengpoleness/Documents/code/xxx/ios/build/xxx.build/Release-iphoneos/xxx.build/Script-492B764475E022A63FB67F55.sh

接著滿屏的快速滾動,可以看到clang依次在編譯各個文件,登錄大概十幾秒閃屏,然后失敗了!??!??

ld: library not foundfor-lPodsclang: error: linker command failed withexitcode1(use -v to see invocation)** BUILD FAILED **The following build commands failed:? ? Ld build/xxx.build/Release-iphoneos/xxx.build/Objects-normal/armv7/xxx normal armv7? ? Ld build/xxx.build/Release-iphoneos/xxx.build/Objects-normal/arm64/xxx normal arm64(2failures)

這個就是找不到pods了,

為什么會這樣?

自己直接用Xcode 親自product-archive 了一遍,okey啊,沒出現(xiàn)任何問題!

那是為什么呢?

各種goole,stackoveflow,但是基本說的是不要只打開project,而是打開workspace,其實這個是針對直接用xcode進(jìn)行編譯或者打包的時候,出現(xiàn)問題的解決方案,

然后嘗試了其他解決方案,諸如ONLY_ACTIVE_ARCHS = NO等,都無效.

最終在下面這篇文章里,找到了靈感:

iOS Integration Tests With Appium

其實解決方案很簡單,執(zhí)行xcodebuild需要指定你所需要對應(yīng)的workspace和scheme

所以命令如下:

xcodebuild -workspace/Users/chengpoleness/Documents/code/xxx/ios/xxx.xcworkspace -scheme xxx

執(zhí)行前,先查看下-list,這個可以知道xcodebuild命令下對應(yīng)的參數(shù)需要填寫的內(nèi)容

chengpoleness@polen ios (develop) $ xcodebuild -list2016-05-0215:24:26.656xcodebuild[16535:154176] [MT] PluginLoading: Required plug-incompatibility UUID ACA8656B-FEA8-4B6D-8E4A-93F4C95C362Cforplug-inat path '~/Library/Application Support/Developer/Shared/Xcode/Plug-ins/XcodeColors.xcplugin'notpresentinDVTPlugInCompatibilityUUIDs2016-05-0215:24:26.661xcodebuild[16535:154176] [MT] PluginLoading: Required plug-incompatibility UUID ACA8656B-FEA8-4B6D-8E4A-93F4C95C362Cforplug-inat path '~/Library/Application Support/Developer/Shared/Xcode/Plug-ins/OMColorSense.xcplugin'notpresentinDVTPlugInCompatibilityUUIDsInformation about project"xxx":? ? Targets:? ? ? ? xxx? ? ? ? xxxTests? ? Build Configurations:? ? ? ? DebugReleaseIfno buildconfigurationisspecifiedand-schemeisnotpassedthen"Release"isused.? ? Schemes:? ? ? ? xxx

然后,成功了,如下:

chengpoleness@polenios (develop) $ xcodebuild -workspace/Users/chengpoleness/Documents/code/xxx/ios/xxx.xcworkspace -scheme xxx2016-05-0215:26:29.627xcodebuild[16552:156501] [MT]PluginLoading:Required plug-incompatibility UUID ACA8656B-FEA8-4B6D-8E4A-93F4C95C362Cforplug-inat path'~/Library/Application Support/Developer/Shared/Xcode/Plug-ins/XcodeColors.xcplugin'not presentinDVTPlugInCompatibilityUUIDs2016-05-0215:26:29.629xcodebuild[16552:156501] [MT]PluginLoading:Required plug-incompatibility UUID ACA8656B-FEA8-4B6D-8E4A-93F4C95C362Cforplug-inat path'~/Library/Application Support/Developer/Shared/Xcode/Plug-ins/OMColorSense.xcplugin'not presentinDVTPlugInCompatibilityUUIDs=== BUILD TARGET Pods OF PROJECT Pods WITH CONFIGURATION Debug ===Check dependencies=== BUILD TARGET xxx OF PROJECT xxx WITH CONFIGURATION Debug ===Check dependenciesPhaseScriptExecution Check\ Pods\ Manifest.lock/Users/chengpoleness/Library/Developer/Xcode/DerivedData/xxx-djwnuzytnxhekrhjhypwxdqrxtet/Build/Intermediates/xxx.build/Debug-iphoneos/xxx.build/Script-492B764475E022A63FB67F55.sh.........Validate/Users/chengpoleness/Library/Developer/Xcode/DerivedData/xxx-djwnuzytnxhekrhjhypwxdqrxtet/Build/Products/Debug-iphoneos/xxx.app? ? cd/Users/chengpoleness/Documents/code/xxx/ios? ? export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/Users/chengpoleness/.rvm/gems/ruby-2.0.0-p598/bin:/Users/chengpoleness/.rvm/gems/ruby-2.0.0-p598@global/bin:/Users/chengpoleness/.rvm/rubies/ruby-2.0.0-p598/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/chengpoleness/.rvm/bin:/usr/local/marven/apache-maven-3.3.9/bin:/Users/chengpoleness/Library/Android/sdk/platform-tools:/Users/chengpoleness/Library/Android/sdk/tools"export PRODUCT_TYPE=com.apple.product-type.application? ? builtin-validationUtility/Users/chengpoleness/Library/Developer/Xcode/DerivedData/xxx-djwnuzytnxhekrhjhypwxdqrxtet/Build/Products/Debug-iphoneos/xxx.app** BUILD SUCCEEDED **

2. 寫腳本

(挑重點地說) 大致流程就是:

2.1 先git 指令,pull到最新的分支

這個很簡單,沒什么好說的了.

git clean -dfgitreset--hardgitfetchgit checkout $BRANCHNAMEgit pull--rebase origin $BRANCHNAMEecho"即將打包的分支log如下:"gitlog-5podupdate--verbose --no-repo-update

不過這幾個指令,帶有刪除性,比如git clean -df,git reset 這種,所以用的時候要謹(jǐn)慎,如果你把腳本放在自己的工程目錄下,就可能一不小心腳本就被刪掉了(親歷者說...??)

所以最終版本改成了

# git updategit checkout$BRANCHNAMEif[ $? -ne0]; thenexit1figit pull#pod update --verbose --no-repo-updateif[ $? -ne0]; thenexit1fi

只保留了checkout 和pull, pod沒有用,是因為我們這邊除了googleMap其他全是自造輪子,所以...并不需要??.

2.2 xcodebuild進(jìn)行編譯

xcodebuild \-workspace $SORCEPATH/xxx.xcworkspace \-scheme $SCHEMENAMEPLQ \-configuration Debug \CODE_SIGN_IDENTITY="iPhone Developer: xxxx(T8TXPM5FC7)"? \PROVISIONING_PROFILE="5110ff0a-b845-475e-a4xxxxxxxxxx" \clean \build \-derivedDataPath $IPAPATH/$BRANCHNAME/$DATE

備注:

我這邊因為是研發(fā)階段,所以打包是debug包,如果是要打release 包,configuration中修改成Release,以及其他對應(yīng)的修改下就行.

2.3 成功后xcrun打包成ipa包

xcrun -sdk iphoneos PackageApplication \? ? ? ? -v $IPAPATH/Build/Products/Debug-iphoneos/$SCHEMENAME.app \-o $IPAPATH/$IPANAME

最終腳本如下:

屏幕快照 2016-05-04 下午8.20.47.png

打包的文件命名我是按照時間來命名的,這樣也方便找,尤其應(yīng)對于可能分分鐘就要突然來打包的測試團(tuán)隊.

3. 生成ipa包,上傳到fir.im

3.1 fir比較好,有命令可以直接上傳,但是需先安裝fir-cli

安裝過程如下:

chengpoleness@polenxcodebuild_iPA $ gem install fir-cliFetching:chunky_png-1.3.5.gem (100%)Successfully installed chunky_png-1.3.5Fetching:rqrcode-0.10.1.gem (100%)Successfully installed rqrcode-0.10.1......? ? ? ? ______________? ? ? ? ________? ? ____/ ____/_/ __ \? ? ? /____/ //? _// /_/ // /_/ /_____/ // // // __/_/ // _, _/_____/ /___/ /____/ //_//___/_/ |_|? ? ? \____/_____/___/......Installing ri documentationforthor-0.19.1Parsing documentationforfir-cli-1.4.9unable to convert"\xCE"from ASCII-8BIT to UTF-8forlib/fir/util/parser/bin/pngcrush, skippingInstalling ri documentationforfir-cli-1.4.913gems installed

安裝如果遇到問題,可以到這里查看原因

3.2 使用fir指令,上傳我們的ipa包

非常簡單

firlogin -T c525718a775b954882xxxxxxxx? ? ? #fir.imtokenfirpublish $IPAPATH/Develop/xxx.ipa

3.3 擴展

另外,也可以直接用fir指令,進(jìn)行打包或者編譯,這個具體也沒研究,其實就是對xcodebuild做了個封裝,本質(zhì)上還是執(zhí)行那些指令,這里不再詳述了.

chengpoleness@polen xcodebuild_iPA $ firCommands:? fir build_apk BUILD_DIR? ? ? ? ? ? ? ? ? ? ? # Build Androidapp(alias: `ba`).? fir build_ipa BUILD_DIR [options] [settings]? # Build iOSapp(alias: `bi`).? firhelp#Describeavailable commands oronespecific command (aliase...? fir info APP_FILE_PATH? ? ? ? ? ? ? ? ? ? ? ? # Show iOS/Androidappinfo, support ipa/apkfile(aliases: `...? fir login? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? # Login fir.im (aliases: `l`).? fir mapping MAPPING_FILE_PATH? ? ? ? ? ? ? ? # Uploadappmappingfileto BugHD.com (aliases: `m`).? fir me? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? # Show current user infoifuser is logined.? fir publish APP_FILE_PATH? ? ? ? ? ? ? ? ? ? # Publish iOS/Androidappto fir.im, support ipa/apkfile(al...? fir upgrade? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? # Upgrade fir-cliand quit (aliases: `u`).? firversion# Show fir-cliversionnumber and quit (aliases: `v`).Options:? -T, [--token=TOKEN]? ? ? ? ? ? ? # User's APITokenat fir.im? -L, [--logfile=LOGFILE]? ? ? ? ? # Path to writable logfile? -V, [--verbose], [--no-verbose]? # Show verbose? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? # Default: true? -q, [--quiet], [--no-quiet]? ? ? # Silence commands? -h, [--help], [--no-help]? ? ? ? # Show thishelpmessage and quit

4. 測試一下

4.1 執(zhí)行.sh文件

chengpoleness@polenxcodebuild_iPA$sh xcodebuild.sh

要是有比較懶的同學(xué),直接把.sh文件,拖到命令行里面也行

4.2 過程與結(jié)果

編譯過程的輸出信息太多,刪減了下,大概看一下流程:

chengpoleness@polen xcodebuild_iPA $ sh xcodebuild.sh HEAD is now at30865e1version3.5.0, build29Already on 'develop'......=== CLEAN TARGET Pods OF PROJECT Pods WITH CONFIGURATION Debug ====== CLEAN TARGET xxx OF PROJECT xxx WITH CONFIGURATION Debug ===......** BUILD SUCCEEDED **/* '以上是編譯成功,接下來打包ipa'? */...Packaging application: '/Users/chengpoleness/Documents/code/xxx/buildiPA/develop/20160502_1801/Build/Products/Debug-iphoneos/xxx.app'Arguments: output=/Users/chengpoleness/Documents/code/xxx/buildiPA/develop/20160502_1801/1xxx_20160502_1801.ipa? verbose=1Environment variables:_system_name = OSX...### Checking original app...Done checking the original app...? adding: Payload/xxxx.app/xxxViewGpsOverlay.nib? ? (in=4889) (out=2502) (deflated49%)total bytes=53793797, compressed=23588024->56% savings]Results at 'xxxxxxxx/Documents/code/xxx/buildiPA/develop/20160502_1801/xxx_20160502_1801.ipa' /* '以上是打包成功,接下來是上傳fir.im' */I, [2016-05-02T18:03:56.614082#47670]? INFO -- : Login succeed, current? user's email: xxxxI, [2016-05-02T18:03:56.614215#47670]? INFO -- : I, [2016-05-02T18:03:57.766434#47674]? INFO -- : Publishing app via chenglong.......I, [2016-05-02T18:03:57.766570#47674]? INFO -- : ? -------------------------------------------- ?I, [2016-05-02T18:03:59.813783#47674]? INFO -- : Fetching com.osmgolf.xxx@fir.im uploading info......I, [2016-05-02T18:03:59.813868#47674]? INFO -- : Uploading app: xxx-3.5.0(Build29)I, [2016-05-02T18:04:00.562896#47674]? INFO -- : Uploading app icon......I, [2016-05-02T18:04:00.563029#47674]? INFO -- : Converting app's icon......I, [2016-05-02T18:04:01.715439#47674]? INFO -- : Uploading app binary......I, [2016-05-02T18:04:16.091891#47674]? INFO -- : Updating devices info......I, [2016-05-02T18:04:16.244710#47674]? INFO -- : Updating app info......I, [2016-05-02T18:04:16.514628#47674]? INFO -- : ? -------------------------------------------- ?I, [2016-05-02T18:04:16.514862#47674]? INFO -- : Fetch app info from fir.imI, [2016-05-02T18:04:17.019304#47674]? INFO -- : Published succeed: http://fir.im/xxxxI, [2016-05-02T18:04:17.019409#47674]? INFO -- : 恭喜!??!上傳fir.im成功!

OK,測試通過.

5.開發(fā)內(nèi)部版“AppStore”

這個就這里不詳述了,因為太簡單.

寫個tableview,把自己的app的鏈接放上去,每次有新版本了,通過這個App就可以下載,之前做過的截圖如下:

至此,自動打包IPA的相關(guān)工作開發(fā)完成,可以投入使用了...

6 擴展-Debug/Release

6.1 背景:

目前的腳本打包都是默認(rèn)打debug包的,但是如果我打Release包怎么辦?如果我Debug包不同網(wǎng)絡(luò)域名想切換怎么辦?

6.2 Debug/Release

Okey,我們依次來解決這些問題:

其實很簡單,xcodebuild指令有個-configuration, 我們使用這個決定是打Debug還是Release

那這個打出來的Debug和Release有什么區(qū)別呢?

這個相當(dāng)于編譯的時候是按照Debug模式或者Release模式來打包,從編譯角度來說,區(qū)別挺大的,比如Debug會在某些堆棧環(huán)節(jié)預(yù)留更多的空間(我之前就遇到過同樣的代碼,但是Debug沒問題,Release會有bug的情況,如果不知道這個的話,很難查出來或者理解這些bug,具體細(xì)節(jié)有空專門細(xì)說吧,這里不詳述了)

所以,解決方案:定義一個CONGRUATION,根據(jù)傳入的參數(shù),決定打Debug還是打Release

# $1表示傳入的第一個參數(shù),啟動腳本傳入Debug或者Release就可以CONGRUATION=$1# 在xcodebuild和xrun的地方都使用$CONGRUATION即可xcodebuild \-workspace $SOURCEPATH/xxx.xcworkspace \-scheme $SCHEMENAME \-configuration $CONGRUATION \-derivedDataPath $IPAPATH \xcrun -sdk iphoneos PackageApplication \? ? ? ? -v $IPAPATH/Build/Products/$CONGRUATION-iphoneos/$SCHEMENAME.app \-o $IPAPATH/$IPANAME

6.2 Different Host 域名切換

這個其實解決方案很多:

方案A:

代碼里寫個Debug頁面,里面設(shè)置可以切換不同的域名即可,

以前公司的APP就是這么做的,因為我們測算環(huán)境非常多,比如:

QA01,QA02,QA03...QA10...

所以直接在APP里就可以進(jìn)行切換不同域名.

polen提醒:

1>? 切換后一定要讓用戶退出,避免發(fā)生不必要的邏輯混亂

2> 需要設(shè)置個宏開關(guān),確保Release版本用戶100%走線上域名,Debug版本可以隨意加各種邏輯判斷.

務(wù)必避免如果代碼不建壯,發(fā)生線上用戶誤入測試環(huán)境的悲劇 (當(dāng)然,這個肯定屬于bug了,嚴(yán)格是不應(yīng)該發(fā)生的了,但是難免意外,沒有東西是100%絕對的,這里只是提醒...)

方案B:

如果你們的測試環(huán)境沒那么多,可能就2-3個,可以在打包的時候,通過腳本決定用哪個環(huán)境,具體怎么做:

CTO說怎么樣能讓腳本給代碼傳個值進(jìn)入,然后代碼在編譯的時候根據(jù)這個值決定用哪個測試環(huán)境.

思考了很多方法,包括用腳本去改代碼(硬改,用sed進(jìn)入到.m文件修改,但這樣太危險,而且很不友好)

后來學(xué)習(xí)到使用PREPROCESSOR_DEFINITIONS,就是xcode在打包前會先從 一個config中讀取一些配置信息,這些在xcode-building里面可以直接設(shè)置,自然也可以通過xcodebuild指定穿進(jìn)去,傳入的就是GCC_PREPROCESSOR_DEFINITIONS

所以就是:

設(shè)定一個SERVER_TYPE宏定義,如果#ifdef? SERVER_TYPE的話,使用傳入的參數(shù),否則使用代碼自定義的邏輯

# $serverType是啟動腳本傳入的參數(shù)值if[$CONGRUATION= Debug ];thenPREPROCESSOR_DEFINITIONS="COCOAPODS=1 SERVER_TYPE=$serverTypeDEBUG=1"elsePREPROCESSOR_DEFINITIONS="COCOAPODS=1 SERVER_TYPE=$serverType"fixcodebuild \-workspace$SOURCEPATH/xxx.xcworkspace \-scheme$SCHEMENAME\GCC_PREPROCESSOR_DEFINITIONS="$PREPROCESSOR_DEFINITIONS"\-configuration$CONGRUATION\-derivedDataPath$IPAPATH\

具體代碼中如何使用:

//HOST_TYPE決定最終的域名#ifdefSERVER_TYPE#defineHOST_TYPE SERVER_TYPE#else#ifdefDEBUG#defineHOST_TYPE HOST_TYPE_CANARY#else#defineHOST_TYPE HOST_TYPE_PRODUCTION#endif#endif

okey,以上就解決了Debug和Release相關(guān)的問題

7.遇到的一些疑難雜癥

br>

7.1 SDK不匹配導(dǎo)致編譯失敗

7.1.1 背景

下午(2016.8.2)正在忙,突然測試同學(xué)跑過來,大喊:“不好了,不好了...”

我以為要說臺風(fēng)來了,(做好了可以下班的準(zhǔn)備..??)

結(jié)果,來了句:

“打包腳本失敗了,失敗了!!”

我擦,啥情況,用了幾個月了好好的,突然就失敗了...

7.1.1 原因

后來確認(rèn)下來是這么個情況,iOS10發(fā)布了,之前一直拖延,最近不是很忙了,于是有同事下載了xcode8-beta3和iOS10的安裝profile。

然而iOS10的SDK更新,導(dǎo)致我們有個代碼邏輯無法實現(xiàn),于是Fix了一下,然后繼續(xù)交給測試同學(xué)打包測試,結(jié)果就打包失敗了,我看了下失敗原因:

error:property 'firstAnchor' not found on object of type 'NSLayoutConstraint *'? ? ? object_setIvar(constraint.firstAnchor, referenceItem, view);

屏幕快照 2016-08-02 下午5.44.54.png

然后查了下代碼,代碼邏輯應(yīng)該是沒問題的。而找不到這個property是因為這個代碼使用的iOS10的SDK,用舊版本自然是找不到,然后問了下測試,直接用xcode編譯可以么?回答說可以。

那分析下來就應(yīng)該是路徑的問題了.

去她位置上看了下,(因為我自己沒裝xcode8,所以自己沒法調(diào)試).一看發(fā)現(xiàn)問題了,她目前是兩個xcode都在,但一個叫Xcode,一個叫Xcode-beta,腳本跑起來的時候,SDK肯定還跑得是以前的xcode的啊,自然編不過,于是改了一下名字,把原來的Xcode改為Xcode7,新的Xcode-beta改為Xcode.

然后,問題解決!??

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容