我在构建独立的 Web Assembly 时遇到了问题,我想要完全控制内存和布局。我不想使用 emscripten,因为正如下面的帖子所说,它没有给我所有我想要的编译时选项(例如堆栈大小控制、能够选择以独立模式导入内存等(我一直在浏览诸如以下内容的页面:如何使用 emscripten 生成独立的 webassembly 此外,Emscripten是矫枉过正的。
到目前为止我做了什么: 我有一个通过自制软件下载的完全工作的llvm 9工具链(我在macos 10.14上。 我遵循的是 https://aransentin.github.io/cwasm/和 https://depth-first.com/articles/2019/10/16/compiling-c-to-webassembly-and-running-it-without-emscripten/我使用 wasi 来获取 C 标准库。使用链接器标志,例如-Wl,-z,stack-size=$[1024 * 1024]
我可以控制堆栈大小。编译成功。伟大!
但是,我需要使用C++标准库来支持我自己的一些和其他第三方库。 据我所知,似乎没有任何简单的方法来获得libc ++和libc++abi。
我尝试了一个"黑客",我下载了Emscripten,并让它构建了自己的libc ++和libc++abi文件。然后我尝试将这些文件和标题复制到正确的位置。 然后我收到错误消息,提到缺少线程API,这显然是由于未使用EMSCRIPTEN编译引起的。所以我定义了EMSCRIPTEN宏,这有点工作。然后我想也许我可以删除 wasi 依赖项并使用 emscripten 的 libc 版本保持一致,但随后也有冲突/丢失的标头。
简而言之,我认为我有点接近我需要的地方,但事情变得非常混乱。我怀疑我采取了最简单的非脚本方法。
有没有人成功地为独立的 Web Assembly 创建了一个构建系统,让你使用 c 和 c++ 标准库?
编辑:
这是我现在拥有的超级黑客构建脚本(它是我在网上找到的大量修改版本(:
DEPS =
OBJ = library.o
STDLIBC_OBJ = $(patsubst %.cpp,%.o,$(wildcard stdlibc/*.cpp))
OUTPUT = library.wasm
DIR := ${CURDIR}
COMPILE_FLAGS = -Wall
--target=wasm32-unknown-wasi
-Os
-D __EMSCRIPTEN__
-D _LIBCPP_HAS_NO_THREADS
-flto
--sysroot ./
-std=c++17
-ffunction-sections
-fdata-sections
-I./libcxx/
-I./libcxx/support/xlocale
-I./libc/include
-DPRINTF_DISABLE_SUPPORT_FLOAT=1
-DPRINTF_DISABLE_SUPPORT_LONG_LONG=1
-DPRINTF_DISABLE_SUPPORT_PTRDIFF_T=1
$(OUTPUT): $(OBJ) $(NANOLIBC_OBJ) Makefile
wasm-ld
-o $(OUTPUT)
--no-entry
--export-all
--initial-memory=131072
--stack-size=$[1024 * 1024]
-error-limit=0
--lto-O3
-O3
-lc -lc++ -lc++abi
--gc-sections
-allow-undefined-file ./stdlibc/wasm.syms
$(OBJ)
$(LIBCXX_OBJ)
$(STDLIBC_OBJ)
%.o: %.cpp $(DEPS) Makefile
clang++
-c
$(COMPILE_FLAGS)
-fno-exceptions
-o $@
$<
library.wat: $(OUTPUT) Makefile
~/build/wabt/wasm2wat -o library.wat $(OUTPUT)
wat: library.wat
clean:
rm -f $(OBJ) $(STDLIBC_OBJ) $(OUTPUT) library.wat
我从emscripten中删除了libc,libc ++和libc ++ abi(但老实说,这是一个糟糕的安装过程。
我一直在逐步尝试填补我猜 emscripten 通常会做的空白,但现在我又陷入了困境:
./libcxx/type_traits:4837:57: error: use of undeclared identifier 'byte'
constexpr typename enable_if<is_integral_v<_Integer>, byte>::type &
^
./libcxx/type_traits:4837:64: error: definition or redeclaration of 'type'
cannot name the global scope
constexpr typename enable_if<is_integral_v<_Integer>, byte>::type &
我不再确定这是否有效,因为系统可能会意外编译特定于平台的内容。我真正想要的是一个垫片,它只能让我主要使用标准容器。 这已经变得有点难以管理。接下来我该怎么做?
编辑2:对,所以缺少C++17类型的特征内容,当我转到C++14(我仍然想要C++17(时,我最终会缺少更多的东西。 肯定卡住了。
编辑3:
我有点重新开始。库正在链接,我能够使用该标准,但是如果我尝试使用例如 std::chrono 的方法(我可以实例化对象(,我会看到如下错误:
wasm-ld: error: /var/folders/9k/zvv02vlj007cc0pm73769y500000gn/T/library-4ff1b5.o: undefined symbol: std::__1::chrono::system_clock::now()
我目前正在使用来自 emscripten 的静态库 abi 和来自我的自制 llvm 安装C++标准库的静态库(我尝试了 emscripten 但也没有用(。
我不太确定这是否与名称重整有关。我目前正在从 webasm 导出所有符号,因此 malloc 和 co. 也被导出。
这是我的构建脚本:
clang++
--target=wasm32-unknown-wasi
--std=c++11
-stdlib=libc++
-O3
-flto
-fno-exceptions
-D WASM_BUILD
-D _LIBCPP_HAS_NO_THREADS
--sysroot /usr/local/opt/wasi-libc
-I/usr/local/opt/wasi-libc/include
-I/usr/local/opt/glm/include
-I./libcxx/
-L./
-lc++
-lc++abi
-nostartfiles
-Wl,-allow-undefined-file wasm.syms
-Wl,--import-memory
-Wl,--no-entry
-Wl,--export-all
-Wl,--lto-O3
-Wl,-lc++,
-Wl,-lc++abi,
-Wl,-z,stack-size=$[1024 * 1024]
-o library.wasm
library.cpp
我的代码:
#include "common_header.h"
#include <glm/glm.hpp>
#include <unordered_map>
#include <vector>
#include <string>
#include <chrono>
template <typename T>
struct BLA {
T x;
};
template <typename T>
BLA<T> make_BLA() {
BLA<T> bla;
std::unordered_map<T, T> map;
std::vector<T> bla2;
std::string str = "WEE";
//str = str.substr(0, 2);
return bla;
}
#ifdef __cplusplus
extern "C" {
#endif
char* malloc_copy(char* input)
{
usize len = strlen(input) + 1;
char* result = (char*)malloc(len);
if (result == NULL) {
return NULL;
}
strncpy(result, input, len);
return result;
}
void malloc_free(char* input)
{
free(input);
}
float32 print_num(float val);
float32 my_sin(float32 val)
{
float32 result = sinf(val);
float32 result_times_2 = print_num(result);
print_num(result_times_2);
return result;
}
long fibonacci(unsigned n) {
if (n < 2) return n;
return fibonacci(n-1) + fibonacci(n-2);
}
void set_char(char* input)
{
input[0] = ''';
uint8 fibonacci_series[] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 };
for (uint8 number : fibonacci_series) {
input[0] = number;
}
auto WEE = make_BLA<int>();
WEE.x = 18;
glm::vec4 v(100.0f, 200.0f, 300.0f, 1.0f);
glm::vec4 v_out = glm::mat4(1.0f) * v;
input[0] = 5 + static_cast<int>(v_out.x) * input[1];
auto start = std::chrono::system_clock::now();
long out = fibonacci(42);
auto end = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds = end-start;
std::time_t end_time = std::chrono::system_clock::to_time_t(end);
auto elapsed = elapsed_seconds.count();
}
#ifdef __cplusplus
}
#endif
当我尝试仅在没有C++的函数上使用"visible"属性动态导出时,项目进行了编译,但 wasm 模块无法在 JavaScript 中加载,所以我认为问题仍然存在。
这是据我所知的。问题是否与我使用的编译器与用于创建静态库的编译器不同有关?(我正在使用自制的叮当 9(。希望不是。那时我会有点卡住,因为我找不到其他方法来获取图书馆。手动 llvm 编译似乎失败了。
优秀的wasi-sdk将上游llvm-project
(提供clang++
(和wasi-libc
作为git子模块,并使用合适的标志编译它们(最值得注意的是禁用wasi-libc中尚不支持的pthread(。
然后,您可以使用以下最小选项集编译自己的C++源代码:
/path/to/wasi-sdk/build/install/opt/wasi-sdk/bin/clang++
-nostartfiles
-fno-exceptions
-Wl,--no-entry
-Wl,--strip-all
-Wl,--export-dynamic
-Wl,--import-memory
-fvisibility=hidden
--sysroot /path/to/wasi-sdk/build/install/opt/wasi-sdk/share/wasi-sysroot
-o out.wasm
source.cpp
如果你想从运行时导入函数,我建议添加一个额外的行:
-Wl,--allow-undefined-file=wasm-import.syms
然后,可以将用换行符分隔的函数名称放入wasm-import.syms
,以便链接器不会抱怨未定义的函数。
请注意,所有这些都完全独立于Emscripten。