詳解Shell腳本實(shí)現(xiàn)iOS自動(dòng)化編譯打包提交

本文始發(fā)于我的博文詳解Shell腳本實(shí)現(xiàn)iOS自動(dòng)化編譯打包提交,現(xiàn)轉(zhuǎn)發(fā)至此。

目錄

  • 前言
  • Shell腳本涉及的工具
    • xcodebuild和xcrun
    • altool
    • fir-cli
    • PlistBuddy
  • 一些概念的區(qū)別
  • 具體實(shí)現(xiàn)
    • xcodebuild和xcrun
    • 準(zhǔn)備Plist文件
    • 獲取命令行參數(shù)
    • 清理構(gòu)建目錄
    • 編譯打包成Archive
    • 將Archive導(dǎo)出
    • 上傳到Fir
    • 驗(yàn)證并上傳到App Store
    • 郵件通知相關(guān)同事
    • 上傳符號表到Bugly
  • 簡單例子
  • 對比實(shí)驗(yàn)
    • 三種方式的對比
    • xcodebuild+xcrun和僅xcodebuild的比較
    • 命令到底做了什么
  • 總結(jié)

前言

現(xiàn)在涉及到編譯打包的工作主要是以下兩個(gè):

  1. 提交測試版本給測試同事
  2. 提交App Store審核

兩個(gè)流程分別是:

  • 修改證書和配置文件,然后「Product -> Archive」編譯打包,之后在自動(dòng)彈出的 「Organizer」 中進(jìn)行選擇,根據(jù)需要導(dǎo)出 ad hoc enterprise 類型的 ipa 包。等待導(dǎo)出之后再提交到Fir上,等Fir提交完成就需要告知測試同事。整個(gè)流程下來一般都要半個(gè)多小時(shí),而且需要人工監(jiān)守操作。
  • 第二個(gè)也是差不多,打包完之后需要操作幾個(gè)步驟然后上傳到App Store,上傳時(shí)間較長,而且中間可能會(huì)有錯(cuò)誤需要處理。上傳后等待蘋果處理二進(jìn)制包,蘋果處理后上去選擇構(gòu)建包,點(diǎn)擊提交審核。

所以研究下自動(dòng)化編譯打包,提高下效率,減少人工操作成本。

主要有兩種實(shí)現(xiàn)途徑,AppleScript和Shell腳本,AppleScript沒怎么研究,網(wǎng)上說是很強(qiáng)大的腳本語言。

下面主要講Shell腳本的實(shí)現(xiàn),網(wǎng)上也有人實(shí)現(xiàn)了并托管在github上,可以參考下。

https://github.com/webfrogs/xcode_shell

Shell腳本涉及的工具

主要是以下幾個(gè)工具:

  1. xcodebuild
  2. xcrun
  3. altool(提交到App Store使用)
  4. fir-cli(上傳到fir時(shí)使用)
  5. Python的smtplib(之前已經(jīng)寫過python的發(fā)郵件了,所以就直接用沒有用Shell寫。)
  6. PlistBuddy
  7. BuglySymboliOS(Bugly的符號表工具包)

xcodebuild和xcrun

xcodebuildxcrun都是來自Command Line Tools,Xcode自帶,如果沒有可以通過以下命令安裝:

xcode-select --install

或者在下面的鏈接下載安裝:

https://developer.apple.com/downloads/

安裝完可在以下路徑看到這兩個(gè)工具:

/Applications/Xcode.app/Contents/Developer/usr/bin/

  • xcodebuild
    主要是用來編譯,打包成Archive和導(dǎo)出ipa包。

https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/xcodebuild.1.html

可以執(zhí)行 xcodebuild -help 查看,主要展示了幾種用法、一些可選項(xiàng),最后是比較重要的exportOptionsPlist文件的一些可選key,這個(gè)文件在后面導(dǎo)出ipa包會(huì)用到。

主要下面三個(gè)查看的命令比較重要:

-showsdks                           display a compact list of the installed SDKs
-showBuildSettings                  display a list of build settings and values
-list                               lists the targets and configurations in a project, or the schemes in a workspace

后面兩個(gè)需要在Xcode的project或者workspace目錄下才能用。

  • xcrun
xcrun -h

主要是打包,看網(wǎng)上比較多是用這個(gè)工具打包各種渠道包。

altool

這個(gè)工具在網(wǎng)上搜索幾乎沒有什么結(jié)果,大概國內(nèi)直接用命令行工具提交App Store的比較少。后來在StackOverflow上才找到相關(guān)的文檔:

https://itunesconnect.apple.com/docs/UsingApplicationLoader.pdf

在上面的文檔第38頁講述了如何使用altool上傳二進(jìn)制文件。

這個(gè)工具實(shí)際上是ApplicationLoader,打開Xcode-左上角Xcode-Open Developer Tool-Application Loader 可看到。有個(gè)“交付您的應(yīng)用”操作,網(wǎng)上看到有人是直接用這個(gè)工具上傳的。

altool的路徑是:

/Applications/Xcode.app/Contents/Applications/Application\ Loader.app/Contents/Frameworks/ITunesSoftwareService.framework/Support/altool

使用時(shí)會(huì)提示下面的錯(cuò)誤:

altool[] *** Error: Exception while launching iTunesTransporter: 
Transporter not found at path: /usr/local/itms/bin/iTMSTransporter. 
You should reinstall the application.

建立個(gè)軟鏈接可解決(類似于Windows的快捷方式):

ln -s /Applications/Xcode.app/Contents/Applications/Application\ Loader.app/Contents/itms /usr/local/itms

注:Xcode 11 的 altool 已經(jīng)被命令 xcrun altool 替代。在終端運(yùn)行xcrun altool -h可以查看說明。

fir-cli

安裝時(shí)會(huì)提示各種權(quán)限不允許,可以執(zhí)行下面命令:

echo 'gem: --bindir /usr/local/bin' >> ~/.gemrc
sudo 'gem install fir-cli

fir有提供Android Studio、Eclipse、gradle插件,可以看下。

http://fir.im/tools

這是它的github地址,其中講到有對?xcodebuild?原生指令進(jìn)行了封裝。

https://github.com/FIRHQ/fir-cli/blob/master/README.md

PlistBuddy

Plist在Mac OSX系統(tǒng)中起著舉足輕重的作用,系統(tǒng)和程序使用Plist文件來存儲(chǔ)自己的安裝/配置/屬性等信息。而PlistBuddy是Mac里一個(gè)用于命令行下讀寫plist文件的工具,在/usr/libexec/下。可以通過它讀取或修改plist文件的內(nèi)容。

這里我僅通過它來獲取內(nèi)部版本號、外部版本號。在一些文章中見過用來修改plist文件的信息來導(dǎo)出出不同需要的包。

一些概念的區(qū)別

Workspace、Project、Scheme、Target的區(qū)別。

下面是官方文檔:

https://developer.apple.com/library/ios/featuredarticles/XcodeConcepts/Concept-Targets.html#//apple_ref/doc/uid/TP40009328-CH4-SW1

下面從上往下大概說下,具體看文檔比較好:

  • Workspace
    Workspace是最大的集合,可以包含多個(gè)Project,可以管理不同的Project之間的關(guān)系。Workspace是以xcworkspace的文件形式存在的。(這點(diǎn)和Project一致)。Workspace的存在是為了解決原來僅有Project的時(shí)候不同的Project之間的引用和調(diào)用困難的問題。同時(shí),一個(gè)WorkspaceProject共用一個(gè)編譯路徑。比如使用CocoaPod、或者使用其他開發(fā)庫/框架。

  • Project
    Project是一個(gè)倉庫,包含編譯一個(gè)或多個(gè)product所需的文件、資源和信息,保持和聚合這些元素間的關(guān)系。(每個(gè)Target能指定自己的Build Settings來覆蓋Project的)

  • Source code, including header files and implementation files
  • Libraries and frameworks, internal and external
  • Resource files
  • Image files
  • Interface Builder (nib) files
  • Scheme
    Scheme包含了一些要構(gòu)建的Scheme,一些構(gòu)建時(shí)用到的設(shè)置,一些要運(yùn)行的測試。同時(shí)只能有一個(gè)Scheme是有效的。

  • Target
    Target是對應(yīng)了具體一個(gè)想要構(gòu)建的Product,包含了一些構(gòu)建這個(gè)Product所需的配置和文件(build settingsbuild phases)。一個(gè)Project可以包含多個(gè)Target

具體實(shí)現(xiàn)

看起來有兩種實(shí)現(xiàn)方法:

  • 網(wǎng)上可以查到的文章,大多數(shù)都是用xcodebuildxcrun實(shí)現(xiàn)的,比如:
xcodebuild -workspace XXX -scheme XXX -configuration Release
xcrun -sdk iphoneos PackageApplication -v "/XXX/XXX.app" -o "/XXX/XXX"

這些文章都是相對比早期的,大多數(shù)用于打包不同渠道包。

  • 另一種是xcodebuildarchive-exportArchive,只有一兩篇文章是用這個(gè),而且也過時(shí)了,因?yàn)楝F(xiàn)在最新是需要用-exportOptionsPlist這個(gè)選項(xiàng)。

我用的是第二種,并用上-exportOptionsPlist選項(xiàng),后面我會(huì)簡單給下這兩種的結(jié)果比較。腳本流程是:

  1. 準(zhǔn)備兩個(gè)Plist文件,用于導(dǎo)出不同ipa包時(shí)使用。
  2. 獲取命令行參數(shù),區(qū)分上傳到Fir還是App Store
  3. 清理構(gòu)建目錄
  4. 編譯打包
  5. 導(dǎo)出包
  6. 上傳到Fir或者驗(yàn)證并上傳到App Store
  7. 發(fā)郵件通知

準(zhǔn)備Plist文件

根據(jù)xcodebuild -help提供的可選key可以知道,compileBitcodeembedOnDemandResourcesAssetPacksInBundleiCloudContainerEnvironmentmanifestonDemandResourcesAssetPacksBaseURLthinning這幾個(gè)key用于非App Store導(dǎo)出的;uploadBitcodeuploadSymbols用于App Store導(dǎo)出;methodteamID共用。

method的可選值為:

app-store, package, ad-hoc, enterprise, development, and developer-id

所以我建了兩個(gè)文件:AppStoreExportOptions.plistAdHocExportOptions.plist

AppStoreExportOptions.plist:method=app-store,uploadBitcode=Y(jié)ES,uploadSymbols=Y(jié)ES

AdHocExportOptions.plist:method=ad-hoc,compileBitcode=NO

獲取命令行參數(shù)

Shell內(nèi)置的getopts命令,這屬于Shell的范疇就不多講了:

if [ $# -lt 1 ];then
    echo "Error! Should enter the archive type (AdHoc or AppStore)."
    echo ""
    exit 2
fi
while getopts 't:' optname
do
    case "$optname" in
    t)
        if [ ${OPTARG} != "AdHoc" ] && [ ${OPTARG} != "AppStore" ];then
            echo "invalid parameter of $OPTARG"
            echo ""
            exit 1
        fi
        type=${OPTARG}
        ;;
    *)
        echo "Error! Unknown error while processing options"
        echo ""
        exit 2
        ;;
    esac
done

清理構(gòu)建目錄

就如在Xcode操作「Product -> Clean」。

log_path="/XXX/XXX"
configuration="Release"
xcodebuild clean -configuration "$configuration" -alltargets >> $log_path

log_path是一個(gè)文檔路徑,只是用來記錄命令的輸出,因?yàn)槎即蛟诮K端會(huì)很多,另外也方便后面分析。后面的命令也是如此。這里面帶的選項(xiàng)可以根據(jù)需要參考xcodebuild -help的信息。

編譯打包成Archive

就如在Xcode操作「Product -> Archive」

workspaceName="XXX.xcworkspace"
scheme="XXX"
configurationBuildDir="XXX/build"
codeSignIdentity="iPhone Distribution: XXX, Ltd. (xxxxxxxxxx)"
adHocProvisioningProfile="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
appStoreProvisioningProfile="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
configuration="Release"
archivePath="/xxx/XXX.xcarchive"

xcodebuild archive -workspace "$workspaceName" -scheme "$scheme" -configuration "$configuration" -archivePath "$archivePath" CONFIGURATION_BUILD_DIR="$configurationBuildDir" CODE_SIGN_IDENTITY="$codeSignIdentity" PROVISIONING_PROFILE="$provisioningProfile" >> $log_path

這里的CONFIGURATION_BUILD_DIR是中間文件生成的路徑,可以不指定;CODE_SIGN_IDENTITY是證書名(在對應(yīng)TARGETSBuild Settings中選擇完Code Sinning,再點(diǎn)擊選擇Other...,就可以得到這串東西);PROVISIONING_PROFILE是配置文件(獲取方法同CODE_SIGN_IDENTITY,格式一般是xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)。還可以添加其他參數(shù),不設(shè)置的都是默認(rèn)使用項(xiàng)目Build Settings里面的配置,包括CODE_SIGN_IDENTITYPROVISIONING_PROFILE

如果是workspace就用-workspace,就像編譯帶有CocoaPods的項(xiàng)目,如果是普通項(xiàng)目則用-project

執(zhí)行完會(huì)生成一個(gè).xcarchive文件和build文件夾如下:

.xcarchive
build文件夾
    |------.a
    |------.app
    |------.app.dSYM
    |------.swiftmodule文件夾
        |------arm.swiftdoc
        |------arm.swiftmodule
        |------arm64.swiftdoc
        |------arm64.swiftmodule

將Archive導(dǎo)出

xcodebuild -exportArchive -archivePath "$archivePath" -exportOptionsPlist "$exportOptionsPlist" -exportPath "/XXX/XXX" >> $log_path

其中$exportOptionsPlist是對應(yīng)使用的Plist的完整路徑(包括文件名)。

然后就會(huì)在指定的exportPath路徑下生成.ipa文件。

上傳到Fir

firApiToken="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
ipaPath="/xxx/xxx.ipa"
fir publish "$ipaPath" -T "$firApiToken" >> $log_path

firApiToken在登錄Fir后,右上角-API token看到。

驗(yàn)證并上傳到App Store

altoolPath="/Applications/Xcode.app/Contents/Applications/Application\ Loader.app/Contents/Frameworks/ITunesSoftwareService.framework/Versions/A/Support/altool"
${altoolPath} --validate-app -f ${ipaPath} -u xxxxxx -p xxxxxx -t ios --output-format xml >>
${altoolPath} --upload-app -f ${ipaPath} -u xxxxxx -p xxxxxx -t ios --output-format xml

在上面的PDF文檔第38頁講明了用法和各個(gè)可選項(xiàng),具體可以看下PDF。需要說明的是,生成的結(jié)果是xml打印在終端,可以保存到文檔再解析出key來判斷是否成功,目前這步還沒做。

這是成功的結(jié)果:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>os-version</key>
    <string>10.11.2</string>
    <key>success-message</key>
    <string>No errors validating archive at /XXX/XXX.ipa</string>
    <key>tool-version</key>
    <string>1.1.902</string>
    <key>xcode-versions</key>
    <array>
        <dict>
            <key>path</key>
            <string>/Applications/Xcode.app</string>
            <key>version.plist</key>
            <dict>
                <key>BuildVersion</key>
                <string>7</string>
                <key>CFBundleShortVersionString</key>
                <string>7.2</string>
                <key>CFBundleVersion</key>
                <string>9548</string>
                <key>ProductBuildVersion</key>
                <string>7C68</string>
                <key>ProjectName</key>
                <string>IDEFrameworks</string>
                <key>SourceVersion</key>
                <string>9548000000000000</string>
            </dict>
        </dict>
    </array>
</dict>
</plist>

這是失敗的結(jié)果(找不到iTMSTransporter的情況,用前面說的ln -s解決):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>os-version</key>
    <string>10.11.2</string>
    <key>product-errors</key>
    <array>
        <dict>
            <key>code</key>
            <integer>-10001</integer>
            <key>message</key>
            <string>Transporter not found at path: /usr/local/itms/bin/iTMSTransporter.  You should reinstall the application.</string>
            <key>userInfo</key>
            <dict>
                <key>MZUnderlyingException</key>
                <string>Transporter not found at path: /usr/local/itms/bin/iTMSTransporter.  You should reinstall the application.</string>
                <key>NSLocalizedDescription</key>
                <string>Transporter not found at path: /usr/local/itms/bin/iTMSTransporter.  You should reinstall the application.</string>
                <key>NSLocalizedFailureReason</key>
                <string>Transporter not found at path: /usr/local/itms/bin/iTMSTransporter.  You should reinstall the application.</string>
            </dict>
        </dict>
    </array>
    <key>tool-version</key>
    <string>1.1.902</string>
    <key>xcode-versions</key>
    <array>
        <dict>
            <key>path</key>
            <string>/Applications/Xcode.app</string>
            <key>version.plist</key>
            <dict>
                <key>BuildVersion</key>
                <string>7</string>
                <key>CFBundleShortVersionString</key>
                <string>7.2</string>
                <key>CFBundleVersion</key>
                <string>9548</string>
                <key>ProductBuildVersion</key>
                <string>7C68</string>
                <key>ProjectName</key>
                <string>IDEFrameworks</string>
                <key>SourceVersion</key>
                <string>9548000000000000</string>
            </dict>
        </dict>
    </array>
</dict>
</plist>

可見,成功會(huì)有個(gè)success-message的key,而失敗會(huì)有product-errors的key。

郵件通知相關(guān)同事

發(fā)郵件時(shí)可能會(huì)想帶上當(dāng)前版本的一些信息,如版本號、內(nèi)部版本號等,可以用PlistBuddy實(shí)現(xiàn)讀取甚至修改Plist文件。

appInfoPlistPath="`pwd`/xxx/xxx-Info.plist"
bundleShortVersion=$(/usr/libexec/PlistBuddy -c "print CFBundleShortVersionString" ${appInfoPlistPath})
bundleVersion=$(/usr/libexec/PlistBuddy -c "print CFBundleVersion" ${appInfoPlistPath})

之后便是發(fā)郵件:

python sendEmail.py "測試版本 iOS ${bundleShortVersion}(${bundleVersion})上傳成功" "趕緊下載體驗(yàn)吧!http://fir.im/meijia"

或者

python sendEmail.py "正式版本 iOS ${bundleShortVersion}(${bundleVersion})提交成功" "iOS ${bundleShortVersion} 提交成功!"

python主要用smtplib,網(wǎng)上的文章大多都是舊的,特別是講到SSL時(shí)特別復(fù)雜,其實(shí)具體看下smtplib的接口文檔就可以實(shí)現(xiàn)了。另外有可能出現(xiàn)標(biāo)題、內(nèi)容亂碼的現(xiàn)象。整合了下面的鏈接解決了:

下面是實(shí)現(xiàn)了SSL Smtp登錄的。

#!/usr/bin/env python3
#coding: utf-8

# sendEmail title content
import sys
import smtplib
from email.mime.text import MIMEText
from email.header import Header

sender = 'xxxxxx@qq.com;'
receiver = 'xxx@qq.com;'
smtpserver = 'smtp.qq.com'
#smtpserver = 'smtp.exmail.qq.com'

username = sender
password = 'xxxxxx'

def send_mail(title, content):

    try:
        msg = MIMEText(content,'plain','utf-8')
        if not isinstance(title,unicode):
            title = unicode(title, 'utf-8')
        msg['Subject'] = title
        msg['From'] = sender
        msg['To'] = receiver
        msg["Accept-Language"]="zh-CN"
        msg["Accept-Charset"]="ISO-8859-1,utf-8"

        smtp = smtplib.SMTP_SSL(smtpserver,465)
        smtp.login(username, password)
        smtp.sendmail(sender, receiver, msg.as_string())
        smtp.quit()
        return True
    except Exception, e:
        print str(e)
        return False

if send_mail(sys.argv[1], sys.argv[2]):
    print "done!"
else:
    print "failed!"

可以賦值給msg['CC']實(shí)現(xiàn)抄送,經(jīng)過測試,抄送的人過多會(huì)有一部分不成功,網(wǎng)上查了是這個(gè)庫的bug。發(fā)送多個(gè)人用分號,另外末尾也要用分號。

上傳符號表到Bugly

用于分析解決崩潰bug挺好用的,而且他們的客服也很及時(shí)。
發(fā)現(xiàn)他們的2.4.1版本有問題,反饋后他們給了2.4.3版本,經(jīng)測試沒問題。

  1. 在Bugly官網(wǎng)下載符號表工具

  2. 設(shè)置settings.txt

  3. 調(diào)用命令

java -jar buglySymboliOS.jar -d -i $dSYM -u -id "xxxxxxxxx" -key "xxxxxxxxxxx" -package "com.xxx.xxx" -version "$version" --o "xxx.zip"

注意版本號之類的要設(shè)置對。

簡單例子

清理構(gòu)建目錄:

xcodebuild clean -configuration Release -alltargets

歸檔(其他參數(shù)不指定的話,默認(rèn)用的是.xcworkspace或.xcodeproj文件里的配置)

xcodebuild archive -workspace xxx.xcworkspace -scheme xxx -configuration Release -archivePath ./xxx.xcarchive

導(dǎo)出IPA

xcodebuild -exportArchive -archivePath ./xxx.xcarchive -exportOptionsPlist ./AdHocExportOptions.plist -exportPath ./

上傳FIR

fir publish ./xxx.ipa -T xxxxxx

提交AppStore

注:Xcode 11 的使用xcrun altool

/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/Frameworks/ITunesSoftwareService.framework/Versions/A/Support/altool --validate-app -f ./xxx.ipa -u xxx -p xxx -t ios --output-format xml
/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/Frameworks/ITunesSoftwareService.framework/Versions/A/Support/altool --upload-app -f ./xxx.ipa -u xxx -p xxx -t ios --output-format xml

發(fā)郵件

python sendEmail.py "郵件內(nèi)容" "用戶名" "密碼"

上傳符號表

java -jar buglySymboliOS.jar -d -i $dSYM -u -id "xxxxxxxxx" -key "xxxxxxxxxxx" -package "com.xxx.xxx" -version "$version" --o "xxx.zip"

對比實(shí)驗(yàn)

為了了解一些區(qū)別,我做了幾個(gè)對比。我這里定義下三種方式,方便下面說明。

  • xcodebuild+xcrun(xcodebuild build和xcrun)
  • 只用xcodebuild(archive和exportArchive),
  • Xcode。

三種方式的對比

我使用xcodebuild+xcrun、僅xcodebuild、Xcode三種分別對相同代碼和配置進(jìn)行操作,根據(jù)結(jié)果做比較:

  • xcodebuild+xcrun

ipa:40.7MB,.app:93.3MB,編譯耗時(shí):8m31s,打包耗時(shí):15s。

  • 僅xcodebuild

ipa:37.3MB,.app:74MB,.xcarchive:227.3MB,編譯耗時(shí):8m24s,打包耗時(shí):26s。

  • Xcode

ipa:37.3MB,.app:74MB,.xcarchive:227.3MB,編譯耗時(shí):8m40s,打包耗時(shí):30s。

Xcode生成的.xcarchive文件可以在以下路徑看到:

/Users/double/Library/Developer/Xcode/Archives

可以看出,<u>僅使用xcodebuild的結(jié)果和使用Xcode編譯打包的結(jié)果是一致的</u>,并且最終的ipa也可以正常安裝使用。而第一種xcodebuild+xcrun的結(jié)果略大些,但是ipa也是可以正常使用的。這時(shí)需要了解下他們的區(qū)別。

xcodebuild+xcrun和僅xcodebuild的比較

  • 使用xcrun打包方式二產(chǎn)生的.xcarchive中的.app

打包生成的.ipa文件大小同樣為37.3MB,與方式二使用Xcodebuild -exportArchive的結(jié)果一致!這樣說明:使用xcrun的打包方法是正常的,和xcodebuild -exportArchive的結(jié)果一致,而且.ipa包僅和.app有關(guān)。那么說明,<u>這兩種方式的不同僅在于xcodebuild build和xcodebuild archive之間的不同</u>。

  • 刪除.xcarchive中其他文件然后exportArchive

這時(shí)命令提示錯(cuò)誤,但是上面我們已經(jīng)得出結(jié)論.ipa的生成只和.app有關(guān),所以可能的原因是,這個(gè)exportArchive命令會(huì)檢查.archive的完整性和正確性,防止生成的.archive不完整或者是偽造的。下面做個(gè)實(shí)驗(yàn)看下。

命令到底做了什么

根據(jù)命令運(yùn)行時(shí)輸出的內(nèi)容,看下中間做了什么

  • xcrun -sdk iphoneos PackageApplication -v xxx.app -o xxx.ipa
Packaging application: '/xxx/xxx.app'
Arguments: output=/xxx/xxx.ipa  verbose=1  
Environment variables:
SDKROOT = /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.2.sdk
......
SHELL = /bin/bash

Output directory: '/xxx/xxx.ipa'
Temporary Directory: '/var/folders/21/6s9bb23j0s1343pm7ltnlgpm0000gn/T/taOIiK9AyK'  (will NOT be deleted on exit when verbose set)
+ /bin/cp -Rp /xxx/xxx.app /var/folders/21/6s9bb23j0s1343pm7ltnlgpm0000gn/T/taOIiK9AyK/Payload
Program /bin/cp returned 0 : []
### Checking original app
+ /usr/bin/codesign --verify -vvvv /xxx/xxx.app
Program /usr/bin/codesign returned 0 : [/xxx/xxx.app: valid on disk
/xxx/xxx.xcarchive/Products/Applications/xxx.app: satisfies its Designated Requirement
]
Done checking the original app
+ /usr/bin/zip --symlinks --verbose --recurse-paths /Users/double/Desktop/1.ipa .
Program /usr/bin/zip returned 0 : [  adding: Payload/   (in=0) (out=0) (stored 0%)
  adding: Payload/xxx.app/  (in=0) (out=0) (stored 0%)
  ......

主要檢查了環(huán)境變量,然后驗(yàn)證簽名,然后壓縮(看到了嗎,居然是/usr/bin/zip),后面adding的基本都是.nib和.png等的壓縮。看起來.archive只是一種壓縮形式,包含了.app、.dSYM、.plist和其他一些文件。

這里的codesign工具就是簽名相關(guān)的,可以查看說明:

SYNOPSIS
     codesign -s identity [-i identifier] [-r requirements] [-fv] [path ...]
     codesign -v [-R requirement] [-v] [path|pid ...]
     codesign -d [-v] [path|pid ...]
     codesign -h [-v] [pid ...]

-s是簽名,-v是驗(yàn)證。所以可以在.app生成后再簽名。

  • xcodebuild clean

清理工作,根據(jù)參數(shù)刪除指定的workplace、target、configuration(release或debug) 的中間文件,都是工程目錄下的build文件夾。

  • xcodebuild archive

下面是里面主要的步驟:

  1. Create product structure 創(chuàng)建.app文件
  2. CompileC 編譯文件(clang編譯,指定了編譯的SDK版本和指令集)
  3. Ld
  4. CreateUniversalBinary (lipo)
  5. CompileStoryboard (ibtool )
  6. CompileAssetCatalog (actool )
  7. ProcessInfoPlistFile (builtin-infoPlistUtility )
  8. GenerateDSYMFile (dsymutil )
  9. LinkStoryboards(ibtool )
  10. Strip
  11. ProcessProductPackaging (builtin-productPackagingUtility )
  12. CodeSign (codesign --force --sign)
  13. Validate (builtin-validationUtility )

總結(jié)

呼呼寫了這么多,終于到總結(jié)部分了。這個(gè)過程學(xué)到了很多東西,腳本成果確實(shí)方便了很多,減少了編譯打包過程中人工監(jiān)守、人工操作的成本,并且測試和提交到appStore的包都驗(yàn)證過可用。

-END-
歡迎到我的博客交流:http://zackzheng.info

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容