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

168

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

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

CivFan
fuente

Respuestas:

281

Al escribir scripts de CMake, hay mucho que necesita saber sobre la sintaxis y cómo usar variables en CMake.

La sintaxis

Cadenas usando set():

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

O con string():

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

Listas que usan 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 archivo:

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

La documentación

El alcance o "¿Qué valor tiene mi variable?"

Primero están las "Variables normales" y las cosas que necesita saber sobre su alcance:

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

En segundo lugar está el "Caché de variables globales". Cosas que debes saber sobre el caché:

  • Si no se define una variable normal con el nombre dado en el alcance actual, CMake buscará una entrada de caché coincidente.
  • Los valores de caché se almacenan en el CMakeCache.txtarchivo en su directorio de salida binario.
  • Los valores en la 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 a typey a docstring. Normalmente no uso la GUI, así que la uso set(... CACHE INTERNAL "")para establecer mis valores globales y persistentes.

    Tenga en cuenta que el INTERNALtipo de variable de caché implicaFORCE

  • En un script CMake, solo puede cambiar las entradas de caché existentes si usa la set(... CACHE ... FORCE)sintaxis. Este comportamiento es utilizado, por ejemplo, por el propio CMake, porque normalmente no fuerza las entradas de caché y, por lo tanto, puede predefinirlo con otro valor.

  • Puede usar la línea de comando para establecer entradas en la caché con la sintaxis cmake -D var:type=value, solo cmake -D var=valueo con cmake -C CMakeInitialCache.cmake.
  • Puede desarmar entradas en la caché con unset(... CACHE).

La memoria caché es global y puede configurarlos prácticamente en cualquier lugar en sus scripts CMake. Pero le recomendaría que lo piense dos veces antes de utilizar las variables de caché (son globales y persistentes). Normalmente prefiero la sintaxis set_property(GLOBAL PROPERTY ...)y set_property(GLOBAL APPEND PROPERTY ...)para definir mis propias variables globales no persistentes.

Errores variables y "¿Cómo depurar los cambios variables?"

Para evitar dificultades, debe saber lo siguiente acerca de las variables:

  • Las variables locales ocultan las variables en caché si ambas tienen el mismo nombre
  • Los find_...comandos, si tienen éxito, escriben sus resultados como variables 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 a b c)es "a;b;c"y set(MyVar "a b c")es"a b c"
    • La recomendación es que siempre use comillas con la única excepción cuando desee dar una lista como lista
    • Generalmente prefiero el list()comando para manejar listas
  • Todo el tema del alcance descrito anteriormente. Especialmente se recomienda su uso en functions()lugar de macros()porque no desea que sus variables locales se muestren en el ámbito primario.
  • Muchas variables utilizadas por CMake se configuran con las llamadas project()y enable_language(). Por lo tanto, podría ser importante establecer algunas variables antes de usar esos comandos.
  • Las variables de entorno pueden diferir de dónde CMake generó el entorno de creación y cuándo se utilizan los archivos de creación.
    • Un cambio en una variable de entorno no reactiva el proceso de generación.
    • Especialmente un entorno IDE generado puede diferir de su línea de comando, por lo que se recomienda transferir sus variables de entorno a algo que se almacena en caché.

A veces solo ayuda la depuración de variables. Lo siguiente puede ayudarlo:

  • Simplemente use el viejo printfestilo de depuración usando el message()comando. También hay algunos módulos listos para usar que se envían con CMake: CMakePrintHelpers.cmake , CMakePrintSystemInformation.cmake
  • Busque en el CMakeCache.txtarchivo en su directorio de salida binario. Este archivo incluso se genera si falla la generación real de su entorno de creación.
  • Use variable_watch () para ver dónde se leen / escriben / eliminan sus variables.
  • Busque en las propiedades del directorio CACHE_VARIABLES y VARIABLES
  • Llame cmake --trace ...para ver el proceso de análisis completo de CMake. Esa es una especie de última reserva, ya que genera mucha salida.

Sintaxis Especial

  • Variables de entorno
    • Puedes leer $ENV{...}y escribir set(ENV{...} ...)variables de entorno
  • Expresiones de generador
    • Las expresiones de generador $<...>solo se evalúan cuando el generador de CMake escribe el entorno de creación (en comparación con las variables normales que se reemplazan "en el lugar" por el analizador)
    • Muy útil, por ejemplo, en las líneas de comando del compilador / enlazador y en entornos de configuración múltiple
  • Referencias
    • Con ${${...}}usted puede dar nombres de variables en una variable y hacer referencia a su contenido.
    • A menudo se usa al dar un nombre de variable como parámetro de función / macro.
  • Valores constantes (ver if()comando)
    • Con if(MyVariable)usted puede verificar directamente una variable para verdadero / falso (no es necesario aquí para el cerramiento ${...})
    • Verdadero si la constante es 1, ON, YES, TRUE, Y, o un número distinto de cero.
    • Falso si la constante es 0, OFF, NO, FALSE, N, IGNORE, NOTFOUND, la cadena vacía, o termina en el sufijo -NOTFOUND.
    • Esta sintaxis a menudo se usa para algo así if(MSVC), pero puede ser confusa para alguien que no conoce este atajo de sintaxis.
  • Sustituciones recursivas
    • Puede construir nombres de variables usando variables. Después de que CMake haya sustituido las variables, verificará nuevamente si el resultado es una variable en sí misma. Esta es una característica muy poderosa utilizada en CMake, por ejemplo, como una plantillaset(CMAKE_${lang}_COMPILER ...)
    • Pero tenga en cuenta que esto puede darle dolor de cabeza en los if()comandos. Aquí hay un ejemplo donde CMAKE_CXX_COMPILER_IDes "MSVC"y MSVCes "1":
      • if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") es cierto, porque se evalúa a if("1" STREQUAL "1")
      • if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") es falso porque se evalúa como if("MSVC" STREQUAL "1")
      • Entonces, la mejor solución aquí sería, ver 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 siempre establecer cmake_policy(SET CMP0054 NEW)"solo interpretar if()argumentos como variables o palabras clave cuando no se citan".
  • El option()comando
    • Principalmente solo cadenas en caché que solo pueden ser ONo OFFy permiten un manejo especial como, por ejemplo, dependencias
    • Pero tenga en cuenta , no confunda el optioncon el setcomando. El valor dado a optiones realmente solo el "valor inicial" (transferido una vez al caché durante el primer paso de configuración) y luego debe ser modificado por el usuario a través de la GUI de CMake .

Referencias

Florian
fuente
Cuando utilizo if ("${MyString}" ...)estoy viendo advertencias: Policy CMP0054 is not set: Only interpret if() arguments as variables or keywords when unquoted. Ver, por ejemplo, Build 367 . ¿Algunas ideas?
jww
Y sin comillas ${MyString}conduce a un montón de errores para "si se dan argumentos ..." como el error CMake cerca de if: "si se dan argumentos" seguido de paréntesis, "NOT", "IGUAL" y similares .
jww
@jww La advertencia significa que MyStringcontiene un nombre de variable que luego será nuevamente desreferenciado . Creo que nadie realmente quiere ese OLDcomportamiento. Así que desde mi punto de vista es totalmente seguro y compatible con la política acaba de establecer CMP0054a NEW(véase la discusión aquí ).
Florian
@jww Y la forma más segura de evitar esos problemas / advertencias es simplemente hacerlo if (MyString ...)(si su código le da la advertencia).
Florian
Gracias. Eliminamos todas las ocurrencias ${MyString}y las reemplazamos con MyString(o creo que las eliminamos todas). Todavía no hay alegría: Build 372 . La basura ni siquiera proviene de nuestro código. Parece provenir de CMake. La línea 283 es if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC").
jww
18

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

Una variable de elemento

Establecer variable:

SET(INSTALL_ETC_DIR "etc")

Usar variable:

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

Variable de elementos múltiples (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 documentos sobre variables

CivFan
fuente
1

$ENV{FOO}para uso, donde FOOse recoge de la variable de entorno. de lo contrario, use como ${FOO}, donde FOOhay alguna otra variable. Para la configuración, SET(FOO "foo")se utilizaría en CMake.

parasitar
fuente