在 Windows 下通过 CMake 使用 Boost 库

介绍在Windows 下使用 Boost 的文章很多,但多是讲如何在Visual Studio 界面下直接操作的,这里介绍一下CMake 项目如何使用 Boost.

本文使用的cmake 版本为 3.23.1

下载 Boost

Boost 最新版的下载地址为 Boost Downloads 我写此文章的时候最新版本为1.79.0。按照 Boost 官方的介绍,.7z 的文件 比 .zip 要小一半,可以的话还是使用.7z 格式。

Boost 的库分为两种,一种是Header only, 也就是只要 include 头文件就可以了。另一种需要提前构建,比如 boost_system、boost_log 等。而编译又分为编译成动态库与静态库两种形式。

Windows 动态库与静态库

开始之前先简单介绍一下动态库与静态库。

所谓动态库,通常只看到DLL 文件,实际上动态库由.lib 文件 与 .dll 两部分组成 .lib 定义了动态库的内容,在程序连接的时候使用,.dll 才是真正的库文件,在运行的时候使用。

静态库只有.lib 文件。在连接的时候,静态库的内容会被连接到应用程序中。在运行的时候不需要任何其它的文件支撑了。下面先介绍 boost 编译成动态还是静态库。

编译成静态库

Windows 上 boost 最简单的编译方法与 Linux 上是一致的。

bootstrap
b2 --prefix=c:\Boost install 

编译后 C:\Boost 下会有两个目录,分别是 include 与 lib

include 目录下的内容如下:

在 Windows 下通过 CMake 使用 Boost 库_第1张图片

lib 目录下的内容为:

在 Windows 下通过 CMake 使用 Boost 库_第2张图片 

可以看到每个文件均有前缀 lib,代表其是静态库。每个库由4个文件组成,分别代表了不同的编译选项:

  • vc142 编译器的名称与版本;
  • mt 支持多线程;
  • gd Debug 版,没有的为 Release 版;
  • x32/x64 64位版本还是32位版本;
  • 1_79 boost 版本; 

 建立一个项目测试 boost

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 

注意里面的选项:

  • --layout 写定义了文件名的结构。支持 versioned、tagged、system 三种选项,缺省就是versioned 编译静态库时使用的就是缺省值,生成的文件名信息最全。tagged 文件名中不包含编译器名称与版本,本例中为vc142,也不包含boost 版本,本例中为1_79

        system 文件名最短,只有库名称。    

  • variant 可以是release 或 debug,也可以是 release, debug 两个都编译。本例中,因为 --layout=system,如果同时编译 release 与 debug 版本,文件名字相同,因此编译时只能选一种,否则会报错。variant 还有其它选项,可以看 b2 --help
  • j 8 编译时可用的线程数
  • address-model 不是多讲,编译 64 位版本。
  • link=shared 就是要编译成动态库。

看看编译后的结果

在 Windows 下通过 CMake 使用 Boost 库_第3张图片

每一个库由两个文件组成,一个连接时使用的 .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 是如何工作的?

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.txt 中设置

单纯设置使用动态库,可以在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

与runtime-link 的区别

强调一下,当我们说编译 boost 成动态库还是静态库时,还有一个概念也很容易混淆。运行时连接,它指的是在编译 boost 时,编译器使用的库,是否要编译到boost 库中。如果包含运行时库,也就是静态方式,编译出来的 boost 库,无论是动态还是静态,都不需要其它库支撑;而如果不包含运行时,动态运行时方式,编译出来的boost 库通常需要 MSVCRTXX.DLL 及MSVCPXX.DLL一类的文件才能运行。编译时b2的选项为 --runtime-link 对应 VC 的编译选项为 Multithreaded 或者 Multithreaded DLL

考虑到我们编译的是boost 而在使用boost 时通常也少不了运行时库,所以除非极特别情况,这一项就保持缺省值,具体细节留给编译器处理就好了。

你可能感兴趣的:(c++)