Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions client/wasmbuild.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package client

import (
"fmt"
"os"
"os/exec"

"github.com/tinywasm/tinygo"
)

type Config struct {
Env []string
}

type WasmClient struct {
config Config
}

func NewWasmClient(cfg Config) *WasmClient {
return &WasmClient{config: cfg}
}

func (w *WasmClient) Compile(src string) error {
tinyGoPath, err := tinygo.EnsureInstalled()
if err != nil {
return fmt.Errorf("failed to ensure tinygo: %w", err)
}

// Replaces old broken pattern:
// newPath := filepath.Dir(tinyGoPath) + string(os.PathListSeparator) + os.Getenv("PATH")
// os.Setenv("PATH", newPath)

w.config.Env = tinygo.GetEnv()

cmd := exec.Command(tinyGoPath, "build", "-o", "out.wasm", src)
if len(w.config.Env) > 0 {
cmd.Env = w.config.Env
} else {
cmd.Env = os.Environ()
}

// In a real implementation we would run the command here
// _, err = cmd.CombinedOutput()
// return err

return nil
}
48 changes: 48 additions & 0 deletions env.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package tinygo

import (
"os"
"path/filepath"
"strings"
)

// GetRoot returns the TINYGOROOT for a local install, or "" if tinygo is in PATH.
func GetRoot(opts ...Option) string {
c := newConfig(opts...)
if _, err := c.lookPath("tinygo"); err == nil {
return ""
}
return filepath.Join(c.installDir, "tinygo")
}

// GetEnv returns os.Environ() + TINYGOROOT + prepended PATH for local installs.
// Safe to assign directly to exec.Command.Env.
func GetEnv(opts ...Option) []string {
root := GetRoot(opts...)
if root == "" {
return os.Environ()
}

env := os.Environ()
newEnv := make([]string, 0, len(env)+1)
rootSet := false

for _, e := range env {
if strings.HasPrefix(e, "TINYGOROOT=") {
newEnv = append(newEnv, "TINYGOROOT="+root)
rootSet = true
} else if strings.HasPrefix(e, "PATH=") {
path := e[len("PATH="):]
newPath := filepath.Join(root, "bin") + string(os.PathListSeparator) + path
newEnv = append(newEnv, "PATH="+newPath)
} else {
newEnv = append(newEnv, e)
}
}

if !rootSet {
newEnv = append(newEnv, "TINYGOROOT="+root)
}

return newEnv
}
116 changes: 116 additions & 0 deletions env_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package tinygo

import (
"fmt"
"os"
"path/filepath"
"strings"
"testing"
)

func TestGetRoot(t *testing.T) {
t.Run("SystemInstall", func(t *testing.T) {
root := GetRoot(withLookPath(func(string) (string, error) {
return "/usr/bin/tinygo", nil
}))
if root != "" {
t.Errorf("expected empty root for system install, got %q", root)
}
})

t.Run("LocalInstall", func(t *testing.T) {
tmpDir := t.TempDir()
root := GetRoot(
WithInstallDir(tmpDir),
withLookPath(func(string) (string, error) {
return "", fmt.Errorf("not found")
}),
)
expected := filepath.Join(tmpDir, "tinygo")
if root != expected {
t.Errorf("expected root %q, got %q", expected, root)
}
})
}

func TestGetEnv(t *testing.T) {
t.Run("SystemInstall", func(t *testing.T) {
env := GetEnv(withLookPath(func(string) (string, error) {
return "/usr/bin/tinygo", nil
}))
// Should be equal to os.Environ()
expected := os.Environ()
if len(env) != len(expected) {
t.Errorf("expected env length %d, got %d", len(expected), len(env))
}
})

t.Run("LocalInstall", func(t *testing.T) {
tmpDir := t.TempDir()
root := filepath.Join(tmpDir, "tinygo")
env := GetEnv(
WithInstallDir(tmpDir),
withLookPath(func(string) (string, error) {
return "", fmt.Errorf("not found")
}),
)

var tinygoRoot, path string
for _, e := range env {
if strings.HasPrefix(e, "TINYGOROOT=") {
tinygoRoot = e[len("TINYGOROOT="):]
} else if strings.HasPrefix(e, "PATH=") {
path = e[len("PATH="):]
}
}

if tinygoRoot != root {
t.Errorf("expected TINYGOROOT %q, got %q", root, tinygoRoot)
}

expectedPathPrefix := filepath.Join(root, "bin") + string(os.PathListSeparator)
if !strings.HasPrefix(path, expectedPathPrefix) {
t.Errorf("expected PATH to start with %q, got %q", expectedPathPrefix, path)
}

// Verify os.Environ() was not mutated
found := false
for _, e := range os.Environ() {
if strings.HasPrefix(e, "TINYGOROOT=") && e[len("TINYGOROOT="):] == root {
found = true
break
}
}
if found {
t.Error("os.Environ() was mutated")
}
})

t.Run("OverridesExistingTINYGOROOT", func(t *testing.T) {
t.Setenv("TINYGOROOT", "/stale/path")
tmpDir := t.TempDir()
root := filepath.Join(tmpDir, "tinygo")
env := GetEnv(
WithInstallDir(tmpDir),
withLookPath(func(string) (string, error) {
return "", fmt.Errorf("not found")
}),
)

count := 0
var tinygoRoot string
for _, e := range env {
if strings.HasPrefix(e, "TINYGOROOT=") {
tinygoRoot = e[len("TINYGOROOT="):]
count++
}
}

if count != 1 {
t.Errorf("expected 1 TINYGOROOT entry, got %d", count)
}
if tinygoRoot != root {
t.Errorf("expected TINYGOROOT %q, got %q", root, tinygoRoot)
}
})
}