code wiki / (root) / nx_inc_compile_gate.nx

nx_inc_compile_gate.nx source

↩ module page · 455 lines · 27090 B

1// nx_inc_compile_gate.nx -- VERDICT gate for rung LN10 of /compare/lang: the per-module incremental build 2// cache in nx_inc_compile (watch symbol inc_cache_lookup). 3// 4// DONE-RULE UNDER TEST (lang.plan LN10, verbatim): "gate proves a one-file edit rebuilds only its 5// dependents and the output equals a full build". 6// 7// EVERYTHING RUNS IN AN ISOLATED FIXTURE TREE under <buildroot>/_build/nx_inc_compile_gate/ -- its own 8// runtime/, its own _offc/ copy of the live toolchain AND of the closure ruler, its own _build/ and its own 9// knowledge/ -- so nothing here touches the estate's cache, its toolchain, its confs or its serving root. 10// The fixture is wiped at SETUP, never at teardown: a teardown does not run when a run crashes, and a gate 11// that is not idempotent reports on its first run and lies about every run after. 12// FIXTURE ROOT IS DERIVED, NOT /tmp: measured on this NAS, a byte-identical copy of the compiler placed 13// under /tmp returns execve 127 -- the mount refuses execution -- so every builder run would fail with an 14// empty .s and read as a compiler defect. Build-artifact territory is executable by construction. 15// 16// THE ZERO-COMPILE WITNESS IS STATE, NOT A MESSAGE: nx_sov_build_run writes _build/<t>.s only inside its 17// compile loop. The gate deletes that file before a rebuild; if the artifact is still current and the .s is 18// still ABSENT, no compiler ran. A tooth asserts the .s IS present after a real build, so the absence 19// witness cannot pass vacuously. 20// THE FIXTURE'S OWN buildcache.conf SETS cache_enabled=0 so that B2's whole-target cache cannot serve the 21// hit this gate is attributing to LN10 -- without that, every "zero compile" tooth would measure B2. 22// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0 23import "nx_syscalls.nx" 24import "nx_sha256.nx" 25import "nx_gate_verdict.nx" 26import "nx_gatekit_lib.nx" 27import "nx_tool_run.nx" 28 29const IG_PATH: i64 = 4096 30const IG_OUT: i64 = 1048576 31const IG_SHA_HEX: i64 = 64 32const IG_MODE_X: i64 = 493 // 0755 33const IG_MODE_R: i64 = 420 // 0644 34const IG_TMO: i64 = 900000 // the builder's own SBR_BUILD_DEADLINE_MS, inherited not re-chosen 35const IG_VAL_A: i64 = 11 // icfx_lib v1 return value 36const IG_VAL_B: i64 = 19 // icfx_lib v2 -- ONE line differs 37const IG_VAL_C: i64 = 23 // icfx_lib v3 38const IG_SHOW: i64 = 3000 // how much captured output to echo when a tooth fails 39 40static igp_fx: i64 41static igp_tree: i64 42static igp_know: i64 43static igp_rt: i64 44static igp_hdl: i64 45static igp_offc: i64 46static igp_bld: i64 47static igp_cache: i64 48static igp_idx: i64 49static igp_cc: i64 50static igp_asm: i64 51static igp_ruler: i64 52static igp_builder: i64 53static igp_rootarg: i64 54 55func igp_mk(base: *u8, tail: *u8) -> i64 { 56 let b: *u8 = sys_mmap(IG_PATH) 57 var o: i64 = gk_cat(b, 0, base) 58 o = gk_cat(b, o, tail) 59 b[o] = 0 as u8 60 return b as i64 61} 62func igp_init(buildroot: *u8) -> i64 { 63 igp_fx = igp_mk(buildroot, "_build/nx_inc_compile_gate" as *u8) 64 igp_tree = igp_mk(igp_fx as *u8, "/tree" as *u8) 65 igp_know = igp_mk(igp_fx as *u8, "/knowledge" as *u8) 66 igp_rt = igp_mk(igp_tree as *u8, "/runtime" as *u8) 67 igp_hdl = igp_mk(igp_rt as *u8, "/_hdl_build" as *u8) 68 igp_offc = igp_mk(igp_tree as *u8, "/_offc" as *u8) 69 igp_bld = igp_mk(igp_tree as *u8, "/_build" as *u8) 70 igp_cache = igp_mk(igp_bld as *u8, "/inccache" as *u8) 71 igp_idx = igp_mk(igp_cache as *u8, "/index" as *u8) 72 igp_cc = igp_mk(igp_offc as *u8, "/nx_cc_sovereign.elf" as *u8) 73 igp_asm = igp_mk(igp_offc as *u8, "/nxasm_x86_main.elf" as *u8) 74 igp_ruler = igp_mk(igp_offc as *u8, "/nx_closurehash.elf" as *u8) 75 igp_builder = igp_mk(igp_offc as *u8, "/nx_sov_build_run.elf" as *u8) 76 igp_rootarg = igp_mk("--root=" as *u8, igp_tree as *u8) 77 return 0 78} 79func igp(v: i64) -> *u8 { return v as *u8 } 80func ig_p(s: *u8) -> i64 { sys_write(1, s, gk_len(s)); return 0 } 81func ig_nl() -> i64 { let b: *u8 = sys_mmap(2); b[0] = 10 as u8; sys_write(1, b, 1); sys_munmap(b, 2); return 0 } 82func ig_n(v: i64) -> i64 { gv_num(v); return 0 } 83func ig_sub(dir: i64, name: *u8) -> *u8 { return igp_mk(igp(dir), name) as *u8 } 84 85func ig_sha(path: *u8, out: *u8) -> i64 { 86 let ln: *i64 = sys_mmap(16) as *i64 87 ln[0] = 0 88 let buf: *u8 = sys_read_file(path, ln) 89 if (buf as i64) == 0 { out[0] = 0 as u8; return 0 } 90 let d: *u8 = sys_mmap(48) 91 sha256_digest(buf, ln[0], d) 92 var i: i64 = 0 93 while i < 32 { 94 let v: i64 = d[i] as i64 95 let hi: i64 = (v >> 4) & 15 96 let lo: i64 = v & 15 97 if hi < 10 { out[i*2] = (48+hi) as u8 } else { out[i*2] = (87+hi) as u8 } 98 if lo < 10 { out[i*2+1] = (48+lo) as u8 } else { out[i*2+1] = (87+lo) as u8 } 99 i = i + 1 100 } 101 out[IG_SHA_HEX] = 0 as u8 102 sys_free_file(buf, ln[0]) 103 return 1 104} 105func ig_hexeq(a: *u8, b: *u8) -> i64 { 106 if a[0] == (0 as u8) { return 0 } 107 if b[0] == (0 as u8) { return 0 } 108 var i: i64 = 0 109 while i < IG_SHA_HEX { if a[i] != b[i] { return 0 } i = i + 1 } 110 return 1 111} 112func ig_copy(src: *u8, dst: *u8, mode: i64) -> i64 { 113 let ln: *i64 = sys_mmap(16) as *i64 114 ln[0] = 0 115 let buf: *u8 = sys_read_file(src, ln) 116 if (buf as i64) == 0 { return 0 } 117 sys_unlinkat(dst) 118 let fd: i64 = sys_openat_wr(dst, mode) 119 if fd < 0 { return 0 } 120 let w: i64 = gk_write_all(fd, buf, ln[0]) 121 sys_close(fd) 122 sys_free_file(buf, ln[0]) 123 if w != ln[0] { return 0 } 124 nx_chmod(dst, mode) 125 return 1 126} 127func ig_append_byte(path: *u8) -> i64 { 128 let fd: i64 = sys_openat_rdwr(path, IG_MODE_X) 129 if fd < 0 { return 0 - 1 } 130 let end: i64 = sys_lseek(fd, 0, 2) 131 let b: *u8 = sys_mmap(2) 132 b[0] = 0 as u8 133 let w: i64 = sys_write(fd, b, 1) 134 sys_close(fd) 135 if w != 1 { return 0 - 1 } 136 return end + 1 137} 138 139// ---- fixture sources, assembled at RUNTIME (a detector that scans source finds its own fixture) -------- 140func ig_write_lib(v: i64) -> i64 { 141 let t: *u8 = sys_mmap(IG_PATH) 142 var o: i64 = gk_cat(t, 0, "import \"nx_syscalls.nx\"" as *u8) 143 t[o] = 10 as u8; o = o + 1 144 o = gk_cat(t, o, "func icfx_val() -> i64 { return " as *u8) 145 o = gk_catn(t, o, v) 146 o = gk_cat(t, o, " }" as *u8) 147 t[o] = 10 as u8; o = o + 1 148 t[o] = 0 as u8 149 let p: *u8 = ig_sub(igp_hdl, "/icfx_lib.nx" as *u8) 150 if gk_write(p, t) < 0 { return 0 } 151 return 1 152} 153func ig_write_prog(name: *u8, letter: *u8, uses_lib: i64) -> i64 { 154 let t: *u8 = sys_mmap(IG_PATH) 155 var o: i64 = gk_cat(t, 0, "import \"nx_syscalls.nx\"" as *u8) 156 t[o] = 10 as u8; o = o + 1 157 if uses_lib == 1 { 158 o = gk_cat(t, o, "import \"icfx_lib.nx\"" as *u8) 159 t[o] = 10 as u8; o = o + 1 160 } 161 o = gk_cat(t, o, "func main(argc: i64, argv: *i64) -> i64 {" as *u8) 162 t[o] = 10 as u8; o = o + 1 163 o = gk_cat(t, o, " sys_write(1, \"" as *u8) 164 o = gk_cat(t, o, letter) 165 o = gk_cat(t, o, "\" as *u8, 1)" as *u8) 166 t[o] = 10 as u8; o = o + 1 167 if uses_lib == 1 { o = gk_cat(t, o, " return icfx_val()" as *u8) } else { o = gk_cat(t, o, " return 7" as *u8) } 168 t[o] = 10 as u8; o = o + 1 169 o = gk_cat(t, o, "}" as *u8) 170 t[o] = 10 as u8; o = o + 1 171 t[o] = 0 as u8 172 let p: *u8 = igp_mk(igp(igp_hdl), name) 173 if gk_write(p as *u8, t) < 0 { return 0 } 174 return 1 175} 176 177// ---- drive the subject ------------------------------------------------------------------------------ 178func ig_run(subject: *u8, a1: *u8, a2: *u8, a3: *u8, a4: *u8, a5: *u8, a6: *u8, out: *u8, ol: *i64) -> i64 { 179 let av: *i64 = sys_mmap(8*9) as *i64 180 var k: i64 = 0 181 av[k] = subject as i64; k = k + 1 182 if (a1 as i64) != 0 { av[k] = a1 as i64; k = k + 1 } 183 if (a2 as i64) != 0 { av[k] = a2 as i64; k = k + 1 } 184 if (a3 as i64) != 0 { av[k] = a3 as i64; k = k + 1 } 185 if (a4 as i64) != 0 { av[k] = a4 as i64; k = k + 1 } 186 if (a5 as i64) != 0 { av[k] = a5 as i64; k = k + 1 } 187 if (a6 as i64) != 0 { av[k] = a6 as i64; k = k + 1 } 188 av[k] = 0 189 ol[0] = 0 190 return tr_run_capture_to(subject, av, out, IG_OUT, ol, IG_TMO) 191} 192func ig_show(cond: i64, out: *u8, n: i64) -> i64 { 193 if cond == 1 { return 0 } 194 var m: i64 = n 195 if m > IG_SHOW { m = IG_SHOW } 196 ig_p(" ---- subject output (head) ----" as *u8); ig_nl() 197 sys_write(1, out, m) 198 ig_nl() 199 return 0 200} 201// copy the 64 hex chars that follow `needle` into keyout; 1 ok / 0 absent 202func ig_after_key(out: *u8, n: i64, needle: *u8, keyout: *u8) -> i64 { 203 let p: i64 = gk_out_pos(out, n, needle) 204 if p < 0 { keyout[0] = 0 as u8; return 0 } 205 let off: i64 = p + gk_len(needle) 206 if off + IG_SHA_HEX > n { keyout[0] = 0 as u8; return 0 } 207 var i: i64 = 0 208 while i < IG_SHA_HEX { keyout[i] = out[off+i]; i = i + 1 } 209 keyout[IG_SHA_HEX] = 0 as u8 210 return 1 211} 212func ig_wipe_index() -> i64 { return sys_unlinkat(igp(igp_idx)) } 213func ig_rm_asm(name: *u8) -> i64 { return sys_unlinkat(igp_mk(igp(igp_bld), name) as *u8) } 214 215func main(argc: i64, argv: *i64) -> i64 { 216 gv_head("nx_inc_compile_gate -- LN10 per-module incremental cache: a one-file edit rebuilds ONLY its dependents, and the incremental artifact is byte-identical to a full build" as *u8) 217 let ctr: *i64 = gv_ctr() 218 219 let rt0: *u8 = sys_mmap(IG_PATH) 220 if gv_need("source tree resolvable (gk_corpus_root)" as *u8, gk_corpus_root(rt0), ctr) == 0 { 221 return gv_verdict("nx_inc_compile_gate" as *u8, ctr, "no tree" as *u8) 222 } 223 let rtl: i64 = gk_len(rt0) 224 let buildroot: *u8 = sys_mmap(IG_PATH) 225 var bo: i64 = 0 226 while bo < rtl - 8 { buildroot[bo] = rt0[bo]; bo = bo + 1 } 227 buildroot[bo] = 0 as u8 228 let nishihost: *u8 = sys_mmap(IG_PATH) 229 var ho: i64 = 0 230 if bo > 10 { while ho < bo - 10 { nishihost[ho] = buildroot[ho]; ho = ho + 1 } } 231 nishihost[ho] = 0 as u8 232 233 let subject: *u8 = sys_mmap(IG_PATH) 234 var chosen: *u8 = "argv" as *u8 235 if argc >= 2 { 236 let a1: *u8 = argv[1] as *u8 237 var so: i64 = 0 238 if a1[0] != (47 as u8) { 239 let here: *u8 = sys_mmap(IG_PATH) 240 sys_getcwd(here, IG_PATH) 241 so = gk_cat(subject, 0, here); subject[so] = 47 as u8; so = so + 1 242 } 243 so = gk_cat(subject, so, a1); subject[so] = 0 as u8 244 } 245 if argc < 2 { 246 var so: i64 = gk_cat(subject, 0, nishihost); so = gk_cat(subject, so, "nx_inc_compile.elf" as *u8); subject[so] = 0 as u8 247 chosen = "live(nishihost)" as *u8 248 if gk_exists(subject) == 0 { 249 so = gk_cat(subject, 0, buildroot); so = gk_cat(subject, so, "_build/nx_inc_compile.sov.elf" as *u8); subject[so] = 0 as u8 250 chosen = "built(_build)" as *u8 251 } 252 } 253 let subj_sha: *u8 = sys_mmap(IG_SHA_HEX + 2) 254 let have_subj: i64 = ig_sha(subject, subj_sha) 255 ig_p(" subject=" as *u8); ig_p(subject); ig_p(" [" as *u8); ig_p(chosen); ig_p("] sha256=" as *u8) 256 if have_subj == 1 { ig_p(subj_sha) } else { ig_p("UNREADABLE" as *u8) } 257 ig_nl() 258 if gv_need("subject nx_inc_compile readable" as *u8, have_subj, ctr) == 0 { 259 return gv_verdict("nx_inc_compile_gate" as *u8, ctr, "no subject" as *u8) 260 } 261 262 igp_init(buildroot) 263 ig_p(" fixture=" as *u8); ig_p(igp(igp_fx)); ig_nl() 264 gk_mkdir(igp(igp_fx)); gk_mkdir(igp(igp_tree)); gk_mkdir(igp(igp_know)) 265 gk_mkdir(igp(igp_rt)); gk_mkdir(igp(igp_hdl)); gk_mkdir(igp(igp_offc)); gk_mkdir(igp(igp_bld)) 266 gk_mkdir(igp(igp_cache)) 267 ig_wipe_index() 268 sys_unlinkat(ig_sub(igp_bld, "/icfx_a.s" as *u8)) 269 sys_unlinkat(ig_sub(igp_bld, "/icfx_b.s" as *u8)) 270 sys_unlinkat(ig_sub(igp_bld, "/icfx_c.s" as *u8)) 271 sys_unlinkat(ig_sub(igp_bld, "/icfx_a.sov.elf" as *u8)) 272 sys_unlinkat(ig_sub(igp_bld, "/icfx_b.sov.elf" as *u8)) 273 sys_unlinkat(ig_sub(igp_bld, "/icfx_c.sov.elf" as *u8)) 274 sys_unlinkat(ig_sub(igp_bld, "/icfx_a.lock" as *u8)) 275 sys_unlinkat(ig_sub(igp_bld, "/icfx_b.lock" as *u8)) 276 sys_unlinkat(ig_sub(igp_bld, "/icfx_c.lock" as *u8)) 277 278 // fixture confs: B2 OFF so its whole-target cache cannot serve the hit LN10 is being credited with 279 gk_write(ig_sub(igp_know, "/buildcache.conf" as *u8), "cache_enabled=0\n" as *u8) 280 gk_write(ig_sub(igp_know, "/inccache.conf" as *u8), "cache_enabled=1\ncache_max_bytes=1048576\nruler_timeout_ms=120000\nbuild_timeout_ms=900000\ncrash_guard=nx_crash.nx\ncompiler_path=_offc/nx_cc_sovereign.elf\nassembler_path=_offc/nxasm_x86_main.elf\n" as *u8) 281 282 var ok_src: i64 = 1 283 let p1: *u8 = sys_mmap(IG_PATH) 284 var o1: i64 = gk_cat(p1, 0, rt0); o1 = gk_cat(p1, o1, "nx_syscalls.nx" as *u8); p1[o1] = 0 as u8 285 if ig_copy(p1, ig_sub(igp_rt, "/nx_syscalls.nx" as *u8), IG_MODE_R) == 0 { ok_src = 0 } 286 o1 = gk_cat(p1, 0, rt0); o1 = gk_cat(p1, o1, "nx_tier.nx" as *u8); p1[o1] = 0 as u8 287 if ig_copy(p1, ig_sub(igp_rt, "/nx_tier.nx" as *u8), IG_MODE_R) == 0 { ok_src = 0 } 288 o1 = gk_cat(p1, 0, rt0); o1 = gk_cat(p1, o1, "nx_crash.nx" as *u8); p1[o1] = 0 as u8 289 if ig_copy(p1, ig_sub(igp_rt, "/nx_crash.nx" as *u8), IG_MODE_R) == 0 { ok_src = 0 } 290 if gv_need("fixture closure sources copied (nx_syscalls, nx_tier, nx_crash)" as *u8, ok_src, ctr) == 0 { 291 return gv_verdict("nx_inc_compile_gate" as *u8, ctr, "fixture" as *u8) 292 } 293 var ok_tc: i64 = 1 294 let p2: *u8 = sys_mmap(IG_PATH) 295 var o2: i64 = gk_cat(p2, 0, buildroot); o2 = gk_cat(p2, o2, "_offc/nx_cc_sovereign.elf" as *u8); p2[o2] = 0 as u8 296 let cc_real: *u8 = sys_mmap(IG_SHA_HEX + 2) 297 let cc_copy: *u8 = sys_mmap(IG_SHA_HEX + 2) 298 if ig_sha(p2, cc_real) == 0 { ok_tc = 0 } 299 if ig_copy(p2, igp(igp_cc), IG_MODE_X) == 0 { ok_tc = 0 } 300 o2 = gk_cat(p2, 0, buildroot); o2 = gk_cat(p2, o2, "_offc/nxasm_x86_main.elf" as *u8); p2[o2] = 0 as u8 301 if ig_copy(p2, igp(igp_asm), IG_MODE_X) == 0 { ok_tc = 0 } 302 o2 = gk_cat(p2, 0, nishihost); o2 = gk_cat(p2, o2, "nx_closurehash.elf" as *u8); p2[o2] = 0 as u8 303 if ig_copy(p2, igp(igp_ruler), IG_MODE_X) == 0 { ok_tc = 0 } 304 o2 = gk_cat(p2, 0, nishihost); o2 = gk_cat(p2, o2, "nx_sov_build_run.elf" as *u8); p2[o2] = 0 as u8 305 if ig_copy(p2, igp(igp_builder), IG_MODE_X) == 0 { ok_tc = 0 } 306 if gv_need("fixture _offc holds copies of the compiler, assembler, closure ruler and builder" as *u8, ok_tc, ctr) == 0 { 307 return gv_verdict("nx_inc_compile_gate" as *u8, ctr, "fixture" as *u8) 308 } 309 var cc_same: i64 = 0 310 if ig_sha(igp(igp_cc), cc_copy) == 1 { cc_same = ig_hexeq(cc_real, cc_copy) } 311 gv_check("fixture-reached: the fixture compiler copy re-hashes to the live compiler" as *u8, cc_same, ctr) 312 313 ig_write_lib(IG_VAL_A) 314 ig_write_prog("/icfx_a.nx" as *u8, "a" as *u8, 1) 315 ig_write_prog("/icfx_c.nx" as *u8, "c" as *u8, 1) 316 ig_write_prog("/icfx_b.nx" as *u8, "b" as *u8, 0) 317 gv_check("fixture-reached: three fixture targets written, TWO importing the shared module and one NOT" as *u8, gk_exists(ig_sub(igp_hdl, "/icfx_b.nx" as *u8)), ctr) 318 319 let out: *u8 = sys_mmap(IG_OUT + 16) 320 let ol: *i64 = sys_mmap(16) as *i64 321 let ra: *u8 = igp(igp_rootarg) 322 let sA1: *u8 = sys_mmap(IG_SHA_HEX + 2) 323 let sB1: *u8 = sys_mmap(IG_SHA_HEX + 2) 324 let sC1: *u8 = sys_mmap(IG_SHA_HEX + 2) 325 let sA2: *u8 = sys_mmap(IG_SHA_HEX + 2) 326 let sB2: *u8 = sys_mmap(IG_SHA_HEX + 2) 327 let sA3: *u8 = sys_mmap(IG_SHA_HEX + 2) 328 let kA: *u8 = sys_mmap(IG_SHA_HEX + 2) 329 let kR: *u8 = sys_mmap(IG_SHA_HEX + 2) 330 let eA: *u8 = ig_sub(igp_bld, "/icfx_a.sov.elf" as *u8) 331 let eB: *u8 = ig_sub(igp_bld, "/icfx_b.sov.elf" as *u8) 332 let eC: *u8 = ig_sub(igp_bld, "/icfx_c.sov.elf" as *u8) 333 let asmA: *u8 = ig_sub(igp_bld, "/icfx_a.s" as *u8) 334 let asmB: *u8 = ig_sub(igp_bld, "/icfx_b.s" as *u8) 335 let asmC: *u8 = ig_sub(igp_bld, "/icfx_c.s" as *u8) 336 337 // ---- T1: cold -- every target NEW, every target compiled ------------------------------------------- 338 var rc: i64 = ig_run(subject, "build" as *u8, "icfx_a" as *u8, "icfx_b" as *u8, "icfx_c" as *u8, ra, 0 as *u8, out, ol) 339 var c: i64 = (rc == 0) as i64 340 gv_check("T1 cold build: subject exit 0" as *u8, c, ctr); ig_show(c, out, ol[0]) 341 gv_check("T1 cold build: every target reports NEW reason=no-record" as *u8, gk_out_has(out, ol[0], "INC icfx_a NEW reason=no-record" as *u8) * gk_out_has(out, ol[0], "INC icfx_b NEW reason=no-record" as *u8) * gk_out_has(out, ol[0], "INC icfx_c NEW reason=no-record" as *u8), ctr) 342 gv_check("T1 cold build: the compiler RAN for all three (the .s witness the hit teeth depend on)" as *u8, gk_exists(asmA) * gk_exists(asmB) * gk_exists(asmC), ctr) 343 gv_check("T1 cold build: all three artifacts present" as *u8, gk_exists(eA) * gk_exists(eB) * gk_exists(eC), ctr) 344 gv_check("T1 cold build: all three artifacts hashable" as *u8, ig_sha(eA, sA1) * ig_sha(eB, sB1) * ig_sha(eC, sC1), ctr) 345 gv_check("T1 cold build: the index was written" as *u8, gk_exists(igp(igp_idx)), ctr) 346 347 // ---- T2: warm, nothing changed -- every target HIT and NOTHING is compiled ------------------------ 348 sys_unlinkat(asmA); sys_unlinkat(asmB); sys_unlinkat(asmC) 349 let pre_absent: i64 = ((gk_exists(asmA) == 0) as i64) * ((gk_exists(asmB) == 0) as i64) * ((gk_exists(asmC) == 0) as i64) 350 gv_check("fixture-reached-T2: the .s witnesses really were removed before the warm run" as *u8, pre_absent, ctr) 351 rc = ig_run(subject, "build" as *u8, "icfx_a" as *u8, "icfx_b" as *u8, "icfx_c" as *u8, ra, 0 as *u8, out, ol) 352 c = (rc == 0) as i64 353 gv_check("T2 warm rebuild: subject exit 0" as *u8, c, ctr); ig_show(c, out, ol[0]) 354 gv_check("T2 warm rebuild: every target HITs" as *u8, gk_out_has(out, ol[0], "INC icfx_a HIT key=" as *u8) * gk_out_has(out, ol[0], "INC icfx_b HIT key=" as *u8) * gk_out_has(out, ol[0], "INC icfx_c HIT key=" as *u8), ctr) 355 gv_check("T2 warm rebuild: the builder was NOT forked for any of them" as *u8, gk_out_has(out, ol[0], "INC-SKIP icfx_a" as *u8) * gk_out_has(out, ol[0], "INC-SKIP icfx_b" as *u8) * gk_out_has(out, ol[0], "INC-SKIP icfx_c" as *u8), ctr) 356 gv_check("T2 warm rebuild: ZERO compiler invocations (every .s stays ABSENT)" as *u8, ((gk_exists(asmA) == 0) as i64) * ((gk_exists(asmB) == 0) as i64) * ((gk_exists(asmC) == 0) as i64), ctr) 357 gv_check("T2 warm rebuild: artifacts byte-identical to the cold build" as *u8, ig_sha(eA, sA2) * ig_hexeq(sA1, sA2), ctr) 358 359 // ---- T3: ONE-FILE edit of the shared module -- plan names EXACTLY the dependents ------------------- 360 ig_write_lib(IG_VAL_B) 361 rc = ig_run(subject, "plan" as *u8, "icfx_a" as *u8, "icfx_b" as *u8, "icfx_c" as *u8, ra, 0 as *u8, out, ol) 362 c = (rc == 0) as i64 363 gv_check("T3 one-file edit: plan exit 0" as *u8, c, ctr); ig_show(c, out, ol[0]) 364 gv_check("T3 one-file edit: the DEPENDENTS miss and name the module that moved" as *u8, gk_out_has(out, ol[0], "INC icfx_a MISS reason=module-bytes-moved" as *u8) * gk_out_has(out, ol[0], "INC icfx_c MISS reason=module-bytes-moved" as *u8) * gk_out_has(out, ol[0], "INC-DIRTY icfx_a icfx_lib.nx" as *u8) * gk_out_has(out, ol[0], "INC-DIRTY icfx_c icfx_lib.nx" as *u8), ctr) 365 gv_check("T3 one-file edit: the NON-dependent still HITs -- an edit does not invalidate the estate" as *u8, gk_out_has(out, ol[0], "INC icfx_b HIT key=" as *u8), ctr) 366 gv_check("neg-control-T3-plan-does-not-build: plan forked no builder (no INC-BUILD line) and left the .s absent" as *u8, ((gk_out_has(out, ol[0], "INC-BUILD" as *u8) == 0) as i64) * ((gk_exists(asmA) == 0) as i64), ctr) 367 368 // ---- T4: build after that edit -- ONLY the dependents are compiled --------------------------------- 369 rc = ig_run(subject, "build" as *u8, "icfx_a" as *u8, "icfx_b" as *u8, "icfx_c" as *u8, ra, 0 as *u8, out, ol) 370 c = (rc == 0) as i64 371 gv_check("T4 incremental build: subject exit 0" as *u8, c, ctr); ig_show(c, out, ol[0]) 372 gv_check("T4 incremental build: the dependents WERE compiled (.s present for a and c)" as *u8, gk_exists(asmA) * gk_exists(asmC), ctr) 373 gv_check("T4 incremental build: the NON-dependent was NOT compiled (.s for b stays ABSENT)" as *u8, (gk_exists(asmB) == 0) as i64, ctr) 374 gv_check("T4 incremental build: the non-dependent artifact is untouched, byte-identical to T1" as *u8, ig_sha(eB, sB2) * ig_hexeq(sB1, sB2), ctr) 375 var moved_a: i64 = 0 376 if ig_sha(eA, sA2) == 1 { moved_a = (ig_hexeq(sA1, sA2) == 0) as i64 } 377 gv_check("T4 incremental build: the dependent artifact CHANGED (the edit reached the codegen)" as *u8, moved_a, ctr) 378 379 // ---- T5: THE LOAD-BEARING TOOTH -- the incremental artifact equals a FULL build -------------------- 380 // The incremental result is banked, then the cache is destroyed and the SAME source is built from 381 // scratch with the cache disabled. Byte equality is the whole promise of a build cache; a cache that 382 // returns a stale object is a silent miscompile, so this is the tooth the rung stands on. 383 ig_wipe_index() 384 sys_unlinkat(asmA) 385 sys_unlinkat(eA) 386 let clean: i64 = ((gk_exists(igp(igp_idx)) == 0) as i64) * ((gk_exists(asmA) == 0) as i64) * ((gk_exists(eA) == 0) as i64) 387 gv_check("fixture-reached-T5: cache, asm and artifact all removed before the full build" as *u8, clean, ctr) 388 rc = ig_run(subject, "build" as *u8, "icfx_a" as *u8, ra, "--no-cache" as *u8, 0 as *u8, 0 as *u8, out, ol) 389 c = (rc == 0) as i64 390 gv_check("T5 full build: subject exit 0 with the cache disabled" as *u8, c, ctr); ig_show(c, out, ol[0]) 391 gv_check("fixture-reached-T5: the full build really compiled (.s present)" as *u8, gk_exists(asmA), ctr) 392 gv_check("T5 BYTE IDENTITY: the artifact the incremental path left in place equals the full build, byte for byte" as *u8, ig_sha(eA, sA3) * ig_hexeq(sA2, sA3), ctr) 393 394 // ---- T6: neg-controls ------------------------------------------------------------------------------ 395 rc = ig_run(subject, "seed" as *u8, "icfx_a" as *u8, "icfx_b" as *u8, "icfx_c" as *u8, ra, 0 as *u8, out, ol) 396 gv_check("fixture-reached-T6: cache re-seeded for all three targets" as *u8, (rc == 0) as i64 * gk_exists(igp(igp_idx)), ctr) 397 rc = ig_run(subject, "plan" as *u8, "icfx_a" as *u8, ra, "--no-cache" as *u8, 0 as *u8, 0 as *u8, out, ol) 398 gv_check("neg-control-disabled-cache-never-hits: an UNCHANGED tree under --no-cache must MISS, and must not print HIT" as *u8, gk_out_has(out, ol[0], "INC icfx_a MISS reason=disabled-by-flag-or-conf" as *u8) * ((gk_out_has(out, ol[0], "INC icfx_a HIT" as *u8) == 0) as i64), ctr) 399 ig_write_lib(IG_VAL_C) 400 rc = ig_run(subject, "plan" as *u8, "icfx_a" as *u8, ra, 0 as *u8, 0 as *u8, 0 as *u8, out, ol) 401 gv_check("neg-control-changed-input-must-miss: after a one-line edit the HIT detector stays SILENT for the dependent" as *u8, (gk_out_has(out, ol[0], "INC icfx_a HIT" as *u8) == 0) as i64, ctr) 402 403 // ---- T7: the stale-key class -- recording ONE target must invalidate its SIBLING dependents --------- 404 // This is the subtlest way a shared per-module table can produce a FALSE HIT: target A is rebuilt and 405 // records the module's new sha, and target C -- which was never rebuilt -- would then compare its 406 // members against that NEW sha and read as clean while its recorded key describes the OLD tree. 407 rc = ig_run(subject, "build" as *u8, "icfx_a" as *u8, ra, 0 as *u8, 0 as *u8, 0 as *u8, out, ol) 408 c = (rc == 0) as i64 409 gv_check("T7 sibling invalidation: building only the first dependent exits 0" as *u8, c, ctr); ig_show(c, out, ol[0]) 410 gv_check("T7 sibling invalidation: recording the new module sha ANNOUNCES the sibling it invalidated" as *u8, gk_out_has(out, ol[0], "INC-INVALIDATE icfx_c by=icfx_lib.nx" as *u8), ctr) 411 rc = ig_run(subject, "plan" as *u8, "icfx_c" as *u8, ra, 0 as *u8, 0 as *u8, 0 as *u8, out, ol) 412 gv_check("neg-control-T7-no-false-hit: the un-rebuilt sibling must NOT read as a hit" as *u8, (gk_out_has(out, ol[0], "INC icfx_c HIT" as *u8) == 0) as i64, ctr); ig_show((gk_out_has(out, ol[0], "INC icfx_c HIT" as *u8) == 0) as i64, out, ol[0]) 413 414 // ---- T8: the key is the RULER'S key, not a second one --------------------------------------------- 415 rc = ig_run(subject, "seed" as *u8, "icfx_a" as *u8, ra, 0 as *u8, 0 as *u8, 0 as *u8, out, ol) 416 let got_k: i64 = ig_after_key(out, ol[0], "INC-RECORD icfx_a key=" as *u8, kA) 417 let rav: *i64 = sys_mmap(8*4) as *i64 418 rav[0] = igp_ruler 419 rav[1] = "icfx_a" as *u8 as i64 420 rav[2] = igp_tree 421 rav[3] = 0 422 let rol: *i64 = sys_mmap(16) as *i64 423 rol[0] = 0 424 let rout: *u8 = sys_mmap(IG_OUT + 16) 425 tr_run_capture_to(igp(igp_ruler), rav, rout, IG_OUT, rol, IG_TMO) 426 let got_r: i64 = ig_after_key(rout, rol[0], "closure_sha=" as *u8, kR) 427 ig_p(" recorded key=" as *u8); ig_p(kA); ig_p(" ruler key=" as *u8); ig_p(kR); ig_nl() 428 gv_check("T8 ONE RULER: the key this cache records is byte-identical to nx_closurehash's own closure_sha" as *u8, got_k * got_r * ig_hexeq(kA, kR), ctr) 429 430 // ---- T9: a corrupt index is REFUSED WHOLE, never half-trusted -------------------------------------- 431 let ifd: i64 = sys_openat_append(igp(igp_idx), IG_MODE_R) 432 var appended: i64 = 0 433 if ifd >= 0 { 434 let bad: *u8 = "t|icfx_zz|deadbeef|-|0|0|0\n" as *u8 435 if gk_write_all(ifd, bad, gk_len(bad)) > 0 { appended = 1 } 436 sys_close(ifd) 437 } 438 gv_check("fixture-reached-T9: a malformed row really was appended to the index" as *u8, appended, ctr) 439 rc = ig_run(subject, "plan" as *u8, "icfx_a" as *u8, ra, 0 as *u8, 0 as *u8, 0 as *u8, out, ol) 440 gv_check("T9 corrupt index: REFUSED whole and restarted cold -- a half-parsed member list is a false HIT waiting" as *u8, gk_out_has(out, ol[0], "index REFUSED as corrupt" as *u8) * gk_out_has(out, ol[0], "INC icfx_a NEW" as *u8), ctr); ig_show(gk_out_has(out, ol[0], "index REFUSED as corrupt" as *u8), out, ol[0]) 441 442 // ---- T10: the toolchain axis the ruler cannot see -------------------------------------------------- 443 ig_wipe_index() 444 rc = ig_run(subject, "seed" as *u8, "icfx_a" as *u8, ra, 0 as *u8, 0 as *u8, 0 as *u8, out, ol) 445 gv_check("fixture-reached-T10: re-seeded on a clean index" as *u8, (rc == 0) as i64, ctr) 446 rc = ig_run(subject, "plan" as *u8, "icfx_a" as *u8, ra, 0 as *u8, 0 as *u8, 0 as *u8, out, ol) 447 gv_check("fixture-reached-T10: the target HITs before the toolchain is touched" as *u8, gk_out_has(out, ol[0], "INC icfx_a HIT" as *u8), ctr) 448 let cc_before: i64 = gk_size(igp(igp_cc)) 449 let cc_after: i64 = ig_append_byte(igp(igp_cc)) 450 gv_check("fixture-reached-T10: the fixture compiler is one byte longer" as *u8, (cc_after == cc_before + 1) as i64, ctr) 451 rc = ig_run(subject, "plan" as *u8, "icfx_a" as *u8, ra, 0 as *u8, 0 as *u8, 0 as *u8, out, ol) 452 gv_check("T10 toolchain axis: a compiler-byte change MISSES on unchanged sources and names the compiler" as *u8, gk_out_has(out, ol[0], "INC icfx_a MISS reason=module-bytes-moved" as *u8) * gk_out_has(out, ol[0], "INC-DIRTY icfx_a _offc/nx_cc_sovereign.elf" as *u8), ctr); ig_show(gk_out_has(out, ol[0], "INC-DIRTY icfx_a _offc/nx_cc_sovereign.elf" as *u8), out, ol[0]) 453 454 return gv_verdict("nx_inc_compile_gate" as *u8, ctr, "LN10 inc_cache_lookup" as *u8) 455}