Quelle est la syntaxe CMake pour définir et utiliser des variables?

168

Je me le demande pour me rappeler la prochaine fois que j'utiliserai CMake. Cela ne colle jamais et les résultats de Google ne sont pas excellents.

Quelle est la syntaxe pour définir et utiliser des variables dans CMake?

CivFan
la source

Réponses:

281

Lorsque vous écrivez des scripts CMake, vous devez connaître beaucoup de choses sur la syntaxe et comment utiliser les variables dans CMake.

La syntaxe

Chaînes utilisant set():

  • set(MyString "Some Text")
  • set(MyStringWithVar "Some other Text: ${MyString}")
  • set(MyStringWithQuot "Some quote: \"${MyStringWithVar}\"")

Ou avec string():

  • string(APPEND MyStringWithContent " ${MyString}")

Listes utilisant set():

  • set(MyList "a" "b" "c")
  • set(MyList ${MyList} "d")

Ou mieux avec list():

  • list(APPEND MyList "a" "b" "c")
  • list(APPEND MyList "d")

Listes de noms de fichiers:

  • set(MySourcesList "File.name" "File with Space.name")
  • list(APPEND MySourcesList "File.name" "File with Space.name")
  • add_excutable(MyExeTarget ${MySourcesList})

La documentation

La portée ou "Quelle est la valeur de ma variable?"

Il y a d'abord les «variables normales» et les choses que vous devez savoir sur leur portée:

  • Variables normales sont visibles à l' CMakeLists.txtils sont fixés et tout appelé à partir de là ( add_subdirectory(), include(), macro()et function()).
  • Les commandes add_subdirectory()et function()sont spéciales, car elles ouvrent leur propre portée.
    • Ce qui signifie que les variables set(...)là-bas ne sont visibles que là-bas et font une copie de toutes les variables normales du niveau de portée à partir duquel elles sont appelées (appelée portée parente).
    • Donc, si vous êtes dans un sous-répertoire ou une fonction, vous pouvez modifier une variable déjà existante dans la portée parent avec set(... PARENT_SCOPE)
    • Vous pouvez utiliser cela par exemple dans les fonctions en passant le nom de la variable comme paramètre de fonction. Un exemple serait le function(xyz _resultVar)réglageset(${_resultVar} 1 PARENT_SCOPE)
  • D'un autre côté, tout ce que vous définissez dans include()ou les macro()scripts modifieront les variables directement dans la portée d'où elles sont appelées.

Deuxièmement, il y a le "cache de variables globales". Ce que vous devez savoir sur le cache:

  • Si aucune variable normale avec le nom donné n'est définie dans la portée actuelle, CMake recherchera une entrée Cache correspondante.
  • Les valeurs du cache sont stockées dans le CMakeCache.txtfichier de votre répertoire de sortie binaire.
  • Les valeurs du cache peuvent être modifiées dans l' application GUI de CMake avant d'être générées. Par conséquent, ils ont - par rapport aux variables normales - a typeet a docstring. Je n'utilise normalement pas l'interface graphique, donc je l'utilise set(... CACHE INTERNAL "")pour définir mes valeurs globales et persistantes.

    Veuillez noter que le INTERNALtype de variable de cache impliqueFORCE

  • Dans un script CMake, vous ne pouvez modifier les entrées de cache existantes que si vous utilisez la set(... CACHE ... FORCE)syntaxe. Ce comportement est utilisé par exemple par CMake lui-même, car il ne force normalement pas les entrées du cache lui-même et vous pouvez donc le prédéfinir avec une autre valeur.

  • Vous pouvez utiliser la ligne de commande pour définir des entrées dans le cache avec la syntaxe cmake -D var:type=value, juste cmake -D var=valueou avec cmake -C CMakeInitialCache.cmake.
  • Vous pouvez annuler la définition des entrées dans le cache avec unset(... CACHE).

Le cache est global et vous pouvez les définir pratiquement n'importe où dans vos scripts CMake. Mais je vous recommanderais de réfléchir à deux fois pour savoir où utiliser les variables de cache (elles sont globales et persistantes). Je préfère normalement la syntaxe set_property(GLOBAL PROPERTY ...)et set_property(GLOBAL APPEND PROPERTY ...)pour définir mes propres variables globales non persistantes.

Pièges variables et "Comment déboguer les changements de variables?"

Pour éviter les pièges, vous devez connaître les éléments suivants concernant les variables:

  • Les variables locales masquent les variables mises en cache si les deux ont le même nom
  • Les find_...commandes - en cas de succès - écrivent leurs résultats sous forme de variables mises en cache "afin qu'aucun appel ne recherche à nouveau"
  • Les listes dans CMake ne sont que des chaînes avec des points-virgules et les guillemets sont donc importants
    • set(MyVar a b c)est "a;b;c"et set(MyVar "a b c")est"a b c"
    • La recommandation est que vous utilisez toujours des guillemets à la seule exception lorsque vous souhaitez donner une liste sous forme de liste
    • Préférez généralement la list()commande pour gérer les listes
  • Toute la question de la portée décrite ci-dessus. En particulier, il est recommandé d'utiliser à la functions()place de macros()parce que vous ne voulez pas que vos variables locales apparaissent dans la portée parent.
  • Un grand nombre de variables utilisées par CMake sont définies avec les appels project()et enable_language(). Il pourrait donc être important de définir certaines variables avant d'utiliser ces commandes.
  • Les variables d'environnement peuvent différer de l'endroit où CMake a généré l'environnement make et du moment où les fichiers make sont utilisés.
    • Un changement dans une variable d'environnement ne déclenche pas à nouveau le processus de génération.
    • En particulier, un environnement IDE généré peut différer de votre ligne de commande, il est donc recommandé de transférer vos variables d'environnement dans quelque chose qui est mis en cache.

Parfois, seules les variables de débogage sont utiles. Les éléments suivants peuvent vous aider:

  • Utilisez simplement l'ancien printfstyle de débogage en utilisant la message()commande. Il existe également des modules prêts à l'emploi livrés avec CMake lui-même: CMakePrintHelpers.cmake , CMakePrintSystemInformation.cmake
  • Regardez dans le CMakeCache.txtfichier dans votre répertoire de sortie binaire. Ce fichier est même généré si la génération réelle de votre environnement make échoue.
  • Utilisez variable_watch () pour voir où vos variables sont lues / écrites / supprimées.
  • Regardez dans les propriétés du répertoire CACHE_VARIABLES et VARIABLES
  • Appelez cmake --trace ...pour voir le processus d'analyse complet de CMake. C'est en quelque sorte la dernière réserve, car cela génère beaucoup de sortie.

Syntaxe spéciale

  • Variables d'environnement
    • Vous pouvez lire $ENV{...}et écrire set(ENV{...} ...)des variables d'environnement
  • Expressions de générateur
    • Les expressions du générateur $<...>ne sont évaluées que lorsque le générateur de CMake écrit l'environnement de création (il est comparé aux variables normales qui sont remplacées "en place" par l'analyseur)
    • Très pratique par exemple dans les lignes de commande du compilateur / éditeur de liens et dans les environnements multi-configuration
  • Références
    • Avec ${${...}}vous pouvez donner des noms de variables dans une variable et référencer son contenu.
    • Souvent utilisé pour donner un nom de variable en tant que paramètre de fonction / macro.
  • Valeurs constantes (voir if()commande)
    • Avec if(MyVariable)vous pouvez directement vérifier une variable pour vrai / faux (pas besoin ici pour l'enfermement ${...})
    • Vrai si la constante est 1, ON, YES, TRUE, You un nombre non nul.
    • Faux si la constante est 0, OFF, NO, FALSE, N, IGNORE, NOTFOUND, la chaîne vide, ou se termine par le suffixe -NOTFOUND.
    • Cette syntaxe est souvent utilisée pour quelque chose comme if(MSVC), mais elle peut être déroutante pour quelqu'un qui ne connaît pas ce raccourci de syntaxe.
  • Substitutions récursives
    • Vous pouvez créer des noms de variables à l'aide de variables. Une fois que CMake a remplacé les variables, il vérifiera à nouveau si le résultat est une variable elle-même. C'est une fonctionnalité très puissante utilisée dans CMake lui-même, par exemple comme une sorte de modèleset(CMAKE_${lang}_COMPILER ...)
    • Mais sachez que cela peut vous donner mal à la tête dans les if()commandes. Voici un exemple où CMAKE_CXX_COMPILER_IDest "MSVC"et MSVCest "1":
      • if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") est vrai, car il évalue if("1" STREQUAL "1")
      • if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") est faux, car il évalue à if("MSVC" STREQUAL "1")
      • La meilleure solution ici serait donc - voir ci-dessus - de vérifier directement if(MSVC)
    • La bonne nouvelle est que cela a été corrigé dans CMake 3.1 avec l'introduction de la politique CMP0054 . Je recommanderais de toujours définir cmake_policy(SET CMP0054 NEW)"n'interpréter les if()arguments que comme des variables ou des mots clés lorsqu'ils ne sont pas entre guillemets".
  • La option()commande
    • Principalement juste des chaînes en cache qui ne peuvent être que ONou OFFet elles permettent une gestion spéciale comme par exemple les dépendances
    • Mais attention , ne confondez pas le optionavec la setcommande. La valeur donnée à optionn'est en réalité que la "valeur initiale" (transférée une fois dans le cache lors de la première étape de configuration) et est ensuite destinée à être modifiée par l'utilisateur via l'interface graphique de CMake .

Références

Florian
la source
Quand je l' utilise , if ("${MyString}" ...)je vois les avertissements: Policy CMP0054 is not set: Only interpret if() arguments as variables or keywords when unquoted. Voir, par exemple, Build 367 . Des idées?
jww
Et la suppression des guillemets ${MyString}conduit à un tas d'erreurs pour "si des arguments donnés ..." comme CMake erreur près de si: "si des arguments donnés" suivis de parantheses, "NOT", "EQUALS" et similaire .
jww
@jww L'avertissement signifie que MyStringcontient un nom de variable qui sera à nouveau dé-référencé. Je crois que personne ne veut vraiment de ce OLDcomportement. Donc , de mon point de vue de son tout à fait sûr et rétrocompatible à la politique qui se trouve juste CMP0054à NEW(voir la discussion ici ).
Florian
@jww Et le moyen le plus sûr d'éviter ces problèmes / avertissements est de simplement faire if (MyString ...)(si c'est votre code qui donne l'avertissement).
Florian
Merci. Nous avons supprimé toutes les occurrences de ${MyString}et l' avons remplacé par MyString(ou je pense que nous les avons toutes supprimées). Toujours pas de joie: Build 372 . La merde ne vient même pas de notre code. Il semble venir de CMake. La ligne 283 est if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC").
jww
18

Voici quelques exemples de base pour démarrer rapidement et sans problèmes.

Une variable d'élément

Définir la variable:

SET(INSTALL_ETC_DIR "etc")

Utiliser la variable:

SET(INSTALL_ETC_CROND_DIR "${INSTALL_ETC_DIR}/cron.d")

Variable multi-éléments (par exemple, liste)

Définir la variable:

SET(PROGRAM_SRCS
        program.c
        program_utils.c
        a_lib.c
        b_lib.c
        config.c
        )

Utiliser la variable:

add_executable(program "${PROGRAM_SRCS}")

CMake docs sur les variables

CivFan
la source
1

$ENV{FOO}pour l'utilisation, où FOOest extrait de la variable d'environnement. sinon utilisez as ${FOO}, où FOOest une autre variable. Pour le réglage, SET(FOO "foo")serait utilisé dans CMake.

parasrish
la source