关键概念

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

目标

目标可能是最重要的一项。目标表示由 CMake 编译的可执行文件、库和实用程序。每条 add_libraryadd_executableadd_custom_target 命令都会创建一个目标。例如,以下命令将创建一个名为“foo”的目标,该目标是一个静态库,其中 foo1.cfoo2.c 是源文件。

add_library(foo STATIC foo1.c foo2.c)

名称“foo”现可在项目中其他任一位置用作库名称,而 CMake 将知道如何在需要时将名称展开至库中。库可以声明为特定类型,例如 STATICSHAREDMODULE,也可以保留其未声明状态。 STATIC 表示库必须构建为静态库。同样,SHARED 表示它必须构建为共享库。 MODULE 表示库必须创建为可以动态加载至可执行文件的形式。模块库在许多(但并非全部)平台上均作为共享库实现。因此,CMake 不允许其他目标链接至模块。如果未指定任何这些选项,则表示可以构建该库为共享库或静态库。在该情况下,CMake 会使用变量 BUILD_SHARED_LIBS 的设置,以确定该库应为 SHARED 还是 STATIC。如果未设置,则 CMake 默认构建静态库。

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

除了存储其类型外,目标还跟踪通用属性。可以使用 set_target_propertiesget_target_property 命令或更通用的 set_propertyget_property 命令来设置和检索这些属性。一个有用的属性是 LINK_FLAGS,用于为特定目标指定附加链接标志。目标会存储要链接到的库列表,这些库是使用 target_link_libraries 命令设置的。传递给此命令的名称可以是库、库的完整路径或 add_library 命令中库的名称。目标还会存储链接时的链接目录以及构建后要执行的自定义命令。

用法要求

CMake 还会从链接的库目标传播“用法要求”。用法要求会影响和<target> 中的源编译。这些要求由链接目标上定义的属性指定。

例如,若要指定链接到库时需要的包含目录,可以执行以下操作

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

现在,任何链接到目标 foo 的内容都会自动将 foo 的二进制文件和源文件作为包含目录。通过“用法要求”引入的包含目录的顺序将与 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)

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

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

在 Windows 平台上,用户通常需要将调试库与调试库进行链接,将优化库与优化库进行链接。 CMake 有助于使用 target_link_libraries 命令来满足此要求,此命令接受一个标记为 debugoptimized 的可选标志。如果库前面带 debugoptimized,则该库只会与适当的配置类型一起进行链接。例如

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

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

对象库

大型项目通常会将其源文件组织到组中,可能分散在单独的子目录中,每个子目录都需要不同的头文件目录和预处理器定义。对于此用例,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 对象文件。对象库可能只包含编译成对象文件的源文件(和头文件)。

源文件

源文件结构在许多方面类似于目标。它存储了文件名、扩展名和与源文件相关的许多通用属性。与目标类似,你可以使用 set_source_files_propertiesget_source_file_property 或更通用的版本来设置和获取属性。

目录、测试和属性

除了目标和源文件,您可能会发现自己偶尔与其他对象(如目录和测试)一起工作。通常,此类交互会采取设置或获取属性(例如 set_directory_propertiesset_tests_properties)的形式。