WebAssembly - Como Desenvolver Sua Primeira Aplicação
O WebAssembly (WASM) é um tipo de código de baixo nível que permite desenvolver aplicações com alto desempenho, como jogos virtuais, realidade virtual e aumentada, edição de foto e vídeo, dentre outras, que requerem desempenho nativo. É focado principalmente para uso na Internet, mas também pode ser utilizado em outros ambientes.[1] Pode-se escrever código em linguagens como C, C++ e Rust e, em seguida, compilá-lo para WebAssembly, que pode ser então convertido pelos navegadores em linguagem de máquina e executado juntamente com JavaScript. [2] O WebAssembly não pretende substituir o JavaScript, mas ser um complemento a ele.
Ele foi criado para ser, dentre outras coisas:
- Rápido
- Seguro
- Portátil
- Independente de hardware
- Independente de linguagem
- Independente de plataforma
- Fácil de ler e inspecionar
Não é esperado que um programador escreva um código em WebAssembly, uma vez que ele pode ser produzido a partir de linguagens de mais alto nível. Porém, é importante conhecer a ferramenta caso ela seja utilizada. Aqui, será mostrado como criar uma aplicação WebAssembly a partir de um código C++, como criar a partir de Rust e como escrever no formato textual de WebAssembly.
Criando WebAssembly a partir de C++
editarInicialmente, é preciso instalar o Emscripten, uma cadeia de ferramentas (toolchain) de compilação para WebAssembly, que permite compilar código em C, C++, Rust e outras linguagens que utilizam LLVM em WebAssembly. [3] Em um diretório, executamos o seguinte comando Git: [4]
git clone https://github.com/emscripten-core/emsdk.git
Em seguida, entramos na pasta criada, pegamos a versão mais recente com o pull do Git e instalamos no nosso computador.
cd emsdk git pull ./emsdk install latest ./emsdk activate latest
Obs.: É preciso ter o Python e o Git instalados anteriormente para ser possível instalar o Emscripten.
Após a instalação, é útil ter adicionar os diretórios emsdk
e emsdk/upstream/emscripten
ao PATH, de modo a permitir a execução dos comandos instalados no shell. Isso pode ser feito com:
source ./emsdk_env.sh echo source /caminho-para-pasta/emsdk/emsdk_env.sh >> ~/.bashrc
Uma vez que temos o Emscripten instalado, podemos compilar os arquivos que desejarmos. Podemos fazer isso de modo a criar um arquivo HTML que utiliza o nosso código, permitindo testar o nosso exemplo num navegador, ou apenas um arquivo JavaScript, que pode ser incorporado posteriormente a um projeto.
Começamos criando um arquivo C++ (Ex.: Hello World, em um arquivo hello.cpp).
#include <iostream> using namespace std; int main() { cout << "Hello World" << endl; return 0; }
Agora, compilamos código no terminal, utilizando o comando em++ (ou emcc, para arquivos C):
em++ hello.cpp -o hello.html
Após a compilação, teremos três arquivos novos:
- Um arquivo hello.html, utilizado para exibir o código no navegador;
- Um arquivo hello.js, que integra o WebAssembly com as aplicações web;
- Um arquivo hello.wasm, em WebAssembly
Para verificar o resultado, abrimos o HTML em um navegador por meio de um servidor HTTP. Se fizermos isso diretamente do disco, haverá um erro, como medida de segurança. [5]
Podemos utilizar para essa situação Python, executando:
python3 -m http.server 8000
E depois indo ao endereço http://localhost/8000
Ou utilizando NodeJS, com o comando:
npx http-server /caminho-para-pasta -o -p 8000
E seguindo ao mesmo endereço http://localhost/8000
após isso.
Veremos, então, o resultado apresentado na imagem.
Se quisermos o arquivo JavaScript apenas, podemos passar apenas o nome do arquivo C++ (sem o arquivo html a ser gerado). considerando o uso de um arquivo hello.cpp, como no caso anterior, podemos usar:
em++ hello.cpp
Isso criará um arquivo .wasm e um arquivo .js, que poderá então ser acoplado a um arquivo HTML.
Para arquivos C o procedimento é idêntico, diferindo apenas no uso do comando emcc
no lugar de em++
Criando WebAssembly a partir de Rust
editarInicialmente, é preciso instalar o Rust, que vem com o rustc
, compilador de Rust; o cargo
, gerenciador de pacotes; e o rustup
, dentre outras ferramentas. [6] Para compilar o código Rust para WebAssembly, necessita-se do wasm-pack
, que pode ser instalado com o gerenciador de pacotes do Rust por meio da instrução: [7]
cargo install wasm-pack
A partir daí, criamos um novo projeto Rust utilizando o cargo
.
cargo new hello_world
Dentro dele, teremos uma pasta src com um arquivo main.rs, que já possuirá um código Hello World em Rust, como mostrado abaixo:
fn main() { println!("Hello, World!"); }
Inicialmente, precisamos adaptar o arquivo main.rs para rodar como WebAssembly.
use wasm_bindgen::prelude::*; #[wasm_bindgen] pub fn hello_world() { web_sys::console::log_1(&"Hello, World!".into()); }
A primeira linha importa ao código funcionalidades do módulo wasm_bindgen::prelude
, que é utilizado na função, que recebe um atributo #[wasm_bindgen].
O web_sys::console::log_1
, por sua vez, permite o acesso ao console do JavaScript.
Para podermos compilar para o formato WebAssembly, precisamos do target wasm32-unknown-unknown
, que pode ser instalado com o rustup
.
rustup target add wasm32-unknown-unknown
E, no arquivo Cargo.toml, adicionamos as linhas:
[dependencies] wasm-bindgen = "0.2"
Finalmente, podemos criar o arquivo WebAssembly com o comando:
cargo build --target wasm32-unknown-unknown
E o arquivo JavaScript para a interação com o WebAssembly com os comandos:
cargo install wasm-bindgen-cli wasm-bindgen target/wasm32-unknown-unknown/debug/hello_world.wasm --out-dir ./out --web
Agora podemos utilizar o nosso WebAssembly na web, criando um arquivo HTML com as seguintes linhas:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Hello, World! em Rust e WebAssembly</title> </head> <body> <script type="module"> import init, { hello_world } from "./out/hello_world.js"; async function run() { await init(); hello_world(); } run(); </script> </body> </html>
Como no caso de C++, devemos executar o arquivo utilizando um servidor HTTP, como Python ou NodeJS.
Escrevendo no formato textual
editarO formato WebAssembly criado anteriormente é um formato binário, difícil de ser entendido por humanos. Porém, há uma representação textual desse formato de forma a ser exibido em editores de texto, chamada WAT (WebAssembly Text Format). [8] Não é necessário utilizar esse formato para a maioria das aplicações, mas é importante caso se queira construir módulos WebAssembly ou compiladores. Aqui não será apresentado um tutorial sobre o formato textual, mas pode-se encontrar um aqui: Understanding_the_text_format
Podemos começar criando um arquivo simple.wat, com o seguinte conteúdo:
(module (func $i (import "my_namespace" "imported_func") (param i32)) (func (export "exported_func") i32.const 42 call $i ) )
Para converter um arquivo WAT em WebAssembly binário, podemos utilizar a ferramenta wabt (WebAssembly Binary Toolkit), que inclui dentro dela a wat2wasm, que, converte códigos WAT em WASM, e a wasm2wat, que faz o oposto. O wabt pode ser obtido por meio de seu repositório no GitHub: [9]
$ git clone --recursive https://github.com/WebAssembly/wabt $ cd wabt $ git submodule update --init $ mkdir build $ cd build $ cmake .. $ cmake --build .
Em seguida, adicionamos o diretório /wabt/out/clang/Debug ao PATH:
echo export PATH="/wabt/out/clang/Debug:$PATH" >> ~/.bashrc source ~/.bashrc
Finalmente, podemos converter o arquivo com:
wat2wasm simple.wat -o simple.wasm
Com isso, obtemos um arquivo em formato binário, que pode ser usado em aplicações web.
Referências
editar- ↑ https://webassembly.github.io/spec/core/intro/introduction.html
- ↑ https://developer.mozilla.org/en-US/docs/WebAssembly
- ↑ https://emscripten.org/docs/introducing_emscripten/about_emscripten.html
- ↑ https://emscripten.org/docs/getting_started/downloads.html
- ↑ https://developer.mozilla.org/en-US/docs/WebAssembly/C_to_Wasm
- ↑ https://www.rust-lang.org/tools/install
- ↑ https://developer.mozilla.org/en-US/docs/WebAssembly/Rust_to_Wasm
- ↑ https://developer.mozilla.org/en-US/docs/WebAssembly/Understanding_the_text_format
- ↑ https://github.com/WebAssembly/wabt