在 Windows 上编译带 ICU 的 Boost::Regex

文章目录
  1. 1. 一、编译 ICU
  2. 2. 二、编译 bjam
  3. 3. 三、编译 Boost::Regex
  4. 4. 四、用户代码

找遍了文档和文章,没有能直接解决问题的,就将我的方法记录下来好了。以及,明天周例行检查过了的话,优先把 CGSS 反向系列的剩下两篇写完。但是心好急啊,还没开始动工论文!

Boost 的 Boost::Regex 默认是不带 ICU 支持的。在这种情况下,它只能用来处理多字节字符串(MBCS)的正则表达式操作,而这个字符串是和系统的区域信息(locale)相关的。同样是简体中文区域,Windows 的字符集是 GBK,OSX 和 Linux 的字符集是 UTF-8。如果不给这个库增加其他字符集的支持,那它基本上就对多字节字符(汉字、喃字、假名等等)无能为力了,毕竟在表示“ASCII 以外的字符”这一个概念时不同编码方案码是不一样的。不过好在 Boost 的设计者很清楚这一项需求,在 boost/regex/icu.hpp 中定义了两个额外的正则表达式结构:wregex(处理宽字符)和 u32regex(处理 UTF-8、UTF-16 和 UTF-32),不过内部要依赖于 ICU。考虑到我准备使用 UTF-8 作为字符串的编码基础,必须要用上 ICU;而要用 ICU,就必须自己编译 Boost。

注意,接下来的部分过程只适用于 Windows,因为 Linux 一般是预置了 ICU 的,只需要简单指定就好,bjam 会自动完成设置。

一、编译 ICU

ICU 也是一个独立的库,和 Boost 是同级的关系。要用 ICU 的功能,必须要先编译 ICU。

到 ICU 的官网上下载源代码包(需要翻墙),编译很简单,打开 source/allinone/allinone.sln,编译就好。注意选择 Release 模式编译。

二、编译 bjam

bjamb2)是 Boost::Build 的别称,是一个自动构建程序,附带在 Boost 源代码包中。正常的情况下,直接执行 bootstrap.bat 就能自动调用 Visual C++ 编译 bjam

三、编译 Boost::Regex

我之前将 ICU 放在了 F:\icu,下面的路径按照需要更改即可。

执行 bjam 的时候它会先启用检测,输出类似这样的提示:

1
2
3
4
5
6
7
8
9
10
11
12
Performing configuration checks

- 32-bit : yes
- arm : no
- mips1 : no
- power : no
- sparc : no
- x86 : yes
- symlinks supported : no
- junctions supported : yes
- hardlinks supported : yes
- has_icu builds : no

如果 has_icu builds 一项值为 no,按照说明中仅指定 ICU_PATH 是不行的,编译出的库照样不包含 ICU 功能(表现为链接时找不到与 ICU 相关的符号)。所以,只好想一种方法让 has_icu builds 这一项变为 yes 了。

检查错误日志 config.log 发现,这一项是用构建一段含 ICU 的代码来检测的。编译是通过了(如果没指定 ICU_PATH 就无法编译),但是说链接时找不到符号。这就好办了,加入静态库搜索路径和库名(注意按照 VC++ 的选项格式)执行,果然就能正常编译链接了,这一项也变成了 yes

我现在所使用的完整选项如下:

1
bjam boost.locale.icu=on boost.locale.winapi=off boost.locale.iconv=off boost.locale.std=off -sHAVE_ICU=1 -sICU_PATH=F:\icu -sICU_LINK="/LIBPATH:F:\icu\lib icudt.lib icuin.lib icuio.lib icule.lib icule.lib iculx.lib icutu.lib icuuc.lib" --with-regex --toolset=msvc-14.0 link=static threading=multi stage

bjam 中的 -s 开关相当于设置临时环境变量,不是选项开关。你也可以通过 set ICU_PATH=F:\icu 这样的语法,在命令提示符中设置。MSVC 14.0 工具集指的是 Visual Studio 2015 的工具链(cl.exe 版本 19)。

bjam 是带缓存的。生成的中间产物在 bin.v2/libs 下,成品在 libs 下。bjam 并不会检查当前的遗留编译结果是否是最新,只要文件存在而且大小不为零就会被跳过,环境改变(如 ICU 的存在性发生改变)也不会引发重新编译。所以清除缓存的话要删除这两个目录,强制完全重新编译。设置缓存在 bin.v2/project-cache.jam,如果初次检测的结果是 ICU 不存在,而后来配置好了 ICU,就需要删除这个文件并重新执行 bjam 来检测,否则一直会用其中缓存的结果。

四、用户代码

首先更新包含文件搜索目录。加入 Boost 的头文件目录(boost/boost)和 ICU 编译后生成的头文件目录(icu/include)。

然后更新库文件搜索目录。加入 Boost 的静态库目录(boost/stage/lib)和 ICU 编译后生成的库文件目录(icu/lib)。

然后包含(注意 BOOST_HAS_ICU 的定义,及其位置):

1
2
3
4
5
6
7
#define BOOST_HAS_ICU
#include <boost/regex.hpp>
#include <boost/regex/icu.hpp>
#pragma comment(lib, "icuuc.lib")
#pragma comment(lib, "icuin.lib")
#pragma comment(lib, "icudt.lib")
#pragma comment(lib, "libboost_regex-vc140-mt-1_59.lib")

注意引用的库名字 libboost_regex-vc140-mt-1_59.lib 的含义如下:

  • regex:正则表达式部分;
  • vc140:用 VS 2015 的 VC++ 工具链编译(编译出的静态库需要用同版本工具链才能链接);
  • mt:线程模型为多线程(上面 threading=multi 指定的,这也是 VC++ 工程的默认选项);
  • 1_59:Boost 版本为 1.59。

在用在自己的工程中时一定要注意改名。当然这些库文件搜索也可以放到 VC++ 工程的“附加库文件”设置中。

此时就可以正常编译链接了。(又是一次数小时排错的过程……)

分享到 评论