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}
)