Emscripten教程之入门指导
翻译:云荒杯倾
本文是Emscripten-WebAssembly专栏系列文章之一,更多文章请查看专栏。
也可以去作者的博客阅读文章。
欢迎加入Wasm和emscripten技术交流群,群聊号码:939206522。
下面是正文:
如果只是想要入门Emscripten的话,使用Emscripten是非常简单的。本教学将教会你从命令行编译Emscripten代码的种种步骤,以及Emscripten代码中怎样使用文件和使用主要的编译优化flag。
要事优先
首先是确保你已经下载并且安装好了Emscripten。根据你的操作系统不同,下载和安装过程稍有不同。
Emscripten主要是通过emcc(Emscripten Compiler Frontend)来工作的。这是个命令行工具,它会调用其他编译需要的工具,可以将它看成是标准编译器比如gcc或者clang的命令行版本。wimdows系统的话,命令行中使用emcc,Linux下使用./emcc。
验证Emscripten
第一次使用Emscripten,请先使用以下命令验证Emscripten是否正确安装:
emcc -v
如果有警告发生,可能是因为缺少一些工具,请去看这个链接解决
如果没有警告或报错,就往下看。
运行Emscripten
现在就可以使用Emscripten把C/C++代码编译成JavaScript了。
首先,写一个待编译为JavaScript的C文件。比如hello_world.c,像下面这样:
    #include <stdio.h>
    int main() {
      printf("hello, world!\n");
      return 0;
    }
注:这是Emscripten提供的测试集中最简单的一个C文件。
为了编译这个C文件,你只需要在test目录下打开命令行,emcc后面跟上这个文件名就行了。
emcc tests/hello_world.c
注:test目录是Emscripten测试集的目录。
这样就会在test目录下生成一个a.out.js文件,你可以使用node运行这个a.out.js。
node a.out.js
就会在node控制台打印出hello world!了。
如果编译失败,你可以在emcc tests/hello_world.c后面加个-v,也就是变成emcc tests/hello_world.c -v ,这样呢就会有一些调试信息,你可以参考他们,找出编译失败的原因。
生成HTML
Emscripten能为刚才输出的那个JavaScript生成HTML文件,你可以使用-o命令指定要输出的html文件名。
emcc tests/hello_world.c -o hello.html
在浏览器打开这个hello.html。你会看到,这个HTML页面中有一块文本区域是为了显示C代码中printf()函数打印的内容。
实际上,这块区域不仅可以显示文本。如果你C代码中调用了SDL的API,那么也可以在一块canvas中显示一个五彩斑斓的cube。比如,hello_world_cube.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;
}
使用文件
C/C++中,可以用libc库的fopen,fclose等API来访问文件
js运行在浏览器的沙盒环境中,并不能直接访问本地文件系统,不过,Emscripten模拟了一个文件系统,这样你可以在你的C/C++代码中继续使用libc的API。
你想访问的文件应该通过preload或者embedded的方式打包到Emscripten虚拟的文件系统中。
测试集中,hello_world_file.cpp展示了怎么加载一个文件。测试代码和测试文件hello_world_file.txt如下面所示:
    #include <stdio.h>
    int main() {
      FILE *file = fopen("tests/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.
    ==
下面命令是在任何编译代码运行前指定一个数据文件预加载到Emscripten的虚拟文件系统。这个方法很有用,因为浏览器只能异步获取数据的,而原生代码(C/C++)很多都是使用的同步文件API,那么,用这个方法可以确保数据加载完成之前,编译代码(C/C++编译之后的js)不会从Emscripten的虚拟文件系统中去取数据,也就不会出错。下面是编译命令:
./emcc tests/hello_world_file.cpp -o hello.html --preload-file tests/hello_world_file.txt
运行生成的HTML,就能看到hello_world_file.txt文件的内容。
优化代码
默认情况下,和gcc以及clang等编译器一样,Emscripten生成的编译代码没有经过编译优化。那么,你可以在命令行参数中使用-O1,生成轻微优化的代码。
./emcc -O1 tests/hello_world.cpp
因为编译生成a.out.js的过程实际上并不真的需要优化,所以实际上你加不加-O1,从编译时间(或者说编译速度)上,你是看不出区别的。
但是真的没区别吗?
你可以看看生成的a.out.js文件,就能发现还是有区别的。-O1的优化有一些微小的优化并且清除了一些运行时断言,比如,在生成的代码中,printf函数,会被替换成put。
想编译优化,不仅可以用-O1,还可以用-O2,-O2优化的程度更厉害。你可以试一下,它的编译代码跟-O1又有很大差别。
Emscripten 测试集
Emscripten给大家提供了非常多的测试用例,几乎覆盖了Emscripten的所有功能。对于开发者来说,这是非常好的资源。
关于测试集的更多情况,可以点击了解。
小贴士和下一步
本教学告诉了你使用Emscripten的第一步,就是使用命令行编译一个c/c++代码为js或者HTML。为了了解更多Emscripten,给你几个小贴士:
- 本站点还有很多高级一点的教程,比如,关于编译和构建一个项目,整合你的原生代码到web环境,打包你的代码等。
 - 关于怎么用Emscripten,测试集是一个很好的参考的地方。比如你想了解--pre-js选项怎么工作,你就在测试集里面搜--pre-js,通常来说你会搜到好几个例子。
 - 读一下本站的settings.js、emcc和emscripten.h的部分。
 - 读一下本站的FAQ。
 
Emscripten主题系列文章是emscripten中文站点的一部分内容。
第一个主题介绍代码可移植性与限制
第二个主题介绍Emscripten的运行时环境
第三个主题第一篇文章介绍连接C++和JavaScript
第三个主题第二篇文章介绍embind
第四个主题介绍文件和文件系统
第六个主题介绍Emscripten如何调试代码
Emscripten教程之入门指导的更多相关文章
- 【ZZ】Web开发的入门指导 | 菜鸟教程
		
Web开发的入门指导 http://www.runoob.com/w3cnote/a-beginners-guide-to-web-development.html
 - 新手入门指导:Vue 2.0 的建议学习顺序
		
起步 1. 扎实的 JavaScript / HTML / CSS 基本功.这是前置条件. 2. 通读官方教程 (guide) 的基础篇.不要用任何构建工具,就只用最简单的 <script> ...
 - React-Native入门指导之iOS篇 —— 一、准备工作
		
React-Native 入门指导系列教程目录 一.准备工作 (已完成) 二.项目介绍与调试 三.CSS样式与Flex布局 四.常用UI控件的使用 五.JSX在React-Native中的应用 六.事 ...
 - React-Native入门指导之iOS篇
		
React-Native 入门指导系列教程目录 一.准备工作 (已完成) 二.项目介绍与调试 三.CSS样式与Flex布局 四.常用UI控件的使用 五.JSX在React-Native中的应用 六.事 ...
 - 全网最完整的Redis入门指导
		
前言 本文提供全网最完整的Redis入门指导教程,下面我们从下载Redis安装包开始,一步一步的学习使用. 下载Redis 官网提供的Redis安装包是服务于Linux的,而我们需要在Window下使 ...
 - Redis入门指导
		
前言 本文提供全网最完整的Redis入门指导教程,下面我们从下载Redis安装包开始,一步一步的学习使用. 下载Redis 官网提供的Redis安装包是服务于Linux的,而我们需要在Window下使 ...
 - Emscripten教程之代码可移植性与限制(一)
		
Emscripten教程之代码可移植性与限制(一) 翻译:云荒杯倾本文是Emscripten-WebAssembly专栏系列文章之一,更多文章请查看专栏.也可以去作者的博客阅读文章.欢迎加入Wasm和 ...
 - Android Studio教程从入门到精通
		
最新2.0系列文章参考: Android Studio2.0 教程从入门到精通Windows版 - 安装篇Android Studio2.0 教程从入门到精通Windows版 - 入门篇Android ...
 - Spring MVC 教程,快速入门,深入分析
		
http://elf8848.iteye.com/blog/875830/ Spring MVC 教程,快速入门,深入分析 博客分类: SPRING Spring MVC 教程快速入门 资源下载: ...
 
随机推荐
- 正则表达式(三)——Java中的相关函数
			
1.前言 之前在学习Python时,我已经说过正则表达式的相关语法,这里不再赘述了,有需要可以参考: 2020.10.7 正则表达式(一) - ShineLe - 博客园 现在开始学习Java中的正则 ...
 - Java:IO流(二)——InputStream/OutputStream具体用法:FileXXXStream、ByteArrayXXXStream
			
1.说明 InputStream和OutputStream是Java标准库中最基本的IO流,它们都位于java.io包中,该包提供了所有同步IO的功能. 2.模块:java.io.InputStrea ...
 - LeetCode-018-四数之和
			
四数之和 题目描述:给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 targe ...
 - [USACO20FEB]Equilateral Triangles P 题解
			
优雅的暴力. 设三个点为 \((i,j,k)\),则有 \(6\) 个未知数即 \(x_i,x_j,x_k,y_i,y_j,y_k\).又因为有 \(2\) 条关于这 \(6\) 个未知数的方程 \( ...
 - 七天接手react项目-起步
			
七天接手react项目-起步 背景 假如七天后必须接手一个 react 项目(spug - 一个开源运维平台),而笔者只会 vue,之前没有接触过 react,此刻能做的就是立刻展开一个"7 ...
 - BBS 项目(四)
			
目录 BBS 项目(四) 首页布局 个人头像显示 个人站点路由设计 个人站点页面设计 base.html site.html 左侧过滤功能 404.html BBS 项目(四) 首页布局 <!D ...
 - WMTS服务解析
			
WMTS服务解析 1.基本思路 拼接地址,请求xml文件 ↓ 处理获取的xml文件,返回参数 ↓ 获取xml解析得到的参数,组织结构 2.对应函数 requestWMTS (olUrl) ↓ hand ...
 - SpringBoot 中实现跨域的几种方式
			
一.为什么会出现跨域问题 出于浏览器的同源策略限制.同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响. ...
 - JavaWeb  Bug处理
			
1.(getParameter) Java文本无法解析Cannot resolve method 'getParameter(java.lang.String)方法 解决方法:1.在projec ...
 - MySQL SQL Injection Cheat Sheet
			
MySQL SQL Injection Cheat Sheet Some useful syntax reminders for SQL Injection into MySQL databases- ...