使用Boost和Emscripten

39

我有一个C++项目,想要将其转化为Web应用程序。为此,我想使用Emscripten来构建该项目。

该项目使用了一些外部库。我已经成功编译或找到了大多数库的JavaScript版本,现在我卡在了Boost库上。实际上,我甚至不知道如何开始使用Boost:它们使用一个引导脚本来生成构建库所需文件。可以向此脚本传递工具集,但是明显不支持Emscripten。

我的项目使用了以下Boost组件:Thread、Regex、FileSystem、Signals、System。如何使用Emscripten编译这些库?

编辑:

在npclaudiu的回答后,我使用gcc工具包引导了库,然后编辑了project-config.jam以配置编译器,将其替换为:

# Compiler configuration. This definition will be used unless
# you already have defined some toolsets in your user-config.jam
# file.
if ! gcc in [ feature.values <toolset> ]
{
    using gcc ;
}

随着

# Compiler configuration. This definition will be used unless
# you already have defined some toolsets in your user-config.jam
# file.
if ! gcc in [ feature.values <toolset> ]
{
    using gcc : : "/full/path/to/em++" ;
}
现在,键入./b2可以有效地构建库。Boost.Signals和Boost.System编译良好,而其他库存在一些错误。
Boost.Thread抱怨:
libs/thread/src/pthread/thread.cpp:503:27: error: use of undeclared identifier 'pthread_yield'
        BOOST_VERIFY(!pthread_yield());
                      ^
Boost.Regex抱怨CHAR_BIT未声明,但似乎这是emscripten的问题。
In file included from libs/regex/build/../src/c_regex_traits.cpp:28:
In file included from ./boost/regex/v4/c_regex_traits.hpp:26:
In file included from ./boost/regex/v4/regex_workaround.hpp:35:
/path/to/emscripten/system/include/libcxx/vector:1989:92: error: use of undeclared identifier 'CHAR_BIT'
static const unsigned __bits_per_word = static_cast<unsigned>(sizeof(__storage_type) * CHAR_BIT);
                                                                                       ^

Boost.FileSystem 由于emscripten似乎也会出现问题:

In file included from libs/filesystem/src/windows_file_codecvt.cpp:21:
/path/to/emscripten/system/include/libcxx/cwchar:117:9: error: no member named 'FILE' in the global namespace
using ::FILE;
      ~~^

网站后端还是前端? - Alex Chamberlain
这将是前端。我希望这个游戏可以在网页浏览器中玩。 - Julien
1
只是好奇,当编译为emscripten代码时,Boost占用多少MB? :D - CoffeDeveloper
6个回答

32

我终于成功使用emscripten编译所需的库。以下是我遵循的步骤。

emscripten中的更改

编辑system/include/libcxx/climits以添加以下定义(请参见http://github.com/kripken/emscripten/issues/531):

#ifndef CHAR_BIT
# define CHAR_BIT __CHAR_BIT__
#endif

#ifndef CHAR_MIN
# define CHAR_MIN (-128)
#endif

#ifndef CHAR_MAX
# define CHAR_MAX 127
#endif

#ifndef SCHAR_MIN
# define SCHAR_MIN (-128)
#endif

#ifndef SCHAR_MAX
# define SCHAR_MAX 127
#endif

#ifndef UCHAR_MAX

# define UCHAR_MAX 255
#endif

#ifndef SHRT_MIN
# define SHRT_MIN (-32767-1)
#endif

#ifndef SHRT_MAX
# define SHRT_MAX 32767
#endif

#ifndef USHRT_MAX
# define USHRT_MAX 65535
#endif

#ifndef INT_MAX
# define INT_MAX __INT_MAX__
#endif

#ifndef INT_MIN
# define INT_MIN (-INT_MAX-1)
# define INT_MIN (-INT_MAX-1)
#endif

#ifndef UINT_MAX
# define UINT_MAX (INT_MAX * 2U + 1)
#endif

#ifndef LONG_MAX
# define LONG_MAX __LONG_MAX__
#endif

#ifndef LONG_MIN
# define LONG_MIN (-LONG_MAX-1)
#endif

#ifndef ULONG_MAX
# define ULONG_MAX (LONG_MAX * 2UL + 1)
#endif
system/include/libcxx/cwchar中添加以下行。
#include <cstdio>

编译 Boost 为共享库

像 npclaudiu 建议的那样,使用 gcc 工具集引导库。然后编辑 project-config.jam 配置编译器并替换:

# Compiler configuration. This definition will be used unless
# you already have defined some toolsets in your user-config.jam
# file.
if ! gcc in [ feature.values <toolset> ]
{
    using gcc ;
}

和,随着

# Compiler configuration. This definition will be used unless
# you already have defined some toolsets in your user-config.jam
# file.
if ! gcc in [ feature.values <toolset> ]
{
    using gcc : : "/full/path/to/emscripten/em++" ;
}

boost/config/posix_features.hpp文件中,大约在第67行左右加入BOOST_HAS_SCHER_YIELD的定义。

之后编译库:./b2 thread regex filesystem signals system

将Boost编译为静态库

完成以上所有步骤,然后编辑tools/build/v2/tools/gcc.jam文件并将以下内容替换为:

toolset.flags gcc.archive .AR $(condition) : $(archiver[1]) ;

随着

toolset.flags gcc.archive .AR $(condition) : "/full/path/to/emscripten/emar" ;

toolset.flags gcc.archive .RANLIB $(condition) : $(ranlib[1]) ;

随着

toolset.flags gcc.archive .RANLIB $(condition) :
  "/full/path/to/emscripten/emranlib" ;

编译库:./b2 link=static variant=release threading=single runtime-link=static thread signals system filesystem regex


做得好,@julien。你尝试使用user-config.jam而不是硬编码路径了吗? - abergmeier
@LCIDFire 我对Boost的配置文件不够熟悉,所以我没有尝试使用user-config.jam。这真的是一个试错的过程,我还有工作要做,才能让我的游戏运行起来。 - Julien
@Julien,感谢您的帖子,我正在处理类似情况的项目,我按照您的建议构建了boost。有一个问题,您如何处理仅在头文件中的那些类/函数?它们没有编译成静态或共享库。 - jerry

12

值得一提的是,Boost现在包括一个名为"emscripten"的工具集,使用这个工具集(根据我的经验)可以避免上述过程。

使用方法与正常方式相同,先像平常一样启动boost,然后使用b2(或bjam)进行编译:

b2 toolset=emscripten 

5
确实很有前途,但我遇到了问题,例如“错误:在寻找最佳转换时发现歧义。尝试从以下内容生成类型 'SEARCHED_LIB':...” 你在OSX上使用Emscripten成功构建了Boost吗? - ofloveandhate
2
看起来这个错误是由 b2 查找外部库的方式引起的(例如 zlib、icu、bzip2 等)。我通过修改“boost_1_67_0/tools/build/src/build/generators.jam”来明确忽略生成器“searched-lib-generator”,绕过了这个问题。 - JoshS
1
如何解决错误:https://github.com/boostorg/regex/issues/59#issuecomment-828707747 - midzer

9

在较新版本的emscripten中,您可以使用端口轻松添加Boost库。现在只需将此标志添加到编译器链接器中即可:

-s USE_BOOST_HEADERS=1

如果您正在使用CMake,则可以像这样添加该标志:

set_target_properties(your_targets_name_here PROPERTIES COMPILE_FLAGS "-s USE_BOOST_HEADERS=1" LINK_FLAGS "-s USE_BOOST_HEADERS=1")

更多问题详情请查看此链接


在不使用emcc的情况下,有没有解决方案?我在使用-s USE_BOOST_HEADERS=1时仍然遇到链接错误。谢谢! - Dani P.

3
您可以尝试配置Boost库,指定gcc作为工具集,因为Emscripten自己推荐它作为gcc的替代品。此外,我认为在这种情况下最好构建Boost作为静态库。请记住,大多数Boost库都是仅包含头文件的,因为它们只定义模板类/函数。

1
我问题中列出的 Boost 库不仅仅是头文件,否则就太简单了 :) 我将尝试您的解决方案,我只需要找到如何告诉 Boost 使用 emcc 而不是 gcc 进行编译。 - Julien
我也遇到了同样的问题,Boost 的头文件库可以通过 -s USE_BOOST_HEADERS=1 参数正常工作,但其他库需要手动编译和链接,我在使用 emcc 或 emcmake 时遇到了麻烦,相关信息请参见 https://stackoverflow.com/questions/66292106/link-errors-with-boost-port-of-header-only-libraries-for-emscripten-in-c-was。 - Dani P.

2

emsdk更新:

经过多次尝试,我成功地使用以下方法将emscripten 1.39编译为Boost 1.71:

如果你还没有安装emsdk,请从https://emscripten.org/docs/getting_started/downloads.html下载并安装。

进入emsdk安装文件夹,并执行以下操作:

./emsdk install latest && ./emsdk activate latest && source ./emsdk_env.sh

导航到您想要克隆Boost存储库的目录并运行

git clone --recursive https://github.com/boostorg/boost.git

您可以添加参数'--jobs N',其中N是克隆子模块的进程数(如果这样做,速度会更快)。

cd boost

现在使用Bootstrap脚本创建Boost-Build可执行文件b2。
./bootstrap.sh

最后,由于您的emsdk已经在上面的步骤中被激活,因此您可以使用emconfigure构建Boost,以便将所有内容配置为调用gcc时使用emscripten。
emconfigure ./b2 toolset=gcc --prefix=<directory_to_install_to> --build-dir=<directory_for_intermediate_build_files>

现在要安装所需的包含文件和库到您选择的前缀目录中,请运行以下命令:
emconfigure ./b2 toolset=gcc --prefix=<directory_to_install_to> --build-dir=<directory_for_intermediate_build_files> install

嘿,在构建后实际的构建对象应该是什么?它们应该是WASM文件,对吧? - HSchmale

1

我不知道你是否看过常见问题解答中的这个问题,但以防万一:

问:我如何链接像SDL、boost等系统库?

答:在编译时,emscripten包含的系统库——libc、libc++(C++ STL)和SDL——会自动被包含进来(只包括必要的部分)。与其他编译器不同的是,你甚至不需要-lSDL(但-lSDL也不会有问题)。

对于emscripten未包含的其他库,例如boost,你需要自己编译并将其链接到程序中,就像它们是项目中的一个模块一样。例如,查看BananaBread链接到libz的方式。(请注意,在特定情况下,如果你只需要boost头文件,则无需编译任何内容。)

另一种未包含库的选项是将其实现为JS库,就像emscripten为libc(减去malloc)和SDL(但不包括libc++或malloc)所做的那样。请参见emcc中的--js-library。


1
我看到了这个问题,所以我尝试使用emscripten编译Boost。 - Julien

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