在基础层面上,使用Emscripten相当简单。本教程将指导您完成从命令行编译第一个Emscripten示例所需的步骤。它还展示了如何处理文件并设置主要的编译器优化标志。
确保您已下载并安装Emscripten(执行此操作的确切方法将取决于您的操作系统:Linux、Windows或Mac)。
可以使用Emscripten编译器前端(emcc)访问Emscripten。此脚本调用构建代码所需的所有其他工具,并且可以作为标准编译器(如gcc或clang)的替代品。它在命令行中使用./emcc
或./em++
调用。
注意
在Windows上,使用略微不同的语法调用该工具:emcc
或em++
。本教程的其余部分将使用Linux方法(./emcc
)。
对于下一部分,您需要打开一个命令提示符
在Linux或macOS上,打开一个终端。
在Windows上,打开Emscripten命令提示符,这是一个已预先配置了正确系统路径和设置以指向活动Emscripten工具的命令提示符。要访问此提示符,请在Windows 8开始屏幕中键入Emscripten,然后选择Emscripten命令提示符选项。
使用命令提示符导航到SDK下的emscripten目录。这是一个位于emsdk根目录下面的文件夹,通常为<emsdk根目录>/upstream/emscripten/。以下示例将依赖于查找相对于该位置的文件。
注意
在旧版本的emscripten中,目录结构不同:版本号出现了,并且后端(fastcomp/upstream)没有出现,因此您将使用类似<emsdk根目录>/emscripten/1.20.0/的内容。
如果您以前没有运行Emscripten,请立即使用以下命令运行它
./emcc -v
如果输出包含有关缺少工具的警告,请参阅验证Emscripten开发环境以获取调试帮助。否则,继续到下一部分,我们将构建一些代码。
您现在可以将第一个C/C++文件编译为JavaScript。
首先,让我们看一下要编译的文件:hello_world.c。这是SDK中最简单的测试代码,如您所见,它所做的只是将“hello, world!”打印到控制台,然后退出。
/*
* Copyright 2011 The Emscripten Authors. All rights reserved.
* Emscripten is available under two separate licenses, the MIT license and the
* University of Illinois/NCSA Open Source License. Both these licenses can be
* found in the LICENSE file.
*/
#include <stdio.h>
int main() {
printf("hello, world!\n");
return 0;
}
要构建此代码的JavaScript版本,只需在emcc之后指定C/C++文件(使用em++强制作为C++编译)
./emcc test/hello_world.c
您应该看到该命令生成两个文件:a.out.js和a.out.wasm。第二个文件是包含已编译代码的WebAssembly文件,第一个文件是包含加载和执行该代码的运行时支持的JavaScript文件。您可以使用node.js运行它们
node a.out.js
这将按预期将“hello, world!”打印到控制台。
注意
较旧版本的node.js尚未支持WebAssembly。在这种情况下,您将看到一条错误消息,建议您使用-sWASM=0
构建以禁用WebAssembly,然后emscripten将以JavaScript的形式发出已编译的代码。通常,WebAssembly是推荐的,因为它具有广泛的浏览器支持并且执行和下载效率更高(因此emscripten默认情况下会发出它),但有时您可能需要您的代码在尚未支持WebAssembly的环境中运行,因此应禁用它。
提示
如果在调用emcc时发生错误,请使用-v
选项运行它,以打印大量有用的调试信息。
注意
在本节以及以后,我们从test/
文件夹运行一些文件。该文件夹包含Emscripten测试套件的文件。有些可以独立运行,但其他文件必须通过测试套件本身运行,有关更多信息,请参阅Emscripten测试套件。
Emscripten还可以生成HTML以测试嵌入式JavaScript。要生成HTML,请使用-o
(输出) 命令并指定html文件作为目标文件
./emcc test/hello_world.c -o hello.html
您现在可以在网页浏览器中打开hello.html
。
注意
不幸的是,一些浏览器(包括Chrome、Safari和Internet Explorer)不支持file://
XHR 请求,并且无法加载HTML所需的额外文件(例如.wasm
文件或打包的文件数据,如下所述)。对于这些浏览器,您需要使用本地Web服务器提供文件服务,然后打开https://127.0.0.1:8000/hello.html
)。
将HTML加载到浏览器后,您将看到一个文本区域,用于显示本机代码中printf()
调用的输出。
HTML输出不仅限于显示文本。您还可以使用SDL API在<canvas>
元素中显示一个彩色立方体(在支持它的浏览器上)。例如,构建hello_world_sdl.c测试代码,然后刷新浏览器
./emcc test/hello_world_sdl.c -o hello.html
第二个示例的源代码如下所示
// Copyright 2011 The Emscripten Authors. All rights reserved.
// Emscripten is available under two separate licenses, the MIT license and the
// University of Illinois/NCSA Open Source License. Both these licenses can be
// found in the LICENSE file.
#include <stdio.h>
#include <SDL/SDL.h>
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
int main(int argc, char** argv) {
printf("hello, world!\n");
SDL_Init(SDL_INIT_VIDEO);
SDL_Surface *screen = SDL_SetVideoMode(256, 256, 32, SDL_SWSURFACE);
#ifdef TEST_SDL_LOCK_OPTS
EM_ASM("SDL.defaults.copyOnLock = false; SDL.defaults.discardOnLock = true; SDL.defaults.opaqueFrontBuffer = false;");
#endif
if (SDL_MUSTLOCK(screen)) SDL_LockSurface(screen);
for (int i = 0; i < 256; i++) {
for (int j = 0; j < 256; j++) {
#ifdef TEST_SDL_LOCK_OPTS
// Alpha behaves like in the browser, so write proper opaque pixels.
int alpha = 255;
#else
// To emulate native behavior with blitting to screen, alpha component is ignored. Test that it is so by outputting
// data (and testing that it does get discarded)
int alpha = (i+j) % 255;
#endif
*((Uint32*)screen->pixels + i * 256 + j) = SDL_MapRGBA(screen->format, i, j, 255-i, alpha);
}
}
if (SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen);
SDL_Flip(screen);
printf("you should see a smoothly-colored square - no sharp lines but the square borders!\n");
printf("and here is some text that should be HTML-friendly: amp: |&| double-quote: |\"| quote: |'| less-than, greater-than, html-like tags: |<cheez></cheez>|\nanother line.\n");
SDL_Quit();
return 0;
}
注意
您的C/C++代码可以使用正常的libc stdio API(fopen
、fclose
等)访问文件
JavaScript通常在网页浏览器的沙盒环境中运行,没有直接访问本地文件系统的权限。Emscripten模拟了一个文件系统,您可以使用正常的libc stdio API从已编译的C/C++代码中访问该文件系统。
您要访问的文件应该被预加载或嵌入到虚拟文件系统中。预加载(或嵌入)将生成一个虚拟文件系统,该文件系统对应于编译时相对于当前目录的文件系统结构。
hello_world_file.cpp示例展示了如何加载文件(测试代码和要加载的文件如下所示)
// Copyright 2012 The Emscripten Authors. All rights reserved.
// Emscripten is available under two separate licenses, the MIT license and the
// University of Illinois/NCSA Open Source License. Both these licenses can be
// found in the LICENSE file.
#include <stdio.h>
int main() {
FILE *file = fopen("test/hello_world_file.txt", "rb");
if (!file) {
printf("cannot open file\n");
return 1;
}
while (!feof(file)) {
char c = fgetc(file);
if (c != EOF) {
putchar(c);
}
}
fclose (file);
return 0;
}
==
This data has been read from a file.
The file is readable as if it were at the same location in the filesystem, including directories, as in the local filesystem where you compiled the source.
==
注意
该示例预期能够加载位于test/hello_world_file.txt中的文件
FILE *file = fopen("test/hello_world_file.txt", "rb");
我们从“上面”test的目录编译示例,以确保虚拟文件系统以相对于编译时目录的正确结构创建。
以下命令用于将数据文件预加载到Emscripten的虚拟文件系统中——在运行任何已编译的代码之前。这种方法很有用,因为浏览器只能异步加载来自网络的数据(除了在Web Workers中),而许多本机代码使用同步文件系统访问。预加载确保在已编译的代码有机会访问Emscripten文件系统之前,数据文件的异步下载已完成(并且文件可用)。
./emcc test/hello_world_file.cpp -o hello.html --preload-file test/hello_world_file.txt
运行上述命令,然后在网页浏览器中打开hello.html,查看hello_world_file.txt中的数据是否正在显示。
有关处理文件系统的更多信息,请参阅文件系统概述、文件系统API和同步虚拟XHR支持的文件系统用法。
Emscripten与gcc和clang一样,默认情况下会生成未优化的代码。可以使用-O1
命令行参数生成略微优化的代码
./emcc -O1 test/hello_world.cpp
在a.out.js中创建的“hello world”代码实际上不需要优化,因此与未优化版本相比,您不会看到速度上的差异。
但是,您可以比较生成的代码以查看差异。 -O1
应用了一些次要优化并删除了一些运行时断言。例如,printf
在生成的代码中将被 puts
替换。
由 -O2
提供的优化(见 这里)更加激进。如果您运行以下命令并检查生成的代码(a.out.js),您会发现它看起来非常不同
./emcc -O2 test/hello_world.cpp
Emscripten 拥有一个全面的测试套件,涵盖了几乎所有 Emscripten 功能。这些测试对于开发人员来说是一项宝贵的资源,因为它们提供了大多数功能的实际示例,并且已知它们在 main
分支上成功构建。
有关更多信息,请参见 Emscripten 测试套件。
本教程指导您完成从命令行调用 Emscripten 的第一步。当然,您还可以使用此工具做更多的事情。以下是使用 Emscripten 的其他一般技巧
本网站包含有关 编译和构建项目、将您的本机代码与 Web 环境集成、打包您的代码 和发布的更多信息。
Emscripten 测试套件是查找使用 Emscripten 示例的好地方。例如,如果您想更好地了解 emcc --pre-js
选项的工作原理,请在测试套件中搜索 --pre-js
:测试套件非常广泛,很可能至少有一些示例。
要了解如何在高级方式中使用 Emscripten,请阅读 src/settings.js 和 emcc,它们描述了编译器选项,以及 emscripten.h,了解您的 C/C++ 程序在使用 Emscripten 编译时可以使用哪些 JavaScript 特定 C API。
阅读 常见问题解答。
如有疑问,请 联系我们!