diff --git a/README.md b/README.md index 1505672a4..c942d83f2 100644 --- a/README.md +++ b/README.md @@ -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. + + + + ## 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. diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index 9bb6be29f..112d62a72 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "rustpython_wasm" version = "0.1.0" -authors = ["rmliddle "] +authors = ["Ryan Liddle "] + +[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' \ No newline at end of file +opt-level = "s" diff --git a/wasm/app/bootstrap.js b/wasm/app/bootstrap.js new file mode 100644 index 000000000..7934d627e --- /dev/null +++ b/wasm/app/bootstrap.js @@ -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)); diff --git a/wasm/app/index.html b/wasm/app/index.html new file mode 100644 index 000000000..00f0d2407 --- /dev/null +++ b/wasm/app/index.html @@ -0,0 +1,10 @@ + + + + + RustPython Starter Application + + + + + diff --git a/wasm/app/index.js b/wasm/app/index.js new file mode 100644 index 000000000..10797d688 --- /dev/null +++ b/wasm/app/index.js @@ -0,0 +1,3 @@ +import * as rp from "rustpython_wasm"; + +rp.run_code("print('Hello Python!')\n"); diff --git a/wasm/app/package.json b/wasm/app/package.json new file mode 100644 index 000000000..a1f9c4191 --- /dev/null +++ b/wasm/app/package.json @@ -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" +} diff --git a/wasm/app/webpack.config.js b/wasm/app/webpack.config.js new file mode 100644 index 000000000..80ad8142d --- /dev/null +++ b/wasm/app/webpack.config.js @@ -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']) + ], +}; diff --git a/wasm/index.html b/wasm/index.html deleted file mode 100644 index 13313d61b..000000000 --- a/wasm/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - RustPython/Wasm - - - - - \ No newline at end of file diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs new file mode 100644 index 000000000..6c5d39209 --- /dev/null +++ b/wasm/src/lib.rs @@ -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"), + } +}