title: No Emscripten, How to Compile C++ With Standard Library to WebAssembly
body: <p>I am having trouble building standalone webassembly with the full control I want over memory and layout. I don't want to use emscripten because, as the following post says, it doesn't give me all of the compile-time options I want (e.g. stack size control, being able to choose to import memory in standalone mode, etc.) I've been folowing pages such as: <a href="https://stackoverflow.com/questions/52054130/how-to-generate-standalone-webassembly-with-emscripten">How to generate standalone webassembly with emscripten</a>
  Also, emscripten is overkill. </p>
  
  <p>What I've done so far:
  I have a fully working llvm 9 toolchain downloaded via homebrew (I am on macos 10.14.)
  I was following a mix of <a href="https://aransentin.github.io/cwasm/" rel="noreferrer">https://aransentin.github.io/cwasm/</a> and <a href="https://depth-first.com/articles/2019/10/16/compiling-c-to-webassembly-and-running-it-without-emscripten/" rel="noreferrer">https://depth-first.com/articles/2019/10/16/compiling-c-to-webassembly-and-running-it-without-emscripten/</a>
  I used wasi to get the C standard library. Using linker flags like <code>-Wl,-z,stack-size=$[1024 * 1024]</code> I could control the stack size. Compilation was successful. Great!</p>
  
  <p>However, I need to use C++ standard libraries to support some of my own and other third party libraries.
  As far as I can tell, there doesn't seem to be any easy way to get libc++ and libc++abi.</p>
  
  <p>I tried a "hack" in which I downloaded Emscripten and had it build its own libc++ and libc++abi files. Then I tried copying those files and headers into the right spot.
  Then I got error messages referring to a missing threading API, which apparently were caused by not compiling with EMSCRIPTEN. So I defined the <strong>EMSCRIPTEN</strong> macro and that sort of worked. Then I thought that maybe I could remove the wasi dependency and use emscripten's version of libc to be consistent, but then there were conflicting / missing headers too.</p>
  
  <p>In short, I think I got somewhat close to where I needed to be, but things just got terribly messy. I doubt I took the simplest non-emscripten approach.</p>
  
  <p>Has anyone successfully created a build system for standalone webassembly that lets you use the c and c++ standard libraries?</p>
  
  <p>EDIT:</p>
  
  <p>This is the super hacky build script I have now (it's a heavily modified version of something I found online):</p>
  
  <pre class="lang-sh prettyprint-override"><code>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 $@ \
          $&lt;
  
  library.wat: $(OUTPUT) Makefile
      ~/build/wabt/wasm2wat -o library.wat $(OUTPUT)
  
  wat: library.wat
  
  clean:
      rm -f $(OBJ) $(STDLIBC_OBJ) $(OUTPUT) library.wat
  </code></pre>
  
  <p>I dropped-in libc, libc++, and libc++abi from emscripten (but honestly this is a terrible installation process.)</p>
  
  <p>I've been incrementally trying to fill in gaps that I guess emscripten would've normally done, but now I'm stuck again:</p>
  
  <pre><code>./libcxx/type_traits:4837:57: error: use of undeclared identifier 'byte'
    constexpr typename enable_if&lt;is_integral_v&lt;_Integer&gt;, byte&gt;::type &amp;
                                                          ^
  ./libcxx/type_traits:4837:64: error: definition or redeclaration of 'type'
        cannot name the global scope
    constexpr typename enable_if&lt;is_integral_v&lt;_Integer&gt;, byte&gt;::type &amp;
  </code></pre>
  
  <p>I am no longer sure if this will even work since the system might accidentally compile something platform-specific in. Really what I'd like is a shim that would just let me use the standard containers mostly.
  This has become kind of unmanageable. What might I do next?</p>
  
  <p>EDIT 2: Right so that's missing C++17 type trait content, and when I go to C++14 (I still want C++17) I end up with more missing things.
  Definitely stuck.</p>
  
  <p>EDIT 3:</p>
  
  <p>I sort of started over. The libraries are linking, and I'm able to use the standard, but I'm seeing errors like the following if I try to use e.g. an std::chrono's methods (I can instantiate the object):</p>
  
  <p><code>wasm-ld: error: /var/folders/9k/zvv02vlj007cc0pm73769y500000gn/T/library-4ff1b5.o: undefined symbol: std::__1::chrono::system_clock::now()</code></p>
  
  <p>I'm currently using the static library abi from emscripten and the static library C++ standard library from my homebrew installation of llvm (I tried the emscripten one but that didn't work either).</p>
  
  <p>I'm not really sure if this is related to name mangling. I'm currently exporting all symbols from webasm so malloc and co. get exported as well. </p>
  
  <p>Here is my build script:</p>
  
  <pre class="lang-sh prettyprint-override"><code>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
  </code></pre>
  
  <p>My code:</p>
  
  <pre class="lang-cpp prettyprint-override"><code>#include "common_header.h"
  
  #include &lt;glm/glm.hpp&gt;
  #include &lt;unordered_map&gt;
  #include &lt;vector&gt;
  #include &lt;string&gt;
  #include &lt;chrono&gt;
  
  template &lt;typename T&gt;
  struct BLA {
      T x;
  };
  template &lt;typename T&gt;
  BLA&lt;T&gt; make_BLA() {
      BLA&lt;T&gt; bla;
  
      std::unordered_map&lt;T, T&gt; map;
      std::vector&lt;T&gt; 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 &lt; 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&lt;int&gt;();
      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&lt;int&gt;(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&lt;double&gt; 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
  
  </code></pre>
  
  <p>When I tried exporting dynamically with the "visible" attribute on only the functions that had no C++, the project compiled, but the wasm module failed to load in JavaScript, so I think the problem was still there.</p>
  
  <p>This is as far as I've gotten. Might the issue be related to the fact that I'm using a different compiler from the one used to create the static libraries? (I'm using homebrew clang 9). Hopefully not. I'd be kind of stuck then because I couldn't find another way to get the libraries. Manual llvm compilation seemed to fail.</p>