MDN wants to learn about developers like you: https://qsurvey.mozilla.com/s3/MDN-dev-survey

Compiling from C/C++ to WebAssembly

这篇翻译不完整。请帮忙从英语翻译这篇文章

当你在用C/C++之类的语言编写模块时,你可以使用Emscripten来将它编译到WebAssembly。让我们来看看它是如何工作的。

Emscripten 环境安装

首先,让我们来配置所需要的开发环境。

所需条件

你需要将下列工具安装在您的电脑上,首先让我们确认下都有哪些。

注意: 在Windows下您可能需要pywin32,为了降低安装pywin32可能遇到的错误,请使用管理员权限在cmd内运行安装程序。

编译Emscripten

接下来,您需要通过源码自己编译一个Emscripten。运行下列命令来自动化地使用Emscripten SDK。(在你想保存Emscripten的文件夹下运行)。

git clone https://github.com/juj/emsdk.git
cd emsdk

# 在 Linux 或者 Mac OS X 上
./emsdk install --build=Release sdk-incoming-64bit binaryen-master-64bit
./emsdk activate --global --build=Release sdk-incoming-64bit binaryen-master-64bit

# 在 Windows 上
emsdk install --build=Release sdk-incoming-64bit binaryen-master-64bit
emsdk activate --global --build=Release sdk-incoming-64bit binaryen-master-64bit

 安装过程可以会花上一点时间,是时候去休息一下。安装程序会设置所有Emscripten运行所需要的环境变量。

Note: --global标识会让PATH变量在全局被设置,所以接下来所打开的终端或者命令行窗口都会被设置。如果您仅仅想让Emscripten在当前窗口生效,就删掉这个标识。

Note: 每当您想要使用Emscripten时,尝试从远程更新最新的emscripten代码是个很好的习惯(运行 git pull)。如果有更新,重新执行 install 和 activate 命令。这样就可以确保您使用的Emscripten一直保持最新。

现在让我们进入emsdk文件夹,输入以下命令来让你进入接下来的流程,编译一个样例C程序到asm.js或者wasm。

# on Linux or Mac OS X
source ./emsdk_env.sh

# on Windows
emsdk_env.bat

编译样例代码

现在环境配置完毕,让我们看看如何使用它把C代码编译到Emscripten。当使用Emscripten来编译的时候有很多种不同的选择,我们介绍其中主要的2种:

  • 编译到 wasm 并且生成一个用来运行我们代码的HTML,将所有 wasm 在web环境下运行所需要的 “胶水” JavaScript代码都添加进去。
  • 编译到 wasm 然后仅仅生成 JavaScripte。

让我们一个一个看看。

生成 HTML 和 JavaScript

我们先来看一个最简单的例子,通过这个,你可以使用Emscripten来将任何代码生成到WebAssembly,然后在浏览器上运行。

  1. 首先我们需要编译一段样例代码。将下方的C代码复制一份然后命名为hello.c保存在一个新的文件夹内。
    #include <stdio.h>
    
    int main(int argc, char ** argv) {
      printf("Hello World\n");
    }
  2. 现在,转到一个已经配置过Emscripten编译环境的终端窗口中,进入刚刚保存hello.c文件的文件夹中,然后运行下列命令:
    emcc hello.c -s WASM=1 -o hello.html

下面列出了我们命令中选项的细节:

  • -s WASM=1 — 指定我们想要的wasm输出形式。如果我们不指定这个选项,Emscripten默认将只会生成asm.js
  • -o hello.html — 指定这个选项将会生成HTML页面来运行我们的代码,并且会生成wasm模块以及编译和实例化wasim模块所需要的“胶水”js代码,这样我们就可以直接在web环境中使用了。

这个时候在您的源码文件夹应该有下列文件:

  • 二进制的wasm模块代码 (hello.wasm)
  • 一个包含了用来在原生C函数和JavaScript/wasm之间转换的胶水代码的JavaScript文件 (hello.js)
  • 一个用来加载,编译,实例化你的wasm代码并且将它输出在浏览器显示上的一个HTML文件 (hello.html)

Running your example

Now all that remains is for you to load the resulting hello.html in a browser that supports WebAssembly. It is enabled by default in Firefox 52+ and Chrome 57+/latest Opera (you can also run wasm code in Firefox 47+ by enabling the javascript.options.wasm flag in about:config, or Chrome (51+) and Opera (38+) by going to chrome://flags and enabling the Experimental WebAssembly flag.)

If everything has worked as planned, you should see "Hello world" output in the Emscripten console appearing on the web page, and your browser’s JavaScript console. Congratulations, you’ve just compiled C to WebAssembly and run it in your browser!

Using a custom HTML template

Sometimes you will want to use a custom HTML template. Let's look at how we can do this.

  1. First of all, save the following C code in a file called hello2.c, in a new directory:

    #include <stdio.h>
    
    int main(int argc, char ** argv) {
        printf("Hello World\n");
    
    }
  2. Search for the file shell_minimal.html in your emsdk repo. Copy it into a sub-directory called html_template inside your previous new directory.

  3. Now navigate into your new directory (again, in your Emscripten compiler environment terminal window), and run the following command:

    emcc -o hello2.html hello2.c -O3 -s WASM=1 --shell-file html_template/shell_minimal.html

    The options we've passed are slightly different this time:

    • We've specified -o hello2.js, meaning that this time the compiler will output the JavaScript glue code, but not the .html.
    • We've also specified --shell-file html_template/shell_minimal.html — this provides the path to the HTML template you want to use to create the HTML you will run your example through.
  4. Now let's run this example. The above command will have generated hello2.html, which will have much the same content as the template with some glue code added into load the generated wasm, run it, etc. Open it in your browser and you'll see much the same output as the last example.

Note: You could specify outputting just JavaScript rather than the full HTML by specifying a .js file instead of an HTML file in the -o flag, e.g. emcc -o hello2.js hello2.c -O3 -s WASM=1. You could then build your custom HTML complete from scratch. This isn't recommended however — Emscripten requires a large variety of JavaScript "glue" code to handle memory allocation, memory leaks, and a host of other problems, which is already included in the provided template. It is easier to use that than having to write it all out yourself, although as you become more experienced at what it all does, you'll be able to create your own customized versions for your needs.

Calling a custom function defined in C

If you have a function defined in your C code that you want to call as needed from JavaScript, you can do this using the Emscripten ccall() function, and the EMSCRIPTEN_KEEPALIVE declaration (which adds your functions to the exported functions list (see Why do functions in my C/C++ source code vanish when I compile to JavaScript, and/or I get No functions to process?)). Let's look at how this works.

  1. To start with, save the following code as hello3.c in a new directory:

    #include <stdio.h>
    #include <emscripten/emscripten.h>
    
    int main(int argc, char ** argv) {
        printf("Hello World\n");
    }
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    int EMSCRIPTEN_KEEPALIVE myFunction(int argc, char ** argv) {
      printf("MyFunction Called\n");
    }
    
    #ifdef __cplusplus
    }
    #endif

    By default, Emscripten-generated code always just calls the main() function, and other functions are eliminated as dead code. Putting EMSCRIPTEN_KEEPALIVE before a function name stops this from happening. You also need to import the emscripten.h library to use EMSCRIPTEN_KEEPALIVE.

    Note: We are including the #ifdef blocks so that if you are trying to include this in C++ code, the example will still work. Due to C versus C++ name mangling rules, this would otherwise break, but here we are setting it so that it treats it as an external C function if you are using C++.

  2. Now add html_template/shell_minimal.html into this new directory too, just for convenience (you'd obviously put this in a central place in your real dev environment).

  3. Now let's run the compilation step again. From inside your latest directory (and while inside your Emscripten compiler environment terminal window), compile your C code with this:

    emcc -o hello3.html hello3.c -O3 -s WASM=1 --shell-file html_template/shell_minimal.html
  4. If you load the example in your browser again, you'll see the same thing as before!

  5. Now we need to run our new myFunction() function from JavaScript. First of all, let's add a <button> as shown below, just above the first opening <script type='text/javascript'> tag.

    <button class="mybutton">Run myFunction</button>
  6. Now add the following code inside the last <script> element (just above the closing </script> tag):

    document.querySelector('.mybutton').addEventListener('click', function(){
      alert('check console');
      var result = Module.ccall('myFunction', // name of C function 
                                 null, // return type
                                 null, // argument types
                                 null); // arguments
    });

This illustrates how ccall() is used to call the exported function.

See also

文档标签和贡献者

 此页面的贡献者: AdrianDuan, disoul
 最后编辑者: AdrianDuan,