iphone - une - write librairie c




Construire une bibliothèque statique de graisse(appareil+simulateur) en utilisant Xcode et SDK 4+ (6)

Il semble que nous puissions - théoriquement - construire une seule bibliothèque statique qui comprend à la fois un simulateur, un iPhone et un iPad.

Cependant, Apple n'a aucune documentation à ce sujet que je puisse trouver, et les modèles par défaut de Xcode ne sont PAS configurés pour faire ceci.

Je suis à la recherche d'une technique simple, portable, réutilisable qui peut être faite à l'intérieur de Xcode.

Un peu d'histoire:

  • En 2008, nous avions l'habitude de créer des librairies statiques simples qui incluaient sim et device. Apple a désactivé cela.
  • Tout au long de l'année 2009, nous avons créé des paires de bibliothèques statiques - une pour la simulation, une pour l'appareil. Apple a maintenant désactivé cela aussi.

Les références:

  1. C'est une excellente idée, c'est une excellente approche, mais ça ne marche pas: http://www.drobnik.com/touch/2010/04/universal-static-libraries/

    • Il y a quelques bugs dans son script qui signifie que cela ne fonctionne que sur sa machine - il devrait utiliser BUILT_PRODUCTS_DIR et / ou BUILD_DIR au lieu de les "estimer".
    • Le dernier Xcode d'Apple vous empêche de faire ce qu'il a fait - il ne fonctionnera tout simplement pas, en raison du changement (documenté) dans la manière dont Xcode traite les cibles)
  2. Un autre interrogateur SO a demandé comment le faire SANS xcode, et avec des réponses centrées sur la partie arm6 vs arm7 - mais a ignoré la partie i386: Comment compiler une bibliothèque statique (graisse) pour armv6, armv7 et i386

    • Depuis les derniers changements d'Apple, la partie Simulateur n'est plus la même que la différence d'arm6 / arm7 - c'est un problème différent, voir ci-dessus)

ALTERNATIVES:

Copier / coller facile de la dernière version (mais les instructions d'installation peuvent changer - voir ci-dessous!)

La bibliothèque de Karl demande beaucoup plus d'efforts pour l'installation, mais une solution à long terme beaucoup plus agréable (elle convertit votre bibliothèque en un cadre).

Utilisez-le, puis modifiez-le pour ajouter un support pour les constructions d'archives - cf @ Frederik's commentaire ci-dessous sur les changements qu'il utilise pour faire ce travail bien avec le mode Archive.

MODIFICATIONS RÉCENTES: 1. Ajout du support pour iOS 10.x (tout en maintenant le support pour les plates-formes plus anciennes)

  1. Informations sur la façon d'utiliser ce script avec un projet intégré dans un autre projet (bien que je recommande fortement de NE PAS faire cela, jamais - Apple a quelques bugs show-stopper dans Xcode si vous intégrez des projets les uns dans les autres, à partir de Xcode 3.x à Xcode 4.6.x)

  2. Script de bonus pour vous permettre d'inclure automatiquement des paquets (c'est-à-dire inclure des fichiers PNG, des fichiers PLIST etc de votre bibliothèque!) - voir ci-dessous (faire défiler vers le bas)

  3. supporte maintenant iPhone5 (en utilisant la solution de contournement d'Apple aux bugs de lipo). NOTE: les instructions d'installation ont changé (je peux probablement simplifier ceci en changeant le script à l'avenir, mais je ne veux pas risquer ça maintenant)

  4. La section "Copier les en-têtes" respecte maintenant le paramètre de construction pour l'emplacement des en-têtes publics (avec l'aimable autorisation de Frederik Wallner)

  5. Ajout d'un paramètre explicite de SYMROOT (peut-être que OBJROOT doit être défini aussi?), Merci à Doug Dickinson

SCRIPT (c'est ce que vous devez copier / coller)

Pour les instructions d'utilisation / installation, voir ci-dessous

##########################################
#
# c.f. https://.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:  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

INSTRUCTIONS D'INSTALLATION

  1. Créer un projet de bibliothèque statique
  2. Sélectionnez la cible
  3. Dans l'onglet "Paramètres de construction", définissez "Build Active Architecture Only" sur "NO" (pour tous les éléments)
  4. Dans l'onglet "Construire les Phases", sélectionnez "Ajouter ... Nouvelle Phase de Construction ... Nouvelle Phase de Construction du Script d'Exécution"
  5. Copiez / collez le script (ci-dessus) dans la boîte

... BONUS utilisation facultative:

  1. FACULTATIF: si vous avez des en-têtes dans votre bibliothèque, ajoutez-les à la phase "Copier les en-têtes"
  2. FACULTATIF: ... et faites-les glisser de la section "Projet" à la section "Public"
  3. FACULTATIF: ... et ils seront exportés AUTOMATIQUEMENT chaque fois que vous construisez l'application, dans un sous-répertoire du répertoire "debug-universal" (ils seront dans usr / local / include)
  4. FACULTATIF: NOTE: si vous essayez également de faire glisser / déposer votre projet dans un autre projet Xcode, cela expose un bogue dans Xcode 4, où il ne peut pas créer un fichier .IPA si vous avez des en-têtes publics dans votre projet. La solution de contournement: ne pas incorporer les projets xcode (trop de bogues dans le code d'Apple!)

Si vous ne trouvez pas le fichier de sortie, voici une solution de contournement:

  1. Ajoutez le code suivant à la toute fin du script (avec l'aimable autorisation de Frederik Wallner): open "$ {CREATING_UNIVERSAL_DIR}"

  2. Apple supprime toutes les sorties après 200 lignes. Sélectionnez votre cible, et dans la phase d'exécution du script, vous DEVEZ décocher: "Afficher les variables d'environnement dans le journal de construction"

  3. Si vous utilisez un répertoire "build output" personnalisé pour XCode4, XCode place tous vos fichiers "inattendus" au mauvais endroit.

    1. Construire le projet
    2. Cliquez sur la dernière icône à droite, dans la partie supérieure gauche de Xcode4.
    3. Sélectionnez l'élément supérieur (il s'agit de votre "build le plus récent" .Apple devrait le sélectionner automatiquement, mais il n'y a pas pensé)
    4. dans la fenêtre principale, faites défiler vers le bas. La dernière ligne devrait être: lipo: pour la configuration actuelle (Debug) création du fichier de sortie: /Users/blah/Library/Developer/Xcode/DerivedData/AppName-ashwnbutvodmoleijzlncudsekyf/Build/Products/Debug-universal/libTargetName.a

    ... c'est l'emplacement de votre construction universelle.

Comment inclure les fichiers "non sourcecode" dans votre projet (PNG, PLIST, XML, etc)

  1. Faites tout ce qui précède, vérifiez que cela fonctionne
  2. Créer une nouvelle phase de script d'exécution qui vient après le premier (copier / coller le code ci-dessous)
  3. Créer une nouvelle cible dans Xcode, de type "bundle"
  4. Dans votre projet principal, dans "Construire les phases", ajoutez le nouveau paquet comme "dépend de" (section supérieure, appuyez sur le bouton +, faites défiler vers le bas, trouvez le fichier ".bundle" dans vos produits)
  5. Dans votre NEW BUNDLE TARGET, dans "Build Phases", ajoutez une section "Copy Bundle Resources" et faites glisser / déposer tous les fichiers PNG etc

Script pour copier automatiquement le (s) paquet (s) intégré (s) dans le même dossier que votre bibliothèque statique 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}"

Bon travail! J'ai piraté quelque chose de similaire, mais j'ai dû le faire fonctionner séparément. Le fait de faire partie du processus de construction le rend beaucoup plus simple.

Un élément de note. J'ai remarqué qu'il ne copiait aucun des fichiers d'inclusion que vous marquez comme public. J'ai adapté ce que j'avais dans mon script au tien et ça marche plutôt bien. Collez ce qui suit à la fin de votre script.

if [ -d "${CURRENTCONFIG_DEVICE_DIR}/usr/local/include" ]
then
  mkdir -p "${CURRENTCONFIG_UNIVERSAL_DIR}/usr/local/include"
  cp "${CURRENTCONFIG_DEVICE_DIR}"/usr/local/include/* "${CURRENTCONFIG_UNIVERSAL_DIR}/usr/local/include"
fi

Il y a un utilitaire de ligne de commande xcodebuild et vous pouvez exécuter la commande shell dans xcode. Donc, si cela ne vous dérange pas d'utiliser un script personnalisé, ce script peut vous aider.

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

Peut-être semble inefficace (je ne suis pas bon au script shell), mais facile à comprendre. J'ai configuré une nouvelle cible exécutant uniquement ce script. Le script est conçu pour la ligne de commande mais pas testé dans :)

Le concept de base est xcodebuild et lipo .

J'ai essayé de nombreuses configurations dans Xcode UI, mais rien n'a fonctionné. Comme il s'agit d'une sorte de traitement par lots, la conception en ligne de commande est plus adaptée. Apple a donc supprimé progressivement la fonction de génération par lots de Xcode. Donc, je ne m'attends pas à ce qu'ils offrent la fonctionnalité de construction par lots basée sur l'interface utilisateur à l'avenir.


J'ai créé un modèle de projet XCode 4 qui vous permet de créer un framework universel aussi facilement qu'une bibliothèque classique.


J'avais besoin d'une librairie statique pour JsonKit, donc j'ai créé un projet de lib statique dans Xcode, puis j'ai exécuté ce script bash dans le répertoire du projet. Tant que vous avez configuré le projet xcode avec "Build active configuration only" désactivé, vous devriez obtenir toutes les architectures dans une lib.

#!/bin/bash
xcodebuild -sdk iphoneos
xcodebuild -sdk iphonesimulator
lipo -create -output libJsonKit.a build/Release-iphoneos/libJsonKit.a build/Release-iphonesimulator/libJsonKit.a

Je l'ai fait dans un modèle Xcode 4 , dans la même veine que le modèle de cadre statique de Karl.

J'ai trouvé que la construction de frameworks statiques (au lieu de bibliothèques statiques) causait des plantages aléatoires avec LLVM, en raison d'un bug de linker apparent - donc, je suppose que les bibliothèques statiques sont toujours utiles!





xcode