web-dev-qa-db-fra.com

Comment détecter l'architecture cible en utilisant CMake?

J'ai fait beaucoup de recherches et je suis incapable de trouver une réponse à cette question ... comment puis-je trouver de manière fiable l'architecture cible pour laquelle je compile, en utilisant CMake? En gros, l’équivalent de QMAKE_TARGET.Arch dans qmake.

La plupart des sources semblent suggérer CMAKE_SYSTEM_PROCESSOR, mais c'est une mauvaise solution car cela renverra toujours i386 sur OS X par exemple, que vous compiliez pour i386, x86_64, ppc ou ppc64.

De même, CMAKE_SIZEOF_VOID_P donne la taille du pointeur du system , pas la cible.

Je comprends qu'il existe CMAKE_OSX_ARCHITECTURES, mais il peut être vide s'il n'est pas défini. Dans ce cas, il semble utiliser par défaut la capacité du système. Alors, comment puis-je trouver les informations sur l'architecture cible?

Et spécifiquement pour OS X, comment puis-je différencier les versions 32, 64 et Intel Universal?

35
Jake Petroules

J'ai donc imaginé une solution plutôt créative à mon problème ... Il semble que CMake ne dispose d'aucune fonctionnalité permettant de détecter l'architecture cible.

Nous savons maintenant que nous pouvons facilement le faire en C, car des symboles tels que __i386__, __x86_64__, etc., seront définis en fonction de votre environnement. Heureusement, CMake a une fonction try_run qui compile et exécute un fichier de code source C arbitraire au cours de la phase de configuration.

Nous pourrions ensuite écrire un petit programme qui utilise un groupe d’ ifdefs et écrit le nom de l’architecture sur la console sous forme de chaîne. Le seul problème est que cela ne fonctionne que si l'hôte et le système cible sont identiques ... cela ne peut pas fonctionner pendant la compilation croisée, car vous pouvez compiler le binaire, mais vous ne pouvez pas l'exécuter pour voir sa sortie.

Voici où les choses deviennent intéressantes. Nous pouvons exploiter le préprocesseur C pour obtenir les informations nécessaires en écrivant délibérément un programme C cassé ... nous utilisons le concept original d'écriture du nom de l'architecture sur la console en fonction de ifdefs, mais au lieu de cela, nous allons simplement placer un # directive du préprocesseur d'erreur à la place d'un appel à printf.

Lorsque la fonction try_run de CMake compile le fichier C, la compilation échouera toujours, mais le message que nous avons placé dans la directive #error apparaîtra dans la sortie d'erreur du compilateur, que try_run nous retournera.

Par conséquent, tout ce que nous avons à faire est d’analyser le nom de l’architecture à partir de la sortie d’erreur du compilateur à l’aide de certaines commandes de chaîne CMake, et nous pouvons récupérer l’architecture cible ... même en cas de compilation croisée.

La partie du code spécifique à OS X utilise principalement CMAKE_OSX_ARCHITECTURES pour déterminer l'architecture cible, mais dans le cas où cela n'est pas spécifié, elle utilisera le même code que d'autres systèmes et renverra correctement x86_64 (pour les systèmes modernes sur lesquels il s'agit de la valeur par défaut du compilateur) ou i386. (pour les systèmes OS X plus anciens tels que Leopard).

J'ai testé et vérifié que cela fonctionnait sous Windows, OS X et Linux avec les générateurs Visual Studio 9 et 10 (x86, x86_64, ia64), Xcode, NMake, les fichiers MSYS Makefiles et Unix Makefiles. Le résultat correct est renvoyé à chaque fois.

Remarque: Cette solution peut échouer si vous transmettez délibérément des options -m32 ou -m64 à votre compilateur, ou tout autre indicateur pouvant affecter l'architecture cible (existe-t-il un moyen de transmettre tous les paramètres de l'environnement à try_run?) ; ce n'est pas quelque chose que j'ai testé. Tant que vous utilisez les paramètres par défaut pour votre générateur et que toutes les cibles sont compilées pour la même architecture, tout devrait bien se passer.

Le code source complet de ma solution est disponible sur GitHub: https://github.com/petroules/solar-cmake/blob/master/TargetArch.cmake

32
Jake Petroules

J'ai une solution pour le cas où l'hôte et le système cible sont identiques.

Vous devez d’abord appeler "uname -m" pour obtenir le " nom du matériel de la machine ". Ensuite, vous devez couper la fin " Retour chariot " pour que la valeur réelle revienne dans la variable fournie.

EXECUTE_PROCESS( COMMAND uname -m COMMAND tr -d '\n' OUTPUT_VARIABLE ARCHITECTURE )

Vous pouvez maintenant imprimer la variable $ {ARCHITECTURE}:

message( STATUS "Architecture: ${ARCHITECTURE}" )

ou faire de la canonisation pour mapper, par exemple "x86_64", "AMD64", ... à par exemple. "64 bits". Il en va de même pour 32Bit . Avec cela, vous pouvez exécuter une compilation dépendante de l'architecture comme:

if( ${ARCHITECTURE} STREQUAL "64Bit" )
    set( BLA_LIBRARY "/opt/lib/libBla.so" )
else()
    set( BLA_LIBRARY "/opt/lib32/libBla.so" )
endif()
14
Angstgeist

Si votre processus de construction impliquait plus de 1 cible, il est préférable de laisser CMake savoir de quoi Arch/toolchain est construit .. .. Vous pouvez suivre les instructions pour CMake cross -compilation, qui vous encourage à créer un fichier CMake de chaîne d'outils, qui vous permet de choisir la chaîne d'outils/le compilateur utilisé.

J'en ai créé un pour construire mon application Linux C++ pour le processeur arm, et je l'ai nommé toolchain-arm.cmake.

Il comprend set(CMAKE_SYSTEM_PROCESSOR arm).

J'ai ensuite exécuté CMake comme suit:

cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE={my toolchain cmake path}/toolchain-arm.cmake {my source path}

dans CMakeList.txt de mon projet, je peux me référer à CMAKE_SYSTEM_PROCESSOR comme je le souhaite.

Lors de la construction pour X86, je n'inclue pas la référence à -DCMAKE_TOOLCHAIN_FILE, laissant CMAKE_SYSTEM_PROCESSOR indéfini ou, du moins, non défini comme arm.

Voici mon toolchain-arm.cmake

SET(CMAKE_SYSTEM_NAME Linux)
SET(CMAKE_SYSTEM_VERSION 1)
set(CMAKE_SYSTEM_PROCESSOR arm)

# specify the cross compiler
SET(ENV{TOOLCHAIN_ROOT} /home/user/toolchain/tools-master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin )
SET(CMAKE_C_COMPILER   $ENV{TOOLCHAIN_ROOT}/arm-linux-gnueabihf-gcc)
SET(CMAKE_CXX_COMPILER $ENV{TOOLCHAIN_ROOT}/arm-linux-gnueabihf-gcc)

# search for programs in the build Host directories
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# for libraries and headers in the target directories
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
4

Cet article est vieux, donc désolé de ressusciter les morts ici, mais je pensais simplement partager la solution que j'avais proposée.

Je ne souhaitais utiliser aucune application externe et, malheureusement, le fichier toolchain.cmake que nous utilisons n'a pas Arch défini dans une autre variable. Je le détecte donc en consultant les variables CMAKE_C_FLAGS et CMAKE_CXX_FLAGS à la recherche de l'argument -march dans GCC. . S'il n'y en a pas, il retourne à CMAKE_Host_SYSTEM_PROCESSOR.

Un rapide coup d'œil à la documentation de Clang semble indiquer que cela ne fonctionnerait pas pour celui-là, mais qu'il aurait simplement besoin d'une seconde étape de regex pour correspondre à l'argument attendu.

set(TARGET_Arch_REGEX "^.*-march[= ]([^ ]+).*$")
string(REGEX MATCH "${TARGET_Arch_REGEX}" TARGET_Arch_MATCH ${CMAKE_C_FLAGS} ${CMAKE_CXX_FLAGS})
if (TARGET_Arch_MATCH)
    string(REGEX REPLACE "${TARGET_Arch_REGEX}" "\\1" TARGET_Arch ${CMAKE_C_FLAGS} ${CMAKE_CXX_FLAGS})
else()
    set(TARGET_Arch ${CMAKE_Host_SYSTEM_PROCESSOR})
endif()
1
Joe Balough

Pour l'instant, vous n'avez besoin d'aucun piratage pour déterminer l'architecture cible: la variable OSX_ARCHITECTURES par cible a été ajoutée à cmake et peut être utilisée dans votre but: http://public.kitware.com/Bug/view .php? id = 8725

0
slashdot