CMake链接第三方库
CMake是一个很方便的工具,为我们提供了多种方式链接第三方扩展库。
方式一: 直接链接已经安装好的库
这类库通常可以通过包管理器来安装,如果不能,你也可以手动把这些库的二进制形式的文件放到系统中正确的位置(例如/usr/lib)。 这样CMake就会自动的在编译时加上相应的参数,自动的链接这些库。 语法格式:
add_exectutable(Target main.cpp) # 假设你的可执行目标名字为Target
target_link_libraries(Target
<PRIVATE|PUBLIC|INTERFACE> <item>...
[<PRIVATE|PUBLIC|INTERFACE> <item>...]...)
scope keywords
PRIVATE
当你使用 PRIVATE 关键字时,表示这个库只会被目标自身使用,而不会传递给依赖这个目标的其他目标。
如果一个库被声明为 PRIVATE,那么只有在声明这个库的目标中可以使用它,而依赖这个目标的其他目标无法直接访问这个库。
用于控制链接的范围,使得链接的库对于其他目标是不可见的。
PUBLIC
当你使用 PUBLIC 关键字时,表示这个库既会被目标自身使用,也会传递给依赖这个目标的其他目标。
如果一个库被声明为 PUBLIC,那么不仅可以在声明这个库的目标中使用它,而且可以在依赖这个目标的其他目标中使用它。
用于将链接的库公开给目标的依赖项。
INTERFACE
INTERFACE 关键字用于指定链接到目标的库只会影响目标的接口,而不会影响目标本身。
当你希望链接到的库只是影响接口时,而不添加到目标自身的链接项时,可以使用 INTERFACE。
通常用于将依赖的信息传递给依赖目标,而不影响当前目标的链接。
比如我要链接一个动态链接库spdlog(C++里面一个好看的日志库),则可以采用如下的操作:
target_link_libraties(Target PRIVATE spdlog)
但是通常情况下只链接这个目标库肯定是不够的,想象一下,在你本地安装了这个库,编译的时候没有任何问题,但是拿给别人编译时,别人就没有这个库,然后别人看着报错一脸懵逼。 还有一些用户安装了,但是安装的配置不是很普通,比如修改了默认安装路径之类的,此时CMake也会报错。因此,Cmake提供了一种机制,可以自动寻找这些库,如果找不到,你可以执行一些相应的处理,比如fetch(), 自动下载这个库的源码并且尝试编译。这样可以极大的方便了用户的编译。
方 法二: FetchContent
FetchContent是一个非常好用的module,可以在编译之前自动下载相关的依赖。此module会随着CMake的安装而预装,因此在使用时只需要include就行。
fetchContent在使用时只需要声明目标模块的名字和下载的链接,支持Git存储库或者URL,并且可以指定HASH并校验下载文件,以及指定GIT的分支或tag。
一个简单的示例用法:
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG 703bd9caab50b139428cea1aaff9974ebee5742e # release-1.10.0
)
# 或者采用URL
FetchContent_Declare(
myCompanyIcons
URL https://intranet.mycompany.com/assets/iconset_1.12.tar.gz
URL_HASH MD5=5588a7b18261c20068beabfb4f530b87
)
# 然后只需要使用MakeAvaliable即可
FetchContent_MakeAvailable(googletest myCompanyIcons) # 里面跟上Declare的名字
这样CMake就会自动下载对应的模块源码并且根据模块定义的CMakeLists.txt中的内容进行编译,并自动进行链接。
当然,别忘了target_link_libraties, 不然CMake可不知道你的哪些可执行文件需要这个库。
补充 find_packages()
原型如下:
find_package(<PackageName> [version] [EXACT] [QUIET] [MODULE]
[REQUIRED] [[COMPONENTS] [components...]]
[OPTIONAL_COMPONENTS components...]
[REGISTRY_VIEW (64|32|64_32|32_64|HOST|TARGET|BOTH)]
[GLOBAL]
[NO_POLICY_SCOPE]
[BYPASS_PROVIDER])
可以在找不到对应的package的时候执行某些操作,例如更加友好的报错,或者要求某个package的版本。
例如,严格要求curl的版本为7.67.0,则可以这样写:
find_package(curl 7.67.0 REQUIRED)