Tabla de contenido
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
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.txt
archivo (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 encabezadocalc 目录
: 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á
include
encalc.h
- El archivo de encabezado correspondiente está
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á
include
ensort.h
- El archivo de encabezado correspondiente está
test1 目录
: Directorio de prueba para probar algoritmos de suma, resta, multiplicación y divisióntest2 目录
: Directorio de prueba para probar el algoritmo de clasificación.
Puede ver que CMakeLists.txt
ahora 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.txt
información sobre el alcance de las variables del archivo:
-
CMakeLists.txt
Las variables en el nodo raíz son válidas globalmente. -
Las variables de los nodos principales
CMakeLists.txt
se pueden utilizar en los nodos secundarios. -
Las variables en los nodos secundarios
CMakeLists.txt
solo 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
: EspecificaCMakeLists.txt
la 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 enALL
el 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.txt
se construye la relación padre-hijo entre archivos.
1.2 Resolución de problemas
En el directorio anterior haremos lo siguiente:
test1 目录
Realice pruebas relacionadas con la calculadora a través de los archivos de prueba entest2 目录
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
. sort
La 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.txt
es 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.txt
la 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.txt
es 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_PATH
que se define en el archivo del nodo raíz. - Línea 5
set
: establece la ruta generada de la biblioteca,LIB_PATH
que 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áticaCALC_LIB
se 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.txt
es 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ámicaSORT_LIB
se define en el archivo del nodo raíz.
El contenido de este archivo calc
es 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.txt
es 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_PATH
las variables se definen en el archivo del nodo raíz - Línea 6
link_libraries
: especifique el programa ejecutable que se vinculará静态库
yCALC_LIB
las variables se definen en el archivo del nodo raíz - Línea 7
set
: Especifique la ruta donde se genera el programa ejecutable yEXEC_PATH
las variables se definen en el archivo del nodo raíz. - Línea 8
add_executable
: Genera un programa ejecutable,APP_NAME_1
las 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.txt
es 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_PATH
las variables se definen en el archivo del nodo raíz - Línea 5
set
: Especifique la ruta donde se genera el programa ejecutable,EXEC_PATH
las 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_PATH
Las variables se definen en el archivo del nodo raíz. - Línea 7
add_executable
: genera un programa ejecutable,APP_NAME_2
las 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 build
algunos 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 命令
:
La siguiente información se puede obtener de la figura anterior:
lib
La biblioteca estática se genera en un directorio en la raíz del proyecto.libcalc.a
lib
La biblioteca dinámica se genera en el directorio del directorio raíz del proyecto.libsort.so
bin
El programa ejecutable se genera en el directorio del directorio raíz del proyecto.test1
bin
El 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 elseif
condiciones. Se puede utilizar la última condición else
, pero el inicio y el final deben aparecer en pares , respectivamente: if
y endif
.
2.1.1 Expresiones básicas
if(<expression>)
Si es una expresión básica, expression
existen 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
condition
es,True
se devolveráFalse
. Si la condicióncondition
es,False
se devolveráTrue
. -
Y
if(<cond1> AND <cond2>)
Si
cond1
ycond2
son ambosTrue
, devuelvaTrue
en caso contrarioFalse
. -
O
if(<cond1> OR <cond2>)
Devuelve si
cond1
alcond2
menos una de las dos condiciones es verdadera y devuelve si ambas condiciones son verdaderas .True
True
False
False
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
-
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 devuelveFalse
. -
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 existeFalse
.
-
-
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 existeFalse
. -
Los enlaces suaves equivalen a atajos en Windows
-
-
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 [comoC:\Users\user\
]
- Si es así
-
Regresa si es una ruta absoluta
True
, regresa si no es una ruta absolutaFalse
.
-
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 contrarioFalse
.
-
-
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 contrarioFalse
.
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///c
En este momento,STREQUAL
comparar las dos cadenas definitivamente será desigual, pero alPATH_EQUAL
comparar 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: foreach
y 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 foreach
esto, podemos items
recorrer los datos que contiene y luego loop_var
sacar 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 rangostop
: 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 方法1
una 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 rangostart
: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 foreach
otra 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 listalist
, queset、list
se 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 list
y 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 LISTS
se 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 方式3
versió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 xxxZIP_LISTS
: Palabra clave, correspondiente a la listalist
, queset 、list
se 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 list
logra 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 , foreach
también puedes usar while
loop, el formato de escritura del juicio condicional correspondiente al final del ciclo if/elseif
es el mismo. while
El formato de sintaxis es el siguiente:
while(<condition>)
<commands>
endwhile()
while
El 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 while
el 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 especialesARGV
para 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 unaENABLE_FEATURE
opción nombrada. Su descripción es "Este es un cambio de compilación". El valor inicial está establecido enOFF
, 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_package
utiliza 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_directories
y para incluir bibliotecas y archivos de encabezado.target_include_directories
target_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>/Modules
el directorio. Cada Find<LibaryName>.cmake
archivo 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_PATH
muestra 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_package
cmake 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>.cmake
archivo 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.cmake
archivo 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
- En
FindMyPackage.cmake
el archivo escribimos el siguiente contenido. Entre ellos,find_path
se 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 unaMyPackage_INCLUDE_DIR
variable;find_library
de 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()
-
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
PROGRAMS
en su lugarFILES
. 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)
usafile
el comando yGLOB
la coincidencia de patrones para almacenar${PROJECT_SOURCE_DIR}/include
todos los archivos en la carpeta.h
en variables personalizadasMY_INCLUDE_FILES
. - La línea 8
install(TARGETS main DESTINATION ${PROJECT_SOURCE_DIR}/install)
instala“main”
el archivo ejecutable nombrado en${PROJECT_SOURCE_DIR}/install
la carpeta. - La línea 9
install(FILES ${MY_INCLUDE_FILES} DESTINATION ${PROJECT_SOURCE_DIR}/install/include)
instalaMY_INCLUDE_FILES
todos los archivos de encabezado de la variable en${PROJECT_SOURCE_DIR}/install/include
la 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(...)