kit

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

resolve.c (46534B)


      1 #include "resolve.h"
      2 
      3 #include "remote.h"
      4 #include "runner.h"
      5 
      6 #undef KIT_TRACE_MODULE
      7 #define KIT_TRACE_MODULE "build/resolve"
      8 
      9 #include <stdio.h>
     10 #include <string.h>
     11 
     12 static int path_join2(char* out, size_t cap, const char* a, const char* b) {
     13   size_t na, nb;
     14   int need_sep;
     15   if (!out || cap == 0u || !a || !b) return BUILD_ERR;
     16   na = strlen(a);
     17   nb = strlen(b);
     18   need_sep = na > 0u && a[na - 1u] != '/';
     19   if (na + (need_sep ? 1u : 0u) + nb + 1u > cap) return BUILD_ERR;
     20   memcpy(out, a, na);
     21   if (need_sep) out[na++] = '/';
     22   memcpy(out + na, b, nb);
     23   out[na + nb] = '\0';
     24   return BUILD_OK;
     25 }
     26 
     27 static int target_copy(KitSlice target, char out[BUILD_TARGET_MAX]) {
     28   if (!target.s || target.len == 0u || target.len >= BUILD_TARGET_MAX)
     29     return BUILD_ERR;
     30   memcpy(out, target.s, target.len);
     31   out[target.len] = '\0';
     32   return BUILD_OK;
     33 }
     34 
     35 static size_t count_lines(const uint8_t* data, size_t len) {
     36   size_t i, n = 0;
     37   for (i = 0; i < len; ++i)
     38     if (data[i] == '\n') ++n;
     39   return n;
     40 }
     41 
     42 static void hash_slice(KitSlice s, uint8_t out[BUILD_HASH_LEN]) {
     43   KitBlobInfo bi;
     44   static const uint8_t empty = 0;
     45   kit_blob_info(&bi, s.len ? s.data : &empty, s.len);
     46   memcpy(out, bi.id, BUILD_HASH_LEN);
     47 }
     48 
     49 static int config_observation_match(const BuildConfig* cfg,
     50                                     const BuildConfigLeaf* obs) {
     51   KitSlice value;
     52   uint8_t value_hash[BUILD_HASH_LEN];
     53   int present = 0;
     54   if (!cfg || !obs) return 0;
     55   if (build_config_get(cfg, kit_slice_cstr(obs->key), &value, &present) !=
     56       BUILD_OK)
     57     return 0;
     58   if (present != obs->present) return 0;
     59   if (!present) return 1;
     60   hash_slice(value, value_hash);
     61   return build_id_eq(value_hash, obs->value_hash);
     62 }
     63 
     64 static int config_leaf_eq(const BuildConfigLeaf* a, const BuildConfigLeaf* b) {
     65   return a && b && strcmp(a->key, b->key) == 0 && a->present == b->present &&
     66          a->has_default == b->has_default &&
     67          build_id_eq(a->value_hash, b->value_hash) &&
     68          build_id_eq(a->default_hash, b->default_hash);
     69 }
     70 
     71 static int config_map_has_key(const BuildConfig* cfg, const char* key) {
     72   KitSlice value;
     73   int present = 0;
     74   if (!cfg || !key) return 0;
     75   if (build_config_get(cfg, kit_slice_cstr(key), &value, &present) != BUILD_OK)
     76     return 0;
     77   return present;
     78 }
     79 
     80 static int projected_config_append(KitBuildCoordinator* c,
     81                                    BuildConfigLeaf** rows, size_t* n,
     82                                    size_t* cap,
     83                                    const BuildConfigLeaf* row) {
     84   size_t i, newcap, old_size, new_size;
     85   BuildConfigLeaf* fresh;
     86   if (!c || !rows || !n || !cap || !row) return BUILD_ERR;
     87   for (i = 0; i < *n; ++i) {
     88     if (config_leaf_eq(&(*rows)[i], row)) return BUILD_OK;
     89   }
     90   if (*n == *cap) {
     91     newcap = *cap ? *cap * 2u : 16u;
     92     if (newcap < *cap || newcap > ((size_t)-1) / sizeof **rows)
     93       return BUILD_ERR;
     94     old_size = *cap * sizeof **rows;
     95     new_size = newcap * sizeof **rows;
     96     fresh = (BuildConfigLeaf*)c->ctx->heap->realloc(
     97         c->ctx->heap, *rows, old_size, new_size, _Alignof(BuildConfigLeaf));
     98     if (!fresh) return BUILD_ERR;
     99     *rows = fresh;
    100     *cap = newcap;
    101   }
    102   (*rows)[(*n)++] = *row;
    103   return BUILD_OK;
    104 }
    105 
    106 static int projected_config_collect(KitBuildCoordinator* c,
    107                                     const BuildLeafSet* direct,
    108                                     const BuildDepEdge* deps,
    109                                     const BuildLeafSet* const* children,
    110                                     size_t nchildren,
    111                                     BuildConfigLeaf** out_rows,
    112                                     size_t* out_n, size_t* out_cap) {
    113   size_t i, j;
    114   if (!c || !direct || !out_rows || !out_n || !out_cap) return BUILD_ERR;
    115   *out_rows = NULL;
    116   *out_n = 0;
    117   *out_cap = 0;
    118   for (i = 0; i < direct->n_configs; ++i) {
    119     if (projected_config_append(c, out_rows, out_n, out_cap,
    120                                 &direct->configs[i]) != BUILD_OK)
    121       return BUILD_ERR;
    122   }
    123   for (i = 0; i < nchildren; ++i) {
    124     BuildConfigEntry overlay_entries[64];
    125     BuildConfig overlay_cfg;
    126     if (!deps || !children || !children[i]) return BUILD_ERR;
    127     build_config_init(&overlay_cfg, overlay_entries,
    128                       sizeof overlay_entries / sizeof overlay_entries[0]);
    129     if (build_coord_config_by_id(c, deps[i].overlay_id, &overlay_cfg) !=
    130         BUILD_OK)
    131       return BUILD_ERR;
    132     for (j = 0; j < children[i]->n_configs; ++j) {
    133       if (config_map_has_key(&overlay_cfg, children[i]->configs[j].key))
    134         continue;
    135       if (projected_config_append(c, out_rows, out_n, out_cap,
    136                                   &children[i]->configs[j]) != BUILD_OK)
    137         return BUILD_ERR;
    138     }
    139   }
    140   return BUILD_OK;
    141 }
    142 
    143 typedef struct BuildSeenId {
    144   uint8_t id[BUILD_HASH_LEN];
    145 } BuildSeenId;
    146 
    147 typedef struct BuildLeafsetCheck {
    148   KitHeap* heap;
    149   BuildSourceLeaf* sources;
    150   size_t n_sources;
    151   size_t cap_sources;
    152   BuildGlobLeaf* globs;
    153   size_t n_globs;
    154   size_t cap_globs;
    155   BuildSeenId* visited;
    156   size_t n_visited;
    157   size_t cap_visited;
    158   char* err;
    159   size_t errcap;
    160 } BuildLeafsetCheck;
    161 
    162 static int leafset_check_err(BuildLeafsetCheck* ck, const char* what,
    163                              const char* key) {
    164   if (ck && ck->err && ck->errcap) {
    165     if (key)
    166       snprintf(ck->err, ck->errcap, "%s: %s", what, key);
    167     else
    168       snprintf(ck->err, ck->errcap, "%s", what);
    169   }
    170   return BUILD_ERR;
    171 }
    172 
    173 static int leafset_check_grow(BuildLeafsetCheck* ck, void** ptr, size_t* cap,
    174                               size_t want, size_t elem_size,
    175                               size_t elem_align) {
    176   size_t newcap, old_size, new_size;
    177   void* fresh;
    178   if (!ck || !ck->heap || !ptr || !cap || elem_size == 0u) return BUILD_ERR;
    179   if (*cap >= want) return BUILD_OK;
    180   newcap = *cap ? *cap : 16u;
    181   while (newcap < want) {
    182     if (newcap > ((size_t)-1) / 2u)
    183       return leafset_check_err(ck, "deepset consistency table too large", NULL);
    184     newcap *= 2u;
    185   }
    186   if (newcap > ((size_t)-1) / elem_size)
    187     return leafset_check_err(ck, "deepset consistency table too large", NULL);
    188   old_size = *cap * elem_size;
    189   new_size = newcap * elem_size;
    190   fresh = ck->heap->realloc(ck->heap, *ptr, old_size, new_size, elem_align);
    191   if (!fresh) return leafset_check_err(ck, "out of memory", NULL);
    192   *ptr = fresh;
    193   *cap = newcap;
    194   return BUILD_OK;
    195 }
    196 
    197 static int leafset_check_source(BuildLeafsetCheck* ck,
    198                                 const BuildSourceLeaf* row) {
    199   size_t i;
    200   if (!ck || !row) return BUILD_ERR;
    201   for (i = 0; i < ck->n_sources; ++i) {
    202     BuildSourceLeaf* seen = &ck->sources[i];
    203     if (strcmp(seen->path, row->path) != 0) continue;
    204     if (seen->absent != row->absent)
    205       return leafset_check_err(ck, "inconsistent source observation",
    206                                row->path);
    207     if (!row->absent && !build_id_eq(seen->blob, row->blob))
    208       return leafset_check_err(ck, "inconsistent source observation",
    209                                row->path);
    210     return BUILD_OK;
    211   }
    212   if (leafset_check_grow(ck, (void**)&ck->sources, &ck->cap_sources,
    213                          ck->n_sources + 1u, sizeof ck->sources[0],
    214                          _Alignof(BuildSourceLeaf)) != BUILD_OK)
    215     return BUILD_ERR;
    216   ck->sources[ck->n_sources++] = *row;
    217   return BUILD_OK;
    218 }
    219 
    220 static int leafset_check_glob(BuildLeafsetCheck* ck, const BuildGlobLeaf* row) {
    221   size_t i;
    222   if (!ck || !row) return BUILD_ERR;
    223   for (i = 0; i < ck->n_globs; ++i) {
    224     BuildGlobLeaf* seen = &ck->globs[i];
    225     if (strcmp(seen->pattern, row->pattern) != 0) continue;
    226     if (!build_id_eq(seen->result_hash, row->result_hash))
    227       return leafset_check_err(ck, "inconsistent glob observation",
    228                                row->pattern);
    229     return BUILD_OK;
    230   }
    231   if (leafset_check_grow(ck, (void**)&ck->globs, &ck->cap_globs,
    232                          ck->n_globs + 1u, sizeof ck->globs[0],
    233                          _Alignof(BuildGlobLeaf)) != BUILD_OK)
    234     return BUILD_ERR;
    235   ck->globs[ck->n_globs++] = *row;
    236   return BUILD_OK;
    237 }
    238 
    239 static int leafset_check_mark_visited(BuildLeafsetCheck* ck,
    240                                       const uint8_t id[BUILD_HASH_LEN],
    241                                       int* already_seen) {
    242   size_t i;
    243   if (!ck || !id || !already_seen) return BUILD_ERR;
    244   *already_seen = 0;
    245   for (i = 0; i < ck->n_visited; ++i) {
    246     if (build_id_eq(ck->visited[i].id, id)) {
    247       *already_seen = 1;
    248       return BUILD_OK;
    249     }
    250   }
    251   if (leafset_check_grow(ck, (void**)&ck->visited, &ck->cap_visited,
    252                          ck->n_visited + 1u, sizeof ck->visited[0],
    253                          _Alignof(BuildSeenId)) != BUILD_OK)
    254     return BUILD_ERR;
    255   memcpy(ck->visited[ck->n_visited++].id, id, BUILD_HASH_LEN);
    256   return BUILD_OK;
    257 }
    258 
    259 static int leafset_check_walk(BuildLeafsetCheck* ck, const BuildLeafSet* node,
    260                               int mark_self) {
    261   size_t i;
    262   if (!ck || !node) return BUILD_ERR;
    263   if (mark_self) {
    264     int already_seen = 0;
    265     if (leafset_check_mark_visited(ck, node->id, &already_seen) != BUILD_OK)
    266       return BUILD_ERR;
    267     if (already_seen) return BUILD_OK;
    268   }
    269   for (i = 0; i < node->n_sources; ++i) {
    270     if (leafset_check_source(ck, &node->sources[i]) != BUILD_OK)
    271       return BUILD_ERR;
    272   }
    273   for (i = 0; i < node->n_globs; ++i) {
    274     if (leafset_check_glob(ck, &node->globs[i]) != BUILD_OK)
    275       return BUILD_ERR;
    276   }
    277   for (i = 0; i < node->n_children; ++i) {
    278     if (leafset_check_walk(ck, node->children[i], 1) != BUILD_OK)
    279       return BUILD_ERR;
    280   }
    281   return BUILD_OK;
    282 }
    283 
    284 static int build_leafset_check_consistent(KitBuildCoordinator* c,
    285                                           const BuildLeafSet* root,
    286                                           int root_has_id, char* err,
    287                                           size_t errcap) {
    288   BuildLeafsetCheck ck;
    289   int ok;
    290   if (!c || !root) return BUILD_ERR;
    291   memset(&ck, 0, sizeof ck);
    292   ck.heap = c->ctx->heap;
    293   ck.err = err;
    294   ck.errcap = errcap;
    295   ok = leafset_check_walk(&ck, root, root_has_id);
    296   if (ck.sources)
    297     ck.heap->free(ck.heap, ck.sources, ck.cap_sources * sizeof ck.sources[0]);
    298   if (ck.globs)
    299     ck.heap->free(ck.heap, ck.globs, ck.cap_globs * sizeof ck.globs[0]);
    300   if (ck.visited)
    301     ck.heap->free(ck.heap, ck.visited, ck.cap_visited * sizeof ck.visited[0]);
    302   return ok;
    303 }
    304 
    305 static int shallow_direct_match(KitBuildCoordinator* c,
    306                                 const BuildShallowTrace* st,
    307                                 const BuildConfig* cfg,
    308                                 const uint8_t argv_id[BUILD_HASH_LEN]) {
    309   uint8_t recipe[BUILD_HASH_LEN];
    310   size_t i;
    311   if (!build_id_eq(st->argv, argv_id)) return 0;
    312   if (build_coord_recipe_id(c, kit_slice_cstr(st->target), recipe) !=
    313           BUILD_OK ||
    314       !build_id_eq(recipe, st->recipe))
    315     return 0;
    316   for (i = 0; i < st->n_configs; ++i) {
    317     if (!config_observation_match(cfg, &st->configs[i])) return 0;
    318   }
    319   for (i = 0; i < st->n_sources; ++i) {
    320     uint8_t blob[BUILD_HASH_LEN];
    321     int present = 0;
    322     if (build_coord_source_hash_target(c, kit_slice_cstr(st->target),
    323                                        kit_slice_cstr(st->sources[i].path),
    324                                        blob, &present) != BUILD_OK)
    325       return 0;
    326     if (st->sources[i].absent) {
    327       if (present) return 0;
    328     } else if (!present || !build_id_eq(blob, st->sources[i].blob)) {
    329       return 0;
    330     }
    331   }
    332   for (i = 0; i < st->n_globs; ++i) {
    333     uint8_t hash[BUILD_HASH_LEN];
    334     if (build_coord_glob_target(c, kit_slice_cstr(st->target),
    335                                 kit_slice_cstr(st->globs[i].pattern), hash,
    336                                 NULL, NULL) != BUILD_OK)
    337       return 0;
    338     if (!build_id_eq(hash, st->globs[i].result_hash)) return 0;
    339   }
    340   for (i = 0; i < st->n_blobs; ++i) {
    341     if (kit_cas_has_blob(c->cas, st->blobs[i].blob) != KIT_OK) return 0;
    342   }
    343   return 1;
    344 }
    345 
    346 static int try_shallow_trace(KitBuildCoordinator* c, KitSlice target,
    347                              const BuildShallowTrace* st,
    348                              const BuildConfig* cfg, const BuildArgv* argv,
    349                              const uint8_t argv_id[BUILD_HASH_LEN],
    350                              const BuildChainFrame* chain,
    351                              BuildResolved* out) {
    352   BuildDepEdge deps_copy[128];
    353   const BuildLeafSet* child_leafsets[128];
    354   BuildDepLog log;
    355   size_t i;
    356   if (!shallow_direct_match(c, st, cfg, argv_id)) return BUILD_ERR;
    357   if (st->n_deps > sizeof deps_copy / sizeof deps_copy[0] ||
    358       st->n_deps > sizeof child_leafsets / sizeof child_leafsets[0])
    359     return BUILD_ERR;
    360   for (i = 0; i < st->n_deps; ++i) {
    361     BuildConfigEntry dep_cfg_entries[128];
    362     BuildConfigEntry overlay_entries[64];
    363     char dep_argv_entries[64][BUILD_VAL_MAX];
    364     BuildConfig dep_cfg, overlay_cfg;
    365     BuildArgv dep_argv;
    366     BuildResolved dep_r;
    367     build_config_init(&dep_cfg, dep_cfg_entries,
    368                       sizeof dep_cfg_entries / sizeof dep_cfg_entries[0]);
    369     build_config_init(&overlay_cfg, overlay_entries,
    370                       sizeof overlay_entries / sizeof overlay_entries[0]);
    371     build_argv_init(&dep_argv, dep_argv_entries,
    372                     sizeof dep_argv_entries / sizeof dep_argv_entries[0]);
    373     if (build_coord_config_by_id(c, st->deps[i].overlay_id, &overlay_cfg) !=
    374             BUILD_OK ||
    375         build_config_overlay_map(cfg, &overlay_cfg, &dep_cfg) != BUILD_OK ||
    376         build_coord_argv_by_id(c, st->deps[i].argv_id, &dep_argv) !=
    377             BUILD_OK ||
    378         build_resolve(c, kit_slice_cstr(st->deps[i].name), &dep_cfg, &dep_argv,
    379                       chain, &dep_r) != BUILD_OK ||
    380         !build_id_eq(dep_r.output_tree, st->deps[i].output_tree) ||
    381         !dep_r.leafset)
    382       return BUILD_ERR;
    383     deps_copy[i] = st->deps[i];
    384     child_leafsets[i] = dep_r.leafset;
    385   }
    386   if (build_materialize(c, st->output, out->path, sizeof out->path) != BUILD_OK)
    387     return BUILD_ERR;
    388   memcpy(out->output_tree, st->output, BUILD_HASH_LEN);
    389   memset(&log, 0, sizeof log);
    390   log.configs = st->configs;
    391   log.n_configs = st->n_configs;
    392   log.sources = st->sources;
    393   log.n_sources = st->n_sources;
    394   log.globs = st->globs;
    395   log.n_globs = st->n_globs;
    396   log.blobs = st->blobs;
    397   log.n_blobs = st->n_blobs;
    398   log.deps = deps_copy;
    399   log.n_deps = st->n_deps;
    400   log.child_leafsets = child_leafsets;
    401   log.n_children = st->n_deps;
    402   return build_runner_record_traces(c, target, argv, &log,
    403                                     out->output_tree, &out->leafset);
    404 }
    405 
    406 static int try_test_shallow_trace(KitBuildCoordinator* c, KitSlice target,
    407                                   const BuildShallowTrace* st,
    408                                   const BuildConfig* cfg,
    409                                   const BuildArgv* argv,
    410                                   const uint8_t argv_id[BUILD_HASH_LEN],
    411                                   const BuildChainFrame* chain,
    412                                   BuildTestResolved* out) {
    413   BuildDepEdge deps_copy[128];
    414   const BuildLeafSet* child_leafsets[128];
    415   BuildDepLog log;
    416   size_t i;
    417   if (!shallow_direct_match(c, st, cfg, argv_id)) return BUILD_ERR;
    418   if (st->n_deps > sizeof deps_copy / sizeof deps_copy[0] ||
    419       st->n_deps > sizeof child_leafsets / sizeof child_leafsets[0])
    420     return BUILD_ERR;
    421   for (i = 0; i < st->n_deps; ++i) {
    422     BuildConfigEntry dep_cfg_entries[128];
    423     BuildConfigEntry overlay_entries[64];
    424     char dep_argv_entries[64][BUILD_VAL_MAX];
    425     BuildConfig dep_cfg, overlay_cfg;
    426     BuildArgv dep_argv;
    427     BuildResolved dep_r;
    428     build_config_init(&dep_cfg, dep_cfg_entries,
    429                       sizeof dep_cfg_entries / sizeof dep_cfg_entries[0]);
    430     build_config_init(&overlay_cfg, overlay_entries,
    431                       sizeof overlay_entries / sizeof overlay_entries[0]);
    432     build_argv_init(&dep_argv, dep_argv_entries,
    433                     sizeof dep_argv_entries / sizeof dep_argv_entries[0]);
    434     if (build_coord_config_by_id(c, st->deps[i].overlay_id, &overlay_cfg) !=
    435             BUILD_OK ||
    436         build_config_overlay_map(cfg, &overlay_cfg, &dep_cfg) != BUILD_OK ||
    437         build_coord_argv_by_id(c, st->deps[i].argv_id, &dep_argv) !=
    438             BUILD_OK ||
    439         build_resolve(c, kit_slice_cstr(st->deps[i].name), &dep_cfg, &dep_argv,
    440                       chain, &dep_r) != BUILD_OK ||
    441         !build_id_eq(dep_r.output_tree, st->deps[i].output_tree) ||
    442         !dep_r.leafset)
    443       return BUILD_ERR;
    444     deps_copy[i] = st->deps[i];
    445     child_leafsets[i] = dep_r.leafset;
    446   }
    447   if (build_materialize(c, st->output, out->path, sizeof out->path) != BUILD_OK)
    448     return BUILD_ERR;
    449   memcpy(out->result_tree, st->output, BUILD_HASH_LEN);
    450   out->status = KIT_TEST_PASS;
    451   out->exit_code = 0;
    452   memset(&log, 0, sizeof log);
    453   log.configs = st->configs;
    454   log.n_configs = st->n_configs;
    455   log.sources = st->sources;
    456   log.n_sources = st->n_sources;
    457   log.globs = st->globs;
    458   log.n_globs = st->n_globs;
    459   log.blobs = st->blobs;
    460   log.n_blobs = st->n_blobs;
    461   log.deps = deps_copy;
    462   log.n_deps = st->n_deps;
    463   log.child_leafsets = child_leafsets;
    464   log.n_children = st->n_deps;
    465   return build_runner_record_test_traces(c, target, argv, &log,
    466                                          out->result_tree, &out->leafset);
    467 }
    468 
    469 static int chain_has(const BuildChainFrame* f, BuildActionKind action,
    470                      KitSlice target,
    471                      const uint8_t config_id[BUILD_HASH_LEN],
    472                      const uint8_t argv_id[BUILD_HASH_LEN]) {
    473   for (; f; f = f->parent) {
    474     if (f->action == action && strlen(f->target) == target.len &&
    475         memcmp(f->target, target.s, target.len) == 0 &&
    476         build_id_eq(f->config_id, config_id) && build_id_eq(f->argv_id, argv_id))
    477       return 1;
    478   }
    479   return 0;
    480 }
    481 
    482 int build_chain_extend(KitBuildCoordinator* c, const BuildChainFrame* parent,
    483                        BuildActionKind action, KitSlice target,
    484                        const uint8_t config_id[BUILD_HASH_LEN],
    485                        const uint8_t argv_id[BUILD_HASH_LEN],
    486                        const BuildChainFrame** out, char* err, size_t errcap) {
    487   BuildChainFrame* f;
    488   if (!c || !config_id || !argv_id || !out) return BUILD_ERR;
    489   *out = NULL;
    490   if (chain_has(parent, action, target, config_id, argv_id)) {
    491     if (err && errcap) snprintf(err, errcap, "dependency cycle at %.*s",
    492                                 KIT_SLICE_ARG(target));
    493     return BUILD_ERR;
    494   }
    495   f = (BuildChainFrame*)c->ctx->heap->alloc(c->ctx->heap, sizeof *f,
    496                                             _Alignof(BuildChainFrame));
    497   if (!f) return BUILD_ERR;
    498   memset(f, 0, sizeof *f);
    499   f->parent = parent;
    500   f->action = action;
    501   if (target_copy(target, f->target) != BUILD_OK) return BUILD_ERR;
    502   memcpy(f->config_id, config_id, BUILD_HASH_LEN);
    503   memcpy(f->argv_id, argv_id, BUILD_HASH_LEN);
    504   *out = f;
    505   return BUILD_OK;
    506 }
    507 
    508 int build_materialize(KitBuildCoordinator* c,
    509                       const uint8_t tree_id[BUILD_HASH_LEN], char* path_out,
    510                       size_t cap) {
    511   char tmp_dir[BUILD_PATH_MAX];
    512   if (!c || !tree_id || !path_out) return BUILD_ERR;
    513   if (build_store_cache_lookup(&c->store, tree_id, path_out, cap) == BUILD_OK)
    514     return BUILD_OK;
    515   if (build_store_cache_materialize(&c->store, tree_id, path_out, cap) ==
    516       BUILD_OK)
    517     return BUILD_OK;
    518   if (c->opts.n_object_remotes && c->host.exec) {
    519     if (path_join2(tmp_dir, sizeof tmp_dir, c->store.root, "tmp") ==
    520             BUILD_OK &&
    521         build_remote_fetch(c->ctx, c->host.exec, c->opts.object_remotes,
    522                            c->opts.n_object_remotes, c->cas,
    523                            kit_slice_cstr(tmp_dir), BUILD_REMOTE_TREE,
    524                            tree_id) == BUILD_OK) {
    525       build_coord_stat_bump(c, BUILD_STAT_OBJECT_FETCH);
    526       if (build_store_cache_materialize(&c->store, tree_id, path_out, cap) ==
    527           BUILD_OK)
    528         return BUILD_OK;
    529     }
    530   }
    531   build_coord_stat_bump(c, BUILD_STAT_MATERIALIZE_MISS);
    532   return BUILD_ERR;
    533 }
    534 
    535 static int verify_cached_hit(KitBuildCoordinator* c, KitSlice target,
    536                              const BuildConfig* cfg, const BuildArgv* argv,
    537                              const BuildChainFrame* chain,
    538                              const uint8_t expected[BUILD_HASH_LEN],
    539                              BuildResolved* out) {
    540   BuildResolved fresh;
    541   char want[BUILD_HEX_LEN], got[BUILD_HEX_LEN];
    542   if (!c || !expected || !out) return BUILD_ERR;
    543   if (!c->opts.verify) return BUILD_OK;
    544   memset(&fresh, 0, sizeof fresh);
    545   if (build_run_recipe_probe(c, target, cfg, argv, chain, &fresh) != BUILD_OK)
    546     return BUILD_ERR;
    547   if (!build_id_eq(fresh.output_tree, expected)) {
    548     kit_hex_encode(want, expected, BUILD_HASH_LEN);
    549     kit_hex_encode(got, fresh.output_tree, BUILD_HASH_LEN);
    550     build_diagf(c->ctx,
    551                 "build: verify mismatch for %.*s: cached %s fresh %s",
    552                 KIT_SLICE_ARG(target), want, got);
    553     return BUILD_ERR;
    554   }
    555   return BUILD_OK;
    556 }
    557 
    558 static int blob_from_materialized_file(KitBuildCoordinator* c, const char* dir,
    559                                        const char* name,
    560                                        uint8_t out[BUILD_HASH_LEN]) {
    561   char path[BUILD_PATH_MAX];
    562   KitFileData fd;
    563   KitBlobInfo bi;
    564   if (!c || !dir || !name || !out) return BUILD_ERR;
    565   if (path_join2(path, sizeof path, dir, name) != BUILD_OK) return BUILD_ERR;
    566   fd.data = NULL;
    567   fd.size = 0;
    568   fd.token = NULL;
    569   if (!c->host.cas_host || !c->host.cas_host->file_io ||
    570       !c->host.cas_host->file_io->read_all ||
    571       c->host.cas_host->file_io->read_all(c->host.cas_host->file_io->user, path,
    572                                           &fd) != KIT_OK)
    573     return BUILD_ERR;
    574   if (kit_cas_add_blob(c->cas, fd.data, fd.size, &bi) != KIT_OK) {
    575     if (c->host.cas_host->file_io->release)
    576       c->host.cas_host->file_io->release(c->host.cas_host->file_io->user, &fd);
    577     return BUILD_ERR;
    578   }
    579   memcpy(out, bi.id, BUILD_HASH_LEN);
    580   if (c->host.cas_host->file_io->release)
    581     c->host.cas_host->file_io->release(c->host.cas_host->file_io->user, &fd);
    582   return BUILD_OK;
    583 }
    584 
    585 static int fill_test_stdio_blobs(KitBuildCoordinator* c, BuildTestResolved* r) {
    586   if (!c || !r) return BUILD_ERR;
    587   return blob_from_materialized_file(c, r->path, "stdout", r->stdout_blob) ==
    588                  BUILD_OK &&
    589              blob_from_materialized_file(c, r->path, "stderr", r->stderr_blob) ==
    590                  BUILD_OK
    591          ? BUILD_OK
    592          : BUILD_ERR;
    593 }
    594 
    595 static int verify_cached_test_hit(KitBuildCoordinator* c, KitSlice target,
    596                                   const BuildConfig* cfg,
    597                                   const BuildArgv* argv,
    598                                   const BuildChainFrame* chain,
    599                                   const uint8_t expected[BUILD_HASH_LEN]) {
    600   BuildTestResolved fresh;
    601   char want[BUILD_HEX_LEN], got[BUILD_HEX_LEN];
    602   if (!c || !expected) return BUILD_ERR;
    603   if (!c->opts.verify) return BUILD_OK;
    604   memset(&fresh, 0, sizeof fresh);
    605   if (build_run_test_recipe(c, target, cfg, argv, chain, &fresh, 0) != BUILD_OK)
    606     return BUILD_ERR;
    607   if (fresh.status != KIT_TEST_PASS || !build_id_eq(fresh.result_tree, expected)) {
    608     kit_hex_encode(want, expected, BUILD_HASH_LEN);
    609     kit_hex_encode(got, fresh.result_tree, BUILD_HASH_LEN);
    610     build_diagf(c->ctx,
    611                 "build test: verify mismatch for %.*s: cached %s fresh %s",
    612                 KIT_SLICE_ARG(target), want, got);
    613     return BUILD_ERR;
    614   }
    615   return BUILD_OK;
    616 }
    617 
    618 int build_resolve(KitBuildCoordinator* c, KitSlice target, const BuildConfig* cfg,
    619                   const BuildArgv* argv, const BuildChainFrame* chain,
    620                   BuildResolved* out) {
    621   uint8_t config_id[BUILD_HASH_LEN], argv_id[BUILD_HASH_LEN];
    622   uint8_t target_key[BUILD_HASH_LEN];
    623   const BuildChainFrame* child;
    624   BuildRecordRow rows[2u * KIT_BUILD_RECORD_CAP];
    625   BuildTargetRecord rec;
    626   size_t i;
    627   char err[128];
    628   int pulled_remote = 0;
    629   if (!c || !cfg || !argv || !out) return BUILD_ERR;
    630   if (build_config_id(c->ctx->heap, cfg, config_id) != BUILD_OK ||
    631       build_argv_id(c->ctx->heap, argv, argv_id) != BUILD_OK)
    632     return BUILD_ERR;
    633   if (c->opts.trace) {
    634     char cfg_hex[BUILD_HEX_LEN], argv_hex[BUILD_HEX_LEN];
    635     kit_hex_encode(cfg_hex, config_id, BUILD_HASH_LEN);
    636     kit_hex_encode(argv_hex, argv_id, BUILD_HASH_LEN);
    637     build_coord_tracef(c, "resolve-start action=build target=%.*s root=%d config=%s argv=%s",
    638                        KIT_SLICE_ARG(target), chain ? 0 : 1, cfg_hex,
    639                        argv_hex);
    640   }
    641   if (build_chain_extend(c, chain, BUILD_ACTION_BUILD, target, config_id,
    642                          argv_id, &child, err, sizeof err) != BUILD_OK) {
    643     build_diagf(c->ctx, "build: %s", err);
    644     return BUILD_ERR;
    645   }
    646   if (build_target_key(target, target_key) == BUILD_OK) {
    647 scan_record:
    648     memset(&rec, 0, sizeof rec);
    649     rec.rows = rows;
    650     rec.cap_rows = sizeof rows / sizeof rows[0];
    651     if (build_store_record_load(&c->store, target_key, target, &rec) ==
    652         BUILD_OK) {
    653       if (rec.n_rows == 0u && !pulled_remote) {
    654         int pulled_now = 0;
    655         pulled_remote = 1;
    656         if (build_coord_trace_remote_pull_once(c, target, &pulled_now) ==
    657                 BUILD_OK &&
    658             pulled_now)
    659           goto scan_record;
    660       }
    661       for (i = 0; i < rec.n_rows; ++i) {
    662         KitFileData fd;
    663         BuildDeepTrace dt;
    664         const BuildLeafSet* leaf = NULL;
    665         int match = 0;
    666         if (rec.rows[i].kind != (uint8_t)BUILD_TRACE_DEEP) continue;
    667         fd.data = NULL;
    668         fd.size = 0;
    669         fd.token = NULL;
    670         if (build_store_get_trace(&c->store, rec.rows[i].trace_id, &fd) !=
    671             BUILD_OK)
    672           continue;
    673         if (c->opts.trace) {
    674           char trace_hex[BUILD_HEX_LEN];
    675           kit_hex_encode(trace_hex, rec.rows[i].trace_id, BUILD_HASH_LEN);
    676           build_coord_tracef(c, "deep-candidate target=%.*s trace=%s",
    677                              KIT_SLICE_ARG(target), trace_hex);
    678         }
    679         memset(&dt, 0, sizeof dt);
    680         err[0] = '\0';
    681         if (build_deep_parse(fd.data, fd.size, &dt, NULL, 0) == BUILD_OK &&
    682             strlen(dt.target) == target.len &&
    683             memcmp(dt.target, target.s, target.len) == 0 &&
    684             build_id_eq(dt.argv, argv_id) &&
    685             build_coord_deepset_load(c, dt.deepset, &leaf) == BUILD_OK &&
    686             build_leafset_check_consistent(c, leaf, 1, err, sizeof err) ==
    687                 BUILD_OK &&
    688             build_leafset_refresh(c, leaf, cfg, &match) == BUILD_OK && match &&
    689             build_materialize(c, dt.output, out->path, sizeof out->path) ==
    690                 BUILD_OK) {
    691           memcpy(out->output_tree, dt.output, BUILD_HASH_LEN);
    692           out->leafset = leaf;
    693           if (verify_cached_hit(c, target, cfg, argv, child, dt.output, out) !=
    694               BUILD_OK) {
    695             build_store_release(&c->store, &fd);
    696             return BUILD_ERR;
    697           }
    698           if (c->opts.trace) {
    699             char out_hex[BUILD_HEX_LEN];
    700             kit_hex_encode(out_hex, dt.output, BUILD_HASH_LEN);
    701             build_coord_tracef(c, "deep-hit target=%.*s output=%s",
    702                                KIT_SLICE_ARG(target), out_hex);
    703           }
    704           build_store_release(&c->store, &fd);
    705           build_coord_stat_bump(c, BUILD_STAT_DEEP_HIT);
    706           return BUILD_OK;
    707         }
    708         if (c->opts.trace)
    709           build_coord_tracef(c, "deep-miss target=%.*s reason=%s",
    710                              KIT_SLICE_ARG(target),
    711                              err[0] ? "malformed" : "no-match");
    712         if (err[0])
    713           KIT_LOGD("skip deep trace for %.*s: %s", KIT_SLICE_ARG(target), err);
    714         err[0] = '\0';
    715         build_store_release(&c->store, &fd);
    716       }
    717       for (i = 0; i < rec.n_rows; ++i) {
    718         KitFileData fd;
    719         BuildConfigLeaf configs[BUILD_RECIPE_CONFIG_CAP];
    720         BuildSourceLeaf sources[BUILD_RECIPE_SOURCE_CAP];
    721         BuildGlobLeaf globs[BUILD_RECIPE_GLOB_CAP];
    722         BuildBlobLeaf blobs[BUILD_RECIPE_BLOB_CAP];
    723         BuildDepEdge deps[BUILD_RECIPE_DEP_CAP];
    724         BuildShallowTrace st;
    725         size_t rows;
    726         if (rec.rows[i].kind != (uint8_t)BUILD_TRACE_SHALLOW) continue;
    727         fd.data = NULL;
    728         fd.size = 0;
    729         fd.token = NULL;
    730         if (build_store_get_trace(&c->store, rec.rows[i].trace_id, &fd) !=
    731             BUILD_OK)
    732           continue;
    733         if (c->opts.trace) {
    734           char trace_hex[BUILD_HEX_LEN];
    735           kit_hex_encode(trace_hex, rec.rows[i].trace_id, BUILD_HASH_LEN);
    736           build_coord_tracef(c, "shallow-candidate target=%.*s trace=%s",
    737                              KIT_SLICE_ARG(target), trace_hex);
    738         }
    739         rows = count_lines(fd.data, fd.size);
    740         memset(&st, 0, sizeof st);
    741         st.configs = configs;
    742         st.cap_configs = sizeof configs / sizeof configs[0];
    743         st.sources = sources;
    744         st.cap_sources = sizeof sources / sizeof sources[0];
    745         st.globs = globs;
    746         st.cap_globs = sizeof globs / sizeof globs[0];
    747         st.blobs = blobs;
    748         st.cap_blobs = sizeof blobs / sizeof blobs[0];
    749         st.deps = deps;
    750         st.cap_deps = sizeof deps / sizeof deps[0];
    751         if (rows <= sizeof configs / sizeof configs[0] &&
    752             build_shallow_parse(fd.data, fd.size, &st, NULL, 0) == BUILD_OK &&
    753             strlen(st.target) == target.len &&
    754             memcmp(st.target, target.s, target.len) == 0 &&
    755             try_shallow_trace(c, target, &st, cfg, argv, argv_id, child, out) ==
    756                 BUILD_OK) {
    757           if (verify_cached_hit(c, target, cfg, argv, child, st.output, out) !=
    758               BUILD_OK) {
    759             build_store_release(&c->store, &fd);
    760             return BUILD_ERR;
    761           }
    762           if (c->opts.trace) {
    763             char out_hex[BUILD_HEX_LEN];
    764             kit_hex_encode(out_hex, st.output, BUILD_HASH_LEN);
    765             build_coord_tracef(c, "shallow-hit target=%.*s output=%s",
    766                                KIT_SLICE_ARG(target), out_hex);
    767           }
    768           build_store_release(&c->store, &fd);
    769           build_coord_stat_bump(c, BUILD_STAT_SHALLOW_HIT);
    770           return BUILD_OK;
    771         }
    772         if (c->opts.trace)
    773           build_coord_tracef(c, "shallow-miss target=%.*s reason=no-match",
    774                              KIT_SLICE_ARG(target));
    775         build_store_release(&c->store, &fd);
    776       }
    777     }
    778   }
    779   build_coord_tracef(c, "recipe-run target=%.*s", KIT_SLICE_ARG(target));
    780   return build_run_recipe(c, target, cfg, argv, child, out);
    781 }
    782 
    783 int build_test_resolve(KitBuildCoordinator* c, KitSlice target,
    784                        const BuildConfig* cfg, const BuildArgv* argv,
    785                        const BuildChainFrame* chain, BuildTestResolved* out) {
    786   uint8_t config_id[BUILD_HASH_LEN], argv_id[BUILD_HASH_LEN];
    787   uint8_t target_key[BUILD_HASH_LEN];
    788   const BuildChainFrame* child;
    789   BuildRecordRow rows[2u * KIT_BUILD_RECORD_CAP];
    790   BuildTargetRecord rec;
    791   size_t i;
    792   char err[128];
    793   int pulled_remote = 0;
    794   if (!c || !cfg || !argv || !out) return BUILD_ERR;
    795   if (build_config_id(c->ctx->heap, cfg, config_id) != BUILD_OK ||
    796       build_argv_id(c->ctx->heap, argv, argv_id) != BUILD_OK)
    797     return BUILD_ERR;
    798   if (c->opts.trace) {
    799     char cfg_hex[BUILD_HEX_LEN], argv_hex[BUILD_HEX_LEN];
    800     kit_hex_encode(cfg_hex, config_id, BUILD_HASH_LEN);
    801     kit_hex_encode(argv_hex, argv_id, BUILD_HASH_LEN);
    802     build_coord_tracef(c, "resolve-start action=test target=%.*s root=%d config=%s argv=%s",
    803                        KIT_SLICE_ARG(target), chain ? 0 : 1, cfg_hex,
    804                        argv_hex);
    805   }
    806   if (build_chain_extend(c, chain, BUILD_ACTION_TEST, target, config_id,
    807                          argv_id, &child, err, sizeof err) != BUILD_OK) {
    808     build_diagf(c->ctx, "build test: %s", err);
    809     return BUILD_ERR;
    810   }
    811   if (build_test_target_key(target, target_key) == BUILD_OK) {
    812 scan_record:
    813     memset(&rec, 0, sizeof rec);
    814     rec.rows = rows;
    815     rec.cap_rows = sizeof rows / sizeof rows[0];
    816     if (build_store_record_load(&c->store, target_key, target, &rec) ==
    817         BUILD_OK) {
    818       if (rec.n_rows == 0u && !pulled_remote) {
    819         int pulled_now = 0;
    820         pulled_remote = 1;
    821         if (build_coord_trace_remote_pull_once(c, target, &pulled_now) ==
    822                 BUILD_OK &&
    823             pulled_now)
    824           goto scan_record;
    825       }
    826       for (i = 0; i < rec.n_rows; ++i) {
    827         KitFileData fd;
    828         BuildDeepTrace dt;
    829         const BuildLeafSet* leaf = NULL;
    830         int match = 0;
    831         if (rec.rows[i].kind != (uint8_t)BUILD_TRACE_DEEP) continue;
    832         fd.data = NULL;
    833         fd.size = 0;
    834         fd.token = NULL;
    835         if (build_store_get_trace(&c->store, rec.rows[i].trace_id, &fd) !=
    836             BUILD_OK)
    837           continue;
    838         if (c->opts.trace) {
    839           char trace_hex[BUILD_HEX_LEN];
    840           kit_hex_encode(trace_hex, rec.rows[i].trace_id, BUILD_HASH_LEN);
    841           build_coord_tracef(c, "deep-candidate target=%.*s trace=%s",
    842                              KIT_SLICE_ARG(target), trace_hex);
    843         }
    844         memset(&dt, 0, sizeof dt);
    845         err[0] = '\0';
    846         if (build_test_deep_parse(fd.data, fd.size, &dt, NULL, 0) == BUILD_OK &&
    847             strlen(dt.target) == target.len &&
    848             memcmp(dt.target, target.s, target.len) == 0 &&
    849             build_id_eq(dt.argv, argv_id) &&
    850             build_coord_deepset_load(c, dt.deepset, &leaf) == BUILD_OK &&
    851             build_leafset_check_consistent(c, leaf, 1, err, sizeof err) ==
    852                 BUILD_OK &&
    853             build_leafset_refresh(c, leaf, cfg, &match) == BUILD_OK && match &&
    854             build_materialize(c, dt.output, out->path, sizeof out->path) ==
    855                 BUILD_OK) {
    856           out->status = KIT_TEST_PASS;
    857           out->exit_code = 0;
    858           memcpy(out->result_tree, dt.output, BUILD_HASH_LEN);
    859           out->leafset = leaf;
    860           if (fill_test_stdio_blobs(c, out) != BUILD_OK ||
    861               verify_cached_test_hit(c, target, cfg, argv, child, dt.output) !=
    862                   BUILD_OK) {
    863             build_store_release(&c->store, &fd);
    864             return BUILD_ERR;
    865           }
    866           if (c->opts.trace) {
    867             char out_hex[BUILD_HEX_LEN];
    868             kit_hex_encode(out_hex, dt.output, BUILD_HASH_LEN);
    869             build_coord_tracef(c, "deep-hit target=%.*s output=%s",
    870                                KIT_SLICE_ARG(target), out_hex);
    871           }
    872           build_store_release(&c->store, &fd);
    873           build_coord_stat_bump(c, BUILD_STAT_TEST_CACHE_HIT);
    874           return BUILD_OK;
    875         }
    876         if (c->opts.trace)
    877           build_coord_tracef(c, "deep-miss target=%.*s reason=%s",
    878                              KIT_SLICE_ARG(target),
    879                              err[0] ? "malformed" : "no-match");
    880         if (err[0])
    881           KIT_LOGD("skip test deep trace for %.*s: %s", KIT_SLICE_ARG(target),
    882                    err);
    883         err[0] = '\0';
    884         build_store_release(&c->store, &fd);
    885       }
    886       for (i = 0; i < rec.n_rows; ++i) {
    887         KitFileData fd;
    888         BuildConfigLeaf configs[BUILD_RECIPE_CONFIG_CAP];
    889         BuildSourceLeaf sources[BUILD_RECIPE_SOURCE_CAP];
    890         BuildGlobLeaf globs[BUILD_RECIPE_GLOB_CAP];
    891         BuildBlobLeaf blobs[BUILD_RECIPE_BLOB_CAP];
    892         BuildDepEdge deps[BUILD_RECIPE_DEP_CAP];
    893         BuildShallowTrace st;
    894         size_t nrows;
    895         if (rec.rows[i].kind != (uint8_t)BUILD_TRACE_SHALLOW) continue;
    896         fd.data = NULL;
    897         fd.size = 0;
    898         fd.token = NULL;
    899         if (build_store_get_trace(&c->store, rec.rows[i].trace_id, &fd) !=
    900             BUILD_OK)
    901           continue;
    902         if (c->opts.trace) {
    903           char trace_hex[BUILD_HEX_LEN];
    904           kit_hex_encode(trace_hex, rec.rows[i].trace_id, BUILD_HASH_LEN);
    905           build_coord_tracef(c, "shallow-candidate target=%.*s trace=%s",
    906                              KIT_SLICE_ARG(target), trace_hex);
    907         }
    908         nrows = count_lines(fd.data, fd.size);
    909         memset(&st, 0, sizeof st);
    910         st.configs = configs;
    911         st.cap_configs = sizeof configs / sizeof configs[0];
    912         st.sources = sources;
    913         st.cap_sources = sizeof sources / sizeof sources[0];
    914         st.globs = globs;
    915         st.cap_globs = sizeof globs / sizeof globs[0];
    916         st.blobs = blobs;
    917         st.cap_blobs = sizeof blobs / sizeof blobs[0];
    918         st.deps = deps;
    919         st.cap_deps = sizeof deps / sizeof deps[0];
    920         if (nrows <= sizeof configs / sizeof configs[0] &&
    921             build_test_shallow_parse(fd.data, fd.size, &st, NULL, 0) ==
    922                 BUILD_OK &&
    923             strlen(st.target) == target.len &&
    924             memcmp(st.target, target.s, target.len) == 0 &&
    925             try_test_shallow_trace(c, target, &st, cfg, argv, argv_id, child,
    926                                    out) == BUILD_OK) {
    927           if (fill_test_stdio_blobs(c, out) != BUILD_OK ||
    928               verify_cached_test_hit(c, target, cfg, argv, child, st.output) !=
    929                   BUILD_OK) {
    930             build_store_release(&c->store, &fd);
    931             return BUILD_ERR;
    932           }
    933           if (c->opts.trace) {
    934             char out_hex[BUILD_HEX_LEN];
    935             kit_hex_encode(out_hex, st.output, BUILD_HASH_LEN);
    936             build_coord_tracef(c, "shallow-hit target=%.*s output=%s",
    937                                KIT_SLICE_ARG(target), out_hex);
    938           }
    939           build_store_release(&c->store, &fd);
    940           build_coord_stat_bump(c, BUILD_STAT_TEST_CACHE_HIT);
    941           return BUILD_OK;
    942         }
    943         if (c->opts.trace)
    944           build_coord_tracef(c, "shallow-miss target=%.*s reason=no-match",
    945                              KIT_SLICE_ARG(target));
    946         build_store_release(&c->store, &fd);
    947       }
    948     }
    949   }
    950   build_coord_tracef(c, "recipe-run target=%.*s", KIT_SLICE_ARG(target));
    951   return build_run_test_recipe(c, target, cfg, argv, child, out, 1);
    952 }
    953 
    954 int build_dispatch(KitBuildCoordinator* c, KitSlice target,
    955                    const BuildConfig* cfg, const BuildArgv* argv,
    956                    const BuildChainFrame* chain,
    957                    BuildTargetFuture** out_future, char* err, size_t errcap) {
    958   uint8_t config_id[BUILD_HASH_LEN], argv_id[BUILD_HASH_LEN];
    959   const BuildChainFrame* child;
    960   BuildResolved r;
    961   int fresh;
    962   if (!c || !cfg || !argv || !out_future) return BUILD_ERR;
    963   if (build_config_id(c->ctx->heap, cfg, config_id) != BUILD_OK ||
    964       build_argv_id(c->ctx->heap, argv, argv_id) != BUILD_OK)
    965     return BUILD_ERR;
    966   if (build_chain_extend(c, chain, BUILD_ACTION_BUILD, target, config_id,
    967                          argv_id, &child, err, errcap) != BUILD_OK)
    968     return BUILD_ERR;
    969   if (build_coord_target_intern(c, target, config_id, argv_id, out_future,
    970                                 &fresh) != BUILD_OK)
    971     return BUILD_ERR;
    972   if (!fresh) return BUILD_OK;
    973   memset(&r, 0, sizeof r);
    974   if (build_resolve(c, target, cfg, argv, chain, &r) == BUILD_OK) {
    975     (void)child;
    976     build_coord_target_complete(c, *out_future, &r);
    977     return BUILD_OK;
    978   }
    979   build_coord_target_fail(c, *out_future);
    980   if (err && errcap) snprintf(err, errcap, "build dispatch failed");
    981   return BUILD_ERR;
    982 }
    983 
    984 int build_leafset_union(KitBuildCoordinator* c, const BuildLeafSet* direct,
    985                         const BuildDepEdge* deps,
    986                         const BuildLeafSet* const* children, size_t nchildren,
    987                         const BuildLeafSet** out) {
    988   BuildDeepSet ds;
    989   BuildLeafSet leaf;
    990   BuildLeafSet check_root;
    991   BuildConfigLeaf* projected_configs = NULL;
    992   size_t n_projected_configs = 0;
    993   size_t cap_projected_configs = 0;
    994   uint8_t(*child_ids)[BUILD_HASH_LEN] = NULL;
    995   const BuildLeafSet** child_ptrs = NULL;
    996   KitWriter* w = NULL;
    997   const uint8_t* bytes;
    998   size_t len, i;
    999   KitBlobInfo bi;
   1000   char err[128];
   1001   int ok = BUILD_ERR;
   1002   if (!c || !direct || !out) return BUILD_ERR;
   1003   if (nchildren && !children) return BUILD_ERR;
   1004   if (nchildren && !deps) return BUILD_ERR;
   1005   if (projected_config_collect(c, direct, deps, children, nchildren,
   1006                                &projected_configs, &n_projected_configs,
   1007                                &cap_projected_configs) != BUILD_OK)
   1008     goto out;
   1009   if (nchildren) {
   1010     child_ids = (uint8_t(*)[BUILD_HASH_LEN])c->ctx->heap->alloc(
   1011         c->ctx->heap, nchildren * sizeof *child_ids, _Alignof(uint8_t));
   1012     child_ptrs = (const BuildLeafSet**)c->ctx->heap->alloc(
   1013         c->ctx->heap, nchildren * sizeof *child_ptrs,
   1014         _Alignof(const BuildLeafSet*));
   1015     if (!child_ids || !child_ptrs) goto out;
   1016     for (i = 0; i < nchildren; ++i) {
   1017       if (!children[i]) goto out;
   1018       memcpy(child_ids[i], children[i]->id, BUILD_HASH_LEN);
   1019       child_ptrs[i] = children[i];
   1020     }
   1021   }
   1022   memset(&check_root, 0, sizeof check_root);
   1023   snprintf(check_root.target, sizeof check_root.target, "%s", direct->target);
   1024   memcpy(check_root.recipe, direct->recipe, BUILD_HASH_LEN);
   1025   check_root.configs = projected_configs;
   1026   check_root.n_configs = n_projected_configs;
   1027   check_root.sources = direct->sources;
   1028   check_root.n_sources = direct->n_sources;
   1029   check_root.globs = direct->globs;
   1030   check_root.n_globs = direct->n_globs;
   1031   check_root.blobs = direct->blobs;
   1032   check_root.n_blobs = direct->n_blobs;
   1033   check_root.children = child_ptrs;
   1034   check_root.n_children = nchildren;
   1035   if (build_leafset_check_consistent(c, &check_root, 0, err, sizeof err) !=
   1036       BUILD_OK) {
   1037     build_diagf(c->ctx, "build: inconsistent deepset closure: %s", err);
   1038     goto out;
   1039   }
   1040   memset(&ds, 0, sizeof ds);
   1041   snprintf(ds.target, sizeof ds.target, "%s", direct->target);
   1042   memcpy(ds.recipe, direct->recipe, BUILD_HASH_LEN);
   1043   ds.configs = projected_configs;
   1044   ds.n_configs = n_projected_configs;
   1045   ds.sources = direct->sources;
   1046   ds.n_sources = direct->n_sources;
   1047   ds.globs = direct->globs;
   1048   ds.n_globs = direct->n_globs;
   1049   ds.blobs = direct->blobs;
   1050   ds.n_blobs = direct->n_blobs;
   1051   ds.children = child_ids;
   1052   ds.n_children = nchildren;
   1053   if (kit_writer_mem(c->ctx->heap, &w) != KIT_OK || !w) goto out;
   1054   if (build_deepset_emit(&ds, w, err, sizeof err) != BUILD_OK ||
   1055       kit_writer_status(w) != KIT_OK)
   1056     goto out;
   1057   bytes = kit_writer_mem_bytes(w, &len);
   1058   build_deepset_id(bytes, len, leaf.id);
   1059   if (kit_cas_add_blob(c->cas, bytes, len, &bi) != KIT_OK ||
   1060       !build_id_eq(bi.id, leaf.id))
   1061     goto out;
   1062   memset(&leaf, 0, sizeof leaf);
   1063   build_deepset_id(bytes, len, leaf.id);
   1064   snprintf(leaf.target, sizeof leaf.target, "%s", direct->target);
   1065   memcpy(leaf.recipe, direct->recipe, BUILD_HASH_LEN);
   1066   leaf.configs = projected_configs;
   1067   leaf.n_configs = n_projected_configs;
   1068   leaf.sources = direct->sources;
   1069   leaf.n_sources = direct->n_sources;
   1070   leaf.globs = direct->globs;
   1071   leaf.n_globs = direct->n_globs;
   1072   leaf.blobs = direct->blobs;
   1073   leaf.n_blobs = direct->n_blobs;
   1074   leaf.children = child_ptrs;
   1075   leaf.n_children = nchildren;
   1076   if (build_coord_leafset_intern(c, &leaf, out) != BUILD_OK) goto out;
   1077   ok = BUILD_OK;
   1078 out:
   1079   if (w) kit_writer_close(w);
   1080   if (child_ids)
   1081     c->ctx->heap->free(c->ctx->heap, child_ids,
   1082                        nchildren * sizeof *child_ids);
   1083   if (child_ptrs)
   1084     c->ctx->heap->free(c->ctx->heap, child_ptrs,
   1085                        nchildren * sizeof *child_ptrs);
   1086   if (projected_configs)
   1087     c->ctx->heap->free(c->ctx->heap, projected_configs,
   1088                        cap_projected_configs * sizeof *projected_configs);
   1089   return ok;
   1090 }
   1091 
   1092 static int build_leafset_refresh_inner(KitBuildCoordinator* c,
   1093                                        const BuildLeafSet* leafset,
   1094                                        const BuildConfig* cfg,
   1095                                        int check_config,
   1096                                        const char* trace_root,
   1097                                        int* all_match) {
   1098   size_t i;
   1099   KitSlice target;
   1100   uint8_t recipe[BUILD_HASH_LEN];
   1101   int matched_all = 1;
   1102   if (!c || !leafset || !cfg || !all_match) return BUILD_ERR;
   1103   *all_match = 0;
   1104   target = kit_slice_cstr(leafset->target);
   1105   if (build_coord_recipe_id(c, target, recipe) != BUILD_OK ||
   1106       !build_id_eq(recipe, leafset->recipe))
   1107     goto done;
   1108   if (check_config) {
   1109     for (i = 0; i < leafset->n_configs; ++i) {
   1110       int matched = config_observation_match(cfg, &leafset->configs[i]);
   1111       if (c->opts.trace)
   1112         build_coord_tracef(c,
   1113                            "deep-config target=%s key=%s scope=root result=%s",
   1114                            leafset->target, leafset->configs[i].key,
   1115                            matched ? "match" : "mismatch");
   1116       if (!matched) matched_all = 0;
   1117     }
   1118   } else if (c->opts.trace) {
   1119     for (i = 0; i < leafset->n_configs; ++i)
   1120       build_coord_tracef(c,
   1121                          "deep-config target=%s child=%s key=%s scope=overlay-shielded",
   1122                          trace_root ? trace_root : leafset->target,
   1123                          leafset->target, leafset->configs[i].key);
   1124   }
   1125   for (i = 0; i < leafset->n_sources; ++i) {
   1126     uint8_t blob[BUILD_HASH_LEN];
   1127     int present = 0;
   1128     if (build_coord_source_hash_target(c, target,
   1129                                        kit_slice_cstr(leafset->sources[i].path),
   1130                                        blob, &present) != BUILD_OK)
   1131       return BUILD_ERR;
   1132     if (leafset->sources[i].absent) {
   1133       if (present) matched_all = 0;
   1134     } else if (!present || !build_id_eq(blob, leafset->sources[i].blob)) {
   1135       matched_all = 0;
   1136     }
   1137   }
   1138   for (i = 0; i < leafset->n_globs; ++i) {
   1139     uint8_t hash[BUILD_HASH_LEN];
   1140     if (build_coord_glob_target(c, target,
   1141                                 kit_slice_cstr(leafset->globs[i].pattern),
   1142                                 hash, NULL, NULL) != BUILD_OK)
   1143       return BUILD_ERR;
   1144     if (!build_id_eq(hash, leafset->globs[i].result_hash)) matched_all = 0;
   1145   }
   1146   for (i = 0; i < leafset->n_blobs; ++i) {
   1147     if (kit_cas_has_blob(c->cas, leafset->blobs[i].blob) != KIT_OK)
   1148       matched_all = 0;
   1149   }
   1150   for (i = 0; i < leafset->n_children; ++i) {
   1151     int child_match = 0;
   1152     if (build_leafset_refresh_inner(c, leafset->children[i], cfg, 0,
   1153                                     trace_root ? trace_root : leafset->target,
   1154                                     &child_match) != BUILD_OK)
   1155       return BUILD_ERR;
   1156     if (!child_match) matched_all = 0;
   1157   }
   1158   if (matched_all) *all_match = 1;
   1159 done:
   1160   return BUILD_OK;
   1161 }
   1162 
   1163 int build_leafset_refresh(KitBuildCoordinator* c, const BuildLeafSet* leafset,
   1164                           const BuildConfig* cfg, int* all_match) {
   1165   return build_leafset_refresh_inner(c, leafset, cfg, 1,
   1166                                      leafset ? leafset->target : NULL,
   1167                                      all_match);
   1168 }