Merge pull request #182 from rmliddle/wasm-steps

Exposes API for running Python via JS
This commit is contained in:
Windel Bouwman
2018-11-12 20:30:52 +01:00
committed by GitHub
9 changed files with 136 additions and 35 deletions

View File

@@ -98,9 +98,14 @@ $ cargo run -- -c 'import statistics'
# Compiling to WebAssembly
At this stage RustPython only has preliminary support for web assembly. The instructions here are intended for developers or those wishing to run a toy example.
## Setup
Using `rustup` add the compile target `wasm32-unknown-emscripten`. To do so you will need to have [rustup](https://rustup.rs/) installed.
To get started, install [wasm-bingden](https://rustwasm.github.io/wasm-bindgen/whirlwind-tour/basic-usage.html)
and [wasm-webpack](https://rustwasm.github.io/wasm-pack/installer/). You will also need to have `npm` installed.
<!-- Using `rustup` add the compile target `wasm32-unknown-emscripten`. To do so you will need to have [rustup](https://rustup.rs/) installed.
```bash
rustup target add wasm32-unknown-emscripten
@@ -114,41 +119,58 @@ cd emsdk-portable/
./emsdk update
./emsdk install sdk-incoming-64bit
./emsdk activate sdk-incoming-64bit
source ./emsdk_env.sh
```
``` -->
## Build
Move into the `wasm` directory. This contains a custom binary crate optimized for a web assembly build.
Move into the `wasm` directory. This contains a custom library crate optimized for wasm build of RustPython.
```bash
cd wasm
```
From here run the build. This can take several minutes depending on the machine.
```
cargo build --target=wasm32-unknown-emscripten --release
```
Upon successful build, the following files will be available:
```
target/wasm32-unknown-emscripten/release/rustpython_wasm.wasm
target/wasm32-unknown-emscripten/release/rustpython_wasm.js
wasm-pack build
```
- `rustpython_wasm.wasm`: the wasm build for rustpython. It includes both an parser and virtual machine.
- `rustpython_wasm.js`: the loading scripts for the above wasm file.
You will also find `index.html` in the `wasm` directory.
From here, you can copy these 3 files into the static assets directory of your web server and you should be
able to see the ouput in the web console of your browser.
Upon successful build, cd in the the `/pkg` directory and run:
```
Hello RustPython!
npm link
```
Now move back out into the `/app` directory. The files here have been adapted from [wasm-pack-template](https://github.com/rustwasm/wasm-pack-template).
Finally, run:
```
npm install
```
and you will be able to run the files with:
```
webpack-dev-server
```
Open a browser console and see the output of rustpython_wasm. To verify this, modify the line in `app/index.js`
```js
rp.run_code("print('Hello Python!')\n");
```
To the following:
```js
rp.run_code("assert(False)\n");
```
and you should observe: `Execution failed` in your console output, indicating that the execution of RustPython has failed.
# Code style
The code style used is the default rustfmt codestyle. Please format your code accordingly.

View File

@@ -1,7 +1,10 @@
[package]
name = "rustpython_wasm"
version = "0.1.0"
authors = ["rmliddle <ryan@rmliddle.com>"]
authors = ["Ryan Liddle <ryan@rmliddle.com>"]
[lib]
crate-type = ["cdylib", "rlib"]
[workspace]
members = []
@@ -9,6 +12,9 @@ members = []
[dependencies]
rustpython_parser = {path = "../parser"}
rustpython_vm = {path = "../vm"}
cfg-if = "0.1.2"
wasm-bindgen = "0.2"
[profile.release]
opt-level = 's'
opt-level = "s"

5
wasm/app/bootstrap.js vendored Normal file
View File

@@ -0,0 +1,5 @@
// A dependency graph that contains any wasm must all be imported
// asynchronously. This `bootstrap.js` file does the single async import, so
// that no one else needs to worry about it again.
import("./index.js")
.catch(e => console.error("Error importing `index.js`:", e));

10
wasm/app/index.html Normal file
View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>RustPython Starter Application</title>
</head>
<body>
<script src="./bootstrap.js"></script>
</body>
</html>

3
wasm/app/index.js Normal file
View File

@@ -0,0 +1,3 @@
import * as rp from "rustpython_wasm";
rp.run_code("print('Hello Python!')\n");

27
wasm/app/package.json Normal file
View File

@@ -0,0 +1,27 @@
{
"name": "app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {
"hello-wasm-pack": "^0.1.0",
"webpack": "^4.16.3",
"webpack-cli": "^3.1.0",
"webpack-dev-server": "^3.1.5",
"copy-webpack-plugin": "^4.5.2"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/RustPython/RustPython.git"
},
"author": "Ryan Liddle",
"license": "MIT",
"bugs": {
"url": "https://github.com/RustPython/RustPython/issues"
},
"homepage": "https://github.com/RustPython/RustPython#readme"
}

View File

@@ -0,0 +1,14 @@
const CopyWebpackPlugin = require("copy-webpack-plugin");
const path = require('path');
module.exports = {
entry: "./bootstrap.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bootstrap.js",
},
mode: "development",
plugins: [
new CopyWebpackPlugin(['index.html'])
],
};

View File

@@ -1,14 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>RustPython/Wasm</title>
<script>
var Module = {
wasmBinaryFile: "rustpython_wasm.wasm"
}
</script>
<script src="rustpython_wasm.js"></script>
</head>
<body></body>
</html>

28
wasm/src/lib.rs Normal file
View File

@@ -0,0 +1,28 @@
extern crate rustpython_vm;
extern crate wasm_bindgen;
use rustpython_vm::compile;
use rustpython_vm::VirtualMachine;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
// Use `js_namespace` here to bind `console.log(..)` instead of just
// `log(..)`
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
#[wasm_bindgen]
pub fn run_code(source: &str) -> () {
//add hash in here
log("Running RustPython");
log(&source.to_string());
let mut vm = VirtualMachine::new();
let code_obj = compile::compile(&mut vm, &source.to_string(), compile::Mode::Exec, None);
let builtins = vm.get_builtin_scope();
let vars = vm.context().new_scope(Some(builtins));
match vm.run_code_obj(code_obj.unwrap(), vars) {
Ok(_value) => log("Execution successful"),
Err(_) => log("Execution failed"),
}
}