仓库源文站点原文


layout: "../layouts/BlogPost.astro" title: "An example of compiling C code to Wasm" slug: example-of-compiling-c-code-to-wasm description: "" added: "July 23 2023" tags: [web]

updatedDate: "Mar 9 2024"

Sometimes you want to use a library that is only available as C or C++ code. Traditionally, this is where you give up. Well, not anymore, because now we have Emscripten and WebAssembly.

WebAssembly is a type of code that can be run in modern web browsers — it is a low-level assembly-like language with a compact binary format that runs with near-native performance and provides languages such as C/C++, C# and Rust with a compilation target so that they can run on the web.

Emscripten compiles C and C++ code, or any other language that uses LLVM, into WebAssembly, and run it on the Web, Node.js, or other wasm runtimes. Emscripten provides Web support for popular portable APIs such as OpenGL, allowing complex graphical native applications to be ported, such as the Unity game engine and Google Earth.

Environment setup

First, let's set up the required development environment. Get the Emscripten SDK, using the instructions on their website or using Homebrew.

# https://formulae.brew.sh/formula/emscripten
brew install emscripten
emcc --version
emcc --help

There are a number of options available when compiling with Emscripten, but the main two scenarios are:

Compiling an example

Take a copy of the following simple C example, and save it in a file called hello.c.

#include <stdio.h>

int main() {
  printf("Hello World\n");
  return 0;
}

To build the JavaScript version of this code, simply specify the C file after emcc command. The Emscripten Compiler Frontend (emcc) is used to call the Emscripten compiler from the command line. It is effectively a drop-in replacement for a standard compiler like gcc or clang.

emcc hello.c

You should see two files generated by that command: a.out.js and a.out.wasm. The second is a WebAssembly file containing the compiled code, and the first is a JavaScript file containing the runtime support to load and execute it. You can run them using node.js node a.out.js, and it prints “Hello World” to the console as expected.

WebAssembly.compile(...).then(module => {
  const instance = new WebAssembly.Instance(module)
  const { add, square } = instance.exports

  console.log('2 + 4 =', add(2, 4))
  console.log('3^2 =', square(3))
  console.log('(2 + 5)^2 =', square(add(2 + 5)))
})

Another command can be used is emcc hello.c -o hello.html. This specifies that we want Emscripten to generate an HTML page to run our code, as well as the Wasm module and the JavaScript "glue" code to compile and instantiate the Wasm so it can be used in the web environment. (Emscripten requires a large variety of JavaScript "glue" code to handle memory allocation, memory leaks, and a host of other problems.) Now all that remains is for you to load the resulting hello.html in a browser that supports WebAssembly.

<img alt="emcc example" src="https://raw.gitmirror.com/kexiZeroing/blog-images/main/ad4c5db4-c53a-424d-a509-f5228cde0a16%202.png" width="700" />

Using a custom HTML template

Sometimes you will want to use a custom HTML template. Search for the file shell_minimal.html in Emscripten repo, and copy it into a subdirectory called html_template.

emcc -o hello2.html hello.c -O3 --shell-file html_template/shell_minimal.html

<img alt="emcc example2" src="https://raw.gitmirror.com/kexiZeroing/blog-images/main/4n1zrr.png" width="700" />