除了 add_library() 与 add_executable(),创建Target 最多的方式可能就是find_package 了。几乎所有cmake 的入门资料都会包含 find_packge的基础功能介绍,这里不在赘述,只是列出几个要点来。
find_package有两种工作模式:module 模式与config 模式。module 模式通常是通过调用Find
CMake 内置了常用的 Find
macOS:/Applications/CMake.app/Contents/share/cmake-3.16/Modules
ubuntu:
windows:C:\Program Files\CMake\share\cmake-3.23\Modules
显然不同的外部模块/包,FindPackage找到的东西是不相同的,多数情况下我们也并不需要详细了解FindPackage 都找到了哪些信息,只要FindPackage运行正常,后面就全交给cmake了,因为cmake 会自动根据需要去使用FindPackage 找到的信息。就我自己的工作经验来看,有两种情况我们需要了解FindPackage 到底都(需要)找到了哪些信息。
一个是我们需要对包中的文件进行特殊操作,比如把找到的包中的文件复制到指定的目录下,这时就需要知道文件的二进制库的名称与位置;另一种情况是我们自己要开发供别人使用的 .cmake文件,此时就必须要按cmake 的要求返回尽可能多的信息,以供调用者使用。
find_package 会通过两种方法返回查找结果,一种是直接定义好的变量,比如:xxx_FOUND、xxx_INCLUDE_DIR。另一种则是要通过get_target_property() 取属性的方法来取得的信息。
property 也分为两种,一种是 CMake 预定义的Target 属性 另一种是通过 set_target_properties() 自己设置的属性。
CMake 预定义的Target 属性https://cmake.org/cmake/help/latest/manual/cmake-properties.7.html#target-properties 简单分析一下cmake 3.24 关于target 的 property 有328个,具体内容就看cmake 文档吧。仅举一个例子说明一下 INTERFACE_LINK_LIBRARIES:
find_package(Boost COMPONENTS log)
get_target_property(BOOST_LOG_ILL Boost::log INTERFACE_LINK_LIBRARIES)
message("INTERFACE_LINK_LIBRARIES :${BOOST_LOG_ILL}")
运行结果:
INTERFACE_LINK_LIBRARIES :Boost::atomic;Boost::chrono;Boost::filesystem;Boost::regex;Boost::thread;Boost::headers
在find_package 中只查找了log 一个组件,通过INTERFACE_LINK_LIBRARIES 属性,可以获取所有它依赖的组件。
是否有简单办法列出一个Target 的所有属性,包括预定义的属性以及自定义的属性?至本文写作时为止,答案是不行!StackOverflow 上有一些方法,但是很丑。
在CMake 找到Target后,经常会提示库文件不在PATH路径中的问题,例如boost_xxx.dll 或 opencv_xxx.dll 不在PATH 中,在程序运行的时候可能找不到对应的.dll
通常在调试程序的时候,只要将其路径加入到PATH 路径中就可以了。
但是在某些情况下,还是需要找到这些dll 的具体位置的,制作安装程序的时候就是如此。
此时可以使用IMPORTED_LOCATION_RELEASE 得到文件的路径,再把它复制到对应的目录下就可以了。
get_target_property(boost_log_dll Boost::log IMPORTED_LOCATION_RELEASE)
message(${boost_log_dll})
运行结果
C:/Boost/lib/boost_log-vc143-mt-x64-1_79.dll
:: 代表别名或是引用的目标(Imported Target)。无论是别名还是引用的库都是指读的。
先看下面的例子,使用boost log 库的时候可以有两种写法
target_link_libraries(${TargetName} boost_log)
或者写成下面这样:
target_link_libraries(${TargetName} Boost::log)
第一种写法,在Linux 下多数情况下是可以了,因为连接的时候会自动连接 libboost_log.lib 而在Windows 下则相当麻烦,在我的机器上要连接的文件是 boost_log-vc143-mt-x64-1_79.lib
如果考虑 release 版与 debug 版要连接不同的库的情况,那要更复杂一些:对于boost debug版是加 -gd 而对于 opencv 则是在库文件名上加d
第二种写法使用了:: 代表此处是一个引用的Target,或者只是一个别名。具体使用的时候,cmake会自动根据编译选项选择对应的库文件,编写CMakeLists 时就不用为不同的编译环境以及外部库的版本而操心了。