xcode4 - subdirectorios - Genera xcarchive en una carpeta específica desde la línea de comando
mover archivos en linux (7)
A partir de 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.
Podrías mover / copiar el archivo aquí. Quería tener un poco más de control sobre el proceso en un script de CI, así que guardé un archivo temporal que podría obtenerse fácilmente 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 script de CI puedo ejecutar lo 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"
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.
Generar el IPA es bastante simple con un poco de Google, sin embargo, cómo se genera el archivo .XCARCHIVE es lo que me elude. Lo más cerca que he encontrado es:
xcodebuild -scheme myscheme archive
Sin embargo, esto almacena el .xcarchive en alguna carpeta difícil de encontrar, por ejemplo:
/Users/me/Library/Developer/Xcode/Archives/2011-12-14/MyApp 14-12-11 11.42 AM.xcarchive
¿Hay alguna manera de controlar dónde se coloca el archivo, 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?
Acabo de resolver este: simplemente agregue el argumento -archivePath
a su línea de comando de construcció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, envío mi compilación a $PWD/Build
)
Esto colocará su carpeta .app en:
Build/Archive.xarchive/Products/Application
Si su objetivo de compilación ya tiene su certificado de firma y su perfil de aprovisionamiento, 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 gusta los caminos relativos, por lo tanto, el pwd
)
El archivo -v arroja mucha información útil - este comando puede no firmar correctamente y todavía salir con el código 0, suspiro!
Si descubre que no puede ejecutar el .ipa incorporado, es probable que sea un problema de firma que puede hacer doble control utilizando:
codesign --verify -vvvv myapp.app
Si se firma correctamente y no se modifica la salida, esto se tendrá en:
myapp.app: valid on disk
myapp.app: satisfies its Designated Requirement
Si no, verás 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á intentando usar un directorio de salida intermedio en lugar del archivo adecuado.
En Xcode 4.6, es posible especificar una acción posterior a la compilación para que el esquema se compile en xcarchive:
echo "ARCHIVE_PATH=/"$ARCHIVE_PATH/"" > $PROJECT_DIR/archive_paths.sh
Se puede usar un script de compilación para verificar si $ ARCHIVE_PATH se define después de ejecutar xcodebuild y, si este es el caso, el xcarchive de salida se puede mover a una carpeta designada.
Este método no es muy fácil de mantener si los objetivos en el proyecto son un gran número, ya que para cada uno es necesario etiquetar el esquema correspondiente como ''compartido'' y agregar la acción posterior a la construcción.
Para solucionar este problema, he creado una secuencia de comandos de compilación que genera la ruta de archivo mediante programación extrayendo la última compilación que coincide con el nombre de destino en el día actual. Este método funciona de manera confiable siempre que no haya compilaciones múltiples con el mismo nombre de destino ejecutándose en la máquina (esto puede ser un problema en entornos de producción donde se ejecutan múltiples compilaciones simultáneas).
#!/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"
El código fuente completo con un ejemplo de proyecto Xcode se puede encontrar aquí:
He aquí algunos detalles que he creado para nuestro sistema Jenkins CI. Estos comandos se deben ejecutar en una secuencia de comandos inmediatamente después de que xcodebuild archive
comando de 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 usa para recolectar artefactos para que sea fácil archivarlos desde Jenkins con un glob como build/*.ipa,build/*.zip
Mi solución actual es cambiar el nombre de la carpeta de archivos existente del usuario, ejecutar la compilación y hacer una búsqueda para copiar los archivos que desee, luego eliminar la carpeta de archivos y cambiar el nombre de la carpeta anterior tal como estaba, con un código como este en mi script de construcción de 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 hacky pero no veo una mejor solución actualmente. Al menos preserva la carpeta ''archivos'' del usuario y todos sus archivos preexistentes.
--¡Nota IMPORTANTE!--
Desde que descubrí que la línea de código donde encuentro el archivo y lo copio en 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!
Similar a los otros, pero tal vez un poco más simple ya que trato de registrar la .xcarchive
archivo .xcarchive
. (Tampoco muevo la carpeta de archivos, así que esto funcionará mejor si estás haciendo varias compilaciones al mismo tiempo).
La secuencia de comandos 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 a xcodebuild archive
.
script de shell post-acción
echo $ARCHIVE_PATH > "$XCARCHIVE_PATH_TMPFILE"
Xcode 5 ahora es compatible con una opción -archivePath
:
xcodebuild -scheme myscheme archive -archivePath /path/to/AppName.xcarchive
También puede exportar una IPA firmada desde el archivo que acaba de crear:
xcodebuild -exportArchive -exportFormat IPA -exportProvisioningProfile my_profile_name -archivePath /path/to/AppName.xcarchive -exportPath /path/to/AppName.ipa