Build in /tmp and immediately discard

This commit is contained in:
2026-04-14 09:49:36 +02:00
parent b766ff1348
commit bf7c844f2e
3 changed files with 158 additions and 6 deletions
+16 -2
View File
@@ -180,6 +180,7 @@ func runBuildModule(ctx context) error {
type buildHAKOptions struct {
filteredHAKs []string
filteredArchives []string
planOnly bool
}
@@ -205,7 +206,10 @@ func runBuildHAKs(ctx context) error {
if opts.planOnly {
result, err = pipeline.PlanHAKsWithProgress(p, progress)
} else {
result, err = pipeline.BuildHAKsWithProgress(p, progress)
result, err = pipeline.BuildHAKsWithOptions(p, pipeline.BuildHAKOptions{
Progress: progress,
ArchiveNames: opts.filteredArchives,
})
}
if err != nil {
return err
@@ -230,13 +234,19 @@ func parseBuildHAKArgs(args []string) (buildHAKOptions, error) {
arg := args[index]
switch arg {
case "-h", "--help":
return opts, errors.New("usage: build-haks [--hak <hak-name> ...] [--plan-only]")
return opts, errors.New("usage: build-haks [--hak <hak-name> ...] [--archive <archive-name> ...] [--plan-only]")
case "--hak":
index++
if index >= len(args) {
return opts, errors.New("--hak requires a value")
}
opts.filteredHAKs = append(opts.filteredHAKs, args[index])
case "--archive":
index++
if index >= len(args) {
return opts, errors.New("--archive requires a value")
}
opts.filteredArchives = append(opts.filteredArchives, args[index])
case "--plan-only":
opts.planOnly = true
default:
@@ -244,6 +254,10 @@ func parseBuildHAKArgs(args []string) (buildHAKOptions, error) {
opts.filteredHAKs = append(opts.filteredHAKs, value)
continue
}
if value, ok := parseInlineFlagValue(arg, "--archive"); ok {
opts.filteredArchives = append(opts.filteredArchives, value)
continue
}
return opts, fmt.Errorf("unknown build-haks argument %q", arg)
}
}
+60 -3
View File
@@ -66,6 +66,11 @@ type hakChunk struct {
type ProgressFunc func(string)
type BuildHAKOptions struct {
Progress ProgressFunc
ArchiveNames []string
}
func Build(p *project.Project) (BuildResult, error) {
var topPackage topdata.PackageResult
var err error
@@ -145,6 +150,10 @@ func BuildHAKsWithProgress(p *project.Project, progress ProgressFunc) (BuildResu
return buildHAKs(p, progress)
}
func BuildHAKsWithOptions(p *project.Project, opts BuildHAKOptions) (BuildResult, error) {
return planOrBuildHAKs(p, opts.Progress, true, opts.ArchiveNames)
}
func PlanHAKs(p *project.Project) (BuildResult, error) {
return planHAKs(p, nil)
}
@@ -154,14 +163,14 @@ func PlanHAKsWithProgress(p *project.Project, progress ProgressFunc) (BuildResul
}
func buildHAKs(p *project.Project, progress ProgressFunc) (BuildResult, error) {
return planOrBuildHAKs(p, progress, true)
return planOrBuildHAKs(p, progress, true, nil)
}
func planHAKs(p *project.Project, progress ProgressFunc) (BuildResult, error) {
return planOrBuildHAKs(p, progress, false)
return planOrBuildHAKs(p, progress, false, nil)
}
func planOrBuildHAKs(p *project.Project, progress ProgressFunc, writeArchives bool) (BuildResult, error) {
func planOrBuildHAKs(p *project.Project, progress ProgressFunc, writeArchives bool, archiveNames []string) (BuildResult, error) {
preserveExistingHAKs := envBool("SOW_BUILD_HAKS_KEEP_EXISTING")
progressf(progress, "Validating project...")
@@ -189,6 +198,10 @@ func planOrBuildHAKs(p *project.Project, progress ProgressFunc, writeArchives bo
if err != nil {
return BuildResult{}, err
}
chunks, err = filterHAKChunksByName(chunks, archiveNames)
if err != nil {
return BuildResult{}, err
}
progressf(progress, "Resolving module HAK order...")
moduleHakOrder, err := resolveModuleHAKOrder(p, chunks)
if err != nil {
@@ -274,6 +287,50 @@ func planOrBuildHAKs(p *project.Project, progress ProgressFunc, writeArchives bo
return result, nil
}
func filterHAKChunksByName(chunks []hakChunk, archiveNames []string) ([]hakChunk, error) {
if len(archiveNames) == 0 {
return chunks, nil
}
wanted := make(map[string]struct{}, len(archiveNames))
for _, name := range archiveNames {
trimmed := strings.TrimSpace(strings.ToLower(name))
if trimmed == "" {
continue
}
wanted[trimmed] = struct{}{}
}
if len(wanted) == 0 {
return chunks, nil
}
filtered := make([]hakChunk, 0, len(wanted))
found := make(map[string]struct{}, len(wanted))
for _, chunk := range chunks {
key := strings.ToLower(strings.TrimSpace(chunk.Name))
if _, ok := wanted[key]; !ok {
continue
}
filtered = append(filtered, chunk)
found[key] = struct{}{}
}
var missing []string
for _, name := range archiveNames {
key := strings.TrimSpace(strings.ToLower(name))
if key == "" {
continue
}
if _, ok := found[key]; !ok {
missing = append(missing, name)
}
}
if len(missing) > 0 {
return nil, fmt.Errorf("unknown hak archive(s): %s", strings.Join(missing, ", "))
}
return filtered, nil
}
func progressf(progress ProgressFunc, message string) {
if progress != nil {
progress(message)
+82 -1
View File
@@ -3,6 +3,7 @@ package pipeline
import (
"bytes"
"encoding/json"
"errors"
"os"
"os/exec"
"path/filepath"
@@ -935,7 +936,6 @@ func TestBuildSplitsMultipleHAKGroupsDeterministically(t *testing.T) {
}
]
}
`)
mustWriteFile(t, filepath.Join(root, "src", "module", "module.ifo.json"), `{
"file_type": "IFO ",
@@ -1039,6 +1039,87 @@ func TestBuildSplitsMultipleHAKGroupsDeterministically(t *testing.T) {
}
}
func TestBuildHAKsWithOptionsBuildsSingleArchiveByName(t *testing.T) {
root := t.TempDir()
mustMkdir(t, filepath.Join(root, "src"))
mustMkdir(t, filepath.Join(root, "src", "module"))
mustMkdir(t, filepath.Join(root, "assets", "core"))
mustMkdir(t, filepath.Join(root, "build"))
mustWriteFile(t, filepath.Join(root, "nwn-tool.json"), `{
"module": {
"name": "Test Module",
"resref": "testmod"
},
"paths": {
"source": "src",
"assets": "assets",
"build": "build"
},
"haks": [
{
"name": "core",
"priority": 1,
"max_bytes": 310,
"split": true,
"include": ["core/**"]
}
]
}
`)
mustWriteFile(t, filepath.Join(root, "src", "module", "module.ifo.json"), `{
"file_type": "IFO ",
"file_version": "V3.2",
"root": {
"struct_type": 0,
"fields": [
{
"label": "Mod_Name",
"type": "CExoString",
"value": "Test Module"
},
{
"label": "Mod_HakList",
"type": "List",
"value": []
}
]
}
}
`)
mustWriteFile(t, filepath.Join(root, "assets", "core", "a.tga"), strings.Repeat("a", 80))
mustWriteFile(t, filepath.Join(root, "assets", "core", "b.tga"), strings.Repeat("b", 80))
mustWriteFile(t, filepath.Join(root, "assets", "core", "c.tga"), strings.Repeat("c", 80))
p, err := project.Load(root)
if err != nil {
t.Fatalf("load project: %v", err)
}
if err := p.ValidateLayout(); err != nil {
t.Fatalf("validate layout: %v", err)
}
if err := p.Scan(); err != nil {
t.Fatalf("scan: %v", err)
}
result, err := BuildHAKsWithOptions(p, BuildHAKOptions{ArchiveNames: []string{"core_02"}})
if err != nil {
t.Fatalf("build filtered haks: %v", err)
}
if len(result.HAKPaths) != 1 {
t.Fatalf("expected 1 hak output, got %d", len(result.HAKPaths))
}
if got := filepath.Base(result.HAKPaths[0]); got != "core_02.hak" {
t.Fatalf("unexpected hak output %q", got)
}
if _, err := os.Stat(filepath.Join(root, "build", "core_01.hak")); !errors.Is(err, os.ErrNotExist) {
t.Fatalf("expected core_01.hak to be absent, got err=%v", err)
}
if _, err := os.Stat(filepath.Join(root, "build", "core.hak")); !errors.Is(err, os.ErrNotExist) {
t.Fatalf("expected unsplit core.hak to be absent, got err=%v", err)
}
}
func TestBuildExcludesOptionalHAKsFromModuleOrder(t *testing.T) {
root := t.TempDir()
mustMkdir(t, filepath.Join(root, "src"))