系列传送门
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="[email protected]"
#开发者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项目自动编译的,要确保下面代码块执行:
///
/// 当编译iOS,生成iOS项目之前时,会调用以下方法(仅“UNITY_5”以上支持)
///
[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),并导入到本设备的钥匙串中时,会发现该证书不包含密钥信息,此时该证书是不能用于打包的。
- 注意:如果直接从开发者后台下载开发者证书(开发者后台->Certificates->选中一个证书并Download),并导入到本设备的钥匙串中时,会发现该证书不包含密钥信息,此时该证书是不能用于打包的。
- “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”命令选项指定的证书一致。
问题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”文件。
provisioningProfiles
com.XXXX.XXXX.XXXX(此处填写app bundle id)
XXXXXX(此处填写证书的名称:开发者后台->Provisioning Profiles->Name字段)
compileBitcode
method
ad-hoc
参考:
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”字段。
......
provisioningProfiles
com.XXXX.XXXX.XXXX(此处填写app bundle id)
XXXXXX(此处填写证书的名称:开发者后台->Provisioning Profiles->Name字段)
......
问题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账号页面生成一个专用密码,用来打包上传。