mirror of
https://github.com/bytedream/litbwraw.git
synced 2025-05-09 12:15:13 +02:00
initial commit
This commit is contained in:
commit
96adaf3211
51
.github/workflows/deploy.yml
vendored
Normal file
51
.github/workflows/deploy.yml
vendored
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
name: Deploy
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pages: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: "pages"
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
environment:
|
||||||
|
name: github-pages
|
||||||
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install mdbook
|
||||||
|
run: cargo install mdbook
|
||||||
|
|
||||||
|
- name: Install emscripten
|
||||||
|
run: sudo apt-get install -y --no-install-recommends emscripten
|
||||||
|
|
||||||
|
- name: Add wasm32-unknown-emscripten toolchain
|
||||||
|
run: rustup target add wasm32-unknown-emscripten
|
||||||
|
|
||||||
|
- name: Build book
|
||||||
|
run: mdbook build
|
||||||
|
|
||||||
|
- name: Build playground
|
||||||
|
run: BOOK_OUTPUT_PATH="$PWD/book" cargo build --release --manifest-path=lua-playground/Cargo.toml --target wasm32-unknown-emscripten
|
||||||
|
|
||||||
|
- name: Setup Pages
|
||||||
|
uses: actions/configure-pages@v2
|
||||||
|
|
||||||
|
- name: Upload book
|
||||||
|
uses: actions/upload-pages-artifact@v1
|
||||||
|
with:
|
||||||
|
path: ./book
|
||||||
|
|
||||||
|
- name: Deploy
|
||||||
|
id: deployment
|
||||||
|
uses: actions/deploy-pages@v1
|
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
book
|
||||||
|
|
||||||
|
lua-playground/target
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) bytedream
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
38
README.md
Normal file
38
README.md
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<div align="center">
|
||||||
|
<h1>Lua in the Browser, with Rust and WebAssembly</h1>
|
||||||
|
<strong>This smol book describes how to use Lua in the Browser, powered by Rust WebAssembly.</strong>
|
||||||
|
<h3><a href="https://bytedream.github.io/litbwraw/">Read the book</a></h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## 🛠 Building the Book
|
||||||
|
|
||||||
|
The book is made using [`mdbook`](https://github.com/rust-lang-nursery/mdBook):
|
||||||
|
```shell
|
||||||
|
$ cargo install mdbook
|
||||||
|
```
|
||||||
|
Make sure the `cargo install` directory is in your `$PATH` so that you can run
|
||||||
|
the binary.
|
||||||
|
|
||||||
|
To build it, simply run this command from this directory:
|
||||||
|
```shell
|
||||||
|
$ mdbook build
|
||||||
|
```
|
||||||
|
This will build the book and output files into the `book` directory. From
|
||||||
|
there you can navigate to the `index.html` file to view it in your browser.
|
||||||
|
|
||||||
|
You could also run the following command to automatically build the book whenever you make changes to it in the `src` directory:
|
||||||
|
```shell
|
||||||
|
$ mdbook serve
|
||||||
|
```
|
||||||
|
|
||||||
|
This book also contains a little demo/repl which is able to execute arbitrary Lua code in the browser via WebAssembly.
|
||||||
|
To build the required files, run the following command:
|
||||||
|
```shell
|
||||||
|
$ BOOK_OUTPUT_PATH="$PWD/book" cargo build --release --target wasm32-unknown-emscripten --manifest-path=lua-playground/Cargo.toml
|
||||||
|
```
|
||||||
|
Make sure to run this command _after_ you build the book.
|
||||||
|
Also, you have to re-run it everytime when the book is rebuilt.
|
||||||
|
|
||||||
|
## ⚖ License
|
||||||
|
|
||||||
|
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for more details.
|
13
book.toml
Normal file
13
book.toml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[book]
|
||||||
|
authors = ["bytedream"]
|
||||||
|
language = "en"
|
||||||
|
multilingual = false
|
||||||
|
src = "src"
|
||||||
|
title = "Lua in the Browser, with Rust and WebAssembly"
|
||||||
|
|
||||||
|
[output.html]
|
||||||
|
additional-js = ["lua-playground/lua-playground-loader.js"]
|
||||||
|
git-repository-url = "https://github.com/bytedream/litbwraw"
|
||||||
|
|
||||||
|
[output.html.playground]
|
||||||
|
editable = true
|
373
lua-playground/Cargo.lock
generated
Normal file
373
lua-playground/Cargo.lock
generated
Normal file
@ -0,0 +1,373 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bstr"
|
||||||
|
version = "1.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "542f33a8835a0884b006a0c3df3dadd99c0c3f296ed26c2fdc8028e01ad6230c"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.3.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "home"
|
||||||
|
version = "0.5.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.151"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lua-playground"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"mlua",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lua-src"
|
||||||
|
version = "546.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2da0daa7eee611a4c30c8f5ee31af55266e26e573971ba9336d2993e2da129b2"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "luajit-src"
|
||||||
|
version = "210.5.3+29b0b28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c2bb89013916ce5c949f01a1fbd6d435a58e1d980767a791d755911211d792d"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"which",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mlua"
|
||||||
|
version = "0.9.2"
|
||||||
|
source = "git+https://github.com/khvzak/mlua.git#0b9a85e1838bed67ae69f11b42de84bcf19da80c"
|
||||||
|
dependencies = [
|
||||||
|
"bstr",
|
||||||
|
"mlua-sys",
|
||||||
|
"num-traits",
|
||||||
|
"once_cell",
|
||||||
|
"rustc-hash",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mlua-sys"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "git+https://github.com/khvzak/mlua.git#0b9a85e1838bed67ae69f11b42de84bcf19da80c"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"lua-src",
|
||||||
|
"luajit-src",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.70"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "0.38.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.193"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.193"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.41"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "which"
|
||||||
|
version = "5.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9bf3ea8596f3a0dd5980b46430f2058dfe2c36a27ccfbb1845d6fbfcd9ba6e14"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"home",
|
||||||
|
"once_cell",
|
||||||
|
"rustix",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.48.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm 0.48.5",
|
||||||
|
"windows_aarch64_msvc 0.48.5",
|
||||||
|
"windows_i686_gnu 0.48.5",
|
||||||
|
"windows_i686_msvc 0.48.5",
|
||||||
|
"windows_x86_64_gnu 0.48.5",
|
||||||
|
"windows_x86_64_gnullvm 0.48.5",
|
||||||
|
"windows_x86_64_msvc 0.48.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm 0.52.0",
|
||||||
|
"windows_aarch64_msvc 0.52.0",
|
||||||
|
"windows_i686_gnu 0.52.0",
|
||||||
|
"windows_i686_msvc 0.52.0",
|
||||||
|
"windows_x86_64_gnu 0.52.0",
|
||||||
|
"windows_x86_64_gnullvm 0.52.0",
|
||||||
|
"windows_x86_64_msvc 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
10
lua-playground/Cargo.toml
Normal file
10
lua-playground/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "lua-playground"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
mlua = { git = "https://github.com/khvzak/mlua.git", features = ["lua51"] }
|
23
lua-playground/build.rs
Normal file
23
lua-playground/build.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
use std::env;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let book_output_path = env::var("BOOK_OUTPUT_PATH").map_or(None, Some);
|
||||||
|
|
||||||
|
let out_dir = env::var("OUT_DIR").unwrap();
|
||||||
|
let pkg_name = env::var("CARGO_PKG_NAME").unwrap();
|
||||||
|
let target_path = PathBuf::from(out_dir)
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.to_path_buf();
|
||||||
|
|
||||||
|
println!("cargo:rustc-link-arg=-sEXPORTED_RUNTIME_METHODS=['cwrap','ccall']");
|
||||||
|
println!("cargo:rustc-link-arg=-sEXPORT_ES6=1");
|
||||||
|
println!("cargo:rustc-link-arg=-sERROR_ON_UNDEFINED_SYMBOLS=0");
|
||||||
|
println!("cargo:rustc-link-arg=--no-entry");
|
||||||
|
println!("cargo:rustc-link-arg=-o{}.js", book_output_path.map(PathBuf::from).unwrap_or(target_path).join(pkg_name).to_string_lossy());
|
||||||
|
}
|
86
lua-playground/lua-playground-loader.js
Normal file
86
lua-playground/lua-playground-loader.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
let luaStdout = null;
|
||||||
|
let luaStderr = null;
|
||||||
|
|
||||||
|
async function run_lua_code(elem) {
|
||||||
|
let result;
|
||||||
|
if (!elem.nextElementSibling) {
|
||||||
|
result = document.createElement('code');
|
||||||
|
result.classList.add('result', 'hljs', 'language-bash');
|
||||||
|
elem.after(result)
|
||||||
|
} else {
|
||||||
|
result = elem.nextElementSibling;
|
||||||
|
}
|
||||||
|
result.innerHTML = '';
|
||||||
|
|
||||||
|
if (window.luaInstance === undefined) {
|
||||||
|
let wasm;
|
||||||
|
try {
|
||||||
|
wasm = await import(window.rootPath + '/lua-playground.js');
|
||||||
|
} catch (e) {
|
||||||
|
result.innerText = 'Failed to load wasm module: ' + e.toString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const module = {
|
||||||
|
print(msg) {
|
||||||
|
if (luaStdout) luaStdout(msg)
|
||||||
|
},
|
||||||
|
printErr(msg) {
|
||||||
|
if (luaStderr) luaStderr(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const luaPlayground = await wasm.default(module);
|
||||||
|
|
||||||
|
window.luaInstance = luaPlayground.ccall('lua_new', 'number', [], []);
|
||||||
|
window.luaExecute = luaPlayground.cwrap('lua_execute', null, ['number', 'string']);
|
||||||
|
}
|
||||||
|
|
||||||
|
luaStdout = (msg) => result.innerHTML += msg + '<br>';
|
||||||
|
luaStderr = (msg) => result.innerHTML += msg + '<br>';
|
||||||
|
window.luaExecute(window.luaInstance, ace.edit(elem).getValue());
|
||||||
|
luaStdout = null;
|
||||||
|
luaStderr = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
const inputElements = document.querySelectorAll('.language-lua.editable');
|
||||||
|
|
||||||
|
for (const inputElem of inputElements) {
|
||||||
|
const editor = ace.edit(inputElem);
|
||||||
|
|
||||||
|
/* adds the run and reset button */
|
||||||
|
const buttons = inputElem.previousElementSibling;
|
||||||
|
const resetButton = document.createElement('button');
|
||||||
|
resetButton.classList.add('fa', 'fa-history', 'reset-button');
|
||||||
|
resetButton.title = 'Undo changes';
|
||||||
|
resetButton.ariaLabel = 'Undo changes';
|
||||||
|
resetButton.onclick = () => editor.setValue(editor.originalCode.trim(), -1);
|
||||||
|
buttons.prepend(resetButton);
|
||||||
|
const runButton = document.createElement('button');
|
||||||
|
runButton.classList.add('fa', 'fa-play', 'play-button');
|
||||||
|
runButton.title = 'Run this code';
|
||||||
|
runButton.ariaLabel = 'Run this code';
|
||||||
|
runButton.onclick = () => run_lua_code(inputElem);
|
||||||
|
buttons.append(runButton);
|
||||||
|
|
||||||
|
/* i don't know why, but the editor always has an extra newline. when selecting it and trimming it, the newline
|
||||||
|
gets removed */
|
||||||
|
editor.setValue(editor.originalCode.trim(), -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function reloadES6() {
|
||||||
|
window.rootPath = document.currentScript.src.replace(/lua-playground\/lua-playground-loader\.js.*/, '')
|
||||||
|
const injectScript = document.createElement('script');
|
||||||
|
injectScript.type = 'module';
|
||||||
|
injectScript.src = document.currentScript.src;
|
||||||
|
document.body.append(injectScript);
|
||||||
|
}
|
||||||
|
|
||||||
|
// this script is not loaded as es6 module, so it has to "elevate" itself to an es6 module by re-injecting itself with
|
||||||
|
// the `reloadES6` function
|
||||||
|
if (window.rootPath) {
|
||||||
|
main()
|
||||||
|
} else {
|
||||||
|
reloadES6()
|
||||||
|
}
|
18
lua-playground/src/lib.rs
Normal file
18
lua-playground/src/lib.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
use std::ffi::{c_char, CStr};
|
||||||
|
use mlua::Lua;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn lua_new() -> *mut Lua {
|
||||||
|
let lua = Lua::new();
|
||||||
|
Box::into_raw(Box::new(lua))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn lua_execute(lua: *mut Lua, to_execute: *const c_char) {
|
||||||
|
let lua: &mut Lua = &mut *lua;
|
||||||
|
let to_execute = CStr::from_ptr(to_execute);
|
||||||
|
|
||||||
|
if let Err(err) = lua.load(&to_execute.to_string_lossy().to_string()).exec() {
|
||||||
|
eprintln!("{}", err)
|
||||||
|
}
|
||||||
|
}
|
13
src/SUMMARY.md
Normal file
13
src/SUMMARY.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Summary
|
||||||
|
|
||||||
|
[Introduction](./introduction.md)
|
||||||
|
|
||||||
|
- [Setup](./setup.md)
|
||||||
|
|
||||||
|
- [Tutorial](tutorial/introduction.md)
|
||||||
|
- [Creating a project](tutorial/creating-a-project.md)
|
||||||
|
- [Adding wasm logic](tutorial/adding-wasm-logic.md)
|
||||||
|
- [Compiling](tutorial/compiling.md)
|
||||||
|
- [Calling from Javascript](tutorial/calling-from-javascript.md)
|
||||||
|
|
||||||
|
- [Testing](./testing.md)
|
11
src/introduction.md
Normal file
11
src/introduction.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Lua in the Browser, with Rust 🦀 and WebAssembly
|
||||||
|
|
||||||
|
This smol book describes how to use Lua in the Browser, powered by Rust WebAssembly.
|
||||||
|
|
||||||
|
> You should have basic knowledge of Rust, Rust FFI and Javascript, the book will not explain language features or constructs that are irrelevant to Rust WebAssembly.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
```lua,editable
|
||||||
|
print("Hello from WebAssembly Lua!")
|
||||||
|
```
|
31
src/setup.md
Normal file
31
src/setup.md
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# Setup
|
||||||
|
|
||||||
|
Before we can start developing, a few prerequisites must be fulfilled.
|
||||||
|
|
||||||
|
## The Rust toolchain
|
||||||
|
|
||||||
|
```shell
|
||||||
|
rustup target add wasm32-unknown-emscripten
|
||||||
|
```
|
||||||
|
|
||||||
|
## The Emscripten compiler
|
||||||
|
|
||||||
|
To build for the `wasm32-unknown-emscripten` target, you need the [emscripten](https://emscripten.org/) compiler toolchain.
|
||||||
|
|
||||||
|
General install instructions are available [here](https://emscripten.org/docs/getting_started/downloads.html) or you look if your package manager has an emscripten package (some examples provided below).
|
||||||
|
|
||||||
|
_Debian_
|
||||||
|
```shell
|
||||||
|
sudo apt install emscripten
|
||||||
|
```
|
||||||
|
|
||||||
|
_Arch Linux_
|
||||||
|
```shell
|
||||||
|
sudo pacman -S emscripten
|
||||||
|
|
||||||
|
# arch does not add the path to the emscripten executables to PATH, so it must be
|
||||||
|
# explicitly added.
|
||||||
|
# you probably want to add this to your bashrc (or any other file which permanently
|
||||||
|
# adds this to PATH) to make it permanently available
|
||||||
|
export PATH=$PATH:/usr/lib/emscripten
|
||||||
|
```
|
32
src/testing.md
Normal file
32
src/testing.md
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# Testing
|
||||||
|
|
||||||
|
Testing is not very different from testing any other ordinary Rust crate.
|
||||||
|
|
||||||
|
When running tests, Rust tries to execute the generated Javascript glue directly which will result in an error.
|
||||||
|
You have to specify the test runner which executes the Javascript, either in the `.cargo/config.toml` file (described [here]()) or via the `CARGO_TARGET_WASM32_UNKNOWN_EMSCRIPTEN_RUNNER` env variable to `node --experimental-default-type=module`.
|
||||||
|
<br>
|
||||||
|
If your crate is a library, you also have to remove the `-o<library name>.js` compiler option as it modifies the output filename which the Rust test suite can't track.
|
||||||
|
Because the `test` subcommand compiles the tests as normal binaries, the Emscripten compiler automatically creates the js glue.
|
||||||
|
|
||||||
|
> Also, in the current stable Rust, you have to set the `-sERROR_ON_UNDEFINED_SYMBOLS=0` compiler option in order to avoid test compilation errors. This is due to an incompatibility between emscripten and the internal Rust libc crate ([rust-lang/rust#116655](https://github.com/rust-lang/rust/issues/116655)) but a fix for it should land in Rust 1.75 ([rust-lang/rust#116527](https://github.com/rust-lang/rust/pull/116527)).
|
||||||
|
> Alternatively you can use the nightly toolchain, the fix is already present there.
|
||||||
|
|
||||||
|
With this done, we can create a simple test:
|
||||||
|
```rust,ignore
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn lua_test() {
|
||||||
|
let lua = mlua::Lua::new();
|
||||||
|
lua.load("print(\"test\")").exec().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
And then run it:
|
||||||
|
```shell
|
||||||
|
# you can omit '--target wasm32-unknown-emscripten' if you added the .cargo/config.toml
|
||||||
|
# file as describe in the "Setup" section
|
||||||
|
cargo test --target wasm32-unknown-emscripten
|
||||||
|
```
|
37
src/tutorial/adding-wasm-logic.md
Normal file
37
src/tutorial/adding-wasm-logic.md
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# Adding wasm logic
|
||||||
|
|
||||||
|
Adding logic on the wasm / Rust side is very much just like writing a (C compatible) shared library.
|
||||||
|
|
||||||
|
Let's begin simple.
|
||||||
|
This function creates a [Lua](https://docs.rs/mlua/latest/mlua/struct.Lua.html) instance and returns the raw pointer to it.
|
||||||
|
```rust,ignore
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn lua_new() -> *mut mlua::Lua {
|
||||||
|
let lua = mlua::Lua::new();
|
||||||
|
Box::into_raw(Box::new(lua))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Alright, good.
|
||||||
|
Now we have a Lua instance, but no way to use it, so let us create one.
|
||||||
|
<br>
|
||||||
|
The function takes the pointer to the Lua struct we create in the `new_lua` function as well as an arbitrary string, which should be lua code, as parameters.
|
||||||
|
It then executes this string via the Lua instance and may write to `stderr` if an error occurs.
|
||||||
|
```rust,ignore
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn lua_execute(lua: *mut mlua::Lua, to_execute: *const std::ffi::c_char) {
|
||||||
|
// casting the raw pointer of the created lua instance back to a usable Rust struct
|
||||||
|
let lua: &mut mlua::Lua = &mut *lua;
|
||||||
|
// converting the c string into a `CStr` (which then can be converted to a `String`)
|
||||||
|
let to_execute = std::ffi::CStr::from_ptr(to_execute);
|
||||||
|
|
||||||
|
// execute the input code via the lua interpreter
|
||||||
|
if let Err(err) = lua.load(&to_execute.to_string_lossy().to_string()).exec() {
|
||||||
|
// because emscripten wraps stderr, we are able to catch the error on the js
|
||||||
|
// side just fine
|
||||||
|
eprintln!("{}", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Okay, this looks great! In theory. So let's head over to the next page to see how to compile the code to make it actually usable via Javascript.
|
189
src/tutorial/calling-from-javascript.md
Normal file
189
src/tutorial/calling-from-javascript.md
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
# Calling from Javascript
|
||||||
|
|
||||||
|
> The following code examples are expecting that the compiled glue and wasm files are available as `target/wasm32-unknown-emscripten/debug/my-project.js` and `target/wasm32-unknown-emscripten/debug/my-project.wasm`.
|
||||||
|
|
||||||
|
## Browser
|
||||||
|
|
||||||
|
> Note that opening the `.html` file as normal file in your browser will prevent the wasm from loading.
|
||||||
|
> You have to serve it with a webserver. `python3 -m http.server` is a good tool for this.
|
||||||
|
|
||||||
|
The following html page will be used as reference in the Javascript code.
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>My Project</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<h3>Code</h3>
|
||||||
|
<textarea id="code"></textarea>
|
||||||
|
<button>Execute</button>
|
||||||
|
</div>
|
||||||
|
<div style="display: flex">
|
||||||
|
<div>
|
||||||
|
<h3>Stderr</h3>
|
||||||
|
<div id="stderr" />
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div>
|
||||||
|
<h3>Stdout</h3>
|
||||||
|
<div id="stdout" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
First things first, we need to load the compiled wasm file.
|
||||||
|
For this, we import the Javascript glue that is generated when compiling and loads and configures the actual wasm file.
|
||||||
|
A custom configuration is fully optional, but needed if you want to do things like catching stdio.
|
||||||
|
The configuration is done via the [Module](https://emscripten.org/docs/api_reference/module.html) object.
|
||||||
|
```javascript
|
||||||
|
// importing the glue
|
||||||
|
const wasm = await import('./target/wasm32-unknown-emscripten/debug/my-project.js');
|
||||||
|
// creating a custom configuration. `print` is equal to stdout, `printErr` is equal to
|
||||||
|
// stderr
|
||||||
|
const module = {
|
||||||
|
print(str) {
|
||||||
|
const stdout = document.getElementById('stdout');
|
||||||
|
const line = document.createElement('p');
|
||||||
|
line.innerText = str;
|
||||||
|
stdout.appendChild(line);
|
||||||
|
},
|
||||||
|
printErr(str) {
|
||||||
|
const stderr = document.getElementById('stderr');
|
||||||
|
const line = document.createElement('p');
|
||||||
|
line.innerText = str;
|
||||||
|
stderr.appendChild(line);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// this loads the wasm file and exposes the `ccall` and `cwrap` functions whic we'll
|
||||||
|
// use in the following code
|
||||||
|
const myProject = await wasm.default(module);
|
||||||
|
```
|
||||||
|
|
||||||
|
With the library loaded, it's time to call our first function, `lua_new`.
|
||||||
|
This is done via the emscripten [ccall](https://emscripten.org/docs/api_reference/preamble.js.html#ccall) function.
|
||||||
|
It takes the function name we want to execute, its return type, the function parameter types and the parameters as arguments.
|
||||||
|
<br>
|
||||||
|
This will return the raw pointer (as js number) to the address where the [Lua](https://docs.rs/mlua/latest/mlua/struct.Lua.html) struct, we created in the Rust code, resides.
|
||||||
|
```javascript
|
||||||
|
const luaInstance = myProject.ccall('lua_new', 'number', [], []);
|
||||||
|
```
|
||||||
|
|
||||||
|
Next up, lets make the `lua_execute` function callable.
|
||||||
|
This time we're using the emscripten [cwrap](https://emscripten.org/docs/api_reference/preamble.js.html#cwrap) function.
|
||||||
|
It wraps a normal Javascript function around the ffi call to the wasm `lua_execute` function, which is the recommended way to handle functions which are invoked multiple times.
|
||||||
|
It takes the function name we want to execute, its return type and the function parameters as arguments.
|
||||||
|
```javascript
|
||||||
|
const luaExecute = myProject.cwrap('lua_execute', null, ['number', 'string']);
|
||||||
|
```
|
||||||
|
|
||||||
|
With this all set up, we are able to call any Lua code via WebAssembly, right in the browser. Great!
|
||||||
|
```javascript
|
||||||
|
luaExecute(luaInstance, 'print("Hello Lua Wasm")');
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Full example as html page with Javascript</summary>
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>My Project</title>
|
||||||
|
<script type="module">
|
||||||
|
const wasm = await import('./target/wasm32-unknown-emscripten/debug/my-project.js');
|
||||||
|
const stdout = document.getElementById('stdout');
|
||||||
|
const stderr = document.getElementById('stderr');
|
||||||
|
const module = {
|
||||||
|
print(str) {
|
||||||
|
const line = document.createElement('p');
|
||||||
|
line.innerText = str;
|
||||||
|
stdout.appendChild(line);
|
||||||
|
},
|
||||||
|
printErr(str) {
|
||||||
|
const line = document.createElement('p');
|
||||||
|
line.innerText = str;
|
||||||
|
stderr.appendChild(line);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const myProject = await wasm.default(module);
|
||||||
|
|
||||||
|
const luaInstance = myProject.ccall('lua_new', 'number', [], []);
|
||||||
|
const luaExecute = myProject.cwrap('lua_execute', null, ['number', 'string']);
|
||||||
|
|
||||||
|
window.execute = () => {
|
||||||
|
// clear the output
|
||||||
|
stdout.innerHTML = '';
|
||||||
|
stderr.innerHTML = '';
|
||||||
|
const code = document.getElementById('code').value;
|
||||||
|
luaExecute(luaInstance, code);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<textarea id="code"></textarea>
|
||||||
|
<button onclick="execute()">Execute</button>
|
||||||
|
</div>
|
||||||
|
<div style="display: flex">
|
||||||
|
<div>
|
||||||
|
<h3>Stderr</h3>
|
||||||
|
<div id="stderr" />
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div>
|
||||||
|
<h3>Stdout</h3>
|
||||||
|
<div id="stdout" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## NodeJS
|
||||||
|
|
||||||
|
> The nodejs implementation is not very different from the browser implementation, so the actions done aren't as detailed described as above.
|
||||||
|
Please read the [Browser](#browser) section first if you want more detailed information.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
class MyProject {
|
||||||
|
#instance;
|
||||||
|
#luaExecute;
|
||||||
|
#stdout;
|
||||||
|
#stderr;
|
||||||
|
|
||||||
|
static async init(): Promise<MyProject> {
|
||||||
|
const myProject = new MyProject();
|
||||||
|
|
||||||
|
const wasm = await import('./target/wasm32-unknown-emscripten/debug/my-project.js');
|
||||||
|
const module = {
|
||||||
|
print(str) {
|
||||||
|
if (myProject.#stdout) myProject.#stdout(str);
|
||||||
|
},
|
||||||
|
printErr(str) {
|
||||||
|
if (myProject.#stderr) myProject.#stderr(str);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const lib = await wasm.default(module);
|
||||||
|
|
||||||
|
myProject.#instance = lib.ccall('lua_new', 'number', [], []);
|
||||||
|
myProject.#luaExecute = lib.cwrap('lua_execute', null, ['number', 'string']);
|
||||||
|
|
||||||
|
return myProject;
|
||||||
|
}
|
||||||
|
|
||||||
|
execute(code, stdout, stderr) {
|
||||||
|
if (stdout) this.#stdout = stdout;
|
||||||
|
if (stderr) this.#stderr = stderr;
|
||||||
|
|
||||||
|
this.#luaExecute(this.#instance, code);
|
||||||
|
|
||||||
|
if (stdout) this.#stdout = null;
|
||||||
|
if (stderr) this.#stderr = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
8
src/tutorial/compiling.md
Normal file
8
src/tutorial/compiling.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Compiling
|
||||||
|
|
||||||
|
Before we can use our Rust code, we have to compile it first.
|
||||||
|
```shell
|
||||||
|
# you can omit '--target wasm32-unknown-emscripten' if you added the .cargo/config.toml
|
||||||
|
# file as describe in the "Setup" section
|
||||||
|
cargo build --target wasm32-unknown-emscripten
|
||||||
|
```
|
98
src/tutorial/creating-a-project.md
Normal file
98
src/tutorial/creating-a-project.md
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
# Creating a project
|
||||||
|
|
||||||
|
## Create the project package
|
||||||
|
|
||||||
|
First, you need to create a normal Rust package.
|
||||||
|
This can either be a binary or library crate, they are working nearly the same.
|
||||||
|
|
||||||
|
As binary:
|
||||||
|
```shell
|
||||||
|
cargo init --bin my-package .
|
||||||
|
```
|
||||||
|
|
||||||
|
As library
|
||||||
|
```shell
|
||||||
|
cargo init --lib my-package .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configure files
|
||||||
|
|
||||||
|
Before you can start writing actual code you have to set up some files in the newly created library directory.
|
||||||
|
|
||||||
|
### `Cargo.toml`
|
||||||
|
|
||||||
|
The `mlua` dependency is the actual lua library which we'll use.
|
||||||
|
The features `lua51`, `lua53`, `lua54` and `luau` are wasm compatible lua version (`lua53` is currently broken because I accidentally removed a function in the PR which added wasm support, oops).
|
||||||
|
```toml
|
||||||
|
[package]
|
||||||
|
name = "my-project"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
mlua = { git = "https://github.com/khvzak/mlua.git", features = ["lua51"] }
|
||||||
|
```
|
||||||
|
|
||||||
|
> If your crate is a library, you have to additionally add this:
|
||||||
|
> ```toml
|
||||||
|
> [lib]
|
||||||
|
> crate-type = ["cdylib"]
|
||||||
|
> ```
|
||||||
|
> This must be done because the emscripten compiler expects the package to behave like a normal C shared library.
|
||||||
|
|
||||||
|
|
||||||
|
### `build.rs`
|
||||||
|
|
||||||
|
You need to set some additional compiler options to be able to call your wasm code from Javascript:
|
||||||
|
- `-sEXPORTED_RUNTIME_METHODS=['cwrap','ccall']`: this exports the `cwrap` and `ccall` Javascript functions which allows us to call our library methods
|
||||||
|
- `-sEXPORT_ES6=1`: this makes the created js glue ES6 compatible. It is not mandatory in general but needed as this tutorial/examples utilizes ES6 imports
|
||||||
|
- `-sERROR_ON_UNDEFINED_SYMBOLS=0` (_optional for binary crates_): this ignores undefined symbols. Typically undefined symbols are not really undefined but the linker just can't find them, which is always the case if your crate is a library
|
||||||
|
|
||||||
|
> If your package is a library, you have to add some additional options:
|
||||||
|
> - `--no-entry`: this defines that the compiled wasm has no main function
|
||||||
|
> - `-o<library name>.js`: by default, only a `.wasm` file is created, but some js glue is needed to call the built wasm file (and the wasm file needs some functions of the glue too). This creates the glue `<library name>.js` file and changes the name of the wasm output file to `<library name>.wasm`. This must be removed when running tests because it changes the output filename and the Rust test suite can't track this
|
||||||
|
|
||||||
|
The best way to do this is by specifying the args in a `build.rs` file which guarantees that they are set when compiling:
|
||||||
|
```rust,ignore
|
||||||
|
fn main() {
|
||||||
|
println!("cargo:rustc-link-arg=-sEXPORTED_RUNTIME_METHODS=['cwrap','ccall']");
|
||||||
|
println!("cargo:rustc-link-arg=-sEXPORT_ES6=1");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> If your package is a library, add the additionally required options to your `build.rs`:
|
||||||
|
> ```rust,ignore
|
||||||
|
> let out_dir = std::env::var("OUT_DIR").unwrap();
|
||||||
|
> let pkg_name = std::env::var("CARGO_PKG_NAME").unwrap();
|
||||||
|
>
|
||||||
|
> // the output files should be placed in the "root" build directory (e.g.
|
||||||
|
> // target/wasm32-unknown-emscripten/debug) but there is no env variable which
|
||||||
|
> // provides this path, so it must be extracted this way
|
||||||
|
>
|
||||||
|
> let target_path = std::path::PathBuf::from(out_dir)
|
||||||
|
> .parent()
|
||||||
|
> .unwrap()
|
||||||
|
> .parent()
|
||||||
|
> .unwrap()
|
||||||
|
> .parent()
|
||||||
|
> .unwrap()
|
||||||
|
> .join(pkg_name);
|
||||||
|
>
|
||||||
|
> println!("cargo:rustc-link-arg=-sERROR_ON_UNDEFINED_SYMBOLS=0");
|
||||||
|
> println!("cargo:rustc-link-arg=--no-entry");
|
||||||
|
> println!("cargo:rustc-link-arg=-o{}.js", target_path.to_string_lossy());
|
||||||
|
> ```
|
||||||
|
|
||||||
|
### `.cargo/config.toml` (optional)
|
||||||
|
|
||||||
|
Here you can set the default target to `wasm32-unknown-emscripten`, so you don't have to specify the `--target wasm32-unknown-emscripten` flag everytime you want to compile your project.
|
||||||
|
<br>
|
||||||
|
You can also set the default runner binary here which is useful when running tests, as Rust tries to execute the generated js glue directly which obviously doesn't work because a Javascript file is not an executable.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[build]
|
||||||
|
target = "wasm32-unknown-emscripten"
|
||||||
|
|
||||||
|
[target.wasm32-unknown-emscripten]
|
||||||
|
runner = "node --experimental-default-type=module"
|
||||||
|
```
|
9
src/tutorial/introduction.md
Normal file
9
src/tutorial/introduction.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Tutorial
|
||||||
|
|
||||||
|
This tutorial covers how to set up a simple project, adding logic to it and calling it from a Javascript (browser) application.
|
||||||
|
|
||||||
|
We will build a simple wasm binary which is able to execute arbitrary Lua input (a repl, basically).
|
||||||
|
|
||||||
|
## What will be covered?
|
||||||
|
- How to set up a project (it's a bit more than just `cargo init`)
|
||||||
|
- Calling the created wasm file from the browser
|
Loading…
x
Reference in New Issue
Block a user