build_coord.c (18108B)
1 #include <kit/build_coord.h> 2 3 #include "build/bundle.h" 4 #include "build/cfg.h" 5 #include "build/coord.h" 6 #include "build/protocol.h" 7 #include "build/resolve.h" 8 #include "build/workspace.h" 9 10 #include <string.h> 11 12 KitStatus kit_build_coordinator_open(const KitContext* ctx, 13 const KitBuildHost* host, 14 KitSlice store_root, 15 const KitBuildOptions* opts, 16 KitBuildCoordinator** out) { 17 return build_coord_open(ctx, host, store_root, opts, out); 18 } 19 20 void kit_build_coordinator_close(KitBuildCoordinator* c) { 21 build_coord_close(c); 22 } 23 24 KitStatus kit_build(KitBuildCoordinator* c, const KitBuildRequest* req, 25 KitBuildResult* out) { 26 BuildConfigEntry cfg_entries[128]; 27 char argv_entries[128][BUILD_VAL_MAX]; 28 BuildConfig cfg; 29 BuildArgv argv; 30 BuildResolved r; 31 char target[BUILD_TARGET_MAX]; 32 if (!c || !req || !out) return KIT_INVALID; 33 if (build_coord_canonical_target(c, req->target, KIT_SLICE_NULL, 34 KIT_SLICE_NULL, target) != BUILD_OK) 35 return KIT_INVALID; 36 build_config_init(&cfg, cfg_entries, sizeof cfg_entries / sizeof cfg_entries[0]); 37 if (build_coord_top_config(c, req->config, req->nconfig, &cfg) != BUILD_OK) 38 return KIT_INVALID; 39 build_argv_init(&argv, argv_entries, 40 sizeof argv_entries / sizeof argv_entries[0]); 41 if (build_argv_set(&argv, req->argv, req->argc) != BUILD_OK) 42 return KIT_INVALID; 43 memset(&r, 0, sizeof r); 44 if (build_resolve(c, kit_slice_cstr(target), &cfg, &argv, NULL, &r) != 45 BUILD_OK) 46 return KIT_ERR; 47 memcpy(out->output_tree, r.output_tree, KIT_BUILD_HASH_LEN); 48 memcpy(out->path, r.path, sizeof out->path); 49 return KIT_OK; 50 } 51 52 KitStatus kit_build_test(KitBuildCoordinator* c, const KitBuildRequest* req, 53 KitBuildTestResult* out) { 54 BuildConfigEntry cfg_entries[128]; 55 char argv_entries[128][BUILD_VAL_MAX]; 56 BuildConfig cfg; 57 BuildArgv argv; 58 BuildTestResolved r; 59 char target[BUILD_TARGET_MAX]; 60 if (!c || !req || !out) return KIT_INVALID; 61 if (!c->host.exec || !c->host.exec->spawn) return KIT_UNSUPPORTED; 62 if (build_coord_canonical_target(c, req->target, KIT_SLICE_NULL, 63 KIT_SLICE_NULL, target) != BUILD_OK) 64 return KIT_INVALID; 65 build_config_init(&cfg, cfg_entries, 66 sizeof cfg_entries / sizeof cfg_entries[0]); 67 if (build_coord_top_config(c, req->config, req->nconfig, &cfg) != BUILD_OK) 68 return KIT_INVALID; 69 build_argv_init(&argv, argv_entries, 70 sizeof argv_entries / sizeof argv_entries[0]); 71 if (build_argv_set(&argv, req->argv, req->argc) != BUILD_OK) 72 return KIT_INVALID; 73 memset(&r, 0, sizeof r); 74 if (build_test_resolve(c, kit_slice_cstr(target), &cfg, &argv, NULL, &r) != 75 BUILD_OK) 76 return KIT_ERR; 77 out->status = r.status; 78 out->exit_code = r.exit_code; 79 memcpy(out->result_tree, r.result_tree, KIT_BUILD_HASH_LEN); 80 memcpy(out->path, r.path, sizeof out->path); 81 memcpy(out->stdout_blob, r.stdout_blob, KIT_BUILD_HASH_LEN); 82 memcpy(out->stderr_blob, r.stderr_blob, KIT_BUILD_HASH_LEN); 83 return KIT_OK; 84 } 85 86 void kit_build_stats(const KitBuildCoordinator* c, KitBuildStats* out) { 87 if (!out) return; 88 memset(out, 0, sizeof *out); 89 if (c) *out = c->stats; 90 } 91 92 KitStatus kit_build_traces_export(KitBuildCoordinator* c, 93 const KitBuildExportOptions* opts) { 94 return build_bundle_export(c, opts) == BUILD_OK ? KIT_OK : KIT_ERR; 95 } 96 97 KitStatus kit_build_traces_import(KitBuildCoordinator* c, 98 const KitBuildImportOptions* opts, 99 KitBuildImportResult* result) { 100 return build_bundle_import(c, opts, result) == BUILD_OK ? KIT_OK : KIT_ERR; 101 } 102 103 struct KitBuildClient { 104 const KitContext* ctx; 105 const KitBuildTransport* transport; 106 KitBuildConn* conn; 107 uint8_t frame[BUILD_FRAME_MAX]; 108 }; 109 110 static KitStatus build_client_error_status(const BuildResp* resp) { 111 if (!resp || resp->status != BUILD_RESP_ERROR) return KIT_MALFORMED; 112 switch ((KitStatus)resp->error_status) { 113 case KIT_OK: 114 case KIT_ERR: 115 case KIT_NOMEM: 116 case KIT_INVALID: 117 case KIT_UNSUPPORTED: 118 case KIT_MALFORMED: 119 case KIT_IO: 120 case KIT_NOT_FOUND: 121 case KIT_AMBIGUOUS: 122 return (KitStatus)resp->error_status; 123 default: 124 return KIT_ERR; 125 } 126 } 127 128 static KitStatus build_client_rpc(KitBuildClient* c, const BuildReq* req, 129 BuildResp* resp, 130 BuildProtoGlobFn glob_cb, 131 void* glob_user) { 132 size_t n = 0; 133 if (!c || !c->transport || !c->conn || !req || !resp) return KIT_INVALID; 134 if (build_proto_encode_req(req, c->frame, sizeof c->frame, &n) != BUILD_OK) 135 return KIT_INVALID; 136 if (c->transport->write_frame(c->transport->user, c->conn, c->frame, n) != 0) 137 return KIT_IO; 138 if (c->transport->read_frame(c->transport->user, c->conn, c->frame, 139 sizeof c->frame, &n) != 0) 140 return KIT_IO; 141 if (build_proto_decode_resp(c->frame, n, req->cmd, resp, glob_cb, 142 glob_user) != BUILD_OK) 143 return KIT_MALFORMED; 144 if (resp->status == BUILD_RESP_ERROR) return build_client_error_status(resp); 145 return KIT_OK; 146 } 147 148 static KitStatus build_client_need_result(KitBuildClient* c, const BuildReq* req, 149 KitBuildResult* out) { 150 BuildResp resp; 151 KitStatus st; 152 if (!out) return KIT_INVALID; 153 memset(out, 0, sizeof *out); 154 st = build_client_rpc(c, req, &resp, NULL, NULL); 155 if (st != KIT_OK) return st; 156 if (resp.status != BUILD_RESP_OK || resp.text.len >= sizeof out->path) 157 return KIT_MALFORMED; 158 memcpy(out->output_tree, resp.id, KIT_BUILD_HASH_LEN); 159 if (resp.text.len) memcpy(out->path, resp.text.s, resp.text.len); 160 out->path[resp.text.len] = '\0'; 161 return KIT_OK; 162 } 163 164 static void build_client_req_from_build_req(BuildReq* req, uint8_t cmd, 165 const KitBuildRequest* in) { 166 memset(req, 0, sizeof *req); 167 req->cmd = cmd; 168 req->arg = in->target; 169 req->overrides = in->config; 170 req->noverrides = in->nconfig; 171 req->argv = in->argv; 172 req->argc = in->argc; 173 } 174 175 KitStatus kit_build_client_open(const KitContext* ctx, 176 const KitBuildTransport* transport, 177 KitBuildClient** out) { 178 const char* endpoint; 179 KitBuildClient* c; 180 KitBuildConn* conn = NULL; 181 if (!out) return KIT_INVALID; 182 *out = NULL; 183 if (!ctx || !ctx->heap || !transport || !transport->dial || 184 !transport->read_frame || !transport->write_frame || !transport->close) 185 return KIT_INVALID; 186 endpoint = kit_debug_getenv(KIT_BUILD_ENV_SOCK); 187 if (!endpoint || !endpoint[0]) return KIT_NOT_FOUND; 188 if (transport->dial(transport->user, kit_slice_cstr(endpoint), &conn) != 0 || 189 !conn) 190 return KIT_IO; 191 c = (KitBuildClient*)ctx->heap->alloc(ctx->heap, sizeof *c, 192 _Alignof(KitBuildClient)); 193 if (!c) { 194 transport->close(transport->user, conn); 195 return KIT_NOMEM; 196 } 197 memset(c, 0, sizeof *c); 198 c->ctx = ctx; 199 c->transport = transport; 200 c->conn = conn; 201 *out = c; 202 return KIT_OK; 203 } 204 205 void kit_build_client_close(KitBuildClient* c) { 206 KitHeap* heap; 207 if (!c) return; 208 if (c->transport && c->transport->close && c->conn) 209 c->transport->close(c->transport->user, c->conn); 210 heap = c->ctx ? c->ctx->heap : NULL; 211 if (heap) heap->free(heap, c, sizeof *c); 212 } 213 214 KitStatus kit_build_client_config_get(KitBuildClient* c, KitSlice key, 215 KitSlice default_value, int has_default, 216 KitSlice* value, int* present) { 217 BuildReq req; 218 BuildResp resp; 219 KitStatus st; 220 KitSlice argv[1]; 221 if (value) *value = KIT_SLICE_NULL; 222 if (present) *present = 0; 223 if (!c || !value || !present) return KIT_INVALID; 224 memset(&req, 0, sizeof req); 225 req.cmd = BUILD_CMD_CONFIG_GET; 226 req.arg = key; 227 if (has_default) { 228 argv[0] = default_value; 229 req.argv = argv; 230 req.argc = 1; 231 } 232 st = build_client_rpc(c, &req, &resp, NULL, NULL); 233 if (st != KIT_OK) return st; 234 if (resp.status == BUILD_RESP_UNSET) return KIT_OK; 235 if (resp.status == BUILD_RESP_DEFAULT) { 236 *value = resp.text; 237 return KIT_OK; 238 } 239 if (resp.status != BUILD_RESP_OK) return KIT_MALFORMED; 240 *value = resp.text; 241 *present = 1; 242 return KIT_OK; 243 } 244 245 KitStatus kit_build_client_source(KitBuildClient* c, KitSlice path, 246 uint8_t blob[KIT_BUILD_HASH_LEN], 247 KitSlice* realpath) { 248 BuildReq req; 249 BuildResp resp; 250 KitStatus st; 251 if (blob) memset(blob, 0, KIT_BUILD_HASH_LEN); 252 if (realpath) *realpath = KIT_SLICE_NULL; 253 if (!c || !blob || !realpath) return KIT_INVALID; 254 memset(&req, 0, sizeof req); 255 req.cmd = BUILD_CMD_SOURCE; 256 req.arg = path; 257 st = build_client_rpc(c, &req, &resp, NULL, NULL); 258 if (st != KIT_OK) return st; 259 if (resp.status == BUILD_RESP_ABSENT) return KIT_NOT_FOUND; 260 if (resp.status != BUILD_RESP_OK) return KIT_MALFORMED; 261 memcpy(blob, resp.id, KIT_BUILD_HASH_LEN); 262 *realpath = resp.text; 263 return KIT_OK; 264 } 265 266 KitStatus kit_build_client_fetch(KitBuildClient* c, 267 const uint8_t blob[KIT_BUILD_HASH_LEN], 268 const KitSlice* urls, size_t nurls, 269 KitSlice* path) { 270 char hex[2u * KIT_BUILD_HASH_LEN + 1u]; 271 BuildReq req; 272 BuildResp resp; 273 KitStatus st; 274 if (path) *path = KIT_SLICE_NULL; 275 if (!c || !blob || !urls || nurls == 0u || !path) return KIT_INVALID; 276 kit_hex_encode(hex, blob, KIT_BUILD_HASH_LEN); 277 memset(&req, 0, sizeof req); 278 req.cmd = BUILD_CMD_FETCH; 279 req.arg = kit_slice_cstr(hex); 280 req.argv = urls; 281 req.argc = nurls; 282 st = build_client_rpc(c, &req, &resp, NULL, NULL); 283 if (st != KIT_OK) return st; 284 if (resp.status != BUILD_RESP_OK || !build_id_eq(resp.id, blob)) 285 return KIT_MALFORMED; 286 *path = resp.text; 287 return KIT_OK; 288 } 289 290 static KitStatus build_client_depfile_submit(KitBuildClient* c, 291 const char* path, size_t len) { 292 uint8_t blob[KIT_BUILD_HASH_LEN]; 293 KitSlice realpath; 294 KitSlice dep; 295 if (!c || !path || len == 0u || len >= BUILD_PATH_MAX) return KIT_INVALID; 296 dep.s = path; 297 dep.len = len; 298 return kit_build_client_source(c, dep, blob, &realpath); 299 } 300 301 static int build_depfile_is_space(unsigned char c) { 302 return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || 303 c == '\v'; 304 } 305 306 static KitStatus build_client_depfile_make(KitBuildClient* c, 307 KitSlice depfile_text) { 308 char path[BUILD_PATH_MAX]; 309 size_t i = 0, npath = 0; 310 int in_deps = 0; 311 KitStatus st; 312 if (!c || (depfile_text.len && !depfile_text.s)) return KIT_INVALID; 313 314 #define SUBMIT_PATH() \ 315 do { \ 316 if (npath != 0u) { \ 317 st = build_client_depfile_submit(c, path, npath); \ 318 if (st != KIT_OK) return st; \ 319 npath = 0u; \ 320 } \ 321 } while (0) 322 #define APPEND_PATH(ch_) \ 323 do { \ 324 if (npath + 1u >= sizeof path) return KIT_INVALID; \ 325 path[npath++] = (char)(ch_); \ 326 } while (0) 327 328 while (i < depfile_text.len) { 329 unsigned char ch = (unsigned char)depfile_text.s[i++]; 330 if (ch == '\\') { 331 if (i >= depfile_text.len) { 332 if (in_deps) APPEND_PATH(ch); 333 continue; 334 } 335 ch = (unsigned char)depfile_text.s[i++]; 336 if (ch == '\r' && i < depfile_text.len && depfile_text.s[i] == '\n') 337 ++i; 338 if (ch == '\n' || ch == '\r') { 339 if (in_deps) SUBMIT_PATH(); 340 continue; 341 } 342 if (in_deps) APPEND_PATH(ch); 343 continue; 344 } 345 if (!in_deps) { 346 if (ch == ':') in_deps = 1; 347 continue; 348 } 349 if (ch == '#') { 350 SUBMIT_PATH(); 351 while (i < depfile_text.len) { 352 ch = (unsigned char)depfile_text.s[i++]; 353 if (ch == '\n' || ch == '\r') break; 354 } 355 in_deps = 0; 356 continue; 357 } 358 if (build_depfile_is_space(ch)) { 359 SUBMIT_PATH(); 360 if (ch == '\n' || ch == '\r') in_deps = 0; 361 continue; 362 } 363 APPEND_PATH(ch); 364 } 365 SUBMIT_PATH(); 366 367 #undef APPEND_PATH 368 #undef SUBMIT_PATH 369 return KIT_OK; 370 } 371 372 static KitStatus build_client_depfile_lines(KitBuildClient* c, 373 KitSlice depfile_text) { 374 size_t i = 0; 375 KitStatus st; 376 if (!c || (depfile_text.len && !depfile_text.s)) return KIT_INVALID; 377 while (i < depfile_text.len) { 378 size_t start, end; 379 while (i < depfile_text.len && 380 (depfile_text.s[i] == '\n' || depfile_text.s[i] == '\r')) 381 ++i; 382 start = i; 383 while (i < depfile_text.len && depfile_text.s[i] != '\n' && 384 depfile_text.s[i] != '\r') 385 ++i; 386 end = i; 387 if (end > start) { 388 st = build_client_depfile_submit(c, depfile_text.s + start, end - start); 389 if (st != KIT_OK) return st; 390 } 391 if (i < depfile_text.len && depfile_text.s[i] == '\r') { 392 ++i; 393 if (i < depfile_text.len && depfile_text.s[i] == '\n') ++i; 394 } else if (i < depfile_text.len && depfile_text.s[i] == '\n') { 395 ++i; 396 } 397 } 398 return KIT_OK; 399 } 400 401 KitStatus kit_build_client_depfile_ex(KitBuildClient* c, KitSlice depfile_text, 402 uint32_t flags) { 403 if (flags & ~((uint32_t)KIT_BUILD_DEPFILE_LINES)) return KIT_INVALID; 404 if (flags & (uint32_t)KIT_BUILD_DEPFILE_LINES) 405 return build_client_depfile_lines(c, depfile_text); 406 return build_client_depfile_make(c, depfile_text); 407 } 408 409 KitStatus kit_build_client_depfile(KitBuildClient* c, KitSlice depfile_text) { 410 return kit_build_client_depfile_ex(c, depfile_text, 411 KIT_BUILD_DEPFILE_DEFAULT); 412 } 413 414 typedef struct BuildClientGlobCb { 415 KitBuildGlobFn cb; 416 void* user; 417 int stopped; 418 } BuildClientGlobCb; 419 420 static int build_client_glob_cb(void* user, KitSlice path) { 421 BuildClientGlobCb* g = (BuildClientGlobCb*)user; 422 if (!g || g->stopped || !g->cb) return 0; 423 if (g->cb(g->user, path)) { 424 g->stopped = 1; 425 return 1; 426 } 427 return 0; 428 } 429 430 KitStatus kit_build_client_glob(KitBuildClient* c, KitSlice pattern, 431 KitBuildGlobFn cb, void* cb_user) { 432 BuildReq req; 433 BuildResp resp; 434 BuildClientGlobCb gcb; 435 size_t n = 0; 436 KitStatus st; 437 if (!c) return KIT_INVALID; 438 memset(&req, 0, sizeof req); 439 req.cmd = BUILD_CMD_GLOB; 440 req.arg = pattern; 441 if (build_proto_encode_req(&req, c->frame, sizeof c->frame, &n) != BUILD_OK) 442 return KIT_INVALID; 443 if (c->transport->write_frame(c->transport->user, c->conn, c->frame, n) != 0) 444 return KIT_IO; 445 memset(&gcb, 0, sizeof gcb); 446 gcb.cb = cb; 447 gcb.user = cb_user; 448 for (;;) { 449 if (c->transport->read_frame(c->transport->user, c->conn, c->frame, 450 sizeof c->frame, &n) != 0) 451 return KIT_IO; 452 if (build_proto_decode_resp(c->frame, n, BUILD_CMD_GLOB, &resp, 453 build_client_glob_cb, &gcb) != BUILD_OK) 454 return KIT_MALFORMED; 455 if (resp.status == BUILD_RESP_ERROR) { 456 st = build_client_error_status(&resp); 457 return st == KIT_OK ? KIT_ERR : st; 458 } 459 if (resp.status == BUILD_RESP_GLOB_END) return KIT_OK; 460 if (resp.status != BUILD_RESP_OK) return KIT_MALFORMED; 461 } 462 } 463 464 KitStatus kit_build_client_need(KitBuildClient* c, 465 const KitBuildRequest* req, 466 KitBuildResult* out) { 467 BuildReq breq; 468 if (!c || !req) return KIT_INVALID; 469 build_client_req_from_build_req(&breq, BUILD_CMD_NEED, req); 470 return build_client_need_result(c, &breq, out); 471 } 472 473 KitStatus kit_build_client_need_submit(KitBuildClient* c, 474 const KitBuildRequest* req, 475 KitBuildNeedToken* out_token) { 476 BuildReq breq; 477 BuildResp resp; 478 KitStatus st; 479 if (out_token) out_token->id = 0; 480 if (!c || !req || !out_token) return KIT_INVALID; 481 build_client_req_from_build_req(&breq, BUILD_CMD_NEED_SUBMIT, req); 482 st = build_client_rpc(c, &breq, &resp, NULL, NULL); 483 if (st != KIT_OK) return st; 484 if (resp.status != BUILD_RESP_OK) return KIT_MALFORMED; 485 out_token->id = resp.token; 486 return KIT_OK; 487 } 488 489 KitStatus kit_build_client_need_await(KitBuildClient* c, 490 KitBuildNeedToken token, 491 KitBuildResult* out) { 492 BuildReq req; 493 if (!c) return KIT_INVALID; 494 memset(&req, 0, sizeof req); 495 req.cmd = BUILD_CMD_NEED_AWAIT; 496 req.token = token.id; 497 return build_client_need_result(c, &req, out); 498 } 499 500 /* ------------------------------------------------------------------ * 501 * Workspace inspection (public wrappers over the internal parser) 502 * ------------------------------------------------------------------ */ 503 504 void kit_build_workspace_init(KitBuildWorkspace* out) { 505 build_workspace_init(out); 506 } 507 508 KitStatus kit_build_workspace_parse(const uint8_t* data, size_t len, 509 KitBuildWorkspace* out, char* err, 510 size_t errcap) { 511 if (!data || !out) return KIT_INVALID; 512 return build_workspace_parse(data, len, out, err, errcap) == BUILD_OK 513 ? KIT_OK 514 : KIT_INVALID; 515 } 516 517 const KitBuildWorkspaceExternal* kit_build_workspace_external_find( 518 const KitBuildWorkspace* ws, KitSlice name) { 519 return build_workspace_external_find(ws, name); 520 }