【问题标题】:Generate xcarchive into a specific folder from the command line从命令行将 xcarchive 生成​​到特定文件夹中
【发布时间】:2012-02-02 02:30:36
【问题描述】:

出于 CI 的目的,我需要能够在我们的夜间构建中生成 XCARCHIVE 和 IPA 文件。 IPA 供我们的测试人员使用,使用我们的临时密钥进行签名,而 XCARCHIVE 将发送给客户端,以便他们可以将其导入 Xcode 并在他们满意时将其提交到应用商店。

通过谷歌搜索生成 IPA 非常简单,但是如何生成 .XCARCHIVE 文件是我所不知道的。我找到的最接近的是:

xcodebuild -scheme myscheme archive

但是,这会将 .xcarchive 存储在一些难以找到的文件夹中,例如:

/Users/me/Library/Developer/Xcode/Archives/2011-12-14/MyApp 14-12-11 11.42 AM.xcarchive

有什么方法可以控制存档的放置位置、名称以及如何避免重新编译它?我想最好的结果是从您执行“xcodebuild build”时生成的 DSYM 和 APP 生成 xcarchive - 这可能吗?

【问题讨论】:

    标签: xcode4 archive ipa


    【解决方案1】:

    Xcode 5 现在支持-archivePath 选项:

    xcodebuild -scheme myscheme archive -archivePath /path/to/AppName.xcarchive
    

    您现在还可以从刚刚构建的存档中导出已签名的 IPA:

    xcodebuild -exportArchive -exportFormat IPA -exportProvisioningProfile my_profile_name -archivePath /path/to/AppName.xcarchive -exportPath /path/to/AppName.ipa
    

    【讨论】:

    • 太棒了!不知道xcodebuild 有这个功能。会很有用的。
    • 更好的解决方案。谢谢。
    • 很高兴他们添加了该参数...这是正确答案,请务必包含 xcarchive 的文件名,而不仅仅是路径...谢谢!!!
    • 在 Xcode 6.1 中尝试过,最终在 -archivePath 处得到一个空文件夹,而真正的存档位于默认位置。
    • -exportFormat-exportProvisioningProfile 现在已弃用,取而代之的是 -exportOptionsPlist,令人讨厌的是,它更难与特定的移动设备一起使用。请参阅 man xcodebuildxcodebuild -help(请注意,每个内容都包含其他内容未涵盖的内容。)
    【解决方案2】:

    从 Xcode 4 Preview 5 开始,可以在方案存档的后期操作中访问三个环境变量。

    ARCHIVE_PATH: The path to the archive.
    ARCHIVE_PRODUCTS_PATH: The installation location for the archived product.
    ARCHIVE_DSYMS_PATH: The path to the product’s dSYM files.
    

    您可以在此处移动/复制存档。我想在 CI 脚本中对流程进行更多控制,因此我保存了一个临时文件,该文件可以轻松地从包含这些值的 CI 脚本中获取。

    BUILD_DIR=$PROJECT_DIR/build
    echo "ARCHIVE_PATH=\"$ARCHIVE_PATH\"" > $BUILD_DIR/archive_paths.sh
    echo "ARCHIVE_PRODUCTS_PATH=\"$ARCHIVE_PRODUCTS_PATH\"" >> $BUILD_DIR/archive_paths.sh
    echo "ARCHIVE_DSYMS_PATH=\"$ARCHIVE_DSYMS_PATH\"" >> $BUILD_DIR/archive_paths.sh
    echo "INFOPLIST_PATH=\"$INFOPLIST_PATH\"" >> $BUILD_DIR/archive_paths.sh
    

    然后在我的 CI 脚本中我可以运行以下命令:

    xcodebuild -alltargets -scheme [Scheme Name] -configuration [Config Name] clean archive
    source build/archive_paths.sh
    ARCHIVE_NAME=AppName-$APP_VERSION-$APP_BUILD.xcarchive
    cp -r "$ARCHIVE_PATH" "$BUILD_DIR/$ARCHIVE_NAME"
    

    【讨论】:

    • 谢谢谢谢谢谢!你到底是怎么找到这个的?几天来我一直在梳理文档,寻找类似的东西。
    • 以@jemmons 评论为基础...您在哪里找到这个?经过几天对每个人的自定义脚本的梳理,我很想阅读一些官方文档
    • 这是我能找到的唯一提及。最好的蹩脚文档。 developer.apple.com/library/ios/releasenotes/developertools/…
    • 我不知道为什么,但是我的“ARCHIVE_”路径是空的。 :( (XCode 5.0.2)
    • 如果我在 IDE 中构建,这些参数会被填充。但是如果我使用 xcodebuild 在命令行上构建,那么它们就不会被设置。 :/ 做 [xcodebuild -sdk iphoneos -project "${PROJECT_NAME}" -scheme "myApp Adhoc" -configuration "Release (Adhoc) - QA" clean archive]
    【解决方案3】:

    我刚刚解决了这个问题 - 只需将参数 -archivePath 添加到您的 xcode 构建命令行,考虑到最初的问题意味着:

    xcodebuild -scheme myscheme archive
    

    变成……

    xcodebuild -scheme myscheme archive -archivePath Build/Archive
    

    (注意:路径是相对的,我将构建输出到$PWD/Build

    这会将您的 .app 文件夹放置在:

    Build/Archive.xarchive/Products/Application
    

    如果您的构建目标中已经包含您的签名证书和配置文件,您可以创建您的 IPA 文件,而无需使用以下命令重新签名:

    xcrun -v -sdk iphoneos PackageApplication -v `pwd`'/Build/Archive.xarchive/Products/Application/my.app' -o `pwd`'/myapp.ipa'
    

    (注意:xcrun 不喜欢相对路径,因此 pwd

    -v args 转储了许多有用的信息 - 此命令可能无法正确签名并仍然以代码 0 退出,叹息!

    如果您发现无法运行构建的 .ipa,则可能是签名问题,您可以使用以下方法进行仔细检查:

    codesign --verify -vvvv myapp.app
    

    如果签名正确且未被篡改,则输出将包含以下内容:

    myapp.app: valid on disk
    myapp.app: satisfies its Designated Requirement
    

    如果没有,您将看到类似以下内容:

    Codesign check fails : /blahpath/myapp.app: a sealed resource is missing or invalid
    file modified: /blahpath/ls-ios-develop.app/Assets.car
    

    ...这通常意味着您正在尝试使用中间输出目录而不是正确的存档。

    【讨论】:

    • xcrun PackageApplication 已被弃用,我相信; xcodebuild -exportArchivexcodebuild archive 之后似乎现在是首选(显然是在 Xcode 6 左右的某个时候,并且在 Xcode 7 左右的 PackageApplication 中开始出现问题)。遗憾的是,功能集并不相同。如果在使用 -exportArchive 时尚未在 pbxproj 中设置特定的配置文件,似乎很难使用它。
    【解决方案4】:

    我当前的解决方案是重命名用户现有的存档文件夹,运行构建,然后执行“查找”以将存档复制到我想要的位置,然后删除存档文件夹并将旧文件夹重命名为原样,使用代码在我的 ruby​​ 构建脚本中是这样的:

    # Move the existing archives out of the way
    system('mv ~/Library/Developer/Xcode/Archives ~/Library/Developer/Xcode/OldArchivesTemp')
    # Build the .app, the .DSYM, and the .xcarchive
    system("xcodebuild -scheme \"#{scheme}\" clean build archive CONFIGURATION_BUILD_DIR=\"#{build_destination_folder}\"")
    # Find the xcarchive wherever it was placed and copy it where i want it
    system("find ~/Library/Developer/Xcode/Archives -name *.xcarchive -exec cp -r {} \"#{build_destination_folder}\" \";\"")
    # Delete the new archives folder with this new xcarchive
    system('rm -rf ~/Library/Developer/Xcode/Archives')
    # Put the old archives back
    system('mv ~/Library/Developer/Xcode/OldArchivesTemp ~/Library/Developer/Xcode/Archives')
    

    它有点老套,但我目前没有看到更好的解决方案。至少它保留了用户的“档案”文件夹及其所有预先存在的档案。

    --重要提示!--

    我后来发现我找到存档并将其 cp 到我想要的文件夹的代码行没有正确复制存档中的符号链接,从而破坏了应用程序中的代码签名。您需要将其替换为“mv”或维护符号链接的东西。干杯!

    【讨论】:

    • 这是一个很棒的发现,但不适用于同时运行大量并发构建的 CI 系统。我要试试这个 bashery:export ARCHIVE_BASEPATH="${HOME}/Library/Developer/Xcode/Archives/$(date +%Y-%m-%d)/${SCHEME}" && \ ls -td "${ARCHIVE_BASEPATH}"* | \ head -n 1 其中 SCHEME 是正在构建的 Xcode 方案的字符串名称(可能包含空格)。如果两个不同的 CI 构建当前正在构建相同的方案,这仍然会有竞争条件。
    【解决方案5】:

    这是我为我们的 Jenkins CI 系统提出的一些 bash。这些命令应该在xcodebuild archive 命令完成后立即在脚本中运行。

    BUILD_DIR="${WORKSPACE}/build"
    XCODE_SCHEME="myscheme"
    
    # Common path and partial filename
    ARCHIVE_BASEPATH="${HOME}/Library/Developer/Xcode/Archives/$(date +%Y-%m-%d)/${XCODE_SCHEME}"
    
    # Find the latest .xcarchive for the given scheme
    NEW_ARCHIVE=$(ls -td "${ARCHIVE_BASEPATH}"* | head -n 1)
    
    # Zip it up so non-Apple systems won't treat it as a dir
    pushd "${NEW_ARCHIVE%/*}"
    zip -r "${BUILD_DIR}/${NEW_ARCHIVE##*/}.zip" "${NEW_ARCHIVE##*/}"
    popd
    
    # Optional, disk cleanup
    rm -rf "${NEW_ARCHIVE}"
    

    BUILD_DIR 用于收集工件,以便使用诸如 build/*.ipa,build/*.zip 之类的 glob 从 Jenkins 轻松归档它们

    【讨论】:

      【解决方案6】:

      与其他类似,但可能更简单一些,因为我尝试记录 .xcarchive 文件的位置。 (我也不会移动存档文件夹,因此如果您同时进行多个构建,这会更好。)

      我的调用者构建脚本会生成一个新的临时文件并将其路径设置为名为@9​​87654322@ 的环境变量。这个环境变量在我的方案的 Archive post-action shell 脚本中可用,然后将 .xcarchive 的路径写入该文件。构建脚本在调用xcodebuild archive 后可以读取该文件。

      事后 shell 脚本

      echo $ARCHIVE_PATH > "$XCARCHIVE_PATH_TMPFILE"
      

      【讨论】:

        【解决方案7】:

        在 Xcode 4.6 上,可以为要编译到 xcarchive 的方案指定构建后操作:

        echo "ARCHIVE_PATH=\"$ARCHIVE_PATH\"" > $PROJECT_DIR/archive_paths.sh
        

        可以使用构建脚本检查在运行 xcodebuild 后是否定义了 $ARCHIVE_PATH,如果是,可以将输出的 xcarchive 移动到指定的文件夹中。

        如果项目中的target数量很多,这种方法维护性不是很好,因为每一个都需要将对应的scheme标记为'shared'并添加post-build action。

        为了解决这个问题,我创建了一个构建脚本,它通过提取与当天目标名称匹配的最后一个构建以编程方式生成存档路径。只要机器上没有运行具有相同目标名称的多个构建(这在运行多个并发构建的生产环境中可能是个问题),此方法就可以可靠地工作。

        #!/bin/bash
        #
        # Script to archive an existing xcode project to a target location.
        # The script checks for a post-build action that defines the $ARCHIVE_PATH as follows:
        # echo "ARCHIVE_PATH=\"$ARCHIVE_PATH\"" > $PROJECT_DIR/archive_paths.sh
        # If such post-build action does not exist or sourcing it doesn't define the $ARCHIVE_PATH   
        # variable, the script tries to generate it programmatically by finding the latest build
        # in the expected archiving folder
        #
        
        post_build_script=archive_paths.sh
        build_errors_file=build_errors.log
        OUTPUT=output/
        XCODEBUILD_CMD='/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild'
        TARGET_SDK=iphoneos
        
        function archive()
        {
            echo "Archiving target '$1'"
        
            # Delete $post_build_script if it already exists as it should be generated by a 
            # post-build action
            rm -f $post_build_script
        
            # Use custom provisioning profile and code sign identity if specified, otherwise
            # default to project settings
            # Note: xcodebuild always returns 0 even if the build failed. We look for failure in
            # the stderr output instead
            if [[ ! -z "$2" ]] && [[ ! -z "$3" ]]; then 
                ${XCODEBUILD_CMD} clean archive -scheme $1 -sdk "${TARGET_SDK}" \
                "CODE_SIGN_IDENTITY=$3" "PROVISIONING_PROFILE=$2" 2>$build_errors_file
            else
                ${XCODEBUILD_CMD} clean archive -scheme $1 -sdk "${TARGET_SDK}"
                2>$build_errors_file  
            fi
        
            errors=`grep -wc "The following build commands failed" $build_errors_file`
            if [ "$errors" != "0" ]
            then
                echo "BUILD FAILED. Error Log:"
                cat $build_errors_file
                rm $build_errors_file
                exit 1
            fi
            rm $build_errors_file
        
            # Check if archive_paths.sh exists
            if [ -f "$post_build_script" ]; then
                source "$post_build_script"
                if [ -z "$ARCHIVE_PATH" ]; then
                    echo "'$post_build_script' exists but ARCHIVE_PATH was not set.
                      Enabling auto-detection" 
                fi
            fi
            if [ -z "$ARCHIVE_PATH" ]; then
                # This is the format of the xcarchive path:
                # /Users/$USER/Library/Developer/Xcode/Archives/`date +%Y-%m-%d`/$1\ 
                # `date +%d-%m-%Y\ %H.%M`.xcarchive
                # In order to avoid mismatches with the hour/minute of creation of the archive and
                # the current time, we list all archives with the correct target that have been
                # built in the current day (this may fail if the build wraps around midnight) and
                # fetch the correct file with a combination of ls and grep.
                # This script can break only if there are multiple targets with exactly the same
                # name running at the same time.
                EXTRACTED_LINE=$(ls -lrt /Users/$USER/Library/Developer/Xcode/Archives/`date
                  +%Y-%m-%d`/ | grep $1\ `date +%d-%m-%Y` | tail -n 1)
                if [ "$EXTRACTED_LINE" == "" ]; then
                    echo "Error: couldn't fetch archive path"
                    exit 1
                fi
                # ls -lrt prints lines with the following format
                # drwxr-xr-x  5 mario  1306712193  170 25 Jul 17:17 ArchiveTest 25-07-2013
                #   17.17.xcarchive
                # We can split this line with the " " separator and take the latest bit:
                #   17.17.xcarchive
                FILE_NAME_SUFFIX=$(echo $EXTRACTED_LINE | awk '{split($0,a," "); print a[11]}')
                if [ "$FILE_NAME_SUFFIX" == "" ]; then
                    echo "Error: couldn't fetch archive path"
                    exit 1
                fi
                # Finally, we can put everything together to generate the path to the xcarchive
                ARCHIVE_PATH="/Users/$USER/Library/Developer/Xcode/Archives/`date 
                  +%Y-%m-%d`/$1 `date +%d-%m-%Y` $FILE_NAME_SUFFIX/"
            fi
        
            # Create output folder if it doesn't already exist
            mkdir -p "$OUTPUT"
        
            # Move archived xcarchive build to designated output folder
            mv -v "$ARCHIVE_PATH" "$OUTPUT"
        }
        
        
        # Check number of command line args
        if [ $# -lt 1 ]; then
            echo "Syntax: `basename $0` <target name> [/path/to/provisioning-profile]
              [<code sign identity]"
            exit 1
        fi
        
        if [ ! -z "$2" ]; then
            PROVISIONING_PROFILE="$2"
        fi
        
        if [ ! -z "$3" ]; then
            SIGN_PROVISIONING_PROFILE="$3"
        else
            if [ ! -z "$PROVISIONING_PROFILE" ]; then
                SIGN_PROVISIONING_PROFILE=$(cat "$PROVISIONING_PROFILE" | egrep -a -o
                  '[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}')
            fi
        fi
        
        
        archive "$1" "$PROVISIONING_PROFILE" "$SIGN_PROVISIONING_PROFILE"
        

        可以在此处找到带有示例 Xcode 项目的完整源代码:

        https://github.com/bizz84/Xcode-xcarchive-command

        【讨论】:

          猜你喜欢
          • 2015-10-19
          • 1970-01-01
          • 2017-03-09
          • 1970-01-01
          • 2017-07-01
          • 1970-01-01
          • 2018-11-05
          • 2013-04-28
          • 1970-01-01
          相关资源
          最近更新 更多