Qual è la syntax CMake per impostare e utilizzare le variabili?

Te lo chiedo come promemoria per me la prossima volta che userò CMake. Non si attacca mai e i risultati di Google non sono eccezionali.

Qual è la syntax per impostare e utilizzare le variabili in CMake?

Quando si scrivono gli script CMake, è necessario conoscere la syntax e le modalità di utilizzo delle variabili in CMake.

La syntax

Stringhe che utilizzano set() :

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

O con string() :

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

Elenchi che utilizzano set() :

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

O meglio con list() :

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

Elenchi di nomi file:

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

La documentazione

  • CMake / Sintassi della lingua
  • CMake: variabili elenca le stringhe
  • CMake: variabili utili
  • Comando CMake set()
  • Comando CMake string()
  • Comando CMake list()
  • Cmake: espressioni generatrici

The Scope o “Che valore ha la mia variabile?”

Innanzitutto ci sono le “variabili normali” e le cose che devi sapere sul loro scopo:

  • Le variabili normali sono visibili a CMakeLists.txt cui sono impostate e ogni cosa chiamata da lì ( add_subdirectory() , include() , macro() e function() ).
  • I add_subdirectory() e function() sono speciali, perché aprono il loro ambito.
    • Le variabili di significato set(...) sono visibili solo lì e fanno una copia di tutte le normali variabili del livello di ambito da cui sono chiamate (chiamato scope genitore).
    • Quindi se sei in una sottodirectory o in una funzione puoi modificare una variabile già esistente nell’ambito genitore con set(... PARENT_SCOPE)
    • È ansible farne uso, ad esempio nelle funzioni, passando il nome della variabile come parametro di funzione. Un esempio potrebbe essere la function(xyz _resultVar) sta impostando set(${_resultVar} 1 PARENT_SCOPE)
  • D’altra parte tutto ciò che imposti negli script include() o macro() modificherà le variabili direttamente nel campo di applicazione da cui vengono chiamate.

In secondo luogo c’è la “Global Variables Cache”. Cose che devi sapere sulla cache:

  • Se nessuna variabile normale con il nome specificato è definita nell’ambito corrente, CMake cercherà una voce Cache corrispondente.
  • I valori della cache sono memorizzati nel file CMakeCache.txt nella directory di output binario.
  • I valori nella cache possono essere modificati nell’applicazione GUI di CMake prima che vengano generati. Pertanto, rispetto alle variabili normali, hanno un type e una docstring . Normalmente non utilizzo la GUI, quindi uso set(... CACHE INTERNAL "") per impostare i miei valori globali e persistenti.

    Si noti che il tipo di variabile di cache INTERNAL implica FORCE

  • In uno script CMake è ansible modificare solo le voci Cache esistenti se si utilizza la syntax set(... CACHE ... FORCE) . Questo comportamento è utilizzato, ad esempio, da CMake stesso, perché normalmente non impone le voci della cache stessa e pertanto è ansible pre-definirlo con un altro valore.

  • È ansible utilizzare la riga di comando per impostare le voci nella cache con la syntax cmake -D var:type=value , solo cmake -D var=value o con cmake -C CMakeInitialCache.cmake .
  • Puoi disinserire le voci nella cache con unset(... CACHE) .

La cache è globale e puoi impostarli praticamente ovunque negli script di CMake. Ma ti consiglierei di pensarci due volte su dove usare le variabili Cache (sono globali e persistenti). Normalmente preferisco la set_property(GLOBAL PROPERTY ...) e set_property(GLOBAL APPEND PROPERTY ...) per definire le mie variabili globali non persistenti.

Difetti variabili e “Come eseguire il debug delle modifiche alle variabili?”

Per evitare le insidie ​​dovresti sapere quanto segue sulle variabili:

  • Le variabili locali nascondono le variabili memorizzate nella cache se entrambe hanno lo stesso nome
  • I comandi find_... – in caso di successo – scrivono i risultati come variabili memorizzate nella cache “in modo che nessuna chiamata esegua nuovamente la ricerca”
  • Gli elenchi in CMake sono solo stringhe con delimitatori di punti e virgola e pertanto le virgolette sono importanti
    • set(MyVar abc) è "a;b;c" e set(MyVar "abc") è "abc"
    • Si consiglia di utilizzare sempre le virgolette con l’unica eccezione quando si desidera fornire un elenco come elenco
    • Generalmente preferisci il comando list() per la gestione degli elenchi
  • L’intero problema dell’ambito sopra descritto. Soprattutto si raccomanda di usare functions() invece di macros() perché non si desidera che le variabili locali vengano mostrate nello scope genitore.
  • Molte variabili utilizzate da CMake sono impostate con le chiamate project() e enable_language() . Quindi potrebbe essere importante impostare alcune variabili prima che questi comandi vengano usati.
  • Le variabili di ambiente possono differire da quelle in cui CMake ha generato l’ambiente di produzione e quando vengono utilizzati i file di make.
    • Una modifica di una variabile d’ambiente non ritriggers il processo di generazione.
    • Soprattutto un ambiente IDE generato può differire dalla tua linea di comando, quindi è consigliabile trasferire le variabili di ambiente in qualcosa che è memorizzato nella cache.

A volte solo il debug delle variabili aiuta. Quanto segue può aiutarti:

  • Basta usare il vecchio stile di debug printf usando il comando message() . Esistono anche alcuni moduli pronti per l’uso forniti con CMake stesso: CMakePrintHelpers.cmake , CMakePrintSystemInformation.cmake
  • Cerca nel file CMakeCache.txt nella directory di output binario. Questo file viene anche generato se la generazione effettiva dell’ambiente di creazione fallisce.
  • Usa variable_watch () per vedere dove le tue variabili sono lette / scritte / rimosse.
  • Cerca nelle proprietà della directory CACHE_VARIABLES e VARIABLES
  • Chiama cmake --trace ... per vedere il processo di analisi completo di CMake. Questa è una sorta di ultima riserva, perché genera molto output.

Sintassi speciale

  • variabili ambientali
    • È ansible leggere le variabili d’ambiente $ENV{...} e scrivere set(ENV{...} ...)
  • Espressioni del generatore
    • Le espressioni del generatore $<...> vengono valutate solo quando il generatore di CMake scrive l’ambiente di creazione (confrontato con le variabili normali che vengono sostituite “dal posto” dal parser)
    • Molto utile, ad esempio, nelle righe di comando del compilatore / linker e negli ambienti multi-configurazione
  • Riferimenti
    • Con ${${...}} puoi dare nomi variabili in una variabile e fare riferimento al suo contenuto.
    • Spesso usato quando si assegna un nome di variabile come parametro funzione / macro.
  • Valori costanti (vedi comando if() )
    • Con if(MyVariable) puoi controllare direttamente una variabile per vero / falso (non è necessario qui per l’allegato ${...} )
    • Vero se la costante è 1 , ON , YES , TRUE , Y o un numero diverso da zero.
    • False se la costante è 0 , OFF , NO , FALSE , N , IGNORE , NOTFOUND , la stringa vuota o termina nel suffisso -NOTFOUND .
    • Questa syntax viene spesso utilizzata per qualcosa come if (MSVC) , ma può essere fonte di confusione per qualcuno che non conosce questa scorciatoia di syntax.
  • Sostituzioni ricorsive
    • È ansible build nomi di variabili usando variabili. Dopo che CMake ha sostituito le variabili, controllerà di nuovo se il risultato è una variabile stessa. Questa è una funzionalità molto potente usata nello stesso CMake, ad esempio come una sorta di set(CMAKE_${lang}_COMPILER ...) template set(CMAKE_${lang}_COMPILER ...)
    • Ma sappi che questo ti può dare mal di testa nei comandi if () . Ecco un esempio in cui CMAKE_CXX_COMPILER_ID è "MSVC" e MSVC è "1" :
      • if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") è true, perché valuta if ("1" STREQUAL "1")
      • if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") è false, perché if ("MSVC" STREQUAL "1")
      • Quindi la soluzione migliore qui sarebbe – vedi sopra – per controllare direttamente if (MSVC)
    • La buona notizia è che questo è stato risolto in CMake 3.1 con l’introduzione della politica CMP0054 . Consiglierei di impostare sempre cmake_policy(SET CMP0054 NEW) su “interpretare solo if() argomenti come variabili o parole chiave quando non quotate”.
  • Il comando option()
    • Principalmente solo le stringhe memorizzate nella cache che possono essere solo ON o OFF e che consentono un trattamento speciale come ad esempio le dipendenze
    • Ma attenzione , non confondere l’ option con il comando set . Il valore assegnato option è in realtà solo il “valore iniziale” (trasferito una volta alla cache durante la prima fase di configurazione) e successivamente è destinato a essere modificato dall’utente tramite la GUI di CMake .

Riferimenti

  • Come viene utilizzato CMake?
  • cmake, perso nel concetto di variabili globali (e alternative PARENT_SCOPE o add_subdirectory)
  • In loop su una lista di stringhe
  • Come memorizzare le impostazioni di generazione di CMake
  • CMake confronta con una stringa vuota con STREQUAL non riuscito
  • cmake: quando quotare le variabili?

Ecco alcuni esempi di base per iniziare in modo rapido e sporco.

Una variabile articolo

Imposta variabile:

 SET(INSTALL_ETC_DIR "etc") 

Usa variabile:

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

Variabile a più voci (ad esempio elenco)

Imposta variabile:

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

Usa variabile:

 add_executable(program "${PROGRAM_SRCS}") 

CMake docs su variabili

$ENV{FOO} per l’utilizzo, dove FOO viene prelevato dalla variabile di ambiente. altrimenti usa ${FOO} , dove FOO è un’altra variabile. Per l’impostazione, SET(FOO "foo") verrebbe utilizzato in cmake.