Files
mesh-sdk/go/cmd/mesh-pack/main.go
T

112 lines
3.6 KiB
Go

// mesh-pack builds a Go bundle by hand with the recipe of the mesh-build-go module and packs it with mesh-sdk's own
// buildstep.Pack, printing the digest, so a hand build has the form of a built artifact and, with the same Go version,
// its digest (novox/hq ADR 0526; fallback record F1; issue 1499 for the Go version).
//
// mesh-pack -dir <module dir> -from <package> -binary <name> [-system arch] [-out <archive file>]
// mesh-pack -pack <directory> [-out <archive file>] (a `files` archive: pack a tree as it is)
//
// The recipe is mesh-catalog modules/mesh-build-go cmd/mesh-build-go/build.go (recipeFlags, recipeLinkerFlags,
// recipeSystemStamp, buildEnv): CGO_ENABLED=0, GOFLAGS "-trimpath -buildvcs=false -mod=mod" (-mod=vendor when the
// directory vendors), GOTOOLCHAIN=local, GOPROXY=off, and -ldflags "-s -w -X main.builtFor=<system>".
package main
import (
"flag"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"git.novox.be/novox/mesh-sdk/go/buildstep"
)
const (
recipeFlags = "-trimpath -buildvcs=false"
recipeLinkerFlags = "-s -w"
recipeSystemStamp = "main.builtFor"
)
// Env is the build module's environment for the compile of dir, added to the caller's own.
func Env(dir string) []string {
mod := "-mod=mod"
if _, err := os.Stat(filepath.Join(dir, "vendor", "modules.txt")); err == nil {
mod = "-mod=vendor"
}
return append(os.Environ(),
"CGO_ENABLED=0",
"GOFLAGS="+recipeFlags+" "+mod,
"GOTOOLCHAIN=local",
"GOPROXY=off",
)
}
// Args is the arguments of the go command of the recipe.
func Args(dir, from, out, binary, system string) []string {
linker := recipeLinkerFlags
if s := strings.TrimSpace(system); s != "" {
linker += " -X " + recipeSystemStamp + "=" + s
}
return []string{"-C", dir, "build", "-ldflags", linker, "-o", filepath.Join(out, binary), "./" + strings.Trim(from, "./")}
}
// Build compiles the package into a fresh directory and answers it.
func Build(dir, from, binary, system string) (string, error) {
out, err := os.MkdirTemp("", "mesh-pack-")
if err != nil {
return "", err
}
cmd := exec.Command("go", Args(dir, from, out, binary, system)...)
cmd.Env = Env(dir)
cmd.Stdout, cmd.Stderr = os.Stderr, os.Stderr
if err := cmd.Run(); err != nil {
os.RemoveAll(out)
return "", fmt.Errorf("go build of %s: %w", from, err)
}
return out, nil
}
func run(args []string) (string, error) {
fs := flag.NewFlagSet("mesh-pack", flag.ContinueOnError)
dir := fs.String("dir", ".", "the Go module's directory")
from := fs.String("from", "", "the package to build, relative to -dir (the artifact's from)")
binary := fs.String("binary", "", "the binary's name in the archive")
system := fs.String("system", "", "the system stamped into main.builtFor (an artifact's system, e.g. arch)")
pack := fs.String("pack", "", "pack this directory as it is (a files archive) instead of building")
outFile := fs.String("out", "", "write the archive here")
if err := fs.Parse(args); err != nil {
return "", err
}
root := *pack
if root == "" {
if *from == "" || *binary == "" {
return "", fmt.Errorf("-from and -binary are needed (or -pack <directory>)")
}
out, err := Build(*dir, *from, *binary, *system)
if err != nil {
return "", err
}
defer os.RemoveAll(out)
root = out
}
archive, digest, err := buildstep.Pack(root)
if err != nil {
return "", err
}
if *outFile != "" {
if err := os.WriteFile(*outFile, archive, 0o644); err != nil {
return "", err
}
}
return digest, nil
}
func main() {
digest, err := run(os.Args[1:])
if err != nil {
fmt.Fprintln(os.Stderr, "mesh-pack:", err)
os.Exit(1)
}
fmt.Println(digest)
}