¿Cuál es la syntax de CMake para establecer y usar variables?

Pregunto esto como un recordatorio para mí la próxima vez que use CMake. Nunca se pega, y los resultados de Google no son geniales.

¿Cuál es la syntax para establecer y usar variables en CMake?

Al escribir scripts CMake, hay mucho que debe saber sobre la syntax y cómo usar variables en CMake.

La syntax

Cadenas que usan set() :

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

O con string() :

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

Listas usando set() :

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

O mejor con list() :

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

Listas de nombres de archivos:

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

La documentación

  • CMake / Language Sintax
  • CMake: Variables lista cadenas
  • CMake: variables útiles
  • Comando CMake set()
  • Comando CMake string()
  • Comando CMake list()
  • Cmake: Expresiones del generador

El scope o “¿Qué valor tiene mi variable?”

Primero están las “Variables normales” y las cosas que necesita saber sobre su scope:

  • Las variables normales son visibles para CMakeLists.txt que están establecidas y todo lo que se llama desde allí ( add_subdirectory() , include() , macro() y function() ).
  • Los add_subdirectory() y function() son especiales porque abren su propio scope.
    • Las variables de significado set(...) solo están visibles allí y hacen una copia de todas las variables normales del nivel de scope desde el que son llamadas (llamado ámbito principal).
    • Entonces, si está en un subdirectorio o una función, puede modificar una variable ya existente en el ámbito principal con set(... PARENT_SCOPE)
    • Puede hacer uso de esto, por ejemplo, en funciones pasando el nombre de la variable como un parámetro de función. Un ejemplo sería la function(xyz _resultVar) está configurando set(${_resultVar} 1 PARENT_SCOPE)
  • Por otro lado, todo lo que establezca en los scripts include() o macro() modificará las variables directamente en el ámbito de donde se invocan.

En segundo lugar, está el “Caché Global de Variables”. Cosas que debes saber sobre el caché:

  • Si no se define una variable normal con el nombre dado en el scope actual, CMake buscará una entrada de caché coincidente.
  • Los valores de caché se almacenan en el archivo CMakeCache.txt en su directorio de salida binario.
  • Los valores en la memoria caché se pueden modificar en la aplicación GUI de CMake antes de que se generen. Por lo tanto, en comparación con las variables normales, tienen un type y una docstring . Normalmente no uso la GUI, así que utilizo set(... CACHE INTERNAL "") para establecer mis valores globales y persistentes.

    Tenga en cuenta que el tipo de variable de caché INTERNAL implica FORCE

  • En una secuencia de comandos de CMake, solo puede cambiar las entradas de la memoria caché existentes si utiliza la syntax set(... CACHE ... FORCE) . Este comportamiento se utiliza, por ejemplo, por CMake, ya que normalmente no obliga a las entradas de caché y, por lo tanto, puede predefinirlo con otro valor.

  • Puede usar la línea de comando para establecer entradas en el Caché con la syntax cmake -D var:type=value , simplemente cmake -D var=value o con cmake -C CMakeInitialCache.cmake .
  • Puede deshacer las entradas en la memoria caché con unset(... CACHE) .

La memoria caché es global y puede establecerlos prácticamente en cualquier lugar en sus secuencias de comandos CMake. Pero recomendaría que lo piense dos veces sobre dónde usar las variables de caché (son globales y son persistentes). Normalmente prefiero la set_property(GLOBAL PROPERTY ...) y set_property(GLOBAL APPEND PROPERTY ...) para definir mis propias variables globales no persistentes.

Errores variables y “Cómo depurar cambios de variables”

Para evitar riesgos, debe saber lo siguiente sobre las variables:

  • Las variables locales ocultan las variables almacenadas en caché si ambas tienen el mismo nombre
  • Los comandos find_... – si tienen éxito – escriben sus resultados como variables almacenadas en caché “para que ninguna llamada vuelva a buscar”
  • Las listas en CMake son solo cadenas con delimitadores de punto y coma y, por lo tanto, las comillas son importantes
    • set(MyVar abc) es "a;b;c" y set(MyVar "abc") es "abc"
    • La recomendación es que siempre use comillas con la única excepción cuando quiera dar una lista como lista
    • Generalmente prefiere el comando list() para manejar listas
  • El problema del scope completo descrito anteriormente. Especialmente se recomienda utilizar functions() lugar de macros() porque no desea que sus variables locales se muestren en el ámbito principal.
  • Muchas de las variables utilizadas por CMake se establecen con las llamadas project() y enable_language() . Por lo tanto, podría ser importante establecer algunas variables antes de que se usen esos comandos.
  • Las variables de entorno pueden diferir de donde CMake generó el entorno make y cuándo se ponen en uso los archivos make.
    • Un cambio en una variable de entorno no vuelve a activar el proceso de generación.
    • Especialmente un entorno IDE generado puede diferir de su línea de comandos, por lo que se recomienda transferir sus variables de entorno a algo que se almacena en caché.

Algunas veces, solo las variables de depuración ayudan. Lo siguiente puede ayudarte:

  • Simplemente use el viejo estilo de depuración de printf utilizando el comando message() . También hay algunos módulos listos para usar enviados con CMake: CMakePrintHelpers.cmake , CMakePrintSystemInformation.cmake
  • Mire en el archivo CMakeCache.txt en su directorio de salida binario. Este archivo se genera incluso si falla la generación real de su entorno make.
  • Use variable_watch () para ver dónde se leen / escriben / eliminan sus variables.
  • Mire en las propiedades del directorio CACHE_VARIABLES y VARIABLES
  • Llame a cmake --trace ... para ver el proceso de análisis completo de CMake. Esa es la última reserva, porque genera una gran cantidad de resultados.

Sintaxis especial

  • Variables de entorno
    • Puede leer las variables de entorno $ENV{...} y escribir set(ENV{...} ...)
  • Expresiones del generador
    • Las expresiones de generador $< ...> solo se evalúan cuando el generador de CMake escribe el entorno make (es una comparación con las variables normales que el analizador reemplaza “in situ”)
    • Muy útil, por ejemplo, en las líneas de comando de comstackdor / enlazador y en entornos de multi-configuración
  • Referencias
    • Con ${${...}} puede dar nombres de variables en una variable y hacer referencia a su contenido.
    • A menudo se usa cuando se da un nombre de variable como parámetro función / macro.
  • Valores constantes (consulte el comando if() )
    • Con if(MyVariable) puede verificar directamente una variable para verdadero / falso (no es necesario aquí para el ${...} adjunto)
    • Verdadero si la constante es 1 , ON , YES , TRUE , Y o un número distinto de cero.
    • False si la constante es 0 , OFF , NO , FALSE , N , IGNORE , NOTFOUND , la cadena vacía, o termina en el sufijo -NOTFOUND .
    • Esta syntax a menudo se usa para algo como if (MSVC) , pero puede ser confuso para alguien que no conoce este atajo de syntax.
  • Sustituciones recursivas
    • Puede construir nombres de variables usando variables. Después de que CMake haya sustituido las variables, volverá a verificar si el resultado es una variable en sí misma. Esta es una característica muy poderosa utilizada en CMake mismo, por ejemplo, como una especie de set(CMAKE_${lang}_COMPILER ...) plantillas set(CMAKE_${lang}_COMPILER ...)
    • Pero ten en cuenta que esto puede darte un dolor de cabeza en los comandos if () . Aquí hay un ejemplo donde CMAKE_CXX_COMPILER_ID es "MSVC" y MSVC es "1" :
      • if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") es verdadero, porque se evalúa como if ("1" STREQUAL "1")
      • if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") es falso, porque evalúa if ("MSVC" STREQUAL "1")
      • Entonces, la mejor solución sería, ver más arriba, verificar directamente if (MSVC)
    • La buena noticia es que esto se solucionó en CMake 3.1 con la introducción de la política CMP0054 . Recomendaría establecer siempre cmake_policy(SET CMP0054 NEW) para “interpretar únicamente los argumentos if() como variables o palabras clave cuando no se incluyen las comillas”.
  • El comando de option()
    • Principalmente cadenas simplemente en caché que solo pueden estar ON o OFF y que permiten un manejo especial como, por ejemplo, dependencias
    • Pero ten en cuenta que no confundas la option con el comando set . El valor otorgado a la option es realmente solo el “valor inicial” (transferido una vez a la memoria caché durante el primer paso de configuración) y luego el usuario debe cambiarlo a través de la GUI de CMake .

Referencias

  • ¿Cómo se usa CMake?
  • cmake, perdido en el concepto de variables globales (y PARENT_SCOPE o add_subdirectory alternatives)
  • Bucle sobre una lista de cuerdas
  • Cómo almacenar la configuración de comstackción de CMake
  • CMake se compara con la cadena vacía con STREQUAL failed
  • cmake: cuando citar variables?

Aquí hay un par de ejemplos básicos para comenzar rápido y sucio.

Una variable de artículo

Establecer variable:

 SET(INSTALL_ETC_DIR "etc") 

Usar variable:

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

Variable de varios elementos (es decir, lista)

Establecer variable:

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

Usar variable:

 add_executable(program "${PROGRAM_SRCS}") 

CMake documenta las variables

$ENV{FOO} para el uso, donde FOO se recoge de la variable de entorno. de lo contrario, use como ${FOO} , donde FOO es alguna otra variable. Para el ajuste, SET(FOO "foo") se usará en cmake.