Cree una biblioteca estática gruesa (dispositivo + simulador) usando Xcode y SDK 4+
Parece que podemos, en teoría, crear una única biblioteca estática que incluya tanto el simulador como el iPhone y el iPad.
Sin embargo, Apple no tiene documentación sobre esto que pueda encontrar, y las plantillas predeterminadas de Xcode NO están configuradas para hacer esto.
Estoy buscando una técnica simple, portátil y reutilizable que se pueda realizar dentro de Xcode.
Algo de historia:
- En 2008, podíamos crear bibliotecas estáticas únicas que incluyeran tanto sim como dispositivo. Apple deshabilitó eso.
- A lo largo de 2009, creamos pares de bibliotecas estáticas: una para sim y otra para dispositivo. Apple ahora también lo ha desactivado.
Referencias:
Esta es una gran idea, es un enfoque excelente, pero no funciona: http://www.drobnik.com/touch/2010/04/universal-static-libraries/
- Hay algunos errores en su script que significan que solo funciona en su máquina; debería usar BUILT_PRODUCTS_DIR y/o BUILD_DIR en lugar de "estimarlos").
- El último Xcode de Apple le impide hacer lo que él ha hecho; simplemente no funcionará debido al cambio (documentado) en la forma en que Xcode procesa los objetivos).
Otro interrogador de SO preguntó cómo hacerlo SIN xcode y con respuestas que se centraron en la parte arm6 vs arm7, pero ignoró la parte i386: ¿ Cómo compilo una biblioteca estática (fat) para armv6, armv7 e i386?
- Desde los últimos cambios de Apple, la parte del Simulador ya no es la misma que la diferencia arm6/arm7; es un problema diferente, ver arriba).
ALTERNATIVAS:
Fácil de copiar y pegar de la última versión (pero las instrucciones de instalación pueden cambiar; ¡consulte a continuación!)
La biblioteca de Karl requiere mucho más esfuerzo de configuración, pero es una solución mucho mejor a largo plazo (convierte su biblioteca en un Framework).
Use esto, luego modifíquelo para agregar soporte para compilaciones de Archivo ; consulte el comentario de @Frederik a continuación sobre los cambios que está usando para que esto funcione bien con el modo Archivo.
CAMBIOS RECIENTES: 1. Se agregó soporte para iOS 10.x (mientras se mantiene el soporte para plataformas más antiguas)
Información sobre cómo usar este script con un proyecto integrado en otro proyecto (aunque recomiendo encarecidamente NO hacerlo nunca; Apple tiene un par de errores que impiden el espectáculo en Xcode si incrusta proyectos uno dentro de otro, desde Xcode 3.x hasta Xcode 4.6.x)
Script de bonificación que le permite incluir paquetes automáticamente (es decir, incluir archivos PNG, archivos PLIST, etc. de su biblioteca). Consulte a continuación (desplácese hacia abajo).
ahora es compatible con iPhone5 (utilizando la solución alternativa de Apple para los errores en lipo). NOTA: las instrucciones de instalación han cambiado (probablemente pueda simplificar esto cambiando el script en el futuro, pero no quiero arriesgarme ahora)
La sección "copiar encabezados" ahora respeta la configuración de compilación para la ubicación de los encabezados públicos (cortesía de Frederik Wallner)
Se agregó la configuración explícita de SYMROOT (¿quizás también sea necesario configurar OBJROOT?), gracias a Doug Dickinson
SCRIPT (esto es lo que tienes que copiar/pegar)
Para instrucciones de uso/instalación, consulte a continuación
##########################################
#
# c.f. https://stackoverflow.com/questions/3520977/build-fat-static-library-device-simulator-using-xcode-and-sdk-4
#
# Version 2.82
#
# Latest Change:
# - MORE tweaks to get the iOS 10+ and 9- working
# - Support iOS 10+
# - Corrected typo for iOS 1-10+ (thanks @stuikomma)
#
# Purpose:
# Automatically create a Universal static library for iPhone + iPad + iPhone Simulator from within XCode
#
# Author: Adam Martin - http://twitter.com/redglassesapps
# Based on: original script from Eonil (main changes: Eonil's script WILL NOT WORK in Xcode GUI - it WILL CRASH YOUR COMPUTER)
#
set -e
set -o pipefail
#################[ Tests: helps workaround any future bugs in Xcode ]########
#
DEBUG_THIS_SCRIPT="false"
if [ $DEBUG_THIS_SCRIPT = "true" ]
then
echo "########### TESTS #############"
echo "Use the following variables when debugging this script; note that they may change on recursions"
echo "BUILD_DIR = $BUILD_DIR"
echo "BUILD_ROOT = $BUILD_ROOT"
echo "CONFIGURATION_BUILD_DIR = $CONFIGURATION_BUILD_DIR"
echo "BUILT_PRODUCTS_DIR = $BUILT_PRODUCTS_DIR"
echo "CONFIGURATION_TEMP_DIR = $CONFIGURATION_TEMP_DIR"
echo "TARGET_BUILD_DIR = $TARGET_BUILD_DIR"
fi
#####################[ part 1 ]##################
# First, work out the BASESDK version number (NB: Apple ought to report this, but they hide it)
# (incidental: searching for substrings in sh is a nightmare! Sob)
SDK_VERSION=$(echo ${SDK_NAME} | grep -o '\d\{1,2\}\.\d\{1,2\}$')
# Next, work out if we're in SIM or DEVICE
if [ ${PLATFORM_NAME} = "iphonesimulator" ]
then
OTHER_SDK_TO_BUILD=iphoneos${SDK_VERSION}
else
OTHER_SDK_TO_BUILD=iphonesimulator${SDK_VERSION}
fi
echo "XCode has selected SDK: ${PLATFORM_NAME} with version: ${SDK_VERSION} (although back-targetting: ${IPHONEOS_DEPLOYMENT_TARGET})"
echo "...therefore, OTHER_SDK_TO_BUILD = ${OTHER_SDK_TO_BUILD}"
#
#####################[ end of part 1 ]##################
#####################[ part 2 ]##################
#
# IF this is the original invocation, invoke WHATEVER other builds are required
#
# Xcode is already building ONE target...
#
# ...but this is a LIBRARY, so Apple is wrong to set it to build just one.
# ...we need to build ALL targets
# ...we MUST NOT re-build the target that is ALREADY being built: Xcode WILL CRASH YOUR COMPUTER if you try this (infinite recursion!)
#
#
# So: build ONLY the missing platforms/configurations.
if [ "true" == ${ALREADYINVOKED:-false} ]
then
echo "RECURSION: I am NOT the root invocation, so I'm NOT going to recurse"
else
# CRITICAL:
# Prevent infinite recursion (Xcode sucks)
export ALREADYINVOKED="true"
echo "RECURSION: I am the root ... recursing all missing build targets NOW..."
echo "RECURSION: ...about to invoke: xcodebuild -configuration \"${CONFIGURATION}\" -project \"${PROJECT_NAME}.xcodeproj\" -target \"${TARGET_NAME}\" -sdk \"${OTHER_SDK_TO_BUILD}\" ${ACTION} RUN_CLANG_STATIC_ANALYZER=NO" BUILD_DIR=\"${BUILD_DIR}\" BUILD_ROOT=\"${BUILD_ROOT}\" SYMROOT=\"${SYMROOT}\"
xcodebuild -configuration "${CONFIGURATION}" -project "${PROJECT_NAME}.xcodeproj" -target "${TARGET_NAME}" -sdk "${OTHER_SDK_TO_BUILD}" ${ACTION} RUN_CLANG_STATIC_ANALYZER=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" SYMROOT="${SYMROOT}"
ACTION="build"
#Merge all platform binaries as a fat binary for each configurations.
# Calculate where the (multiple) built files are coming from:
CURRENTCONFIG_DEVICE_DIR=${SYMROOT}/${CONFIGURATION}-iphoneos
CURRENTCONFIG_SIMULATOR_DIR=${SYMROOT}/${CONFIGURATION}-iphonesimulator
echo "Taking device build from: ${CURRENTCONFIG_DEVICE_DIR}"
echo "Taking simulator build from: ${CURRENTCONFIG_SIMULATOR_DIR}"
CREATING_UNIVERSAL_DIR=${SYMROOT}/${CONFIGURATION}-universal
echo "...I will output a universal build to: ${CREATING_UNIVERSAL_DIR}"
# ... remove the products of previous runs of this script
# NB: this directory is ONLY created by this script - it should be safe to delete!
rm -rf "${CREATING_UNIVERSAL_DIR}"
mkdir "${CREATING_UNIVERSAL_DIR}"
#
echo "lipo: for current configuration (${CONFIGURATION}) creating output file: ${CREATING_UNIVERSAL_DIR}/${EXECUTABLE_NAME}"
xcrun -sdk iphoneos lipo -create -output "${CREATING_UNIVERSAL_DIR}/${EXECUTABLE_NAME}" "${CURRENTCONFIG_DEVICE_DIR}/${EXECUTABLE_NAME}" "${CURRENTCONFIG_SIMULATOR_DIR}/${EXECUTABLE_NAME}"
#########
#
# Added: StackOverflow suggestion to also copy "include" files
# (untested, but should work OK)
#
echo "Fetching headers from ${PUBLIC_HEADERS_FOLDER_PATH}"
echo " (if you embed your library project in another project, you will need to add"
echo " a "User Search Headers" build setting of: (NB INCLUDE THE DOUBLE QUOTES BELOW!)"
echo ' "$(TARGET_BUILD_DIR)/usr/local/include/"'
if [ -d "${CURRENTCONFIG_DEVICE_DIR}${PUBLIC_HEADERS_FOLDER_PATH}" ]
then
mkdir -p "${CREATING_UNIVERSAL_DIR}${PUBLIC_HEADERS_FOLDER_PATH}"
# * needs to be outside the double quotes?
cp -r "${CURRENTCONFIG_DEVICE_DIR}${PUBLIC_HEADERS_FOLDER_PATH}"* "${CREATING_UNIVERSAL_DIR}${PUBLIC_HEADERS_FOLDER_PATH}"
fi
fi
INSTRUCCIONES DE INSTALACIÓN
- Crear un proyecto de biblioteca estática
- Seleccione el objetivo
- En la pestaña "Configuración de compilación", establezca "Construir solo arquitectura activa" en "NO" (para todos los elementos)
- En la pestaña "Fases de compilación", seleccione "Agregar... Nueva fase de compilación... Nueva fase de compilación de script de ejecución"
- Copie/pegue el script (arriba) en el cuadro
...BONIFICACIÓN de uso OPCIONAL:
- OPCIONAL: si tiene encabezados en su biblioteca, agréguelos a la fase "Copiar encabezados"
- OPCIONAL: ...y arrástrelos y suéltelos desde la sección "Proyecto" a la sección "Público"
- OPCIONAL: ...y se exportarán AUTOMÁTICAMENTE cada vez que cree la aplicación, a un subdirectorio del directorio "debug-universal" (estarán en usr/local/include)
- OPCIONAL: NOTA: si también intenta arrastrar/soltar su proyecto en otro proyecto de Xcode, esto expone un error en Xcode 4, donde no puede crear un archivo .IPA si tiene encabezados públicos en su proyecto de arrastrar/soltar. La solución alternativa: no incrustar proyectos xcode (¡demasiados errores en el código de Apple!)
Si no puede encontrar el archivo de salida, aquí tiene una solución alternativa:
Agregue el siguiente código al final del script (cortesía de Frederik Wallner): abra "${CREATING_UNIVERSAL_DIR}"
Apple elimina todos los resultados después de 200 líneas. Seleccione su destino y, en la fase de ejecución de script, DEBE desmarcar: "Mostrar variables de entorno en el registro de compilación"
Si está utilizando un directorio personalizado de "salida de compilación" para XCode4, entonces XCode coloca todos sus archivos "inesperados" en el lugar equivocado.
- construir el proyecto
- Haga clic en el último icono a la derecha, en el área superior izquierda de Xcode4.
- Seleccione el elemento superior (esta es su "compilación más reciente". Apple debería seleccionarla automáticamente, pero no pensaron en eso)
- en la ventana principal, desplácese hasta el final. La última línea debería decir: lipo: para la configuración actual (Depuración) creando el archivo de salida: /Users/blah/Library/Developer/Xcode/DerivedData/AppName-ashwnbutvodmoleijzlncudsekyf/Build/Products/Debug-universal/libTargetName.a
...esa es la ubicación de tu Universal Build.
Cómo incluir archivos "sin código fuente" en su proyecto (PNG, PLIST, XML, etc.)
- Haz todo lo anterior, comprueba que funciona.
- Cree una nueva fase de Ejecutar script que venga DESPUÉS DE LA PRIMERA (copie/pegue el código a continuación)
- Crea un nuevo Target en Xcode, de tipo "paquete"
- En su PROYECTO PRINCIPAL, en "Fases de compilación", agregue el nuevo paquete como algo de lo que "depende" (sección superior, presione el botón más, desplácese hacia abajo, busque el archivo ".bundle" en sus Productos)
- En su NUEVO OBJETIVO DEL PAQUETE, en "Fases de compilación", agregue una sección "Copiar recursos del paquete" y arrastre y suelte todos los archivos PNG, etc.
Script para copiar automáticamente los paquetes creados en la misma carpeta que su biblioteca estática FAT:
echo "RunScript2:"
echo "Autocopying any bundles into the 'universal' output folder created by RunScript1"
CREATING_UNIVERSAL_DIR=${SYMROOT}/${CONFIGURATION}-universal
cp -r "${BUILT_PRODUCTS_DIR}/"*.bundle "${CREATING_UNIVERSAL_DIR}"
He pasado muchas horas intentando crear una biblioteca estática completa que funcione en armv7, armv7s y el simulador. Finalmente encontré una solución .
Lo esencial es construir las dos bibliotecas (una para el dispositivo y luego otra para el simulador) por separado, cambiarles el nombre para distinguirlas entre sí y luego lipocrearlas en una sola biblioteca.
lipo -create libPhone.a libSimulator.a -output libUniversal.a
¡Lo probé y funciona!
Creé una plantilla de proyecto XCode 4 que le permite crear un marco universal tan fácilmente como crear una biblioteca normal.
Hay una utilidad de línea de comandos xcodebuild
y puede ejecutar el comando Shell dentro de xcode. Entonces, si no le importa usar un script personalizado, este script puede ayudarlo.
#Configurations.
#This script designed for Mac OS X command-line, so does not use Xcode build variables.
#But you can use it freely if you want.
TARGET=sns
ACTION="clean build"
FILE_NAME=libsns.a
DEVICE=iphoneos3.2
SIMULATOR=iphonesimulator3.2
#Build for all platforms/configurations.
xcodebuild -configuration Debug -target ${TARGET} -sdk ${DEVICE} ${ACTION} RUN_CLANG_STATIC_ANALYZER=NO
xcodebuild -configuration Debug -target ${TARGET} -sdk ${SIMULATOR} ${ACTION} RUN_CLANG_STATIC_ANALYZER=NO
xcodebuild -configuration Release -target ${TARGET} -sdk ${DEVICE} ${ACTION} RUN_CLANG_STATIC_ANALYZER=NO
xcodebuild -configuration Release -target ${TARGET} -sdk ${SIMULATOR} ${ACTION} RUN_CLANG_STATIC_ANALYZER=NO
#Merge all platform binaries as a fat binary for each configurations.
DEBUG_DEVICE_DIR=${SYMROOT}/Debug-iphoneos
DEBUG_SIMULATOR_DIR=${SYMROOT}/Debug-iphonesimulator
DEBUG_UNIVERSAL_DIR=${SYMROOT}/Debug-universal
RELEASE_DEVICE_DIR=${SYMROOT}/Release-iphoneos
RELEASE_SIMULATOR_DIR=${SYMROOT}/Release-iphonesimulator
RELEASE_UNIVERSAL_DIR=${SYMROOT}/Release-universal
rm -rf "${DEBUG_UNIVERSAL_DIR}"
rm -rf "${RELEASE_UNIVERSAL_DIR}"
mkdir "${DEBUG_UNIVERSAL_DIR}"
mkdir "${RELEASE_UNIVERSAL_DIR}"
lipo -create -output "${DEBUG_UNIVERSAL_DIR}/${FILE_NAME}" "${DEBUG_DEVICE_DIR}/${FILE_NAME}" "${DEBUG_SIMULATOR_DIR}/${FILE_NAME}"
lipo -create -output "${RELEASE_UNIVERSAL_DIR}/${FILE_NAME}" "${RELEASE_DEVICE_DIR}/${FILE_NAME}" "${RELEASE_SIMULATOR_DIR}/${FILE_NAME}"
Quizás parezca ineficiente (no soy bueno con los scripts de shell), pero es fácil de entender. Configuré un nuevo objetivo ejecutando solo este script. El script está diseñado para la línea de comandos pero no se ha probado en :)
El concepto central es xcodebuild
y lipo
.
Probé muchas configuraciones dentro de la interfaz de usuario de Xcode, pero nada funcionó. Debido a que se trata de un tipo de procesamiento por lotes, el diseño de línea de comandos es más adecuado, por lo que Apple eliminó gradualmente la función de compilación por lotes de Xcode. Por lo tanto, no espero que ofrezcan la función de creación por lotes basada en UI en el futuro.