Skip to content

Commit

Permalink
Import
Browse files Browse the repository at this point in the history
  • Loading branch information
remko committed Oct 27, 2023
0 parents commit 2086520
Show file tree
Hide file tree
Showing 23 changed files with 5,384 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
public/uxn-wasm/dist
build/
13 changes: 13 additions & 0 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
env:
browser: true
es2021: true
node: true
extends:
- 'eslint:recommended'
parserOptions:
ecmaVersion: 12
sourceType: module
plugins:
- prettier
rules:
"prettier/prettier": "error"
21 changes: 21 additions & 0 deletions .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: "Setup project"
runs:
using: "composite"
steps:
- uses: actions/setup-node@v3
with:
node-version: 17
cache: 'yarn'
- run: |
curl -L -s https://github.com/WebAssembly/wabt/releases/download/1.0.31/wabt-1.0.31-ubuntu.tar.gz | tar xvz -C /tmp
sudo mv /tmp/wabt-1.0.31/bin/* /usr/local/bin
sudo mv /tmp/wabt-1.0.31/include/* /usr/local/include
sudo mv /tmp/wabt-1.0.31/lib/*.a /usr/local/lib
sudo mv /tmp/wabt-1.0.31/share/wabt /usr/local/share
shell: bash
- run: |
curl -L -s https://rabbits.srht.site/uxn/uxn-essentials-lin64.tar.gz | tar xvz
echo "$GITHUB_WORKSPACE/uxn" >> $GITHUB_PATH
shell: bash
- run: yarnpkg --pure-lockfile
shell: bash
17 changes: 17 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Build

on:
push:
pull_request:
workflow_dispatch:
workflow_call:

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- run: make
- run: make lint
- run: make test
31 changes: 31 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Publish

on:
release:
types: [created]

jobs:
build:
uses: ./.github/workflows/build.yml

publish:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- run: make
- name: "Upload artifacts"
run: |
for f in `find build -name 'uxn-wasm.*'`; do
curl --fail \
-H "Authorization: token $GITHUB_TOKEN" \
-H "Content-Type: $(file -b --mime-type $f)" \
--data-binary @$f \
"$RELEASE_ASSETS_UPLOAD_URL?name=$(basename $f)"
done
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_ASSETS_UPLOAD_URL: https://uploads.github.com/repos/${{ github.event.repository.full_name}}/releases/${{ github.event.release.id }}/assets
shell: bash

4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/node_modules
/build
/public/uxn-wasm
/*.wasm
33 changes: 33 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
WAT2WASM=wat2wasm
ESLINT=./node_modules/.bin/eslint
WAT2WASM_FLAGS=
BUILD_FLAGS=
ifeq ($(DEBUG),1)
WAT2WASM_FLAGS:=$(WAT2WASM_FLAGS) --debug-names
BUILD_FLAGS:=--development
endif

all: build

.PHONY: build
build: uxn.wasm
./build.js $(BUILD_FLAGS)

dev:
./build.js $(BUILD_FLAGS) --watch

.PHONY: test
test:
./test.js

test-dev:
./test.js --watch

%.wasm: %.wat
$(WAT2WASM) $(WAT2WASM_FLAGS) -o $@ $<

clean:
-rm -rf build public/uxn-wasm uxn.wasm

lint:
$(ESLINT) .
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Uxn implementation in WebAssembly

[![Build](https://github.com/remko/uxn.wasm/actions/workflows/build.yml/badge.svg)](https://github.com/remko/uxn.wasm/actions/workflows/build.yml)
140 changes: 140 additions & 0 deletions build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#!/usr/bin/env node
/* eslint-env node */

const esbuild = require("esbuild");
const path = require("path");
const fs = require("fs");
const { createServer } = require("http");
const { wasmTextPlugin } = require("./scripts/esbuild/wasm-text");
const { uxntalPlugin } = require("./scripts/esbuild/uxntal");

let dev = false;
let watch = false;
for (const arg of process.argv.slice(2)) {
switch (arg) {
case "--development":
dev = true;
break;
case "--watch":
watch = true;
break;
}
}

const INDEX_TEMPLATE = `<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="theme-color" content="#000000" />
<link href="/uxn-wasm/dist/$BASE.css" rel="stylesheet" />
<title>$TITLE</title>
</head>
<body>
<script type="text/javascript" src="/uxn-wasm/dist/$BASE.js"></script>
</body>
</html>
`;

async function handleBuildFinished(result) {
const indexes = [["UXN.WASM Tests", "tests", "public/uxn-wasm/tests"]];
for (const [title, base, outpath] of indexes) {
let index = INDEX_TEMPLATE.replace(/\$BASE/g, base).replace(
/\$TITLE/g,
title
);
for (const [out] of Object.entries(result.metafile.outputs)) {
const outfile = path.basename(out);
const sourcefile = outfile.replace(/-c\$[^.]+\./, ".");
// console.log("%s -> %s", sourcefile, outfile);
index = index.replace(`/${sourcefile}`, `/${outfile}`);
}
await fs.promises.mkdir(outpath, { recursive: true });
await fs.promises.writeFile(path.join(outpath, "index.html"), index);
}
}

const buildOptions = {
bundle: true,
logLevel: "info",
entryPoints: [path.join(__dirname, "src", "web", "waforth")],
minify: !dev,
format: "iife",
loader: {
".wasm": "binary",
".rom": "binary",
},
sourcemap: true,
plugins: [wasmTextPlugin({ debug: true }), uxntalPlugin()],
};

const packageBuildOptions = {
...buildOptions,
entryPoints: [path.join(__dirname, "uxn")],
outfile: path.join(__dirname, "build", "uxn-wasm.js"),
format: "iife",
globalName: "UxnWASM",
};

let webBuildOptions = {
...buildOptions,
entryPoints: [path.join(__dirname, "tests", "tests")],
entryNames: dev ? "[name]" : "[name]-c$[hash]",
assetNames: "[name]-c$[hash]",
// target: "es6",
outdir: path.join(__dirname, "public/uxn-wasm/dist"),
publicPath: "/uxn-wasm/dist",
external: ["fs", "stream", "util", "events", "path"],
metafile: true,
plugins: buildOptions.plugins.concat([
{
name: "watch",
setup(build) {
build.onEnd((result) => {
if (result.errors.length > 0) {
return;
}
handleBuildFinished(result);
});
},
},
]),
};

if (watch) {
(async () => {
// Simple static file server
createServer(async function (req, res) {
const url = req.url.replace(/\?.*/g, "");
let f = path.join(__dirname, "public", url);
try {
if ((await fs.promises.lstat(f)).isDirectory()) {
f = path.join(f, "index.html");
}
} catch (e) {
// pass
}
try {
const data = await fs.promises.readFile(f);
res.writeHead(200);
res.end(data);
} catch (err) {
res.writeHead(404);
res.end(JSON.stringify(err));
}
}).listen(8080);

console.log("listening on port 8080");

const webCtx = await esbuild.context(webBuildOptions);
webCtx.watch();

const packageCtx = await esbuild.context(packageBuildOptions);
packageCtx.watch();
})();
} else {
esbuild.build(packageBuildOptions);
esbuild.build(webBuildOptions);
}
15 changes: 15 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "uxn.wasm",
"version": "0.0.1",
"main": "index.js",
"author": "Remko Tronçon <[email protected]>",
"license": "MIT",
"devDependencies": {
"chai": "^4.3.6",
"esbuild": "^0.19.5",
"eslint": "^8.13.0",
"eslint-plugin-prettier": "^4.0.0",
"mocha": "^9.2.2",
"prettier": "^2.6.2"
}
}
1 change: 1 addition & 0 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<html><head><meta http-equiv="refresh" content="0;url=/uxn-wasm/tests" /></head></html>
40 changes: 40 additions & 0 deletions scripts/esbuild/uxntal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/* eslint-env node */

const { promisify } = require("util");
const exec = promisify(require("child_process").exec);
const path = require("path");

function uxntalPlugin() {
return {
name: "uxntal",
setup(build) {
build.onResolve({ filter: /.\.tal$/ }, async (args) => {
if (args.resolveDir === "") {
return;
}
const talPath = path.isAbsolute(args.path)
? args.path
: path.join(args.resolveDir, args.path);
return {
path: talPath,
namespace: "uxntal",
watchFiles: [talPath],
};
});
build.onLoad({ filter: /.*/, namespace: "uxntal" }, async (args) => {
// console.log("wat: compiling %s", args.path);
const r = await exec(`uxnasm ${args.path} -`, {
encoding: "buffer",
});
return {
contents: r.stdout,
loader: "binary",
};
});
},
};
}

module.exports = {
uxntalPlugin,
};
45 changes: 45 additions & 0 deletions scripts/esbuild/wasm-text.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/* eslint-env node */

const { promisify } = require("util");
const exec = promisify(require("child_process").exec);
const path = require("path");

function wasmTextPlugin({ debug } = {}) {
return {
name: "wasm-text",
setup(build) {
build.onResolve({ filter: /.\.wat$/ }, async (args) => {
if (args.resolveDir === "") {
return;
}
const watPath = path.isAbsolute(args.path)
? args.path
: path.join(args.resolveDir, args.path);
return {
path: watPath,
namespace: "wasm-text",
watchFiles: [watPath],
};
});
build.onLoad({ filter: /.*/, namespace: "wasm-text" }, async (args) => {
let flags = [];
if (debug) {
flags.push("--debug-names");
}
// console.log("wat: compiling %s", args.path);
const r = await exec(
`wat2wasm ${flags.join(" ")} --output=- ${args.path}`,
{ encoding: "buffer" }
);
return {
contents: r.stdout,
loader: "binary",
};
});
},
};
}

module.exports = {
wasmTextPlugin,
};
Loading

0 comments on commit 2086520

Please sign in to comment.