mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-07 18:35:34 +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:
+110
-33
@@ -4,10 +4,13 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"golang.org/x/tools/go/packages"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func buildJS(bi *buildInfo) error {
|
func buildJS(bi *buildInfo) error {
|
||||||
@@ -35,34 +38,93 @@ func buildJS(bi *buildInfo) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, user-scalable=no">
|
<meta name="viewport" content="width=device-width, user-scalable=no">
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
<script src="wasm_exec.js"></script>
|
<script src="wasm.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>
|
|
||||||
<style>
|
<style>
|
||||||
body,pre { margin:0;padding:0; }
|
body,pre { margin:0;padding:0; }
|
||||||
</style>
|
</style>
|
||||||
@@ -70,16 +132,31 @@ func buildJS(bi *buildInfo) error {
|
|||||||
<body>
|
<body>
|
||||||
</body>
|
</body>
|
||||||
</html>`
|
</html>`
|
||||||
if err := ioutil.WriteFile(filepath.Join(out, "index.html"), []byte(indexhtml), 0600); err != nil {
|
// jsSetGo sets the `window.go` variable.
|
||||||
return err
|
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] = {};
|
||||||
}
|
}
|
||||||
goroot, err := runCmd(exec.Command("go", "env", "GOROOT"))
|
Object.assign(defaultGo["importObject"][key], go["importObject"][key]);
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
wasmjs := filepath.Join(goroot, "misc", "wasm", "wasm_exec.js")
|
window.go = defaultGo;
|
||||||
if _, err := os.Stat(wasmjs); err != nil {
|
if (!WebAssembly.instantiateStreaming) { // polyfill
|
||||||
return fmt.Errorf("failed to find $GOROOT/misc/wasm/wasm_exec.js driver: %v", err)
|
WebAssembly.instantiateStreaming = async (resp, importObject) => {
|
||||||
}
|
const source = await (await resp).arrayBuffer();
|
||||||
return copyFile(filepath.Join(out, "wasm_exec.js"), wasmjs)
|
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