CMake - 在CMakeLists.txt文件中何时包含include_directories?

4

我有一个C++项目,在其中有以下代码行:

#include <curl/curl.h>

我正在使用CURL库从传感器获取IMU数据。我准备了一个看起来像这样的CMakeLists.txt文件:

cmake_minimum_required(VERSION 3.5.1)
project(imu_data_access_cmake_test)
set(CMAKE_CXX_STANDARD 11)
find_package(CURL REQUIRED)
include_directories(${CURL_INCLUDE_DIRS})
set(SOURCES imu_data_access.cpp ${CURL_INCLUDE_DIRS}/curl/curl.h)
add_executable(imu_data_access_cmake_test ${SOURCES})
#add_executable(imu_data_access_cmake_test imu_data_access.cpp)
target_link_libraries(imu_data_access_cmake_test ${CURL_LIBRARIES})

这个方法很有效。不过,如果我去掉include_directories这行,并将add_executable改为只使用我的imu_data_access.cpp作为源文件,cmake就不会抛出错误,一切仍然正常。我认为这是由于target_link_libraries这行引起的。
如何确定应该使用include_directories还是target_link_libraries来包含库?如果包/库有一个.so文件,我们可以简单地忽略该包/库的include_directories吗?
编辑:如何知道是否应该使用include_directoriestarget_link_libraries 来包含库?
3个回答

6
如何确定使用include_directories还是target_link_libraries来包含库?
除非一个库是头文件库,否则需要调用target_link_libraries才能使用它。
如果Find*脚本(由find_package()调用)设置了包含目录变量(可以从脚本的文档中获取此信息),它会假定这些目录将被包含(通过include_directoriestarget_include_directories)。
通过target_link_libraries传播包含目录发生在IMPORTED目标的情况下,通常命名为<namespace>::<name>
在您特定的情况下,include_directories没有影响,只是因为给定的目录已经被编译器默认搜索,这在通过软件包管理器安装库时很常见。但是,find_package旨在即使在非系统安装的情况下也能正常工作。

那么,如果系统路径中存在一个目录,即使我不使用 include_directories,编译器也会选择它吗? - skr
同时,我的问题意在询问如何知晓何时应该使用 include_directories 还是 target_link_libraries 来引用一个库? - skr
2
是的,编译器对系统路径有概念,因此它在任何情况下都会包含这些目录。至于你的问题,“vs”是错误的 - target_link_libraries 应该在任何情况下使用。我已经更新了答案。 - Tsyvarev

3
一个好的实践方法是使用target_include_directories()而不是include_directories()。其好处在于您可以指示包含文件的可见性,例如,如果您配置了一个库:
add_library(foo foo.cpp)
target_include_directories(foo PUBLIC foo_include)
target_include_directories(foo PRIVATE private_include)

如果你将foo链接到可执行文件bar,那么bar将可以访问foo_include目录,但无法访问private_include目录。命令include_directories()适用于包含该命令的CMakeLists.txt所在的目录,因此它将适用于该目录及其子目录中声明的任何库/可执行文件。
对于target_link_libraries(),行为会有所不同,因为它可以是一个库列表,在这种情况下没有包含目录的概念。但它可以是一个IMPORTED库,其中可以包含更多信息,例如包含目录(例如Boost和Qt)。
在cURL的情况下,文档指出CURL_LIBRARIES包含库列表。在你的情况下,cURL头文件可能被找到,因为默认情况下CMake会在标准位置搜索头文件,因此在这种情况下target_link_libraries()对包含目录没有影响。

2

我看到了几个可能帮助您改进代码的事情。

避免过度指定最低版本

通常我喜欢确保我的CMake脚本不会过度指定版本。在这个脚本中,似乎3.1版本以上的任何内容都可以工作。因此,我倾向于将第一行更改为以下内容:

cmake_minimum_required(VERSION 3.1)

保持软件包的一致性

如您所知,find_package() 通常会为软件包设置 package_INCLUDE_DIRSpackage_LIBRARIES。我发现始终使用这些变量比仅使用最少量的变量更有利于简化 CMake 脚本的维护。因此,我不会担心它们是否必需,而是会这样做:

find_package(CURL REQUIRED)
include_directories(${CURL_INCLUDE_DIRS})
add_executable(imu_data_access_cmake_test imu_data_access.cpp)
target_link_libraries(imu_data_access_cmake_test ${CURL_LIBRARIES})

不要将系统包含文件放在源代码中

请注意,我已省略了此行:

set(SOURCES imu_data_access.cpp ${CURL_INCLUDE_DIRS}/curl/curl.h)

并不是每个库包含的文件都需要或应该作为源代码列出。 一般来说,我只包括我的源代码,并且我建议你的项目也这样做。


如果我将CMake脚本剥离到最少,是否会减少编译时间?在CMake中不避免无需包含的文件/行是否存在任何缺点? - skr
由于编译时需要包含文件,因此在CMake中省略include_directories行不太可能对编译时间产生任何可测量的影响。请在您的计算机上测试以验证。 - Edward

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接