Tutorial de CMake a nivel de niñera (Parte 1)

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.Chacer descripción general

CMake es una herramienta de creación de proyectos y es multiplataforma. En cuanto a la construcción de proyectos, también estamos familiarizados con Makefile (construcción de proyectos mediante el comando make), la mayoría del software IDE integra make, como: nmake de VS, make de GNU en Linux, qmake de Qt, etc. Descubra que el archivo MAKE generalmente depende de la plataforma de compilación actual, la carga de trabajo de escribir el archivo MAKE es relativamente grande y es fácil cometer errores al resolver dependencias.

CMake puede resolver exactamente los problemas anteriores. Permite a los desarrolladores especificar el proceso de compilación de todo el proyecto. Según la plataforma de compilación, el 自动生成本地化的Makefile和工程文件usuario final solo necesita makecompilar. Por lo tanto, CMake puede considerarse como una herramienta que genera automáticamente Makefiles. El proceso de compilación es el siguiente en la imagen:

imagen-20230309130644912

  • makefileLa línea discontinua azul representa el proceso de construcción de un proyecto utilizando
  • La línea roja continua representa cmakeel proceso de construcción de un proyecto utilizando

Después de presentar las funciones de CMake, resumamos sus ventajas:

  • Multiplataforma

  • Capacidad para gestionar grandes proyectos.

  • Simplifique el proceso de compilación y el proceso de compilación.

  • Extensible: puede escribir módulos de funciones específicas para cmake para expandir las funciones de cmake

2. Uso de CMake

CMakeAdmite comandos en mayúsculas, minúsculas y mayúsculas y minúsculas. Si CMakeLists.txtla herramienta que utiliza al escribir un archivo tiene un símbolo del sistema correspondiente, déjela como está y no le preste demasiada atención.

2.1 Notas

2.1.1 Líneas de comentarios

CMakeUso , #que 行注释se puede colocar en cualquier lugar.

# 这是一个 CMakeLists.txt 文件
cmake_minimum_required(VERSION 3.0.0)

2.1.2 Bloque de comentarios

CMakeUtilice #[[ ]]el formulario 块注释.

#[[ 这是一个 CMakeLists.txt 文件。
这是一个 CMakeLists.txt 文件
这是一个 CMakeLists.txt 文件]]
cmake_minimum_required(VERSION 3.0.0)

2.1 Sólo archivos fuente

2.1.1 Vivir en la misma habitación

  1. Trabajo de preparación, para facilitar las pruebas, he preparado varios archivos de prueba en mi computadora local.

    • agregar.c

      #include <stdio.h>
      #include "head.h"
      
      int add(int a, int b)
      {
          return a+b;
      }
      
    • sub.c

      #include <stdio.h>
      #include "head.h"
      
      // 你好
      int subtract(int a, int b)
      {
          return a-b;
      }
      
    • mucho c

      #include <stdio.h>
      #include "head.h"
      
      int multiply(int a, int b)
      {
          return a*b;
      }
      
    • div.c

      #include <stdio.h>
      #include "head.h"
      
      double divide(int a, int b)
      {
          return (double)a/b;
      }
      
    • cabeza.h

      #ifndef _HEAD_H
      #define _HEAD_H
      // 加法
      int add(int a, int b);
      // 减法
      int subtract(int a, int b);
      // 乘法
      int multiply(int a, int b);
      // 除法
      double divide(int a, int b);
      #endif
      
    • C Principal

      #include <stdio.h>
      #include "head.h"
      
      int main()
      {
          int a = 20;
          int b = 12;
          printf("a = %d, b = %d\n", a, b);
          printf("a + b = %d\n", add(a, b));
          printf("a - b = %d\n", subtract(a, b));
          printf("a * b = %d\n", multiply(a, b));
          printf("a / b = %f\n", divide(a, b));
          return 0;
      }
      
  2. La estructura de directorios de los archivos anteriores es la siguiente:

    $ tree
    .
    ├── add.c
    ├── div.c
    ├── head.h
    ├── main.c
    ├── mult.c
    └── sub.c
    
  3. agregar CMakeLists.txtarchivo

    Agregue un nuevo archivo CMakeLists.txt en el directorio donde se encuentra el archivo fuente anterior. El contenido del archivo es el siguiente:

    cmake_minimum_required(VERSION 3.0)
    project(CALC)
    add_executable(app add.c div.c main.c mult.c sub.c)
    

    A continuación, presentaremos sucesivamente los tres comandos agregados en el archivo CMakeLists.txt:

    • cmake_minimum_required:Especifique la versión mínima de cmake utilizada

      • Opcional, no obligatorio, puede haber una advertencia si no se agrega
    • project: Defina el nombre del proyecto y especifique la versión del proyecto, la descripción del proyecto, la dirección de la página de inicio web y los idiomas admitidos (todos los idiomas son compatibles de forma predeterminada). Si no los necesita, puede ignorarlos. Solo necesita para especificar el nombre del proyecto.

      # PROJECT 指令的语法是:
      project(<PROJECT-NAME> [<language-name>...])
      project(<PROJECT-NAME>
             [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
             [DESCRIPTION <project-description-string>]
             [HOMEPAGE_URL <url-string>]
             [LANGUAGES <language-name>...])
      
    • add_executable: La definición del proyecto generará un programa ejecutable.

      add_executable(可执行程序名 源文件名称)
      
      • El nombre del programa ejecutable aquí no tiene projectnada que ver con el nombre del proyecto en

      • El nombre del archivo fuente puede ser uno o varios, si hay varios espacios o ;intervalos disponibles.

        # 样式1
        add_executable(app add.c div.c main.c mult.c sub.c)
        # 样式2
        add_executable(app add.c;div.c;main.c;mult.c;sub.c)
        
  4. ejecutar CMakecomando

    Todo está listo. Después de editar el archivo CMakeLists.txt, puede ejecutar cmakeel comando.

    # cmake 命令原型
    $ cmake CMakeLists.txt文件所在路径
    
    $ tree
    .
    ├── add.c
    ├── CMakeLists.txt
    ├── div.c
    ├── head.h
    ├── main.c
    ├── mult.c
    └── sub.c
    
    0 directories, 7 files
    robin@OS:~/Linux/3Day/calc$ cmake .
    

    Después de ejecutar el comando cmake, se ejecutarán los comandos en CMakeLists.txt, así que tenga cuidado cmakede no cometer errores al especificar la ruta del comando.

    Después de ejecutar el comando, verifique si hay más archivos en el directorio donde se encuentra el archivo fuente:

    $ tree -L 1
    .
    ├── add.c
    ├── CMakeCache.txt            #  new add file
    ├── CMakeFiles                   #  new add dir
    ├── cmake_install.cmake     #  new add file
    ├── CMakeLists.txt
    ├── div.c
    ├── head.h
    ├── main.c
    ├── Makefile                        # new add file
    ├── mult.c
    └── sub.c
    

    Podemos ver que se genera un archivo en el directorio correspondiente makefile, si ejecutamos makeel comando en este momento podemos construir el proyecto y obtener el programa ejecutable requerido.

    $ make
    Scanning dependencies of target app
    [ 16%] Building C object CMakeFiles/app.dir/add.c.o
    [ 33%] Building C object CMakeFiles/app.dir/div.c.o
    [ 50%] Building C object CMakeFiles/app.dir/main.c.o
    [ 66%] Building C object CMakeFiles/app.dir/mult.c.o
    [ 83%] Building C object CMakeFiles/app.dir/sub.c.o
    [100%] Linking C executable app
    [100%] Built target app
    
    # 查看可执行程序是否已经生成
    $ tree -L 1
    .
    ├── add.c
    ├── app					# 生成的可执行程序
    ├── CMakeCache.txt
    ├── CMakeFiles
    ├── cmake_install.cmake
    ├── CMakeLists.txt
    ├── div.c
    ├── head.h
    ├── main.c
    ├── Makefile
    ├── mult.c
    └── sub.c
    

    appFinalmente, se compila el programa ejecutable (el nombre se CMakeLists.txtespecifica en).

2.1.2 Sala privada VIP

Como se puede ver en el ejemplo anterior, si CMakeLists.txtejecuta cmakeel comando en el directorio donde se encuentra el archivo, se generarán algunos directorios y archivos ( 包括 makefile 文件), si makefile文件ejecuta makeel comando nuevamente, el programa también generará algunos archivos intermedios y un archivo ejecutable durante el proceso de compilación. Esto hará que todo el directorio del proyecto parezca confuso y difícil de administrar y mantener. En este momento, podemos colocar los archivos generados que no tienen nada que ver con el código fuente del proyecto en el directorio correspondiente. Por ejemplo, nombre este directorio build:

$ mkdir build
$ cd build
$ 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/Linux/build

Ahora cmakeel comando se buildejecuta en el directorio, pero CMakeLists.txtel archivo está builden el directorio un nivel por encima del directorio, por lo que cmakela ruta especificada después del comando ..es el directorio un nivel por encima del directorio actual.

Cuando se ejecuta el comando, buildse generará un makefilearchivo en el directorio

$ tree build -L 1
build
├── CMakeCache.txt
├── CMakeFiles
├── cmake_install.cmake
└── Makefile

1 directory, 3 files

De esta manera, puede buildejecutar makecomandos en el directorio para compilar el proyecto y los archivos relacionados generados naturalmente se almacenarán en buildel directorio. De esta manera, todos los archivos pasados cmake​​​​y makegenerados quedan completamente aislados de los archivos fuente del proyecto y todos regresan a sus hogares para encontrar a su propia madre.

2.2 Orden privada

2.2.1 Definir variables

En el ejemplo anterior, se proporcionan un total de 5 archivos fuente. Suponiendo que estos cinco archivos fuente deben usarse repetidamente, es realmente problemático escribir sus nombres directamente cada vez. En este momento, debemos definir una variable para Consulte el archivo. La cadena correspondiente al nombre se almacena y debe usarse para definir variables en cmake set.

# SET 指令的语法是:
# [] 中的参数为可选项, 如不需要可以不写
SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
  • VAR:nombre de la variable
  • VALUE:variable
# 方式1: 各个源文件之间使用空格间隔
# set(SRC_LIST add.c  div.c   main.c  mult.c  sub.c)

# 方式2: 各个源文件之间使用分号 ; 间隔
set(SRC_LIST add.c;div.c;main.c;mult.c;sub.c)
add_executable(app  ${SRC_LIST})

2.2.2 Especificar el estándar C++ utilizado

Al escribir un programa C++, puede usar nuevas funciones como C++11, C++14, C++17, C++20, etc. Luego, debe determinar cuál usar en el comando de compilación al compilar. .estándar:

$ g++ *.cpp -std=c++11 -o app

En el ejemplo anterior, los parámetros -std=c++11se usan para especificar que el programa debe compilarse usando el estándar C++ 11, y el estándar C++ corresponde a una macro llamada DCMAKE_CXX_STANDARD. Hay dos formas de especificar el estándar C++ en CMake:

  1. Especificado por el comando set en CMakeLists.txt

    #增加-std=c++11
    set(CMAKE_CXX_STANDARD 11)
    #增加-std=c++14
    set(CMAKE_CXX_STANDARD 14)
    #增加-std=c++17
    set(CMAKE_CXX_STANDARD 17)
    
  2. Especifique el valor de esta macro al ejecutar el comando cmake

    #增加-std=c++11
    cmake CMakeLists.txt文件路径 -DCMAKE_CXX_STANDARD=11
    #增加-std=c++14
    cmake CMakeLists.txt文件路径 -DCMAKE_CXX_STANDARD=14
    #增加-std=c++17
    cmake CMakeLists.txt文件路径 -DCMAKE_CXX_STANDARD=17
    

    En el ejemplo anterior, la ruta después de CMake debe modificarse según corresponda de acuerdo con la situación real.

2.2.3 Especificar la ruta de salida

Especifique la ruta de salida del programa ejecutable en CMake, que también corresponde a una macro llamada EXECUTABLE_OUTPUT_PATH, y su valor aún lo setestablece el comando:

set(HOME /home/robin/Linux/Sort)
set(EXECUTABLE_OUTPUT_PATH ${HOME}/bin)
  • Línea 1: Defina una variable para almacenar una ruta absoluta
  • La segunda línea: establezca el valor de la ruta empalmada en EXECUTABLE_OUTPUT_PATHla macro
    • Si el subdirectorio en esta ruta no existe, se generará automáticamente, no es necesario crearlo manualmente

Dado que el programa ejecutable se obtiene en función del archivo MAKE generado por el comando cmake y luego ejecutado por el comando make, si se usa la ruta relativa ./xxx/xxx al especificar aquí la ruta de generación del programa ejecutable, entonces ./ en esta ruta Correspondiente al directorio donde se encuentra el archivo makefile.

2.3 Buscar archivos

Si hay muchos archivos fuente en un proyecto, CMakeLists.txtes imposible enumerar cada archivo en el directorio del proyecto uno por uno al escribir el archivo, lo que es demasiado problemático y poco realista. Por lo tanto, CMake nos proporciona un comando para buscar archivos, puedes usar aux_source_directoryel comando o filecomando.

2.3.1 Método 1

Utilice el comando en CMake aux_source_directorypara buscar en una ruta determinada 所有源文件. El formato del comando es:

aux_source_directory(< dir > < variable >)
  • dir: Directorio para buscar
  • variable: diralmacena la lista de archivos fuente buscados en el directorio en esta variable
cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
# 搜索 src 目录下的源文件
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_LIST)
add_executable(app  ${SRC_LIST})

2.3.2 Método 2

Si hay muchos archivos fuente en un proyecto, CMakeLists.txtes imposible enumerar cada archivo en el directorio del proyecto uno por uno al escribir el archivo, lo que es demasiado problemático. Por lo tanto, CMake nos proporciona un comando para buscar archivos, y es file(当然,除了搜索以外通过 file 还可以做其他事情).

file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)
  • GLOB: Genera una lista de todos los nombres de archivos que cumplen con las condiciones buscadas en el directorio especificado y la almacena en una variable.
  • GLOB_RECURSE: busque recursivamente el directorio especificado, genere una lista de los nombres de archivos buscados que cumplan las condiciones y guárdela en una variable.

Busque todos los archivos fuente en el directorio src del directorio actual y guárdelos en variables

file(GLOB MAIN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
file(GLOB MAIN_HEAD ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)
  • La macro CMAKE_CURRENT_SOURCE_DIR indica la ruta donde se encuentra el archivo CMakeLists.txt al que se accede actualmente.

  • Puede agregar comillas dobles o no para la ruta del archivo y el tipo que se buscará:

    file(GLOB MAIN_HEAD "${CMAKE_CURRENT_SOURCE_DIR}/src/*.h")
    

2.4 Incluir archivos de encabezado

Al compilar archivos fuente del proyecto, a menudo es necesario especificar la ruta del archivo de encabezado correspondiente al archivo fuente, para garantizar que el compilador pueda encontrar estos archivos de encabezado durante el proceso de compilación y pasar la compilación con éxito. Configurar el directorio que se incluirá en CMake también es muy simple. Se puede hacer con un comando, que es include_directories:

include_directories(headpath)

Por ejemplo, hay varios archivos fuente y su estructura de directorios es la siguiente:

$ tree
.
├── build
├── CMakeLists.txt
├── include
│   └── head.h
└── src
    ├── add.cpp
    ├── div.cpp
    ├── main.cpp
    ├── mult.cpp
    └── sub.cpp

3 directories, 7 files

CMakeLists.txtEl contenido del archivo es el siguiente:

cmake_minimum_required(VERSION 3.0)
project(CALC)
set(CMAKE_CXX_STANDARD 11)
set(HOME /home/robin/Linux/calc)
set(EXECUTABLE_OUTPUT_PATH ${HOME}/bin/)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_executable(app  ${SRC_LIST})

Entre ellos, la sexta línea especifica la ruta al archivo de encabezado, y PROJECT_SOURCE_DIRel valor correspondiente a la macro es el directorio que sigue cuando usamos el comando cmake, que generalmente es el directorio raíz del proyecto.

2.5 Crear biblioteca dinámica o biblioteca estática

A veces, el código fuente que escribimos no necesita ser compilado en programas ejecutables, pero algunas bibliotecas estáticas o dinámicas se generan para que las utilicen terceros. A continuación se explica cómo generar estos dos tipos de archivos de biblioteca en cmake.

2.5.1 Crear una biblioteca estática

  • ESTÁTICO

En cmake, si desea crear una biblioteca estática, los comandos que debe utilizar son los siguientes:

add_library(库名称 STATIC 源文件1 [源文件2] ...) 

En Linux, el nombre de la biblioteca estática se divide en tres partes: lib+ 库名字+ .a. Aquí solo necesita especificar el nombre de la biblioteca. Las otras dos partes se completarán automáticamente cuando se genere el archivo.

Aunque el nombre de la biblioteca en Windows es diferente del formato de Linux, solo necesita especificar el nombre.

srcHay un directorio a continuación. Los archivos fuente en el directorio deben compilarse en una biblioteca estática y luego usarse:

$ tree
.
├── build
├── CMakeLists.txt
├── include               # 头文件目录
│   └── head.h
├── main.cpp            # 用于测试的源文件
└── src                      # 源文件目录
    ├── add.cpp
    ├── div.cpp
    ├── mult.cpp
    └── sub.cpp

Según la estructura de directorios anterior, CMakeLists.txtel archivo se puede escribir así:

cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
add_library(calc STATIC ${SRC_LIST})

Esto eventualmente generará el archivo de biblioteca estática correspondiente libcalc.a.

2.5.2 Crear bibliotecas dinámicas

  • COMPARTIDO

En cmake, si desea crear una biblioteca dinámica, los comandos que debe utilizar son los siguientes:

add_library(库名称 SHARED 源文件1 [源文件2] ...) 

En Linux, el nombre de la biblioteca dinámica se divide en tres partes: lib+ 库名字+ .so. Aquí solo necesita especificar el nombre de la biblioteca. Las otras dos partes se completarán automáticamente cuando se genere el archivo.

Aunque el nombre de la biblioteca en Windows es diferente del formato de Linux, solo necesita especificar el nombre.

Según la estructura de directorios anterior, CMakeLists.txtel archivo se puede escribir así:

cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
add_library(calc SHARED ${SRC_LIST})

Esto eventualmente generará el archivo de biblioteca dinámica correspondiente libcalc.so.

2.5.3 Especificar la ruta de salida

Forma 1: para bibliotecas dinámicas

Para los archivos de biblioteca generados, la ruta de salida se puede especificar de la misma manera que para los programas ejecutables. 由于在Linux下生成的动态库默认是有执行权限的, por lo que puedes especificar el directorio que genera de la misma manera que genera un programa ejecutable:

cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
# 设置动态库生成路径
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
add_library(calc SHARED ${SRC_LIST})

Para este método, en realidad establece una ruta setpara la macro mediante un comando , y esta ruta es la ruta donde se genera el archivo ejecutable.EXECUTABLE_OUTPUT_PATH

Método 2: se aplica a ambos

Dado que las bibliotecas estáticas generadas en Linux no tienen permisos ejecutables de forma predeterminada, no se pueden usar EXECUTABLE_OUTPUT_PATHmacros al especificar la ruta para generar bibliotecas estáticas, sino que se deben LIBRARY_OUTPUT_PATHusar.Esta macro es aplicable tanto a archivos de biblioteca estática como a archivos de biblioteca dinámica.

cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
# 设置动态库/静态库生成路径
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
# 生成动态库
#add_library(calc SHARED ${SRC_LIST})
# 生成静态库
add_library(calc STATIC ${SRC_LIST})

2.6 Incluir archivos de biblioteca

En el proceso de escribir un programa, puede utilizar algunas bibliotecas dinámicas proporcionadas por el sistema o bibliotecas dinámicas o archivos de biblioteca estática producidos por usted mismo. cmake también nos proporciona comandos relevantes para cargar bibliotecas dinámicas.

2.6.1 Vincular biblioteca estática

src
├── add.cpp
├── div.cpp
├── main.cpp
├── mult.cpp
└── sub.cpp

Ahora compilamos los archivos srcen el directorio anterior add.cpp、div.cpp、mult.cpp、sub.cppen un archivo de biblioteca estática libcalc.a.

Crear y utilizar bibliotecas de enlaces estáticos mediante comandos

La estructura del directorio de prueba es la siguiente:

$ tree 
.
├── build
├── CMakeLists.txt
├── include
│   └── head.h
├── lib
│   └── libcalc.a     # 制作出的静态库的名字
└── src
    └── main.cpp

4 directories, 4 files

En cmake, el comando para vincular la biblioteca estática es el siguiente:

link_libraries(<static lib> [<static lib>...])
  • Parámetro 1 : especifique el nombre de la biblioteca estática que se vinculará
    • Puede ser nombre completolibxxx.a
    • También puede ser el nombre después de pellizcar la cabeza ( lib) y quitarle la cola ( ).axxx
  • Parámetro 2-N : el nombre de la otra biblioteca estática que se vinculará

Si el sistema no proporciona la biblioteca estática (hágalo usted mismo o utilice una biblioteca estática proporcionada por un tercero), es posible que no se encuentre la biblioteca estática. En este caso, también puede especificar la ruta de la biblioteca estática:

link_directories(<lib path>)

De esta forma, el CMakeLists.txtcontenido del archivo modificado es el siguiente:

cmake_minimum_required(VERSION 3.0)
project(CALC)
# 搜索指定目录下源文件
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
# 包含头文件路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 包含静态库路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
# 链接静态库
link_libraries(calc)
add_executable(app ${SRC_LIST})

Después de agregar el código en la línea 8 , puede encontrar esta biblioteca estática de acuerdo con la ruta especificada por el parámetro.

2.6.2 Vincular bibliotecas dinámicas

En el proceso de programación, además de introducir bibliotecas estáticas en el proyecto, muchas veces también se utilizan algunas bibliotecas estándar o dinámicas proporcionadas por terceros. En cuanto a la producción y uso de bibliotecas dinámicas y cómo cargarlas en la memoria, las bibliotecas estáticas son todos diferentes, no entraré en detalles aquí. Si tiene alguna duda, consulte la biblioteca estática y la biblioteca dinámica de Linux.

cmakeEl comando para vincular la biblioteca dinámica es el siguiente :

target_link_libraries(
    <target> 
    <PRIVATE|PUBLIC|INTERFACE> <item>... 
    [<PRIVATE|PUBLIC|INTERFACE> <item>...]...) 
  • objetivo : especifique el nombre del archivo para cargar la biblioteca dinámica

    • El archivo puede ser un archivo fuente.
    • Este archivo puede ser un archivo de biblioteca dinámica.
    • El archivo puede ser un archivo ejecutable.
  • PRIVADO|PÚBLICO|INTERFACE : Permisos de acceso para bibliotecas dinámicas, el valor predeterminado esPUBLIC

    • Si no hay dependencias entre las distintas bibliotecas dinámicas, no es necesario realizar ninguna configuración. No hay diferencia entre las tres. Generalmente no es necesario especificar, solo use el PÚBLICO predeterminado.

    • 动态库的链接具有传递性, si la biblioteca dinámica A se vincula a las bibliotecas dinámicas B y C, y la biblioteca dinámica D se vincula a la biblioteca dinámica A, entonces la biblioteca dinámica D es equivalente a vincularse también a las bibliotecas dinámicas B y C, y puede usar los métodos definidos en las bibliotecas dinámicas B y C. .

      target_link_libraries(A B C)
      target_link_libraries(D A)
      
      • PUBLIC: La biblioteca detrás de pública se vinculará al objetivo anterior y los símbolos internos también se exportarán y proporcionarán a terceros.

      • PRIVATE: La biblioteca detrás de privada solo se vincula al objetivo anterior y se finaliza. El tercero no puede detectar qué biblioteca ha ajustado.

      • INTERFACE: La biblioteca importada después de la interfaz no se vinculará al destino anterior y solo se exportarán los símbolos.

Biblioteca dinámica del sistema de enlace

La vinculación de bibliotecas dinámicas es completamente diferente a la de bibliotecas estáticas:

  • La biblioteca estática se empaquetará en el programa ejecutable durante la fase de vinculación de generación del programa ejecutable, de modo que cuando se inicie el programa ejecutable, la biblioteca estática se cargará en la memoria.
  • La biblioteca dinámica no se empaquetará en el programa ejecutable durante la fase de vinculación de generación del programa ejecutable. La biblioteca dinámica solo se cargará en la memoria cuando se inicie el programa ejecutable y se llamen las funciones de la biblioteca dinámica.

Por lo tanto, cmakeal especificar la biblioteca dinámica a vincular,应该将命令写到生成了可执行文件之后:

cmake_minimum_required(VERSION 3.0)
project(TEST)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
# 添加并指定最终生成的可执行程序名
add_executable(app ${SRC_LIST})
# 指定可执行程序要链接的动态库名字
target_link_libraries(app pthread) 

en target_link_libraries(app pthread):

  • app:Correspondiente al nombre del programa ejecutable final generado

  • pthread: Esta es la biblioteca dinámica que cargará el programa ejecutable. Esta biblioteca es la biblioteca de subprocesos proporcionada por el sistema. Su nombre completo es. Cuando se libpthread.soespecifica, la cabeza (lib) y la cola (.so) generalmente están pellizcadas.

Vincular bibliotecas dinámicas de terceros

Ahora, yo mismo he generado una biblioteca dinámica y la estructura de directorio correspondiente es la siguiente:

$ tree 
.
├── build
├── CMakeLists.txt
├── include
│   └── head.h            # 动态库对应的头文件
├── lib
│   └── libcalc.so        # 自己制作的动态库文件
└── main.cpp              # 测试用的源文件

3 directories, 4 files 

Supongamos que en el archivo de prueba se utilizan main.cppla biblioteca dinámica producida por usted y la biblioteca de subprocesos proporcionada por el sistema. En este momento, el archivo se puede escribir así:libcalc.soCMakeLists.txt

cmake_minimum_required(VERSION 3.0)
project(TEST)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
include_directories(${PROJECT_SOURCE_DIR}/include)
add_executable(app ${SRC_LIST})
target_link_libraries(app pthread calc) 

En la sexta línea , hay nombres de bibliotecas dinámicas que pthread、calcel programa ejecutable appvinculará . Cuando appse genera el programa ejecutable y se ejecuta el archivo, aparecerá el siguiente mensaje de error:

$ ./app 
./app: error while loading shared libraries: libcalc.so: cannot open shared object file: No such file or directory 

Esto se debe a que después de iniciar el programa ejecutable, calcse carga la biblioteca dinámica, pero no se conoce la ubicación de la biblioteca dinámica para resolver el problema de que la biblioteca dinámica no se puede cargar , por lo que la carga falla. En CMake, puede cargar el biblioteca dinámica antes de generar el programa ejecutable. , use el comando para especificar la ubicación de la biblioteca dinámica que se vinculará. Este comando también se usa para especificar la ubicación de la biblioteca estática:

link_directories(path)

Entonces el CMakeLists.txtarchivo modificado debería verse así:

cmake_minimum_required(VERSION 3.0)
project(TEST)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
# 指定源文件或者动态库对应的头文件路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 指定要链接的动态库的路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
# 添加并生成一个可执行程序
add_executable(app ${SRC_LIST})
# 指定要链接的动态库
target_link_libraries(app pthread calc) 

Después de link_directoriesespecificar la ruta de la biblioteca dinámica, no habrá ningún problema de que no se pueda encontrar la biblioteca dinámica al ejecutar el programa ejecutable generado.

Recordatorio: utilice el comando target_link_libraries para vincular bibliotecas dinámicas o archivos de biblioteca estática.

2.7 registro

En CMake, puede utilizar el usuario para mostrar un mensaje. El nombre del comando es message:

message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] "message to display" ...)
  • (无): noticias importantes
  • STATUS: mensaje no importante
  • WARNING: Advertencia de CMake, continuará ejecutándose
  • AUTHOR_WARNING: Advertencia de CMake (dev), continuará ejecutándose
  • SEND_ERROR: Error de CMake, continúa la ejecución, pero omite el paso de generación
  • FATAL_ERROR: Error de CMake, finalizando todo el procesamiento

La herramienta de línea de comandos de CMake muestra mensajes en stdout STATUSy todos los demás mensajes en stderr. La GUI de CMake muestra todos los mensajes en su área de registro.

El texto de los mensajes de advertencia y error de CMake se muestra utilizando un lenguaje de marcado simple. El texto no tiene sangría, las líneas que exceden la longitud se ajustan y las líneas nuevas se utilizan como separadores entre párrafos.

# 输出一般日志信息
message(STATUS "source path: ${PROJECT_SOURCE_DIR}")
# 输出警告信息
message(WARNING "source path: ${PROJECT_SOURCE_DIR}")
# 输出错误信息
message(FATAL_ERROR "source path: ${PROJECT_SOURCE_DIR}") 

2.8 Operaciones variables

2.8.1 Adición

A veces, los archivos fuente del proyecto no están necesariamente en el mismo directorio, pero estos archivos fuente eventualmente deben compilarse juntos para generar el archivo ejecutable o el archivo de biblioteca final. Si filebuscamos los archivos fuente en cada directorio a través de comandos, al final debemos realizar una operación de empalme de cadenas, puede usar setcomandos o comandos para empalmar cadenas list.

Usar empalme conjunto

Si usa set para empalmar cadenas, el formato de comando correspondiente es el siguiente:

set(变量名1 ${变量名1} ${变量名2} ...)

El comando anterior en realidad concatena todas las cadenas a partir del segundo parámetro y finalmente almacena el resultado en el primer parámetro. Si hay datos originales en el primer parámetro, los datos originales se sobrescribirán.

cmake_minimum_required(VERSION 3.0)
project(TEST)
set(TEMP "hello,world")
file(GLOB SRC_1 ${PROJECT_SOURCE_DIR}/src1/*.cpp)
file(GLOB SRC_2 ${PROJECT_SOURCE_DIR}/src2/*.cpp)
# 追加(拼接)
set(SRC_1 ${SRC_1} ${SRC_2} ${TEMP})
message(STATUS "message: ${SRC_1}") 

Usar empalme de listas

Si usa la lista para empalmar cadenas, el formato de comando correspondiente es el siguiente:

list(APPEND <list> [<element> ...])

listLa función del comando es setrelativamente poderosa. El empalme de cadenas es solo una de sus funciones, por lo que debemos especificar la operación que queremos realizar en la posición de su primer parámetro, lo que significa APPENDagregar datos. Los parámetros posteriores setson los mismos.

cmake_minimum_required(VERSION 3.0)
project(TEST)
set(TEMP "hello,world")
file(GLOB SRC_1 ${PROJECT_SOURCE_DIR}/src1/*.cpp)
file(GLOB SRC_2 ${PROJECT_SOURCE_DIR}/src2/*.cpp)
# 追加(拼接)
list(APPEND SRC_1 ${SRC_1} ${SRC_2} ${TEMP})
message(STATUS "message: ${SRC_1}") 

En CMake, setpuedes crear uno usando el comando list. A listes internamente un 分号;conjunto de cadenas divididas por . Por ejemplo, set(var a b c d e)el comando creará un list:a;b;c;d;e, pero lo que obtendrá cuando finalmente imprima el valor de la variable es abcde.

set(tmp1 a;b;c;d;e)
set(tmp2 a b c d e)
message(${tmp1})
message(${tmp2})

Resultado de salida:

abcde
abcde

2.8.2 Eliminación de cuerdas

fileObtenemos todos los archivos fuente en un directorio buscando en un directorio, pero algunos de los archivos fuente no son los que necesitamos, como por ejemplo:

$ tree
.
├── add.cpp
├── div.cpp
├── main.cpp
├── mult.cpp
└── sub.cpp

0 directories, 5 files

Hay cinco archivos fuente en el directorio actual, uno de los cuales main.cppes un archivo de prueba. Si queremos generar una biblioteca dinámica a partir de archivos fuente relacionados con la calculadora para que otros la usen, entonces solo necesitamos add.cpp、div.cp、mult.cpp、sub.cppestos cuatro archivos fuente. En este momento, main.cppdebe excluir los datos buscados. Para lograr esta función, también puede utilizarlist

list(REMOVE_ITEM <list> <value> [<value> ...])

A través del prototipo de comando anterior, puede ver que eliminar y agregar datos son similares, excepto que el primer parámetro ha cambiado REMOVE_ITEM.

cmake_minimum_required(VERSION 3.0)
project(TEST)
set(TEMP "hello,world")
file(GLOB SRC_1 ${PROJECT_SOURCE_DIR}/*.cpp)
# 移除前日志
message(STATUS "message: ${SRC_1}")
# 移除 main.cpp
list(REMOVE_ITEM SRC_1 ${PROJECT_SOURCE_DIR}/main.cpp)
# 移除后日志
message(STATUS "message: ${SRC_1}") 

Como puede ver, 第8行simplemente especifique el nombre del archivo que desea eliminar list. Pero asegúrese de tener en cuenta que cuando busca archivos fuente mediante el comando file, obtiene la ruta absoluta del archivo (la ruta correspondiente a cada archivo en la lista es un elemento, y todas son rutas absolutas), por lo que también debe elimínelo al eliminar Se debe especificar la ruta absoluta del archivo; de lo contrario, la operación de eliminación no se realizará correctamente.

Hay listotras funciones del comando, pero no se usan comúnmente, por lo que no las presentaré una por una con ejemplos aquí.

  1. Obtenga la longitud de la lista.

    list(LENGTH <list> <output variable>)
    
    • LENGTH: El subcomando LENGTH se utiliza para leer la longitud de la lista.

    • <list>: Lista de operaciones actuales

    • <output variable>: Variable recién creada que se utiliza para almacenar la longitud de la lista.

  2. Lea el elemento en el índice especificado en la lista. Se pueden especificar varios índices.

    list(GET <list> <element index> [<element index> ...] <output variable>)
    
    • <list>: Lista de operaciones actuales

    • <element index>: Índice del elemento de la lista

      • La numeración comienza desde 0 y el elemento con índice 0 es el primer elemento de la lista;
      • El índice también puede ser un número negativo, -1que significa el último elemento de la lista, -2que significa el penúltimo elemento de la lista, y así sucesivamente.
      • Cuando el índice (ya sea positivo o negativo) excede la longitud de la lista, se informará un error durante la ejecución.
    • <output variable>: Una variable recién creada que almacena el resultado devuelto del elemento de índice especificado, que también es una lista.

  3. Une los elementos de la lista con un conector (cadena) para formar una cadena

    list (JOIN <list> <glue> <output variable>)
    
    • <list>: Lista de operaciones actuales

    • <glue>:Conector especificado (cadena)

    • <output variable>: Variable recién creada para almacenar la cadena devuelta

  4. Encuentra si el elemento especificado existe en la lista; si no se encuentra, devuelve -1

    list(FIND <list> <value> <output variable>)
    
    • <list>: Lista de operaciones actuales

    • <value>: El elemento que debe buscarse en la lista.

    • <output variable>: Variable recién creada

      • Si la lista <list>existe <value>, devuelve <value>el índice en la lista.

      • Devuelve -1 si no se encuentra.

  5. Agregar elementos a la lista

    list (APPEND <list> [<element> ...])
    
  6. Insertar varios elementos en la posición especificada en la lista.

    list(INSERT <list> <element_index> <element> [<element> ...])
    
  7. Insertar un elemento en la lista en el índice 0

    list (PREPEND <list> [<element> ...])
    
  8. Eliminar el último elemento de la lista.

    list (POP_BACK <list> [<out-var>...])
    
  9. Eliminar el primer elemento de la lista.

    list (POP_FRONT <list> [<out-var>...])
    
  10. Elimina el elemento especificado de la lista.

    list (REMOVE_ITEM <list> <value> [<value> ...])
    
  11. Elimina el elemento en el índice especificado de la lista.

    list (REMOVE_AT <list> <index> [<index> ...])
    
  12. Eliminar elementos duplicados de la lista

    list (REMOVE_DUPLICATES <list>)
    
  13. cambio de lista

    list(REVERSE <list>)
    
  14. Clasificación de listas

    list (SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>])
    
    • COMPARE: Especifique el método de clasificación. Los siguientes valores están disponibles:

      • STRING: Ordenar en orden alfabético, que es el método de clasificación predeterminado.
      • FILE_BASENAME: Si se trata de una serie de nombres de ruta, se utilizará el nombre base para ordenar.
      • NATURAL: Ordenar usando el orden de los números naturales
    • CASE: Indique si distingue entre mayúsculas y minúsculas. Los siguientes valores están disponibles:

      • SENSITIVE: Ordene distinguiendo entre mayúsculas y minúsculas, que es el valor predeterminado.
      • INSENSITIVE: Ordenar sin distinguir entre mayúsculas y minúsculas
    • ORDER: especifique el orden de clasificación. Los siguientes valores están disponibles:

      • ASCENDING: Ordenar en orden ascendente, valor predeterminado

      • DESCENDING: Ordenar en orden descendente

2.9 Definición de macros

Al probar el programa, podemos agregar algunas definiciones de macro al código y usar estas macros para controlar si los códigos surten efecto, como se muestra a continuación:

#include <stdio.h>
#define NUMBER  3

int main()
{
    int a = 10;
#ifdef DEBUG
    printf("我是一个程序猿, 我不会爬树...\n");
#endif
    for(int i=0; i<NUMBER; ++i)
    {
        printf("hello, GCC!!!\n");
    }
    return 0;
} 

La macro se juzga en la séptima línea del programa DEBUG. Si la macro está definida, la salida del registro se realizará en la octava línea. Si la macro no está definida, la octava línea equivale a estar comentada, por lo que no se puede visto al final para registrar entradas y salidas ( esta macro no está definida en el código anterior ).

Para que las pruebas sean más flexibles, no podemos definir esta macro en el código, sino definirla durante la prueba. Una forma es especificarla gcc/g++en el comando, de la siguiente manera:

$ gcc test.c -DDEBUG -o app

Especifique el nombre de la macro que se definirá gcc/g++mediante parámetros en el comando , lo que equivale a definir una macro en el código con el nombre .-DDEBUG

CMakeTambién podemos hacer cosas similares en , y el comando correspondiente se llama add_definitions:

add_definitions(-D宏名称)

Escriba uno para el archivo fuente anterior CMakeLists.txt, el contenido es el siguiente:

cmake_minimum_required(VERSION 3.0)
project(TEST)
# 自定义 DEBUG 宏
add_definitions(-DDEBUG)
add_executable(app ./test.c) 

De esta manera, se puede generar la octava línea de registro en el código anterior.

3. Macros predefinidas

La siguiente lista ha compilado algunas CMakemacros de uso común para usted:

Macro Función
PROJECT_SOURCE_DIR El directorio que sigue inmediatamente al comando cmake suele ser el directorio raíz del proyecto.
PROJECT_BINARY_DIR Directorio donde se ejecuta el comando cmake
CMAKE_CURRENT_SOURCE_DIR La ruta donde se encuentra el CMakeLists.txt actualmente procesado
CMAKE_CURRENT_BINARY_DIR directorio de compilación de destino
EXECUTABLE_OUTPUT_PATH Redefina la ubicación de almacenamiento del archivo ejecutable binario de destino [no apto para bibliotecas estáticas ]
LIBRARY_OUTPUT_PATH Redefinir la ubicación de almacenamiento del archivo de la biblioteca de enlaces de destino
NOMBRE DEL PROYECTO Devuelve el nombre del proyecto definido a través de la directiva PROJECT
CMAKE_BINARY_DIR La ruta de construcción real del proyecto, suponiendo que la construcción se realiza en buildel directorio, luego se obtiene la ruta de este directorio

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.

Supongo que te gusta

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