Change Output Locations

This commit is contained in:
2026-04-13 15:05:43 +02:00
parent a4fa2156ca
commit 129602a482
6 changed files with 107 additions and 25 deletions
+1 -1
View File
@@ -70,7 +70,7 @@ var commands = []command{
}, },
{ {
name: "build-topdata", name: "build-topdata",
description: "Build canonical topdata outputs from topdata/data into build/topdata.", description: "Build canonical topdata outputs into topdata/assets/2da and build/sow_tlk.tlk, then package sow_top.hak.",
run: runBuildTopData, run: runBuildTopData,
}, },
{ {
+4
View File
@@ -330,6 +330,7 @@ func manifestHAKPaths(buildDir string) ([]string, error) {
func topPackageSourcePaths(p *project.Project) ([]string, error) { func topPackageSourcePaths(p *project.Project) ([]string, error) {
sourceDir := p.TopDataSourceDir() sourceDir := p.TopDataSourceDir()
generated2DADir := filepath.Join(sourceDir, "assets", "2da")
paths := make([]string, 0) paths := make([]string, 0)
for _, candidate := range []string{ for _, candidate := range []string{
filepath.Join(sourceDir, ".tlk_state.json"), filepath.Join(sourceDir, ".tlk_state.json"),
@@ -359,6 +360,9 @@ func topPackageSourcePaths(p *project.Project) ([]string, error) {
if walkErr != nil { if walkErr != nil {
return walkErr return walkErr
} }
if d.IsDir() && path == generated2DADir {
return filepath.SkipDir
}
if d.IsDir() { if d.IsDir() {
return nil return nil
} }
+5 -6
View File
@@ -192,12 +192,11 @@ func buildNativeUnchecked(p *project.Project, progress func(string), prebuiltWik
return BuildResult{}, err return BuildResult{}, err
} }
buildDir := p.TopDataBuildDir() output2DA := compiled2DAOutputDir(p)
output2DA := filepath.Join(buildDir, "2da") outputTLK := compiledTLKOutputDir(p)
outputTLK := filepath.Join(buildDir, "tlk") progress("Preparing native topdata build outputs...")
progress("Preparing native topdata build directories...") if err := os.RemoveAll(output2DA); err != nil {
if err := os.RemoveAll(buildDir); err != nil { return BuildResult{}, fmt.Errorf("clean topdata 2da output dir: %w", err)
return BuildResult{}, fmt.Errorf("clean topdata build dir: %w", err)
} }
if err := os.MkdirAll(output2DA, 0o755); err != nil { if err := os.MkdirAll(output2DA, 0o755); err != nil {
return BuildResult{}, fmt.Errorf("create 2da output dir: %w", err) return BuildResult{}, fmt.Errorf("create 2da output dir: %w", err)
+56 -12
View File
@@ -21,6 +21,29 @@ const (
PackageTLKFileName = defaultTLKName PackageTLKFileName = defaultTLKName
) )
func compiled2DAOutputDir(p *project.Project) string {
if useDedicatedTopDataBuildDir(p) {
return filepath.Join(p.TopDataBuildDir(), "2da")
}
return filepath.Join(p.TopDataSourceDir(), "assets", "2da")
}
func compiledTLKOutputPath(p *project.Project) string {
if useDedicatedTopDataBuildDir(p) {
return filepath.Join(p.TopDataBuildDir(), "tlk", defaultTLKName)
}
return filepath.Join(p.BuildDir(), defaultTLKName)
}
func compiledTLKOutputDir(p *project.Project) string {
return filepath.Dir(compiledTLKOutputPath(p))
}
func useDedicatedTopDataBuildDir(p *project.Project) bool {
defaultBuildDir := filepath.Join(p.BuildDir(), "topdata")
return p.TopDataBuildDir() != defaultBuildDir
}
func BuildPackage(p *project.Project, progress func(string)) (PackageResult, error) { func BuildPackage(p *project.Project, progress func(string)) (PackageResult, error) {
if progress == nil { if progress == nil {
progress = func(string) {} progress = func(string) {}
@@ -68,9 +91,12 @@ func packageBuiltTopData(p *project.Project, nativeResult BuildResult, progress
} }
progress("Preparing compiled TLK release output...") progress("Preparing compiled TLK release output...")
sourceTLK, err := resolveCompiledTLKPath(nativeResult.OutputTLKDir) sourceTLK := compiledTLKOutputPath(p)
if err != nil { if nativeResult.OutputTLKDir != "" {
return PackageResult{}, err sourceTLK, err = resolveCompiledTLKPath(nativeResult.OutputTLKDir)
if err != nil {
return PackageResult{}, err
}
} }
if err := copyFile(outputTLK, sourceTLK); err != nil { if err := copyFile(outputTLK, sourceTLK); err != nil {
return PackageResult{}, err return PackageResult{}, err
@@ -113,12 +139,16 @@ func collectTopPackageResources(p *project.Project, compiled2DADir string) ([]er
assetFiles := 0 assetFiles := 0
assetsDir := filepath.Join(p.TopDataSourceDir(), "assets") assetsDir := filepath.Join(p.TopDataSourceDir(), "assets")
skipDir := compiled2DAOutputDir(p)
info, err := os.Stat(assetsDir) info, err := os.Stat(assetsDir)
if err == nil && info.IsDir() { if err == nil && info.IsDir() {
err = filepath.WalkDir(assetsDir, func(path string, d os.DirEntry, walkErr error) error { err = filepath.WalkDir(assetsDir, func(path string, d os.DirEntry, walkErr error) error {
if walkErr != nil { if walkErr != nil {
return walkErr return walkErr
} }
if d.IsDir() && path == skipDir {
return filepath.SkipDir
}
if d.IsDir() { if d.IsDir() {
return nil return nil
} }
@@ -214,6 +244,9 @@ func resolveCompiledTLKPath(outputDir string) (string, error) {
} }
func copyFile(dst, src string) error { func copyFile(dst, src string) error {
if samePath(dst, src) {
return nil
}
input, err := os.Open(src) input, err := os.Open(src)
if err != nil { if err != nil {
return fmt.Errorf("open %s: %w", src, err) return fmt.Errorf("open %s: %w", src, err)
@@ -232,18 +265,26 @@ func copyFile(dst, src string) error {
return nil return nil
} }
func samePath(left, right string) bool {
leftAbs, leftErr := filepath.Abs(left)
rightAbs, rightErr := filepath.Abs(right)
if leftErr != nil || rightErr != nil {
return left == right
}
return leftAbs == rightAbs
}
func packagedBuildResult(p *project.Project) (BuildResult, error) { func packagedBuildResult(p *project.Project) (BuildResult, error) {
if !p.HasTopData() { if !p.HasTopData() {
return BuildResult{}, errors.New("topdata is not configured for this project") return BuildResult{}, errors.New("topdata is not configured for this project")
} }
buildDir := p.TopDataBuildDir() output2DA := compiled2DAOutputDir(p)
output2DA := filepath.Join(buildDir, "2da") outputTLK := compiledTLKOutputDir(p)
outputTLK := filepath.Join(buildDir, "tlk")
if _, err := os.Stat(output2DA); err != nil { if _, err := os.Stat(output2DA); err != nil {
return BuildResult{}, fmt.Errorf("topdata build output missing: run build-topdata first") return BuildResult{}, fmt.Errorf("topdata build output missing: run build-topdata first")
} }
if _, err := os.Stat(outputTLK); err != nil { if _, err := os.Stat(compiledTLKOutputPath(p)); err != nil {
return BuildResult{}, fmt.Errorf("topdata tlk output missing: run build-topdata first") return BuildResult{}, fmt.Errorf("topdata tlk output missing: run build-topdata first")
} }
@@ -251,16 +292,15 @@ func packagedBuildResult(p *project.Project) (BuildResult, error) {
if err != nil { if err != nil {
return BuildResult{}, err return BuildResult{}, err
} }
filesTLK, oldestTLK, err := countCompiledFiles(outputTLK, ".tlk") filesTLK := 1
tlkInfo, err := os.Stat(compiledTLKOutputPath(p))
if err != nil { if err != nil {
return BuildResult{}, err return BuildResult{}, fmt.Errorf("stat compiled tlk output %s: %w", compiledTLKOutputPath(p), err)
} }
oldestTLK := tlkInfo.ModTime()
if files2DA == 0 { if files2DA == 0 {
return BuildResult{}, fmt.Errorf("topdata build output missing: run build-topdata first") return BuildResult{}, fmt.Errorf("topdata build output missing: run build-topdata first")
} }
if filesTLK != 1 {
return BuildResult{}, fmt.Errorf("expected exactly 1 compiled tlk output, found %d", filesTLK)
}
newestSource, newestPath, err := newestTopDataSource(p.TopDataSourceDir()) newestSource, newestPath, err := newestTopDataSource(p.TopDataSourceDir())
if err != nil { if err != nil {
@@ -311,6 +351,7 @@ func newestTopDataSource(sourceDir string) (time.Time, string, error) {
newestPath := "" newestPath := ""
templatesDir := filepath.Join(sourceDir, "templates") templatesDir := filepath.Join(sourceDir, "templates")
wikiDir := filepath.Join(sourceDir, wikiRootDirName) wikiDir := filepath.Join(sourceDir, wikiRootDirName)
generated2DADir := filepath.Join(sourceDir, "assets", "2da")
err := filepath.WalkDir(sourceDir, func(path string, d fs.DirEntry, walkErr error) error { err := filepath.WalkDir(sourceDir, func(path string, d fs.DirEntry, walkErr error) error {
if walkErr != nil { if walkErr != nil {
return walkErr return walkErr
@@ -321,6 +362,9 @@ func newestTopDataSource(sourceDir string) (time.Time, string, error) {
if d.IsDir() && path == wikiDir { if d.IsDir() && path == wikiDir {
return filepath.SkipDir return filepath.SkipDir
} }
if d.IsDir() && path == generated2DADir {
return filepath.SkipDir
}
if d.IsDir() { if d.IsDir() {
return nil return nil
} }
+7 -4
View File
@@ -945,6 +945,7 @@ func validateNativeOutputCatalog(dataDir string, report *ValidationReport) {
func validateTopPackageAssets(sourceDir, dataDir string, report *ValidationReport) { func validateTopPackageAssets(sourceDir, dataDir string, report *ValidationReport) {
assetsDir := filepath.Join(sourceDir, "assets") assetsDir := filepath.Join(sourceDir, "assets")
generated2DADir := filepath.Join(assetsDir, "2da")
info, err := os.Stat(assetsDir) info, err := os.Stat(assetsDir)
if err != nil { if err != nil {
if errors.Is(err, os.ErrNotExist) { if errors.Is(err, os.ErrNotExist) {
@@ -981,6 +982,9 @@ func validateTopPackageAssets(sourceDir, dataDir string, report *ValidationRepor
if walkErr != nil { if walkErr != nil {
return walkErr return walkErr
} }
if d.IsDir() && path == generated2DADir {
return filepath.SkipDir
}
if d.IsDir() { if d.IsDir() {
return nil return nil
} }
@@ -1313,9 +1317,8 @@ func Compare(p *project.Project, progress func(string)) (CompareResult, error) {
if progress == nil { if progress == nil {
progress = func(string) {} progress = func(string) {}
} }
buildDir := p.TopDataBuildDir() output2DA := compiled2DAOutputDir(p)
output2DA := filepath.Join(buildDir, "2da") outputTLK := compiledTLKOutputDir(p)
outputTLK := filepath.Join(buildDir, "tlk")
if _, err := os.Stat(output2DA); err != nil { if _, err := os.Stat(output2DA); err != nil {
return CompareResult{}, fmt.Errorf("topdata build output missing: run build-topdata first") return CompareResult{}, fmt.Errorf("topdata build output missing: run build-topdata first")
} }
@@ -1466,7 +1469,7 @@ func compareNativeSelfCheck(p *project.Project, actual2DA, actualTLK string, pro
} }
func compareNativeCoverage(p *project.Project, actual2DA string, progress func(string)) (int, int, int, error) { func compareNativeCoverage(p *project.Project, actual2DA string, progress func(string)) (int, int, int, error) {
actualTLK := filepath.Join(filepath.Dir(actual2DA), "tlk") actualTLK := compiledTLKOutputDir(p)
compared2DA, _, pass, mismatch, err := compareNativeSelfCheck(p, actual2DA, actualTLK, progress) compared2DA, _, pass, mismatch, err := compareNativeSelfCheck(p, actual2DA, actualTLK, progress)
if err != nil { if err != nil {
return 0, 0, 0, err return 0, 0, 0, err
+34 -2
View File
@@ -3640,8 +3640,8 @@ func TestBuildNativeRemovesStaleTopdataOutputs(t *testing.T) {
] ]
}`+"\n") }`+"\n")
writeFile(t, filepath.Join(root, "topdata", "data", "repadjust", "lock.json"), "{}\n") writeFile(t, filepath.Join(root, "topdata", "data", "repadjust", "lock.json"), "{}\n")
mkdirAll(t, filepath.Join(root, "build", "topdata", "2da")) mkdirAll(t, filepath.Join(root, "topdata", "assets", "2da"))
writeFile(t, filepath.Join(root, "build", "topdata", "2da", "stale.2da"), "stale\n") writeFile(t, filepath.Join(root, "topdata", "assets", "2da", "stale.2da"), "stale\n")
p := testProject(root) p := testProject(root)
p.Config.TopData.ReferenceBuilder = "" p.Config.TopData.ReferenceBuilder = ""
@@ -3654,6 +3654,38 @@ func TestBuildNativeRemovesStaleTopdataOutputs(t *testing.T) {
} }
} }
func TestBuildNativeWritesCompiledOutputsToAssetsAndBuild(t *testing.T) {
root := testProjectRoot(t)
mkdirAll(t, filepath.Join(root, "topdata", "data", "repadjust"))
writeFile(t, filepath.Join(root, "topdata", "base_dialog.json"), "{}\n")
writeFile(t, filepath.Join(root, "topdata", "data", "repadjust", "base.json"), `{
"output": "repadjust.2da",
"columns": ["LABEL"],
"rows": [
{"id": 0, "LABEL": "TEST"}
]
}`+"\n")
p := testProject(root)
p.Config.TopData.ReferenceBuilder = ""
result, err := BuildNative(p, nil)
if err != nil {
t.Fatalf("BuildNative failed: %v", err)
}
if got, want := result.Output2DADir, filepath.Join(root, "topdata", "assets", "2da"); got != want {
t.Fatalf("unexpected 2da output dir: got %q want %q", got, want)
}
if got, want := result.OutputTLKDir, filepath.Join(root, "build"); got != want {
t.Fatalf("unexpected tlk output dir: got %q want %q", got, want)
}
if _, err := os.Stat(filepath.Join(result.Output2DADir, "repadjust.2da")); err != nil {
t.Fatalf("expected compiled 2da output: %v", err)
}
if _, err := os.Stat(filepath.Join(result.OutputTLKDir, defaultTLKName)); err != nil {
t.Fatalf("expected compiled tlk output: %v", err)
}
}
func TestValidateAndBuildTopdataDoNotRequireReferenceBuilder(t *testing.T) { func TestValidateAndBuildTopdataDoNotRequireReferenceBuilder(t *testing.T) {
root := testProjectRoot(t) root := testProjectRoot(t)
mkdirAll(t, filepath.Join(root, "topdata", "data", "repadjust")) mkdirAll(t, filepath.Join(root, "topdata", "data", "repadjust"))