From 129602a482848094829effa28f1dc2e7b798ac7b Mon Sep 17 00:00:00 2001 From: vickydotbat Date: Mon, 13 Apr 2026 15:05:43 +0200 Subject: [PATCH] Change Output Locations --- internal/app/app.go | 2 +- internal/pipeline/compare.go | 4 ++ internal/topdata/native.go | 11 +++--- internal/topdata/top_package.go | 68 ++++++++++++++++++++++++++------ internal/topdata/topdata.go | 11 ++++-- internal/topdata/topdata_test.go | 36 ++++++++++++++++- 6 files changed, 107 insertions(+), 25 deletions(-) diff --git a/internal/app/app.go b/internal/app/app.go index 47fe8d2..1563ab8 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -70,7 +70,7 @@ var commands = []command{ }, { 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, }, { diff --git a/internal/pipeline/compare.go b/internal/pipeline/compare.go index f20f764..bac4756 100644 --- a/internal/pipeline/compare.go +++ b/internal/pipeline/compare.go @@ -330,6 +330,7 @@ func manifestHAKPaths(buildDir string) ([]string, error) { func topPackageSourcePaths(p *project.Project) ([]string, error) { sourceDir := p.TopDataSourceDir() + generated2DADir := filepath.Join(sourceDir, "assets", "2da") paths := make([]string, 0) for _, candidate := range []string{ filepath.Join(sourceDir, ".tlk_state.json"), @@ -359,6 +360,9 @@ func topPackageSourcePaths(p *project.Project) ([]string, error) { if walkErr != nil { return walkErr } + if d.IsDir() && path == generated2DADir { + return filepath.SkipDir + } if d.IsDir() { return nil } diff --git a/internal/topdata/native.go b/internal/topdata/native.go index 60194c6..1891937 100644 --- a/internal/topdata/native.go +++ b/internal/topdata/native.go @@ -192,12 +192,11 @@ func buildNativeUnchecked(p *project.Project, progress func(string), prebuiltWik return BuildResult{}, err } - buildDir := p.TopDataBuildDir() - output2DA := filepath.Join(buildDir, "2da") - outputTLK := filepath.Join(buildDir, "tlk") - progress("Preparing native topdata build directories...") - if err := os.RemoveAll(buildDir); err != nil { - return BuildResult{}, fmt.Errorf("clean topdata build dir: %w", err) + output2DA := compiled2DAOutputDir(p) + outputTLK := compiledTLKOutputDir(p) + progress("Preparing native topdata build outputs...") + if err := os.RemoveAll(output2DA); err != nil { + return BuildResult{}, fmt.Errorf("clean topdata 2da output dir: %w", err) } if err := os.MkdirAll(output2DA, 0o755); err != nil { return BuildResult{}, fmt.Errorf("create 2da output dir: %w", err) diff --git a/internal/topdata/top_package.go b/internal/topdata/top_package.go index ec23be1..038807c 100644 --- a/internal/topdata/top_package.go +++ b/internal/topdata/top_package.go @@ -21,6 +21,29 @@ const ( 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) { if progress == nil { progress = func(string) {} @@ -68,9 +91,12 @@ func packageBuiltTopData(p *project.Project, nativeResult BuildResult, progress } progress("Preparing compiled TLK release output...") - sourceTLK, err := resolveCompiledTLKPath(nativeResult.OutputTLKDir) - if err != nil { - return PackageResult{}, err + sourceTLK := compiledTLKOutputPath(p) + if nativeResult.OutputTLKDir != "" { + sourceTLK, err = resolveCompiledTLKPath(nativeResult.OutputTLKDir) + if err != nil { + return PackageResult{}, err + } } if err := copyFile(outputTLK, sourceTLK); err != nil { return PackageResult{}, err @@ -113,12 +139,16 @@ func collectTopPackageResources(p *project.Project, compiled2DADir string) ([]er assetFiles := 0 assetsDir := filepath.Join(p.TopDataSourceDir(), "assets") + skipDir := compiled2DAOutputDir(p) info, err := os.Stat(assetsDir) if err == nil && info.IsDir() { err = filepath.WalkDir(assetsDir, func(path string, d os.DirEntry, walkErr error) error { if walkErr != nil { return walkErr } + if d.IsDir() && path == skipDir { + return filepath.SkipDir + } if d.IsDir() { return nil } @@ -214,6 +244,9 @@ func resolveCompiledTLKPath(outputDir string) (string, error) { } func copyFile(dst, src string) error { + if samePath(dst, src) { + return nil + } input, err := os.Open(src) if err != nil { return fmt.Errorf("open %s: %w", src, err) @@ -232,18 +265,26 @@ func copyFile(dst, src string) error { 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) { if !p.HasTopData() { return BuildResult{}, errors.New("topdata is not configured for this project") } - buildDir := p.TopDataBuildDir() - output2DA := filepath.Join(buildDir, "2da") - outputTLK := filepath.Join(buildDir, "tlk") + output2DA := compiled2DAOutputDir(p) + outputTLK := compiledTLKOutputDir(p) if _, err := os.Stat(output2DA); err != nil { 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") } @@ -251,16 +292,15 @@ func packagedBuildResult(p *project.Project) (BuildResult, error) { if err != nil { return BuildResult{}, err } - filesTLK, oldestTLK, err := countCompiledFiles(outputTLK, ".tlk") + filesTLK := 1 + tlkInfo, err := os.Stat(compiledTLKOutputPath(p)) 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 { 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()) if err != nil { @@ -311,6 +351,7 @@ func newestTopDataSource(sourceDir string) (time.Time, string, error) { newestPath := "" templatesDir := filepath.Join(sourceDir, "templates") wikiDir := filepath.Join(sourceDir, wikiRootDirName) + generated2DADir := filepath.Join(sourceDir, "assets", "2da") err := filepath.WalkDir(sourceDir, func(path string, d fs.DirEntry, walkErr error) error { if walkErr != nil { return walkErr @@ -321,6 +362,9 @@ func newestTopDataSource(sourceDir string) (time.Time, string, error) { if d.IsDir() && path == wikiDir { return filepath.SkipDir } + if d.IsDir() && path == generated2DADir { + return filepath.SkipDir + } if d.IsDir() { return nil } diff --git a/internal/topdata/topdata.go b/internal/topdata/topdata.go index 01249cb..f9cb13d 100644 --- a/internal/topdata/topdata.go +++ b/internal/topdata/topdata.go @@ -945,6 +945,7 @@ func validateNativeOutputCatalog(dataDir string, report *ValidationReport) { func validateTopPackageAssets(sourceDir, dataDir string, report *ValidationReport) { assetsDir := filepath.Join(sourceDir, "assets") + generated2DADir := filepath.Join(assetsDir, "2da") info, err := os.Stat(assetsDir) if err != nil { if errors.Is(err, os.ErrNotExist) { @@ -981,6 +982,9 @@ func validateTopPackageAssets(sourceDir, dataDir string, report *ValidationRepor if walkErr != nil { return walkErr } + if d.IsDir() && path == generated2DADir { + return filepath.SkipDir + } if d.IsDir() { return nil } @@ -1313,9 +1317,8 @@ func Compare(p *project.Project, progress func(string)) (CompareResult, error) { if progress == nil { progress = func(string) {} } - buildDir := p.TopDataBuildDir() - output2DA := filepath.Join(buildDir, "2da") - outputTLK := filepath.Join(buildDir, "tlk") + output2DA := compiled2DAOutputDir(p) + outputTLK := compiledTLKOutputDir(p) if _, err := os.Stat(output2DA); err != nil { 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) { - actualTLK := filepath.Join(filepath.Dir(actual2DA), "tlk") + actualTLK := compiledTLKOutputDir(p) compared2DA, _, pass, mismatch, err := compareNativeSelfCheck(p, actual2DA, actualTLK, progress) if err != nil { return 0, 0, 0, err diff --git a/internal/topdata/topdata_test.go b/internal/topdata/topdata_test.go index 570a082..08adf28 100644 --- a/internal/topdata/topdata_test.go +++ b/internal/topdata/topdata_test.go @@ -3640,8 +3640,8 @@ func TestBuildNativeRemovesStaleTopdataOutputs(t *testing.T) { ] }`+"\n") writeFile(t, filepath.Join(root, "topdata", "data", "repadjust", "lock.json"), "{}\n") - mkdirAll(t, filepath.Join(root, "build", "topdata", "2da")) - writeFile(t, filepath.Join(root, "build", "topdata", "2da", "stale.2da"), "stale\n") + mkdirAll(t, filepath.Join(root, "topdata", "assets", "2da")) + writeFile(t, filepath.Join(root, "topdata", "assets", "2da", "stale.2da"), "stale\n") p := testProject(root) 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) { root := testProjectRoot(t) mkdirAll(t, filepath.Join(root, "topdata", "data", "repadjust"))