feat: transitive quoted includes participate in the c-object cache key

The key previously covered the #source bytes + the block's DECLARED
headers, so a unit whose impl is a thin wrapper over an undeclared
header (vendors/kb_text_shape: two-line impl.c, all code in
kb/kb_text_shape.h) would serve STALE cached objects after an
upstream upgrade. collectIncludeDepBytes now walks the transitive
closure of quoted #include lines (includer-dir first, then -I dirs;
angle/system includes never participate; unresolvable names skip) and
the dep contents fold into the key — no sidecar, no compare logic, a
changed header is just a different key. Verified live: appending to
kb_text_shape.h mints a new cache entry; reverting hits the old one.
This commit is contained in:
agra
2026-06-12 18:48:56 +03:00
parent b06776d6e9
commit 4b9324e585
2 changed files with 126 additions and 23 deletions

View File

@@ -3,12 +3,13 @@ const c_import = @import("c_import.zig");
const SRC = "int f(void) { return 1; }";
const HDR = "int f(void);";
const DEP = "#define INNER 1";
const VER = "19.1.7";
const none: []const []const u8 = &.{};
fn baseKey() u64 {
return c_import.cSourceCacheKey(SRC, &.{HDR}, &.{"A=1"}, &.{"-O2"}, &.{"inc"}, VER, "arm64-apple-darwin", "/sdk");
return c_import.cSourceCacheKey(SRC, &.{HDR}, &.{DEP}, &.{"A=1"}, &.{"-O2"}, &.{"inc"}, VER, "arm64-apple-darwin", "/sdk");
}
test "cSourceCacheKey: stable when nothing changes" {
@@ -16,42 +17,52 @@ test "cSourceCacheKey: stable when nothing changes" {
}
test "cSourceCacheKey: source bytes vary the key" {
const other = c_import.cSourceCacheKey("int f(void) { return 2; }", &.{HDR}, &.{"A=1"}, &.{"-O2"}, &.{"inc"}, VER, "arm64-apple-darwin", "/sdk");
const other = c_import.cSourceCacheKey("int f(void) { return 2; }", &.{HDR}, &.{DEP}, &.{"A=1"}, &.{"-O2"}, &.{"inc"}, VER, "arm64-apple-darwin", "/sdk");
try std.testing.expect(baseKey() != other);
}
test "cSourceCacheKey: declared header content varies the key" {
const other = c_import.cSourceCacheKey(SRC, &.{"int f(void); int g(void);"}, &.{"A=1"}, &.{"-O2"}, &.{"inc"}, VER, "arm64-apple-darwin", "/sdk");
const other = c_import.cSourceCacheKey(SRC, &.{"int f(void); int g(void);"}, &.{DEP}, &.{"A=1"}, &.{"-O2"}, &.{"inc"}, VER, "arm64-apple-darwin", "/sdk");
try std.testing.expect(baseKey() != other);
}
test "cSourceCacheKey: transitive dep content varies the key" {
const other = c_import.cSourceCacheKey(SRC, &.{HDR}, &.{"#define INNER 2"}, &.{"A=1"}, &.{"-O2"}, &.{"inc"}, VER, "arm64-apple-darwin", "/sdk");
try std.testing.expect(baseKey() != other);
// a header is not a dep (same string, different role)
const as_header = c_import.cSourceCacheKey(SRC, &.{"X"}, none, none, none, none, VER, null, null);
const as_dep = c_import.cSourceCacheKey(SRC, none, &.{"X"}, none, none, none, VER, null, null);
try std.testing.expect(as_header != as_dep);
}
test "cSourceCacheKey: defines vary the key (value and order)" {
const v2 = c_import.cSourceCacheKey(SRC, &.{HDR}, &.{"A=2"}, &.{"-O2"}, &.{"inc"}, VER, "arm64-apple-darwin", "/sdk");
const v2 = c_import.cSourceCacheKey(SRC, &.{HDR}, &.{DEP}, &.{"A=2"}, &.{"-O2"}, &.{"inc"}, VER, "arm64-apple-darwin", "/sdk");
try std.testing.expect(baseKey() != v2);
const ab = c_import.cSourceCacheKey(SRC, &.{HDR}, &.{ "A=1", "B=1" }, &.{"-O2"}, &.{"inc"}, VER, "arm64-apple-darwin", "/sdk");
const ba = c_import.cSourceCacheKey(SRC, &.{HDR}, &.{ "B=1", "A=1" }, &.{"-O2"}, &.{"inc"}, VER, "arm64-apple-darwin", "/sdk");
const ab = c_import.cSourceCacheKey(SRC, &.{HDR}, &.{DEP}, &.{ "A=1", "B=1" }, &.{"-O2"}, &.{"inc"}, VER, "arm64-apple-darwin", "/sdk");
const ba = c_import.cSourceCacheKey(SRC, &.{HDR}, &.{DEP}, &.{ "B=1", "A=1" }, &.{"-O2"}, &.{"inc"}, VER, "arm64-apple-darwin", "/sdk");
try std.testing.expect(ab != ba);
}
test "cSourceCacheKey: flags vary the key" {
const other = c_import.cSourceCacheKey(SRC, &.{HDR}, &.{"A=1"}, &.{"-O3"}, &.{"inc"}, VER, "arm64-apple-darwin", "/sdk");
const other = c_import.cSourceCacheKey(SRC, &.{HDR}, &.{DEP}, &.{"A=1"}, &.{"-O3"}, &.{"inc"}, VER, "arm64-apple-darwin", "/sdk");
try std.testing.expect(baseKey() != other);
}
test "cSourceCacheKey: a define is not a flag (same string, different role)" {
const as_define = c_import.cSourceCacheKey(SRC, none, &.{"X"}, none, none, VER, null, null);
const as_flag = c_import.cSourceCacheKey(SRC, none, none, &.{"X"}, none, VER, null, null);
const as_define = c_import.cSourceCacheKey(SRC, none, none, &.{"X"}, none, none, VER, null, null);
const as_flag = c_import.cSourceCacheKey(SRC, none, none, none, &.{"X"}, none, VER, null, null);
try std.testing.expect(as_define != as_flag);
}
test "cSourceCacheKey: include dirs vary the key" {
const other = c_import.cSourceCacheKey(SRC, &.{HDR}, &.{"A=1"}, &.{"-O2"}, &.{"other"}, VER, "arm64-apple-darwin", "/sdk");
const other = c_import.cSourceCacheKey(SRC, &.{HDR}, &.{DEP}, &.{"A=1"}, &.{"-O2"}, &.{"other"}, VER, "arm64-apple-darwin", "/sdk");
try std.testing.expect(baseKey() != other);
}
test "cSourceCacheKey: llvm version varies the key" {
const other = c_import.cSourceCacheKey(SRC, &.{HDR}, &.{"A=1"}, &.{"-O2"}, &.{"inc"}, "20.0.0", "arm64-apple-darwin", "/sdk");
const other = c_import.cSourceCacheKey(SRC, &.{HDR}, &.{DEP}, &.{"A=1"}, &.{"-O2"}, &.{"inc"}, "20.0.0", "arm64-apple-darwin", "/sdk");
try std.testing.expect(baseKey() != other);
}
@@ -65,13 +76,32 @@ test "objectMagicOk: accepts Mach-O and ELF, rejects garbage and truncation" {
}
test "cSourceCacheKey: triple and sysroot vary the key; absent is not empty" {
const other_triple = c_import.cSourceCacheKey(SRC, &.{HDR}, &.{"A=1"}, &.{"-O2"}, &.{"inc"}, VER, "x86_64-apple-darwin", "/sdk");
const other_triple = c_import.cSourceCacheKey(SRC, &.{HDR}, &.{DEP}, &.{"A=1"}, &.{"-O2"}, &.{"inc"}, VER, "x86_64-apple-darwin", "/sdk");
try std.testing.expect(baseKey() != other_triple);
const other_sysroot = c_import.cSourceCacheKey(SRC, &.{HDR}, &.{"A=1"}, &.{"-O2"}, &.{"inc"}, VER, "arm64-apple-darwin", "/ndk");
const other_sysroot = c_import.cSourceCacheKey(SRC, &.{HDR}, &.{DEP}, &.{"A=1"}, &.{"-O2"}, &.{"inc"}, VER, "arm64-apple-darwin", "/ndk");
try std.testing.expect(baseKey() != other_sysroot);
const absent = c_import.cSourceCacheKey(SRC, none, none, none, none, VER, null, null);
const empty = c_import.cSourceCacheKey(SRC, none, none, none, none, VER, "", "");
const absent = c_import.cSourceCacheKey(SRC, none, none, none, none, none, VER, null, null);
const empty = c_import.cSourceCacheKey(SRC, none, none, none, none, none, VER, "", "");
try std.testing.expect(absent != empty);
}
test "scanQuotedIncludes: quoted forms collected in order, angle and noise skipped" {
const src =
\\#include "a.h"
\\ # include "sub/b.h"
\\#include <system.h>
\\#includex "not_an_include.h"
\\int f(void);
\\#include ""
\\#include "c.h"
;
var out = std.ArrayList([]const u8).empty;
try c_import.scanQuotedIncludes(std.testing.allocator, src, &out);
defer out.deinit(std.testing.allocator);
try std.testing.expectEqual(@as(usize, 3), out.items.len);
try std.testing.expectEqualStrings("a.h", out.items[0]);
try std.testing.expectEqualStrings("sub/b.h", out.items[1]);
try std.testing.expectEqualStrings("c.h", out.items[2]);
}