kit

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

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 }