coord.c (61479B)
1 #include "coord.h" 2 3 #include "bundle.h" 4 #include "dist/tar.h" 5 6 #include <kit/compress.h> 7 #include <kit/package.h> 8 9 #include <stdarg.h> 10 #include <stdio.h> 11 #include <string.h> 12 #include <stdlib.h> 13 14 struct BuildTargetFuture { 15 char target[BUILD_TARGET_MAX]; 16 uint8_t config_id[BUILD_HASH_LEN]; 17 uint8_t argv_id[BUILD_HASH_LEN]; 18 int done; 19 int failed; 20 BuildResolved result; 21 struct BuildTargetFuture* next; 22 }; 23 24 struct BuildTargetTable { 25 BuildTargetFuture* futures; 26 }; 27 28 struct BuildPulledSet { 29 char target[BUILD_TARGET_MAX]; 30 struct BuildPulledSet* next; 31 }; 32 33 struct BuildSourceMemo { 34 char root[BUILD_PATH_MAX]; 35 char path[BUILD_PATH_MAX]; 36 uint8_t blob[BUILD_HASH_LEN]; 37 int present; 38 struct BuildSourceMemo* next; 39 }; 40 41 struct BuildGlobMemo { 42 char root[BUILD_PATH_MAX]; 43 char pattern[BUILD_PATTERN_MAX]; 44 uint8_t result_hash[BUILD_HASH_LEN]; 45 BuildPathBlob* entries; 46 size_t n_entries; 47 struct BuildGlobMemo* next; 48 }; 49 50 typedef struct BuildDeepSetMemo { 51 BuildLeafSet leaf; 52 BuildConfigLeaf* configs; 53 BuildSourceLeaf* sources; 54 BuildGlobLeaf* globs; 55 BuildBlobLeaf* blobs; 56 const BuildLeafSet** children; 57 int valid_known; 58 int valid; 59 struct BuildDeepSetMemo* next; 60 } BuildDeepSetMemo; 61 62 struct BuildExternalWorkspace { 63 char name[BUILD_KEY_MAX]; 64 char root[BUILD_PATH_MAX]; 65 char def_name[BUILD_PATH_MAX]; 66 BuildWorkspace workspace; 67 struct BuildExternalWorkspace* next; 68 }; 69 70 typedef struct BuildGlobExpand { 71 KitBuildCoordinator* c; 72 const char* root; 73 KitSlice pattern; 74 BuildPathBlob* entries; 75 size_t n; 76 size_t cap; 77 int failed; 78 } BuildGlobExpand; 79 80 static int build_coord_source_hash_root(KitBuildCoordinator* c, 81 const char* root, KitSlice path, 82 uint8_t out_blob[BUILD_HASH_LEN], 83 int* present); 84 static int build_coord_glob_root(KitBuildCoordinator* c, const char* root, 85 KitSlice pattern, 86 uint8_t out_result_hash[BUILD_HASH_LEN], 87 BuildCoordGlobFn cb, void* cb_user); 88 static void release_file_data(KitBuildCoordinator* c, KitFileData* fd); 89 90 static int path_set(char* out, size_t cap, KitSlice s) { 91 if (!out || cap == 0u || !s.s || s.len + 1u > cap) return BUILD_ERR; 92 memcpy(out, s.s, s.len); 93 out[s.len] = '\0'; 94 return BUILD_OK; 95 } 96 97 static int path_join2(char* out, size_t cap, const char* a, const char* b) { 98 size_t na, nb; 99 int need_sep; 100 if (!out || cap == 0u || !a || !b) return BUILD_ERR; 101 na = strlen(a); 102 nb = strlen(b); 103 need_sep = na > 0u && a[na - 1u] != '/'; 104 if (na + (need_sep ? 1u : 0u) + nb + 1u > cap) return BUILD_ERR; 105 memcpy(out, a, na); 106 if (need_sep) out[na++] = '/'; 107 memcpy(out + na, b, nb); 108 out[na + nb] = '\0'; 109 return BUILD_OK; 110 } 111 112 static int slice_copy(char* out, size_t cap, KitSlice s) { 113 if (!out || cap == 0u || !s.s || s.len + 1u > cap) return BUILD_ERR; 114 memcpy(out, s.s, s.len); 115 out[s.len] = '\0'; 116 return BUILD_OK; 117 } 118 119 static int rel_path_safe(KitSlice s) { 120 size_t i, start = 0; 121 if (!s.s || s.len == 0u || s.len >= BUILD_PATH_MAX) return 0; 122 if (s.s[0] == '/') return 0; 123 for (i = 0; i <= s.len; ++i) { 124 if (i == s.len || s.s[i] == '/') { 125 size_t n = i - start; 126 if (n == 0u) return 0; 127 if (n == 1u && s.s[start] == '.') return 0; 128 if (n == 2u && s.s[start] == '.' && s.s[start + 1u] == '.') return 0; 129 start = i + 1u; 130 } else if (s.s[i] == '\0' || s.s[i] == '\\' || s.s[i] == ':') { 131 return 0; 132 } 133 } 134 return 1; 135 } 136 137 static int glob_pattern_safe(KitSlice s) { 138 size_t i, start = 0; 139 if (!s.s || s.len == 0u || s.len >= BUILD_PATTERN_MAX) return 0; 140 if (s.s[0] == '/') return 0; 141 for (i = 0; i <= s.len; ++i) { 142 if (i == s.len || s.s[i] == '/') { 143 size_t n = i - start; 144 if (n == 0u) return 0; 145 if (n == 1u && s.s[start] == '.') return 0; 146 if (n == 2u && s.s[start] == '.' && s.s[start + 1u] == '.') return 0; 147 start = i + 1u; 148 } else if (s.s[i] == '\0' || s.s[i] == '\\' || s.s[i] == ':' || 149 s.s[i] == '[' || s.s[i] == ']') { 150 return 0; 151 } 152 } 153 return 1; 154 } 155 156 static int glob_match_range(const char* pat, size_t pn, const char* text, 157 size_t tn) { 158 size_t pi = 0, ti = 0; 159 size_t star = (size_t)-1, mark = 0; 160 while (ti < tn) { 161 if (pi < pn && pat[pi] == '*') { 162 star = pi++; 163 mark = ti; 164 } else if (pi < pn && (pat[pi] == '?' || pat[pi] == text[ti])) { 165 ++pi; 166 ++ti; 167 } else if (star != (size_t)-1) { 168 pi = star + 1u; 169 ti = ++mark; 170 } else { 171 return 0; 172 } 173 } 174 while (pi < pn && pat[pi] == '*') ++pi; 175 return pi == pn; 176 } 177 178 static int glob_segment_double_star(const char* pat, size_t start, 179 size_t end) { 180 return end == start + 2u && pat[start] == '*' && pat[start + 1u] == '*'; 181 } 182 183 static int glob_match_segments(const char* pat, size_t pn, size_t pp, 184 const char* text, size_t tn, size_t tp) { 185 size_t pe = pp, te = tp; 186 if (pp == pn) return tp == tn; 187 while (pe < pn && pat[pe] != '/') ++pe; 188 if (glob_segment_double_star(pat, pp, pe)) { 189 size_t next_pp = pe < pn ? pe + 1u : pn; 190 if (glob_match_segments(pat, pn, next_pp, text, tn, tp)) return 1; 191 if (tp == tn) return 0; 192 while (te < tn && text[te] != '/') ++te; 193 return glob_match_segments(pat, pn, pp, text, tn, 194 te < tn ? te + 1u : tn); 195 } 196 if (tp == tn) return 0; 197 while (te < tn && text[te] != '/') ++te; 198 if (!glob_match_range(pat + pp, pe - pp, text + tp, te - tp)) return 0; 199 if (pe == pn || te == tn) return pe == pn && te == tn; 200 return glob_match_segments(pat, pn, pe + 1u, text, tn, te + 1u); 201 } 202 203 static int glob_match_path(KitSlice pattern, const char* path) { 204 if (!pattern.s || !path) return 0; 205 return glob_match_segments(pattern.s, pattern.len, 0, path, strlen(path), 0); 206 } 207 208 static int path_blob_cmp_qsort(const void* a, const void* b) { 209 const BuildPathBlob* pa = (const BuildPathBlob*)a; 210 const BuildPathBlob* pb = (const BuildPathBlob*)b; 211 return strcmp(pa->path, pb->path); 212 } 213 214 static int glob_entries_push(BuildGlobExpand* g, const char* rel, 215 const uint8_t blob[BUILD_HASH_LEN]) { 216 BuildPathBlob* next; 217 size_t new_cap; 218 if (!g || !rel || strlen(rel) >= BUILD_PATH_MAX) return BUILD_ERR; 219 if (g->n == g->cap) { 220 new_cap = g->cap ? g->cap * 2u : 16u; 221 next = (BuildPathBlob*)g->c->ctx->heap->realloc( 222 g->c->ctx->heap, g->entries, g->cap * sizeof *g->entries, 223 new_cap * sizeof *g->entries, _Alignof(BuildPathBlob)); 224 if (!next) return BUILD_ERR; 225 g->entries = next; 226 g->cap = new_cap; 227 } 228 snprintf(g->entries[g->n].path, sizeof g->entries[g->n].path, "%s", rel); 229 memcpy(g->entries[g->n].blob, blob, BUILD_HASH_LEN); 230 ++g->n; 231 return BUILD_OK; 232 } 233 234 static int glob_walk_cb(void* user, const char* source_path, 235 const char* tree_path, int executable) { 236 BuildGlobExpand* g = (BuildGlobExpand*)user; 237 uint8_t blob[BUILD_HASH_LEN]; 238 int present = 0; 239 (void)source_path; 240 (void)executable; 241 if (!g || !tree_path) return 1; 242 if (!glob_match_path(g->pattern, tree_path)) return 0; 243 if (build_coord_source_hash_root(g->c, g->root, kit_slice_cstr(tree_path), 244 blob, &present) != BUILD_OK || 245 !present || 246 glob_entries_push(g, tree_path, blob) != BUILD_OK) { 247 g->failed = 1; 248 return 1; 249 } 250 return 0; 251 } 252 253 static int read_workspace_manifest(KitBuildCoordinator* c, const char* root, 254 BuildWorkspace* out, int* present) { 255 char path[BUILD_PATH_MAX]; 256 KitFileData fd; 257 char err[160]; 258 if (!c || !root || !out || !present) return BUILD_ERR; 259 *present = 0; 260 build_workspace_init(out); 261 if (path_join2(path, sizeof path, root, "WORKSPACE.kit") != BUILD_OK) 262 return BUILD_ERR; 263 memset(&fd, 0, sizeof fd); 264 if (c->host.cas_host->file_io->read_all(c->host.cas_host->file_io->user, path, 265 &fd) != KIT_OK) 266 return BUILD_OK; 267 *present = 1; 268 if (build_workspace_parse(fd.data, fd.size, out, err, sizeof err) != 269 BUILD_OK) { 270 build_diagf(c->ctx, "build: %s: %s", path, err); 271 release_file_data(c, &fd); 272 return BUILD_ERR; 273 } 274 release_file_data(c, &fd); 275 return BUILD_OK; 276 } 277 278 static int external_key(const BuildWorkspaceExternal* ext, char out[BUILD_HEX_LEN]) { 279 KitBlobInfo bi; 280 char buf[BUILD_PATH_MAX + 160]; 281 char archive[BUILD_HEX_LEN], package[BUILD_HEX_LEN]; 282 const char* fmt = "unknown"; 283 if (!ext || !out) return BUILD_ERR; 284 if (ext->format == BUILD_WS_EXT_TREE) fmt = "tree"; 285 if (ext->format == BUILD_WS_EXT_KPKG) fmt = "kpkg"; 286 if (ext->format == BUILD_WS_EXT_TARGZ) fmt = "tar.gz"; 287 kit_hex_encode(archive, ext->archive, BUILD_HASH_LEN); 288 if (ext->has_package) 289 kit_hex_encode(package, ext->package, BUILD_HASH_LEN); 290 else 291 package[0] = '\0'; 292 snprintf(buf, sizeof buf, "%s %s %s %s", fmt, archive, package, 293 ext->strip_prefix); 294 kit_blob_info(&bi, (const uint8_t*)buf, strlen(buf)); 295 kit_hex_encode(out, bi.id, BUILD_HASH_LEN); 296 return BUILD_OK; 297 } 298 299 static int path_parent_dir(const char* path, char* out, size_t cap) { 300 size_t n, i; 301 if (!path || !out || cap == 0u) return BUILD_ERR; 302 n = strlen(path); 303 while (n && (path[n - 1u] == '/' || path[n - 1u] == '\\')) --n; 304 for (i = n; i > 0u; --i) { 305 if (path[i - 1u] == '/' || path[i - 1u] == '\\') { 306 if (i > cap) return BUILD_ERR; 307 memcpy(out, path, i - 1u); 308 out[i - 1u] = '\0'; 309 return BUILD_OK; 310 } 311 } 312 out[0] = '\0'; 313 return BUILD_OK; 314 } 315 316 static int external_write_file(KitBuildCoordinator* c, const char* root, 317 const char* rel, const uint8_t* data, 318 size_t len, int executable) { 319 char path[BUILD_PATH_MAX]; 320 char parent[BUILD_PATH_MAX]; 321 KitWriter* w = NULL; 322 KitStatus st; 323 if (!c || !root || !rel || (!data && len)) return BUILD_ERR; 324 if (path_join2(path, sizeof path, root, rel) != BUILD_OK || 325 path_parent_dir(path, parent, sizeof parent) != BUILD_OK) 326 return BUILD_ERR; 327 if (parent[0] && c->host.cas_host->mkdir_p && 328 c->host.cas_host->mkdir_p(c->host.cas_host->user, parent) != 0) 329 return BUILD_ERR; 330 if (!c->host.cas_host->file_io || 331 c->host.cas_host->file_io->open_writer(c->host.cas_host->file_io->user, 332 path, &w) != KIT_OK || 333 !w) 334 return BUILD_ERR; 335 st = len ? kit_writer_write(w, data, len) : KIT_OK; 336 if (st == KIT_OK) st = kit_writer_status(w); 337 kit_writer_close(w); 338 if (st != KIT_OK) return BUILD_ERR; 339 if (executable && c->host.cas_host->mark_executable && 340 c->host.cas_host->mark_executable(c->host.cas_host->user, path) != 0) 341 return BUILD_ERR; 342 return BUILD_OK; 343 } 344 345 static int external_tar_relpath(const BuildWorkspaceExternal* ext, 346 const DistTarEntry* e, 347 char out[BUILD_PATH_MAX], int* skip) { 348 const char* name; 349 const char* rel; 350 size_t prefix_len; 351 if (!ext || !e || !out || !skip) return BUILD_ERR; 352 *skip = 0; 353 name = e->name; 354 if (!name[0]) return BUILD_ERR; 355 if (ext->strip_prefix[0]) { 356 prefix_len = strlen(ext->strip_prefix); 357 if (strncmp(name, ext->strip_prefix, prefix_len) != 0) 358 return BUILD_ERR; 359 if (name[prefix_len] == '\0') { 360 *skip = 1; 361 return BUILD_OK; 362 } 363 if (name[prefix_len] != '/') return BUILD_ERR; 364 rel = name + prefix_len + 1u; 365 } else { 366 rel = name; 367 } 368 if (!rel[0]) { 369 *skip = 1; 370 return BUILD_OK; 371 } 372 if (strlen(rel) >= BUILD_PATH_MAX || !rel_path_safe(kit_slice_cstr(rel))) 373 return BUILD_ERR; 374 snprintf(out, BUILD_PATH_MAX, "%s", rel); 375 return BUILD_OK; 376 } 377 378 static int materialize_targz_external(KitBuildCoordinator* c, 379 const BuildWorkspaceExternal* ext, 380 const char* root) { 381 KitSlice urls[8]; 382 KitFileData fd; 383 KitWriter* tar_w = NULL; 384 const uint8_t* tar_bytes; 385 size_t tar_len; 386 DistTarEntry entries[DIST_MAX_FILES]; 387 size_t nentries = 0; 388 char blob_path[BUILD_PATH_MAX]; 389 size_t i; 390 int ok = BUILD_ERR; 391 if (!c || !ext || !root) return BUILD_ERR; 392 for (i = 0; i < ext->n_urls; ++i) urls[i] = kit_slice_cstr(ext->urls[i]); 393 if (build_coord_fetch_blob(c, ext->archive, urls, ext->n_urls, blob_path, 394 sizeof blob_path) != BUILD_OK) 395 return BUILD_ERR; 396 memset(&fd, 0, sizeof fd); 397 if (kit_cas_get_blob(c->cas, ext->archive, &fd) != KIT_OK) return BUILD_ERR; 398 if (kit_writer_mem(c->ctx->heap, &tar_w) != KIT_OK || !tar_w) 399 goto out; 400 if (kit_decompress(c->ctx, KIT_COMPRESS_GZIP, fd.data, fd.size, tar_w) != 401 KIT_OK || 402 kit_writer_status(tar_w) != KIT_OK) 403 goto out; 404 tar_bytes = kit_writer_mem_bytes(tar_w, &tar_len); 405 if (dist_tar_iter(tar_bytes, tar_len, entries, 406 sizeof entries / sizeof entries[0], &nentries) != DIST_OK) 407 goto out; 408 for (i = 0; i < nentries; ++i) { 409 char rel[BUILD_PATH_MAX]; 410 int skip = 0; 411 if (entries[i].type == '5') continue; 412 if (entries[i].type != '\0' && entries[i].type != '0') goto out; 413 if (external_tar_relpath(ext, &entries[i], rel, &skip) != BUILD_OK) 414 goto out; 415 if (skip) continue; 416 if (external_write_file(c, root, rel, entries[i].data, entries[i].size, 417 (entries[i].mode & 0111u) != 0u) != BUILD_OK) 418 goto out; 419 } 420 ok = BUILD_OK; 421 out: 422 if (tar_w) kit_writer_close(tar_w); 423 kit_cas_release(c->cas, &fd); 424 return ok; 425 } 426 427 static int trusted_pkgid(const char* trusted, uint8_t out[BUILD_HASH_LEN], 428 int* present) { 429 const char* p; 430 char hex[BUILD_HEX_LEN]; 431 if (!trusted || !out || !present) return BUILD_ERR; 432 *present = 0; 433 p = strstr(trusted, "pkgid="); 434 if (!p) return BUILD_OK; 435 p += 6u; 436 if (strlen(p) < BUILD_HEX_LEN - 1u) return BUILD_ERR; 437 memcpy(hex, p, BUILD_HEX_LEN - 1u); 438 hex[BUILD_HEX_LEN - 1u] = '\0'; 439 if (kit_hex_decode(out, hex, BUILD_HASH_LEN) != KIT_OK) return BUILD_ERR; 440 *present = 1; 441 return BUILD_OK; 442 } 443 444 static int ensure_external_workspace(KitBuildCoordinator* c, 445 const BuildWorkspace* parent_ws, 446 KitSlice repo, KitSlice instance, 447 BuildExternalWorkspace** out) { 448 BuildExternalWorkspace* ew; 449 const BuildWorkspaceExternal* ext; 450 char key[BUILD_HEX_LEN]; 451 char parent[BUILD_PATH_MAX]; 452 char root[BUILD_PATH_MAX]; 453 int present = 0; 454 if (!c || !parent_ws || !out || !repo.s || repo.len == 0u || 455 !instance.s || instance.len == 0u || instance.len >= BUILD_KEY_MAX) 456 return BUILD_ERR; 457 for (ew = c->externals; ew; ew = ew->next) { 458 if (strlen(ew->name) == instance.len && 459 memcmp(ew->name, instance.s, instance.len) == 0) { 460 *out = ew; 461 return BUILD_OK; 462 } 463 } 464 ext = build_workspace_external_find(parent_ws, repo); 465 if (!ext) { 466 build_diagf(c->ctx, "build: unknown external repo @%.*s", 467 KIT_SLICE_ARG(repo)); 468 return BUILD_ERR; 469 } 470 if (external_key(ext, key) != BUILD_OK || 471 path_join2(parent, sizeof parent, c->store.root, "external") != 472 BUILD_OK || 473 path_join2(root, sizeof root, parent, key) != BUILD_OK) 474 return BUILD_ERR; 475 if (c->host.cas_host->mkdir_p && 476 c->host.cas_host->mkdir_p(c->host.cas_host->user, parent) != 0) 477 return BUILD_ERR; 478 if (ext->format == BUILD_WS_EXT_TREE) { 479 if (kit_cas_verify_tree(c->cas, ext->archive) != KIT_OK) 480 return BUILD_ERR; 481 if (c->host.cas_host->mkdir_p && 482 c->host.cas_host->mkdir_p(c->host.cas_host->user, root) != 0) 483 return BUILD_ERR; 484 if (kit_cas_materialize_tree(c->cas, ext->archive, root) != KIT_OK) 485 return BUILD_ERR; 486 } else if (ext->format == BUILD_WS_EXT_KPKG) { 487 KitFileData fd; 488 KitPkgVerifyOptions opts; 489 KitPkgVerifyResult result; 490 KitSlice urls[8]; 491 size_t i; 492 char blob_path[BUILD_PATH_MAX]; 493 for (i = 0; i < ext->n_urls; ++i) urls[i] = kit_slice_cstr(ext->urls[i]); 494 if (build_coord_fetch_blob(c, ext->archive, urls, ext->n_urls, blob_path, 495 sizeof blob_path) != BUILD_OK) 496 return BUILD_ERR; 497 memset(&fd, 0, sizeof fd); 498 if (kit_cas_get_blob(c->cas, ext->archive, &fd) != KIT_OK) return BUILD_ERR; 499 memset(&opts, 0, sizeof opts); 500 memset(&result, 0, sizeof result); 501 opts.pkg_data = fd.data; 502 opts.pkg_len = fd.size; 503 opts.format = KIT_PKG_FORMAT_KPKG; 504 opts.unpack_dir = root; 505 opts.tofu = 1; 506 if (kit_pkg_verify(c->ctx, c->host.cas_host, &opts, &result) != KIT_OK) { 507 kit_cas_release(c->cas, &fd); 508 return BUILD_ERR; 509 } 510 if (ext->has_package) { 511 uint8_t got[BUILD_HASH_LEN]; 512 int has_pkgid = 0; 513 if (trusted_pkgid(result.trusted, got, &has_pkgid) != BUILD_OK || 514 !has_pkgid || !build_id_eq(got, ext->package)) { 515 kit_cas_release(c->cas, &fd); 516 return BUILD_ERR; 517 } 518 } 519 kit_cas_release(c->cas, &fd); 520 } else if (ext->format == BUILD_WS_EXT_TARGZ) { 521 if (c->host.cas_host->mkdir_p && 522 c->host.cas_host->mkdir_p(c->host.cas_host->user, root) != 0) 523 return BUILD_ERR; 524 if (materialize_targz_external(c, ext, root) != BUILD_OK) 525 return BUILD_ERR; 526 } else { 527 return BUILD_ERR; 528 } 529 ew = (BuildExternalWorkspace*)c->ctx->heap->alloc( 530 c->ctx->heap, sizeof *ew, _Alignof(BuildExternalWorkspace)); 531 if (!ew) return BUILD_ERR; 532 memset(ew, 0, sizeof *ew); 533 memcpy(ew->name, instance.s, instance.len); 534 ew->name[instance.len] = '\0'; 535 snprintf(ew->root, sizeof ew->root, "%s", root); 536 if (read_workspace_manifest(c, root, &ew->workspace, &present) != BUILD_OK) { 537 c->ctx->heap->free(c->ctx->heap, ew, sizeof *ew); 538 return BUILD_ERR; 539 } 540 snprintf(ew->def_name, sizeof ew->def_name, "%s", 541 ew->workspace.def_name[0] ? ew->workspace.def_name : "BUILD.kit"); 542 ew->next = c->externals; 543 c->externals = ew; 544 *out = ew; 545 return BUILD_OK; 546 } 547 548 static int target_workspace(KitBuildCoordinator* c, KitSlice target, 549 char repo[BUILD_KEY_MAX], const char** root_out, 550 const char** def_name_out, 551 const BuildWorkspace** ws_out) { 552 char package[BUILD_PATH_MAX]; 553 char local[BUILD_TARGET_MAX]; 554 if (!c || !root_out || !def_name_out || !ws_out) return BUILD_ERR; 555 if (build_target_split_repo(target, repo, package, local) != BUILD_OK) 556 return BUILD_ERR; 557 if (repo && repo[0]) { 558 const BuildWorkspace* cur_ws = &c->root_workspace; 559 const char* cur_root = c->workspace_root; 560 const char* cur_def_name = c->build_def_name; 561 char instance[BUILD_KEY_MAX]; 562 size_t instance_len = 0; 563 size_t pos = 0; 564 size_t repo_len = strlen(repo); 565 while (pos < repo_len) { 566 BuildExternalWorkspace* ew = NULL; 567 KitSlice name; 568 KitSlice inst; 569 size_t start = pos; 570 while (pos < repo_len && repo[pos] != '+') ++pos; 571 if (pos == start) return BUILD_ERR; 572 if (instance_len) { 573 if (instance_len + 1u >= sizeof instance) return BUILD_ERR; 574 instance[instance_len++] = '+'; 575 } 576 if (instance_len + (pos - start) + 1u > sizeof instance) 577 return BUILD_ERR; 578 memcpy(instance + instance_len, repo + start, pos - start); 579 instance_len += pos - start; 580 instance[instance_len] = '\0'; 581 name.s = repo + start; 582 name.len = pos - start; 583 inst = kit_slice_cstr(instance); 584 if (ensure_external_workspace(c, cur_ws, name, inst, &ew) != BUILD_OK) 585 return BUILD_ERR; 586 cur_ws = &ew->workspace; 587 cur_root = ew->root; 588 cur_def_name = ew->def_name; 589 if (pos < repo_len && repo[pos] == '+') { 590 ++pos; 591 if (pos == repo_len) return BUILD_ERR; 592 } 593 } 594 *root_out = cur_root; 595 *def_name_out = cur_def_name; 596 *ws_out = cur_ws; 597 } else { 598 *root_out = c->workspace_root; 599 *def_name_out = c->build_def_name; 600 *ws_out = &c->root_workspace; 601 } 602 return BUILD_OK; 603 } 604 605 static void free_deepsets(KitBuildCoordinator* c) { 606 BuildDeepSetMemo* n; 607 if (!c || !c->ctx || !c->ctx->heap) return; 608 n = c->deepsets; 609 while (n) { 610 BuildDeepSetMemo* next = n->next; 611 if (n->configs) 612 c->ctx->heap->free(c->ctx->heap, n->configs, 613 n->leaf.n_configs * sizeof *n->configs); 614 if (n->sources) 615 c->ctx->heap->free(c->ctx->heap, n->sources, 616 n->leaf.n_sources * sizeof *n->sources); 617 if (n->globs) 618 c->ctx->heap->free(c->ctx->heap, n->globs, 619 n->leaf.n_globs * sizeof *n->globs); 620 if (n->blobs) 621 c->ctx->heap->free(c->ctx->heap, n->blobs, 622 n->leaf.n_blobs * sizeof *n->blobs); 623 if (n->children) 624 c->ctx->heap->free(c->ctx->heap, n->children, 625 n->leaf.n_children * sizeof *n->children); 626 c->ctx->heap->free(c->ctx->heap, n, sizeof *n); 627 n = next; 628 } 629 c->deepsets = NULL; 630 } 631 632 static void free_targets(KitBuildCoordinator* c) { 633 BuildTargetFuture* f; 634 BuildTargetTable* t; 635 if (!c || !c->ctx || !c->ctx->heap || !c->targets) return; 636 t = c->targets; 637 f = t->futures; 638 while (f) { 639 BuildTargetFuture* next = f->next; 640 c->ctx->heap->free(c->ctx->heap, f, sizeof *f); 641 f = next; 642 } 643 c->ctx->heap->free(c->ctx->heap, t, sizeof *t); 644 c->targets = NULL; 645 } 646 647 static void free_pulled(KitBuildCoordinator* c) { 648 BuildPulledSet* n; 649 if (!c || !c->ctx || !c->ctx->heap) return; 650 n = c->pulled; 651 while (n) { 652 BuildPulledSet* next = n->next; 653 c->ctx->heap->free(c->ctx->heap, n, sizeof *n); 654 n = next; 655 } 656 c->pulled = NULL; 657 } 658 659 static void free_external_workspaces(KitBuildCoordinator* c) { 660 BuildExternalWorkspace* ew; 661 if (!c || !c->ctx || !c->ctx->heap) return; 662 ew = c->externals; 663 while (ew) { 664 BuildExternalWorkspace* next = ew->next; 665 c->ctx->heap->free(c->ctx->heap, ew, sizeof *ew); 666 ew = next; 667 } 668 c->externals = NULL; 669 } 670 671 static void free_sources(KitBuildCoordinator* c) { 672 BuildSourceMemo* n; 673 if (!c || !c->ctx || !c->ctx->heap) return; 674 n = c->sources; 675 while (n) { 676 BuildSourceMemo* next = n->next; 677 c->ctx->heap->free(c->ctx->heap, n, sizeof *n); 678 n = next; 679 } 680 c->sources = NULL; 681 } 682 683 static void free_globs(KitBuildCoordinator* c) { 684 BuildGlobMemo* n; 685 if (!c || !c->ctx || !c->ctx->heap) return; 686 n = c->globs; 687 while (n) { 688 BuildGlobMemo* next = n->next; 689 if (n->entries) 690 c->ctx->heap->free(c->ctx->heap, n->entries, 691 n->n_entries * sizeof *n->entries); 692 c->ctx->heap->free(c->ctx->heap, n, sizeof *n); 693 n = next; 694 } 695 c->globs = NULL; 696 } 697 698 void build_coord_stat_bump(KitBuildCoordinator* c, BuildStatField f) { 699 if (!c) return; 700 switch (f) { 701 case BUILD_STAT_DEEP_HIT: ++c->stats.deep_hits; break; 702 case BUILD_STAT_SHALLOW_HIT: ++c->stats.shallow_hits; break; 703 case BUILD_STAT_RECIPE_RUN: ++c->stats.recipes_run; break; 704 case BUILD_STAT_MATERIALIZE_MISS: ++c->stats.materialize_misses; break; 705 case BUILD_STAT_OBJECT_FETCH: ++c->stats.object_fetches; break; 706 case BUILD_STAT_TRACE_PULL: ++c->stats.trace_pulls; break; 707 case BUILD_STAT_TEST_RUN: ++c->stats.test_runs; break; 708 case BUILD_STAT_TEST_CACHE_HIT: ++c->stats.test_cache_hits; break; 709 case BUILD_STAT_TEST_FAILURE: ++c->stats.test_failures; break; 710 } 711 } 712 713 void build_coord_tracef(KitBuildCoordinator* c, const char* fmt, ...) { 714 char line[1024]; 715 KitSlice slice; 716 va_list ap; 717 int n; 718 if (!c || !c->opts.trace || !fmt) return; 719 va_start(ap, fmt); 720 n = vsnprintf(line, sizeof line, fmt, ap); 721 va_end(ap); 722 if (n < 0) return; 723 if ((size_t)n >= sizeof line) { 724 line[sizeof line - 1u] = '\0'; 725 n = (int)strlen(line); 726 } 727 slice.s = line; 728 slice.len = (size_t)n; 729 c->opts.trace(c->opts.trace_user, slice); 730 } 731 732 KitStatus build_coord_open(const KitContext* ctx, const KitBuildHost* host, 733 KitSlice store_root, const KitBuildOptions* opts, 734 KitBuildCoordinator** out) { 735 KitBuildCoordinator* c; 736 KitHeap* h; 737 int have_workspace = 0; 738 KitStatus st; 739 740 if (!ctx || !ctx->heap || !host || !host->cas_host || 741 !host->cas_host->file_io || !host->store_io || !opts || !out) 742 return KIT_INVALID; 743 *out = NULL; 744 h = ctx->heap; 745 c = (KitBuildCoordinator*)h->alloc(h, sizeof *c, 746 _Alignof(KitBuildCoordinator)); 747 if (!c) return KIT_NOMEM; 748 memset(c, 0, sizeof *c); 749 c->ctx_storage = *ctx; 750 c->ctx = &c->ctx_storage; 751 c->host = *host; 752 c->opts = *opts; 753 c->jobs_limit = opts->jobs > 0 ? opts->jobs : 1; 754 if (!host->sched) c->jobs_limit = 1; 755 if (path_set(c->workspace_root, sizeof c->workspace_root, 756 opts->workspace_root) != BUILD_OK || 757 path_set(c->store_root, sizeof c->store_root, store_root) != BUILD_OK || 758 path_join2(c->cas_root, sizeof c->cas_root, c->store_root, "cas") != 759 BUILD_OK) { 760 h->free(h, c, sizeof *c); 761 return KIT_INVALID; 762 } 763 build_workspace_init(&c->root_workspace); 764 if (read_workspace_manifest(c, c->workspace_root, &c->root_workspace, 765 &have_workspace) != BUILD_OK) { 766 h->free(h, c, sizeof *c); 767 return KIT_MALFORMED; 768 } 769 770 if (!opts->build_def_path.s || opts->build_def_path.len == 0u) { 771 if (path_set(c->build_def_name, sizeof c->build_def_name, 772 kit_slice_cstr(c->root_workspace.def_name[0] 773 ? c->root_workspace.def_name 774 : "BUILD.kit")) != BUILD_OK) { 775 h->free(h, c, sizeof *c); 776 return KIT_INVALID; 777 } 778 } else if (!rel_path_safe(opts->build_def_path) || 779 memchr(opts->build_def_path.s, '/', opts->build_def_path.len) || 780 path_set(c->build_def_name, sizeof c->build_def_name, 781 opts->build_def_path) != BUILD_OK) { 782 build_diagf(&c->ctx_storage, "build: bad build definition name"); 783 h->free(h, c, sizeof *c); 784 return KIT_INVALID; 785 } 786 787 st = kit_cas_open(c->ctx, host->cas_host, c->cas_root, &c->cas); 788 if (st != KIT_OK) { 789 h->free(h, c, sizeof *c); 790 return st; 791 } 792 if (build_store_open(c->ctx, c->cas, host->store_io, host->cas_host, 793 store_root, &c->store) != BUILD_OK) { 794 kit_cas_close(c->cas); 795 h->free(h, c, sizeof *c); 796 return KIT_IO; 797 } 798 *out = c; 799 return KIT_OK; 800 } 801 802 void build_coord_close(KitBuildCoordinator* c) { 803 KitHeap* h; 804 if (!c) return; 805 h = c->ctx->heap; 806 free_targets(c); 807 free_pulled(c); 808 free_external_workspaces(c); 809 free_sources(c); 810 free_globs(c); 811 free_deepsets(c); 812 kit_cas_close(c->cas); 813 h->free(h, c, sizeof *c); 814 } 815 816 int build_coord_source_hash(KitBuildCoordinator* c, KitSlice path, 817 uint8_t out_blob[BUILD_HASH_LEN], int* present) { 818 return build_coord_source_hash_root(c, c ? c->workspace_root : NULL, path, 819 out_blob, present); 820 } 821 822 int build_coord_source_hash_target(KitBuildCoordinator* c, KitSlice target, 823 KitSlice path, 824 uint8_t out_blob[BUILD_HASH_LEN], 825 int* present) { 826 char repo[BUILD_KEY_MAX]; 827 const char* root; 828 const char* def_name; 829 const BuildWorkspace* ws; 830 (void)def_name; 831 (void)ws; 832 if (target_workspace(c, target, repo, &root, &def_name, &ws) != BUILD_OK) 833 return BUILD_ERR; 834 return build_coord_source_hash_root(c, root, path, out_blob, present); 835 } 836 837 static int build_coord_source_hash_root(KitBuildCoordinator* c, const char* root, 838 KitSlice path, 839 uint8_t out_blob[BUILD_HASH_LEN], 840 int* present) { 841 char full[BUILD_PATH_MAX]; 842 char rel[BUILD_PATH_MAX]; 843 KitFileData fd; 844 KitBlobInfo info; 845 BuildSourceMemo* memo; 846 BuildSourceMemo* fresh = NULL; 847 if (!c || !root || !out_blob || !present || !rel_path_safe(path)) 848 return BUILD_ERR; 849 if (slice_copy(rel, sizeof rel, path) != BUILD_OK) return BUILD_ERR; 850 for (memo = c->sources; memo; memo = memo->next) { 851 if (strcmp(memo->root, root) == 0 && strcmp(memo->path, rel) == 0) { 852 memcpy(out_blob, memo->blob, BUILD_HASH_LEN); 853 *present = memo->present; 854 return BUILD_OK; 855 } 856 } 857 fresh = (BuildSourceMemo*)c->ctx->heap->alloc(c->ctx->heap, sizeof *fresh, 858 _Alignof(BuildSourceMemo)); 859 if (!fresh) return BUILD_ERR; 860 memset(fresh, 0, sizeof *fresh); 861 snprintf(fresh->root, sizeof fresh->root, "%s", root); 862 snprintf(fresh->path, sizeof fresh->path, "%s", rel); 863 *present = 0; 864 if (path_join2(full, sizeof full, root, rel) != BUILD_OK) 865 goto err; 866 fd.data = NULL; 867 fd.size = 0; 868 fd.token = NULL; 869 if (c->host.cas_host->file_io->read_all(c->host.cas_host->file_io->user, full, 870 &fd) != KIT_OK) { 871 memset(out_blob, 0, BUILD_HASH_LEN); 872 memset(fresh->blob, 0, BUILD_HASH_LEN); 873 fresh->present = 0; 874 fresh->next = c->sources; 875 c->sources = fresh; 876 return BUILD_OK; 877 } 878 if (kit_cas_add_blob(c->cas, fd.data, fd.size, &info) != KIT_OK) { 879 if (c->host.cas_host->file_io->release) 880 c->host.cas_host->file_io->release(c->host.cas_host->file_io->user, &fd); 881 goto err; 882 } 883 memcpy(out_blob, info.id, BUILD_HASH_LEN); 884 memcpy(fresh->blob, info.id, BUILD_HASH_LEN); 885 *present = 1; 886 fresh->present = 1; 887 if (c->host.cas_host->file_io->release) 888 c->host.cas_host->file_io->release(c->host.cas_host->file_io->user, &fd); 889 fresh->next = c->sources; 890 c->sources = fresh; 891 return BUILD_OK; 892 893 err: 894 if (fresh) c->ctx->heap->free(c->ctx->heap, fresh, sizeof *fresh); 895 return BUILD_ERR; 896 } 897 898 int build_coord_fetch_blob(KitBuildCoordinator* c, 899 const uint8_t expected_blob[BUILD_HASH_LEN], 900 const KitSlice* urls, size_t nurls, char* path_out, 901 size_t path_cap) { 902 char tmp_parent[BUILD_PATH_MAX]; 903 char tmp_dir[BUILD_PATH_MAX]; 904 char dest[BUILD_PATH_MAX]; 905 size_t i; 906 int installed = 0; 907 if (!c || !expected_blob || !path_out || path_cap == 0u) 908 return BUILD_ERR; 909 path_out[0] = '\0'; 910 if (kit_cas_has_blob(c->cas, expected_blob) == KIT_OK) 911 return kit_cas_blob_path(c->cas, expected_blob, path_out, path_cap) == 912 KIT_OK 913 ? BUILD_OK 914 : BUILD_ERR; 915 if (!urls || nurls == 0u) return BUILD_ERR; 916 if (!c->host.fetch || !c->host.fetch->fetch_url || !c->host.store_io || 917 !c->host.store_io->make_temp_dir || !c->host.store_io->remove) 918 return BUILD_ERR; 919 if (path_join2(tmp_parent, sizeof tmp_parent, c->store.root, "tmp") != 920 BUILD_OK || 921 c->host.store_io->make_temp_dir(c->host.store_io->user, 922 kit_slice_cstr(tmp_parent), tmp_dir, 923 sizeof tmp_dir) != 0 || 924 path_join2(dest, sizeof dest, tmp_dir, "fetch") != BUILD_OK) 925 return BUILD_ERR; 926 927 for (i = 0; i < nurls; ++i) { 928 KitFileData fd; 929 KitBlobInfo info; 930 fd.data = NULL; 931 fd.size = 0; 932 fd.token = NULL; 933 if (c->host.fetch->fetch_url(c->host.fetch->user, urls[i], 934 kit_slice_cstr(dest)) != 0) 935 continue; 936 if (c->host.cas_host->file_io->read_all(c->host.cas_host->file_io->user, 937 dest, &fd) != KIT_OK) 938 continue; 939 kit_blob_info(&info, fd.data, fd.size); 940 if (build_id_eq(info.id, expected_blob) && 941 kit_cas_add_blob(c->cas, fd.data, fd.size, &info) == KIT_OK) 942 installed = 1; 943 if (c->host.cas_host->file_io->release) 944 c->host.cas_host->file_io->release(c->host.cas_host->file_io->user, &fd); 945 if (installed) break; 946 } 947 948 (void)c->host.store_io->remove(c->host.store_io->user, 949 kit_slice_cstr(tmp_dir), 1); 950 if (!installed) return BUILD_ERR; 951 return kit_cas_blob_path(c->cas, expected_blob, path_out, path_cap) == KIT_OK 952 ? BUILD_OK 953 : BUILD_ERR; 954 } 955 956 int build_coord_glob(KitBuildCoordinator* c, KitSlice pattern, 957 uint8_t out_result_hash[BUILD_HASH_LEN], 958 BuildCoordGlobFn cb, void* cb_user) { 959 return build_coord_glob_root(c, c ? c->workspace_root : NULL, pattern, 960 out_result_hash, cb, cb_user); 961 } 962 963 static int build_coord_glob_root(KitBuildCoordinator* c, const char* root, 964 KitSlice pattern, 965 uint8_t out_result_hash[BUILD_HASH_LEN], 966 BuildCoordGlobFn cb, void* cb_user) { 967 BuildGlobExpand g; 968 size_t i, out_n = 0; 969 char pat[BUILD_PATTERN_MAX]; 970 BuildGlobMemo* memo; 971 BuildGlobMemo* fresh = NULL; 972 if (!c || !root || !out_result_hash || !glob_pattern_safe(pattern) || 973 !c->host.cas_host || !c->host.cas_host->walk_regular_files) 974 return BUILD_ERR; 975 if (slice_copy(pat, sizeof pat, pattern) != BUILD_OK) return BUILD_ERR; 976 for (memo = c->globs; memo; memo = memo->next) { 977 if (strcmp(memo->root, root) == 0 && strcmp(memo->pattern, pat) == 0) { 978 memcpy(out_result_hash, memo->result_hash, BUILD_HASH_LEN); 979 if (cb) { 980 for (i = 0; i < memo->n_entries; ++i) { 981 if (cb(cb_user, memo->entries[i].path, memo->entries[i].blob)) break; 982 } 983 } 984 return BUILD_OK; 985 } 986 } 987 memset(&g, 0, sizeof g); 988 g.c = c; 989 g.root = root; 990 g.pattern = pattern; 991 if (c->host.cas_host->walk_regular_files(c->host.cas_host->user, 992 root, glob_walk_cb, 993 &g) != 0 || 994 g.failed) 995 goto err; 996 if (g.n) qsort(g.entries, g.n, sizeof *g.entries, path_blob_cmp_qsort); 997 for (i = 0; i < g.n; ++i) { 998 if (out_n && strcmp(g.entries[out_n - 1u].path, g.entries[i].path) == 0) { 999 if (!build_id_eq(g.entries[out_n - 1u].blob, g.entries[i].blob)) 1000 goto err; 1001 continue; 1002 } 1003 if (out_n != i) g.entries[out_n] = g.entries[i]; 1004 ++out_n; 1005 } 1006 if (build_glob_result_hash(c->ctx->heap, g.entries, out_n, 1007 out_result_hash) != BUILD_OK) 1008 goto err; 1009 fresh = (BuildGlobMemo*)c->ctx->heap->alloc(c->ctx->heap, sizeof *fresh, 1010 _Alignof(BuildGlobMemo)); 1011 if (!fresh) goto err; 1012 memset(fresh, 0, sizeof *fresh); 1013 snprintf(fresh->root, sizeof fresh->root, "%s", root); 1014 snprintf(fresh->pattern, sizeof fresh->pattern, "%s", pat); 1015 memcpy(fresh->result_hash, out_result_hash, BUILD_HASH_LEN); 1016 if (out_n) { 1017 fresh->entries = (BuildPathBlob*)c->ctx->heap->alloc( 1018 c->ctx->heap, out_n * sizeof *fresh->entries, _Alignof(BuildPathBlob)); 1019 if (!fresh->entries) goto err; 1020 memcpy(fresh->entries, g.entries, out_n * sizeof *fresh->entries); 1021 fresh->n_entries = out_n; 1022 } 1023 fresh->next = c->globs; 1024 c->globs = fresh; 1025 fresh = NULL; 1026 if (cb) { 1027 for (i = 0; i < out_n; ++i) { 1028 if (cb(cb_user, g.entries[i].path, g.entries[i].blob)) break; 1029 } 1030 } 1031 if (g.entries) 1032 c->ctx->heap->free(c->ctx->heap, g.entries, g.cap * sizeof *g.entries); 1033 return BUILD_OK; 1034 1035 err: 1036 if (fresh) { 1037 if (fresh->entries) 1038 c->ctx->heap->free(c->ctx->heap, fresh->entries, 1039 fresh->n_entries * sizeof *fresh->entries); 1040 c->ctx->heap->free(c->ctx->heap, fresh, sizeof *fresh); 1041 } 1042 if (g.entries) 1043 c->ctx->heap->free(c->ctx->heap, g.entries, g.cap * sizeof *g.entries); 1044 return BUILD_ERR; 1045 } 1046 1047 int build_coord_glob_target(KitBuildCoordinator* c, KitSlice target, 1048 KitSlice pattern, 1049 uint8_t out_result_hash[BUILD_HASH_LEN], 1050 BuildCoordGlobFn cb, void* cb_user) { 1051 char repo[BUILD_KEY_MAX]; 1052 const char* root; 1053 const char* def_name; 1054 const BuildWorkspace* ws; 1055 (void)def_name; 1056 (void)ws; 1057 if (target_workspace(c, target, repo, &root, &def_name, &ws) != BUILD_OK) 1058 return BUILD_ERR; 1059 return build_coord_glob_root(c, root, pattern, out_result_hash, cb, cb_user); 1060 } 1061 1062 int build_coord_config_by_id(KitBuildCoordinator* c, 1063 const uint8_t config_id[BUILD_HASH_LEN], 1064 BuildConfig* out) { 1065 KitFileData fd; 1066 int r; 1067 if (!c || !config_id || !out) return BUILD_ERR; 1068 fd.data = NULL; 1069 fd.size = 0; 1070 fd.token = NULL; 1071 if (kit_cas_get_blob(c->cas, config_id, &fd) != KIT_OK) return BUILD_ERR; 1072 r = build_config_parse(fd.data, fd.size, out, NULL, 0); 1073 kit_cas_release(c->cas, &fd); 1074 return r; 1075 } 1076 1077 int build_coord_argv_by_id(KitBuildCoordinator* c, 1078 const uint8_t argv_id[BUILD_HASH_LEN], 1079 BuildArgv* out) { 1080 KitFileData fd; 1081 int r; 1082 if (!c || !argv_id || !out) return BUILD_ERR; 1083 fd.data = NULL; 1084 fd.size = 0; 1085 fd.token = NULL; 1086 if (kit_cas_get_blob(c->cas, argv_id, &fd) != KIT_OK) return BUILD_ERR; 1087 r = build_argv_parse(fd.data, fd.size, out, NULL, 0); 1088 kit_cas_release(c->cas, &fd); 1089 return r; 1090 } 1091 1092 int build_coord_top_config(KitBuildCoordinator* c, const KitBuildKV* overrides, 1093 size_t noverrides, BuildConfig* out) { 1094 char err[160]; 1095 if (!c || !out) return BUILD_ERR; 1096 if (build_workspace_config_apply(&c->root_workspace, c->opts.profile, 1097 overrides, noverrides, out, err, 1098 sizeof err) != BUILD_OK) { 1099 build_diagf(c->ctx, "build: %s", err); 1100 return BUILD_ERR; 1101 } 1102 return BUILD_OK; 1103 } 1104 1105 int build_coord_canonical_target(KitBuildCoordinator* c, KitSlice label, 1106 KitSlice current_repo, 1107 KitSlice current_package, 1108 char out[BUILD_TARGET_MAX]) { 1109 char err[128]; 1110 if (!c || !out) return BUILD_ERR; 1111 if (build_target_canonicalize(label, current_repo, current_package, out, err, 1112 sizeof err) != BUILD_OK) { 1113 build_diagf(c->ctx, "build: %s: %.*s", err, KIT_SLICE_ARG(label)); 1114 return BUILD_ERR; 1115 } 1116 return BUILD_OK; 1117 } 1118 1119 static int package_def_path(const char* root, const char* def_name, 1120 const char* package, char out[BUILD_PATH_MAX]) { 1121 char rel[BUILD_PATH_MAX]; 1122 if (!root || !def_name || !package || !out) return BUILD_ERR; 1123 if (package[0]) { 1124 if (path_join2(rel, sizeof rel, package, def_name) != BUILD_OK) 1125 return BUILD_ERR; 1126 } else { 1127 if (path_set(rel, sizeof rel, kit_slice_cstr(def_name)) != 1128 BUILD_OK) 1129 return BUILD_ERR; 1130 } 1131 return path_join2(out, BUILD_PATH_MAX, root, rel); 1132 } 1133 1134 static void release_file_data(KitBuildCoordinator* c, KitFileData* fd) { 1135 if (!c || !fd) return; 1136 if (fd->data && c->host.cas_host && c->host.cas_host->file_io && 1137 c->host.cas_host->file_io->release) 1138 c->host.cas_host->file_io->release(c->host.cas_host->file_io->user, fd); 1139 fd->data = NULL; 1140 fd->size = 0; 1141 fd->token = NULL; 1142 } 1143 1144 static int read_package_defn(KitBuildCoordinator* c, const char* root, 1145 const char* def_name, const char* package, 1146 BuildDefn* defn, KitFileData* fd, char* err, 1147 size_t errcap) { 1148 char path[BUILD_PATH_MAX]; 1149 BuildTargetDefn* targets = NULL; 1150 BuildDefaultDefn* defaults = NULL; 1151 BuildRuleDefn* rules = NULL; 1152 KitHeap* h; 1153 enum { MAX_TARGETS = 256, MAX_DEFAULTS = 64, MAX_RULES = 128 }; 1154 if (!c || !defn || !fd) return BUILD_ERR; 1155 h = c->ctx->heap; 1156 memset(defn, 0, sizeof *defn); 1157 memset(fd, 0, sizeof *fd); 1158 targets = (BuildTargetDefn*)h->alloc(h, MAX_TARGETS * sizeof *targets, 1159 _Alignof(BuildTargetDefn)); 1160 defaults = (BuildDefaultDefn*)h->alloc(h, MAX_DEFAULTS * sizeof *defaults, 1161 _Alignof(BuildDefaultDefn)); 1162 rules = (BuildRuleDefn*)h->alloc(h, MAX_RULES * sizeof *rules, 1163 _Alignof(BuildRuleDefn)); 1164 if (!targets || !defaults || !rules) goto err; 1165 if (package_def_path(root, def_name, package, path) != BUILD_OK) goto err; 1166 if (c->host.cas_host->file_io->read_all(c->host.cas_host->file_io->user, path, 1167 fd) != KIT_OK) { 1168 if (err && errcap) snprintf(err, errcap, "failed to read package %s", path); 1169 goto err; 1170 } 1171 defn->targets = targets; 1172 defn->cap_targets = MAX_TARGETS; 1173 defn->defaults = defaults; 1174 defn->cap_defaults = MAX_DEFAULTS; 1175 defn->rules = rules; 1176 defn->cap_rules = MAX_RULES; 1177 if (build_defn_parse(fd->data, fd->size, defn, err, errcap) != BUILD_OK) 1178 goto err; 1179 return BUILD_OK; 1180 1181 err: 1182 release_file_data(c, fd); 1183 if (rules) h->free(h, rules, MAX_RULES * sizeof *rules); 1184 if (defaults) h->free(h, defaults, MAX_DEFAULTS * sizeof *defaults); 1185 if (targets) h->free(h, targets, MAX_TARGETS * sizeof *targets); 1186 memset(defn, 0, sizeof *defn); 1187 return BUILD_ERR; 1188 } 1189 1190 static void release_package_defn(KitBuildCoordinator* c, BuildDefn* defn, 1191 KitFileData* fd) { 1192 KitHeap* h; 1193 if (!c || !defn) return; 1194 h = c->ctx->heap; 1195 release_file_data(c, fd); 1196 if (defn->rules) 1197 h->free(h, defn->rules, defn->cap_rules * sizeof *defn->rules); 1198 if (defn->defaults) 1199 h->free(h, defn->defaults, 1200 defn->cap_defaults * sizeof *defn->defaults); 1201 if (defn->targets) 1202 h->free(h, defn->targets, defn->cap_targets * sizeof *defn->targets); 1203 memset(defn, 0, sizeof *defn); 1204 } 1205 1206 static int file_present(KitBuildCoordinator* c, const char* root, 1207 const char* rel) { 1208 char full[BUILD_PATH_MAX]; 1209 KitFileData fd; 1210 if (!c || !root || !rel) return 0; 1211 memset(&fd, 0, sizeof fd); 1212 if (path_join2(full, sizeof full, root, rel) != BUILD_OK) 1213 return 0; 1214 if (c->host.cas_host->file_io->read_all(c->host.cas_host->file_io->user, full, 1215 &fd) != KIT_OK) 1216 return 0; 1217 release_file_data(c, &fd); 1218 return 1; 1219 } 1220 1221 static int join_rel2(char* out, size_t cap, const char* a, const char* b) { 1222 if (!a || !a[0]) return path_set(out, cap, kit_slice_cstr(b)); 1223 return path_join2(out, cap, a, b); 1224 } 1225 1226 static int redo_candidate_at(KitBuildCoordinator* c, const char* workspace_root, 1227 const char* root, const char* local, 1228 char out[BUILD_PATH_MAX]) { 1229 char cand[BUILD_PATH_MAX]; 1230 char leaf[BUILD_PATH_MAX]; 1231 size_t i; 1232 if (snprintf(leaf, sizeof leaf, "%s.do", local) >= (int)sizeof leaf) 1233 return BUILD_ERR; 1234 if (join_rel2(cand, sizeof cand, root, leaf) == BUILD_OK && 1235 file_present(c, workspace_root, cand)) { 1236 snprintf(out, BUILD_PATH_MAX, "%s", cand); 1237 return BUILD_OK; 1238 } 1239 for (i = 0; local[i]; ++i) { 1240 if (local[i] != '.') continue; 1241 if (snprintf(leaf, sizeof leaf, "default%s.do", local + i) >= 1242 (int)sizeof leaf) 1243 return BUILD_ERR; 1244 if (join_rel2(cand, sizeof cand, root, leaf) == BUILD_OK && 1245 file_present(c, workspace_root, cand)) { 1246 snprintf(out, BUILD_PATH_MAX, "%s", cand); 1247 return BUILD_OK; 1248 } 1249 } 1250 if (join_rel2(cand, sizeof cand, root, "default.do") == BUILD_OK && 1251 file_present(c, workspace_root, cand)) { 1252 snprintf(out, BUILD_PATH_MAX, "%s", cand); 1253 return BUILD_OK; 1254 } 1255 return BUILD_ERR; 1256 } 1257 1258 static int redo_try_search(KitBuildCoordinator* c, const char* workspace_root, 1259 const char* ancestor, const char* search, 1260 const char* local, char out[BUILD_PATH_MAX]) { 1261 char root[BUILD_PATH_MAX]; 1262 if (!search || strcmp(search, ".") == 0) { 1263 snprintf(root, sizeof root, "%s", ancestor ? ancestor : ""); 1264 } else if (!ancestor || !ancestor[0]) { 1265 if (path_set(root, sizeof root, kit_slice_cstr(search)) != BUILD_OK) 1266 return BUILD_ERR; 1267 } else if (path_join2(root, sizeof root, ancestor, search) != BUILD_OK) { 1268 return BUILD_ERR; 1269 } 1270 return redo_candidate_at(c, workspace_root, root, local, out); 1271 } 1272 1273 static int parent_package(char pkg[BUILD_PATH_MAX]) { 1274 size_t n; 1275 if (!pkg || !pkg[0]) return 0; 1276 n = strlen(pkg); 1277 while (n > 0u && pkg[n - 1u] != '/') --n; 1278 if (n == 0u) { 1279 pkg[0] = '\0'; 1280 } else { 1281 pkg[n - 1u] = '\0'; 1282 } 1283 return 1; 1284 } 1285 1286 static int resolve_redo(KitBuildCoordinator* c, const char* workspace_root, 1287 const BuildDefn* defn, const char* package, 1288 const char* local, char out[BUILD_PATH_MAX]) { 1289 char ancestor[BUILD_PATH_MAX]; 1290 size_t i; 1291 if (!defn || !defn->redo.enabled) return BUILD_ERR; 1292 snprintf(ancestor, sizeof ancestor, "%s", package ? package : ""); 1293 for (;;) { 1294 for (i = 0; i < defn->redo.n_search; ++i) { 1295 if (redo_try_search(c, workspace_root, ancestor, defn->redo.search[i], 1296 local, out) == BUILD_OK) 1297 return BUILD_OK; 1298 } 1299 if (!defn->redo.walk_parents || !parent_package(ancestor)) break; 1300 } 1301 return BUILD_ERR; 1302 } 1303 1304 int build_coord_resolve_recipe(KitBuildCoordinator* c, KitSlice canonical_target, 1305 BuildRecipeResolution* out) { 1306 BuildDefn defn; 1307 KitFileData fd; 1308 char repo[BUILD_KEY_MAX]; 1309 char package[BUILD_PATH_MAX]; 1310 char local[BUILD_TARGET_MAX]; 1311 char err[160]; 1312 const char* workspace_root; 1313 const char* def_name; 1314 const BuildWorkspace* ws; 1315 const BuildTargetDefn* target; 1316 const BuildRuleDefn* rule = NULL; 1317 const BuildDefaultDefn* def = NULL; 1318 const char* type = NULL; 1319 const char* recipe = NULL; 1320 char redo_recipe[BUILD_PATH_MAX]; 1321 int ambiguous = 0; 1322 int recipe_workspace_rel = 0; 1323 int ok = BUILD_ERR; 1324 1325 if (!c || !out || 1326 build_target_split_repo(canonical_target, repo, package, local) != 1327 BUILD_OK) 1328 return BUILD_ERR; 1329 if (target_workspace(c, canonical_target, repo, &workspace_root, &def_name, 1330 &ws) != BUILD_OK) 1331 return BUILD_ERR; 1332 (void)ws; 1333 memset(out, 0, sizeof *out); 1334 if (read_package_defn(c, workspace_root, def_name, package, &defn, &fd, err, 1335 sizeof err) != BUILD_OK) { 1336 build_diagf(c->ctx, "build: %s", err); 1337 return BUILD_ERR; 1338 } 1339 1340 target = build_defn_find(&defn, kit_slice_cstr(local)); 1341 if (target) { 1342 if (target->has_recipe) recipe = target->recipe_path; 1343 if (target->has_type) type = target->type; 1344 } else { 1345 rule = build_defn_rule_match(&defn, kit_slice_cstr(local), &ambiguous); 1346 if (ambiguous) { 1347 build_diagf(c->ctx, "build: ambiguous rules for %.*s", 1348 KIT_SLICE_ARG(canonical_target)); 1349 goto out_release; 1350 } 1351 if (rule) { 1352 if (rule->has_recipe) recipe = rule->recipe_path; 1353 if (rule->has_type) type = rule->type; 1354 } 1355 } 1356 if (!recipe && 1357 resolve_redo(c, workspace_root, &defn, package, local, redo_recipe) == 1358 BUILD_OK) { 1359 recipe = redo_recipe; 1360 recipe_workspace_rel = 1; 1361 } 1362 if (!recipe && type) { 1363 def = build_defn_default_find(&defn, kit_slice_cstr(type)); 1364 if (def) recipe = def->recipe_path; 1365 } 1366 if (!recipe) { 1367 build_diagf(c->ctx, "build: unknown target %.*s", 1368 KIT_SLICE_ARG(canonical_target)); 1369 goto out_release; 1370 } 1371 if ((recipe_workspace_rel 1372 ? path_set(out->recipe_relpath, sizeof out->recipe_relpath, 1373 kit_slice_cstr(recipe)) 1374 : build_recipe_path_resolve(kit_slice_cstr(package), 1375 kit_slice_cstr(recipe), 1376 out->recipe_relpath)) != BUILD_OK || 1377 path_join2(out->recipe_abspath, sizeof out->recipe_abspath, 1378 workspace_root, out->recipe_relpath) != BUILD_OK) 1379 goto out_release; 1380 snprintf(out->canonical_target, sizeof out->canonical_target, "%.*s", 1381 KIT_SLICE_ARG(canonical_target)); 1382 snprintf(out->repo, sizeof out->repo, "%s", repo); 1383 snprintf(out->package, sizeof out->package, "%s", package); 1384 snprintf(out->local_name, sizeof out->local_name, "%s", local); 1385 snprintf(out->workspace_root, sizeof out->workspace_root, "%s", 1386 workspace_root); 1387 ok = BUILD_OK; 1388 1389 out_release: 1390 release_package_defn(c, &defn, &fd); 1391 return ok; 1392 } 1393 1394 int build_coord_recipe_id(KitBuildCoordinator* c, KitSlice target, 1395 uint8_t out[BUILD_HASH_LEN]) { 1396 BuildRecipeResolution r; 1397 KitFileData fd; 1398 KitBlobInfo info; 1399 if (!c || !out) return BUILD_ERR; 1400 if (build_coord_resolve_recipe(c, target, &r) != BUILD_OK) 1401 return BUILD_ERR; 1402 fd.data = NULL; 1403 fd.size = 0; 1404 fd.token = NULL; 1405 if (c->host.cas_host->file_io->read_all(c->host.cas_host->file_io->user, 1406 r.recipe_abspath, &fd) != KIT_OK) 1407 return BUILD_ERR; 1408 if (kit_cas_add_blob(c->cas, fd.data, fd.size, &info) != KIT_OK) { 1409 if (c->host.cas_host->file_io->release) 1410 c->host.cas_host->file_io->release(c->host.cas_host->file_io->user, &fd); 1411 return BUILD_ERR; 1412 } 1413 memcpy(out, info.id, BUILD_HASH_LEN); 1414 if (c->host.cas_host->file_io->release) 1415 c->host.cas_host->file_io->release(c->host.cas_host->file_io->user, &fd); 1416 return BUILD_OK; 1417 } 1418 1419 int build_coord_leafset_intern(KitBuildCoordinator* c, const BuildLeafSet* in, 1420 const BuildLeafSet** out) { 1421 BuildDeepSetMemo* n; 1422 if (!c || !in || !out) return BUILD_ERR; 1423 for (n = c->deepsets; n; n = n->next) { 1424 if (build_id_eq(n->leaf.id, in->id)) { 1425 *out = &n->leaf; 1426 return BUILD_OK; 1427 } 1428 } 1429 n = (BuildDeepSetMemo*)c->ctx->heap->alloc(c->ctx->heap, sizeof *n, 1430 _Alignof(BuildDeepSetMemo)); 1431 if (!n) return BUILD_ERR; 1432 memset(n, 0, sizeof *n); 1433 n->leaf = *in; 1434 if (in->n_configs) { 1435 n->configs = (BuildConfigLeaf*)c->ctx->heap->alloc( 1436 c->ctx->heap, in->n_configs * sizeof *n->configs, 1437 _Alignof(BuildConfigLeaf)); 1438 if (!n->configs) goto oom; 1439 memcpy(n->configs, in->configs, in->n_configs * sizeof *n->configs); 1440 } 1441 if (in->n_sources) { 1442 n->sources = (BuildSourceLeaf*)c->ctx->heap->alloc( 1443 c->ctx->heap, in->n_sources * sizeof *n->sources, 1444 _Alignof(BuildSourceLeaf)); 1445 if (!n->sources) goto oom; 1446 memcpy(n->sources, in->sources, in->n_sources * sizeof *n->sources); 1447 } 1448 if (in->n_globs) { 1449 n->globs = (BuildGlobLeaf*)c->ctx->heap->alloc( 1450 c->ctx->heap, in->n_globs * sizeof *n->globs, _Alignof(BuildGlobLeaf)); 1451 if (!n->globs) goto oom; 1452 memcpy(n->globs, in->globs, in->n_globs * sizeof *n->globs); 1453 } 1454 if (in->n_blobs) { 1455 n->blobs = (BuildBlobLeaf*)c->ctx->heap->alloc( 1456 c->ctx->heap, in->n_blobs * sizeof *n->blobs, 1457 _Alignof(BuildBlobLeaf)); 1458 if (!n->blobs) goto oom; 1459 memcpy(n->blobs, in->blobs, in->n_blobs * sizeof *n->blobs); 1460 } 1461 if (in->n_children) { 1462 n->children = (const BuildLeafSet**)c->ctx->heap->alloc( 1463 c->ctx->heap, in->n_children * sizeof *n->children, 1464 _Alignof(const BuildLeafSet*)); 1465 if (!n->children) goto oom; 1466 memcpy(n->children, in->children, in->n_children * sizeof *n->children); 1467 } 1468 n->leaf.configs = n->configs; 1469 n->leaf.sources = n->sources; 1470 n->leaf.globs = n->globs; 1471 n->leaf.blobs = n->blobs; 1472 n->leaf.children = n->children; 1473 n->next = c->deepsets; 1474 c->deepsets = n; 1475 *out = &n->leaf; 1476 return BUILD_OK; 1477 1478 oom: 1479 if (n->configs) 1480 c->ctx->heap->free(c->ctx->heap, n->configs, 1481 in->n_configs * sizeof *n->configs); 1482 if (n->sources) 1483 c->ctx->heap->free(c->ctx->heap, n->sources, 1484 in->n_sources * sizeof *n->sources); 1485 if (n->globs) 1486 c->ctx->heap->free(c->ctx->heap, n->globs, 1487 in->n_globs * sizeof *n->globs); 1488 if (n->blobs) 1489 c->ctx->heap->free(c->ctx->heap, n->blobs, 1490 in->n_blobs * sizeof *n->blobs); 1491 if (n->children) 1492 c->ctx->heap->free(c->ctx->heap, n->children, 1493 in->n_children * sizeof *n->children); 1494 c->ctx->heap->free(c->ctx->heap, n, sizeof *n); 1495 return BUILD_ERR; 1496 } 1497 1498 static void count_deepset_rows(const uint8_t* data, size_t len, size_t* ncfg, 1499 size_t* ns, size_t* ng, size_t* nf, 1500 size_t* nc) { 1501 size_t pos = 0; 1502 int sec = 0; 1503 *ncfg = 0; 1504 *ns = 0; 1505 *ng = 0; 1506 *nf = 0; 1507 *nc = 0; 1508 while (pos < len) { 1509 size_t start = pos; 1510 while (pos < len && data[pos] != '\n') ++pos; 1511 if (pos == len) break; 1512 if (pos > start) { 1513 size_t n = pos - start; 1514 if (n == sizeof("[config]") - 1u && 1515 memcmp(data + start, "[config]", n) == 0) { 1516 sec = 1; 1517 } else if (n == sizeof("[source]") - 1u && 1518 memcmp(data + start, "[source]", n) == 0) { 1519 sec = 2; 1520 } else if (n == sizeof("[glob]") - 1u && 1521 memcmp(data + start, "[glob]", n) == 0) { 1522 sec = 3; 1523 } else if (n == sizeof("[child]") - 1u && 1524 memcmp(data + start, "[child]", n) == 0) { 1525 sec = 4; 1526 } else if (n == sizeof("[blob]") - 1u && 1527 memcmp(data + start, "[blob]", n) == 0) { 1528 sec = 5; 1529 } else if (data[start] != '[' && data[start] != 'k') { 1530 if (sec == 1) ++*ncfg; 1531 if (sec == 2) ++*ns; 1532 if (sec == 3) ++*ng; 1533 if (sec == 4) ++*nc; 1534 if (sec == 5) ++*nf; 1535 } 1536 } 1537 ++pos; 1538 } 1539 } 1540 1541 int build_coord_deepset_load(KitBuildCoordinator* c, 1542 const uint8_t deepset_id[BUILD_HASH_LEN], 1543 const BuildLeafSet** out) { 1544 BuildDeepSetMemo* memo; 1545 KitFileData fd; 1546 BuildDeepSet ds; 1547 BuildLeafSet leaf; 1548 BuildConfigLeaf* configs = NULL; 1549 BuildSourceLeaf* sources = NULL; 1550 BuildGlobLeaf* globs = NULL; 1551 BuildBlobLeaf* blobs = NULL; 1552 uint8_t(*child_ids)[BUILD_HASH_LEN] = NULL; 1553 const BuildLeafSet** children = NULL; 1554 size_t ncfg, ns, ng, nf, nc, i; 1555 int ok = BUILD_ERR; 1556 if (!c || !deepset_id || !out) return BUILD_ERR; 1557 for (memo = c->deepsets; memo; memo = memo->next) { 1558 if (build_id_eq(memo->leaf.id, deepset_id)) { 1559 *out = &memo->leaf; 1560 return BUILD_OK; 1561 } 1562 } 1563 fd.data = NULL; 1564 fd.size = 0; 1565 fd.token = NULL; 1566 if (kit_cas_get_blob(c->cas, deepset_id, &fd) != KIT_OK) return BUILD_ERR; 1567 count_deepset_rows(fd.data, fd.size, &ncfg, &ns, &ng, &nf, &nc); 1568 if (ncfg) { 1569 configs = (BuildConfigLeaf*)c->ctx->heap->alloc( 1570 c->ctx->heap, ncfg * sizeof *configs, _Alignof(BuildConfigLeaf)); 1571 if (!configs) goto out_release; 1572 } 1573 if (ns) { 1574 sources = (BuildSourceLeaf*)c->ctx->heap->alloc( 1575 c->ctx->heap, ns * sizeof *sources, _Alignof(BuildSourceLeaf)); 1576 if (!sources) goto out_release; 1577 } 1578 if (ng) { 1579 globs = (BuildGlobLeaf*)c->ctx->heap->alloc( 1580 c->ctx->heap, ng * sizeof *globs, _Alignof(BuildGlobLeaf)); 1581 if (!globs) goto out_release; 1582 } 1583 if (nf) { 1584 blobs = (BuildBlobLeaf*)c->ctx->heap->alloc( 1585 c->ctx->heap, nf * sizeof *blobs, _Alignof(BuildBlobLeaf)); 1586 if (!blobs) goto out_release; 1587 } 1588 if (nc) { 1589 child_ids = (uint8_t(*)[BUILD_HASH_LEN])c->ctx->heap->alloc( 1590 c->ctx->heap, nc * sizeof *child_ids, _Alignof(uint8_t)); 1591 children = (const BuildLeafSet**)c->ctx->heap->alloc( 1592 c->ctx->heap, nc * sizeof *children, _Alignof(const BuildLeafSet*)); 1593 if (!child_ids || !children) goto out_release; 1594 } 1595 memset(&ds, 0, sizeof ds); 1596 ds.configs = configs; 1597 ds.cap_configs = ncfg; 1598 ds.sources = sources; 1599 ds.cap_sources = ns; 1600 ds.globs = globs; 1601 ds.cap_globs = ng; 1602 ds.blobs = blobs; 1603 ds.cap_blobs = nf; 1604 ds.children = child_ids; 1605 ds.cap_children = nc; 1606 if (build_deepset_parse(fd.data, fd.size, &ds, NULL, 0) != BUILD_OK) 1607 goto out_release; 1608 for (i = 0; i < ds.n_children; ++i) { 1609 if (build_coord_deepset_load(c, ds.children[i], &children[i]) != BUILD_OK) 1610 goto out_release; 1611 } 1612 memset(&leaf, 0, sizeof leaf); 1613 memcpy(leaf.id, deepset_id, BUILD_HASH_LEN); 1614 memcpy(leaf.recipe, ds.recipe, BUILD_HASH_LEN); 1615 snprintf(leaf.target, sizeof leaf.target, "%s", ds.target); 1616 leaf.configs = configs; 1617 leaf.n_configs = ds.n_configs; 1618 leaf.sources = sources; 1619 leaf.n_sources = ds.n_sources; 1620 leaf.globs = globs; 1621 leaf.n_globs = ds.n_globs; 1622 leaf.blobs = blobs; 1623 leaf.n_blobs = ds.n_blobs; 1624 leaf.children = children; 1625 leaf.n_children = ds.n_children; 1626 if (build_coord_leafset_intern(c, &leaf, out) != BUILD_OK) goto out_release; 1627 ok = BUILD_OK; 1628 1629 out_release: 1630 kit_cas_release(c->cas, &fd); 1631 if (configs) 1632 c->ctx->heap->free(c->ctx->heap, configs, ncfg * sizeof *configs); 1633 if (sources) 1634 c->ctx->heap->free(c->ctx->heap, sources, ns * sizeof *sources); 1635 if (globs) c->ctx->heap->free(c->ctx->heap, globs, ng * sizeof *globs); 1636 if (blobs) 1637 c->ctx->heap->free(c->ctx->heap, blobs, nf * sizeof *blobs); 1638 if (child_ids) 1639 c->ctx->heap->free(c->ctx->heap, child_ids, nc * sizeof *child_ids); 1640 if (children) 1641 c->ctx->heap->free(c->ctx->heap, children, nc * sizeof *children); 1642 return ok; 1643 } 1644 1645 int build_coord_deepset_valid_get(KitBuildCoordinator* c, 1646 const uint8_t deepset_id[BUILD_HASH_LEN], 1647 int* known, int* valid) { 1648 BuildDeepSetMemo* n; 1649 if (known) *known = 0; 1650 if (valid) *valid = 0; 1651 if (!c || !deepset_id) return BUILD_ERR; 1652 for (n = c->deepsets; n; n = n->next) { 1653 if (build_id_eq(n->leaf.id, deepset_id)) { 1654 if (known) *known = n->valid_known; 1655 if (valid) *valid = n->valid; 1656 return BUILD_OK; 1657 } 1658 } 1659 return BUILD_OK; 1660 } 1661 1662 void build_coord_deepset_valid_set(KitBuildCoordinator* c, 1663 const uint8_t deepset_id[BUILD_HASH_LEN], 1664 int valid) { 1665 BuildDeepSetMemo* n; 1666 if (!c || !deepset_id) return; 1667 for (n = c->deepsets; n; n = n->next) { 1668 if (build_id_eq(n->leaf.id, deepset_id)) { 1669 n->valid_known = 1; 1670 n->valid = valid ? 1 : 0; 1671 return; 1672 } 1673 } 1674 } 1675 1676 int build_coord_trace_remote_pull_once(KitBuildCoordinator* c, KitSlice target, 1677 int* pulled_now) { 1678 BuildPulledSet* n; 1679 if (!c || !pulled_now) return BUILD_ERR; 1680 *pulled_now = 0; 1681 if (!c->opts.n_trace_remotes) return BUILD_OK; 1682 if (!target.s || target.len == 0u || target.len >= BUILD_TARGET_MAX) 1683 return BUILD_ERR; 1684 for (n = c->pulled; n; n = n->next) { 1685 if (strlen(n->target) == target.len && 1686 memcmp(n->target, target.s, target.len) == 0) 1687 return BUILD_OK; 1688 } 1689 n = (BuildPulledSet*)c->ctx->heap->alloc(c->ctx->heap, sizeof *n, 1690 _Alignof(BuildPulledSet)); 1691 if (!n) return BUILD_ERR; 1692 memset(n, 0, sizeof *n); 1693 memcpy(n->target, target.s, target.len); 1694 n->target[target.len] = '\0'; 1695 n->next = c->pulled; 1696 c->pulled = n; 1697 if (build_trace_remote_pull(c, target) == BUILD_OK) { 1698 *pulled_now = 1; 1699 build_coord_stat_bump(c, BUILD_STAT_TRACE_PULL); 1700 return BUILD_OK; 1701 } 1702 return BUILD_OK; 1703 } 1704 1705 void build_coord_jobs_acquire(KitBuildCoordinator* c) { (void)c; } 1706 void build_coord_jobs_release(KitBuildCoordinator* c) { (void)c; } 1707 1708 int build_coord_spawn(KitBuildCoordinator* c, void (*fn)(void*), void* arg) { 1709 if (!c || !c->host.sched || !c->host.sched->thread_spawn || !fn) 1710 return BUILD_ERR; 1711 return c->host.sched->thread_spawn(c->host.sched->user, fn, arg, NULL) == 0 1712 ? BUILD_OK 1713 : BUILD_ERR; 1714 } 1715 1716 int build_coord_target_intern(KitBuildCoordinator* c, KitSlice target, 1717 const uint8_t config_id[BUILD_HASH_LEN], 1718 const uint8_t argv_id[BUILD_HASH_LEN], 1719 BuildTargetFuture** out, int* is_fresh) { 1720 BuildTargetTable* t; 1721 BuildTargetFuture* f; 1722 if (!c || !out || !is_fresh || !target.s || target.len == 0u || 1723 target.len >= BUILD_TARGET_MAX || !config_id || !argv_id) 1724 return BUILD_ERR; 1725 if (!c->targets) { 1726 c->targets = (BuildTargetTable*)c->ctx->heap->alloc( 1727 c->ctx->heap, sizeof *c->targets, _Alignof(BuildTargetTable)); 1728 if (!c->targets) return BUILD_ERR; 1729 memset(c->targets, 0, sizeof *c->targets); 1730 } 1731 t = c->targets; 1732 for (f = t->futures; f; f = f->next) { 1733 if (strlen(f->target) == target.len && 1734 memcmp(f->target, target.s, target.len) == 0 && 1735 build_id_eq(f->config_id, config_id) && 1736 build_id_eq(f->argv_id, argv_id)) { 1737 *out = f; 1738 *is_fresh = 0; 1739 return BUILD_OK; 1740 } 1741 } 1742 f = (BuildTargetFuture*)c->ctx->heap->alloc(c->ctx->heap, sizeof *f, 1743 _Alignof(BuildTargetFuture)); 1744 if (!f) return BUILD_ERR; 1745 memset(f, 0, sizeof *f); 1746 memcpy(f->target, target.s, target.len); 1747 f->target[target.len] = '\0'; 1748 memcpy(f->config_id, config_id, BUILD_HASH_LEN); 1749 memcpy(f->argv_id, argv_id, BUILD_HASH_LEN); 1750 f->next = t->futures; 1751 t->futures = f; 1752 *out = f; 1753 *is_fresh = 1; 1754 return BUILD_OK; 1755 } 1756 1757 int build_coord_target_await(KitBuildCoordinator* c, BuildTargetFuture* f, 1758 BuildResolved* out) { 1759 (void)c; 1760 if (!f || !out || !f->done || f->failed) return BUILD_ERR; 1761 *out = f->result; 1762 return BUILD_OK; 1763 } 1764 1765 void build_coord_target_complete(KitBuildCoordinator* c, BuildTargetFuture* f, 1766 const BuildResolved* r) { 1767 (void)c; 1768 if (!f || !r) return; 1769 f->result = *r; 1770 f->done = 1; 1771 f->failed = 0; 1772 } 1773 1774 void build_coord_target_fail(KitBuildCoordinator* c, BuildTargetFuture* f) { 1775 (void)c; 1776 if (!f) return; 1777 f->done = 1; 1778 f->failed = 1; 1779 }