将Boost添加到CMake项目中?

17

背景

我完全不懂C++,遇到了一个又一个的问题,所以如果这很简单而且我“只是那么愚蠢”,请原谅我...

我的项目应该最终在Linux中编译和运行。不幸的是,在我的C++开发环境遇到了很多问题(仍未解决)之后,我放弃了在Linux中开发,并转向Windows Visual Studio 2017。我希望先在Windows中让我的代码工作,然后,由于C ++被认为是一种便携式语言,它应该只需要进行最少量的更改就可以在Linux中正常工作。

Visual Studio似乎工作了一天左右。我可以编写代码,点击“编译”,然后像魔术一样运行。我组合了几个类来构建有向无环图,使用标准库进行哈希表,然后我尝试创建一个套接字......

Windows和Linux使用不同的套接字库(<sys/socket.h> vs <winsock.h>),因此我需要某种方式来抽象出差异,并且我更喜欢一个成熟的标准。在Google上搜索时,我找到了Boost库,似乎符合我的需求......这就是一切都变得糟糕的时候。

我的项目设置

因为这个项目将在多个平台和IDE上开发(有些人使用Windows + Visual Studio,有些人使用Mac + Eclipse,其他人使用Linux + VIM),所以我选择将其作为CMake项目。经过几个小时的阅读、学习和研究,似乎CMake应该能够给我想要的东西(方便且可重复的跨平台构建,几乎没有依赖问题)。

Creating a new project, pt 1 Creating a new project, pt 2

我的源代码(直接来自Boost在Windows上入门指南)如下:

CMakeProject2.cpp

#include <boost/lambda/lambda.hpp>
#include <iostream>
#include <iterator>
#include <algorithm>

int main()
{
    using namespace boost::lambda;
    typedef std::istream_iterator<int> in;

    std::for_each(
        in(std::cin), in(), std::cout << (_1 * 3) << " ");
}

根据在Windows上开始使用Boost指南,我从这里下载了Boost:

https://dl.bintray.com/boostorg/release/1.67.0/source/boost_1_67_0.zip

有趣的是,入门指南的标题为“在Windows上启动Boost - 1.69.0”,但它链接到Boost版本1.67.0。

下载并解压ZIP文件后,我得到了一大堆文件,但不知道该把它们放在哪里:

Boost download directory

迄今为止尝试让它工作的方法

我尝试将Boost库添加到我的项目中, 但是没有出现任何预期的菜单选项:

Right-clicking on project in Solution Explorer

尽管我无法 找到 任何 一个 页面来警告你这个陷阱,显然CMake项目没有难以捉摸的“属性”窗口——而是必须通过CMakeLists.txt文件来包含第三方库。
首先,我将Boost ZIP文件的整个540 MB内容复制到我的项目中,文件夹名为“Boost”:

Boost in project

接着,我尝试了一系列不同的CMakeList.txt命令:

根据如何在CMakeLists.txt中添加Boost库?

set(Boost_USE_STATIC_LIBS OFF) 
set(Boost_USE_MULTITHREADED ON)  
set(Boost_USE_STATIC_RUNTIME OFF) 
find_package(Boost REQUIRED COMPONENTS lambda) 

if(Boost_FOUND)
    include_directories(${Boost_INCLUDE_DIRS}) 
    add_executable(CMakeProject2 "CMakeProject2.cpp" "CMakeProject2.h") 
    target_link_libraries(CMakeProject2 ${Boost_LIBRARIES})
endif()

根据https://www.selectiveintellect.net/blog/2016/7/29/using-cmake-to-add-third-party-libraries-to-your-project-1的介绍:
include("Boost")
add_subdirectory("Boost")
add_subdirectory("boost")
add_subdirectory("Boost/boost")
add_subdirectory("Boost/boost/lambda")

target_link_libraries(boost)
target_link_libraries(Boost)

根据https://cmake.org/pipermail/cmake/2009-November/033249.html的说法:

SET (Boost_FIND_REQUIRED TRUE)
SET (Boost_FIND_QUIETLY TRUE)
SET (Boost_DEBUG FALSE)
set (Boost_USE_MULTITHREADED TRUE)
set (Boost_USE_STATIC_LIBS TRUE)
SET (Boost_ADDITIONAL_VERSIONS "1.67" "1.67.0")

FIND_PACKAGE(Boost COMPONENTS  lambda)
INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS})
LINK_DIRECTORIES(${Boost_LIBRARY_DIRS})

我尝试了几个咒语(因为不熟悉C++或CMake作为工具),但要么从CMakeLists.txt中收到错误,要么从CMakeProject2.cpp中收到“无法打开源文件“boost/lambda/lambda.hpp””的错误。实际上,关于那些“CMakeLists.txt”的错误,在我的文件中添加了足够的行后,我开始经常崩溃Visual Studio。请注意,我有第八代i7、32GB的RAM和一个M.2 NVMe硬盘——所以我相当惊讶,几行文本文件足以惹恼微软,让我的电脑每次锁定10分钟。

如果所有这些都失败了,我尝试将我需要的文件直接复制到我的项目中:

New project setup

现在,我是C/C++开发的新手,所有可能出错的事情都出错了。到目前为止,我花了将近两个星期的时间,在两台电脑上、三个IDE和四个编译器上勉强编译出一个"Hello, World"应用程序。我还没有成功地引入任何第三方库,无论是从哪里下载的,无论其受欢迎程度或简单程度如何,也没有编译出引用该库的可运行程序。所以当我说:我不知道"只有头文件库"和...其他什么区别时,请相信我。我只知道根据在Windows上开始使用Boost指南,大部分Boost都是"只有头文件"的,因此我不需要任何构建步骤--使用它应该很简单。此外,这个例子(使用boost::lambda)是-根据他们的说明-一个只有头文件的库,因此应该非常容易使用。
现在,我稍微更新了源代码,让它查找当前目录,而不是查找系统包含目录(在我目前的认识中,在Windows中不存在这个目录):
#include "boost/lambda/lambda.hpp"
#include <iostream>
#include <iterator>
#include <algorithm>

int main()
{
    using namespace boost::lambda;
    typedef std::istream_iterator<int> in;

    std::for_each(
        in(std::cin), in(), std::cout << (_1 * 3) << " ");
}

现在我可以手动验证此文件是否存在(可以在文件资源管理器中找到文件CMakeProject2 \ CMakeProject2 \ boost \ lambda \ lambda.hpp) - 但我仍然会收到错误消息:“无法打开源文件“boost / lambda / lambda.hpp””。进一步的搜索使我更新了我的CMakeLists.txt文件,将其更新为当前形式:
# CMakeList.txt : CMake project for CMakeProject2, include source and define
# project specific logic here.
#
cmake_minimum_required (VERSION 3.8)

# Add source to this project's executable.
file(GLOB CMakeProject2_SRC
    "*.h"
    "*.cpp"
    "**/*.h"
    "**/*.cpp"
    "**/*.hpp"
    "boost/lambda/lambda.hpp"
)
add_executable (CMakeProject2 ${CMakeProject2_SRC})
#add_executable (CMakeProject2 "CMakeProject2.cpp" "CMakeProject2.h")

# TODO: Add tests and install targets if needed.

尽管如此,我仍然遇到了错误:无法打开源文件 "boost/lambda/lambda.hpp"。此时我快要抓狂了。我做错了什么?我不知道什么?为什么像"Hello, World"这样简单的Boost等效物对我不起作用?

尝试使用基于目标的API。target_link_libraries(you_exe PUBLIC boost::lambda) 这个单一命令应该会添加包含目录以及链接库。 - Guillaume Racicot
@GuillaumeRacicot https://imgur.com/ChwBo57 没有运气 - stevendesu
要使用目标,您必须首先找到 boost 软件包:find_package(Boost COMPONENT lambda REQUIRED) - Guillaume Racicot
第一次尝试使用boost时,我遇到了类似的错误。现在我不在我的主要电脑旁边,等我有实际接触它的时候,我会告诉你的。我知道我成功地让它工作了。据我记得,是cmake没有正确检测到更新的boost版本,但我现在不能百分之百确定。 - Guillaume Racicot
我正在调整我的设置,我想我会有一个答案给你。对我来说确实是版本的问题。 - Guillaume Racicot
显示剩余2条评论
4个回答

10

以下配方应该可行

官方Boost二进制文件位置下载Boost二进制文件并安装到C:\ Boost。

大多数时候,您不需要自己构建Boost。

您的CMakeLists.txt应如下所示

cmake_minimum_required (VERSION 3.8)

project(boostAndCMake)

set(BOOST_ROOT "C:\Boost") # either set it here or from the command line  
set(Boost_USE_STATIC_LIBS OFF) 
set(Boost_USE_MULTITHREADED ON)  
set(Boost_USE_STATIC_RUNTIME OFF) 
find_package(Boost REQUIRED COMPONENTS system) # header only libraries must not be added here

add_executable(CMakeProject2 CMakeProject2.cpp CMakeProject2.h) 
target_include_directories(CMakeProject2 PUBLIC ${Boost_INCLUDE_DIRS}) 
target_link_libraries(CMakeProject2 ${Boost_LIBRARIES})

因为我们在find_package调用中使用了REQUIRED,所以如果找不到它,CMake将失败执行并跳过脚本的其余部分。因此,无需检查Boost_FOUND。只有当你省略了REQUIRED时,才需要检查它。
现在从命令行中调用,在你的脚本所在目录中:
cmake -H. -Bbuildit -G "Visual Studio 15 2017" -DBOOST_ROOT=C:\Boost 

这将在当前目录下创建一个名为buildit的构建目录,进一步在构建目录内创建适用于Visual Studio 2017的解决方案,并提供变量BOOST_ROOT的设置,该变量用于在find_package调用中识别计算机上Boost目录。要查看在find_package(Boost ...)调用中可用的选项,请参见CMake中FindBoost文档。

仅限头文件库

如果您的库仅包含头文件,则需要从find_package(Boost ...)调用中省略它们。要查看哪些库不是仅限头文件,请参见此帖子

使用较新的Boost版本

如果您的CMake安装无法找到所请求的版本(例如1.69.0),但支持更近期的Boost版本命名方案,则可以使用set(Boost_ADDITIONAL_VERSIONS "1.69.0" "1.69")进行设置。最近一次更改Boost命名方案是从1.65.1到1.66。


据我所知,顶部的“下载最新版本”链接只是下载了一个旧版本(1.66.0与1.67.0相比)的Boost目录,其中包含几个Boost头文件、文档等。鉴于您说要下载Boost“二进制文件”,我想您指的是版本文件夹内的其中一个EXE文件。点击“1.69.0”后,会显示几个EXE文件--我怎么知道该下载哪一个?我不确定我的MSVC版本是多少(或者如何找出):https://imgur.com/3TRTJgl - stevendesu
这是针对您的编译器Visual Studio 15 2017的MSVC 14.1版本。请参见此处:https://blogs.msdn.microsoft.com/vcblog/2017/11/15/side-by-side-minor-version-msvc-toolsets-in-visual-studio-2017/ - vre
下载了 boost_1_69_0-msvc-14.1-64,双击可执行文件后,同意运行来自未知发布者的应用程序,Windows 的“Antimalware Service Executable”开始锁定我的整个系统。如果这种情况持续太久,我将禁用反恶意软件/防病毒软件并重试。现在 explorer.exe 无响应。 - stevendesu
在我的电脑无响应了整整20分钟后,我强制关机,重新启动并再次尝试(没有先禁用防病毒软件,以查看是否只是偶然冻结)。资源管理器再次冻结。10分钟后,我进行了另一次硬关机、重启、禁用防病毒软件并运行它——立即起作用。然后尝试了你的CMakeLists.txt,仍然不行:https://imgur.com/sz8mbnu - stevendesu
我经常忘记在目标后添加PUBLICPRIVATEINTERFACE关键字。因此,它应该写成target_include_directories(CMakeProject2 PUBLIC ${Boost_INCLUDE_DIRS}) - vre
显示剩余12条评论

4
以下是Boost 1.68和CMake 3.12的工作设置。据说CMake 3.12无法正确检测Boost 1.69,因为cmake本身无法构建boost,所以cmake必须提供一个FindBoost.cmake模块来跟踪boost变化。总之,CMakeLists.txt非常简短:
cmake_minimum_required(VERSION 3.11)

project(foobar)

find_package(Boost 1.68 REQUIRED)
add_executable(foo foo.cpp)
target_link_libraries(foo PUBLIC Boost::boost)

当然,你可以将它分成许多子目录。

在命令行中调用CMake应该像这样:

cmake -DCMAKE_PREFIX_PATH=path_to_local_directory ..

其中path_to_local_directory是您希望依赖于的所有库的安装路径,它适用于Boost、nlohmann_json、glfw3、Qt等等,无所不包 *(1)。对于我的情况,它是C:/local/,另一个情况则是../external/(是的,它可以是项目本地的目录!)

让我们来看一下我自己的C:/local/

ls -l /c/local/
total 12
drwxr-xr-x 1 myself 197609 0 May 26  2018 boost_1_67_0/
drwxr-xr-x 1 myself 197609 0 Sep  5 02:02 boost_1_68_0/

警告: 确保您的编译器架构与已安装的boost版本相同,否则cmake将无法找到它。

我认为就是这样。下一个CMake版本(3.14)应该可以与最新的boost一起使用。


*(1) 该库需要导出其CMake目标,或者您必须提供FindXXX.cmake文件。


如果您的CMake版本支持更近期的Boost版本命名方案,则可以使用set(Boost_ADDITIONAL_VERSIONS "1.69.0" "1.69")。 Boost命名方案的最后更改是从1.65.1到1.66。 - vre
@vre 谢谢您提供的信息,我会在我的项目中尝试它。 - Guillaume Racicot

1
我正在使用带有 Boost 版本 1.78 的 CMake 3.22。
最简单的解决方案是在调用 CMake 时设置 Boost_INCLUDE_DIR
cmake -DBoost_INCLUDE_DIR=boost

将Boost库所在的目录传递给程序。如果你正在使用Visual Studio,你也可以在CMake设置中进行设置:

enter image description here

或者,在CMakeSettings.json文件中:
"cmakeCommandArgs": "-DBoost_INCLUDE_DIR=boost",

在我看来,这比使用set函数更好,因为你没有硬编码路径。

-3
在你的CMakeList.txt中添加target_include_directories(CMakeProject2 PRIVATE .)
.是从CMakeLists.txt到boost/lambda/lambda.hpp的相对路径。
同时,你不应该将任何.hpp文件添加到源列表中。

好的,我看了你的截图...首先,target_include_directories()命令是为了将一个包含目录添加到cmake目标中(即可执行文件CMakeProject2),并且必须放在add_executable()之后。此外,在add_executable()中最好使用明确的文件名,而不是通配符。在你的情况下应该这样写:add_executable(CMakeProject2 CMakeProject2.cpp)。 - rene-d
但这并没有真正解释为什么找不到boost/lambda/lambda.hpp。 - rene-d
target_include_directories 移动并用明确的文件名替换 glob 后,仍然出现错误。 - stevendesu
这个问题引起了我的兴趣...所以我刚刚建立了一个像你的项目一样的工程...在Linux上使用cmake 3.6,在macOS上使用3.13进行了测试 - 好吧,它不是vs2017和windows。正如你所猜到的那样,它可以运行。https://github.com/rene-d/test-CMakeProject2。 - rene-d
听起来像是我在使用Clang++时遇到的问题(参见:https://dev59.com/P-k5XIcBkEYKwwoY597M)。最终我加入了Clang IRC并进行了数小时的调试,但没有人能够解决它。有时候我觉得这就是我的生活:https://xkcd.com/2083/。 - stevendesu
让我们在聊天中继续这个讨论 - rene-d

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