From ce7b067785c4f928b9e4563c8c3a2380f050360a Mon Sep 17 00:00:00 2001 From: CharlesKWON Date: Tue, 5 May 2026 19:21:45 +0900 Subject: [PATCH] fix(cli): multi-PRG build adds every input dir to the include path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each PRG file's preprocessor instance was set up with only its OWN directory on the include search path (`filepath.Dir(prgFile)`). That worked for self-contained files but broke any multi-file build where one PRG `#include`s a header that lives next to a SIBLING PRG — the other file's directory wasn't on the path, so the include silently failed and PP just skipped it ("// #include \"FiveSqlDef.ch\" — not found (skipped)"). This was the root cause behind test_sql_standards's mass-failure pattern. The test does #include "FiveSqlDef.ch" ... Assert( ..., h["columns"][1][1][1] == ND_FN .AND. ... ) `FiveSqlDef.ch` lives in `_FiveSql2/src/` (next to TSqlExecutor.prg and friends), but the test source sits in `_FiveSql2/test/`. Building with `./five build _FiveSql2/test/test_sql_standards.prg _FiveSql2/src/*.prg` should resolve the header from a sibling input file's directory — but only the test's own dir was searched, so ND_FN / ND_LIT / ND_BIN / ND_UNI all stayed undefined and the identifiers fell through to runtime memvar lookup, returning NIL. Every assertion that compared against the constants therefore silently failed (24 / 64 passing because non-constant assertions still worked). buildMultiPRGWithIncludes now seeds the user-include list with the directory of every input PRG before handing off to buildMultiPRG. A test under one directory can now resolve a `#include` that lives next to a sibling source file in the same multi-file build. Result: test_sql_standards goes from 24 / 64 to **64 / 64**. The parser was already correct end-to-end — every SQL:2003-2023 construct it had been advertising actually worked; the test just couldn't read the constants it was asserting against. Wired test_sql_standards into the std.ch runner with a per-test override so it picks up the FiveSql2 src files. Suite stands at 17/17. Other gates green: go test ./... : PASS FiveSql2 SQL:1999 : 43/43 FiveSql2 standards : 64/64 (was 24/64) Harbour compat : 56/56 std.ch suite : 17/17 FRB suite : 7/7 Co-Authored-By: Claude Opus 4.7 (1M context) --- cmd/five/main.go | 18 ++++++++++++++++++ tests/std_ch/run.sh | 12 +++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/cmd/five/main.go b/cmd/five/main.go index 08d6163..4d59d58 100644 --- a/cmd/five/main.go +++ b/cmd/five/main.go @@ -324,6 +324,24 @@ func buildPRGWithIncludes(prgFile, output string, includes []string) { // buildMultiPRGWithIncludes is buildMultiPRG with -I support. func buildMultiPRGWithIncludes(prgFiles []string, output string, includes []string) { + // Multi-file builds bring along sibling PRGs whose own #include + // references a .ch file living next to them (e.g. FiveSqlDef.ch + // in _FiveSql2/src/). Each file's PP only adds its OWN dir by + // default, so a test under _FiveSql2/test/ couldn't find a .ch + // kept in _FiveSql2/src/. Promote every input file's dir into + // the shared user-include list so siblings can resolve each + // other's headers. + seen := map[string]bool{} + for _, dir := range includes { + seen[dir] = true + } + for _, f := range prgFiles { + dir := filepath.Dir(f) + if dir != "" && !seen[dir] { + seen[dir] = true + includes = append(includes, dir) + } + } userIncludeDirs = includes buildMultiPRG(prgFiles, output) } diff --git a/tests/std_ch/run.sh b/tests/std_ch/run.sh index 0abf7c9..5fbbb52 100755 --- a/tests/std_ch/run.sh +++ b/tests/std_ch/run.sh @@ -32,6 +32,7 @@ TESTS=( test_block_comma test_compound_lhs test_join_hash + test_sql_standards ) work="$(mktemp -d)" @@ -41,8 +42,17 @@ pass=0 fail=0 for name in "${TESTS[@]}"; do src="$ROOT/tests/std_ch/${name}.prg" + # test_sql_standards lives in our suite but its `#include + # "FiveSqlDef.ch"` resolves only when the FiveSql2 src files + # are part of the same build (they sit alongside the .ch). + # Other tests build standalone. + extras=() + if [ "$name" = "test_sql_standards" ]; then + src="$ROOT/_FiveSql2/test/test_sql_standards.prg" + extras=( $ROOT/_FiveSql2/src/*.prg ) + fi bin="$work/${name}" - if ! "$FIVE" build "$src" -o "$bin" >/dev/null 2>"$work/${name}.err"; then + if ! "$FIVE" build "$src" "${extras[@]}" -o "$bin" >/dev/null 2>"$work/${name}.err"; then echo "FAIL build $name" cat "$work/${name}.err" | sed 's/^/ /' fail=$((fail+1))