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
|
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.
|
// Add go.sum.
|
||||||
gosumPath := filepath.Join(moduleRoot, "go.sum")
|
gosumPath := filepath.Join(moduleRoot, "go.sum")
|
||||||
if err := addFile(tw, gosumPath, "go.sum"); err != nil {
|
if err := addFile(tw, gosumPath, "go.sum"); err != nil {
|
||||||
@@ -122,6 +131,38 @@ func generateArchive(moduleRoot, wazeroDir, outputPath string) (int, error) {
|
|||||||
return totalFiles, nil
|
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.
|
// 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.
|
// 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) {
|
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
|
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
|
// 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
|
// 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
|
// 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
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Locate the sysshim directory (relative to the wasmforge module root).
|
// Locate the sysshim directory (relative to the wasmforge module root, or
|
||||||
moduleRoot := findModuleRoot()
|
// from the embedded build assets when running as a standalone binary).
|
||||||
if moduleRoot == "" {
|
base, sysErr := sysshimSourceBase()
|
||||||
|
if sysErr != nil {
|
||||||
if win32APIs {
|
if win32APIs {
|
||||||
return "", fmt.Errorf("cannot find wasmforge module root for sysshim injection")
|
return "", fmt.Errorf("locating wasmforge sysshim source: %w", sysErr)
|
||||||
}
|
}
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
sysshimDir := filepath.Join(moduleRoot, "internal", "sysshim")
|
sysshimDir := filepath.Join(base, "sysshim")
|
||||||
if _, err := os.Stat(sysshimDir); err != nil {
|
if _, err := os.Stat(sysshimDir); err != nil {
|
||||||
if win32APIs {
|
if win32APIs {
|
||||||
return "", fmt.Errorf("sysshim directory not found at %s", sysshimDir)
|
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
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
moduleRoot := findModuleRoot()
|
base, sysErr := sysshimSourceBase()
|
||||||
if moduleRoot == "" {
|
if sysErr != nil {
|
||||||
return "", fmt.Errorf("cannot find wasmforge module root for purego sysshim injection")
|
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 {
|
if _, err := os.Stat(puregoSysshimDir); err != nil {
|
||||||
return "", fmt.Errorf("purego sysshim directory not found at %s", puregoSysshimDir)
|
return "", fmt.Errorf("purego sysshim directory not found at %s", puregoSysshimDir)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user