Guía completa de CMake. Primera parte: sintaxis


Introduccion


CMake es un conjunto de utilidades abierto y multiplataforma diseñado para automatizar las pruebas, la compilación y la creación de paquetes de proyectos en C / C ++. Al escribir una vez un pequeño script que todos entiendan, asegurará la misma compilación de su proyecto en cualquier plataforma donde CMake esté disponible.


El lenguaje CMake , traducido a un archivo de ensamblaje nativo (por ejemplo, Makefile o Ninja), define el proceso de toda la gestión de proyectos. A su disposición, en el lado funcional, solo hay equipos que se pueden formar en estructuras bastante complejas. Comenzaremos con ellos.


Lanzamiento de CMake


Los siguientes son ejemplos del uso del lenguaje CMake que debe practicar. Experimente con el código fuente modificando los comandos existentes y agregando otros nuevos. Para ejecutar estos ejemplos, instale CMake desde el sitio web oficial .


Equipos


Los comandos en CMake son similares a las funciones en muchos lenguajes de programación. Para llamar a un comando, debe escribir su nombre y luego pasarle los argumentos entre paréntesis, separados por espacios. En el ejemplo anterior, se pasan seis argumentos al comando de message para enviarlos a la consola:


 #    "CMake is the most powerful buildsystem!" message("CMake " "is " "the " "most " "powerful " "buildsystem!") 

Argumentos


Los argumentos enmarcados entre comillas dobles le permiten escapar y sustituir variables dentro de usted. Los argumentos sin marco no permiten la producción de tales cosas y no pueden incluir los caracteres ()#"\ y espacios, pero son más convenientes de usar. Ejemplo:


 #  "Hello, my lovely CMake",    "!": message("Hello, my lovely CMake\t!") #  "Hello,_my_lovely_CMake!"  : message(Hello,_my_lovely_CMake!) 

Vale la pena señalar que el argumento Walk;around;the;forest se expandirá a la lista Walk around the forest , ya que cualquier argumento sin marco se expande automáticamente a una lista de valores (siempre que los valores del argumento original estén separados por punto y coma), pero con un doble las comillas como argumento no se produce dicha transformación (los caracteres de punto y coma simplemente desaparecen). Esta característica fue mencionada en los comentarios.


Comentarios


Los comentarios comienzan con un signo de libra y terminan al final de la línea donde se imprimieron. El sistema de compilación ignora el texto contenido en los comentarios y no tiene ningún efecto en su funcionamiento. Los ejemplos anteriores también demuestran el uso de comentarios.


Variables


Las variables se pueden definir llamando al comando set y eliminando llamando a unset . Puede obtener el valor de una variable construyendo ${VARIABLE} . Si la variable aún no se ha definido y en algún lugar fue necesario obtener su valor, entonces esta variable se convertirá en una cadena vacía. Un ejemplo:


 #   VARIABLE   "Mr. Thomas": set(VARIABLE "Mr. Thomas") #  "His name is: Mr. Thomas": message("His name is: " ${VARIABLE}) #  "'BINGO' is equal to: []",   "BINGO"  : message("'BINGO' is equal to: [${BINGO}]") #   VARIABLE: unset(VARIABLE) 

Opciones


CMake admite opciones de configuración que están sujetas a modificaciones por parte del usuario. Las opciones son similares a las variables y se establecen mediante el comando de option , que toma solo tres argumentos: el nombre de la variable, la descripción de la cadena de la variable y el valor predeterminado de la variable ( ON u OFF ):


 #   `USE_ANOTHER_LIBRARY`   # "Do you want to use an another library?"   "OFF": option(USE_ANOTHER_LIBRARY "Do you want to use an another library?" OFF) 

Expresiones lógicas


Antes de continuar con el estudio de operadores condicionales y construcciones cíclicas, es necesario comprender el trabajo de las expresiones lógicas. Las expresiones lógicas se utilizan al verificar condiciones y pueden tomar uno de dos valores: verdadero o falso. Por ejemplo, la expresión 52 LESS 58 será verdadera, ya que 52 <58. La expresión 88 EQUAL 88 será verdadera, 63 GREATER 104 será falsa. Puede comparar no solo números, sino también cadenas, versiones, archivos, pertenencia a listas y expresiones regulares. Una lista completa de expresiones lógicas se puede encontrar aquí .


Declaraciones condicionales


Los operadores condicionales en CMake funcionan exactamente como en otros lenguajes de programación. En este ejemplo, solo funcionará el primer operador condicional, que verifica que 5> 1. Las condiciones segunda y tercera son falsas, ya que 5 no puede ser menor o igual a uno. Los bloques de comando elseif y else son opcionales, y endif obligatorio y señala la finalización de las comprobaciones anteriores.


 #  "Of course, 5 > 1!": if(5 GREATER 1) message("Of course, 5 > 1!") elseif(5 LESS 1) message("Oh no, 5 < 1!") else() message("Oh my god, 5 == 1!") endif() 

Ciclos


Los bucles en CMake son similares a los bucles en otros lenguajes de programación. En el ejemplo anterior, el valor de la variable VARIABLE se establece en Airport , y luego se ejecutan cuatro comandos anidados secuencialmente hasta que el valor de la variable VARIABLE sea ​​igual a Airport . El último cuarto comando de set(VARIABLE "Police station") establece el valor de la variable marcada en la Police station , por lo que el ciclo se detendrá inmediatamente antes de llegar a la segunda iteración. El comando endwhile señala la finalización de la lista de comandos anidados en el bucle.


 #      "VARIABLE is still 'Airport'": set(VARIABLE Airport) while(${VARIABLE} STREQUAL Airport) message("VARIABLE is still '${VARIABLE}'") message("VARIABLE is still '${VARIABLE}'") message("VARIABLE is still '${VARIABLE}'") set(VARIABLE "Police station") endwhile() 

Este ejemplo de foreach funciona de la siguiente manera: en cada iteración de este bucle, a la variable VARIABLE se le asigna el siguiente valor de la lista Give me the sugar please! y luego se ejecuta el comando de message(${VARIABLE}) , que muestra el valor actual de la variable VARIABLE . Cuando no hay valores en la lista, el ciclo finaliza su ejecución. El comando endforeach señala la finalización de la lista de comandos anidados en el bucle.


 #  "Give me the sugar please!"   : foreach(VARIABLE Give me the sugar please!) message(${VARIABLE}) endforeach() 

Hay 3 formas más de escribir un foreach . El primer ciclo en este ejemplo genera números enteros de 0 a 10 en lugar de la lista, el segundo ciclo genera en el rango de 3 a 15, y el tercer ciclo funciona en el segmento de 50 a 90, pero con un paso de 10.


 #  "0 1 2 3 4 5 6 7 8 9 10"   : foreach(VARIABLE RANGE 10) message(${VARIABLE}) endforeach() #  "3 4 5 6 7 8 9 10 11 12 13 14 15"   : foreach(VARIABLE RANGE 3 15) message(${VARIABLE}) endforeach() #  "50 60 70 80 90"   : foreach(VARIABLE RANGE 50 90 10) message(${VARIABLE}) endforeach() 

Funciones y macros


La sintaxis de CMake le permite definir sus propios comandos que se pueden llamar exactamente como integrados. El siguiente ejemplo demuestra el uso de funciones y macros: primero, una función y una macro se definen con sus propios comandos, y cuando se les llama, sus comandos se ejecutan secuencialmente.


 #   "print_numbers": function(print_numbers NUM1 NUM2 NUM3) message(${NUM1} " " ${NUM2} " " ${NUM3}) endfunction() #   "print_words": macro(print_words WORD1 WORD2 WORD3) message(${WORD1} " " ${WORD2} " " ${WORD3}) endmacro() #   "print_numbers",   "12 89 225": print_numbers(12 89 225) #   "print_words",   "Hey Hello Goodbye": print_words(Hey Hello Goodbye) 

El comando de function toma el nombre de la función futura como primer argumento, y el resto de los argumentos son los nombres de los parámetros que pueden usarse como variables normales. Los parámetros son visibles solo para la función que se está definiendo, lo que significa que no podemos acceder a sus parámetros fuera de la función. Además, todas las demás variables definidas y redefinidas dentro de la función son visibles solo para sí mismas.


Las macros son similares a las funciones con la excepción de que no tienen su propio alcance: todas las variables dentro de las macros se consideran globales. Puede leer más sobre las diferencias entre macros y funciones aquí .


Como se señaló en los comentarios, las macros en CMake son similares a las macros en el preprocesador C: si coloca el comando return en el cuerpo de la macro, saldrá de la función de llamada (o del script completo), como lo demuestra este ejemplo:


 #  ,   : macro(demonstrate_macro) return() endmacro() #  ,   : function(demonstrate_func) demonstrate_macro() message("The function was invoked!") endfunction() #  "Something happened with the function!" demonstrate_func() message("Something happened with the function!") 

En el ejemplo anterior, la función demonstrate_func no tendrá tiempo para imprimir el mensaje ¡ The function was invoked! , como antes, se sustituirá el lugar de invocación de la macro demonstrate_macro y se ejecutará el comando de salida.


Analizando argumentos


Como se señaló en los comentarios, el poderoso mecanismo cmake_parse_arguments permite analizar argumentos pasados ​​a una función o macro.


Este comando acepta el prefijo utilizado en la definición de variables (consulte el siguiente párrafo), una lista de opciones utilizadas sin valores posteriores, una lista de palabras clave seguidas de un solo valor, una lista de palabras clave seguidas de conjuntos de valores y una lista de todos los valores pasados ​​a la función o macro


El trabajo del mecanismo de análisis de argumentos es convertir los argumentos recibidos en valores variables. Por lo tanto, el comando considerado para cada opción y palabra clave define su propia variable de la forma <Prefix>_<OptionOrKeyword> , que encapsula un cierto valor. Para las opciones, estos son valores booleanos (verdadero: se indica la opción; de lo contrario, falso), y para las palabras clave, todos los valores transferidos ubicados después de ellos.


La función custom_function contiene una llamada a cmake_parse_arguments y luego imprime los valores de ciertas variables. A continuación, se llama a la función con los argumentos LOW NUMBER 30 COLORS red green blue , después de lo cual se imprime en la pantalla:


 function(custom_function) #       : cmake_parse_arguments(CUSTOM_FUNCTION "LOW;HIGH" "NUMBER" "COLORS" ${ARGV}) #  "'LOW' = [TRUE]": message("'LOW' = [${CUSTOM_FUNCTION_LOW}]") # "'HIGH' = [FALSE]": message("'HIGH' = [${CUSTOM_FUNCTION_HIGH}]") #  "'NUMBER' = [30]": message("'NUMBER' = [${CUSTOM_FUNCTION_NUMBER}]") #  "'COLORS' = [red;green;blue]": message("'COLORS' = [${CUSTOM_FUNCTION_COLORS}]") endfunction() #   "custom_function"   : custom_function(LOW NUMBER 30 COLORS red green blue) 

Alcances


En la sección anterior, aprendió que algunas construcciones en CMake pueden definir su propio alcance. De hecho, todas las variables se consideran globales de forma predeterminada (el acceso a ellas está en todas partes), con la excepción de aquellas que se han definido y redefinido en funciones. También hay variables de caché que tienen su propio alcance, pero no se usan con tanta frecuencia.


Como se menciona en los comentarios, las variables se pueden definir en el ámbito "principal" utilizando el comando set(VARIABLE ... PARENT_SCOPE) . Este ejemplo demuestra esta característica:


 # ,   "VARIABLE"   # "In the parent scope..."    : function(demonstrate_variable) set(VARIABLE "In the parent scope..." PARENT_SCOPE) endfunction() #   "VARIABLE"    : demonstrate_variable() #      "VARIABLE" : message("'VARIABLE' is equal to: ${VARIABLE}") 

Si PARENT_SCOPE elimina de la definición de la variable VARIABLE , entonces la variable será accesible solo para la función demonstrate_variable , y en el ámbito global tomará un valor vacío.


Conclusión


Esto concluye la sintaxis de CMake. El próximo artículo se lanzará en unos días e introducirá el uso del sistema de compilación CMake. Hasta pronto!

Source: https://habr.com/ru/post/es431428/


All Articles