2012-01-04 13 views
29

Para propósitos de CI, necesito poder generar un XCARCHIVE y un archivo IPA en nuestra compilación nocturna. El IPA es para nuestros probadores, que se debe firmar con nuestras claves ad-hoc, y XCARCHIVE se envía al cliente para que pueda importarlo a Xcode y enviarlo a la tienda de aplicaciones cuando estén contentos con él.Genere xcarchive en una carpeta específica desde la línea de comandos

Generar el IPA es bastante simple con un poco de google, sin embargo, cómo se genera el archivo .XCARCHIVE es lo que me escapa. Lo más cerca que he encontrado es:

xcodebuild -scheme myscheme archive 

Sin embargo, esto almacena el .xcarchive en algunos difíciles de encontrar la carpeta, por ejemplo:

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

¿Hay alguna manera de controlar que el archivo está poner, ¿cómo se llama y cómo evitar tener que volver a compilarlo? Supongo que el mejor resultado posible sería generar el xcarchive de la DSYM y la APLICACIÓN que se generan cuando haces una 'construcción xcodebuild' - ¿es esto posible?

Respuesta

2

Mi solución actual es cambiar el nombre de la carpeta de archivos existente del usuario, ejecutar la compilación y hacer 'buscar' para copiar los archivos donde yo quiera, luego eliminar la carpeta de archivos y cambiar el nombre de la carpeta anterior tal como estaba, con código como este en mi script de construcción 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') 

Es un poco raro, pero no veo una mejor solución actualmente. Al menos preserva la carpeta 'archivos' del usuario y todos sus archivos preexistentes.

nota --Importante -

que ya descubrió que la línea de código donde encuentro el archivo y cp a la carpeta que quiero no copia los enlaces simbólicos dentro del archivo correctamente, rompiendo así la firma del código en la aplicación. Querrá reemplazar eso con un 'mv' o algo que mantenga enlaces simbólicos. ¡Aclamaciones!

+0

Este es un gran descubrimiento, pero no funciona para un sistema de CI con muchas compilaciones simultáneas que se ejecutan al mismo tiempo. Voy a probar este bit of bashery: export ARCHIVE_BASEPATH = "$ {HOME}/Library/Developer/Xcode/Archives/$ (date +% Y- m m%)/$ {SCHEME} "&& ls -td" $ {ARCHIVE_BASEPATH} "* | \ head -n 1 Donde SCHEME es el nombre de la cadena del esquema Xcode que se está construyendo (posiblemente con espacios). Esto todavía tendrá una condición de carrera si dos compilaciones de CI diferentes están construyendo el mismo esquema. – phatblat

41

Comenzando con Xcode 4 Preview 5 hay tres variables de entorno a las que se puede acceder en las acciones posteriores del archivo de esquema.

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. 

Se podía mover/copiar el archivo aquí. Quería tener un poco más de control sobre el proceso en una secuencia de comandos de CI, así que guardé un archivo temporal que podría ser fácilmente fuente en mi script de CI que contenía estos valores.

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 

Luego, en mi guión CI que puede ejecutar el siguiente:

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" 
+4

¡Gracias, gracias, gracias! ¿Cómo demonios encontraste esto? He peinado los documentos durante días buscando algo como esto. – jemmons

+0

para construir en @jemmons comment ... ¿dónde encontraste esto? Después de días de revisar los guiones personalizados de todos me gustaría leer algunos documentos oficiales – evanflash

+0

Esta es la única mención que puedo encontrar. Una mala documentación en su mejor momento. http://developer.apple.com/library/ios/releasenotes/developertools/rn-xcode/#//apple_ref/doc/uid/TP40001051-SW72 –

0

Al igual que los otros, pero tal vez un poco más simple ya que intento grabar la ubicación del archivo de .xcarchive. (Tampoco muevo la carpeta de archivos, así que esto funcionará mejor si está haciendo varias compilaciones al mismo tiempo).

Mi script de compilación de llamada genera un nuevo archivo de temp y establece su ruta a una variable de entorno llamada XCARCHIVE_PATH_TMPFILE. Esta variable de entorno está disponible en la secuencia de comandos del shell post-acción Archive de mi esquema, que luego escribe la ruta de .xcarchive a ese archivo. La secuencia de comandos de compilación que luego puede leer ese archivo después de llamar al xcodebuild archive.

posterior a la acción de secuencia de comandos shell

echo $ARCHIVE_PATH > "$XCARCHIVE_PATH_TMPFILE" 
2

Aquí hay un poco de fiesta que yo he llegado con nuestro sistema de Jenkins CI. Estos comandos se deben ejecutar en una secuencia de comandos inmediatamente después de que finalice el comando 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}" 

El BUILD_DIR se utiliza para recoger los artefactos para que sea más fácil de archivarlos de Jenkins con un pegote como build/*.ipa,build/*.zip

0

En Xcode 4.6, es posible especificar una acción posterior a la generación para que el esquema compilar en un xcarchive:

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

un script de construcción se puede utilizar para comprobar si $ archive_path se define después de ejecutar xcodebuild y si este es el caso, el xcarchive de salida se puede mover en una carpeta designada.

Este método no es muy fácil de mantener si los objetivos del proyecto son un número grande, como para cada uno de ellos es necesario etiquetar el esquema correspondiente como 'compartida' y añadir la acción posterior a la generación.

Para hacer frente a este problema, he creado un script de construcción que genera la vía de respaldo mediante programación mediante la extracción de la última versión que coincide con el nombre de destino en el día actual. Este método funciona con fiabilidad siempre que no haya múltiples construye con el mismo nombre de destino que se ejecuta en la máquina (esto puede ser un problema en entornos de producción donde concurrentes múltiples compilaciones son de ejecución).

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

código fuente completo con un proyecto de ejemplo Xcode se puede encontrar aquí:

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

50

Xcode 5 ahora soporta una opción -archivePath:

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

También puede ahora exportar una firmado IPA desde el archivo que acaba de compilar:

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

¡Genial! No sabía que 'xcodebuild' tiene esta función. Va a ser tan útil. –

+0

Solución mucho mejor. Gracias –

+0

Me alegro de que hayan agregado ese argumento ... esta es la respuesta correcta, asegúrate de incluir el nombre de archivo de xcarchive y no solo el camino ... ¡Gracias! –

8

acabo de haber resuelto este - sólo tiene que añadir el argumento -archivePath a la línea de comando de generación Xcode, dada la pregunta inicial que significaría:

xcodebuild -scheme myscheme archive 

se convierte ...

xcodebuild -scheme myscheme archive -archivePath Build/Archive 

(Nota: Las rutas son relativas, salida I mi construcción a $PWD/Build)

Esto entonces colocará su.carpeta de aplicación en:

Build/Archive.xarchive/Products/Application 

Si su destino de generación ya tiene su certificado de firma y archivo de suministro en ella a continuación, puede crear su archivo IPA sin volver a firmar con el siguiente comando:

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

(Nota : xcrun no le gustan las rutas relativas, por lo tanto, pwd)

El archivo -v arroja mucha información útil: este comando puede no firmar correctamente y aún así salir con el código 0, suspiro!

Si están encontrando que no se puede ejecutar la construyeron .ipa es probable que sea un problema de firma que se puede hacer un doble control sobre el uso de:

codesign --verify -vvvv myapp.app 

Si está firmado correctamente y poco alterado la salida tendrá esto en:

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

Si no se verá algo similar a esto:

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

... lo que generalmente significa que está tratando de usar un directorio de salida intermedio en lugar del archivo correcto.

+0

gracias. Tu información resolvió mi problema. – firebear

+0

'xcrun PackageApplication' está en desuso, creo; 'xcodebuild -exportArchive' después de un' xcodebuild archive' parece ser el preferido ahora (aparentemente fue el caso en algún momento alrededor de Xcode 6, y las cosas comenzaron a romperse en PackageApplication alrededor de Xcode 7). Lamentablemente, los conjuntos de funcionalidades no son idénticos; parece difícil obtener un perfil de aprovisionamiento particular si no estaba configurado en pbxproj cuando se usa '-exportArchive'. – leander

Cuestiones relacionadas