Emscripten教程之入門指導

翻譯:雲荒杯傾
本文是Emscripten-WebAssembly專欄系列文章之一,更多文章請查看專欄。
也能夠去做者的博客閱讀文章。
歡迎加入Wasm和emscripten技術交流羣,羣聊號碼:939206522。html

下面是正文:

若是隻是想要入門Emscripten的話,使用Emscripten是很是簡單的。本教學將教會你從命令行編譯Emscripten代碼的種種步驟,以及Emscripten代碼中怎樣使用文件和使用主要的編譯優化flag。node

要事優先

首先是確保你已經下載而且安裝好了Emscripten。根據你的操做系統不一樣,下載和安裝過程稍有不一樣。c++

Emscripten主要是經過emcc(Emscripten Compiler Frontend)來工做的。這是個命令行工具,它會調用其餘編譯須要的工具,能夠將它當作是標準編譯器好比gcc或者clang的命令行版本。wimdows系統的話,命令行中使用emcc,Linux下使用./emcc。git

驗證Emscripten

第一次使用Emscripten,請先使用如下命令驗證Emscripten是否正確安裝:github

emcc -v

若是有警告發生,多是由於缺乏一些工具,請去看這個連接解決
若是沒有警告或報錯,就往下看。web

運行Emscripten

如今就可使用Emscripten把C/C++代碼編譯成JavaScript了。canvas

首先,寫一個待編譯爲JavaScript的C文件。好比hello_world.c,像下面這樣:segmentfault

#include <stdio.h>
    
    int main() {
      printf("hello, world!\n");
      return 0;
    }

注:這是Emscripten提供的測試集中最簡單的一個C文件。瀏覽器

爲了編譯這個C文件,你只須要在test目錄下打開命令行,emcc後面跟上這個文件名就好了。less

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如何調試代碼

相關文章
相關標籤/搜索