Tutorial de nivel de niñera de CMake (Parte 2)

Reimpresión

Autor: Su Bingqu

Enlace: https://subingwen.cn/cmake/CMake-advanced/

Fuente: DaBing de iProgramming. Los derechos de autor pertenecen al autor. Para reimpresiones comerciales, comuníquese con el autor para obtener autorización. Para reimpresiones no comerciales, indique la fuente.


ColorMarkDown

Insertar descripción de la imagen aquí

Gitee: Tutorial de nivel de niñera de CMake: Big C que ama la programación


1. CMake anidado

Si el proyecto es muy grande, o hay muchos directorios de código fuente en el proyecto, si solo se usa uno al administrar el proyecto a través de CMake CMakeLists.txt, entonces este archivo será relativamente complicado. Una forma de simplificarlo es agregarlo a cada directorio de código fuente. Un CMakeLists.txtarchivo (no es necesario el directorio de encabezado) para que cada archivo sea menos complejo, más flexible y más fácil de mantener.

Primero echemos un vistazo a la estructura del directorio a continuación:

$ tree
.
├── build
├── calc
│   ├── add.cpp
│   ├── CMakeLists.txt
│   ├── div.cpp
│   ├── mult.cpp
│   └── sub.cpp
├── CMakeLists.txt
├── include
│   ├── calc.h
│   └── sort.h
├── sort
│   ├── CMakeLists.txt
│   ├── insert.cpp
│   └── select.cpp
├── test1
│   ├── calc.cpp
│   └── CMakeLists.txt
└── test2
    ├── CMakeLists.txt
    └── sort.cpp

6 directories, 15 files
  • include 目录: Directorio de archivos de encabezado
  • calc 目录: Los algoritmos de suma, resta, multiplicación y división correspondientes a los cuatro archivos fuente en el directorio
    • El archivo de encabezado correspondiente está includeencalc.h
  • sort 目录: Los dos archivos fuente en el directorio corresponden a los algoritmos de clasificación por inserción y clasificación por selección.
    • El archivo de encabezado correspondiente está includeensort.h
  • test1 目录: Directorio de prueba para probar algoritmos de suma, resta, multiplicación y división
  • test2 目录: Directorio de prueba para probar el algoritmo de clasificación.

Puede ver que CMakeLists.txtahora se han agregado los archivos necesarios para cada directorio de archivos fuente. A continuación, analizaremos el contenido que es necesario agregar en cada archivo.

1.1 Preparación

1.1.1 Relación de nodo

Como todos sabemos, el directorio de Linux es una estructura de árbol, por lo que 嵌套的 CMake 也是一个树状结构,最顶层的 CMakeLists.txt 是根节点,其次都是子节点。necesitamos conocer cierta CMakeLists.txtinformación sobre el alcance de las variables del archivo:

  • CMakeLists.txtLas variables en el nodo raíz son válidas globalmente.

  • Las variables de los nodos principales CMakeLists.txtse pueden utilizar en los nodos secundarios.

  • Las variables en los nodos secundarios CMakeLists.txtsolo se pueden usar en el nodo actual

1.1.2 Agregar subdirectorio

A continuación, también necesitamos saber cómo se establece la relación entre los nodos padre e hijo en CMake. Aquí necesitamos usar un comando de CMake:

add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
  • source_dir: Especifica CMakeLists.txtla ubicación de los archivos fuente y de código, lo que en realidad significa especificar subdirectorios.
  • binary_dir: Especifica la ruta del archivo de salida. Generalmente, no es necesario especificarla y se puede ignorar.
  • EXCLUDE_FROM_ALL: Los destinos en la ruta secundaria no se incluirán en ALLel destino de la ruta principal de forma predeterminada y también se excluirán del archivo del proyecto IDE. Los usuarios deben crear objetivos explícitamente en subrutas.

De esta forma, CMakeLists.txtse construye la relación padre-hijo entre archivos.

1.2 Resolución de problemas

En el directorio anterior haremos lo siguiente:

  1. test1 目录Realice pruebas relacionadas con la calculadora a través de los archivos de prueba en
  2. test2 目录Realice pruebas relacionadas con la clasificación a través de archivos de prueba en

Ahora es equivalente a las pruebas modulares: para los archivos fuente en el directorio y, se pueden compilar en archivos de biblioteca (ya sea bibliotecas estáticas o bibliotecas dinámicas) y luego proporcionarlos a los archivos de prueba para su uso calc. sortLa esencia del archivo de la biblioteca es en realidad código, pero ha cambiado del formato de texto al formato binario.

1.2.1 Directorio raíz

El contenido de los archivos en el directorio raíz CMakeLists.txtes el siguiente:

cmake_minimum_required(VERSION 3.0)
project(test)
# 定义变量
# 静态库生成的路径
set(LIB_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib)
# 测试程序生成的路径
set(EXEC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)
# 头文件目录
set(HEAD_PATH ${CMAKE_CURRENT_SOURCE_DIR}/include)
# 静态库的名字
set(CALC_LIB calc)
set(SORT_LIB sort)
# 可执行程序的名字
set(APP_NAME_1 test1)
set(APP_NAME_2 test2)
# 添加子目录
add_subdirectory(calc)
add_subdirectory(sort)
add_subdirectory(test1)
add_subdirectory(test2)

Se hacen dos cosas principales en el archivo correspondiente al nodo raíz: 定义全局变量y 添加子目录.

  • Las variables globales definidas son utilizadas principalmente por los subnodos para mejorar CMakeLists.txtla legibilidad y el mantenimiento de los archivos en los subnodos, evitar la redundancia y reducir la probabilidad de viajes de negocios.

  • Se agregan un total de cuatro subdirectorios, cada uno con un archivo CMakeLists.txt, para determinar su relación padre-hijo.

1.2.2 directorio de cálculo

El contenido de los archivos en el directorio calc CMakeLists.txtes el siguiente:

cmake_minimum_required(VERSION 3.0)
project(CALCLIB)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})
set(LIBRARY_OUTPUT_PATH ${LIB_PATH})
add_library(${CALC_LIB} STATIC ${SRC})
  • Línea 3 aux_source_directory: busca todos los archivos fuente en el directorio actual (directorio calc)
  • Línea 4 include_directories: contiene la ruta del archivo de encabezado, HEAD_PATHque se define en el archivo del nodo raíz.
  • Línea 5 set: establece la ruta generada de la biblioteca, LIB_PATHque se define en el archivo del nodo raíz
  • Línea 6 add_library: Generar una biblioteca estática El nombre de la biblioteca estática CALC_LIBse define en el archivo del nodo raíz.

1.2.3 ordenar directorio

El contenido de los archivos en el directorio de clasificación CMakeLists.txtes el siguiente:

cmake_minimum_required(VERSION 3.0)
project(SORTLIB)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})
set(LIBRARY_OUTPUT_PATH ${LIB_PATH})
add_library(${SORT_LIB} SHARED ${SRC})
  • Línea 6 add_library: Generar una biblioteca dinámica El nombre de la biblioteca dinámica SORT_LIBse define en el archivo del nodo raíz.

El contenido de este archivo calces similar al contenido del archivo de nodo, excepto que esta vez se genera una biblioteca dinámica.

Al generar un archivo de biblioteca, la biblioteca puede ser una biblioteca estática o una biblioteca dinámica, que generalmente debe determinarse en función de la situación real. Si la biblioteca generada es relativamente grande, se recomienda convertirla en una biblioteca dinámica.

1.2.4 directorio prueba1

El contenido de los archivos en el directorio test1 CMakeLists.txtes el siguiente:

cmake_minimum_required(VERSION 3.0)
project(CALCTEST)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})
# include_directories(${HEAD_PATH})
link_libraries(${CALC_LIB})
set(EXECUTABLE_OUTPUT_PATH ${EXEC_PATH})
add_executable(${APP_NAME_1} ${SRC})
  • Línea 4 include_directories: especifique la ruta del archivo de encabezado, HEAD_PATHlas variables se definen en el archivo del nodo raíz
  • Línea 6 link_libraries: especifique el programa ejecutable que se vinculará 静态库y CALC_LIBlas variables se definen en el archivo del nodo raíz
  • Línea 7 set: Especifique la ruta donde se genera el programa ejecutable y EXEC_PATHlas variables se definen en el archivo del nodo raíz.
  • Línea 8 add_executable: Genera un programa ejecutable, APP_NAME_1las variables se definen en el archivo del nodo raíz.

El programa ejecutable aquí está vinculado a una biblioteca estática y, finalmente, la biblioteca estática se empaquetará en el programa ejecutable y, una vez iniciado el programa ejecutable, la biblioteca estática se cargará en la memoria.

1.2.5 directorio prueba2

El contenido de los archivos en el directorio test2 CMakeLists.txtes el siguiente:

cmake_minimum_required(VERSION 3.0)
project(CALCTEST)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})
# include_directories(${HEAD_PATH})
link_libraries(${CALC_LIB})
set(EXECUTABLE_OUTPUT_PATH ${EXEC_PATH})
add_executable(${APP_NAME_1} ${SRC})
  • La cuarta línea include_directories: contiene la ruta del archivo de encabezado, HEAD_PATHlas variables se definen en el archivo del nodo raíz
  • Línea 5 set: Especifique la ruta donde se genera el programa ejecutable, EXEC_PATHlas variables se definen en el archivo del nodo raíz.
  • Línea 6 link_directories: Especifique la ruta de la biblioteca dinámica que será vinculada por el programa ejecutable. LIB_PATHLas variables se definen en el archivo del nodo raíz.
  • Línea 7 add_executable: genera un programa ejecutable, APP_NAME_2las variables se definen en el archivo del nodo raíz
  • Línea 8 target_link_libraries: Especifique el nombre de la biblioteca dinámica a la que vinculará el programa ejecutable.

Al generar un programa ejecutable, la biblioteca dinámica no se empaquetará dentro del programa ejecutable. Cuando se inicia el programa ejecutable, la biblioteca dinámica no se cargará en la memoria. Solo cuando el programa ejecutable llame a una función en la biblioteca dinámica, la biblioteca dinámica se cargará en la memoria y varios procesos pueden compartir la misma ubicación en la memoria Una biblioteca dinámica, por lo que las bibliotecas dinámicas también se denominan bibliotecas compartidas.

1.2.6 Construyendo el proyecto

Después de que todo esté listo, comience a construir el proyecto, ingrese al directorio del nodo raíz build 目录y ejecute cmake 命令, de la siguiente manera:

$ cmake ..
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/robin/abc/cmake/calc/build

Puede ver que buildalgunos archivos y directorios se generan en el directorio, como se muestra a continuación:

$ tree build -L 1     
build
├── calc                  # 目录
├── CMakeCache.txt        # 文件
├── CMakeFiles            # 目录
├── cmake_install.cmake   # 文件
├── Makefile              # 文件
├── sort                  # 目录
├── test1                 # 目录
└── test2                 # 目录

Luego ejecute build 目录a continuación make 命令:

imagen-20230313234101139

La siguiente información se puede obtener de la figura anterior:

  1. libLa biblioteca estática se genera en un directorio en la raíz del proyecto.libcalc.a
  2. libLa biblioteca dinámica se genera en el directorio del directorio raíz del proyecto.libsort.so
  3. binEl programa ejecutable se genera en el directorio del directorio raíz del proyecto.test1
  4. binEl programa ejecutable se genera en el directorio del directorio raíz del proyecto.test2

Finalmente, echemos un vistazo a si los archivos mencionados anteriormente realmente se generan en los directorios correspondientes:

$ tree bin/ lib/
bin/
├── test1
└── test2
lib/
├── libcalc.a
└── libsort.so

Se puede ver que es cierto y el proyecto está completado.

Escribe al final:

En el proyecto, si un determinado módulo del programa se convierte en una biblioteca dinámica o una biblioteca estática 并且在CMakeLists.txt 中指定了库的输出目录, y luego otros módulos necesitan cargar el archivo de biblioteca generado, puede usarlo directamente en este momento.如果没有指定库的输出路径或者需要直接加载外部提供的库文件,此时就需要使用 link_directories 将库文件路径指定出来。

2. Control de procesos

El control de procesos también se puede realizar en CMakeLists.txt de CMake, lo que significa que las sumas se pueden realizar como si se escribieran 条件判断scripts de shell 循环.

2.1 Sentencia condicional

El formato de sintaxis para el juicio condicional es el siguiente:

if(<condition>)
  <commands>
 elseif(<condition>)	# 可选快, 可以重复
  <commands>
else() 				    # 可选快
  <commands>
endif()

Al hacer juicios condicionales, si hay varias condiciones, puede escribir varias elseifcondiciones. Se puede utilizar la última condición else, pero el inicio y el final deben aparecer en pares , respectivamente: ify endif.

2.1.1 Expresiones básicas

if(<expression>)

Si es una expresión básica, expressionexisten las siguientes tres situaciones: 常量, 变量, 字符串.

  • Si es 1, ON, YES, TRUE, Y, 非零值, 非空字符串el juicio condicional regresaTrue

  • Si es 0, OFF, NO, FALSE, N, IGNORE, NOTFOUND, 空字符串el juicio condicional regresaFalse

2.1.2 Juicio lógico

  • NO

    if(NOT <condition>)
    

    De hecho, esta es una operación de negación. Si la condición conditiones, Truese devolverá False. Si la condición conditiones, Falsese devolverá True.

  • Y

    if(<cond1> AND <cond2>)
    

    Si cond1y cond2son ambos True, devuelva Trueen caso contrario False.

  • O

    if(<cond1> OR <cond2>)
    

    Devuelve si cond1al cond2menos una de las dos condiciones es verdadera y devuelve si ambas condiciones son verdaderas .TrueTrueFalseFalse

2.1.3 Comparación

  • Comparación basada en números

    if(<variable|string> LESS <variable|string>)
    if(<variable|string> GREATER <variable|string>)
    if(<variable|string> EQUAL <variable|string>)
    if(<variable|string> LESS_EQUAL <variable|string>)
    if(<variable|string> GREATER_EQUAL <variable|string>)
    
    • variable:variable, string:cadena

    • LESS: Si el valor de la izquierda 小于está a la derecha, devuelveTrue

    • GREATER: Si el valor de la izquierda 大于está a la derecha, devuelveTrue

    • EQUAL: Si el valor de la izquierda 等于está a la derecha, devuelveTrue

    • LESS_EQUAL: Si el valor de la izquierda 小于等于está a la derecha, devuelveTrue

    • GREATER_EQUAL: Si el valor de la izquierda 大于等于está a la derecha, devuelveTrue

  • Comparación basada en cadenas

    if(<variable|string> STRLESS <variable|string>)
    if(<variable|string> STRGREATER <variable|string>)
    if(<variable|string> STREQUAL <variable|string>)
    if(<variable|string> STRLESS_EQUAL <variable|string>)
    if(<variable|string> STRGREATER_EQUAL <variable|string>)
    
    • variable:variable, string:cadena

    • STRLESS: Si la cadena izquierda está 小于en el lado derecho, devuelveTrue

    • STRGREATER: Si la cadena izquierda está 大于en el lado derecho, devuelveTrue

    • STREQUAL: Si la cadena izquierda está 等于en el lado derecho, devuelveTrue

    • STRLESS_EQUAL: Si la cadena izquierda está 小于等于en el lado derecho, devuelveTrue

    • STRGREATER_EQUAL: Si la cadena izquierda está 大于等于en el lado derecho, devuelveTrue

2.1.4 Operaciones de archivos

  1. Determinar si existe un archivo o directorio [ EXISTS]

    if(EXISTS path-to-file-or-directory)
    

    Devuelve si el archivo o directorio existe True, en caso contrario devuelve False.

  2. Determinar si es un directorio [ IS_DIRECTORY]

    if(IS_DIRECTORY path)
    
    • La ruta al directorio aquí debe ser una ruta absoluta [como /home/user/]

    • Devuelve si el directorio existe True, devuelve si el directorio no existe False.

  3. Determinar si se trata de una conexión suave[ IS_SYMLINK]

    if(IS_SYMLINK file-name)
    
    • La ruta correspondiente al nombre del archivo aquí debe ser una ruta absoluta [como /home/user/file-name]

    • Devuelve si el enlace suave existe True, devuelve si el enlace suave no existe False.

    • Los enlaces suaves equivalen a atajos en Windows

  4. Determinar si es una ruta absoluta [ IS_ABSOLUTE]

    if(IS_ABSOLUTE path)
    
    • En cuanto a caminos absolutos:

      • Si es así Linux, la ruta debe describirse comenzando desde el directorio raíz [por ejemplo /home/user/file-name]
      • Si es así Windows, la ruta debe describirse a partir de la letra de la unidad [como C:\Users\user\]
    • Regresa si es una ruta absoluta True, regresa si no es una ruta absoluta False.

2.1.5 Otros

  • Determinar si un elemento está en la lista [ IN_LIST]

    if(<variable|string> IN_LIST <variable>)
    
    • Requisito de versión de CMake: mayor o igual a 3.3

    • Devuelve si este elemento está en la lista True, en caso contrario False.

  • Compara dos caminos para la igualdad [ PATH_EQUAL]

    if(<variable|string> PATH_EQUAL <variable|string>)
    
    • Requisito de versión de CMake: mayor o igual a 3.24
    • Devuelve si este elemento está en la lista True, en caso contrario False.

    La comparación de rutas es en realidad la comparación de otra cadena, si no hay problema con la escritura del formato de ruta, también puedes compararla de la siguiente manera [] STREQUAL:

    if(<variable|string> STREQUAL <variable|string>)
    

    Cuando escribimos una determinada ruta, es posible que escribamos algunos separadores más debido a una mala operación, como escribir /a/b/c. /a//b///cEn este momento, STREQUALcomparar las dos cadenas definitivamente será desigual, pero al PATH_EQUALcomparar las dos rutas, los resultados obtenidos son iguales. Puedes ver el siguiente ejemplo:

    cmake_minimum_required(VERSION 3.26)
    project(test)
    
    if("/home//robin///Linux" PATH_EQUAL "/home/robin/Linux")
        message("路径相等")
    else()
        message("路径不相等")
    endif()
    
    message(STATUS "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@")
    
    if("/home//robin///Linux" STREQUAL "/home/robin/Linux")
        message("路径相等")
    else()
        message("路径不相等")
    endif()
    

    La información del registro de salida es la siguiente:

    路径相等
    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    路径不相等
    

    De los resultados obtenidos, podemos sacar una conclusión: al comparar rutas, si usa PATH_EQUAL, puede eliminar automáticamente las líneas divisorias redundantes en la ruta y luego comparar las rutas. Si usa STREQUAL, solo puede comparar cadenas.

    Para juicios más condicionales sobre si, consulte la documentación oficial.

2.2 Bucle

Hay dos formas de realizar un bucle en CMake, a saber: foreachy while.

2.2.1 para cada uno

Utilice foreach para realizar un bucle, el formato de sintaxis es el siguiente:

foreach(<loop_var> <items>)
    <commands>
endforeach()

A través de foreachesto, podemos itemsrecorrer los datos que contiene y luego loop_varsacar el valor actual atravesado. Existen los siguientes usos al obtener el valor:

Método 1

foreach(<loop_var> RANGE <stop>)
  • RANGE: Palabra clave, que indica que se va a recorrer el rango
  • stop: Este es uno 正整数,表示范围的结束值, al atravesar 从 0 开始,最大值为 stop.
  • loop_var: Almacena el valor extraído cada vez que pasa por el bucle.

Por ejemplo:

cmake_minimum_required(VERSION 3.2)
project(test)
# 循环
foreach(item RANGE 10)
    message(STATUS "当前遍历的值为: ${item}" )
endforeach()

La información del registro de salida es la siguiente:

$ cmake ..
-- 当前遍历的值为: 0
-- 当前遍历的值为: 1
-- 当前遍历的值为: 2
-- 当前遍历的值为: 3
-- 当前遍历的值为: 4
-- 当前遍历的值为: 5
-- 当前遍历的值为: 6
-- 当前遍历的值为: 7
-- 当前遍历的值为: 8
-- 当前遍历的值为: 9
-- 当前遍历的值为: 10
-- Configuring done
-- Generating done
-- Build files have been written to: /home/robin/abc/a/build

Permítanme enfatizar nuevamente: al atravesar un rango de números enteros, el rango obtenido es así [0, parada], y el lado derecho es el rango cerrado que contiene el valor de parada.

Método 2

foreach(<loop_var> RANGE <start> <stop> [<step>])

Esta es 方法1una versión mejorada de lo anterior. Cuando atravesamos un rango de números enteros, además de especificar el rango inicial, también podemos especificar el tamaño del paso.

  • RANGE: Palabra clave, que indica que se va a recorrer el rango
  • start:Esto es un正整数,表示范围的起始值,也就是说最小值为 start
  • stop:Esto es un正整数,表示范围的结束值,也就是说最大值为 stop
  • step: Controla el tamaño del paso para aumentar cada vez que se recorre,默认为1,可以不设置
  • loop_var: Almacena el valor extraído cada vez que pasa por el bucle.

Por ejemplo:

cmake_minimum_required(VERSION 3.2)
project(test)

foreach(item RANGE 10 30 2)
    message(STATUS "当前遍历的值为: ${item}" )
endforeach()

Los resultados de salida son los siguientes:

$ cmake ..
-- 当前遍历的值为: 10
-- 当前遍历的值为: 12
-- 当前遍历的值为: 14
-- 当前遍历的值为: 16
-- 当前遍历的值为: 18
-- 当前遍历的值为: 20
-- 当前遍历的值为: 22
-- 当前遍历的值为: 24
-- 当前遍历的值为: 26
-- 当前遍历的值为: 28
-- 当前遍历的值为: 30
-- Configuring done
-- Generating done
-- Build files have been written to: /home/robin/abc/a/build

Permítanme enfatizar nuevamente: cuando se utiliza el método anterior para atravesar un intervalo entero, el rango obtenido es así [inicio, parada] Los lados izquierdo y derecho son intervalos cerrados, incluidos los dos valores de inicio y parada. El tamaño del paso es el predeterminado. Es 1, no se puede configurar.

Método 3

foreach(<loop_var> IN [LISTS [<lists>]] [ITEMS [<items>]])

Esta es foreachotra variante, de esta manera podemos atravesar datos más complejos, los dos primeros métodos solo son adecuados para atravesar dentro de un cierto rango de enteros positivos.

  • IN: Palabra clave, expresada en xxx

  • LISTS: Palabra clave, correspondiente a la lista list, que set、listse puede obtener mediante

  • ITEMS: Palabra clave, correspondiente a una lista

  • loop_var: Almacena el valor extraído cada vez que pasa por el bucle.

cmake_minimum_required(VERSION 3.2)
project(test)
# 创建 list
set(WORD a b c d)
set(NAME ace sabo luffy)
# 遍历 list
foreach(item IN LISTS WORD NAME)
    message(STATUS "当前遍历的值为: ${item}" )
endforeach()

En el ejemplo anterior, se crean dos listas listy ambas se atraviesan durante el recorrido ( 可以根据实际需求选择同时遍历多个或者只遍历一个). La información del registro de salida es la siguiente:

$ cd build/
$ cmake ..
-- 当前遍历的值为: a
-- 当前遍历的值为: b
-- 当前遍历的值为: c
-- 当前遍历的值为: d
-- 当前遍历的值为: ace
-- 当前遍历的值为: sabo
-- 当前遍历的值为: luffy
-- Configuring done
-- Generating done
-- Build files have been written to: /home/robin/abc/a/build

Se generan un total de 7 cadenas, lo que indica que no hay ningún problema con el recorrido. A continuación mire de otra manera:

cmake_minimum_required(VERSION 3.2)
project(test)

set(WORD a b c "d e f")
set(NAME ace sabo luffy)
foreach(item IN ITEMS ${WORD} ${NAME})
    message(STATUS "当前遍历的值为: ${item}" )
endforeach()

En el ejemplo anterior, la palabra clave LISTSse cambia a "" durante el proceso transversal ITEMS, seguida de una o más listas, pero esta vez es necesario ${}eliminar los valores de la lista. La información de salida es la misma que en el ejemplo anterior:

$ cd build/
$ cmake ..
-- 当前遍历的值为: a
-- 当前遍历的值为: b
-- 当前遍历的值为: c
-- 当前遍历的值为: d e f
-- 当前遍历的值为: ace
-- 当前遍历的值为: sabo
-- 当前遍历的值为: luffy
-- Configuring done
-- Generating done
-- Build files have been written to: /home/robin/abc/a/build

Pequeños detalles: al organizar la lista mediante set, si hay espacios en una cadena, puede entre comillas dobles. Para conocer métodos de operación específicos, consulte el ejemplo anterior.

Método 4

Nota: Este método de bucle requiere que la versión de CMake sea mayor o igual a 3.17.

foreach(<loop_var>... IN ZIP_LISTS <lists>)

De esta forma se recorre una o más listas, lo que puede entenderse como una 方式3versión mejorada. Debido a que es imposible atravesar varias listas mediante el método anterior, pero desea extraer los elementos de la lista especificada y usarlos, esto se puede lograr fácilmente en esta versión mejorada.

  • loop_var: Almacena el valor extraído en cada bucle. Puede especificar múltiples variables según el número de listas a recorrer, que se utilizan para almacenar el valor actualmente extraído de la lista correspondiente.
    • 如果指定了多个变量名,它们的数量应该和列表的数量相等
    • 如果只给出了一个 loop_var,那么它将一系列的 loop_var_N 变量来存储对应列表中的当前项,也就是说 loop_var_0 对应第一个列表,loop_var_1 对应第二个列表,以此类推......
    • 如果遍历的多个列表中一个列表较短,当它遍历完成之后将不会再参与后续的遍历(因为其它列表还没有遍历完)。
  • IN: Palabra clave, expresada en xxx
  • ZIP_LISTS: Palabra clave, correspondiente a la lista list, que set 、listse puede obtener mediante
cmake_minimum_required(VERSION 3.17)
project(test)
# 通过list给列表添加数据
list(APPEND WORD hello world "hello world")
list(APPEND NAME ace sabo luffy zoro sanji)
# 遍历列表
foreach(item1 item2 IN ZIP_LISTS WORD NAME)
    message(STATUS "当前遍历的值为: item1 = ${item1}, item2=${item2}" )
endforeach()

message("=============================")
# 遍历列表
foreach(item  IN ZIP_LISTS WORD NAME)
    message(STATUS "当前遍历的值为: item1 = ${item_0}, item2=${item_1}" )
endforeach()

En este ejemplo, la adición de datos de lista se listlogra mediante . Se utilizan dos métodos al recorrer la lista: uno proporciona múltiples variables para almacenar los valores en la lista actual y el otro solo tiene una variable, pero debe ser operado por el método cuando realmente se obtiene el valor. Nota 变量名_0、变量名_1、变量名_N: El número correspondiente a la primera lista es 0, el número correspondiente a la primera lista es 0 y el número correspondiente a la primera lista es 0.

El resultado del ejemplo anterior es el siguiente:

$ cd build/
$ cmake ..
-- 当前遍历的值为: item1 = hello, item2=ace
-- 当前遍历的值为: item1 = world, item2=sabo
-- 当前遍历的值为: item1 = hello world, item2=luffy
-- 当前遍历的值为: item1 = , item2=zoro
-- 当前遍历的值为: item1 = , item2=sanji
=============================
-- 当前遍历的值为: item1 = hello, item2=ace
-- 当前遍历的值为: item1 = world, item2=sabo
-- 当前遍历的值为: item1 = hello world, item2=luffy
-- 当前遍历的值为: item1 = , item2=zoro
-- 当前遍历的值为: item1 = , item2=sanji
-- Configuring done (0.0s)
-- Generating done (0.0s)
-- Build files have been written to: /home/robin/abc/a/build

2.2.2 mientras

Además de usar , foreachtambién puedes usar whileloop, el formato de escritura del juicio condicional correspondiente al final del ciclo if/elseifes el mismo. whileEl formato de sintaxis es el siguiente:

while(<condition>)
    <commands>
endwhile()

whileEl ciclo es relativamente simple, solo necesita especificar las condiciones para que finalice el ciclo:

cmake_minimum_required(VERSION 3.5)
project(test)
# 创建一个列表 NAME
set(NAME luffy sanji zoro nami robin)
# 得到列表长度
list(LENGTH NAME LEN)
# 循环
while(${LEN} GREATER  0)
    message(STATUS "names = ${NAME}")
    # 弹出列表头部元素
    list(POP_FRONT NAME)
    # 更新列表长度
    list(LENGTH NAME LEN)
endwhile()

Los resultados de salida son los siguientes:

$ cd build/
$ cmake ..
-- names = luffy;sanji;zoro;nami;robin
-- names = sanji;zoro;nami;robin
-- names = zoro;nami;robin
-- names = nami;robin
-- names = robin
-- Configuring done (0.0s)
-- Generating done (0.0s)
-- Build files have been written to: /home/robin/abc/a/build

Puede ver que cuando aparecen todos los elementos de la lista, la longitud de la lista se vuelve 0 y whileel bucle sale.


Autor: Su Bingque
Enlace: https://subingwen.cn/cmake/CMake-advanced/
Fuente: Da Bing, a quien le encanta programar.
Los derechos de autor pertenecen al autor. Para reimpresiones comerciales, comuníquese con el autor para obtener autorización. Para reimpresiones no comerciales, indique la fuente.

3. Usando funciones

Cómo definir funciones usando cmake:

function(FunctionName ARG1 ARG2)
    ...
endfunction()
function(FunctionName ARGS)
    ...
endfunction()
  • FunctionName:El nombre de la función se escribe en la primera posición del parámetro, seguido de los parámetros de la función. Podemos usar múltiples parámetros para aceptar entradas externas respectivamente, o podemos usar variables internas especiales ARGVpara aceptar todas las entradas (para lograr una longitud de parámetro variable).

A continuación se muestra un ejemplo de uso:

function(print_variables_1 ARG1 ARG2)
  message("${ARG1}")
  message("${ARG2}")
endfunction()
print_variables_1("Hello" "World")
 Hello
 World
function(print_variables_2)
    foreach(variable ${ARGV})
        message("${variable}")
    endforeach()
endfunction()
print_variables_2("Hello" "World" "CMake" "Function")
 Hello
 World
 CMake
 Function

Nota: El alcance de la función es global, ya sea que esté definido en el código fuente principal o en el código fuente secundario de CMakeLists.txt, se puede acceder a él.

4. Seleccione un proceso de construcción

Para brindar flexibilidad al proyecto y brindar a los usuarios ciertas opciones durante la compilación, cmake proporciona la función de opción, que permite a los usuarios pasar algunas variables en el comando cmake, lo que puede afectar el proceso de compilación.

cmake_minimum_required (VERSION 3.20)
project (Tutorial)

option(ENABLE_FEATURE "This is a build switch" OFF)

if (ENABLE_FEATURE)
  message("The feature is on")
else()
  message("The feature is off")
endif()
  • option(ENABLE_FEATURE "This is a build switch" OFF)significa que se define una ENABLE_FEATUREopción nombrada. Su descripción es "Este es un cambio de compilación". El valor inicial está establecido en OFF, lo que significa que la función está desactivada de forma predeterminada.

Si pasamos el parámetro -D al llamar al comando cmake, entonces esta opción se puede modificar en el momento de la compilación para cambiar el efecto de compilación.

cmake --build . -DENABLE_FEATURE=ON

5.Paquete

Un paquete (Paquete) puede entenderse como una colección de software más grande que una biblioteca y está compuesto por una serie de carpetas que contienen bibliotecas y archivos de encabezado.

5.1 Buscar paquetes

El uso se find_packageutiliza para buscar y cargar la biblioteca o módulo externo especificado en el sistema. Se utiliza para buscar y configurar automáticamente las dependencias requeridas por el proyecto durante el proceso de compilación de CMake.

La sintaxis de este comando es la siguiente:

find_package(<package_name> [version] [EXACT] [QUIET] [REQUIRED] [COMPONENTS <component1> <component2> ...])
  • <package_name>: El nombre de la biblioteca o módulo externo que se buscará.
  • version: el número de versión de la biblioteca o módulo, que puede ser un número de versión específico o un rango de números de versión.
  • EXACT: Indica que la versión buscada debe coincidir exactamente con la versión especificada.
  • QUIET: controla si se genera información detallada sobre el proceso de búsqueda.
  • REQUIRED:: El proceso de compilación fallará si no se puede encontrar la biblioteca o el módulo especificado.
  • COMPONENTS: especifica un componente o complemento específico que debe encontrarse.

Una vez que el objetivo se encuentra exitosamente, puede usar comandos como include_directoriesy para incluir bibliotecas y archivos de encabezado.target_include_directoriestarget_link_libraries

Aquí hay un ejemplo:

find_package(OpenCV 4.2.0 REQUIRED COMPONENTS core highgui)
  • En este ejemplo, CMake buscará en el sistema una biblioteca llamada "OpenCV" y lo obligará a encontrar la versión 4.2.0. Al mismo tiempo, es necesario encontrar los dos componentes "core" y "highgui".

​cmake predefine oficialmente muchas descripciones de dependencia para buscar paquetes, que se almacenan en <CMakePath>/share/cmake-<version>/Modulesel directorio. Cada Find<LibaryName>.cmakearchivo nombrado con puede ayudarnos a encontrar un paquete. Si ninguna de estas rutas satisface nuestras necesidades, también podemos agregar una ruta de búsqueda para nuestro propio paquete agregándola a una variable como se CMAKE_MODULE_PATHmuestra a continuación.

list(APPEND CMAKE_MODULE_PATH "/path/to/package1")
list(APPEND CMAKE_MODULE_PATH "/path/to/package2")
list(APPEND CMAKE_MODULE_PATH "/path/to/package3")

Para obtener más información sobre find_package, consulte: https://blog.csdn.net/zhanghm1995/article/details/105466372.

5.2 Crear paquete

Si queremos que find_packagecmake encuentre los paquetes que escribimos, debemos organizar nuestros paquetes de acuerdo con los requisitos de cmake. La operación clave es crear un Find<LibraryName>.cmakearchivo llamado y describir en este archivo la información de la biblioteca asociada con este paquete.

A continuación se muestra un ejemplo. Almacene todo el contenido relacionado con el paquete en el directorio MyPackage y escriba un FindMyPackage.cmakearchivo para describir la información de dependencia del paquete.

.
├── CMakeLists.txt
├── MyPackage
│   ├── FindMyPackage.cmake
│   ├── module1
│   │   ├── include
│   │   │   └── func1.h
│   │   └── lib
│   │       └── libfunc1.a
│   └── module2
│       ├── include
│       │   └── func2.h
│       └── lib
│           └── libfunc1.a
└── tutorial.cxx
  1. En FindMyPackage.cmakeel archivo escribimos el siguiente contenido. Entre ellos, find_pathse utiliza para especificar el archivo de encabezado utilizado por el paquete, así como la ruta que se debe buscar al especificar el archivo de encabezado, y guardar el resultado en una MyPackage_INCLUDE_DIRvariable; find_libraryde ​​manera similar.
find_path(MyPackage_INCLUDE_DIR
    NAMES func1.h func2.h
    PATHS ${CMAKE_CURRENT_LIST_DIR}/module1/include ${CMAKE_CURRENT_LIST_DIR}/module2/include
)

find_library(MyPackage_LIBRARY
    NAMES func1 func2
    PATHS ${CMAKE_CURRENT_LIST_DIR}/module1/lib ${CMAKE_CURRENT_LIST_DIR}/module2/lib 
)

set(MyPackage_FOUND FALSE)
if(MyPackage_INCLUDE_DIR AND MyPackage_LIBRARY)
    set(MyPackage_FOUND TRUE)
endif()
  1. Luego usamos esta biblioteca en nuestro proyecto.

    Nota: Hemos agregado la ruta del paquete a la ruta de búsqueda de cmake para que cmake list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/MyPackage)pueda encontrarlo.find_package

cmake_minimum_required (VERSION 3.20)
project (Tutorial)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/MyPackage)
find_package(MyPackage REQUIRED)
add_executable(Tutorial tutorial.cxx)
target_include_directories(Tutorial PRIVATE ${MyPackage_INCLUDE_DIR})
target_link_libraries(Tutorial PRIVATE ${MyPackage_LIBRARY})

6. Proyecto de instalación

Después de completar la construcción del proyecto, es posible que necesitemos colocar el producto compilado en un directorio determinado para ejecutarlo, lo que implica instalar nuestro proyecto. El contenido del proyecto de instalación puede incluir: archivos ejecutables, archivos de biblioteca, archivos de configuración, archivos de encabezado, documentos de ayuda, etc.

función de instalación

Si necesita instalar el proyecto, además de usar cmake para ejecutar el comando de compilación, también debe ejecutar el comando de instalación, como se muestra a continuación.

#cmake --build .
cmake --install .

método uno

install(PROGRAMS <data_files> DESTINATION <destination>)
  • El primer parámetro se utiliza para especificar el tipo de archivo que se copiará. Como se usa a continuación TARGETS, FILES.

Nota: Si necesita copiar el archivo ejecutable, asegúrese de utilizar PROGRAMSen su lugar FILES. De lo contrario, cmake modificará los permisos de ejecución del archivo ejecutable a un archivo normal durante el proceso de instalación, privando los permisos del archivo ejecutable.

cmake_minimum_required(VERSION 3.10)
project(INSTALL)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
add_executable(main main.cpp)

message(STATUS "installing exectuables into " ${PROJECT_SOURCE_DIR}/install)
file(GLOB MY_INCLUDE_FILES  ${PROJECT_SOURCE_DIR}/include/*.h)
install(TARGETS main DESTINATION ${PROJECT_SOURCE_DIR}/install)
install(FILES ${MY_INCLUDE_FILES}   DESTINATION  ${PROJECT_SOURCE_DIR}/install/include)
  • DESTINATION: destino, meta
  • La línea 7 file(GLOB MY_INCLUDE_FILES ${PROJECT_SOURCE_DIR}/include/*.h)usa fileel comando y GLOBla coincidencia de patrones para almacenar ${PROJECT_SOURCE_DIR}/includetodos los archivos en la carpeta .hen variables personalizadas MY_INCLUDE_FILES.
  • La línea 8 install(TARGETS main DESTINATION ${PROJECT_SOURCE_DIR}/install)instala “main” el archivo ejecutable nombrado en ${PROJECT_SOURCE_DIR}/installla carpeta.
  • La línea 9 install(FILES ${MY_INCLUDE_FILES} DESTINATION ${PROJECT_SOURCE_DIR}/install/include)instala MY_INCLUDE_FILEStodos los archivos de encabezado de la variable en ${PROJECT_SOURCE_DIR}/install/includela carpeta.

Si necesita confirmar la existencia de un archivo con anticipación durante el proceso de copia para evitar errores por no poder encontrar el archivo, puede utilizar el siguiente método.

if(EXISTS "path/to/file.txt")
    # do something
else()
    # do something
endif()

camino dos

Si desea ejecutar algunos programas para ayudar en el proceso de instalación, puede utilizar el siguiente código para lograrlo. Puede utilizar este método para crear nuevas carpetas o ejecutar scripts de Python.

install(CODE "EXECUTE_PROCESS(COMMAND echo \"Hello, World!\")"
# install(...)

Supongo que te gusta

Origin blog.csdn.net/qq_47355554/article/details/132644478
Recomendado
Clasificación