kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit 8b8690a117ea8e74ec13bbd64458f29402317ed1
parent 70e13d667703d79d7c61f6d01569038e9e0e88d4
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Fri, 19 Jun 2026 10:49:03 -0700

Validate deepset source and glob consistency

Diffstat:
Msrc/build/resolve.c | 197+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 197 insertions(+), 0 deletions(-)

diff --git a/src/build/resolve.c b/src/build/resolve.c @@ -3,6 +3,9 @@ #include "remote.h" #include "runner.h" +#undef KIT_TRACE_MODULE +#define KIT_TRACE_MODULE "build/resolve" + #include <stdio.h> #include <string.h> @@ -58,6 +61,168 @@ static int config_observation_match(const BuildConfig* cfg, return build_id_eq(value_hash, obs->value_hash); } +typedef struct BuildSeenId { + uint8_t id[BUILD_HASH_LEN]; +} BuildSeenId; + +typedef struct BuildLeafsetCheck { + KitHeap* heap; + BuildSourceLeaf* sources; + size_t n_sources; + size_t cap_sources; + BuildGlobLeaf* globs; + size_t n_globs; + size_t cap_globs; + BuildSeenId* visited; + size_t n_visited; + size_t cap_visited; + char* err; + size_t errcap; +} BuildLeafsetCheck; + +static int leafset_check_err(BuildLeafsetCheck* ck, const char* what, + const char* key) { + if (ck && ck->err && ck->errcap) { + if (key) + snprintf(ck->err, ck->errcap, "%s: %s", what, key); + else + snprintf(ck->err, ck->errcap, "%s", what); + } + return BUILD_ERR; +} + +static int leafset_check_grow(BuildLeafsetCheck* ck, void** ptr, size_t* cap, + size_t want, size_t elem_size, + size_t elem_align) { + size_t newcap, old_size, new_size; + void* fresh; + if (!ck || !ck->heap || !ptr || !cap || elem_size == 0u) return BUILD_ERR; + if (*cap >= want) return BUILD_OK; + newcap = *cap ? *cap : 16u; + while (newcap < want) { + if (newcap > ((size_t)-1) / 2u) + return leafset_check_err(ck, "deepset consistency table too large", NULL); + newcap *= 2u; + } + if (newcap > ((size_t)-1) / elem_size) + return leafset_check_err(ck, "deepset consistency table too large", NULL); + old_size = *cap * elem_size; + new_size = newcap * elem_size; + fresh = ck->heap->realloc(ck->heap, *ptr, old_size, new_size, elem_align); + if (!fresh) return leafset_check_err(ck, "out of memory", NULL); + *ptr = fresh; + *cap = newcap; + return BUILD_OK; +} + +static int leafset_check_source(BuildLeafsetCheck* ck, + const BuildSourceLeaf* row) { + size_t i; + if (!ck || !row) return BUILD_ERR; + for (i = 0; i < ck->n_sources; ++i) { + BuildSourceLeaf* seen = &ck->sources[i]; + if (strcmp(seen->path, row->path) != 0) continue; + if (seen->absent != row->absent) + return leafset_check_err(ck, "inconsistent source observation", + row->path); + if (!row->absent && !build_id_eq(seen->blob, row->blob)) + return leafset_check_err(ck, "inconsistent source observation", + row->path); + return BUILD_OK; + } + if (leafset_check_grow(ck, (void**)&ck->sources, &ck->cap_sources, + ck->n_sources + 1u, sizeof ck->sources[0], + _Alignof(BuildSourceLeaf)) != BUILD_OK) + return BUILD_ERR; + ck->sources[ck->n_sources++] = *row; + return BUILD_OK; +} + +static int leafset_check_glob(BuildLeafsetCheck* ck, const BuildGlobLeaf* row) { + size_t i; + if (!ck || !row) return BUILD_ERR; + for (i = 0; i < ck->n_globs; ++i) { + BuildGlobLeaf* seen = &ck->globs[i]; + if (strcmp(seen->pattern, row->pattern) != 0) continue; + if (!build_id_eq(seen->result_hash, row->result_hash)) + return leafset_check_err(ck, "inconsistent glob observation", + row->pattern); + return BUILD_OK; + } + if (leafset_check_grow(ck, (void**)&ck->globs, &ck->cap_globs, + ck->n_globs + 1u, sizeof ck->globs[0], + _Alignof(BuildGlobLeaf)) != BUILD_OK) + return BUILD_ERR; + ck->globs[ck->n_globs++] = *row; + return BUILD_OK; +} + +static int leafset_check_mark_visited(BuildLeafsetCheck* ck, + const uint8_t id[BUILD_HASH_LEN], + int* already_seen) { + size_t i; + if (!ck || !id || !already_seen) return BUILD_ERR; + *already_seen = 0; + for (i = 0; i < ck->n_visited; ++i) { + if (build_id_eq(ck->visited[i].id, id)) { + *already_seen = 1; + return BUILD_OK; + } + } + if (leafset_check_grow(ck, (void**)&ck->visited, &ck->cap_visited, + ck->n_visited + 1u, sizeof ck->visited[0], + _Alignof(BuildSeenId)) != BUILD_OK) + return BUILD_ERR; + memcpy(ck->visited[ck->n_visited++].id, id, BUILD_HASH_LEN); + return BUILD_OK; +} + +static int leafset_check_walk(BuildLeafsetCheck* ck, const BuildLeafSet* node, + int mark_self) { + size_t i; + if (!ck || !node) return BUILD_ERR; + if (mark_self) { + int already_seen = 0; + if (leafset_check_mark_visited(ck, node->id, &already_seen) != BUILD_OK) + return BUILD_ERR; + if (already_seen) return BUILD_OK; + } + for (i = 0; i < node->n_sources; ++i) { + if (leafset_check_source(ck, &node->sources[i]) != BUILD_OK) + return BUILD_ERR; + } + for (i = 0; i < node->n_globs; ++i) { + if (leafset_check_glob(ck, &node->globs[i]) != BUILD_OK) + return BUILD_ERR; + } + for (i = 0; i < node->n_children; ++i) { + if (leafset_check_walk(ck, node->children[i], 1) != BUILD_OK) + return BUILD_ERR; + } + return BUILD_OK; +} + +static int build_leafset_check_consistent(KitBuildCoordinator* c, + const BuildLeafSet* root, + int root_has_id, char* err, + size_t errcap) { + BuildLeafsetCheck ck; + int ok; + if (!c || !root) return BUILD_ERR; + memset(&ck, 0, sizeof ck); + ck.heap = c->ctx->heap; + ck.err = err; + ck.errcap = errcap; + ok = leafset_check_walk(&ck, root, root_has_id); + if (ck.sources) + ck.heap->free(ck.heap, ck.sources, ck.cap_sources * sizeof ck.sources[0]); + if (ck.globs) + ck.heap->free(ck.heap, ck.globs, ck.cap_globs * sizeof ck.globs[0]); + if (ck.visited) + ck.heap->free(ck.heap, ck.visited, ck.cap_visited * sizeof ck.visited[0]); + return ok; +} + static int shallow_direct_match(KitBuildCoordinator* c, const BuildShallowTrace* st, const BuildConfig* cfg, @@ -417,11 +582,14 @@ scan_record: BUILD_OK) continue; memset(&dt, 0, sizeof dt); + err[0] = '\0'; if (build_deep_parse(fd.data, fd.size, &dt, NULL, 0) == BUILD_OK && strlen(dt.target) == target.len && memcmp(dt.target, target.s, target.len) == 0 && build_id_eq(dt.argv, argv_id) && build_coord_deepset_load(c, dt.deepset, &leaf) == BUILD_OK && + build_leafset_check_consistent(c, leaf, 1, err, sizeof err) == + BUILD_OK && build_leafset_refresh(c, leaf, cfg, &match) == BUILD_OK && match && build_materialize(c, dt.output, out->path, sizeof out->path) == BUILD_OK) { @@ -436,6 +604,9 @@ scan_record: build_coord_stat_bump(c, BUILD_STAT_DEEP_HIT); return BUILD_OK; } + if (err[0]) + KIT_LOGD("skip deep trace for %.*s: %s", KIT_SLICE_ARG(target), err); + err[0] = '\0'; build_store_release(&c->store, &fd); } for (i = 0; i < rec.n_rows; ++i) { @@ -536,11 +707,14 @@ scan_record: BUILD_OK) continue; memset(&dt, 0, sizeof dt); + err[0] = '\0'; if (build_test_deep_parse(fd.data, fd.size, &dt, NULL, 0) == BUILD_OK && strlen(dt.target) == target.len && memcmp(dt.target, target.s, target.len) == 0 && build_id_eq(dt.argv, argv_id) && build_coord_deepset_load(c, dt.deepset, &leaf) == BUILD_OK && + build_leafset_check_consistent(c, leaf, 1, err, sizeof err) == + BUILD_OK && build_leafset_refresh(c, leaf, cfg, &match) == BUILD_OK && match && build_materialize(c, dt.output, out->path, sizeof out->path) == BUILD_OK) { @@ -558,6 +732,10 @@ scan_record: build_coord_stat_bump(c, BUILD_STAT_TEST_CACHE_HIT); return BUILD_OK; } + if (err[0]) + KIT_LOGD("skip test deep trace for %.*s: %s", KIT_SLICE_ARG(target), + err); + err[0] = '\0'; build_store_release(&c->store, &fd); } for (i = 0; i < rec.n_rows; ++i) { @@ -647,6 +825,7 @@ int build_leafset_union(KitBuildCoordinator* c, const BuildLeafSet* direct, const BuildLeafSet** out) { BuildDeepSet ds; BuildLeafSet leaf; + BuildLeafSet check_root; uint8_t(*child_ids)[BUILD_HASH_LEN] = NULL; const BuildLeafSet** child_ptrs = NULL; KitWriter* w = NULL; @@ -670,6 +849,24 @@ int build_leafset_union(KitBuildCoordinator* c, const BuildLeafSet* direct, child_ptrs[i] = children[i]; } } + memset(&check_root, 0, sizeof check_root); + snprintf(check_root.target, sizeof check_root.target, "%s", direct->target); + memcpy(check_root.recipe, direct->recipe, BUILD_HASH_LEN); + check_root.configs = direct->configs; + check_root.n_configs = direct->n_configs; + check_root.sources = direct->sources; + check_root.n_sources = direct->n_sources; + check_root.globs = direct->globs; + check_root.n_globs = direct->n_globs; + check_root.blobs = direct->blobs; + check_root.n_blobs = direct->n_blobs; + check_root.children = child_ptrs; + check_root.n_children = nchildren; + if (build_leafset_check_consistent(c, &check_root, 0, err, sizeof err) != + BUILD_OK) { + build_diagf(c->ctx, "build: inconsistent deepset closure: %s", err); + goto out; + } memset(&ds, 0, sizeof ds); snprintf(ds.target, sizeof ds.target, "%s", direct->target); memcpy(ds.recipe, direct->recipe, BUILD_HASH_LEN);