我将如何使用 CMake 包含 asio 库?

Posted

技术标签:

【中文标题】我将如何使用 CMake 包含 asio 库?【英文标题】:How would I include asio library using CMake? 【发布时间】:2020-03-08 22:17:48 【问题描述】:

我正在尝试为一个班级开发一个项目,并且我想使用 CMake 来构建该项目。我目前的项目看起来像

|-bin
|-CMakeLists.txt
|-include
 |-asio-1.12.2
 |-chat_message.hpp
 |-chat_message.cpp
 |-CMakeLists.txt
|-src
 |-Server.cpp

虽然我的 Server.cpp 需要 /include/asio-1.12.2/include 中的 asio.hpp。 教授有一个用标志编译它的makefile -DASIO_STANDALONE -Wall -O0 -g -std=c++11 -I./include -I./include/asio-1.12.2/include。我的 CMakeLists 文件如下所示: ./CMakeLists.txt

CMAKE_MINIMUM_REQUIRED(VERSION 3.12)
PROJECT(Server VERSION 0.0.1)
SET(CPP_STANDARD 11)
SET(CPP_STANDARD_REQUIRED True)
ADD_SUBDIRECTORY(include)
ADD_EXECUTABLE(Server src/Server.cpp)
TARGET_LINK_LIBRARIES(
   Server PRIVATE
   chat_message
   asio
)

./include/CMakeLists.txt

ADD_LIBRARY(
   chat_message
   chat_message.cpp
   chat_message.hpp
)
ADD_LIBRARY(
   asio
   asio-1.12.2/include/asio.cpp
   asio-1.12.2/include/asio.hpp
)
TARGET_INCLUDE_DIRECTORIES(
   chat_message PUBLIC "$CMAKE_SOURCE_DIR/include"
   asio PUBLIC "$CMAKE_SOURCE_DIR/include/asio-1.12.2/include"
)

如何将 asio 头文件链接到带有所需标志的 Server.cpp 文件?

【问题讨论】:

请注意,受target_include_directories 命令影响的唯一目标是第一个参数 表示的目标。对于进程两个目标,您需要调用target_include_directories 两次。 【参考方案1】:

首先,作为Tzyvarev pointed out in the comments,您必须将target_include_directories() 命令拆分为两个单独的命令。然后,这会将asiochat_message 的包含目录传播到您的Server 目标,这会将正确的包含标志添加到编译器标志。

注意:我建议从 CMAKE_SOURCE_DIR 切换到 CMAKE_CURRENT_SOURCE_DIR 并相应地改变你的路径,以使你的生活更轻松,如果你将来决定改变你的项目结构,因为你会通常将 CMakeLists.txt 文件保存在与其创建的目标的源代码相同的目录中。

-DASIO_STANDALONE 选项可以通过target_compile_definitions() 调用添加:

target_compile_definitions(asio PUBLIC ASIO_STANDALONE)

请注意,您不需要 -D - CMake 将为您生成正确的编译器标志。此外,由于这是 asio 目标的要求,并且它的所有消费者都需要它,所以应该将它添加到其中,而不是它的消费者 - 然后它将根据需要传播到依赖项。

在您的 CMakeLists.txt 中,您设置了 CPP_STANDARDCPP_STANDARD_REQUIRED 变量。你所追求的分别是CMAKE_CXX_STANDARDCMAKE_CXX_STANDARD_REQUIRED

这将为整个项目的所有目标设置标志。

有多种方法可以添加错误、优化和调试符号标志,您使用哪一种取决于您的用例。以下不是详尽的列表。

如果您希望构建库的每个人都拥有这些,而不考虑构建配置(调试/发布/等),您可以在 CMakeLists.txt 中设置 CMAKE_CXX_FLAGS 变量 如果您希望每个人都拥有标志,但仅限于某些构建类型,请设置CMAKE_CXX_FLAGS_<CONFIG> 变量,其中<CONFIG> 是选择的构建类型(DEBUG/RELEASE/MINSIZEREL/RELWITHDEBINFO 默认可用)李> 如果您不想将标志强加于每个人,在调用 CMake 之前,您可以设置 CXXFLAGS 环境变量。但请注意,根据文档,如果在 CMake 脚本中设置了 CMAKE_CXX_FLAGS,这将无效。 如果您想为单个目标添加标志,可以在其上调用target_compile_options,并设置适当的可见性选项以启用/禁用向消费者的传播。

但是,通常您在使用这些时确实需要考虑可移植性。例如,GCC 可能支持在 Clang 中可能不同的某个标志。

编辑地址this评论

由于只有头文件的 ASIO 库不喜欢使用上面提到的编译器定义进行编译,因此有两种方法可以解决它:

移除 ASIO_STANDALONE 编译器标志

从您的角度来看,这将是最简单的事情,但作为连锁反应,它需要您在系统上安装 Boost,因为没有上面的标志会导致预处理器运行通过一些 Boost 包括。可能还有其他影响,但这是我在继续下面的解决方案之前遇到的第一个。

保留标志,并使用 CMake 接口库

add_library() 可以让您添加一个实际上不会产生任何编译对象/库/可执行文件的目标,而只是一个逻辑 CMake 目标,它可以像任何其他目标一样拥有属性 - 包括目录、链接库等。所以至少你可以这样做:

add_library(asio INTERFACE)
target_compile_options(asio INTERFACE ASIO_STANDALONE)
target_include_directories(asio INTERFACE <dir where asio.hpp lives>)
target_link_libraries(asio INTERFACE <threads>)  # Using ASIO requires you link your final executable/library with your system's threading library (e.g. pthread on linux)

然后当你像链接另一个目标时

target_link_libraries(any_lib PRIVATE asio)

any_lib 将继承使用 ASIO 构建所需的所有属性。

您选择的解决方案将由您的用例决定,但如果您必须像您的教授那样做,那么请走 INTERFACE 库路线。

【讨论】:

这里建议的所有内容都对我有所帮助,谢谢。现在唯一吐出的是错误:“不要编译定义了 ASIO_HEADER_ONLY 的 Asio 库源”@Pesho_T @Ruben-96 好的,我想我知道怎么了,我会编辑我的答案来帮助你解决它

以上是关于我将如何使用 CMake 包含 asio 库?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Cmake,如何防止库源包含在我的 IDE 中?

CMake项目结构:如何正确地将库合并在一起并将它们包含在多个可执行文件中

CMake:如何只包含 OpenCV 的一部分?

[cmake]如何使用 cmake 在 windows 上包含和链接系统库

CMake C++ 包含静态系统库到项目 - 如何

如何使用cmake生成基于静态库的动态链接库