diff options
Diffstat (limited to 'files/zh-cn/webassembly/c_to_wasm/index.html')
-rw-r--r-- | files/zh-cn/webassembly/c_to_wasm/index.html | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/files/zh-cn/webassembly/c_to_wasm/index.html b/files/zh-cn/webassembly/c_to_wasm/index.html new file mode 100644 index 0000000000..afcdbbc3e1 --- /dev/null +++ b/files/zh-cn/webassembly/c_to_wasm/index.html @@ -0,0 +1,252 @@ +--- +title: 编译 C/C++ 为 WebAssembly +slug: WebAssembly/C_to_wasm +tags: + - C + - C++ + - Emscripten + - WebAssembly + - wasm + - 编译 +translation_of: WebAssembly/C_to_wasm +--- +<div>{{WebAssemblySidebar}}</div> + +<p class="summary">当你在用C/C++之类的语言编写模块时,你可以使用<a href="/en-US/docs/Mozilla/Projects/Emscripten">Emscripten</a>来将它编译到WebAssembly。让我们来看看它是如何工作的。</p> + +<h2 id="Emscripten_环境安装">Emscripten 环境安装</h2> + +<p>首先,让我们来配置所需要的开发环境。</p> + +<h3 id="所需条件">所需条件</h3> + +<p>你需要将下列工具安装在您的电脑上,首先让我们确认下都有哪些。</p> + +<ul> + <li><a href="https://git-scm.com/">Git</a> — Linux和OS X的机器一般已经预装了,在Windows下您可以从这里下载<a href="https://git-scm.com/download/win">Git for Windows installer</a>。</li> + <li>CMake — 在Linux或者OS X上,使用类似 apt-get 或 <a href="http://brew.sh/">brew</a> 这样的包管理器来安装它,请确保依赖以及路径是否正确。在Windows上,使用<a href="https://cmake.org/download/">CMake installer</a>。</li> + <li>主系统编译器 — 在Linux下,<a href="http://askubuntu.com/questions/154402/install-gcc-on-ubuntu-12-04-lts">安装 GCC</a>。在OS X下,<a href="https://itunes.apple.com/us/app/xcode/id497799835">安装 Xcode</a>。在Windows下,安装<a href="https://www.microsoft.com/en-us/download/details.aspx?id=48146">Visual Studio Community 2015 with Update 3 or newer</a>。</li> + <li>Python 2.7.x — On Linux and OS X, this is most likely provided out of the box. 从 <a href="https://wiki.python.org/moin/BeginnersGuide/Downloadhere">初学者指南</a> 获取帮助。 在 Windows 上, 从 <a href="https://www.python.org/downloads/">Python 主页</a>获取安装包。</li> +</ul> + +<div class="note"> +<p><strong>注意: </strong>在Windows下您可能需要<a href="https://sourceforge.net/projects/pywin32/files/pywin32/">pywin32</a>,为了降低安装pywin32可能遇到的错误,请使用管理员权限在cmd内运行安装程序。</p> +</div> + +<h3 id="编译Emscripten">编译Emscripten</h3> + +<p>接下来,您需要通过源码自己编译一个Emscripten。运行下列命令来自动化地使用Emscripten SDK。(在你想保存Emscripten的文件夹下运行)。</p> + +<pre class="brush: bash notranslate">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 +# 如果在你的macos上获得以下错误 +Error: No tool or SDK found by name 'sdk-incoming-64bit' +# 请执行 +<code>./emsdk install latest +</code># 按照提示配置环境变量即可 +./emsdk activate latest + + +# 在 Windows 上 +emsdk install --build=Release sdk-incoming-64bit binaryen-master-64bit +emsdk activate --global --build=Release sdk-incoming-64bit binaryen-master-64bit + +# 注意:Windows 版本的 Visual Studio 2017 已经被支持,但需要在 emsdk install 需要追加 --vs2017 参数。 +</pre> + +<p> 安装过程可以会花上一点时间,是时候去休息一下。安装程序会设置所有Emscripten运行所需要的环境变量。</p> + +<div class="note"> +<p><strong>注意: </strong>--global标识会让PATH变量在全局被设置,所以接下来所打开的终端或者命令行窗口都会被设置。如果您仅仅想让Emscripten在当前窗口生效,就删掉这个标识。</p> +</div> + +<div class="note"> +<p><strong>注意</strong>: 每当您想要使用Emscripten时,尝试从远程更新最新的emscripten代码是个很好的习惯(运行 git pull)。如果有更新,重新执行 install 和 activate 命令。这样就可以确保您使用的Emscripten一直保持最新。</p> +</div> + +<p>现在让我们进入emsdk文件夹,输入以下命令来让你进入接下来的流程,编译一个样例C程序到asm.js或者wasm。</p> + +<pre class="brush: bash notranslate"># on Linux or Mac OS X +source ./emsdk_env.sh + +# on Windows +emsdk_env.bat +</pre> + +<h2 id="编译样例代码">编译样例代码</h2> + +<p>现在环境配置完毕,让我们看看如何使用它把C代码编译到Emscripten。当使用Emscripten来编译的时候有很多种不同的选择,我们介绍其中主要的2种:</p> + +<ul> + <li>编译到 wasm 并且生成一个用来运行我们代码的HTML,将所有 wasm 在web环境下运行所需要的 “胶水” JavaScript代码都添加进去。</li> + <li>编译到 wasm 然后仅仅生成 JavaScript。</li> +</ul> + +<p>让我们一个一个看看。</p> + +<h3 id="生成_HTML_和_JavaScript">生成 HTML 和 JavaScript</h3> + +<p>我们先来看一个最简单的例子,通过这个,你可以使用Emscripten来将任何代码生成到WebAssembly,然后在浏览器上运行。</p> + +<ol> + <li>首先我们需要编译一段样例代码。将下方的C代码复制一份然后命名为hello.c保存在一个新的文件夹内。 + <pre class="brush: cpp notranslate">#include <stdio.h> + +int main(int argc, char ** argv) { + printf("Hello World\n"); +}</pre> + </li> + <li>现在,转到一个已经配置过Emscripten编译环境的终端窗口中,进入刚刚保存hello.c文件的文件夹中,然后运行下列命令: + <pre class="brush: bash notranslate">emcc hello.c -s WASM=1 -o hello.html</pre> + </li> +</ol> + +<p>下面列出了我们命令中选项的细节:</p> + +<ul> + <li><code>-s WASM=1</code> — 指定我们想要的wasm输出形式。如果我们不指定这个选项,Emscripten默认将只会生成<a href="http://asmjs.org/">asm.js</a>。</li> + <li><code>-o hello.html</code> — 指定这个选项将会生成HTML页面来运行我们的代码,并且会生成wasm模块,以及编译和实例化wasm模块所需要的“胶水”js代码,这样我们就可以直接在web环境中使用了。</li> +</ul> + +<p>这个时候在您的源码文件夹应该有下列文件:</p> + +<ul> + <li><code>hello.wasm</code> 二进制的wasm模块代码</li> + <li><code>hello.js</code> 一个包含了用来在原生C函数和JavaScript/wasm之间转换的胶水代码的JavaScript文件</li> + <li><code>hello.html</code> 一个用来加载,编译,实例化你的wasm代码并且将它输出在浏览器显示上的一个HTML文件</li> +</ul> + +<h3 id="运行你的例子">运行你的例子</h3> + +<p>现在使用一个支持 WebAssembly 的浏览器,加载生成的 <code>hello.html</code> 。</p> + +<div class="note"> +<p>提示 :Firefox 52+ 和 Chrome 57+ 和最新版本的 Opera 已经默认启用,你也可以在 Firefox 47+ 中通过在 <em>about:config</em> 页面启用 <code>javascript.options.wasm</code> 字段获得支持,Chrome 51+ 和 Opera 38+ 可以在 <em>chrome://flags 页面启用 Experimental WebAssembly 选项以支持 </em>WebAssembly<em>。</em></p> +</div> + +<p>如果一切顺利,你应该可以在页面上的 <code>Emscripten 控制台</code>和 <code>浏览器控制台</code> 中看到 "Hello World" 的输出。</p> + +<p>恭喜!你已经成功将 C 代码编译成 JavaScript 并且在浏览器中执行了!</p> + +<h2 id="使用自定义HTML模板">使用自定义HTML模板</h2> + +<p>有些时候你可能想要使用一个自定义的 HTML 模板。让我们看看怎么实现。</p> + +<ol> + <li> + <p>首先,在一个新文件夹中保存以下 C 代码到 hello2.c 中:</p> + + <pre class="brush: cpp notranslate">#include <stdio.h> + +int main(int argc, char ** argv) { + printf("Hello World\n"); +}</pre> + </li> + <li> + <p>在 emsdk 中搜索一个叫做 <code>shell_minimal.html</code> 的文件,然后复制它到刚刚创建的目录下的 <code>html_template</code> 文件夹。</p> + + <pre class="brush: bash notranslate">mkdir html_template +cp ~/emsdk/emscripten/1.38.15/src/shell_minimal.html html_template</pre> + </li> + <li> + <p>现在使用你的Emscripten编译器环境的终端窗口进入你的新目录, 然后运行下面的命令:</p> + + <pre class="brush: bash notranslate">emcc -o hello2.html hello2.c -O3 -s WASM=1 --shell-file html_template/shell_minimal.html</pre> + + <p>这次使用的选项略有不同:</p> + + <ul> + <li>我们使用了 <code>-o hello2.html</code> ,这意味编译器将仍然输出 js 胶水代码 和 html 文件。</li> + <li>我们还使用了 <code>--shell-file html_template/shell_minimal.html</code> ,这指定了您要运行的例子使用 HTML 页面模板。</li> + </ul> + </li> + <li> + <p>下面让我们来运行这个例子。上面的命令已经生成了 hello2.html,内容和我们使用的模板非常相像,只不过多加了一些 js 胶水和加载wasm文件的代码。 在浏览器中打开它,你会看到与上一个例子相同的输出。</p> + </li> +</ol> + +<div class="note"> +<p><strong>注意:</strong>通过用.js取代.htm(l)作为文件后缀名,你就可以得到只有JavaScript的输出文件,而不再是完整的HTML文件。例如:<code>emcc -o hello2.js hello2.c -O3 -s WASM=1</code>. 你可以完全从零开始创建你自己的HTML文件。尽管如此,不推荐这样做。因为Emscripten需要大量的JavaScript“胶水”代码从而能够 处理内存分配、内存泄漏以及大量的其他问题。这些问题都已经在提供的模板中得到了处理。使用模板要比自己编写模板要容易得多。不过,当对模板所做的事情越来越熟悉的时候,你就能够按照自己的需要创建定制化的模板了。</p> +</div> + +<h2 id="调用一个定义在C中的自定义方法">调用一个定义在C中的自定义方法</h2> + +<p>如果需要调用一个在 C 语言自定义的函数,你可以使用 Emscripten 中的 <code>ccall()</code> 函数,以及 <code>EMSCRIPTEN_KEEPALIVE</code> 声明 (将你的函数添加到导出函数列表中(详见 <a href="https://kripken.github.io/emscripten-site/docs/getting_started/FAQ.html#why-do-functions-in-my-c-c-source-code-vanish-when-i-compile-to-javascript-and-or-i-get-no-functions-to-process">Why do functions in my C/C++ source code vanish when I compile to JavaScript, and/or I get No functions to process?</a>))。</p> + +<p>接下来让我们看看这是怎么实现的。</p> + +<ol> + <li> + <p>首先,将以下代码在新目录中保存为 <code>hello3.c</code> :</p> + + <pre class="brush: cpp notranslate">#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("我的函数已被调用\n"); +} + +#ifdef __cplusplus +} +#endif</pre> + + <p>默认情况下,Emscripten 生成的代码只会调用 <code>main()</code> 函数,其它的函数将被视为无用代码。在一个函数名之前添加 <code>EMSCRIPTEN_KEEPALIVE</code> 能够防止这样的事情发生。你需要导入 <code>emscripten.h</code> 库来使用 <code>EMSCRIPTEN_KEEPALIVE</code>。</p> + + <div class="note"> + <p><strong>注意</strong>: 为了保证万一你想在 C++ 代码中引用这些代码时代码可以正常工作,我们添加了 <code>#ifdef</code> 代码块。由于 C 与 C++ 中名字修饰规则的差异,添加的代码块有可能产生问题,但目前我们设置了这一额外的代码块以保证你使用 C++ 时,这些代码会被视为外部 C 语言函数。</p> + </div> + </li> + <li> + <p>为了方便起见,现在将 <code>html_template/shell_minimal.html</code> 也添加到这一目录(但在实际开发环境中你肯定需要将其放到某一特定位置)。</p> + </li> + <li> + <p>运行以下命令编译:(注意由于使用ccall函数,需要添加指定参数)</p> + + <pre class="brush: bash notranslate">emcc -o hello3.html hello3.c -O3 -s WASM=1 -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall']" --shell-file html_template/shell_minimal.html</pre> + </li> + <li> + <p>如果你在浏览器中在此加载实例,你将看到和之前相同的结果。</p> + </li> + <li> + <p>现在我们需要运行新的 <code>myFunction()</code> JavaScript 函数。首先,按照以下实例添加一个 {{htmlelement("button")}} ,就在 <code><script type='text/javascript'></code> 开头标签之前。</p> + + <pre class="brush: html notranslate"><button class="mybutton">运行我的函数</button></pre> + </li> + <li> + <p>现在在最后一个 {{htmlelement("script")}} 元素 (就在 <code></script></code> 关闭标签之前)中添加以下代码:</p> + + <pre class="brush: js notranslate">document.querySelector('.mybutton').addEventListener('click', function(){ + alert('检查控制台'); + var result = Module.ccall('myFunction', // name of C function + null, // return type + null, // argument types + null); // arguments +});</pre> + </li> +</ol> + +<p>以上就是如何使用 <code>ccall()</code> 调用导出的函数的方式。</p> + +<h2 id="另请参见">另请参见</h2> + +<ul> + <li><a href="http://emscripten.org/">emscripten.org</a> — 了解更多 Emscripten 以及它的多种设置</li> + <li><a href="https://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html#calling-compiled-c-functions-from-javascript-using-ccall-cwrap">Calling compiled C functions from JavaScript using ccall/cwrap</a></li> + <li><a href="https://kripken.github.io/emscripten-site/docs/getting_started/FAQ.html#why-do-functions-in-my-c-c-source-code-vanish-when-i-compile-to-javascript-and-or-i-get-no-functions-to-process">Why do functions in my C/C++ source code vanish when I compile to JavaScript, and/or I get No functions to process?</a></li> + <li><a href="https://research.mozilla.org/webassembly/">WebAssembly on Mozilla Research</a></li> + <li> + <p><a href="https://wiki.developer.mozilla.org/en-US/docs/WebAssembly/existing_C_to_wasm">Compiling an Existing C Module to WebAssembly</a></p> + </li> +</ul> |