web-dev-qa-db-fra.com

Générez xcarchive dans un dossier spécifique à partir de la ligne de commande

Pour les besoins de CI, j'ai besoin de pouvoir générer un XCARCHIVE et un fichier IPA dans notre version nocturne. L'IPA est destiné à nos testeurs, à signer avec nos clés ad hoc, et le XCARCHIVE est à envoyer au client afin qu'il puisse l'importer dans Xcode et le soumettre à l'App Store quand il en est satisfait.

La génération de l'IPA est assez simple avec un peu de recherche sur Google, mais comment générer le fichier .XCARCHIVE est ce qui m'échappe. Le plus proche que j'ai trouvé est:

xcodebuild -scheme myscheme archive

Cependant, cela stocke le .xcarchive dans un dossier difficile à trouver, par exemple:

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

Existe-t-il un moyen de contrôler où l'archive est placée, quel est son nom et comment éviter d'avoir à la recompiler? Je suppose que le meilleur résultat possible serait de générer le xcarchive à partir du DSYM et de l'APP qui sont générés lorsque vous effectuez une "construction de xcodebuild" - est-ce possible?

34
Chris

Ma solution actuelle consiste à renommer le dossier d'archives existant de l'utilisateur, à exécuter la génération et à faire une "recherche" pour copier les archives où je veux, puis à supprimer le dossier d'archives et à renommer l'ancien dossier tel qu'il était, avec un code comme celui-ci dans mon Ruby script de construction:

# 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')

C'est un peu hacky mais je ne vois pas de meilleure solution actuellement. Au moins, il conserve le dossier "archives" de l'utilisateur et toutes ses archives préexistantes.

--Note importante!--

Depuis, j'ai découvert que la ligne de code où je trouve l'archive et la cp dans le dossier que je veux ne copie pas correctement les liens symboliques à l'intérieur de l'archive, brisant ainsi la signature de code dans l'application. Vous voudrez remplacer cela par un "mv" ou quelque chose qui maintient des liens symboliques. À votre santé!

2
Chris

Xcode 5 prend désormais en charge un -archivePath option:

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

Vous pouvez également désormais exporter une IPA signée à partir de l'archive que vous venez de créer:

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

À partir de Xcode 4 Preview 5, trois variables d'environnement sont accessibles dans les post-actions de l'archive de schéma.

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.

Vous pouvez déplacer/copier l'archive ici. Je voulais avoir un peu plus de contrôle sur le processus dans un script CI, j'ai donc enregistré un fichier temporaire qui pourrait facilement être trouvé dans mon script CI qui contenait ces valeurs.

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

Ensuite, dans mon script CI, je peux exécuter ce qui suit:

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"
41
Aron

Je viens de résoudre celui-ci - ajoutez simplement l'argument -archivePath à votre ligne de commande de construction xcode, étant donné la question initiale qui signifierait:

xcodebuild -scheme myscheme archive

devient ...

xcodebuild -scheme myscheme archive -archivePath Build/Archive

(Remarque: les chemins d'accès sont relatifs, je génère ma version sur $PWD/Build)

Cela placera ensuite votre dossier .app dans:

Build/Archive.xarchive/Products/Application

Si votre cible de build contient déjà votre certificat de signature et votre profil d'approvisionnement, vous pouvez ensuite créer votre fichier IPA sans re-signer à l'aide de la commande suivante:

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

(Remarque: xcrun n'aime pas les chemins relatifs d'où le pwd)

Le -v args dump beaucoup d'informations utiles - cette commande peut échouer à signer correctement et toujours quitter avec le code 0, soupir!

Si vous constatez que vous ne pouvez pas exécuter le .ipa intégré, il s'agit probablement d'un problème de signature que vous pouvez vérifier à l'aide de:

codesign --verify -vvvv myapp.app

S'il est signé correctement et non altéré, la sortie aura ceci dans:

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

Sinon, vous verrez quelque chose de similaire à ceci:

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

... ce qui signifie généralement que vous essayez d'utiliser un répertoire de sortie intermédiaire plutôt que l'archive appropriée.

9
Oliver Dungey

Voici un peu de bash que j'ai trouvé pour notre système CI Jenkins. Ces commandes doivent être exécutées dans un script immédiatement après le xcodebuild archive la commande se termine.

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}"

Le BUILD_DIR est utilisé pour collecter des artefacts afin qu'il soit facile de les archiver à partir de Jenkins avec un glob tel que build/*.ipa,build/*.Zip

2
phatblat

Sur Xcode 4.6, il est possible de spécifier une action post-build pour le schéma à compiler dans un xcarchive:

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

Un script de construction peut être utilisé pour vérifier si $ ARCHIVE_PATH est défini après l'exécution de xcodebuild et si c'est le cas, la sortie xcarchive peut être déplacée dans un dossier désigné.

Cette méthode n'est pas très maintenable si les cibles dans le projet sont un grand nombre, car pour chacune, il est nécessaire de marquer le schéma correspondant comme "partagé" et d'ajouter l'action post-construction.

Pour résoudre ce problème, j'ai créé un script de génération qui génère le chemin d'archivage par programme en extrayant la dernière génération qui correspond au nom cible du jour en cours. Cette méthode fonctionne de manière fiable tant qu'il n'y a pas plusieurs versions avec le même nom cible en cours d'exécution sur la machine (cela peut être un problème dans les environnements de production où plusieurs versions simultanées sont exécutées).

#!/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"

Le code source complet avec un exemple de projet Xcode peut être trouvé ici:

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

0
bizz84

Similaire aux autres, mais peut-être un peu plus simple puisque j'essaie d'enregistrer le .xcarchive emplacement du fichier. (Je ne déplace pas non plus le dossier d'archives, donc cela fonctionnera mieux si vous faites plusieurs builds en même temps.)

Mon script de génération d'appelant génère un nouveau fichier temporaire et définit son chemin vers une variable d'environnement nommée XCARCHIVE_PATH_TMPFILE. Cette variable d'environnement est disponible dans le script Shell post-action Archive de mon schéma, qui écrit ensuite le chemin d'accès du fichier .xcarchive à ce fichier. Le script de génération qui peut ensuite lire ce fichier après avoir appelé xcodebuild archive.

script Shell post-action

echo $ARCHIVE_PATH > "$XCARCHIVE_PATH_TMPFILE"
0
zekel