IOS:使用shell命令打包并上傳Itunes

系列傳送門

Android:
Unity3d:命令行打包Android
IOS:
Unity3d:命令行編譯IOS
IOS:使用shell命令打包并上傳Itunes
Unity3d:使用Jenkins自動編譯打包IOS(只能打包Development)
Unity3d:使用Jenkins自動編譯打包IOS(打包Ad-hoc,上傳itunes)

打包并上傳itunes用的腳本:

#!/bin/sh
###########配置開始###########
#設置當前系統登陸的用戶和登陸密碼,用于解鎖Keychain
LOGIN_USER_NAME=當前系統登陸的用戶名
UNLOCK_KEYCHAINS_PW=當前系統登陸用的密碼
#設置Apple開發者證書名稱
#(“鑰匙串->上部‘登陸’選項->下部‘證書’選項->雙擊用到的證書->證書信息里的‘常用名稱’字段”)
CODE_SIGN_IDENTITY="iPhone Distribution: XXXXXXX (XXXXXX)"
#設置開發者Team ID
#(“鑰匙串->上部‘登陸’選項->下部‘證書’選項->雙擊用到的證書->證書信息里的‘組織單位’字段”)
DEVELOPMENT_TEAM="XXXXXXXX"
#設置當前App開發測試用或發布用的描述文件UUID
#(開發者后臺->Provisioning Profiles->選中一個描述文件(.mobileprovision)并Download)
#使用“security cms -D -i XXX.mobileprovision”命令查看該描述文件的UUID
#雙擊(.mobileprovision)文件,即可把該證書導入~/Library/MobileDevice/Provisioning Profiles目錄中
PROVISIONING_PROFILE="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
#設置要打包的項目路徑
IOS_PROJECT_PATH="/Users/${LOGIN_USER_NAME}/Documents/JenkinsProjects/XXXXXXX/IOSProjectOutput"
#設置ipa文件的輸出路徑
EXPORT_PATH="/Users/${LOGIN_USER_NAME}/Documents/JenkinsProjects/XXXXXXX/IOSOutput"
#設置打包ipa文件的時用的配置文件(文件內指定了打包用的證書和發布方式:ad-hoc或app-store)
EXPORT_OPTIONS_PLIST_PATH="/Users/${LOGIN_USER_NAME}/Documents/JenkinsBuild/export-options.plist"
#打包完成后默認的ipa包名(Unity3d打的包一般都叫Unity-iPhone.ipa,無需修改)
OLD_IPA_NAME="${EXPORT_PATH}/Unity-iPhone.ipa"
#新的ipa包名 mv ${OLD_IPA_NAME} ${IPA_PATH},
#里面用${BUILD_TIMESTAMP}和${BUILD_ID}參數,是因為用的Jenkins執行改shell文件的
#${BUILD_ID}參數是Jenkins自帶的
#${BUILD_TIMESTAMP}參數需要Jenkins安裝一個Zentimestamp plugin
IPA_PATH="${EXPORT_PATH}/XXXXXXX_${BUILD_TIMESTAMP}_${BUILD_ID}.ipa"
#開發者Apple賬號
APP_ID="XXXX@XXXX.com"
#開發者Apple賬號的密碼
#如果開啟了雙重認證,需要在開發者Apple賬號頁面生成一個專用密碼
APP_PW="XXXX"
###########配置結束###########

#判斷EXPORT_PATH路徑是否存在
if [ -d "${EXPORT_PATH}" ];then
    rm -rf ${EXPORT_PATH}
fi
mkdir ${EXPORT_PATH}
#
/usr/bin/xcodebuild -version
/usr/bin/agvtool mvers -terse1
/usr/bin/agvtool vers -terse
#獲取鑰匙串和密碼
/usr/bin/security list-keychains -s /Users/${LOGIN_USER_NAME}/Library/Keychains/login.keychain
/usr/bin/security default-keychain -d user -s /Users/${LOGIN_USER_NAME}/Library/Keychains/login.keychain
/usr/bin/security unlock-keychain -p ${UNLOCK_KEYCHAINS_PW} /Users/${LOGIN_USER_NAME}/Library/Keychains/login.keychain
/usr/bin/security show-keychain-info /Users/${LOGIN_USER_NAME}/Library/Keychains/login.keychain
/usr/bin/security find-identity -p codesigning -v
/usr/bin/security find-certificate -a -c ${DEVELOPMENT_TEAM} -Z | grep ^SHA-1
#檢測當前項目信息,下面兩個命令只能在項目目錄下使用
/usr/bin/xcodebuild -showsdks
/usr/bin/xcodebuild -list
#修改project.pbxproj文件,禁止打包時自動簽名,這個在Unity3d里可以設置的,如果設置過了,則無需執行該命令
sed -i "" s/'ProvisioningStyle = Automatic;'/'ProvisioningStyle = Manual;'/g Unity-iPhone.xcodeproj/project.pbxproj
#動態修改版本號
#/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString 3.0.0" ${IOS_PROJECT_PATH}/Info.plist
#動態修改版本Code(BUILD_ID),自動上傳itunes一定要配置這個
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion ${BUILD_ID}" ${IOS_PROJECT_PATH}/Info.plist
#生成archive文件,參數說明:
#-workspace:項目名稱.xcworkspace(或-project:項目名稱.xcodeproj)
#-scheme:通常默認為項目名稱
#-configuration:配置(Release,或Debug;未設置時默認為Release)
#-archivePath:archive包保存目錄
#CODE_SIGN_IDENTITY:證書(證書名稱)
#PROVISIONING_PROFILE:描述文件UUID
#注意:生成archive包時,使用證書,以及描述文件UUID
/usr/bin/xcodebuild clean -scheme Unity-iPhone -sdk "iphoneos11.2" -configuration Release -allowProvisioningUpdates archive -archivePath ${IOS_PROJECT_PATH}/build/Release-iphoneos/Unity-iPhone.xcarchive DEVELOPMENT_TEAM="${DEVELOPMENT_TEAM}" CODE_SIGN_IDENTITY="${CODE_SIGN_IDENTITY}" PROVISIONING_PROFILE="${PROVISIONING_PROFILE}"
#生成ipa包,參數說明:
#-archivePath: archive包保存目錄;
#-exportPath:ipa包保存目錄;
/usr/libexec/PlistBuddy -c "Print :ApplicationProperties:CFBundleVersion" ${IOS_PROJECT_PATH}/build/Release-iphoneos/Unity-iPhone.xcarchive/Info.plist
/usr/libexec/PlistBuddy -c "Print :ApplicationProperties:CFBundleShortVersionString" ${IOS_PROJECT_PATH}/build/Release-iphoneos/Unity-iPhone.xcarchive/Info.plist
/usr/bin/xcodebuild -exportArchive -archivePath ${IOS_PROJECT_PATH}/build/Release-iphoneos/Unity-iPhone.xcarchive -exportPath ${EXPORT_PATH} -exportOptionsPlist "${EXPORT_OPTIONS_PLIST_PATH}"
#重命名ipa包名
mv ${OLD_IPA_NAME} ${IPA_PATH}
#驗證ipa包信息
/Applications/Xcode.app/Contents/Applications/Application\ Loader.app/Contents/Frameworks/ITunesSoftwareService.framework/Versions/A/Support/altool --validate-app -f ${IPA_PATH} -u ${APP_ID} -p ${APP_PW} --output-format xml
#上傳iTunes
/Applications/Xcode.app/Contents/Applications/Application\ Loader.app/Contents/Frameworks/ITunesSoftwareService.framework/Versions/A/Support/altool --upload-app -f ${IPA_PATH} -u ${APP_ID} -p ${APP_PW} --output-format xml

打包時遇到的異常以及解決方法:

問題1:

ld: framework not found CoroTelephony
clang: error: linker command failed with exit code 1 (use -v to see invocation)

** ARCHIVE FAILED **
The following build commands failed:
Ld /Users/當前系統登陸的用戶名/Library/Developer/Xcode/DerivedData/Unity-iPhone-dauuedqxizlrpndipmklonloczlt/Build/Intermediates.noindex/ArchiveIntermediates/Unity-iPhone/IntermediateBuildFilesPath/Unity-iPhone.build/Release-iphoneos/Unity-iPhone.build/Objects-normal/armv7/app名稱 normal armv7
Ld /Users/當前系統登陸的用戶名/Library/Developer/Xcode/DerivedData/Unity-iPhone-dauuedqxizlrpndipmklonloczlt/Build/Intermediates.noindex/ArchiveIntermediates/Unity-iPhone/IntermediateBuildFilesPath/Unity-iPhone.build/Release-iphoneos/Unity-iPhone.build/Objects-normal/arm64/app名稱 normal arm64

原因:
是因為app用到的一些類庫(framework,tbd文件等)沒有導進去,造成編譯失敗。

解決:
最主要的一句是“ld: framework not found CoroTelephony”,不要被“Ld /Users…..normal armv7/arm64”迷惑,要向前多仔細看看,把缺失的類庫(framework,tbd文件等)導進去。

備注:
如果是由unity3d項目自動編譯的,要確保下面代碼塊執行:

/// <summary>
/// 當編譯iOS,生成iOS項目之前時,會調用以下方法(僅“UNITY_5”以上支持)
/// </summary>
[PostProcessBuild]
public static void OnPostProcessBuild (BuildTarget buildTarget, string buildPath)
{
  if (buildTarget != BuildTarget.iOS)
  {
    return;
  }
  PBXProject  pbxProject = new PBXProject ();
  string pbxProjPath = PBXProject.GetPBXProjectPath (buildPath);
  string targetGuid = pbxProject.TargetGuidByName (PBXProject.GetUnityTargetName ());
  // 讀取
  pbxProject.ReadFromString (File.ReadAllText (pbxProjPath));
  // 1、設置關閉Bitcode(如果不需要,可注釋掉)
  pbxProject.SetBuildProperty (targetGuid, ENABLE_BITCODE_KEY, "NO");
  // 2、添加Framework
  pbxProject.AddFrameworkToProject (targetGuid, "AudioToolBox.framework", false);
  pbxProject.AddFrameworkToProject (targetGuid, "CoreAudio.framework", false);
  // 3、添加tbd
  pbxProject.AddFileToBuild(targetGuid, pbxProject.AddFile("usr/lib/" + "libc++.tbd", "Frameworks/" + "libc++.tbd", PBXSourceTree.Sdk));
  pbxProject.AddFileToBuild(targetGuid, pbxProject.AddFile("usr/lib/" + "libz.tbd", "Frameworks/" + "libz.tbd", PBXSourceTree.Sdk));
  .........
  // 保存
  File.WriteAllText (pbxProjPath, pbxProject.WriteToString ());
}

問題2:

error: exportArchive: No profiles for 'com.xxxx.xxxx.xxx(App bundle Id)' were found
Error Domain=IDEProfileLocatorErrorDomain Code=1 "No profiles for 'com.xxxx.xxxx.xxx(App bundle Id)' were found" UserInfo={NSLocalizedDescription=No profiles for 'com.xxxx.xxxx.xxx(App bundle Id)' were found, NSLocalizedRecoverySuggestion=Xcode couldn't find any iOS Ad Hoc provisioning profiles matching 'com.xxxx.xxxx.xxx(App bundle Id)'. Automatic signing is disabled and unable to generate a profile. To enable automatic signing, pass -allowProvisioningUpdates to xcodebuild.}

原因:
是因為打Ad Hoc包時,沒找到對應的證書。

解決:
在執行打包命令時,指定“CODE_SIGN_IDENTITY”命令選項和“PROVISIONING_PROFILE”命令選項。

備注:

  • “CODE_SIGN_IDENTITY”命令選項:
    Apple 開發者證書名稱,可以從“鑰匙串->上部‘登陸’選項->下部‘證書’選項->雙擊用到的證書->證書信息里的‘常用名稱’字段”得到。
    本設備上沒有Apple 開發者證書的話,可以從擁有該證書的mac的鑰匙串里導出一個“.p12”文件,然后復制到該設備,雙擊“.p12”文件,該證書就導入到該設備的鑰匙串了。
    • 注意:如果直接從開發者后臺下載開發者證書(開發者后臺->Certificates->選中一個證書并Download),并導入到本設備的鑰匙串中時,會發現該證書不包含密鑰信息,此時該證書是不能用于打包的。
      image.png
  • “PROVISIONING_PROFILE”命令選項:
    描述文件(Provisioning Profile)的UUID,該描述文件可以從Apple開發者后臺(開發者后臺->Provisioning Profiles->選中一個證書并Download)下載,是一個“xxxxxx.mobileprovision”文件。
    可以使用“security cms -D -i XXX.mobileprovision”命令查看該描述文件的UUID。
    • 注意 1:如果“CODE_SIGN_IDENTITY”命令選項指定的證書是Development的,那么“PROVISIONING_PROFILE”命令選項指定的描述文件UUID也必須是Development的;如果一個是Distribution的,另一個也必須是Distribution的。
    • 注意 2:下載的描述文件綁定的App ID必須要和準備打包的app的bundle Id一致,而且該描述文件的Certificates也必須和“CODE_SIGN_IDENTITY”命令選項指定的證書一致。
      image.png

      image.png

問題3:

xcodebuild: error: Unknown build action 'Distribution:XXXXX’

原因:
是因為在執行“/usr/bin/xcodebuild archive”打包命令時,指定的開發者證書字段“CODE_SIGN_IDENTITY”的值不正確。

解決:
我遇到的是,這里少了一個空格,應該是'Distribution: XXXXX’。

問題4:

xcodebuild: error: invalid option '-exportProvisioningProfile'

原因:
是因為在執行“/usr/bin/xcodebuild archive”打包命令時,指定了無效的命令選項“-exportProvisioningProfile”。

解決:
刪除這個命令選項。

備注:
應該是老的打包方式的參數,現在不需要了。

問題5:

xcodebuild: error: invalid option '-exportFormat

原因:
是因為在執行“/usr/bin/xcodebuild archive”打包命令時,指定了無效的命令選項“-exportFormat”。

解決:
刪除這個命令選項。

備注:
應該是老的打包方式的參數,現在不需要了。

問題6:

Code Signing Error: Unity-iPhone has conflicting provisioning settings.
Unity-iPhone is automatically signed, but provisioning profile xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx has been manually specified.
Set the provisioning profile value to "Automatic" in the build settings editor, or switch to manual signing in the project editor.

原因:
是因為xcode編譯時,默認開啟了自動簽名功能。

解決:
關閉這個功能。但官方并沒有提供關閉的命令,所以需要使用shell指令修改項目文件內容,關閉該功能:

sed -i "" s/'ProvisioningStyle = Automatic;'/'ProvisioningStyle = Manual;'/g Unity-iPhone.xcodeproj/project.pbxproj

備注:
一定要放在 /usr/bin/xcodebuild 打包命令之前

問題7:

error: Couldn't load -exportOptionsPlist: The file “export-options.plist” couldn’t be opened because there is no such file.
Error Domain=NSCocoaErrorDomain Code=260 "The file “export-options.plist” couldn’t be opened because there is no such file." UserInfo={NSFilePath=/Users/當前系統登陸的用戶名/Documents/XXXX/export-options.plist, NSUnderlyingError=0x7fcc9c701aa0 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}

原因:
是因為“/usr/bin/xcodebuild -exportArchive”命令中有個參數“-exportOptionsPlist”,該參數指定的“export-options.plist”文件不存在。

解決:
需要在“/Users/XXXXX/Documents/XXXX/”目錄下創建一個“export-options.plist”文件。

<?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>provisioningProfiles</key>
    <dict>
        <key>com.XXXX.XXXX.XXXX(此處填寫app bundle id)</key>
        <string>XXXXXX(此處填寫證書的名稱:開發者后臺->Provisioning Profiles->Name字段)</string>
    </dict>
    <key>compileBitcode</key>
    <false/>
    <key>method</key>
    <string>ad-hoc</string>
</dict>
</plist>

參考:
http://blog.csdn.net/andanlan/article/details/78113330?locationNum=9&fps=1

問題8:

error: Couldn't load -exportOptionsPlist: The data couldn’t be read because it isn’t in the correct format.
Error Domain=NSCocoaErrorDomain Code=3840 "Cannot parse a NULL or zero-length data" UserInfo={NSDebugDescription=Cannot parse a NULL or zero-length data}

原因:
是因為“/usr/bin/xcodebuild -exportArchive”命令中有個參數“-exportOptionsPlist”,該參數指定的“export-options.plist”文件內容為空。

解決:
根據需要,配置“export-options.plist”文件。

參考:
http://blog.csdn.net/andanlan/article/details/78113330?locationNum=9&fps=1

問題9:

error: exportArchive: “xxxxxxxx.app" requires a provisioning profile.
Error Domain=IDEProvisioningErrorDomain Code=9 ""xxxxxxxx.app" requires a provisioning profile." UserInfo={NSLocalizedDescription="xxxxxxxx.app" requires a provisioning profile., NSLocalizedRecoverySuggestion=Add a profile to the "provisioningProfiles" dictionary in your Export Options property list.}

原因:
是因為“/usr/bin/xcodebuild -exportArchive”命令中有個參數“-exportOptionsPlist”,該參數指定的“export-options.plist”文件中,沒有設置“provisioningProfiles”字段,該字段指定了打包時需要的證書名。

解決:
設置“provisioningProfiles”字段。

    ......
    <key>provisioningProfiles</key>
    <dict>
        <key>com.XXXX.XXXX.XXXX(此處填寫app bundle id)</key>
        <string>XXXXXX(此處填寫證書的名稱:開發者后臺->Provisioning Profiles->Name字段)</string>
    </dict>
    ......

問題10:

/Applications/Xcode.app/Contents/Applications/Application: No such file or directory

原因:
是因為在執行altool命令時,altool的文件路徑里有帶空格的文件夾“Application Loader.app”。

解決:
需要這樣寫帶空格的文件夾:“Application Loader.app”->“Application\ Loader.app”。

注意:
在shell里,不能把altool的路徑定義為一個變量,然后再引用變量去執行altool命令,比如:

ALTOOL_PATH="/Applications/Xcode.app/Contents/Applications/Application\ Loader.app/Contents/Frameworks/ITunesSoftwareService.framework/Versions/A/Support/altool"
${ALTOOL_PATH} --validate-app -f ${IPA_PATH} -u ${APP_ID} -p ${APP_PW} --output-format xml

這樣調用,雖然“Application”后面加了“\”,但依然會出現上面“No such file or directory”的異常

問題11:

altool[43032:542579] *** Error: Unable to validate your application. Sign in with the app-specific password you generated. If you forgot the app-specific password or need to create a new one, go to appleid.apple.com

原因:
是因為蘋果開發者賬號登陸開啟了雙重驗證。

解決:
需要開發者在開發者Apple賬號頁面生成一個專用密碼,用來打包上傳。

image.png

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

推薦閱讀更多精彩內容