按:

这个系列与 cmake 有关的记录也好,笔记也好,成书也好,目前暂且归属在 cmake-hello 标记之下。

建立库或执行文件

库和 add_library

add_library 指令能够定义一个库作为目标(Target)。

1
2
3
4
5
6
7
8
9
10
11
12
13
# Normal Libraries
add_library(<name> [STATIC | SHARED | MODULE]
            [EXCLUDE_FROM_ALL]
            source1 [source2 ...])
# Imported Libraries
add_library(<name> <SHARED|STATIC|MODULE|UNKNOWN> IMPORTED
            [GLOBAL])
# Object Libraries
add_library(<name> OBJECT <src>...)
# Alias Libraries
add_library(<name> ALIAS <target>)
# Interface Libraries
add_library(<name> INTERFACE [IMPORTED [GLOBAL]])

你可以建立 C++ 的动态库、静态库等等。在不同的操作系统中静态库和动态库有细微的差别,但总的来说它们都是可以复用的二进制代码模块。

MODULE 是特殊的模组,又被称作 DSO 组,这种库无法像动态库、静态库一样被链接到其它目标中,但可以被动态装载到某可执行文件的运行实例中,所以这是一种运行时插件特有的形态,关于这方面需要进一步参阅其它 DSO 有关资料。

动态库,静态库,MODULE 都是常规类型的库。

Imported Libraries & add-library

https://cmake.org/cmake/help/v3.8/command/add_library.html#imported-libraries

1
2
add_library(<name> <SHARED|STATIC|MODULE|UNKNOWN> IMPORTED
            [GLOBAL])

一个 IMPORTED library 目标 (Imported library target)能够对项目之外的库文件进行参考和引用,且在本项目构建时无需对该外部引用库执行任何构建动作。对于外部引用库来说, IMPORTED 目标属性为 True。 The target name has scope in the directory in which it is created and below, but the GLOBAL option extends visibility. It may be referenced like any target built within the project. IMPORTED libraries are useful for convenient reference from commands like target_link_libraries(). Details about the imported library are specified by setting properties whose names begin in IMPORTED_ and INTERFACE_. The most important such property is IMPORTED_LOCATION (and its per-configuration variant IMPORTED_LOCATION_) which specifies the location of the main library file on disk. See documentation of the IMPORTED_* and INTERFACE_* properties for more information.

Add Static Library

下面的例子出自 z02-library-1,定义了一个名为 muchs 的静态库,然后定义了一个可执行文件目标,名为 library-1-test-program,并且将 muchs 链入其中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# The muchs library
# set (header_files ${CMAKE_CURRENT_SOURCE_DIR}/include/muchs/muchs.hh)
FILE(GLOB_RECURSE header_files ${CMAKE_CURRENT_SOURCE_DIR}/include/*.hh)
LIST(APPEND source_files library.cc)

# library
add_library(muchs STATIC ${source_files} ${header_files})
target_include_directories(muchs PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

# The main program

# The sources shared between the main program and the tests
set(PROJECT_SOURCES main.cc)

add_executable(library-1-test-program ${PROJECT_SOURCES})
target_link_libraries(library-1-test-program PRIVATE muchs)

在 linux 中,muchs 静态库的输出文件名为 libmuchs.a,如果是动态库,则名为 libmuchs.so。

target_include_directories 指令为 muchs 指明了包含文件的搜索路径。这不是必须的,因为我们也可以使用全局的 include_directories 指令来指明头文件搜索路径。

target_link_libraries 指令为可执行文件目标指明了要链接的动态库或者静态库。PRIVATE 是指不关心要链入的库的依赖库。也可以指定为 INTERFACEPUBLIC,其中 PUBLIC 可以适应大多数依赖场景,而 PRIVATE 对依赖关系切断的最严厉。而 INTERFACE 表明仅仅关心库及其依赖库的头文件,忽略 .a 或 .so(即使有也忽略),适用于仅有头文件的库,后续的小节会专门介绍。

PUBLIC, PRIVATEINTERFACE 的更精确的介绍后续将在专门的小节中予以说明。

Add Shared Library

添加一个动态库的目标(Target)和静态库目标没有什么不同,仅仅是将 STATIC 改为 SHARED

1
2
# add_library(muchs STATIC ${source_files} ${header_files})
add_library(muchs SHARED ${source_files} ${header_files})

我们也有一个完整独立的示例 z03-library-2。

Add a header only library

对于C++项目来说,一种可能是制作一个仅有头文件(没有链接时的 .a 或 .so 文件)的库。通常当你开发了一个模版库(C++ Template)的时候,你需要建立这样一个仅头文件的库作为目标(Target)。

下面是这样的一个 Library 项目的 CMakeLists.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
SET(header_files ${CMAKE_CURRENT_SOURCE_DIR}/include/monid/monid.hh)

# Header only library, therefore INTERFACE
add_library(monid INTERFACE)
# INTERFACE targets only have INTERFACE properties
target_include_directories(monid INTERFACE
        ${CMAKE_CURRENT_SOURCE_DIR}/include
        ${header_files}
        )
target_compile_features(monid INTERFACE cxx_std_11)


# The main program ---------------

# The sources shared between the main program and the tests
set(PROJECT_SOURCES main.cc)

add_executable(library-3-test-program ${PROJECT_SOURCES})
target_link_libraries(library-3-test-program PRIVATE monid)

这个片段取自于 z04-header-library,并略微有所修改以便更明晰。

target_compile_features 指令的用途在于为库或可执行文件指明编译器必须具备的特性约束。如果指定的特性在 CMAKE_C_COMPILE_FEATURESCMAKE_CXX_COMPILE_FEATURES 中没有被定义和请求的话,CMake将会报告一个错误。通常对于库来说,这样的指定可以被延伸到引入该库的新Target,从而解决特定的编译器特征的延续性问题,例如编译器被约束为支持C++11标准:

1
target_compile_features(monid INTERFACE cxx_std_11)

关于 CXX 已知的特性,可以查阅:CMAKE_CXX_KNOWN_FEATURES。当某个特性被请求,那么相关的编译器命令行选项将被自动追加到 CMAKE_C_FLAGS 或 CMAKE_CXX_FLAGS 之中,例如对于 gcc 的 -std=gnu++11

🔚