web-dev-qa-db-fra.com

Compilation d'une bibliothèque C++ externe à utiliser avec un projet iOS

L’utilisation des bibliothèques C++ étant totalement nouvelle pour moi, je vous prie d’apprécier que cela soit peut-être un peu spécifique à mon cas (faites-le moi savoir et je peux vous fournir plus de détails). 

J'ai une bibliothèque externe C++ que j'essaie d'utiliser avec un projet iOS. La bibliothèque suit un modèle de configuration, make, make pour générer un fichier de bibliothèque .a. Lorsque j'essaie d'ajouter ce fichier de bibliothèque à Xcode, le message d'erreur suivant s'affiche:

fichier ignorant /Users/Developer/iOS/TestProj/libpresage.a, le fichier était construit pour l'archive qui n'est pas l'architecture en cours de liaison (i386):

/Users/Developer/iOS/TestProj/libpresage.a

Sur la base de cette question , j'ai essayé de transformer NO en architecture active uniquement, et j'obtiens la même erreur. Cela me fait penser que j'ai compilé la bibliothèque pour une architecture incorrecte. 

Lancer lipo -info sur le fichier .a donne: 

le fichier d'entrée libpresage.a n'est pas un fichier fat. Fichier non fat: libpresage.a.

est l'architecture: x86_64

Etant donné que ce ne sont pas armv7s, armv7 ou arm64, j’essaie de compiler à nouveau la bibliothèque C++ avec les paramètres suivants:

1) Essayer 

./configure CC="gcc -Arch armv7s" \
                 CXX="g++ -Arch armv7s" \
                 CPP="gcc -E" CXXCPP="g++ -E"

Erreur lors de la compilation, je reçois:

ld: library not found for -lcrt1.3.1.o
clang: error: linker command failed with exit code 1 (use -v to see invocation)

2) Essayer 

./configure CC="gcc -Arch arm64" \
                 CXX="g++ -Arch arm64" \
                 CPP="gcc -E" CXXCPP="g++ -E"

Erreur lors de la compilation, je reçois:

ld: warning: ld: warning: ignorant le fichier /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib/libSystem.dylib, manquant requis architecture arm64 dans le fichier /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib/libSystem.dylib (2 tranches) en ignorant le fichier /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib/libstdc++.dylib, manquant requis architecture arm64 dans le fichier /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib/libstdc++.dylib (2 tranches)

ld: les exécutables principaux dynamiques doivent être liés à libSystem.dylib pour architecture arm64 clang: error: la commande de l'éditeur de liens a échoué avec le code de sortie 1 (utilisez -v pour voir l'invocation)

Y a-t-il quelque chose d'évident qui me manque?

MODIFIER:

Merci pour les réponses. J'ai donc réussi à obtenir la bibliothèque dans Xcode en tant que cible de construction personnalisée, en pointant la commande "make" vers les bibliothèques MakeFile. Cette construction va bien.

Mes pas d'ici:

  • Ajoutez une dépendance de ma cible d'application iOS Objective C à la cible de génération personnalisée.
  • Référencez la bibliothèque et créez un wrapper Objective C++. 
  • Cela semble aller jusqu'à ce que j'aie besoin d'appeler la bibliothèque externe C++, puis j'obtiens le message d'erreur lors de la compilation:

Symboles non définis pour l'architecture armv7: "Presage :: Presage (PresageCallback *)", référencé depuis: - [PresageBridge init] dans PresageBridge.o "Presage :: ~ Presage ()", référencé à partir de: - [PresageBridge init] dans PresageBridge.o ld: symbole (s) non trouvé (s) pour l'architecture armv7 clang: erreur: la commande de l'éditeur de liens a échoué avec le code de sortie 1 (utilisez -v pour voir l'invocation)

  • Mon wrapper Objective C++ (en reliant l'en-tête de la bibliothèque externe C++ presage.h):

    #import "PresageBridge.h"
    #include "presage.h"
    
    @implementation PresageBridge
    
    - (instancetype)init
    {
        if(self = [super init])
        {
    
           Presage hello(&callback);
        }
    
        return self;
    }
    
  • D'après le code ci-dessus, il ne semble pas que l'en-tête me manque. Ce qui est intéressant, c'est que j'ai également essayé de créer une instance d'autres classes dans la bibliothèque externe et qu'elles semblent fonctionner, ce qui suggère que Xcode peut pas lier correctement presage.h pour une raison quelconque. 

16
HHHH

J'ai donc utilisé de nombreuses bibliothèques C++ tierces dans mes projets iOS. Il existe différentes stratégies que les gens utilisent pour cela. Comme certains l'ont déjà mentionné, vous pouvez directement inclure le code dans le projet, construire la bibliothèque statique avec Xcode ou le créer en ligne de commande. Dans le cas de bibliothèques C++ multiplateformes qui utilisent le système de configuration et de construction GNU, je préfère la ligne de commande. Vous ne devez la construire qu'une seule fois et vous ne devez la revoir que si vous devez mettre à jour la version ou ajouter une nouvelle tranche d'architecture. 

L'approche généralisée que vous souhaitez est la suivante:

  • Déterminez les bons arguments de configuration à utiliser pour construire chaque tranche. En règle générale, vous ne devez vous concentrer que sur l'un des bras et sur le i386. Les autres sont faciles à faire. Dans certains cas, vous devez réellement modifier le fichier de configuration pour ajouter l'hôte ou effectuer d'autres ajustements.

  • Une fois que vous pouvez créer toutes les tranches, vous voulez exécuter lipo pour créer un gros binaire.

La meilleure façon de résoudre ce problème consiste à créer un script de construction qui effectuera tout le travail à votre place. De cette façon, il est plus facile de refaire. Plus important encore, vous pouvez réutiliser le script ou le permuter pour créer d’autres bibliothèques externes.

Vous pouvez créer le script de nombreuses façons. En voici un. J'ai plusieurs variantes de ce type de script. Ce script a été utilisé pour construire cURL. Il a plus ou moins travaillé pour le présage avec très peu de mod (c.-à-d. Un changement de boucle en présage). Notez que je ne l'ai pas testé dans Xcode (c'est-à-dire le lier et l'exécuter). J'ai constaté que je devais désactiver sqlite, sinon il construisait des outils qui ne construisaient pas correctement. Si vous en avez besoin, vous pouvez comprendre cette partie.

Vous pouvez le rendre plus lisse de plusieurs façons. Par exemple, utiliser un tableau pour stocker toutes les architectures. Ceci est juste la force brute.

Les points clés du script sont:

  1. Obtenir le dernier SDK
  2. Construire chaque tranche
  3. Puis courir lipo

Notez que cela devrait fonctionner par défaut, cependant, YMMV. Soyez prêt à avoir à déboguer si nécessaire. Par exemple, je n'ai pas confirmé le type d'hôte, mais c'est généralement ce que j'ai toujours utilisé. Vous voulez mettre ceci dans le répertoire pour presage (même répertoire que configure). Lorsque cela est fait, toutes les architectures sont dans le répertoire de sortie. La bibliothèque universelle est dans le répertoire presage.

Rappelez-vous également qu'il est de votre responsabilité de lier correctement dans la bibliothèque universelle et de définir correctement le chemin de recherche des fichiers d'en-tête.

#!/bin/bash

PLATFORMPATH="/Applications/Xcode.app/Contents/Developer/Platforms"
TOOLSPATH="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin"
export IPHONEOS_DEPLOYMENT_TARGET="8.0"
pwd=`pwd`

findLatestSDKVersion()
{
    sdks=`ls $PLATFORMPATH/$1.platform/Developer/SDKs`
    arr=()
    for sdk in $sdks
    do
       arr[${#arr[@]}]=$sdk
    done

    # Last item will be the current SDK, since it is alpha ordered
    count=${#arr[@]}
    if [ $count -gt 0 ]; then
       sdk=${arr[$count-1]:${#1}}
       num=`expr ${#sdk}-4`
       SDKVERSION=${sdk:0:$num}
    else
       SDKVERSION="8.0"
    fi
}

buildit()
{
    target=$1
    hosttarget=$1
    platform=$2

    if [[ $hosttarget == "x86_64" ]]; then
        hostarget="i386"
    Elif [[ $hosttarget == "arm64" ]]; then
        hosttarget="arm"
    fi

    export CC="$(xcrun -sdk iphoneos -find clang)"
    export CPP="$CC -E"
    export CFLAGS="-Arch ${target} -isysroot $PLATFORMPATH/$platform.platform/Developer/SDKs/$platform$SDKVERSION.sdk -miphoneos-version-min=$SDKVERSION"
    export AR=$(xcrun -sdk iphoneos -find ar)
    export RANLIB=$(xcrun -sdk iphoneos -find ranlib)
    export CPPFLAGS="-Arch ${target}  -isysroot $PLATFORMPATH/$platform.platform/Developer/SDKs/$platform$SDKVERSION.sdk -miphoneos-version-min=$SDKVERSION"
    export LDFLAGS="-Arch ${target} -isysroot $PLATFORMPATH/$platform.platform/Developer/SDKs/$platform$SDKVERSION.sdk"

    mkdir -p $pwd/output/$target

     ./configure --prefix="$pwd/output/$target" --disable-shared --disable-sqlite --Host=$hosttarget-Apple-darwin

    make clean
    make
    make install
}

findLatestSDKVersion iPhoneOS

buildit armv7 iPhoneOS
buildit armv7s iPhoneOS
buildit arm64 iPhoneOS
buildit i386 iPhoneSimulator
buildit x86_64 iPhoneSimulator

LIPO=$(xcrun -sdk iphoneos -find lipo)
$LIPO -create $pwd/output/armv7/lib/libpresage.a  $pwd/output/armv7s/lib/libpresage.a $pwd/output/arm64/lib/libpresage.a $pwd/output/x86_64/lib/libpresage.a $pwd/output/i386/lib/libpresage.a -output libpresage.a
29
Mobile Ben

Étant donné que vous êtes nouveau avec les bibliothèques C++, je suppose que vous devrez faire un peu plus de recherches.

Cependant, je vais essayer de souligner certaines étapes que vous devez prendre en compte:

  • vous devez vous assurer de compiler pour la même architecture à la fois la bibliothèque statique (.a) et le projet
  • de votre erreur, vous devez compiler votre bibliothèque statique pour i386 OR changez votre projet en x86_64 (la différence entre ces architectures est un peu plus complexe, mais pour le moment disons que i386 correspond au bureau 32 bits tandis que x86_64 correspond au bureau 64 bits)
  • les architectures de bras sont pour iPhone, pas pour votre MacOS (c'est pourquoi il ne parvient pas à trouver des bibliothèques avec une architecture de bras dans le dossier MacOSX)!

Il y a plusieurs façons d'aborder ces problèmes.

Pour le premier, je suggérerais d'inclure dans votre espace de travail la bibliothèque statique et de l'ajouter comme dépendance à votre cible de construction. Pour cela, vous devez comprendre les constructions XCode.

J'imagine que vous essayez en fait de créer une application pour téléphone. Par conséquent, pour la troisième option, vous devez configurer votre version de g ++ de manière à ce qu'elle se penche sur l'iPhoneSDK à partir de XCode lors de la liaison de cibles bras (recherchez iPhoneOS.platform).

Construire un bras ne fonctionnera que sur les iPhones. Si vous voulez que cela fonctionne sur le simulateur, vous devez lier votre bibliothèque statique à des bibliothèques situées dans le fichier iPhoneSimulator.platform.

Si vous souhaitez que votre bibliothèque statique fonctionne à la fois pour iPhone et pour le simulateur iPhone, vous devez créer une bibliothèque "fat" (une bibliothèque contenant essentiellement des symboles pour les deux plates-formes).

Si ces plateformes vous manquent, vous pouvez les télécharger à partir de XCode (mais je crois qu’elles sont là)

Comme vous pouvez le constater, les choses vont devenir de plus en plus complexes en cours de route, donc je recommande fortement d'utiliser XCode pour compiler la bibliothèque statique (c'est toujours faisable avec g ++ thou).

Je crois que les concepts suivants sur lesquels vous seriez utile de rechercher:

  • bras, x86, x86_64
  • bibliothèque statique
  • liaison statique
  • fat lib (bibliothèque universelle)
  • Espace de travail XCode avec plusieurs projets

J'espère que cela t'aides :).

6
MichaelCMS

Voici ce qui a fonctionné pour moi dans Xcode 9 pour appareils iOS (iPhone X):
1) Compilez dylib avec ces drapeaux comme suit:
a) "Répertoire d'installation": @ executable_path/Frameworks b) "Chemin de recherche Runpath": @ chemin_exécutable/Frameworks
Voir l'image ci-dessous:
Paramètres de Dylib dans Xcode 9

2) Dans le projet Xcode où le dylib est utilisé/lié:
a) "Chemin de recherche Runpath":
@ chemin_exécutable/Frameworks
b) Dans "Phase de construction-> Bibliothèques intégrées", assurez-vous de sélectionner "Destination" comme "Exécutables" et le sous-chemin comme "Frameworks", puis "Cocher le code sur la copie":
Réglage de l’application iOS de liaison

Cette méthode est testée et utilisée avec Xcode 9.2 et iPhone X. 

David

1
us_david

Redéfinissez votre architecture par défaut, puis essayez les options suivantes . 1. Dans Build Phases-> Link Binary With Libraries, ajoutez les bibliothèques libz.dylib et libxml2.dylib à votre projet. 2. Dans BuildSettings-> Chemins de recherche, définissez Toujours rechercher les chemins d'utilisateurs sur Oui, et sous Chemins de recherche du cadre, ajoutez le chemin correct de votre cadre. Utilisez terminal pour obtenir le bon chemin de votre framework. 3. Essayez de définir votre "Source du compilateur en tant que" sur C++ ou utilisez hit et trial et vérifiez toutes les options. Complier Source As est également sous BuildSettings. Utilisez cmd + f pour le rechercher.

Essayez-les et laissez-moi savoir, et dites-moi également à propos du framework ou du sdk que vous essayez d'utiliser dans votre projet. 

0
Nauman

C++ fonctionne sur iOS, vous pouvez simplement l'ajouter à votre projet. Ou si vous voulez vraiment avoir votre bibliothèque dynamique, vous pouvez la compiler avec Xcode et spécifier votre architecture cible. 

0
debug

Le script de Mobile Ben est génial, à part la dernière ligne codant en dur un nom de bibliothèque dans le script. Ce qui suit est un remplacement de la dernière commande lipo et utilise de manière dynamique lipo fusionne chaque fichier de bibliothèque créé après la compilation.

Changer la ligne de Mobile Ben:

$LIPO -create $pwd/output/armv7/lib/libpresage.a  $pwd/output/armv7s/lib/libpresage.a $pwd/output/arm64/lib/libpresage.a $pwd/output/x86_64/lib/libpresage.a $pwd/output/i386/lib/libpresage.a -output libpresage.a

à:

for t in `ls $pwd/output/armv7/lib/*.a` ;
do
        export LIB=`basename $t`
        export ARCHSTRING=""
        for a in `ls $pwd/output`
        do
                export ARCSTRING="$ARCSTRING $pwd/output/$a/lib/$LIB "
        done
        $LIPO -create $ARCSTRING  -output $LIB
done

Remarque: je ne souhaitais pas modifier la réponse de Mobile Ben avec une nouvelle fonctionnalité de code et l'ajouter à la mise en forme d'un commentaire.

0
A.Badger