介绍在Windows 下使用 Boost 的文章很多,但多是讲如何在Visual Studio 界面下直接操作的,这里介绍一下CMake 项目如何使用 Boost.
本文使用的cmake 版本为 3.23.1
Boost 最新版的下载地址为 Boost Downloads 我写此文章的时候最新版本为1.79.0。按照 Boost 官方的介绍,.7z 的文件 比 .zip 要小一半,可以的话还是使用.7z 格式。
Boost 的库分为两种,一种是Header only, 也就是只要 include 头文件就可以了。另一种需要提前构建,比如 boost_system、boost_log 等。而编译又分为编译成动态库与静态库两种形式。
开始之前先简单介绍一下动态库与静态库。
所谓动态库,通常只看到DLL 文件,实际上动态库由.lib 文件 与 .dll 两部分组成 .lib 定义了动态库的内容,在程序连接的时候使用,.dll 才是真正的库文件,在运行的时候使用。
静态库只有.lib 文件。在连接的时候,静态库的内容会被连接到应用程序中。在运行的时候不需要任何其它的文件支撑了。下面先介绍 boost 编译成动态还是静态库。
Windows 上 boost 最简单的编译方法与 Linux 上是一致的。
bootstrap
b2 --prefix=c:\Boost install
编译后 C:\Boost 下会有两个目录,分别是 include 与 lib
include 目录下的内容如下:
lib 目录下的内容为:
可以看到每个文件均有前缀 lib,代表其是静态库。每个库由4个文件组成,分别代表了不同的编译选项:
CMakeLists.txt 内容如下:
cmake_minimum_required(VERSION 3.23 FATAL_ERROR)
project(Compiling_Boost CXX)
set(BOOST_ROOT C:/boost)
set(Boost_LIBRARY_DIRS c:/boost/lib)
find_package(Boost 1.78 REQUIRED COMPONENTS regex)
link_directories(${Boost_LIBRARY_DIRS})
include_directories(${Boost_INCLUDE_DIRS})
message(Boost\ version: ${Boost_VERSION_STRING})
message(Boost\ include\ dir: ${Boost_INCLUDE_DIRS})
message(Boost\ library\ dir: ${Boost_LIBRARY_DIRS})
message("Found Boost Libraries:")
foreach(boost_lib IN LISTS Boost_LIBRARIES)
message(${boost_lib})
string(REGEX MATCH ".+/.*boost_([^-]+)-.+\.(lib|a)" boost_lib_name ${boost_lib})
set(boost_lib_name ${CMAKE_MATCH_1})
set(boost_target Boost::${boost_lib_name})
if(TARGET ${boost_target})
message(STATUS "Boost target found: " ${boost_target})
endif(TARGET ${boost_target})
endforeach(boost_lib)
add_executable(regex regex.cpp)
#cmake -G "MinGW Makefiles" ..
regex.cpp 的内容如下:
#include
#include
#include
int main()
{
std::string line;
boost::regex pat( "^Subject: (Re: |Aw: )*(.*)" );
while (std::cin)
{
std::getline(std::cin, line);
boost::smatch matches;
if (boost::regex_match(line, matches, pat))
std::cout << matches[2] << std::endl;
}
}
编译并执行
mkdir build
cd build
cmake ..
cmake --build . --config Release
因为我的项目确定要使用 64位多线程版本,因此使用下面的命令进行编译
bootstrap
b2 --layout=system variant=release -j 8 address-model=64 link=shared threading=multi --prefix=c:\Boost install
注意里面的选项:
system 文件名最短,只有库名称。
看看编译后的结果
每一个库由两个文件组成,一个连接时使用的 .lib 另一个是运行时使用的 .dll
由于是动态库,文件名前没有 lib 前缀。
编译我们的例子:
C:\src\boost_test\build>cmake ..
-- Building for: Visual Studio 16 2019
-- Selecting Windows SDK version 10.0.19041.0 to target Windows 10.0.18363.
-- The CXX compiler identification is MSVC 19.28.29920.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Tools/MSVC/14.28.29910/bin/Hostx64/x64/cl.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found Boost: C:/Boost/lib/cmake/Boost-1.79.0/BoostConfig.cmake (found suitable version "1.79.0", minimum required is "1.78") found components: regex
Boost version:1.79.0
Boost include dir:C:/Boost/include
Boost library dir:c:/boost/lib
Found Boost Libraries:
Boost::regex
-- Configuring done
-- Generating done
-- Build files have been written to: C:/src/boost_test/build
C:\src\boost_test\build>cmake --build . --config Release
Microsoft (R) Build Engine version 16.9.2+58c36d143 for .NET Framework
Copyright (C) Microsoft Corporation. All rights reserved.
Checking Build System
Building Custom Rule C:/src/boost_test/CMakeLists.txt
regex.cpp
regex.vcxproj -> C:\src\boost_test\build\Release\regex.exe
Building Custom Rule C:/src/boost_test/CMakeLists.txt
regex 编译成功!
cmake 是如何确定要使用动态库还是静态库呢?方法有很多,可以整体设置,也可以一个库一个库的设置。我将一个项目从Linux 转到 Windows 时,刚开始编译连接的时候报错:boost_system.lib 没找到,好不容易将 boost 编译好了 boost_system.lib也找到了,又报 boost_log-vc142-mt-x64-1_79.lib 没找到,而它们都是用的同一个命令加入到项目中的。
target_link_libraries(mylib
boost_system
boost_log
boost_log_setup
boost_date_time)
cmake 通过如下几种方式设置连接要使用的库:
这个最容易理解,在没有其它设置的情况下,尽量与项目中的其它设置相匹配,找到哪个就用哪个。
至于如何找,不同的cmake 版本也稍有不同。不过C:\Boost\lib\cmake 起了很大的作用。但是并不是所有的cmake 都支持这种方法,如果你的cmake 版本不支持此方法的话,在b2 时加上 --no-cmake-config 不生成此目录。
单纯设置使用动态库,可以在CMakeLists 中加入
SET(BOOST_ALL_DYN_LINK ON)
也可以只指定一个库,比如 log
SET(BOOST_LOG_DYN_LINK ON)
在CMakeLists 中可设置的变量比较多,这里列几个例子,值得一提的是,这些设置要在find_package 之前进行设置,然后cmake 会按照这些设置去找对应的版本。详细信息可以参见 boost 官网 Configuring and building the library - 1.79.0 (boost.org)
set(BUILD_SHARED_LIBS ON)
set(Boost_USE_DEBUG_LIBS ON)
set(Boost_USE_RELEASE_LIBS ON)
set(Boost_USE_STATIC_LIBS OFF)
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_RUNTIME OFF)
set(Boost_USE_DEBUG_RUNTIME ON)
set(Boost_COMPILER "-vc142")
set(Boost_DEBUG ON)
find_package(Boost 1.79 REQUIRED COMPONENTS system log log_setup)
此方法极其容易出问题,不到万不得已不要使用。我在此介绍此方法可不是为了让你使用它,而是实在找不到问题的时候,查查是不是别人使用了此方法。
要在源代码中设置,就是在代码中加入以下语句:
#define BOOST_ALL_DYN_LINK 1
当然也可以只指定一个库
#define BOOST_LOG_DYN_LINK 1
强调一下,当我们说编译 boost 成动态库还是静态库时,还有一个概念也很容易混淆。运行时连接,它指的是在编译 boost 时,编译器使用的库,是否要编译到boost 库中。如果包含运行时库,也就是静态方式,编译出来的 boost 库,无论是动态还是静态,都不需要其它库支撑;而如果不包含运行时,动态运行时方式,编译出来的boost 库通常需要 MSVCRTXX.DLL 及MSVCPXX.DLL一类的文件才能运行。编译时b2的选项为 --runtime-link 对应 VC 的编译选项为 Multithreaded
或者 Multithreaded DLL
考虑到我们编译的是boost 而在使用boost 时通常也少不了运行时库,所以除非极特别情况,这一项就保持缺省值,具体细节留给编译器处理就好了。