-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwasmbuild.go
More file actions
146 lines (125 loc) · 3.89 KB
/
Copy pathwasmbuild.go
File metadata and controls
146 lines (125 loc) · 3.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package client
import (
"os"
"path/filepath"
"sync"
. "github.com/tinywasm/fmt"
"github.com/tinywasm/js"
"github.com/tinywasm/tinygo"
)
func runtimeFromMode(mode string) js.Runtime {
if mode == "L" {
return js.RuntimeGo
}
return js.RuntimeTinyGo
}
// RunWasmBuildClient captures the subset of WasmClient methods used by RunWasmBuild.
// It is exported so tests can provide lightweight fakes without pulling in gobuild.
type RunWasmBuildClient interface {
SetMode(string)
UseDiskStorage()
SetLog(func(...any))
Compile() error
LogSuccessState(...any)
}
type runWasmBuildDeps struct {
ensureTinyGoInstalled func() (string, error)
tinyGoEnv func() []string
newClient func(*Config) RunWasmBuildClient
}
var (
wasmBuildDeps = runWasmBuildDeps{
ensureTinyGoInstalled: func() (string, error) {
return tinygo.EnsureInstalled()
},
tinyGoEnv: func() []string {
return tinygo.GetEnv()
},
newClient: func(cfg *Config) RunWasmBuildClient {
return New(cfg)
},
}
wasmBuildDepsMu sync.Mutex
)
// RunWasmBuildHooks lets tests override RunWasmBuild dependencies (installer, env provider, client factory).
// Use SetRunWasmBuildHooks in tests to temporarily replace these functions.
type RunWasmBuildHooks struct {
EnsureTinyGoInstalled func() (string, error)
TinyGoEnv func() []string
NewClient func(*Config) RunWasmBuildClient
}
// SetRunWasmBuildHooks updates RunWasmBuild dependencies for the duration of a test.
// It returns a restore function that should be deferred.
func SetRunWasmBuildHooks(h RunWasmBuildHooks) (restore func()) {
wasmBuildDepsMu.Lock()
prev := wasmBuildDeps
if h.EnsureTinyGoInstalled != nil {
wasmBuildDeps.ensureTinyGoInstalled = h.EnsureTinyGoInstalled
}
if h.TinyGoEnv != nil {
wasmBuildDeps.tinyGoEnv = h.TinyGoEnv
}
if h.NewClient != nil {
wasmBuildDeps.newClient = h.NewClient
}
wasmBuildDepsMu.Unlock()
return func() {
wasmBuildDepsMu.Lock()
wasmBuildDeps = prev
wasmBuildDepsMu.Unlock()
}
}
// WasmBuildArgs defines the arguments for the RunWasmBuild function.
type WasmBuildArgs struct {
Stdlib bool // true = Go standard compiler mode "L", false = TinyGo mode "S"
}
// RunWasmBuild performs the common logic for the wasmbuild CLI.
func RunWasmBuild(args WasmBuildArgs) error {
// 1. If not stdlib: call EnsureTinyGoInstalled() and get env from tinygo package
if !args.Stdlib {
_, err := wasmBuildDeps.ensureTinyGoInstalled()
if err != nil {
return Errf("error ensuring TinyGo installation: %w", err)
}
}
// 2. Verify input: check that web/client.go exists
inputPath := filepath.Join("web", "client.go")
if _, err := os.Stat(inputPath); os.IsNotExist(err) {
return Errf("input file not found: %s", inputPath)
}
// 3. Create output dir: web/public
outputDir := filepath.Join("web", "public")
if err := os.MkdirAll(outputDir, 0755); err != nil {
return Errf("failed to create output directory: %w", err)
}
// 4. Generate script.js
mode := "S"
if args.Stdlib {
mode = "L"
}
js.SetRuntime(runtimeFromMode(mode))
jsContent := js.PageBootstrap().Content
scriptPath := filepath.Join(outputDir, "script.js")
if err := os.WriteFile(scriptPath, []byte(jsContent), 0644); err != nil {
return Errf("failed to write script.js: %w", err)
}
// 5. Compile WASM
// Get environment with TINYGOROOT and updated PATH (safe for subprocess injection)
cfg := NewConfig()
if !args.Stdlib {
cfg.Env = wasmBuildDeps.tinyGoEnv()
}
// NewConfig() defaults should be SourceDir="web" and OutputDir="web/public",
// but we explicitly set them based on the required layout for safety.
cfg.SourceDir = func() string { return "web" }
cfg.OutputDir = func() string { return outputDir }
w := wasmBuildDeps.newClient(cfg)
w.SetMode(mode)
w.UseDiskStorage()
w.SetLog(Println)
if err := w.Compile(); err != nil {
return Errf("WASM compilation failed: %w", err)
}
w.LogSuccessState("compiled")
return nil
}