目录

CMake构建目标

除了控制台可执行程序,CMake还支持定义其他类型的可执行文件,如苹果平台上的应用包和Windows GUI应用程序。除了可执行文件外,开发人员还经常需要构建和链接库。CMake支持几种不同类型的库,包括静态库、共享库、模块库和框架库。CMake还提供了非常强大的功能来管理目标之间的依赖关系以及库的链接方式。

可执行程序

add_executable命令的更完整形式如下:

1
2
3
4
add_executable(targetName [WIN32] [MACOSX_BUNDLE]
    [EXCLUDE_FROM_ALL]
    source1 [source2 ...]
)

WIN32

当在Windows平台上构建可执行文件时,该选项指示CMake将可执行文件构建为Windows GUI应用程序。这意味着它将被创建一个WinMain入口点,并且链接/SUBSYSTEM:WINDOWS选项。在所有其他平台上,WIN32选项被忽略。

MACOSX_BUNDLE

当在苹果平台上构建时,该选项指示CMake构建一个应用包。它不仅适用于macOS,也适用于iOS。CMake将为bundles生成一个基本的Info.plist文件。在非苹果平台上,MACOSX_BUNDLE关键字会被忽略。

EXCLUDE_FROM_ALL

有时一个项目定义了许多目标,但默认情况下,只有其中的一部分应该被构建。当在构建时没有指定目标时,会构建默认的ALL目标。如果一个目标是用EXCLUDE_FROM_ALL选项定义的,它将不会包括在默认的ALL目标中。只有当构建命令明确要求该目标,或者当该目标是另一个默认构建的目标的依赖项时,才会被构建。

创建可执行文件是任何构建系统的一个基本需求。对于许多大型项目来说,创建和使用库的能力对于保持项目的可管理性也是至关重要的。CMake支持构建各种不同类型的库,处理各种平台上的差异,但仍支持每种库的本地特性。库是用add_library命令定义的,其最基本的形式如下:

1
2
3
4
add_library(targetName [STATIC | SHARED | MODULE]
    [EXCLUDE_FROM_ALL]
    source1 [source2 ...]
)

EXCLUDE_FROM_ALL关键字的作用与add_executable命令中的作用完全相同,即防止库目标被包含在默认的ALL目标中。库的类型由关键字STATIC、SHARED或MODULE中的一个指定。

STATIC

指定一个静态库或存档。在Windows上,默认的库名是targetName.lib,而在类Unix平台上,通常是libtargetName.a。

SHARED

指定一个共享或动态链接库。在Windows上,默认的库名是targetName.dll,在苹果平台上是libtargetName.dylib,在其他类Unix平台上,通常是libtargetName.so。在苹果平台上,共享库也可以被标记为框架。

MODULE

指定一个有点像共享库的库,但在运行时动态加载,而不是直接链接到库或可执行文件中。这些通常是插件或可选组件,用户可以选择是否加载。在Windows 平台上,不会为DLL创建导入库。

构建库最好的做法是不指定库类型,让开发者在构建项目时自行选择。这由CMake变量BUILD_SHARED_LIBS的值决定,如果BUILD_SHARED_LIBS被设置为true,目标将是一个共享库,否则将是静态库。设置这个变量的一个方法是在cmake命令行中加入一个-D选项,如下:

1
cmake -DBUILD_SHARED_LIBS=YES /path/to/source

也可以在CMakeLists.txt文件任何add_library命令之前设置,但当开发者想要使用不同类型库时还得修改CMakeLists.txt,不灵活。

1
set(BUILD_SHARED_LIBS YES)

链接目标

在现实中,库之间存在几种不同类型的依赖关系。

PRIVATE

私有的依赖关系是指库A在它自己的内部实现中使用库B。其他链接到库A的东西不需要知道B,因为它是A的一个内部实现细节。

PUBLIC

公共依赖关系是指库A不仅在内部使用库B,而且在其接口中也使用B。这意味着没有B就不能使用A,所以任何使用A的东西也会对B有直接依赖。

INTERFACE

接口依赖关系是指为了使用库A,必须同时使用库B的一部分。与公共依赖不同的是,库A内部不需要B,它只在其接口中使用B。

CMake用target_link_libraries命令处理这些依赖关系。该命令的一般形式是:

1
2
3
4
5
target_link_libraries(targetName
    <PRIVATE|PUBLIC|INTERFACE> item1 [item2 ...]
    [<PRIVATE|PUBLIC|INTERFACE> item3 [item4 ...]]
    ...
)

这允许项目精确地定义一个库是如何依赖其他库的,然后CMake负责管理整个库链的依赖关系。举个例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
add_library(collector src1.cpp)
add_library(algo src2.cpp)
add_library(engine src3.cpp)
add_library(ui src4.cpp)
add_executable(myApp main.cpp)

target_link_libraries(collector
    PUBLIC ui
    PRIVATE algo engine
)
target_link_libraries(myApp PRIVATE collector)

在这个例子中,ui库是作为PUBLIC链接到collector库的,所以即使myApp只直接链接到collector,myApp也需要链接到ui。另一方面,algo库和engine库是作为PRIVATE链接到collector的,所以myApp不会直接链接到它们。

链接非目标

除了CMake目标之外,以下这些也可以被指定为target_link_libraries命令中的项目。

库文件的完整路径

CMake将把库文件添加到链接器命令中。如果库文件发生变化,CMake将检测到这种变化并重新链接目标。注意,从CMake 3.3版本开始,链接器命令总是使用指定的完整路径,但在3.3版本之前,有些情况下CMake可能会要求链接器搜索库。

普通的库名

如果只给了库的名字而没有给路径,链接器命令将搜索该库。这对于由系统提供的库来说是很常见的。

链接标志

作为一种特殊情况,除-l或-framework外,以连字符开头的项目将被视为要添加到链接器命令中的标志。这些标志应该只用于PRIVATE项目,因为如果定义为PUBLIC或INTERFACE,它们会被带到其他目标上,这可能不安全。

除了上述内容外,由于历史原因,任何项目前面都可以加上debug、optimized或general等关键词。如果一个项目前面有debug关键字,那么它只会在debug构建被添加。如果一个项目前面有optimized关键字,那么只有不是debug构建时才会被加入。general关键字指定该项目应该被添加到所有的构建配置中,如果没有使用关键字,默认为general。新项目应该避免使用debug、optimized和general关键字,因为新的CMake有更好的方法。

旧式的CMake

target_link_libraries命令还有其他一些形式,其中一些在2.8.11版之前就已经是CMake的一部分了。这里讨论这些形式是为了理解老的CMake项目,但一般不鼓励在新项目中使用这些形式。

1
target_link_libraries(targetName item [item...])

上述形式通常等同于PUBLIC链接。但在某些情况下,它们可能被视为PRIVATE。特别是,如果一个项目用新旧两种形式的命令定义了一个库的依赖关系链,旧式的形式一般会被视为PRIVATE。

以下是几种被弃用的形式:

1
2
3
4
5
6
7
8
target_link_libraries(targetName
	LINK_INTERFACE_LIBRARIES item [item...]
)

target_link_libraries(targetName
    <LINK_PRIVATE|LINK_PUBLIC> lib [lib...]
    [<LINK_PRIVATE|LINK_PUBLIC> lib [lib...]]
)