mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
cmd/gogio: [wasm] embed .js files
Similar to the Android, which includes all .jar files into the .apk. Now, the `gogio -target js` will include all `*_js.js` files into the resulting `wasm.js`. That change make possible to adds custom wasm-imports, which might be used in future versions of Gio. Signed-off-by: Inkeliz <inkeliz@inkeliz.com>
This commit is contained in:
+112
-35
@@ -4,10 +4,13 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"golang.org/x/tools/go/packages"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func buildJS(bi *buildInfo) error {
|
||||
@@ -35,34 +38,93 @@ func buildJS(bi *buildInfo) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
const indexhtml = `<!doctype html>
|
||||
if err := ioutil.WriteFile(filepath.Join(out, "index.html"), []byte(jsIndex), 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
goroot, err := runCmd(exec.Command("go", "env", "GOROOT"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wasmJS := filepath.Join(goroot, "misc", "wasm", "wasm_exec.js")
|
||||
if _, err := os.Stat(wasmJS); err != nil {
|
||||
return fmt.Errorf("failed to find $GOROOT/misc/wasm/wasm_exec.js driver: %v", err)
|
||||
}
|
||||
pkgs, err := packages.Load(&packages.Config{
|
||||
Mode: packages.NeedName | packages.NeedFiles | packages.NeedImports | packages.NeedDeps,
|
||||
Env: append(os.Environ(), "GOOS=js", "GOARCH=wasm"),
|
||||
}, bi.pkgPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
extraJS, err := findPackagesJS(pkgs[0], make(map[string]bool))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return mergeJSFiles(filepath.Join(out, "wasm.js"), append([]string{wasmJS}, extraJS...)...)
|
||||
}
|
||||
|
||||
func findPackagesJS(p *packages.Package, visited map[string]bool) (extraJS []string, err error) {
|
||||
if len(p.GoFiles) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
js, err := filepath.Glob(filepath.Join(filepath.Dir(p.GoFiles[0]), "*_js.js"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
extraJS = append(extraJS, js...)
|
||||
for _, imp := range p.Imports {
|
||||
if !visited[imp.ID] {
|
||||
extra, err := findPackagesJS(imp, visited)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
extraJS = append(extraJS, extra...)
|
||||
visited[imp.ID] = true
|
||||
}
|
||||
}
|
||||
return extraJS, nil
|
||||
}
|
||||
|
||||
// mergeJSFiles will merge all files into a single `wasm.js`. It will prepend the jsSetGo
|
||||
// and append the jsStartGo.
|
||||
func mergeJSFiles(dst string, files ...string) (err error) {
|
||||
w, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if cerr := w.Close(); err != nil {
|
||||
err = cerr
|
||||
}
|
||||
}()
|
||||
_, err = io.Copy(w, strings.NewReader(jsSetGo))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range files {
|
||||
r, err := os.Open(files[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(w, r)
|
||||
r.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err = io.Copy(w, strings.NewReader(jsStartGo))
|
||||
return err
|
||||
}
|
||||
|
||||
const (
|
||||
jsIndex = `<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<script src="wasm_exec.js"></script>
|
||||
<script>
|
||||
if (!WebAssembly.instantiateStreaming) { // polyfill
|
||||
WebAssembly.instantiateStreaming = async (resp, importObject) => {
|
||||
const source = await (await resp).arrayBuffer();
|
||||
return await WebAssembly.instantiate(source, importObject);
|
||||
};
|
||||
}
|
||||
|
||||
const go = new Go();
|
||||
|
||||
// Pick up argv from the argv query argument (if set).
|
||||
const params = new URLSearchParams(location.search);
|
||||
const argv = params.get("argv");
|
||||
if (argv) {
|
||||
go.argv = go.argv.concat(argv.split(" "));
|
||||
}
|
||||
|
||||
WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject).then((result) => {
|
||||
go.run(result.instance);
|
||||
});
|
||||
</script>
|
||||
<script src="wasm.js"></script>
|
||||
<style>
|
||||
body,pre { margin:0;padding:0; }
|
||||
</style>
|
||||
@@ -70,16 +132,31 @@ func buildJS(bi *buildInfo) error {
|
||||
<body>
|
||||
</body>
|
||||
</html>`
|
||||
if err := ioutil.WriteFile(filepath.Join(out, "index.html"), []byte(indexhtml), 0600); err != nil {
|
||||
return err
|
||||
// jsSetGo sets the `window.go` variable.
|
||||
jsSetGo = `(() => {
|
||||
window.go = {argv: [], env: {}, importObject: {go: {}}};
|
||||
window.go["argv"] = (new URLSearchParams(location.search).get("argv") ?? "").split(" ");
|
||||
})();`
|
||||
// jsStartGo initializes the main.wasm.
|
||||
jsStartGo = `(() => {
|
||||
defaultGo = new Go();
|
||||
Object.assign(defaultGo["argv"], go["argv"]);
|
||||
Object.assign(defaultGo["env"], go["env"]);
|
||||
for (let key in go["importObject"]) {
|
||||
if (typeof defaultGo["importObject"][key] === "undefined") {
|
||||
defaultGo["importObject"][key] = {};
|
||||
}
|
||||
Object.assign(defaultGo["importObject"][key], go["importObject"][key]);
|
||||
}
|
||||
goroot, err := runCmd(exec.Command("go", "env", "GOROOT"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wasmjs := filepath.Join(goroot, "misc", "wasm", "wasm_exec.js")
|
||||
if _, err := os.Stat(wasmjs); err != nil {
|
||||
return fmt.Errorf("failed to find $GOROOT/misc/wasm/wasm_exec.js driver: %v", err)
|
||||
}
|
||||
return copyFile(filepath.Join(out, "wasm_exec.js"), wasmjs)
|
||||
}
|
||||
window.go = defaultGo;
|
||||
if (!WebAssembly.instantiateStreaming) { // polyfill
|
||||
WebAssembly.instantiateStreaming = async (resp, importObject) => {
|
||||
const source = await (await resp).arrayBuffer();
|
||||
return await WebAssembly.instantiate(source, importObject);
|
||||
};
|
||||
}
|
||||
WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject).then((result) => {
|
||||
go.run(result.instance);
|
||||
});
|
||||
})();`
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user