Xcode4 从命令行将XCARVE生成到特定文件夹中

Xcode4 从命令行将XCARVE生成到特定文件夹中,xcode4,archive,ipa,Xcode4,Archive,Ipa,出于CI的目的,我需要能够在夜间构建中生成XCARCHIVE和IPA文件。IPA是为我们的测试人员准备的,将使用我们的特别密钥进行签名,XCARGIVE将发送到客户端,以便他们可以将其导入到Xcode中,并在满意时将其提交到应用商店 用谷歌搜索一下,生成IPA就足够简单了,但是如何生成.xarchive文件却让我迷惑不解。我发现最接近的是: xcodebuild -scheme myscheme archive 但是,这会将.xc存档存储在一些难以找到的文件夹中,例如: /Users/me/L

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

用谷歌搜索一下,生成IPA就足够简单了,但是如何生成.xarchive文件却让我迷惑不解。我发现最接近的是:

xcodebuild -scheme myscheme archive
但是,这会将.xc存档存储在一些难以找到的文件夹中,例如:

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

是否有某种方法可以控制归档文件的位置、名称以及如何避免重新编译?我想最好的可能结果是从您执行“xcodebuild构建”时生成的DSYM和应用程序生成xcarchive,这可能吗?

我当前的解决方案是重命名用户的现有归档文件夹,运行构建,然后执行“查找”以将归档复制到我想要的位置,然后删除归档文件夹并重新命名旧文件夹,在我的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')
这有点骇客,但我没有看到一个更好的解决方案,目前。至少它保留了用户的“归档”文件夹及其所有预先存在的归档

--重要提示--


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

从Xcode 4 Preview 5开始,有三个环境变量可以在scheme archive的post操作中访问

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"

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

我的调用者构建脚本生成一个新的tempfile,并将其路径设置为名为
xccarchive\u path\u TMPFILE
的环境变量。此环境变量可在我的方案的Archive post action shell脚本中使用,该脚本将.xArchive的路径写入该文件。在调用
xcodebuild archive
后可以读取该文件的生成脚本

动作后shell脚本


这是我为我们的詹金斯CI系统设计的一个小把戏。这些命令应该在
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用于收集工件,以便使用glob(例如
BUILD/*.ipa,BUILD/*.zip)从Jenkins处轻松存档工件。在Xcode 4.6上,可以为要编译到xcarchive中的方案指定编译后操作:

echo "ARCHIVE_PATH=\"$ARCHIVE_PATH\"" > $PROJECT_DIR/archive_paths.sh
生成脚本可用于检查运行xcodebuild后是否定义了$ARCHIVE\u路径,如果是这种情况,则可以将输出xcarchive移动到指定的文件夹中

如果项目中的目标数量很大,则此方法不太可维护,对于每个目标,有必要将相应的方案标记为“共享”,并添加构建后操作

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

#/bin/bash
#
#将现有xcode项目归档到目标位置的脚本。
#脚本检查生成后操作,该操作定义$ARCHIVE\u路径,如下所示:
#echo“ARCHIVE\u PATH=\”$ARCHIVE\u PATH\”>$PROJECT\u DIR/ARCHIVE\u PATH.sh
#如果此类生成后操作不存在或不存在,则不会定义$ARCHIVE\u路径
#变量,脚本尝试通过查找最新版本以编程方式生成它
#在预期的存档文件夹中
#
post_build_script=archive_path.sh
build\u errors\u file=build\u errors.log
输出=输出/
XCODEBUILD_CMD='/Applications/Xcode.app/Contents/Developer/usr/bin/XCODEBUILD'
TARGET_SDK=iphoneos
函数存档()
{
回显“存档目标“$1”
#删除$post_build_脚本(如果它已经存在),因为它应该由
#建造后行动
rm-f$后期构建脚本
#如果指定,请使用自定义配置文件和代码符号标识,否则
#默认为项目设置
#注意:即使生成失败,xcodebuild始终返回0。我们在中查找失败
#而不是stderr输出
如果[!-z“$2”]&&[!-z“$3”]];则
${XCODEBUILD\u CMD}清理归档-方案$1-sdk“${TARGET\u sdk}”\
“代码\u签名\u标识=$3”“设置\u配置文件=$2”2>$build\u errors\u文件
其他的
${XCODEBUILD\u CMD}清理归档-方案$1-sdk“${TARGET\u sdk}”
2> $build\u errors\u文件
fi
errors=`grep-wc“以下生成命令失败”$build\u errors\u file`
如果[“$errors”!=“0”]
然后
echo“生成失败。错误日志:”
cat$build\u错误\u文件
rm$build\u错误\u文件
出口1
fi
rm$build\u错误\u文件
#检查archive_paths.sh是否存在
如果[-f“$post_build_script”];则
源“$post_构建_脚本”
如果[-z“$ARCHIVE_PATH”];则
echo“$post_build_script”存在,但未设置存档路径。
启用自动检测“
fi
fi
如果[-z“$ARCHIVE_PATH”];则
#这是XCARVE路径的格式:
#/用户
echo "ARCHIVE_PATH=\"$ARCHIVE_PATH\"" > $PROJECT_DIR/archive_paths.sh
#!/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"
xcodebuild -scheme myscheme archive -archivePath /path/to/AppName.xcarchive
xcodebuild -exportArchive -exportFormat IPA -exportProvisioningProfile my_profile_name -archivePath /path/to/AppName.xcarchive -exportPath /path/to/AppName.ipa
xcodebuild -scheme myscheme archive
xcodebuild -scheme myscheme archive -archivePath Build/Archive
Build/Archive.xarchive/Products/Application
xcrun -v -sdk iphoneos PackageApplication -v `pwd`'/Build/Archive.xarchive/Products/Application/my.app' -o `pwd`'/myapp.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