CMake所学

向大佬lyf学习,先把其8服务器中所授fine

文章目录

  • 前言
  • [一、CMakeList.txt 命令](#一、CMakeList.txt 命令)
    • [1.1 最外层CMakeLists](#1.1 最外层CMakeLists)
      • [1.1.1 cmake_minimum_required()](#1.1.1 cmake_minimum_required())
      • [1.1.2 project()](#1.1.2 project())
      • [1.1.3 set()](#1.1.3 set())
      • [1.1.4 add_subdirectory()](#1.1.4 add_subdirectory())
    • [1.2. 内层CMakeLists](#1.2. 内层CMakeLists)
      • [1.2.1 开头几行的重复](#1.2.1 开头几行的重复)
      • [1.2.2 find_package()](#1.2.2 find_package())
      • [1.2.3 find_path()](#1.2.3 find_path())
      • [1.2.4 file()](#1.2.4 file())
      • [1.2.5 add_library()](#1.2.5 add_library())
      • [1.2.6 target_include_directories()](#1.2.6 target_include_directories())
      • [1.2.7 set_target_properties()](#1.2.7 set_target_properties())
      • [1.2.8 install()命令](#1.2.8 install()命令)
      • [1.2.9 target_link_libraries](#1.2.9 target_link_libraries)
  • [二、clangd 如何使用 compile_commands.json](#二、clangd 如何使用 compile_commands.json)
    • [2.1 compile_commands.json](#2.1 compile_commands.json)
    • [2.2 clangd利用compile_commands.json](#2.2 clangd利用compile_commands.json)
  • 三、静态库和动态库
    • [3.1 静态库(Static Library)](#3.1 静态库(Static Library))
    • [3.2 动态库(Shared Library 或 Dynamic Library)](#3.2 动态库(Shared Library 或 Dynamic Library))
  • 四、targets(目标)的类型
    • [4.1 可执行文件(Executable Target)](#4.1 可执行文件(Executable Target))
    • [4.2 静态库(Static Library Target)](#4.2 静态库(Static Library Target))
    • [4.3 动态库(Shared Library Target)](#4.3 动态库(Shared Library Target))
    • [4.4 模块库(Module Library Target)](#4.4 模块库(Module Library Target))
    • [4.5 接口库(Interface Library Target)](#4.5 接口库(Interface Library Target))
    • [4.6 自定义目标(Custom Target)](#4.6 自定义目标(Custom Target))
    • [4.7 导入目标(Imported Target)](#4.7 导入目标(Imported Target))
    • [4.8 外部目标(External Project Target)](#4.8 外部目标(External Project Target))
    • [4.9 ALIAS 目标](#4.9 ALIAS 目标)
    • [4.10 OBJECT 库目标(Object Library Target)](#4.10 OBJECT 库目标(Object Library Target))
    • [4.11 UTILITY 目标](#4.11 UTILITY 目标)
  • 总结

前言

不能光写代码,也需要提升宏观的工程能力,

还是那句话,深度。


一、CMakeList.txt 命令

1.1 最外层CMakeLists

1.1.1 cmake_minimum_required()

指定项目所需的 CMake 最低版本。如果当前 CMake 版本低于指定版本,CMake 会发出错误并停止配置过程。

通过指定最低版本,可以确保项目使用的 CMake 功能和特性在该版本中可用,从而避免因版本不兼容而导致的问题。

1.1.2 project()

定义一个项目的名称和相关信息

project() 命令会生成一些有用的变量,如:

PROJECT_NAME: 项目名称。

PROJECT_SOURCE_DIR: 项目源代码的根目录。

PROJECT_BINARY_DIR: 项目构建目录。

1.1.3 set()

用于为 CMake 脚本中的变量赋值,可以为 CMake 自带的变量(如 CMAKE_CXX_FLAGS_DEBUG、CMAKE_BUILD_TYPE)或用户自定义的变量赋值

典型的用法是 set(变量名 值),通过这种方式将某个值赋给 CMake 中的某个变量。

bash 复制代码
set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb -DDEBUG")

如此处,是将"$ENV{CXXFLAGS} -O0 -Wall -g -ggdb -DDEBUG"赋给CMAKE_CXX_FLAGS_DEBUG ,CMAKE_CXX_FLAGS_DEBUG 是CMake内部变量,是 CMake 提供的一个预定义变量,它专门用于存储在 Debug 构建类型 下(即 CMAKE_BUILD_TYPE 设置为 Debug 时)的编译器选项

ENV是系统环境变量,在linux中可以通过printenv来查看env信息,如果 $ ENV{CXXFLAGS}没找到CXXFLAGS的话,就会返回空字符串从而直接将-O0 -Wall -g -ggdb -DDEBUG赋给CMAKE_CXX_FLAGS_DEBUG

这句话在 CMake 中的作用是为 C++ 编译器设置在 Debug 模式下的编译标志

"$ENV{CXXFLAGS} -O0 -Wall -g -ggdb -DDEBUG":

  • $ENV{CXXFLAGS}:这是 CMake 语法,用于从操作系统的环境变量 CXXFLAGS 中读取值。如果 CXXFLAGS 存在,它的值会被插入到这个位置;如果不存在,这部分将为空。
  • -O0:关闭优化。Debug 模式下,编译器不会优化代码,这样可以让代码结构尽量保持原样,以便调试。
  • -Wall:启用所有常规警告。编译器会输出所有常见的潜在问题警告,帮助开发者发现代码中的潜在错误。
  • -g:生成调试信息。这会让编译器在生成的可执行文件中包含调试符号,以便在调试器(如 GDB)中使用。
  • -ggdb:为 GDB 调试器生成更多的调试信息,优化调试体验。
    -DDEBUG:定义 DEBUG 宏。在代码中,可能会有一些只在 Debug 模式下执行的代码,通过 #ifdef DEBUG 进行条件编译,这个宏可以帮助启用这些代码块。

1.1.4 add_subdirectory()

add_subdirectory(src) 是 CMake 中的一个命令,用于将指定的子目录(这里是 src 目录)添加到当前 CMake 项目中。CMake 会递归地处理该子目录中的 CMakeLists.txt 文件,并将该目录中的构建目标、编译选项等内容合并到主项目中

1.2. 内层CMakeLists

1.2.1 开头几行的重复

内存CMakeLists.txt中开头几行,一般会有与外层开始几行一样或者类似的重复,如下:

  • cmake_minimum_required 的重复

    目的:每个 CMakeLists.txt 文件都应该有一个 cmake_minimum_required() 调用,哪怕在父目录已经调用过。其目的是确保每个独立的 CMakeLists 文件都知道需要的最低版本。

    独立性:虽然项目是从外层的 CMakeLists.txt 开始构建的,但 CMake 是递归处理 add_subdirectory() 中的子目录。每个 CMakeLists.txt 文件都是相对独立的模块,有可能在不同上下文中单独运行。例如,如果你单独运行 src 目录中的 CMakeLists 文件(例如开发中测试子模块时),cmake_minimum_required() 能确保它使用的是正确版本的 CMake。

  • project() 的重复

    项目分层管理:尽管外层的 CMakeLists 文件已经定义了项目名称和相关配置,但每个子目录可能被视为一个独立的逻辑模块,尤其是当项目复杂时。通过在 src 目录的 CMakeLists 文件中定义自己的 project(),可以给该子目录赋予自己的版本号和名称。这在大型项目中有助于管理不同子模块的独立性。

    版本号独立性:project() 定义的项目版本号可以在不同模块中有所区别。例如,外层的项目可能有一个总版本号,而内部的子模块(如 CUDA-GEMM-Algorithms)可以有独立的版本号,方便模块化管理。

    语言设置:LANGUAGES CXX CUDA 的定义也需要在每个 CMakeLists.txt 中明确指定,因为不同的子模块可能使用不同的语言。虽然你的项目同时支持 C++ 和 CUDA,但其他项目或子模块可能会使用不同的语言设置。

  • set(CMAKE_CXX_STANDARD 17) 的重复

    确保一致性:对于每个 CMakeLists.txt 文件中明确指定 C++ 标准(如 C++17),可以确保即使子目录的 CMakeLists 在不同的环境下被单独使用,依然会遵循相同的标准。尽管顶层 CMakeLists 中已经指定了 C++17 标准,但每个子模块如果有自己的设置,就可以防止标准的遗失或错误继承。

    防止未来修改:如果你不在子目录的 CMakeLists 中显式设置 CMAKE_CXX_STANDARD,顶层的设置有可能在某些复杂的构建配置中被覆盖或丢失。为了确保子目录构建时依然使用正确的 C++ 标准,最好在每个模块中都显式设置。

独立构建支持:有些项目希望支持在不同目录下独立构建子模块。比如你可能只想单独编译 src 目录下的内容,而不是整个项目。通过在子目录中定义 cmake_minimum_required、project() 和其他配置,你可以独立构建和测试这个子模块,而不依赖于外层的 CMakeLists.txt。

模块化开发:在模块化开发的场景中,不同模块可以由不同的开发团队或个人维护,甚至单独发布。为了确保每个模块都能独立运行和测试,重复这些命令可以确保一致性和模块化开发的独立性。

1.2.2 find_package()

find_package(CUDAToolkit REQUIRED)

REQUIRED:这个关键字告诉 CMake,如果找不到指定的包,构建过程应当立即停止并报错。

如果不加 REQUIRED,CMake 会继续执行,但 CUDAToolkit_FOUND 变量将被设置为 FALSE,你可以在脚本中自己处理找不到包的情况。

如果加上 REQUIRED,则找不到包时,CMake 将会自动停止并抛出错误信息,提示 CUDA 工具包是必需的,无法继续构建。

find_package查找方法有四层路径:

1.系统路径,CMake 会根据操作系统的标准路径查找。(在 Linux 中,默认查找 /usr/local/cuda、/usr/local/lib 等目录。

在 Windows 中,默认查找 C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA。)

2.环境变量,CMake 也会检查以下环境变量:CUDA_PATH:NVIDIA 通常会将 CUDA 安装路径设置为 CUDA_PATH 环境变量。CMake 会读取这个变量以查找 CUDA 工具包的安装路径。PATH、LD_LIBRARY_PATH、CMAKE_PREFIX_PATH:这些变量可能包含与 CUDA 相关的路径。

3.CMake 模块路径:CMake 可能会在 CMAKE_MODULE_PATH 或 CMAKE_PREFIX_PATH 中指定的路径下查找 CUDAToolkit。

4.NVIDIA 提供的配置文件:CMake 会使用 CUDAToolkitConfig.cmake 文件,这是 NVIDIA 提供的官方配置文件,用于帮助 CMake 定位 CUDA 工具包。

find_package()找到包之后会加载包的配置(注意,不是加载包,只是其配置)

通过加载包的配置,而将包的路径、库、头文件等信息存储到一些CMake变量中。

可以通过以target_link_libraries(my_target PRIVATE ${CUDAToolkit_LIBRARIES})等方式直接引用这些变量

同时find_package()也会通过加载包的配置而自动导入一些常用的目标(不是所有,如果你要用的没有被导入,需要手动引入,而不能直接target_link_lib...)

1.2.3 find_path()

find_path() 是 CMake 中的一个命令,专门用于查找指定文件的路径,通常用于查找头文件的包含目录。它会在系统中搜索某个特定的路径,找到指定的文件,然后将该文件所在的目录路径存储到一个 CMake 变量中,以便后续使用

示例:

bash 复制代码
find_path(CUDA_GEMM_INCLUDE_DIRS cuda_gemm_utils.hpp HINTS ${CMAKE_SOURCE_DIR}/include)

此中含义是

在${CMAKE_SOURCE_DIR}/include里面查找cuda_gemm_utils.hpp文件,若找到,则将文件的目录路径存储到CUDA_GEMM_INCLUDE_DIRS (是一个自定义的CMake变量)中。

find_path()(动态查找判断并赋值)相当于set()(静态赋值)的增强版本,在赋值之前多了一个查找和验证的逻辑,并且存入的不是后面的字符串,而是找到文件的目录路径。

1.2.4 file()

file(GLOB ...) 是 CMake 中用于文件查找和模式匹配的命令,它可以根据给定的文件模式来查找匹配的文件,并将匹配的文件列表存储在变量中。

bash 复制代码
file(GLOB CUDA_GEMM_HEADERS ${CMAKE_SOURCE_DIR}/include/*.hpp ${CMAKE_SOURCE_DIR}/include/*.cuh)
  • file(GLOB CUDA_GEMM_HEADERS ...):

    file(GLOB ...) 命令用于查找符合条件的文件,并将结果存储在变量 CUDA_GEMM_HEADERS 中。

    CUDA_GEMM_HEADERS 是自定义的变量名,用来存储查找到的文件列表。

  • ${CMAKE_SOURCE_DIR}/include/* .hpp 和 ${CMAKE_SOURCE_DIR}/include/* .cuh:

    GLOB 的第二个参数是文件模式,在这里指定了两个文件模式:

    ${CMAKE_SOURCE_DIR}/include/* .hpp:表示查找项目根目录下 include 文件夹中所有以 .hpp 结尾的头文件。

    ${CMAKE_SOURCE_DIR}/include/* .cuh:表示查找项目根目录下 include 文件夹中所有以 .cuh 结尾的 CUDA 头文件。

    CMAKE_SOURCE_DIR 是 CMake 内置的变量,表示项目的根目录。

在此命令中,CMake 会根据你提供的文件模式查找匹配的 .hpp 和 .cuh 文件,并将这些文件的路径存储在 CUDA_GEMM_HEADERS 变量中。

file()(动态查找判断并赋值)相当于set()(静态赋值)的增强版本,在赋值之前多了一个查找和判断的逻辑并且存入的不是后面的字符串,而是找到匹配文件的文件列表。

1.2.5 add_library()

add_library() 命令用于定义和创建库目标。add_library(cuda_gemm) 命令会创建一个名为 cuda_gemm 的库目标。这个库可以是静态库(.a)、动态库(.so 或 .dll),或者模块库(.mod),具体类型由自己指定。

基本用法:

bash 复制代码
add_library(<name> [STATIC | SHARED | MODULE] [sources...])
  • STATIC:静态库(.a 或 .lib)。默认如果未指定类型。
  • SHARED:动态库(.so 或 .dll)。
  • MODULE:模块库,主要用于动态加载的插件库。

1.2.6 target_include_directories()

target_include_directories() 是 CMake 用来为指定的目标(可执行文件或库)添加头文件的搜索路径的命令。通过这个命令,编译器会知道在哪里查找头文件(#include 的文件)

基本用法:

bash 复制代码
target_include_directories(<target> [PUBLIC | PRIVATE | INTERFACE] <directories>...)
  • < target>:指定目标名称(库或可执行文件)
  • [PUBLIC | PRIVATE | INTERFACE]:指定包含路径的可见性:
    PRIVATE:仅对当前目标有效,依赖此目标的其他目标不能继承这些路径。
    INTERFACE:仅对依赖此目标的其他目标有效,当前目标自身不会使用这些路径。
    PUBLIC:当前目标和依赖此目标的其他目标都可以使用这些包含路径。
  • < directories>:一个或多个要添加的包含目录路径。

示例:

bash 复制代码
add_library( cuda_gemm  SHARED src )
target_include_directories(cuda_gemm PUBLIC ${CUDA_GEMM_INCLUDE_DIRS})

不一定非要是库目标,也可以是可执行目标

1.2.7 set_target_properties()

set_target_properties() 是 CMake 中用于设置目标属性的命令,它可以为指定的目标(例如可执行文件或库)设置各种属性。

bash 复制代码
set_target_properties(cuda_gemm PROPERTIES CUDA_ARCHITECTURES native)

set_target_properties(cuda_gemm ...):指定要设置属性的目标是 cuda_gemm,这是之前用 add_library(cuda_gemm ...) 创建的 CUDA 库目标。

PROPERTIES:表示接下来设置的内容是 cuda_gemm 目标的属性。

CUDA_ARCHITECTURES native:设置属性的名称和对应的值。在这个例子中,你设置的是 CUDA_ARCHITECTURES 属性,其值为 native。

cmake会根据project、add library等命令识别到cuda_gemm是一个cuda目标,而在find_package的时候cmake会加载包配置文件从而导入一系列cuda目标,也会给这些cuda目标引入一系列与CUDA相关的属性,就包括CUDA_ARCHITECTURES。

CMake在编译CUDA目标时会把CUDA_ARCHITECTURES属性给NVCC使用,告诉NVCC是为什么架构生成的代码

native是不指定架构号,自动检测当前系统上的GPU,并生成相应代码。

1.2.8 install()命令

install() 命令是 CMake 用来将生成的二进制文件、库文件、头文件等安装到指定位置的命令。它用于定义项目的安装规则,通常用于在构建系统中,将生成的文件拷贝到系统或指定的安装目录中,方便其他用户或程序使用。

基本语法:

bash 复制代码
install(TARGETS <target>... [EXPORT <export-name>]
        [ARCHIVE | LIBRARY | RUNTIME | OBJECTS | FRAMEWORK | BUNDLE]
        [DESTINATION <dir>] 
        [PERMISSIONS permissions...]
        [CONFIGURATIONS [Debug|Release|...]]
        [COMPONENT <component>])
        
install(FILES <file>... DESTINATION <dir> [PERMISSIONS permissions...])
install(DIRECTORY <dir>... DESTINATION <dir> [PERMISSIONS permissions...])
  • TARGETS:指定目标(可执行文件、库等)进行安装。
  • FILES:指定具体的文件安装。
  • DIRECTORY:将整个目录内容进行安装。
  • DESTINATION:定义安装的目标路径,即文件安装到哪个位置。
  • PERMISSIONS:可选参数,定义安装文件的权限。
  • CONFIGURATIONS:指定安装哪些构建配置(如 Debug, Release)。
bash 复制代码
install(TARGETS cuda_gemm DESTINATION lib)

将cuda_gemm目标安装到lib里面。

install() 命令中的 DESTINATION 选项决定了文件的安装位置,但这个位置通常是相对于 CMake 安装前缀 的默认路径。CMake 的安装前缀可以通过 CMAKE_INSTALL_PREFIX 变量指定,默认情况下,这个前缀通常是 /usr/local(Linux 和 macOS)或 C:/Program Files(Windows)。

install() 命令在 CMake 中的主要作用是定义项目的安装规则,但它并不是项目构建和运行的必需部分。通常情况下,没有 install() 命令,程序依然可以正常编译、运行和调试,但 install() 提供了项目发布、部署或在其他环境中使用你的软件时的重要功能。

要在 /usr/local/lib 和 /usr/local/include 里面找到相应的东西的话,可能得先确保你是否有相关读写权限。

target_link_libraries() 是 CMake 中用于将一个目标(通常是可执行文件或库)与其他库链接的命令。它将指定的库添加到目标的链接过程中,使目标能够使用这些库中的符号和功能。

基本语法:

bash 复制代码
target_link_libraries(<target> [PRIVATE | PUBLIC | INTERFACE] <lib1> <lib2> ...)
  • < target>:需要链接库的目标,通常是通过 add_library() 或 add_executable() 定义的目标。
  • PRIVATE、PUBLIC、INTERFACE:指定链接库的可见性(控制库链接和传递的范围)。
  • PRIVATE:库只会被当前目标使用,依赖于该目标的其他目标不会继承这些链接库。
  • PUBLIC:库不仅会被当前目标使用,依赖该目标的其他目标也会继承这些链接库。
  • INTERFACE:库不会直接链接到当前目标,但依赖该目标的其他目标会继承这些库。

二、clangd 如何使用 compile_commands.json

2.1 compile_commands.json

compile_commands.json 中的每条记录都包含三个重要的字段:

directory:编译时的工作目录。

command:完整的编译命令,包含编译器、编译选项、头文件搜索路径、宏定义等。

file:源文件的路径。

这三个字段的作用如下:

directory:表示运行编译命令时的当前目录。编译器通常会使用这个目录作为基准来解析相对路径的头文件或者依赖文件路径。clangd 通过这个字段确保在正确的目录下解析文件路径,正确查找头文件等依赖项。

command:这个字段是实际用于编译该源文件的完整命令行。它包括:

编译器路径(如 /usr/bin/g++),clangd 使用这个信息来模拟真实的编译环境。

编译选项(如 -Wall, -O2, -g),这些选项会影响 clangd 如何解析代码。

头文件包含路径(如 -I/path/to/includes),clangd 通过这些选项能够找到头文件,并理解项目的依赖关系。

宏定义(如 -DDEBUG),这些定义会影响代码的编译和行为,clangd 使用这些定义来精确解析带有条件编译的代码。

file:表示当前需要编译的源文件。clangd 通过这个字段知道要解析和分析哪个源文件。

2.2 clangd利用compile_commands.json

  1. 解析 compile_commands.json 文件

    当你在某个项目中使用 clangd 时,clangd 会首先查找并解析构建目录中的 compile_commands.json 文件。每一条记录代表一个源文件的编译命令,clangd 会根据需要处理不同的源文件。

  2. 智能跳转

    智能跳转是指当你在编辑器中点击某个符号(如函数、变量、类名等)时,clangd 能够跳转到符号的定义或声明处。为了做到这一点,clangd 需要完整解析代码的上下文,这就依赖于正确的编译配置。具体过程如下:

    通过 compile_commands.json 中的 file 字段找到对应的源文件。

    从 directory 字段得知该文件的工作目录,并进入该目录,确保相对路径能够正确解析。

    通过 command 字段获取完整的编译命令,解析头文件路径、宏定义等编译选项。

    使用这些信息构建编译环境,然后解析源文件,构建符号表。

    当用户点击某个符号时,clangd 利用解析的符号表找到其定义或声明,并跳转到对应的位置。

  3. 代码补全

    代码补全功能依赖于对代码的准确解析。在编辑器中,当你输入一个变量或对象名称时,clangd 可以根据上下文提供可能的补全项。为了提供精确的补全,clangd 必须知道项目的完整编译过程。以下是具体步骤:

    clangd 从 compile_commands.json 中提取与当前文件相关的编译命令(通过 file 字段匹配当前正在编辑的文件)。

    它使用 command 字段中的编译选项来理解该文件是如何编译的,尤其是头文件搜索路径和宏定义。

    通过 directory 字段,clangd 确保在正确的目录中解析相对路径的头文件。

    利用这些信息,clangd 可以解析当前文件和其依赖的头文件,分析代码结构。

    当用户请求代码补全时,clangd 根据已有的符号表和代码上下文生成可能的补全建议。

  4. 错误诊断和静态分析

    通过 compile_commands.json 提供的信息,clangd 也可以对代码进行更精确的错误诊断。例如,某些编译选项(如宏定义、头文件搜索路径)可能会影响到编译器能否正确处理某段代码。clangd 使用这些选项来确保它分析代码时的环境与实际编译环境一致,这样可以帮助开发者更早地发现编译错误或潜在问题。

三、静态库和动态库

3.1 静态库(Static Library)

扩展名:静态库在 Linux 和 macOS 中通常是 .a 文件,在 Windows 中是 .lib 文件。

编译时链接:静态库在编译阶段被直接链接到可执行文件中。在生成可执行文件时,静态库的代码被完整地拷贝到最终的可执行文件中。

  • 优点:

    独立性:静态链接的可执行文件不依赖于外部的库文件,在运行时不需要额外的库文件。

    运行效率:静态库在运行时无需动态加载,运行速度更快,因为所有库代码都已经编译进可执行文件中。

  • 缺点:

    文件体积大:由于库的代码被拷贝到可执行文件中,生成的可执行文件通常比动态链接方式大。

    更新不便:如果静态库有更新,所有依赖该库的可执行文件都需要重新编译。

    CMake 中创建静态库: 使用 add_library() 命令,并将 STATIC 作为参数:

bash 复制代码
add_library(my_static_lib STATIC lib_source1.cpp lib_source2.cpp)

这会生成一个静态库 my_static_lib.a(在 Linux/macOS)或 my_static_lib.lib(在 Windows)。

3.2 动态库(Shared Library 或 Dynamic Library)

扩展名:动态库在 Linux 中是 .so 文件,在 macOS 中是 .dylib 文件,在 Windows 中是 .dll 文件。

运行时链接:动态库在运行时才被加载到内存中。可执行文件只包含对动态库的引用,而不直接包含库的代码。在可执行文件运行时,操作系统会加载动态库,并链接到可执行文件中。

  • 优点:

    节省空间:由于多个可执行文件可以共享一个动态库,生成的可执行文件较小,而且系统内存中也只需加载一次动态库。

    更新方便:动态库更新时,不需要重新编译可执行文件。只需替换动态库即可使所有依赖的可执行文件使用新版库。

  • 缺点:

    运行时依赖:可执行文件在运行时依赖于动态库,因此动态库必须存在于系统的库路径中。如果动态库缺失或版本不兼容,可执行文件将无法运行。

    加载开销:在运行时加载和链接动态库可能会引入一定的性能开销。

    CMake 中创建动态库: 使用 add_library() 命令,并将 SHARED 作为参数:

bash 复制代码
add_library(my_shared_lib SHARED lib_source1.cpp lib_source2.cpp)

这会生成一个动态库 my_shared_lib.so(在 Linux)或 my_shared_lib.dll(在 Windows)。

四、targets(目标)的类型

4.1 可执行文件(Executable Target)

命令:add_executable( [sources...])

描述:可执行文件 target 用于生成最终的二进制可执行文件。这个 target 通常包含源文件,并通过编译、链接生成一个可以运行的可执行程序。

bash 复制代码
add_executable(my_executable main.cpp)

4.2 静态库(Static Library Target)

命令:add_library( STATIC [sources...])

描述:静态库 target 生成一个静态库(.a 或 .lib)。静态库在编译阶段被打包到最终的可执行文件中,不需要在运行时加载。

bash 复制代码
add_library(my_static_lib STATIC lib1.cpp lib2.cpp)

4.3 动态库(Shared Library Target)

命令:add_library( SHARED [sources...])

描述:动态库 target 生成一个动态链接库(.so、.dll 或 .dylib)。动态库在运行时加载,多个可执行文件可以共享同一份库。

bash 复制代码
add_library(my_shared_lib SHARED lib1.cpp lib2.cpp)

4.4 模块库(Module Library Target)

命令:add_library( MODULE [sources...])

描述:模块库 target 生成一个模块库。模块库在运行时动态加载,但不会自动链接到其他目标。它通常用于插件系统或自定义动态加载场景。

bash 复制代码
add_library(my_module MODULE plugin.cpp)

4.5 接口库(Interface Library Target)

命令:add_library( INTERFACE)

描述:接口库 target 不生成任何二进制文件。它只定义了一组编译选项、包含路径、依赖库等,供其他 targets 使用。接口库主要用于组织和复用编译选项和依赖。

bash 复制代码
add_library(my_interface_lib INTERFACE)
target_include_directories(my_interface_lib INTERFACE include/)

4.6 自定义目标(Custom Target)

命令:add_custom_target( [ALL] [COMMAND command1 [ARGS ...]] ...)

描述:自定义目标 target 不生成可执行文件或库,它定义了一些自定义命令或操作(例如脚本执行、文件生成等)。add_custom_target 可以用来创建不依赖于源代码的构建任务。

bash 复制代码
add_custom_target(run_tests ALL
  COMMAND my_executable --run-tests
)

4.7 导入目标(Imported Target)

命令:add_library( IMPORTED) 或 add_executable( IMPORTED)

描述:导入目标 target 表示一个已经存在的库或可执行文件,而不是由 CMake 构建的目标。IMPORTED 目标通常用于链接外部的预编译库或工具,它不会被 CMake 构建,但可以链接到其他 CMake targets。

bash 复制代码
add_library(my_imported_lib STATIC IMPORTED)
set_target_properties(my_imported_lib PROPERTIES
  IMPORTED_LOCATION /path/to/libmy_imported_lib.a
)

注意:find_package() 加载包配置之后会导入的目标的类型就是导入目标类型

4.8 外部目标(External Project Target)

命令:通过 ExternalProject_Add() 命令创建。

描述:外部项目 target 是 CMake ExternalProject 模块的一部分,用于从外部构建其他项目。可以用它来自动下载、配置、编译和安装外部项目。

bash 复制代码
include(ExternalProject)
ExternalProject_Add(my_external_project
  GIT_REPOSITORY https://github.com/user/repo.git
  GIT_TAG main
  CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
)

4.9 ALIAS 目标

命令:add_library( ALIAS ) 或 add_executable( ALIAS )

描述:ALIAS 目标是已有目标的别名,不能直接创建新的构建产物。它为现有目标提供了一个新的名称,可以更方便地引用该目标。

bash 复制代码
add_library(my_lib STATIC lib.cpp)
add_library(my_alias ALIAS my_lib)
target_link_libraries(my_target my_alias)

4.10 OBJECT 库目标(Object Library Target)

命令:add_library( OBJECT [sources...])

描述:对象库 target 生成的是编译后的目标文件(.o 或 .obj),而不是库文件。它可以在多个 target 之间共享编译后的对象文件,而不生成最终的库。

bash 复制代码
add_library(my_object_lib OBJECT file1.cpp file2.cpp)
target_link_libraries(my_target PRIVATE $<TARGET_OBJECTS:my_object_lib>)

4.11 UTILITY 目标

描述:UTILITY 目标是 makefile 或 Ninja 文件中的概念,通常用于表示一些辅助的目标,比如 all、clean。它们是构建工具的内部目标,CMake 并没有专门的命令来定义 UTILITY 目标。


总结

又臭又长,希望可以一劳提高效率。

相关推荐
计时开始不睡觉11 分钟前
【微信小程序前端开发】入门Day03 —— 页面导航、事件、生命周期、WXS 脚本及自定义组件
开发语言·前端·javascript·微信小程序
爱科技的幸运小鸭15 分钟前
运动耳机哪个牌子的好?5大质量不凡的运动耳机测评力荐!
人工智能·科技·数码相机·学习·云计算·生活
小媛早点睡19 分钟前
day02笔试练习
java·开发语言·算法
倔强的石头10621 分钟前
【C语言指南】数据类型详解(下)——自定义类型
c语言·开发语言·html
Stark、24 分钟前
《数据结构》--链表【包含跳表概念】
开发语言·数据结构·c++
冷白白26 分钟前
【C++】单例模式
开发语言·c++·单例模式·c
薰衣草233328 分钟前
java部分总结
java·开发语言
s_little_monster30 分钟前
【C++】多态(下)
开发语言·c++·经验分享·笔记·学习
杰哥在此39 分钟前
Python知识点:如何使用Raspberry Pi与Python进行边缘计算
开发语言·python·面试·编程·边缘计算
北沐xxx1 小时前
VMware虚拟机Centos操作系统——配置docker,运行本地打包的镜像,进入conda环境(vmware,docker新手小白)
开发语言·python·docker·centos·conda