Cmake 中的重要概念

news/2024/6/3 19:11:20 标签: c++, 机器人, 自动驾驶, stm32, 物联网, Cmake

文章目录

  • 前言
  • 一、重要概念——目标(Target)
    • 1.1 使用要求
    • 1.2 使用目标指定优化库或调试库
    • 1.3 对象库
    • 1.4 源文件
    • 1.5 目录、测试和属性
  • 二、Policies
    • 2.1 设计目标
    • 2.2 Setting Policies
  • 三、Modules(模块,组)
    • 3.1 使用模块
      • 3.1.2 查找模块
      • 3.1.3 公用模块
  • 四、Installing Files
  • `少侠请留步,点赞、收藏、关注`


前言

本章将介绍 CMake 的主要概念。在开始使用 CMake 时,您会遇到各种概念,如目标、生成器和命令。了解这些概念将为您提供创建有效 CMakeLists 文件所需的工作知识。许多 CMake 对象(如目标、目录和源文件)都有与之相关的属性。属性是附加到特定对象的键值对。访问属性的最通用方法是使用 set_property 和 get_property 命令。这些命令允许您设置或获取 CMake 中任何具有属性的对象的属性。有关支持的属性列表,请参阅 cmake-properties 手册。通过命令行运行 cmake 并使用 --help-property-list 选项,可获得 CMake 支持的全部属性列表。


一、重要概念——目标(Target)

最重要的一项可能是目标。目标代表 CMake 构建的可执行文件、库和实用程序。每一条 add_library、add_executable 和 add_custom_target 命令都会创建一个目标。例如,下面的命令将创建一个名为 "foo "的目标,它是一个静态库,源文件为 foo1.c 和 foo2.c。

add_library(foo STATIC foo1.c foo2.c)

现在,"foo "这个名字可以在项目的其他地方作为库名使用,CMake 知道如何在需要时将这个名字扩展到库中。库可以声明为 STATIC、SHARED、MODULE 等特定类型,也可以不声明。STATIC 表示该库必须作为静态库构建。同样,SHARED 表示必须作为共享库构建。MODULE 表示必须创建该库,以便动态加载到可执行文件中。在许多平台上,模块库都是作为共享库实现的,但并非所有平台都是如此。因此,CMake 不允许其他目标链接到模块。如果这些选项都未指定,则表明该库可作为共享或静态库构建。在这种情况下,CMake 会使用变量 BUILD_SHARED_LIBS 的设置来决定该库应该是共享的还是静态的。如果没有设置该变量,CMake 将默认联编静态库。

同样,可执行文件也有一些选项。默认情况下,可执行文件是一个具有主入口点的传统控制台应用程序。可以指定一个 WIN32 选项,在 Windows 系统上请求使用 WinMain 入口点,而在非 Windows 系统上保留 main 入口点。

除了存储其类型外,目标程序还会跟踪一般属性。这些属性可以使用 set_target_properties 和 get_target_property 命令,或更通用的 set_property 和 get_property 命令进行设置和检索。其中一个有用的属性是 LINK_FLAGS,用于指定特定目标的附加链接标志。目标存储了一个链接所针对的库列表,可以使用 target_link_libraries 命令进行设置。传入该命令的名称可以是库、库的完整路径或 add_library 命令中的库名。目标还存储了链接时要使用的链接目录,以及构建后要执行的自定义命令。

1.1 使用要求

CMake 还会传播链接库目标中的 “使用要求”。使用要求会影响 中源代码的编译。它们由链接目标上定义的属性指定。

例如,要指定链接到库时所需的包含目录,可以使用下面的方法:

add_library(foo foo.cxx)
target_include_directories(foo PUBLIC
                           "${CMAKE_CURRENT_BINARY_DIR}"
                           "${CMAKE_CURRENT_SOURCE_DIR}"
                           )

现在,任何指向目标 foo 的链接都会自动将 foo 的二进制文件和源代码作为包含目录。通过 "使用要求 "引入的 include 目录的顺序将与 target_link_libraries 调用中目标的顺序一致。

对于 CMake 创建的每个库或可执行文件,它都会使用 target_link_libraries 命令跟踪该目标所依赖的所有库。例如

add_library(foo foo.cxx)
target_link_libraries(foo bar)

add_executable(foobar foobar.cxx)
target_link_libraries(foobar foo)

会将库 "foo "和 "bar "链接到可执行文件 "foobar "中,即使只明确指定了 “foo”。

1.2 使用目标指定优化库或调试库

在 Windows 平台上,用户通常需要将调试库与调试库链接,将优化库与优化库链接。CMake 的 target_link_libraries 命令可帮助满足这一要求,该命令可接受一个标记为调试或优化的可选标志。如果一个库前面标有调试或优化,那么该库将只与相应的配置类型链接。例如

add_executable(foo foo.c)
target_link_libraries(foo debug libdebug optimized libopt)

在这种情况下,如果选择了调试构建,foo 将与 libdebug 链接;如果选择了优化构建,foo 将与 libopt 链接。

1.3 对象库

大型项目通常会将源文件组织成不同的组,这些组可能位于不同的子目录中,每个组都需要不同的包含目录和预处理器定义。针对这种情况,CMake 提出了对象库的概念。

对象库是编译成对象文件的源文件集合,它不会被链接到库文件中,也不会被制作成归档文件。相反,由 add_library 或 add_executable 创建的其他目标可以使用 $<TARGET_OBJECTS:name> 形式的表达式作为源引用对象,其中 "name "是由 add_library 调用创建的目标。例如

add_library(A OBJECT a.cpp)
add_library(B OBJECT b.cpp)
add_library(Combined $<TARGET_OBJECTS:A> $<TARGET_OBJECTS:B>)

将在一个名为 Combined 的库中包含 A 和 B 对象文件。对象库只能包含编译为对象文件的源代码(和头文件)。

1.4 源文件

源文件结构在很多方面与目标文件类似。它存储了文件名、扩展名以及与源文件相关的一些常规属性。与目标文件一样,可以使用 set_source_files_properties 和 get_source_file_property 或更通用的版本来设置和获取属性。

1.5 目录、测试和属性

除了目标和源文件外,您可能偶尔还会发现自己在处理目录和测试等其他对象。通常,这种交互方式是设置或获取这些对象的属性(如 set_directory_properties 或 set_tests_properties)。


二、Policies

CMake 有时会出现与旧版本不完全向后兼容的新功能或更改。当有人试图在新版 CMake 中使用旧版 CMakeLists 文件时,就会产生问题。为了帮助最终用户和开发人员解决这些问题,我们引入了 cmake-policies。策略是一种帮助提高向后兼容性和跟踪不同版本 CMake 之间兼容性问题的机制。

2.1 设计目标

CMake 策略机制有四个主要设计目标:

  1. 现有项目应使用比项目作者使用的 CMake 版本更新的 CMake 进行构建。(用户不需要编辑代码就能完成项目的联编。可能会发出警告,但项目应能联编。)

  2. 新界面的正确性或旧界面的错误修复不应受到兼容性要求的限制。任何降低最新界面正确性的做法对新项目都是不公平的。

  3. 对 CMake 所做的每一项更改都应记录在案,这些更改可能需要更改项目的 CMakeLists 文件。(每项变更都应有一个唯一的标识符,以便在警告和错误信息中引用。只有当项目以某种方式表示支持新行为时,新行为才会启用。)

  4. 我们必须能够最终删除实现与古老 CMake 版本兼容的代码。(为了保持代码的整洁并允许内部重构,删除这些代码是必要的。移除后,在尝试构建为旧版本编写的项目时,就会出现失败的提示信息。)

CMake 中的所有策略都以 CMPNNNN 的形式命名,其中 NNNN 是一个整数值。策略通常既支持与 CMake 早期版本兼容的旧行为,也支持被认为正确并优先用于新项目的新行为。每个策略都有文档详细说明更改的动机以及新旧行为。

2.2 Setting Policies

项目可配置每个策略的设置,以要求旧的或新的行为。当 CMake 遇到可能受特定策略影响的用户代码时,它会检查项目是否设置了该策略。如果已设置策略(旧或新),CMake 将遵循指定的行为。如果未设置策略,则使用旧的行为,但会发出警告,告知项目作者设置策略。

有几种方法可以设置策略的行为。最快捷的方法是将所有策略设置为与项目编写时的 CMake 发行版本相对应的版本。设置策略版本会要求在 CMake 的相应版本或更早版本中引入的所有策略都采用新的行为。以后版本中引入的策略会被标记为 “未设置”,以便生成适当的警告信息。策略版本使用 cmake_policy 命令的 VERSION 签名来设置。例如,代码

cmake_policy(VERSION 3.20)

将请求 CMake 3.20 或更早版本中引入的所有策略的新行为。

cmake_minimum_required 命令将要求 CMake 的最低版本,并调用 cmake_policy。项目应始终以开头

cmake_minimum_required(VERSION 3.20)
project(MyProject)
# ...code using CMake 3.20 policies

这表明运行 CMake 的人必须至少有 3.20 版本。如果运行的是旧版本的 CMake,则会显示一条错误信息,告诉他们项目至少需要指定版本的 CMake。

当然,"3.20 "应替换为您当前编写的 CMake 版本。如果愿意,您也可以单独设置每项策略;这有时对项目作者很有帮助,因为他们希望逐步转换项目以使用新行为,或消除对旧行为依赖性的警告。cmake_policy 命令的 SET 选项可用于明确请求特定策略的新旧行为。

例如,CMake 2.6 引入了 CMP0002 策略,它要求所有逻辑目标名称必须是全局唯一的(以前在某些情况下,重复的目标名称会意外工作,但不会被诊断出来)。使用重复目标名称并意外运行的项目将收到引用该策略的警告。可以使用代码

cmake_policy(SET CMP0002 OLD)

明确告诉 CMake 使用旧的策略行为(默许目标名称重复)。另一种方法是使用代码

cmake_policy(SET CMP0002 NEW)

来明确告诉 CMake 使用新的行为,并在创建重复目标时产生错误。一旦将此添加到项目中,在作者删除任何重复的目标名称之前,项目将无法编译。

CMake 发布新版本时,会引入一些新策略,这些新策略仍然可以编译旧项目,因为默认情况下,它们不会请求任何新策略的新行为。启动新项目时,应始终使用 cmake_minimum_required 命令指定 CMake 支持的最新版本。这将确保编写的项目使用该版本 CMake 的策略,而不是使用任何旧的行为。如果没有设置策略版本,CMake 将发出警告并假定策略版本为 2.4。这将允许未指定 cmake_minimum_required 的现有项目按照 CMake 2.4 版本进行编译。

三、Modules(模块,组)

3.1 使用模块

代码重用是软件开发中的一项重要技术,CMake 就是为支持代码重用而设计的。允许 CMakeLists 文件使用可重复使用的模块,能让整个社区共享可重复使用的代码段。对于 CMake 而言,这些代码段被称为 cmake-modules,可在安装的 Modules 子目录中找到。

可以使用模块文件的完整路径指定模块的位置,也可以让 CMake 自行查找模块。CMake 会在 CMAKE_MODULE_PATH 所指定的目录中查找模块;如果找不到,就会在 Modules 子目录中查找。这样,项目就可以覆盖 CMake 提供的模块,并根据自己的需要进行定制。模块可分为几大类:

3.1.2 查找模块

这些模块支持 find_package 命令,用于确定属于特定软件包的软件元素(如头文件或库)的位置。不要直接包含它们。请使用 find_package 命令。每个模块都附有文档,说明它所查找的软件包以及提供结果的变量。

3.1.3 公用模块

实用程序模块是 CMake 命令的一部分,被放入一个文件中;然后可以使用 include 命令将它们包含到其他 CMakeLists 文件中。例如,以下命令将包含 CMake 中的 CheckTypeSize 模块,然后使用它定义的宏。

include(CheckTypeSize)
check_type_size(long SIZEOF_LONG)

这些模块对系统进行测试,以提供有关目标平台或编译器的信息,例如浮点数的大小或对 ANSI C++ 流的支持。其中许多模块的名称都以 Test 或 Check 为前缀,如 TestBigEndian 和 CheckTypeSize。其中有些模块会尝试编译代码,以确定正确的结果。在这种情况下,源代码的名称通常与模块相同,但扩展名为 .c 或 .cxx。实用工具模块还提供有用的宏和函数,这些宏和函数用 CMake 语言实现,用于特定的常用情况。详情请参见各模块的文档。


四、Installing Files

软件通常安装在一个独立于源代码和构建树的目录中。这样可以使软件以简洁的形式发布,并将用户与构建过程的细节隔离开来。CMake 提供 install 命令来指定项目的安装方式。该命令由 CMakeLists 文件中的项目调用,并告诉 CMake 如何生成安装脚本。这些脚本会在安装时被执行,以完成文件的实际安装。对于 Makefile 生成器(UNIX、NMake、MinGW 等),用户只需运行 make install(或 nmake install),make 工具就会调用 CMake 的安装模块。对于基于图形用户界面的系统(Visual Studio、Xcode 等),用户只需构建名为 INSTALL 的目标即可。

每次调用 install 命令都会定义一些安装规则。在一个 CMakeLists 文件(源代码目录)中,这些规则将按照调用相应命令的顺序进行评估。在 CMake 3.14 中,跨多个目录的顺序发生了变化。

install 命令有几个签名,是为常见的安装用例而设计的。命令的特定调用会指定签名作为第一个参数。签名包括 TARGETS、FILES 或 PROGRAMS、DIRECTORY、SCRIPT、CODE 和 EXPORT。


少侠请留步,点赞、收藏、关注


http://www.niftyadmin.cn/n/5159817.html

相关文章

什么GAN生成对抗网络?生成对抗网络可以干什么?

生成对抗网络(Generative Adversarial Nets,简称GAN)。神经网络分很多种,有普通的前向传播网络,有分析图片的CNN卷积神经网络,有分析系列化数据比如语言、文字的RNN循环神经网络,这些神经网络都是用来输入数据,得到想要的结果,我们看中的是这些神经网络中很好地将数据与…

易点易动结合手持终端PDA的固定资产盘点解决方案

本文将详细介绍易点易动固定资产管理系统如何结合手持终端PDA进行固定资产盘点&#xff0c;分析其在盘点过程中的优势和操作要点。通过利用先进的技术手段&#xff0c;企业能够实现高效准确的固定资产盘点管理&#xff0c;提升资产管理效率&#xff0c;为企业的发展打下坚实基础…

viple进阶2:打印九九乘法表

&#xff08;1&#xff09;题目 题目&#xff1a;使用viple打印九九乘法表 &#xff08;2&#xff09;设计与实现 观察效果图&#xff0c;发现&#xff1a; 1、第1行&#xff0c;有1个公式&#xff1b;第2行有2个公式&#xff1b;第3行有3个公式&#xff0c;以此类推&#x…

Go (三) 面向对象1

一、面向对象 1.1、初识golang面向对象 golang支持面向对象编程(OOP)。 golang没有类(class)&#xff0c;go语言的结构体&#xff08;struct&#xff09;和其他编程语言的类&#xff08;class&#xff09;有同等的地位。简单理解golang是根据struct来实现面向对象编程(OOP)的。…

软考:中级软件设计师:2022年下半年上午软件设计师考题

软考&#xff1a;中级软件设计师:2022年下半年上午软件设计师考题 提示&#xff1a;系列被面试官问的问题&#xff0c;我自己当时不会&#xff0c;所以下来自己复盘一下&#xff0c;认真学习和总结&#xff0c;以应对未来更多的可能性 关于互联网大厂的笔试面试&#xff0c;都…

golang的defer执行时机案例分析

package main import "fmt"func calcFunc(x int, y int) int {return x y }func main() {// defer语句的执行顺序是&#xff0c;从右到左,逆序执行deferDemo()// deferDemo1函数demo1 : deferDemo1()fmt.Println(demo1) // 0// deferDemo2函数demo2 : deferDemo2()f…

kubernetes集群编排(6)

目录 k8s调度 nodename nodeselector nodeaffinity podaffinity podantiaffinity Taints cordon、drain、delete k8s调度 nodename [rootk8s2 node]# vim nodename.yaml apiVersion: v1 kind: Pod metadata:name: nginxlabels:app: nginxspec:containers:- name: nginximage: n…

【星海出品】VUE(六)

插槽Slots 传递属性 attribute App,vue <script> import SlotsBase from "./components/SlotsBase.vue" import SlotsTow from "./components/SlotsTow.vue" export default {components:{SlotsBase,SlotsTow},data(){return{message: "父集 m…