Files
metro-puzzle-jp/tools/build/main.go
claude ae50d444a4 Byt namn till metro-puzzle-jp (Gitea-repots namn)
Mapp, Go-modul, binär, VS Code-sökvägar, dokumentation och visningsnamn
följer nu repots namn på Gitea. Arbetsnamnet jp-subway-pussel finns kvar
som historik i spelbeskrivningen §15.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-23 19:02:41 +02:00

206 lines
5.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Byggverktyg: `go run ./tools/build [flaggor]`
//
// Skrivet i Go så att samma kommando fungerar på Linux och Windows utan
// make, bash eller andra extra verktyg. Bygger binären med rätt flaggor för
// debug/release, stämplar version + läge via -ldflags och kopierar
// resources/ bredvid binären.
//
// debug -> build/debug/<binär> + resources/
// release -> build/release/<os>-<arch>/<binär> + resources/
package main
import (
"flag"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"time"
)
const (
appName = "metro-puzzle-jp"
mainPkg = "./cmd/" + appName
)
func main() {
mode := flag.String("mode", "debug", "debug | release")
goos := flag.String("os", runtime.GOOS, "målplattform: linux | windows")
goarch := flag.String("arch", runtime.GOARCH, "arkitektur: amd64 | arm64")
run := flag.Bool("run", false, "starta binären efter bygget (kräver att os/arch matchar den här datorn)")
flag.Parse()
if *mode != "debug" && *mode != "release" {
fatal("-mode måste vara debug eller release")
}
switch *goos {
case "linux", "windows":
case "darwin":
fatal("macOS kräver cgo och måste byggas på en Mac (`go build` fungerar där, men inte korsbygge härifrån)")
default:
fatal("stöd saknas för -os " + *goos)
}
if *goos == "linux" && (*goos != runtime.GOOS || *goarch != runtime.GOARCH) {
fatal("korsbygge till linux/" + *goarch + " kräver en C-korskompilator (cgo); bygg på målmaskinen i stället")
}
root := repoRoot()
module := modulePath(root)
version := gitVersion(root)
outDir := filepath.Join(root, "build", "debug")
if *mode == "release" {
outDir = filepath.Join(root, "build", "release", *goos+"-"+*goarch)
}
exe := appName
if *goos == "windows" {
exe += ".exe"
}
exePath := filepath.Join(outDir, exe)
must(os.MkdirAll(outDir, 0o755))
ldflags := []string{
"-X", module + "/internal/buildinfo.Version=" + version,
"-X", module + "/internal/buildinfo.Mode=" + *mode,
}
args := []string{"build", "-o", exePath}
if *mode == "release" {
ldflags = append(ldflags, "-s", "-w") // strippa symboler/debuginfo
if *goos == "windows" {
ldflags = append(ldflags, "-H", "windowsgui") // inget konsolfönster
}
args = append(args, "-trimpath")
} else {
args = append(args, "-gcflags=all=-N -l") // ingen optimering/inlining -> bättre debugging
}
args = append(args, "-ldflags", strings.Join(ldflags, " "), mainPkg)
cmd := exec.Command("go", args...)
cmd.Dir = root
cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
cmd.Env = append(os.Environ(), "GOOS="+*goos, "GOARCH="+*goarch)
if *goos == "windows" {
// Ebitengine på Windows är ren Go -> korsbygge från Linux utan C-verktyg.
cmd.Env = append(cmd.Env, "CGO_ENABLED=0")
}
start := time.Now()
fmt.Printf("» go %s\n", strings.Join(args, " "))
must(cmd.Run())
// Kopiera resources/ bredvid binären (ersätt gammal kopia).
dst := filepath.Join(outDir, "resources")
must(os.RemoveAll(dst))
n, err := copyTree(filepath.Join(root, "resources"), dst)
must(err)
st, _ := os.Stat(exePath)
fmt.Printf("✓ %s-bygge klart på %s\n binär: %s (%.1f MB, %s)\n resurser: %s (%d filer)\n",
*mode, time.Since(start).Round(time.Millisecond), rel(root, exePath), float64(st.Size())/1e6, version, rel(root, dst), n)
if *run {
if *goos != runtime.GOOS || *goarch != runtime.GOARCH {
fatal("kan inte köra en " + *goos + "/" + *goarch + "-binär här")
}
r := exec.Command(exePath, flag.Args()...)
r.Dir = outDir
r.Stdout, r.Stderr, r.Stdin = os.Stdout, os.Stderr, os.Stdin
if err := r.Run(); err != nil {
fatal(err.Error())
}
}
}
// repoRoot letar uppåt från arbetskatalogen efter go.mod.
func repoRoot() string {
dir, err := os.Getwd()
must(err)
for {
if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil {
return dir
}
parent := filepath.Dir(dir)
if parent == dir {
fatal("hittade ingen go.mod ovanför arbetskatalogen – kör från repot")
}
dir = parent
}
}
func modulePath(root string) string {
b, err := os.ReadFile(filepath.Join(root, "go.mod"))
must(err)
for _, line := range strings.Split(string(b), "\n") {
if strings.HasPrefix(line, "module ") {
return strings.TrimSpace(strings.TrimPrefix(line, "module "))
}
}
fatal("go.mod saknar module-rad")
return ""
}
func gitVersion(root string) string {
cmd := exec.Command("git", "describe", "--tags", "--always", "--dirty")
cmd.Dir = root
out, err := cmd.Output()
if err != nil {
return "dev"
}
return strings.TrimSpace(string(out))
}
// copyTree kopierar en mapp rekursivt. README.md-filer i resursmappen är
// dokumentation för utvecklare och hoppas över.
func copyTree(src, dst string) (int, error) {
n := 0
err := filepath.WalkDir(src, func(p string, d os.DirEntry, err error) error {
if err != nil {
return err
}
relp, _ := filepath.Rel(src, p)
target := filepath.Join(dst, relp)
if d.IsDir() {
return os.MkdirAll(target, 0o755)
}
if strings.EqualFold(d.Name(), "README.md") {
return nil
}
in, err := os.Open(p)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(target)
if err != nil {
return err
}
defer out.Close()
if _, err := io.Copy(out, in); err != nil {
return err
}
n++
return nil
})
return n, err
}
func rel(root, p string) string {
if r, err := filepath.Rel(root, p); err == nil {
return r
}
return p
}
func must(err error) {
if err != nil {
fatal(err.Error())
}
}
func fatal(msg string) {
fmt.Fprintln(os.Stderr, "build: "+msg)
os.Exit(1)
}