0x01 介绍
在 CMake 中使用第三方库通常有两种方法:使用 find_package 命令和手动指定路径。
使用 find_package 命令用于在系统中查找指定的第三方库,如果找到,则会将相关的信息存储在一个名为 <PackageName>_FOUND 的变量中。如果 find_package 找到了指定的第三方库,则可以使用以下命令来使用该库:
find_package(<PackageName> REQUIRED)
include_directories(${<PackageName>_INCLUDE_DIRS})
target_link_libraries(<target> ${<PackageName>_LIBRARIES})
其中,<PackageName> 是要查找的第三方库的名称,REQUIRED 指示如果找不到该库,则 CMake 会生成错误。<PackageName>_INCLUDE_DIRS 和 <PackageName>_LIBRARIES 分别包含该库的头文件路径和库文件的路径。<target> 是要链接该库的目标的名称。手动指定路径如果 find_package 命令无法找到第三方库,或者您希望手动指定第三方库的路径,则可以使用以下命令:
include_directories(<include_dir>)
link_directories(<library_dir>)
target_link_libraries(<target> <library>)
include_directories 指定头文件的搜索路径。
link_directories 指定库文件的搜索路径。
target_link_libraries 指定要链接的库文件的名称。
使用这些命令时,还需要注意几点:
- 库文件的名称可能因平台而异,例如在 Unix 平台上是 .a 文件,在 Windows 平台上是 .lib 文件。因此,可能需要手动指定库文件的名称。
- 在 Windows 平台上,库文件的名称通常包含一个前缀和一个后缀,例如 "libfoo.a" 或 "foo.lib"。在使用 target_link_libraries 命令时,您可能需要省略前缀和后缀,例如 "foo"。
- 在 Windows 平台上,如果第三方库是动态库(.dll),则还需要将动态库的路径添加到系统的环境变量中,或者使用 SET(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION ".") 命令将动态库复制到可执行文件的目录中。
以下是一个使用 find_package 命令的例子:
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
target_link_libraries(<target> ${OpenCV_LIBRARIES})
以下是一个手动指定路径的例子:
include_directories(/usr/local/include)
link_directories(/usr/local/lib)
target_link_libraries(<target> foo)
我们假设第三方库 foo 的头文件位于 /usr/local/include 目录中,库文件位于 /usr/local/lib 目录中,库文件的名称为 foo。
如果您还想要更复杂的控制,可以使用 CMake 的高级功能。例如,可以使用 if 语句来检查第三方库是否存在,并根据情况使用不同的方法来链接库。
0x02 示例:
几乎所有重要的项目都需要包含第三方库、头文件或程序。CMake 支持使用find_package()函数查找这些工具的路径。这将从CMAKE_MODULE_PATH中的文件夹列表中搜索格式为FindXXX.cmake的 CMake 模块。在 Linux 上,默认搜索路径将包含/usr/share/cmake/Modules。在我的系统上,这包括对大约 1420 个通用第三方库的支持。
本教程中的文件如下:
$ tree . ├── CMakeLists.txt ├── main.cpp
- [CMakeLists.txt] - 包含要运行的 CMake 命令
cmake_minimum_required(VERSION 3.5) # Set the project name project (third_party_include) # find a boost install with the libraries filesystem and system find_package(Boost 1.46.1 REQUIRED COMPONENTS filesystem system) # check if boost was found if(Boost_FOUND) message ("boost found") else() message (FATAL_ERROR "Cannot find Boost") endif() # Add an executable add_executable(third_party_include main.cpp) # link against the boost libraries target_link_libraries( third_party_include PRIVATE Boost::filesystem )
- [main.cpp] - 具有 main 的源文件
#include <iostream> #include <boost/shared_ptr.hpp> #include <boost/filesystem.hpp> int main(int argc, char *argv[]) { std::cout << "Hello Third Party Include!" << std::endl; // use a shared ptr boost::shared_ptr<int> isp(new int(4)); // trivial use of boost filesystem boost::filesystem::path path = "/usr/share/cmake/modules"; if(path.is_relative()) { std::cout << "Path is relative" << std::endl; } else { std::cout << "Path is not relative" << std::endl; } return 0; }
ln28@DESKTOP-FS9U3GT:/mnt/d/Project/Cmake_examples/cmake_basics_08/build$ dpkg -S /usr/include/boost/version.hpp libboost1.71-dev:amd64: /usr/include/boost/version.hpp ln28@DESKTOP-FS9U3GT:/mnt/d/Project/Cmake_examples/cmake_basics_08/build$ sudo apt install mlocate Reading package lists... Done Building dependency tree Reading state information... Done mlocate is already the newest version (0.26-3ubuntu3). 0 upgraded, 0 newly installed, 0 to remove and 144 not upgraded. ln28@DESKTOP-FS9U3GT:/mnt/d/Project/Cmake_examples/cmake_basics_08/build$ locate libboost_iostreams.so locate: can not stat () `/var/lib/mlocate/mlocate.db': No such file or directory ln28@DESKTOP-FS9U3GT:/mnt/d/Project/Cmake_examples/cmake_basics_08/build$ sudo updatedb
ln28@DESKTOP-FS9U3GT:/mnt/d/Project/Cmake_examples/cmake_basics_08/build$ cmake .. && make -j24 VERBOSE=1 -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1463 ] _boost_TEST_VERSIONS = "1.72.0;1.72;1.71.0;1.71" -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1464 ] Boost_USE_MULTITHREADED = "TRUE" -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1465 ] Boost_USE_STATIC_LIBS = <unset> -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1466 ] Boost_USE_STATIC_RUNTIME = <unset> -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1467 ] Boost_ADDITIONAL_VERSIONS = <unset> -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1468 ] Boost_NO_SYSTEM_PATHS = <unset> -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1537 ] BOOST_ROOT = <unset> -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1538 ] ENV{BOOST_ROOT} = <unset> -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1539 ] BOOST_INCLUDEDIR = <unset> -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1540 ] ENV{BOOST_INCLUDEDIR} = <unset> -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1541 ] BOOST_LIBRARYDIR = <unset> -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1542 ] ENV{BOOST_LIBRARYDIR} = <unset> -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1630 ] location of version.hpp: /usr/include/boost/version.hpp -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1670 ] Boost_VERSION = "107100" -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1671 ] Boost_VERSION_STRING = "1.71.0" -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1672 ] Boost_VERSION_MACRO = "107100" -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1673 ] Boost_VERSION_MAJOR = "1" -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1674 ] Boost_VERSION_MINOR = "71" -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1675 ] Boost_VERSION_PATCH = "0" -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1676 ] Boost_VERSION_COUNT = "3" -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1693 ] Boost_LIB_PREFIX = "" -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1694 ] Boost_NAMESPACE = "boost" -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:790 ] _boost_COMPILER = "-gcc9" (guessed) -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1730 ] _boost_MULTITHREADED = "-mt" -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1807 ] _boost_ARCHITECTURE_TAG = "" (detected) -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1811 ] _boost_RELEASE_ABI_TAG = "-" -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1812 ] _boost_DEBUG_ABI_TAG = "-d" -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1872 ] _boost_LIBRARY_SEARCH_DIRS_RELEASE = "/usr/include/lib;/usr/include/../lib;/usr/include/stage/lib;PATHS;C:/boost/lib;C:/boost;/sw/local/lib" -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:1873 ] _boost_LIBRARY_SEARCH_DIRS_DEBUG = "/usr/include/lib;/usr/include/../lib;/usr/include/stage/lib;PATHS;C:/boost/lib;C:/boost;/sw/local/lib" -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:2058 ] Searching for FILESYSTEM_LIBRARY_RELEASE: boost_filesystem-gcc9-mt-1_71;boost_filesystem-gcc9-mt;boost_filesystem-gcc9-mt;boost_filesystem-mt-1_71;boost_filesystem-mt;boost_filesystem-mt;boost_filesystem-mt;boost_filesystem -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:2113 ] Searching for FILESYSTEM_LIBRARY_DEBUG: boost_filesystem-gcc9-mt-d-1_71;boost_filesystem-gcc9-mt-d;boost_filesystem-gcc9-mt-d;boost_filesystem-mt-d-1_71;boost_filesystem-mt-d;boost_filesystem-mt-d;boost_filesystem-mt;boost_filesystem -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:2058 ] Searching for SYSTEM_LIBRARY_RELEASE: boost_system-gcc9-mt-1_71;boost_system-gcc9-mt;boost_system-gcc9-mt;boost_system-mt-1_71;boost_system-mt;boost_system-mt;boost_system-mt;boost_system -- [ /usr/share/cmake-3.16/Modules/FindBoost.cmake:2113 ] Searching for SYSTEM_LIBRARY_DEBUG: boost_system-gcc9-mt-d-1_71;boost_system-gcc9-mt-d;boost_system-gcc9-mt-d;boost_system-mt-d-1_71;boost_system-mt-d;boost_system-mt-d;boost_system-mt;boost_system CMake Error at /usr/share/cmake-3.16/Modules/FindPackageHandleStandardArgs.cmake:146 (message): Could NOT find Boost (missing: filesystem system) (found suitable version "1.71.0", minimum required is "1.71.0") Call Stack (most recent call first): /usr/share/cmake-3.16/Modules/FindPackageHandleStandardArgs.cmake:393 (_FPHSA_FAILURE_MESSAGE) /usr/share/cmake-3.16/Modules/FindBoost.cmake:2179 (find_package_handle_standard_args) CMakeLists.txt:8 (find_package)
1.安装第三方boost库
此示例要求将 Boost 库安装在默认系统位置。
sudo apt-get install libboost-all-dev -y
2.CMake查找一个包
如上所述,find_package()函数将从CMAKE_MODULE_PATH中的文件夹列表中搜索格式为FindXXX.cmake的 CMake 模块。find_package的参数的确切格式将取决于你要查找的模块。这通常记录在文件FindXXX.cmake的顶部
下面是查找 Boost 的基本示例:
find_package(Boost 1.46.1 REQUIRED COMPONENTS filesystem system)
这些参数是:
- Boost - 库的名称。这是用于查找模块文件 FindBoost.cmake 的一部分。
- 1.46.1 - 要查找的 Boost 的最低版本。
- REQUIRED - 告诉模块这是必需的,如果失败,则找不到该模块。
- COMPONENTS - 要查找的库列表。
Boost includes 可以接受更多参数,还可以利用其他变量。更复杂的设置将在后面的示例中提供。
3.检查是否找到该包
大多数包含的软件包都会设置一个变量XXX_FOUND,该变量可用于检查该软件包在系统上是否可用。
在本例中,变量为BOOST_FOUND:
if(Boost_FOUND) message ("boost found") include_directories(${Boost_INCLUDE_DIRS}) else() message (FATAL_ERROR "Cannot find Boost") endif()
4.导出变量
在找到包之后,它通常会导出变量,这些变量可以告诉用户在哪里可以找到库、头文件或可执行文件。与XXX_FOUND变量类似,它们是特定于包的,通常记录在FindXXX.cmake文件的顶部。
本例中导出的变量包括:
- Boost_INCLUDE_DIRS - Boost 头文件的路径
在某些情况下,你还可以通过使用 ccmake 或 cmake-gui 检查缓存来检查这些变量。
5.别名 / 导入目标
大多数现代 CMake 库在其模块文件中导出别名目标。导入目标的好处在于,它们还可以填充头文件目录和链接库。
例如,从 CMake 的 3.5 版开始,Boost 模块就支持此功能。
类似于将你自己的别名目标用于库,模块中的别名可以让引用找到的目标变得更容易。
在 Boost 的例子中,所有目标通过使用标识符Boost::加子模块的名字来导出。例如,你可以使用:
- Boost::boost 仅适用于库的头文件
- Boost::system 对于 Boost 系统库
- Boost::filesystem 对于文件系统库
与你自己的目标一样,这些目标包含它们的依赖项,因此链接到 Boost::filesystem 将自动添加 Boost::boost和Boost::system依赖。
要链接到导入的目标,可以使用以下命令:
target_link_libraries( third_party_include PRIVATE Boost::filesystem )
6.非别名目标
虽然大多数现代库使用导入的目标,但并非所有模块都已更新。在库尚未更新的情况下,你通常会发现以下变量可用:
- xxx_INCLUDE_DIRS - 指向库的 include 目录的变量
- xxx_LIBRARY - 指向库路径的变量.
然后,可以将这些文件添加到 target_include_directory 和 target_link_library 中:
# Include the boost headers target_include_directories( third_party_include PRIVATE ${Boost_INCLUDE_DIRS} ) # link against the boost libraries target_link_libraries( third_party_include PRIVATE ${Boost_SYSTEM_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} )