mirror of
https://github.com/praetorian-inc/wasmforge
synced 2026-06-22 22:52:31 +00:00
fix(build): bundle sysshim into build_assets for distribution mode (#4)
When wasmforge runs as a standalone binary with no source tree on disk, injectSysshimReplace and injectPuregoSysshimReplace failed with "cannot find wasmforge module root for sysshim injection" because all four findModuleRoot() fallback paths returned "". Three-part fix: - cmd/gen-build-assets: add addSysshimDir (recursive walk) and include the internal/sysshim tree under the "sysshim/" prefix in the bundle. File count rises from ~399 to 442. - internal/build/asset_cache.go: new file providing ensureAssetsExtracted(), a process-lifetime sync.Once cache that extracts build_assets.tar.gz to a temp dir on first call. - internal/build/compiler.go: add sysshimSourceBase() helper that prefers the on-disk module root (dev mode) and falls back to the extracted asset cache (distribution mode); rewire both injection functions to use it instead of calling findModuleRoot() directly.
This commit is contained in:
@@ -112,6 +112,15 @@ func generateArchive(moduleRoot, wazeroDir, outputPath string) (int, error) {
|
||||
totalFiles += n
|
||||
}
|
||||
|
||||
// Add sysshim tree (recursive — has subdirs and per-subdir go.mod files).
|
||||
// Needed at runtime when a guest depends on golang.org/x/sys and the
|
||||
// wasmforge module source isn't otherwise locatable on disk.
|
||||
n, err = addSysshimDir(tw, filepath.Join(moduleRoot, "internal", "sysshim"))
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("adding sysshim: %w", err)
|
||||
}
|
||||
totalFiles += n
|
||||
|
||||
// Add go.sum.
|
||||
gosumPath := filepath.Join(moduleRoot, "go.sum")
|
||||
if err := addFile(tw, gosumPath, "go.sum"); err != nil {
|
||||
@@ -122,6 +131,38 @@ func generateArchive(moduleRoot, wazeroDir, outputPath string) (int, error) {
|
||||
return totalFiles, nil
|
||||
}
|
||||
|
||||
// addSysshimDir adds the internal/sysshim tree under the "sysshim/" prefix.
|
||||
// Walks recursively because sysshim has nested subdirs (unix, purego,
|
||||
// windows, windows/svc, etc.) and per-subdir go.mod files that the
|
||||
// injection step needs at runtime. Includes *.go, *.s, and go.mod; excludes
|
||||
// _test.go.
|
||||
func addSysshimDir(tw *tar.Writer, sysshimDir string) (int, error) {
|
||||
count := 0
|
||||
err := filepath.Walk(sysshimDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
name := info.Name()
|
||||
if name != "go.mod" && !isSourceFile(name) {
|
||||
return nil
|
||||
}
|
||||
rel, err := filepath.Rel(sysshimDir, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
archivePath := filepath.ToSlash(filepath.Join("sysshim", rel))
|
||||
if err := addFile(tw, path, archivePath); err != nil {
|
||||
return err
|
||||
}
|
||||
count++
|
||||
return nil
|
||||
})
|
||||
return count, err
|
||||
}
|
||||
|
||||
// addWazeroDir adds the wazero fork to the archive under the "wazero/" prefix.
|
||||
// It applies the wazero-specific skip list and only includes *.go, *.s, go.mod, go.sum files.
|
||||
func addWazeroDir(tw *tar.Writer, wazeroDir string) (int, error) {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
cachedAssetDirOnce sync.Once
|
||||
cachedAssetDir string
|
||||
cachedAssetDirErr error
|
||||
)
|
||||
|
||||
// ensureAssetsExtracted returns a process-lifetime temp directory containing
|
||||
// the extracted contents of the embedded build_assets bundle. The first call
|
||||
// performs the extraction; subsequent calls return the cached path.
|
||||
//
|
||||
// The directory is not cleaned up — it lives under the OS temp directory
|
||||
// (e.g. /tmp on Linux, /var/folders/... on macOS) where the OS reclaims it
|
||||
// eventually. This avoids re-extracting on every sysshim injection and lets
|
||||
// the directory survive across multiple build pipeline stages within a
|
||||
// single wasmforge invocation.
|
||||
//
|
||||
// Callers should access well-known subtrees via their bundled prefixes:
|
||||
// "hostmod", "runtime", "names", "wazero", "sysshim", and "go.sum".
|
||||
func ensureAssetsExtracted() (string, error) {
|
||||
cachedAssetDirOnce.Do(func() {
|
||||
dir, err := os.MkdirTemp("", "wasmforge-assets-*")
|
||||
if err != nil {
|
||||
cachedAssetDirErr = fmt.Errorf("creating asset cache dir: %w", err)
|
||||
return
|
||||
}
|
||||
if err := extractBuildAssets(dir); err != nil {
|
||||
os.RemoveAll(dir)
|
||||
cachedAssetDirErr = fmt.Errorf("extracting build assets: %w", err)
|
||||
return
|
||||
}
|
||||
cachedAssetDir = dir
|
||||
})
|
||||
return cachedAssetDir, cachedAssetDirErr
|
||||
}
|
||||
Binary file not shown.
@@ -254,6 +254,19 @@ func CompileWASM(patchedGOROOT, pkg, tmpDir string, verbose, win32APIs bool, tar
|
||||
return wasmOut, nil
|
||||
}
|
||||
|
||||
// sysshimSourceBase returns a directory B such that filepath.Join(B,
|
||||
// "sysshim", <pkg>) is a valid wasmforge sysshim package source tree.
|
||||
// In development mode (wasmforge module source on disk), B is
|
||||
// <moduleRoot>/internal. In distribution mode (standalone binary), B is
|
||||
// the cached temp directory populated from the embedded build_assets
|
||||
// bundle, where the sysshim tree was packed under the "sysshim/" prefix.
|
||||
func sysshimSourceBase() (string, error) {
|
||||
if root := findModuleRoot(); root != "" {
|
||||
return filepath.Join(root, "internal"), nil
|
||||
}
|
||||
return ensureAssetsExtracted()
|
||||
}
|
||||
|
||||
// injectSysshimReplace checks whether the guest module at pkgDir depends on
|
||||
// golang.org/x/sys. If it does, it creates a temporary copy of the module
|
||||
// directory with the sysshim replacing the original x/sys code. For vendored
|
||||
@@ -274,15 +287,16 @@ func injectSysshimReplace(pkgDir, tmpDir string, verbose, win32APIs bool) (strin
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Locate the sysshim directory (relative to the wasmforge module root).
|
||||
moduleRoot := findModuleRoot()
|
||||
if moduleRoot == "" {
|
||||
// Locate the sysshim directory (relative to the wasmforge module root, or
|
||||
// from the embedded build assets when running as a standalone binary).
|
||||
base, sysErr := sysshimSourceBase()
|
||||
if sysErr != nil {
|
||||
if win32APIs {
|
||||
return "", fmt.Errorf("cannot find wasmforge module root for sysshim injection")
|
||||
return "", fmt.Errorf("locating wasmforge sysshim source: %w", sysErr)
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
sysshimDir := filepath.Join(moduleRoot, "internal", "sysshim")
|
||||
sysshimDir := filepath.Join(base, "sysshim")
|
||||
if _, err := os.Stat(sysshimDir); err != nil {
|
||||
if win32APIs {
|
||||
return "", fmt.Errorf("sysshim directory not found at %s", sysshimDir)
|
||||
@@ -567,11 +581,11 @@ func injectPuregoSysshimReplace(pkgDir, tmpDir string, verbose bool) (string, er
|
||||
return "", nil
|
||||
}
|
||||
|
||||
moduleRoot := findModuleRoot()
|
||||
if moduleRoot == "" {
|
||||
return "", fmt.Errorf("cannot find wasmforge module root for purego sysshim injection")
|
||||
base, sysErr := sysshimSourceBase()
|
||||
if sysErr != nil {
|
||||
return "", fmt.Errorf("locating wasmforge purego sysshim source: %w", sysErr)
|
||||
}
|
||||
puregoSysshimDir := filepath.Join(moduleRoot, "internal", "sysshim", "purego")
|
||||
puregoSysshimDir := filepath.Join(base, "sysshim", "purego")
|
||||
if _, err := os.Stat(puregoSysshimDir); err != nil {
|
||||
return "", fmt.Errorf("purego sysshim directory not found at %s", puregoSysshimDir)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user