美文网首页iOS DevelopmentiOS Developer程序员
IOS:使用shell命令打包并上传Itunes

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

作者: Alisallon | 来源:发表于2018-01-25 17:19 被阅读0次

    系列传送门

    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

    相关文章

      网友评论

        本文标题:IOS:使用shell命令打包并上传Itunes

        本文链接:https://www.haomeiwen.com/subject/kobvaxtx.html