【学习笔记】Mastering CMake (五)—— 关键概念

前言:

学习笔记,随时更新。如有谬误,欢迎指正。


说明:

  1. 红色字体为较为重要部分。
  2. 绿色字体为个人理解部分。

5 关键概念

这一章提供了 CMake 主要概念的介绍。当你开始使用 CMake 的时候,你将遇到各种各样的概念,如目标、生成器和命令(在CMake中,这些概念被以C++类的形式实现并且被很多CMake的命令所引用。)。理解这些概念你将会获得你需要的知识来创建更高效的 CMakeLists 文件。许多 CMake 对象,如目标、目录和源文件,都有与它们相关联的属性。属性是附加到特定对象的键值对。访问属性最通用的方法是通过 set_propertyget_property 命令。这些命令允许你从 CMake 中任何有属性的对象中设置或获取属性。有关支持的属性列表,请参阅 cmake-properties 手册。通过运行 cmake 并使用 --help-property-list 选项 ,可以在命令行中获得 CMake 支持的完整属性列表。

5.0 主要结构

----------这一节截取自本书的旧版本的本章节,个人认为非常有助于理解 CMake 的运行原理,因此摘录在此处(以下一些概念和流程可以在 cmake 源码中找到,有兴趣可以调试下源码)----------

在详细了解 CMake 的类之前,很值得(先)去了解一下他们的基础关系。最低层级的是源文件。这些对应于典型的 C 或 C++ 源代码文件。源文件被联合成目标。一个典型的目标就是一个可执行文件或库。目录代表着源码树的一个目录,目录一般都会有一个 CMakeLists 文件,一个或多个目标与目录相关联。每一个目录有一个局部生成器,用来为该目录生成 Makefiles 或者工程文件。多个局部生成器共享一个用来监督整个构建过程的公共的全局生成器。最后,全局生成器由 cmake 类自身所生成和驱动

下图显示了 CMake 的基本类的结构。我们现在更深入细节地了解以下 CMake 的概念。 CMake 的的执行始于 cmake 类实例的构建,在构建的同时传递参数给它。这个类管理着所有的配置流程,并且持有构建流程中的全局信息,比如缓存值。 cmake 类最开始做的事情之一就是根据用户选择的生成器来创建全局生成器(例如 Visual Studio 10 、 Borland Makefiles 或者是UNIX Makefiles )。这时, cmake 类通过调用配置和生成方法来将控制(权)交给它所创建的全局生成器。

全局生成器负责管理一个工程所有 Makefiles (或者工程文件)的配置和生成。事实上大部分工作实际是由全局生成器创建出来的局部生成器来完成的。被处理的工程的每一个目录都会被创建一个局部生成器。因此一个工程将会只有一个全局生成器,而可能会有许多局部生成器。例如,在Visual Studio 7中,全局生成器为整个工程创建一个解决方案文件,而局部生成器为每个目标在其(目标的)目录中生成项目文件

对于“ UNIX Makefiles ”生成器,局部生成器创建了绝大部分 Makefiles ,全局生成器编排了过程并创建了顶层的 Makefile不同生成器的实现细节差别很大。 Visual Studio 6 生成器使用 .dsp 和 .dsw 文件模板, 对它们执行变量替换。 Visual Studio 7 及以上直接生成 XML 文件,并没有使用任何模板文件。 Makefile 生成器,包括 UNIX 、 NMake 、 Borland 等使用一系列的模板规则和替换来生成它们的 Makefiles 。

每一个局部生成器有一个 cmMakefile 类实例, cmMakefile 是用来存储 CMakeLists 解析结果的。具体地说,工程中的每一个目录都会有一个 cmMakefile 实例,这就是为什么 cmMakefile 类经常代指一个目录。这对于不使用 Makefiles 的构建系统来说更加清晰。那个实例将持有解析自那个目录下的 CMakeLists 文件的所有信息(见上图)。一种理解 cmMakefile 类的方式是把它当作一个结构,这个结构被它的父目录的一些变量初始化,然后 CMakeists 文件被处理后这个结构又被填充

CMake 的每一个命令被以单独的 C++ 类(的形式)实现,(命令)有两个主要部分。命令的第一个部分是 InitialPass 方法,用来接收当前正在处理的目录的参数和 cmMakefile 实例,然后执行它的操作。例如 set 命令,它(的 InitialPass )处理它的参数,如果参数是正确的,则调用 cmMakefile 中的方法来设置变量(的值)。命令的结果总是被存储在 cmMakefile 实例中。信息从来不会被存储在命令中。命令最后的一部分是 FinalPass 。命令的 FinalPass 在所有命令的 InitialPass 被调用后才被执行。大部分命令没有 FinalPass ,但是在某些特殊情况下,需要使用全局信息做一些事情,但这些信息在 IntialPass 期间无法获得。

一旦所有的 CMakeLists 文件被处理了,生成器将会使用 cmMakefile 实例收集到的信息来为目标构建系统创建适当的文件(如 Makefiles )。

现在我们已经讨论过 CMake 的整体过程,现在让我们看下 cmMakefile 实例中存储的一些关键项。

5.1 目标

可能最重要的项就是目标( cmTarget )。目标代表 CMake 构建的可执行文件、库和工具。每一个 add_libraryadd_execuatbleadd_custom_target 命令都会创建一个目标。例如,下面的命令将会使用 foo1.c 和 foo2.c 作为源文件来创建一个名叫 foo 的静态库目标。

add_library(foo STATIC foo1.c foo2.c)

现在名称 foo 可以被当作一个库的名字在项目的其他任何地方使用, CMake 会知道如何在需要的时候将名称扩展到库。库可以被声明成特殊类型,如 STATIC 、 SHARED 、 MODULE 或者不声明。 STATIC 表明此库必须被构建为静态库。同理, SHARED 表明此库必须被构建为动态库。 MODULE 表明库必须被创建,这样它就能被动态地加载进一个可执行文件。在许多操作系统中 MODULE 与 SHARED 是一样的,但是在其他的一些系统中两者是不一样的,如 Mac OS X 。如果这些选项都没被指定,这表明库可以既可以被构建成静态的也可以被构建成动态的。这种情况下, CMake 使用 BUILD_SHARED_LIBS 变量设置来决定库是 STATIC 还是 SHARED 。如果( BUILD_SHARED_LIBS )未被设置, CMake 默认构建静态库

同样地,可执行文件也有一些选项。可执行文件默认情况下是有一个 main 入口的传统的控制台程序。如果在可执行文件名称后面指定了 WIN32 ,则可执行文件将会被编译成一个 MS Windows 可执行程序,操作系统将会在(可执行文件)启动时调用 WinMain 来替代 main 。 WIN32 在非 Windows 系统中是无效的。

为了存储它们的类型,目标也携带了常规的属性。这些属性可以使用 set_target_propertiesget_target_properties 命令或者更通用的 set_propertiesget_properties 来设置和获取。最常用的是用来为特殊目标指定链接标识符的 LINK_FLAGS 属性。目标存储了他所链接的库的列表,这些库使用 target_link_libraries 来设置。这个命令所传入的名字可以是库、库的完整路径或者在add_library中设置的库的名称。它们也存储着链接时所用的链接目录、目标的安装位置和链接后所执行的自定义命令。

5.2 使用要求

CMake 也会通过被链接的库目标传播“使用需求”。使用需求会影响目标中源代码的编译。使用需求由被链接的目标上定义的属性指定。

例如,要指定当链接到某个库时所必须包含的目录,你可以这样做:

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

现在,任何链接到目标 foo 的东西都会自动将 foo 的二进制文件(目录)和源代码(目录)作为包含目录。通过“使用需求”引入的包含目录的顺序将与 target_link_libraries 调用中的目标的顺序相匹配。

对于 CMake 创建的每一个库, 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 被显式链接到 foobar ,但 foo 和 bar 都会被链接到 foobar 可执行文件中。

5.3 对目标指定优化或调试库

在 Windows 平台上,用户经常需要将调试库与调试库相链接,将优化库与优化库相链接。 CMake 通过 target_link_libraries 命令来帮助满足这一要求,该命令接受标记为 debug 或 optimized 的可选标志。如果一个库之前带有 debug 或 optimized ,那么该库将只在适当的配置类型时才被链接。例如:

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

在这种情况下,如果选择了 debug 版本,则 foo 将被链接到 libdebug ;如果选择了 optimized 版本, foo 将被链接到 libopt 。

5.4 对象库

大型项目经常将它们的源文件组织成组,可能在单独的子目录中,每个子目录都需要不同的包含目录和预处理器定义。对于这种情况, CMake 开发了对象库的概念。

对象库是编译成对象文件的源文件的集合,该对象文件没有链接到(动态)库文件或(编译)到静态库。相反,由 add_libraryadd_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 对象文件。对象库可能只包含编译成对象文件的源文件(和头文件)。

5.5 源文件

源文件的结构在很多方面与目标一样( cmSourceFile )。它存储着文件名、扩展和许多与源文件相关的常规属性。就像目标一样,你可以使用 set_source_files_propertiesget_source_files_properties 或者更通用的版本(指的是 set_propertiesget_properties )来获取和设置属性。

5.6 目录、测试和属性

除了目标和源文件,你可能发现你会偶尔遇到其他对象,如目录和测试。正常情况下对这些的交互都以设置或获取这些对象的属性(如 set_directory_propertiesset_tests_properties )的方式呈现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值