Emscripten是一个基于LLVM的项目,可以把C和C++代码编译成高度优化的JavaScript代码(asm.js格式),可以在web中以近乎原生的速度运行C和C++代码,而且不需要插件。 emscripten logo

Porting

把现有的C/C++直接编译,并且可以在所有现代浏览器中运行

APIs

Emscripten把OpenGL转译成WebGL, 并且可以使用你熟悉的API开发,比如SDL, 或者直接使用html5

Fast

得益于 LLVM, Emscripten 和 asm.js,代码可以用接近原生代码的速度运行

1.About Emscripten

       Emscripten是一个开源 LLVM to JavaScript 编译器. 有了Emscripten你可以:

  • 把 C 和 C++ 代码编译成 JavaScript
  • 把可以编译成 LLVM 字节码的其他代码编译成 JavaScript.
  • 把其他语言的 C/C++ runtimes 编译成 JavaScript, 并在web中以间接方法运行这种语言 (已经被 Python 和 Lua 使用)!

2.Emscripten Toolchain

       Emscripten工具链的流程图如下。核心是Emscripten编译器前端(emcc),它是标准编译器(比如gcc)的替代实现。

Emscripten Toolchain

       Emcc 使用 Clang 把 C/C++ 文件编译为 LLVM 字节码, 使用 Fastcomp (Emscripten 的编译器核心 — 一个 LLVM 后端) 来把字节码编译成 JavaScript. 输出的 JavaScript 可以被 node.js 执行, 或者嵌入 HTML 在浏览器中运行

3.Download and install

       windows可以选择安装完整版的Emscripten SDK或者绿色版for Win,linux和mac用户只能下载绿色版for Unix like

       我是mac用户,讲解一下绿色版的安装方法

  1. 下载并解压emsdk_protable并解压到某个文件夹下
  2. 打开终端并进入到emsdk文件夹下,然后执行下列命令
# 获取最新可用工具链的信息
./emsdk update

# 下载并安装最新的工具链
./emsdk install latest

# 让 "最新的" SDK "激活"
./emsdk activate latest

3.Mac和linux需要,吧emsdk的路径添加到PATH

# 在Linux/Mac OS X上添加emsdk的路径到PATH
source ./emsdk_env.sh

这步windows用户不需要,windows在执行activate命令时已经完成PATH的修改

4.使用Emscripten

       在终端下输入emcc -v,看到当前的Emscripten版本和llvm版本

4.1 hello.c

       新建一个hello.c文件,在文件内添加如下内容

#include<stdio.h>

int main() {
  printf("hello, world!\n");
  return 0;
}

       然后运行

emcc hello.c

       当前文件夹下应该生成了a.out.js,可以使用node.js运行

node a.out.js

       正常的话命令行中显示“hello, world!”

       然后运行

emcc hello.c -o hello_world.html

       当前文件夹下生成hello_world.htmlhello_world.js两个文件,在浏览器中打开hello_world.html,可以看到类似这样的效果

4.2 hello_world_sdl.cpp

       hello_world_sdl.cpp文件,在文件内添加如下内容

#include <stdio.h>
#include <SDL/SDL.h>

#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif

extern "C" 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;
}

       然后运行

emcc hello_world_sdl.cpp -o hello_world_sdl.html

       浏览器中打开hello_world_sdl.html可以看到类似这样的页面

4.3 hello_world_gles.cpp

       新建一个hello_world_gles.cpp,添加这里的代码

       编译,生成类似这个网页

4.4 glfw

       新建一个glfw.c,并添加这里的代码

       浏览器中打开glfw.html可以看到类似这样的页面,移动鼠标或者敲键盘,输入都能被捕捉并在下方的虚拟终端中显示

4.5 lua虚拟机

       下载lua源代码,用这的也可以

       将lua源码所在文件夹的名称改为lua,在这里再新建一个文件夹dist,dist和lua文件夹在同一层。进入lua文件夹,执行下面的命令

make emscripten

       在dist文件夹中会生成lua.vm.js

4.5.1 在node.js中使用lua.vm.js

       执行

$ npm install lua.vm.js

       然后编写node.js代码

var LuaVM = require('lua.vm.js');

var l = new LuaVM.Lua.State();
l.execute('print("Hello, world")');

4.5.2 在网页中使用lua.vm.js

       可以参考这个示例