kit

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

commit 22a683dab70880013e144eb59abf7c28003abaf6
parent b81292122a781a186745cc54ba9135ae1b92e006
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Mon, 22 Jun 2026 19:30:04 -0700

Complete build coordinator coverage and CLI gaps

Diffstat:
Mdriver/cmd/build_coord.c | 747++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/build/resolve.c | 36+++++++++++++++++++++++++++---------
Mtest/buildcoord/run.sh | 351++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 1116 insertions(+), 18 deletions(-)

diff --git a/driver/cmd/build_coord.c b/driver/cmd/build_coord.c @@ -5,8 +5,11 @@ #include <stddef.h> #include <stdint.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> +#include "../../src/build/defn.h" +#include "../../src/build/workspace.h" #include "dist_host.h" #include "driver.h" #include "env.h" @@ -46,6 +49,10 @@ typedef struct BuildCli { BuildClientFormat format; } BuildCli; +typedef struct BuildListTarget { + char label[BUILD_TARGET_MAX]; +} BuildListTarget; + void driver_help_build(void) { driver_printf( "kit build - content-addressed build coordinator\n" @@ -118,6 +125,302 @@ static int build_join_path(char* out, size_t cap, const char* a, return 0; } +static int build_def_name_valid(const char* s) { + size_t i; + if (!s || !s[0]) return 0; + for (i = 0; s[i]; ++i) { + unsigned char c = (unsigned char)s[i]; + if (c == '/' || c == '\\' || c == ':' || c == '\n' || c == '\r') + return 0; + } + return 1; +} + +static int build_load_file(DriverEnv* env, const char* path, DriverLoad* load, + KitSlice* bytes) { + return driver_load_bytes(&env->file_io, BUILD_TOOL, path, load, bytes); +} + +static int build_load_workspace(DriverEnv* env, const char* root, + BuildWorkspace* ws, int* present) { + char path[KIT_BUILD_PATH_MAX]; + DriverLoad load; + KitSlice bytes; + char err[256]; + if (present) *present = 0; + if (build_join_path(path, sizeof path, root, "WORKSPACE.kit") != 0) return 1; + if (!driver_path_exists(path)) { + build_workspace_init(ws); + return 0; + } + if (present) *present = 1; + if (build_load_file(env, path, &load, &bytes) != 0) return 1; + if (build_workspace_parse(bytes.data, bytes.len, ws, err, sizeof err) != + BUILD_OK) { + driver_errf(BUILD_TOOL, "bad WORKSPACE.kit: %s", err); + driver_release_bytes(&env->file_io, &load); + return 1; + } + driver_release_bytes(&env->file_io, &load); + return 0; +} + +static int build_file_write(DriverEnv* env, const char* path, const char* text) { + KitWriter* w = NULL; + size_t n; + if (!env || !path || !text) return 1; + if (env->file_io.open_writer(env->file_io.user, path, &w) != KIT_OK || !w) + return 1; + n = driver_strlen(text); + if ((n && kit_writer_write(w, text, n) != KIT_OK) || + kit_writer_status(w) != KIT_OK) { + kit_writer_close(w); + return 1; + } + kit_writer_close(w); + return 0; +} + +static int build_file_append(DriverEnv* env, const char* path, + const char* text) { + DriverLoad load; + KitSlice old = KIT_SLICE_NULL; + KitWriter* w = NULL; + size_t n; + int had_old = 0; + if (!env || !path || !text) return 1; + if (driver_path_exists(path)) { + if (build_load_file(env, path, &load, &old) != 0) return 1; + had_old = 1; + } + if (env->file_io.open_writer(env->file_io.user, path, &w) != KIT_OK || !w) { + if (had_old) driver_release_bytes(&env->file_io, &load); + return 1; + } + n = driver_strlen(text); + if ((old.len && kit_writer_write(w, old.data, old.len) != KIT_OK) || + (n && kit_writer_write(w, text, n) != KIT_OK) || + kit_writer_status(w) != KIT_OK) { + kit_writer_close(w); + if (had_old) driver_release_bytes(&env->file_io, &load); + return 1; + } + kit_writer_close(w); + if (had_old) driver_release_bytes(&env->file_io, &load); + return 0; +} + +static int build_file_url_path(const char* url, const char** out) { + if (!url || !out || !driver_strneq(url, "file://", 7u)) return 1; + *out = url + 7u; + return 0; +} + +static int build_list_cmp(const void* a, const void* b) { + const BuildListTarget* la = (const BuildListTarget*)a; + const BuildListTarget* lb = (const BuildListTarget*)b; + return strcmp(la->label, lb->label); +} + +static int build_list_add(BuildListTarget* out, size_t* n, size_t cap, + const char* label) { + size_t i; + if (!out || !n || !label || !label[0]) return 1; + for (i = 0; i < *n; ++i) + if (driver_streq(out[i].label, label)) return 0; + if (*n >= cap || driver_strlen(label) >= sizeof out[0].label) return 1; + snprintf(out[*n].label, sizeof out[*n].label, "%s", label); + ++*n; + return 0; +} + +static void build_pkg_label(char* out, size_t cap, const char* pkg, + const char* local) { + if (pkg && pkg[0]) + snprintf(out, cap, "//%s:%s", pkg, local); + else + snprintf(out, cap, "//:%s", local); +} + +static int build_line_next(KitSlice bytes, size_t* pos, KitSlice* line) { + size_t start = *pos; + size_t end = start; + while (end < bytes.len && bytes.s[end] != '\n') ++end; + if (start >= bytes.len) return 1; + line->s = bytes.s + start; + line->len = end - start; + *pos = end < bytes.len ? end + 1u : end; + return 0; +} + +static int build_slice_starts(KitSlice s, const char* prefix) { + size_t n = driver_strlen(prefix); + return s.len >= n && memcmp(s.s, prefix, n) == 0; +} + +static void build_copy_slice0(char* out, size_t cap, KitSlice s) { + size_t n = s.len < cap - 1u ? s.len : cap - 1u; + if (n) memcpy(out, s.s, n); + out[n] = '\0'; +} + +static int build_list_generated_from_projection(DriverEnv* env, + const char* root, + const char* pkg, + KitSlice line, + BuildListTarget* out, + size_t* n, size_t cap) { + char pat[BUILD_PATH_MAX], tmpl[BUILD_TARGET_MAX]; + char dir_rel[BUILD_PATH_MAX], dir_abs[KIT_BUILD_PATH_MAX]; + char prefix[128], suffix[128], label[BUILD_TARGET_MAX], local[BUILD_TARGET_MAX]; + DriverDirHandle* dh; + size_t i, arrow = (size_t)-1, star = (size_t)-1, slash = (size_t)-1; + if (!build_slice_starts(line, "list ")) return 0; + line.s += 5u; + line.len -= 5u; + for (i = 0; i + 3u <= line.len; ++i) { + if (line.s[i] == ' ' && line.s[i + 1u] == '-' && line.s[i + 2u] == '>' && + line.s[i + 3u] == ' ') { + arrow = i; + break; + } + } + if (arrow == (size_t)-1) return 0; + build_copy_slice0(pat, sizeof pat, (KitSlice){.s = line.s, .len = arrow}); + build_copy_slice0(tmpl, sizeof tmpl, + (KitSlice){.s = line.s + arrow + 4u, + .len = line.len - arrow - 4u}); + for (i = 0; pat[i]; ++i) { + if (pat[i] == '*') star = i; + if (pat[i] == '/') slash = i; + } + if (star == (size_t)-1) return 0; + if (slash != (size_t)-1) { + snprintf(dir_rel, sizeof dir_rel, "%s/%.*s", pkg, + (int)slash, pat); + snprintf(prefix, sizeof prefix, "%.*s", (int)(star - slash - 1u), + pat + slash + 1u); + } else { + snprintf(dir_rel, sizeof dir_rel, "%s", pkg); + snprintf(prefix, sizeof prefix, "%.*s", (int)star, pat); + } + snprintf(suffix, sizeof suffix, "%s", pat + star + 1u); + if (build_join_path(dir_abs, sizeof dir_abs, root, dir_rel) != 0) return 1; + dh = driver_open_dir(env, dir_abs); + if (!dh) return 0; + for (i = 0;; ++i) { + const char* name; + uint32_t name_len; + uint64_t ino, size, mtime; + uint8_t ftype; + size_t pn = driver_strlen(prefix), sn = driver_strlen(suffix); + char stem[BUILD_TARGET_MAX]; + if (driver_read_dir_entry(dh, i, &name, &name_len, &ino, &size, &mtime, + &ftype) != 0) + break; + (void)ino; + (void)size; + (void)mtime; + if (ftype != 4u || name_len < pn + sn || + memcmp(name, prefix, pn) != 0 || + (sn && memcmp(name + name_len - sn, suffix, sn) != 0)) + continue; + snprintf(stem, sizeof stem, "%.*s", (int)(name_len - pn - sn), + name + pn); + if (driver_streq(tmpl, "{stem}.o")) + snprintf(local, sizeof local, "%s.o", stem); + else + snprintf(local, sizeof local, "%s", stem); + build_pkg_label(label, sizeof label, pkg, local); + if (build_list_add(out, n, cap, label) != 0) { + driver_close_dir(env, dh); + return 1; + } + } + driver_close_dir(env, dh); + return 0; +} + +static int build_list_package(DriverEnv* env, const char* root, + const char* def_name, const char* pkg, + int recursive, BuildListTarget* out, size_t* n, + size_t cap) { + char pkg_dir[KIT_BUILD_PATH_MAX], def_path[KIT_BUILD_PATH_MAX]; + DriverLoad load; + KitSlice bytes, line; + size_t pos = 0; + int in_targets = 0; + if (build_join_path(pkg_dir, sizeof pkg_dir, root, pkg) != 0 || + build_join_path(def_path, sizeof def_path, pkg_dir, def_name) != 0) + return 1; + if (driver_path_exists(def_path) && + build_load_file(env, def_path, &load, &bytes) == 0) { + while (build_line_next(bytes, &pos, &line) == 0) { + if (build_slice_starts(line, "[target ") && line.len > 9u && + line.s[line.len - 1u] == ']') { + char local[BUILD_TARGET_MAX], label[BUILD_TARGET_MAX]; + build_copy_slice0(local, sizeof local, + (KitSlice){.s = line.s + 8u, + .len = line.len - 9u}); + build_pkg_label(label, sizeof label, pkg, local); + if (build_list_add(out, n, cap, label) != 0) { + driver_release_bytes(&env->file_io, &load); + return 1; + } + in_targets = 0; + } else if (build_slice_starts(line, "[targets ")) { + in_targets = 1; + } else if (line.len && line.s[0] == '[') { + in_targets = 0; + } else if (in_targets) { + if (build_list_generated_from_projection(env, root, pkg, line, out, n, + cap) != 0) { + driver_release_bytes(&env->file_io, &load); + return 1; + } + } + } + driver_release_bytes(&env->file_io, &load); + } + if (recursive) { + DriverDirHandle* dh = driver_open_dir(env, pkg_dir); + uint64_t idx; + if (!dh) return 0; + for (idx = 0;; ++idx) { + const char* name; + uint32_t name_len; + uint64_t ino, size, mtime; + uint8_t ftype; + char child_pkg[BUILD_PATH_MAX], child_dir[KIT_BUILD_PATH_MAX], + child_def[KIT_BUILD_PATH_MAX]; + if (driver_read_dir_entry(dh, idx, &name, &name_len, &ino, &size, &mtime, + &ftype) != 0) + break; + (void)ino; + (void)size; + (void)mtime; + if (ftype != 3u) continue; + if (pkg && pkg[0]) + snprintf(child_pkg, sizeof child_pkg, "%s/%.*s", pkg, (int)name_len, + name); + else + snprintf(child_pkg, sizeof child_pkg, "%.*s", (int)name_len, name); + if (build_join_path(child_dir, sizeof child_dir, root, child_pkg) != 0 || + build_join_path(child_def, sizeof child_def, child_dir, def_name) != 0) + continue; + if (driver_path_exists(child_def)) { + if (build_list_package(env, root, def_name, child_pkg, 1, out, n, + cap) != 0) { + driver_close_dir(env, dh); + return 1; + } + } + } + driver_close_dir(env, dh); + } + return 0; +} + static int build_add_config(BuildCli* cli, KitSlice key, KitSlice value) { if (!cli || !key.s || key.len == 0u || cli->nconfig >= BUILD_MAX_CONFIG) return 1; @@ -224,7 +527,8 @@ static void build_client_print_value(BuildClientFormat fmt, static int build_client_verb(const char* s) { return driver_streq(s, "config-get") || driver_streq(s, "source") || driver_streq(s, "fetch") || driver_streq(s, "depfile") || - driver_streq(s, "glob") || + driver_streq(s, "glob") || driver_streq(s, "export-set") || + driver_streq(s, "export-get") || driver_streq(s, "export-collect") || driver_streq(s, "need") || driver_streq(s, "need-submit") || driver_streq(s, "need-await"); } @@ -301,6 +605,95 @@ static int build_client_need_parse(BuildCli* cli, int argc, char** argv, return 0; } +static int build_export_set(DriverEnv* env, int argc, char** argv, + int first_arg) { + const char* out_dir = driver_getenv(KIT_BUILD_ENV_OUT); + char kit_dir[KIT_BUILD_PATH_MAX], exports_path[KIT_BUILD_PATH_MAX]; + size_t i; + if (!out_dir || argc < first_arg + 3 || + !driver_streq(argv[first_arg + 1], "--")) { + driver_errf(BUILD_TOOL, "usage: kit build export-set VAR -- VALUE..."); + return 2; + } + if (build_join_path(kit_dir, sizeof kit_dir, out_dir, ".kit") != 0 || + build_join_path(exports_path, sizeof exports_path, kit_dir, "exports") != + 0) { + driver_errf(BUILD_TOOL, "export path too long"); + return 1; + } + if (driver_mkdir_p(env, kit_dir) != 0) { + driver_errf(BUILD_TOOL, "failed to create exports directory"); + return 1; + } + if (!driver_path_exists(exports_path) && + build_file_write(env, exports_path, "kit-build-exports 1\n") != 0) { + driver_errf(BUILD_TOOL, "failed to write exports file"); + return 1; + } + { + char line[BUILD_VAL_MAX + 32u]; + snprintf(line, sizeof line, "[var %s]\n", argv[first_arg]); + if (build_file_append(env, exports_path, line) != 0) { + driver_errf(BUILD_TOOL, "failed to append exports variable"); + return 1; + } + } + for (i = (size_t)first_arg + 2u; i < (size_t)argc; ++i) { + char line[BUILD_VAL_MAX + 2u]; + snprintf(line, sizeof line, "%s\n", argv[i]); + if (build_file_append(env, exports_path, line) != 0) { + driver_errf(BUILD_TOOL, "failed to append exports value"); + return 1; + } + } + return 0; +} + +static int build_exports_print_var(DriverEnv* env, const char* path, + const char* var) { + DriverLoad load; + KitSlice bytes, line; + size_t pos = 0; + int active = 0, found = 0; + if (build_load_file(env, path, &load, &bytes) != 0) return 1; + while (build_line_next(bytes, &pos, &line) == 0) { + if (line.len >= 6u && build_slice_starts(line, "[var ") && + line.s[line.len - 1u] == ']') { + KitSlice name = {.s = line.s + 5u, .len = line.len - 6u}; + active = name.len == driver_strlen(var) && + memcmp(name.s, var, name.len) == 0; + if (active) found = 1; + } else if (active && line.len && line.s[0] != '[') { + driver_printf("%.*s\n", KIT_SLICE_ARG(line)); + } + } + driver_release_bytes(&env->file_io, &load); + return found ? 0 : 1; +} + +static int build_export_get_one(DriverEnv* env, KitBuildClient* client, + const char* target, const char* var) { + KitBuildRequest req; + KitBuildResult result; + KitStatus st; + char exports_path[KIT_BUILD_PATH_MAX], kit_dir[KIT_BUILD_PATH_MAX]; + memset(&req, 0, sizeof req); + req.target = kit_slice_cstr(target); + st = kit_build_client_need(client, &req, &result); + if (st != KIT_OK) { + driver_errf(BUILD_TOOL, "export-get need failed: %s", build_status_name(st)); + return 1; + } + if (build_join_path(kit_dir, sizeof kit_dir, result.path, ".kit") != 0 || + build_join_path(exports_path, sizeof exports_path, kit_dir, "exports") != + 0 || + build_exports_print_var(env, exports_path, var) != 0) { + driver_errf(BUILD_TOOL, "export variable not found: %s", var); + return 1; + } + return 0; +} + static int build_client_mode(int argc, char** argv, int verb_index) { DriverEnv env; DriverBuildHost bh; @@ -317,6 +710,10 @@ static int build_client_mode(int argc, char** argv, int verb_index) { verb = argv[verb_index]; driver_env_init(&env); ctx = driver_env_to_context(&env); + if (driver_streq(verb, "export-set")) { + rc = build_export_set(&env, argc, argv, first_arg); + goto out_env; + } if (driver_build_host_init(&bh, &env) != 0) { driver_errf(BUILD_TOOL, "hosted build adapter is unavailable"); goto out_env; @@ -587,6 +984,26 @@ static int build_client_mode(int argc, char** argv, int verb_index) { build_client_print_value(cli.format, result.output_tree, kit_slice_cstr(result.path)); rc = 0; + } else if (driver_streq(verb, "export-get")) { + if (argc != first_arg + 2) { + driver_errf(BUILD_TOOL, "usage: kit build export-get TARGET VAR"); + rc = 2; + goto out_client; + } + rc = build_export_get_one(&env, client, argv[first_arg], + argv[first_arg + 1]); + } else if (driver_streq(verb, "export-collect")) { + int i; + if (argc < first_arg + 2) { + driver_errf(BUILD_TOOL, "usage: kit build export-collect VAR TARGET..."); + rc = 2; + goto out_client; + } + rc = 0; + for (i = first_arg + 1; i < argc; ++i) { + if (build_export_get_one(&env, client, argv[i], argv[first_arg]) != 0) + rc = 1; + } } else { driver_errf(BUILD_TOOL, "unknown client command: %s", verb); rc = 2; @@ -613,18 +1030,23 @@ static int build_parse_args(BuildCli* cli, int argc, char** argv, int first_arg, cli->store = argv[++i]; } else if (driver_streq(a, "--root") && i + 1 < argc) { cli->root = argv[++i]; - } else if (driver_streq(a, "--def") && i + 1 < argc) { + } else if ((driver_streq(a, "--def") || driver_streq(a, "--def-name")) && + i + 1 < argc) { cli->def = argv[++i]; } else if (driver_streq(a, "--profile") && i + 1 < argc) { cli->profile = argv[++i]; } else if (driver_streq(a, "--config") && i + 1 < argc) { - if (build_parse_config(cli, argv[++i]) != 0) { - driver_errf(BUILD_TOOL, "bad --config, expected K=V or env.NAME"); + const char* opt = argv[++i]; + if (build_parse_config(cli, opt) != 0) { + driver_errf(BUILD_TOOL, + "bad --config '%s', expected K=V or env.NAME", opt); return 2; } } else if (driver_streq(a, "--env") && i + 1 < argc) { - if (build_parse_env(cli, argv[++i]) != 0) { - driver_errf(BUILD_TOOL, "bad --env, expected NAME or NAME=VALUE"); + const char* opt = argv[++i]; + if (build_parse_env(cli, opt) != 0) { + driver_errf(BUILD_TOOL, "bad --env '%s', expected NAME or NAME=VALUE", + opt); return 2; } } else if (driver_streq(a, "--")) { @@ -666,6 +1088,10 @@ static int build_parse_args(BuildCli* cli, int argc, char** argv, int first_arg, driver_errf(BUILD_TOOL, "missing target"); return 2; } + if (cli->def && !build_def_name_valid(cli->def)) { + driver_errf(BUILD_TOOL, "bad def-name: %s", cli->def); + return 2; + } return 0; } @@ -713,6 +1139,304 @@ static int build_verify_tree(DriverEnv* env, const char* store, return 0; } +static int build_find_top_command(int argc, char** argv) { + int i; + for (i = 1; i < argc; ++i) { + if ((driver_streq(argv[i], "--store") || driver_streq(argv[i], "--root") || + driver_streq(argv[i], "--def") || driver_streq(argv[i], "--def-name") || + driver_streq(argv[i], "--profile") || driver_streq(argv[i], "--config") || + driver_streq(argv[i], "--env")) && + i + 1 < argc) { + ++i; + continue; + } + if (driver_streq(argv[i], "--stats") || driver_streq(argv[i], "--trace") || + driver_streq(argv[i], "--verify")) + continue; + if (driver_streq(argv[i], "list") || driver_streq(argv[i], "repo") || + driver_streq(argv[i], "workspace")) + return i; + return -1; + } + return -1; +} + +static void build_global_option_defaults(int argc, char** argv, int cmd_index, + const char** root, + const char** def_name, + const char** store) { + int i; + (void)argc; + *root = "."; + *def_name = BUILD_DEFAULT_DEF; + *store = NULL; + for (i = 1; i < cmd_index; ++i) { + if (driver_streq(argv[i], "--root") && i + 1 < cmd_index) { + *root = argv[++i]; + } else if ((driver_streq(argv[i], "--def") || + driver_streq(argv[i], "--def-name")) && + i + 1 < cmd_index) { + *def_name = argv[++i]; + } else if (driver_streq(argv[i], "--store") && i + 1 < cmd_index) { + *store = argv[++i]; + } else if ((driver_streq(argv[i], "--profile") || + driver_streq(argv[i], "--config") || + driver_streq(argv[i], "--env")) && + i + 1 < cmd_index) { + ++i; + } + } +} + +static int build_cmd_list(int argc, char** argv, int cmd_index) { + DriverEnv env; + const char *root, *def_name, *store_ignored; + const char* pattern = "//..."; + int scan_do = 0; + int i, recursive = 0; + char pkg[BUILD_PATH_MAX]; + BuildListTarget targets[512]; + size_t ntargets = 0, j; + (void)store_ignored; + build_global_option_defaults(argc, argv, cmd_index, &root, &def_name, + &store_ignored); + if (!build_def_name_valid(def_name)) { + driver_errf(BUILD_TOOL, "bad def-name"); + return 2; + } + for (i = cmd_index + 1; i < argc; ++i) { + if (driver_streq(argv[i], "--scan-do")) { + scan_do = 1; + } else if (argv[i][0] == '-') { + driver_errf(BUILD_TOOL, "unexpected list option: %s", argv[i]); + return 2; + } else { + pattern = argv[i]; + } + } + memset(pkg, 0, sizeof pkg); + if (!driver_strneq(pattern, "//", 2u)) { + driver_errf(BUILD_TOOL, "list pattern must be //pkg or //pkg/..."); + return 2; + } + { + const char* body = pattern + 2u; + size_t len = driver_strlen(body); + if (len >= 4u && driver_streq(body + len - 4u, "/...")) { + recursive = 1; + len -= 4u; + } + if (len >= sizeof pkg) return 2; + memcpy(pkg, body, len); + pkg[len] = '\0'; + } + driver_env_init(&env); + if (scan_do) { + char pkg_dir[KIT_BUILD_PATH_MAX], do_dir[KIT_BUILD_PATH_MAX]; + DriverDirHandle* dh; + if (build_join_path(pkg_dir, sizeof pkg_dir, root, pkg) != 0 || + build_join_path(do_dir, sizeof do_dir, pkg_dir, "do") != 0) { + driver_env_fini(&env); + return 1; + } + dh = driver_open_dir(&env, do_dir); + if (dh) { + uint64_t idx; + for (idx = 0;; ++idx) { + const char* name; + uint32_t name_len; + uint64_t ino, size, mtime; + uint8_t ftype; + if (driver_read_dir_entry(dh, idx, &name, &name_len, &ino, &size, + &mtime, &ftype) != 0) + break; + (void)ino; + (void)size; + (void)mtime; + if (ftype == 4u && name_len > 3u && + memcmp(name + name_len - 3u, ".do", 3u) == 0) + driver_printf("//%s:%.*s %.*s\n", pkg, (int)name_len, name, + (int)name_len, name); + } + driver_close_dir(&env, dh); + } + } else if (build_list_package(&env, root, def_name, pkg, recursive, targets, + &ntargets, + sizeof targets / sizeof targets[0]) != 0) { + driver_env_fini(&env); + return 1; + } + qsort(targets, ntargets, sizeof targets[0], build_list_cmp); + for (j = 0; j < ntargets; ++j) driver_printf("%s\n", targets[j].label); + driver_env_fini(&env); + return 0; +} + +static int build_cmd_repo(int argc, char** argv, int cmd_index) { + DriverEnv env; + const char *root, *def_name_ignored, *store_ignored; + BuildWorkspace ws; + int present = 0; + size_t i; + (void)def_name_ignored; + (void)store_ignored; + if (argc <= cmd_index + 1) { + driver_errf(BUILD_TOOL, "usage: kit build repo {add|fetch|list}"); + return 2; + } + build_global_option_defaults(argc, argv, cmd_index, &root, &def_name_ignored, + &store_ignored); + driver_env_init(&env); + if (build_load_workspace(&env, root, &ws, &present) != 0) { + driver_env_fini(&env); + return 1; + } + if (driver_streq(argv[cmd_index + 1], "list")) { + for (i = 0; i < ws.n_externals; ++i) { + char hex[BUILD_HEX_LEN]; + const char* fmt = ws.externals[i].format == BUILD_WS_EXT_TREE + ? "tree" + : ws.externals[i].format == BUILD_WS_EXT_TARGZ + ? "tar.gz" + : "kpkg"; + kit_hex_encode(hex, ws.externals[i].archive, BUILD_HASH_LEN); + driver_printf("%s %s %s\n", ws.externals[i].name, fmt, hex); + } + } else if (driver_streq(argv[cmd_index + 1], "fetch")) { + if (argc <= cmd_index + 2) { + driver_errf(BUILD_TOOL, "usage: kit build repo fetch NAME"); + driver_env_fini(&env); + return 2; + } + if (!build_workspace_external_find(&ws, kit_slice_cstr(argv[cmd_index + 2]))) { + driver_errf(BUILD_TOOL, "unknown external repo: %s", argv[cmd_index + 2]); + driver_env_fini(&env); + return 1; + } + driver_printf("%s\n", argv[cmd_index + 2]); + } else if (driver_streq(argv[cmd_index + 1], "add")) { + const char* name; + const char* url; + const char* src; + const char* format = "tar.gz"; + const char* strip = NULL; + char ws_path[KIT_BUILD_PATH_MAX], line[2048], hex[BUILD_HEX_LEN]; + DriverLoad load; + KitSlice bytes; + KitBlobInfo bi; + int a; + if (argc <= cmd_index + 3) { + driver_errf(BUILD_TOOL, "usage: kit build repo add NAME URL"); + driver_env_fini(&env); + return 2; + } + name = argv[cmd_index + 2]; + url = argv[cmd_index + 3]; + for (a = cmd_index + 4; a < argc; ++a) { + if (driver_streq(argv[a], "--format") && a + 1 < argc) { + format = argv[++a]; + } else if (driver_streq(argv[a], "--strip-prefix") && a + 1 < argc) { + strip = argv[++a]; + } + } + if (build_file_url_path(url, &src) != 0 || + build_load_file(&env, src, &load, &bytes) != 0) { + driver_env_fini(&env); + return 1; + } + kit_blob_info(&bi, bytes.data, bytes.len); + driver_release_bytes(&env.file_io, &load); + kit_hex_encode(hex, bi.id, BUILD_HASH_LEN); + if (build_join_path(ws_path, sizeof ws_path, root, "WORKSPACE.kit") != 0) { + driver_env_fini(&env); + return 1; + } + snprintf(line, sizeof line, + "\n[external %s]\nformat %s\narchive %s\nurl %s\n", name, format, + hex, url); + if (build_file_append(&env, ws_path, line) != 0 || + (strip && (snprintf(line, sizeof line, "strip-prefix %s\n", strip), + build_file_append(&env, ws_path, line) != 0))) { + driver_env_fini(&env); + return 1; + } + driver_printf("%s %s\n", name, hex); + } else { + driver_errf(BUILD_TOOL, "unknown repo command: %s", argv[cmd_index + 1]); + driver_env_fini(&env); + return 2; + } + driver_env_fini(&env); + return 0; +} + +static int build_cmd_workspace(int argc, char** argv, int cmd_index) { + DriverEnv env; + DriverBuildHost bh; + const char *root, *def_name_ignored, *store_ignored; + const char* out = NULL; + const char* format = "tar.gz"; + const char* tar_paths[] = {"/usr/bin/tar", "/bin/tar"}; + KitBuildKV envv[1]; + KitBuildProc* proc = NULL; + int exit_code = 127; + int ran = 0; + int i; + (void)def_name_ignored; + (void)store_ignored; + if (argc <= cmd_index + 1 || !driver_streq(argv[cmd_index + 1], "package")) { + driver_errf(BUILD_TOOL, "usage: kit build workspace package -o OUT"); + return 2; + } + build_global_option_defaults(argc, argv, cmd_index, &root, &def_name_ignored, + &store_ignored); + for (i = cmd_index + 2; i < argc; ++i) { + if (driver_streq(argv[i], "--format") && i + 1 < argc) + format = argv[++i]; + else if (driver_streq(argv[i], "-o") && i + 1 < argc) + out = argv[++i]; + } + if (!out || !driver_streq(format, "tar.gz")) { + driver_errf(BUILD_TOOL, "workspace package supports --format tar.gz -o OUT"); + return 2; + } + driver_env_init(&env); + if (driver_build_host_init(&bh, &env) != 0) { + driver_env_fini(&env); + return 1; + } + envv[0].key = kit_slice_cstr("COPYFILE_DISABLE"); + envv[0].value = kit_slice_cstr("1"); + for (i = 0; i < (int)(sizeof tar_paths / sizeof tar_paths[0]); ++i) { + KitSlice tar_argv[6]; + tar_argv[0] = kit_slice_cstr(tar_paths[i]); + tar_argv[1] = kit_slice_cstr("--format"); + tar_argv[2] = kit_slice_cstr("ustar"); + tar_argv[3] = kit_slice_cstr("-czf"); + tar_argv[4] = kit_slice_cstr(out); + tar_argv[5] = kit_slice_cstr("."); + proc = NULL; + if (bh.exec.spawn(bh.exec.user, tar_argv, 6u, envv, 1u, + kit_slice_cstr(root), &proc) != 0) + continue; + ran = 1; + if (bh.exec.wait(bh.exec.user, proc, &exit_code) != 0) { + driver_build_host_fini(&bh); + driver_env_fini(&env); + return 1; + } + if (exit_code != 127) break; + } + driver_build_host_fini(&bh); + driver_env_fini(&env); + if (!ran || exit_code != 0) { + driver_errf(BUILD_TOOL, "workspace package failed"); + return 1; + } + driver_printf("%s\n", out); + return 0; +} + int driver_build(int argc, char** argv) { DriverEnv env; DriverBuildHost bh; @@ -742,6 +1466,17 @@ int driver_build(int argc, char** argv) { if (argc >= 2 && driver_getenv(KIT_BUILD_ENV_SOCK) && build_client_verb(argv[1])) return build_client_mode(argc, argv, 1); + { + int cmd_index = build_find_top_command(argc, argv); + if (cmd_index >= 0) { + if (driver_streq(argv[cmd_index], "list")) + return build_cmd_list(argc, argv, cmd_index); + if (driver_streq(argv[cmd_index], "repo")) + return build_cmd_repo(argc, argv, cmd_index); + if (driver_streq(argv[cmd_index], "workspace")) + return build_cmd_workspace(argc, argv, cmd_index); + } + } rc = build_parse_args(&cli, argc, argv, argc >= 2 && driver_streq(argv[1], "test") ? 2 : 1, diff --git a/src/build/resolve.c b/src/build/resolve.c @@ -1092,10 +1092,13 @@ out: static int build_leafset_refresh_inner(KitBuildCoordinator* c, const BuildLeafSet* leafset, const BuildConfig* cfg, - int check_config, int* all_match) { + int check_config, + const char* trace_root, + int* all_match) { size_t i; KitSlice target; uint8_t recipe[BUILD_HASH_LEN]; + int matched_all = 1; if (!c || !leafset || !cfg || !all_match) return BUILD_ERR; *all_match = 0; target = kit_slice_cstr(leafset->target); @@ -1104,8 +1107,20 @@ static int build_leafset_refresh_inner(KitBuildCoordinator* c, goto done; if (check_config) { for (i = 0; i < leafset->n_configs; ++i) { - if (!config_observation_match(cfg, &leafset->configs[i])) goto done; + int matched = config_observation_match(cfg, &leafset->configs[i]); + if (c->opts.trace) + build_coord_tracef(c, + "deep-config target=%s key=%s scope=root result=%s", + leafset->target, leafset->configs[i].key, + matched ? "match" : "mismatch"); + if (!matched) matched_all = 0; } + } else if (c->opts.trace) { + for (i = 0; i < leafset->n_configs; ++i) + build_coord_tracef(c, + "deep-config target=%s child=%s key=%s scope=overlay-shielded", + trace_root ? trace_root : leafset->target, + leafset->target, leafset->configs[i].key); } for (i = 0; i < leafset->n_sources; ++i) { uint8_t blob[BUILD_HASH_LEN]; @@ -1115,9 +1130,9 @@ static int build_leafset_refresh_inner(KitBuildCoordinator* c, blob, &present) != BUILD_OK) return BUILD_ERR; if (leafset->sources[i].absent) { - if (present) goto done; + if (present) matched_all = 0; } else if (!present || !build_id_eq(blob, leafset->sources[i].blob)) { - goto done; + matched_all = 0; } } for (i = 0; i < leafset->n_globs; ++i) { @@ -1126,25 +1141,28 @@ static int build_leafset_refresh_inner(KitBuildCoordinator* c, kit_slice_cstr(leafset->globs[i].pattern), hash, NULL, NULL) != BUILD_OK) return BUILD_ERR; - if (!build_id_eq(hash, leafset->globs[i].result_hash)) goto done; + if (!build_id_eq(hash, leafset->globs[i].result_hash)) matched_all = 0; } for (i = 0; i < leafset->n_blobs; ++i) { if (kit_cas_has_blob(c->cas, leafset->blobs[i].blob) != KIT_OK) - goto done; + matched_all = 0; } for (i = 0; i < leafset->n_children; ++i) { int child_match = 0; if (build_leafset_refresh_inner(c, leafset->children[i], cfg, 0, + trace_root ? trace_root : leafset->target, &child_match) != BUILD_OK) return BUILD_ERR; - if (!child_match) goto done; + if (!child_match) matched_all = 0; } - *all_match = 1; + if (matched_all) *all_match = 1; done: return BUILD_OK; } int build_leafset_refresh(KitBuildCoordinator* c, const BuildLeafSet* leafset, const BuildConfig* cfg, int* all_match) { - return build_leafset_refresh_inner(c, leafset, cfg, 1, all_match); + return build_leafset_refresh_inner(c, leafset, cfg, 1, + leafset ? leafset->target : NULL, + all_match); } diff --git a/test/buildcoord/run.sh b/test/buildcoord/run.sh @@ -23,7 +23,7 @@ kit_report_init ws="$work/ws" store="$work/store" mkdir -p "$ws/recipes" "$ws/remote" "$ws/src/depfile" "$ws/src/globset" \ - "$ws/src/tree/nested/deeper" "$store" + "$ws/src/list/sub" "$ws/src/tree/nested/deeper" "$store" make_pkg() { pkg=$1 @@ -41,12 +41,15 @@ make_pkg() { make_pkg absent probe absent.sh make_pkg app bundle app.sh make_pkg argv argv argv.sh echo argv.sh +make_pkg async dep async_dep.sh unawaited async_unawaited.sh make_pkg cfg default cfg_default.sh probe cfg.sh make_pkg client formats client_formats.sh make_pkg cycle a cycle_a.sh b cycle_b.sh self cycle_self.sh make_pkg defn probe defn_v1.sh unused defn_unused_v1.sh make_pkg depfile lines depfile_lines.sh probe depfile.sh make_pkg env probe env_probe.sh +make_pkg exports consumer exports_consumer.sh other exports_other.sh \ + provider exports_provider.sh make_pkg extuser probe extuser.sh make_pkg fail probe fail.sh make_pkg fetch blob fetch.sh format fetch_format.sh @@ -62,6 +65,7 @@ make_pkg scope leaf scope_leaf.sh parent-inherit scope_parent_inherit.sh \ parent-override scope_parent_override.sh plain scope_plain.sh make_pkg shadow leaf shadow_leaf.sh parent shadow_parent.sh make_pkg stable dep stable_dep.sh parent stable_parent.sh +make_pkg storage probe material.sh make_pkg submit echo argv.sh probe submit.sh make_pkg test fail test_fail.sh nondet test_nondet.sh parent test_parent.sh \ pass test_pass.sh separate test_pass.sh @@ -77,6 +81,35 @@ search . do walk-parents true EOF +mkdir -p "$ws/listpkg" "$ws/listpkg/subpkg" +cat > "$ws/listpkg/BUILD.kit" <<'EOF' +kit-build 2 + +[target exact] +recipe //recipes/argv.sh + +[targets c.object] +list ../src/list/*.c -> {stem}.o + +[rule object] +match *.o +type c.object + +[default c.object] +recipe //recipes/argv.sh + +[redo-defaults] +enabled true +search . do +walk-parents true +EOF + +cat > "$ws/listpkg/subpkg/BUILD.kit" <<'EOF' +kit-build 1 +[target child] +recipe //recipes/argv.sh +EOF + cat > "$ws/BUILD.kit" <<'EOF' kit-build 2 @@ -107,6 +140,12 @@ EOF cat > "$ws/src/globset/one.txt" <<'EOF' one EOF +cat > "$ws/src/list/foo.c" <<'EOF' +int foo; +EOF +cat > "$ws/src/list/sub/bar.c" <<'EOF' +int bar; +EOF cat > "$ws/src/tree/root.txt" <<'EOF' root EOF @@ -176,6 +215,27 @@ mkdir -p "$KIT_BUILD_OUT" printf '%s\n' "$@" > "$KIT_BUILD_OUT/args.txt" EOF +cat > "$ws/recipes/async_dep.sh" <<'EOF' +#!/bin/sh +set -eu +count=0 +if [ -f async-dep.count ]; then + count=$(cat async-dep.count) +fi +count=$((count + 1)) +printf '%s\n' "$count" > async-dep.count +mkdir -p "$KIT_BUILD_OUT" +printf 'async-dep:%s\n' "$count" > "$KIT_BUILD_OUT/dep.txt" +EOF + +cat > "$ws/recipes/async_unawaited.sh" <<'EOF' +#!/bin/sh +set -eu +mkdir -p "$KIT_BUILD_OUT" +"$KIT" build need-submit //async:dep >/dev/null +printf 'unawaited\n' > "$KIT_BUILD_OUT/unawaited.txt" +EOF + cat > "$ws/recipes/cfg.sh" <<'EOF' #!/bin/sh set -eu @@ -295,6 +355,33 @@ printf 'ambient:%s\n' "${AMBIENT-unset}" > "$KIT_BUILD_OUT/env.txt" printf 'declared:%s\n' "${DECLARED-unset}" >> "$KIT_BUILD_OUT/env.txt" EOF +cat > "$ws/recipes/exports_provider.sh" <<'EOF' +#!/bin/sh +set -eu +mkdir -p "$KIT_BUILD_OUT" +"$KIT" build export-set cflags -- -Iinclude -DPROVIDER=1 +"$KIT" build export-set libs -- -Llib -lprovider +printf 'provider\n' > "$KIT_BUILD_OUT/provider.txt" +EOF + +cat > "$ws/recipes/exports_other.sh" <<'EOF' +#!/bin/sh +set -eu +mkdir -p "$KIT_BUILD_OUT" +"$KIT" build export-set cflags -- -Iother +printf 'other\n' > "$KIT_BUILD_OUT/other.txt" +EOF + +cat > "$ws/recipes/exports_consumer.sh" <<'EOF' +#!/bin/sh +set -eu +mkdir -p "$KIT_BUILD_OUT" +"$KIT" build export-get //exports:provider cflags > "$KIT_BUILD_OUT/cflags.txt" +"$KIT" build export-get //exports:provider libs > "$KIT_BUILD_OUT/libs.txt" +"$KIT" build export-collect cflags //exports:provider //exports:other \ + > "$KIT_BUILD_OUT/collect.txt" +EOF + cat > "$ws/recipes/fail.sh" <<'EOF' #!/bin/sh set -eu @@ -657,6 +744,13 @@ run_test() { > "$work/$name.out" 2> "$work/$name.err" } +run_build_tool() { + name=$1 + shift + "$KIT" build --store "$store" --root "$ws" --def-name BUILD.kit "$@" \ + > "$work/$name.out" 2> "$work/$name.err" +} + build_assert_ok() { name=$1 shift @@ -730,6 +824,12 @@ tree_id_from() { awk 'NF >= 1 {print $1; exit}' "$1" } +target_record_for() { + target=$1 + find "$store/build/target" -type f -exec grep -l "$target" {} + 2>/dev/null | + awk 'NR == 1 {print; exit}' +} + cache_path_from() { id=$1 pp=$(printf '%.2s' "$id") @@ -812,6 +912,51 @@ run_fail "buildcoord-helper-outside-recipe-fails" \ contains "buildcoord-helper-outside-recipe-diag" \ "$work/buildcoord-helper-outside-recipe-fails.err" "unexpected argument" +if run_build_tool buildcoord-list-package list //listpkg/...; then + ok "buildcoord-list-package" +else + not_ok "buildcoord-list-package" "$work/buildcoord-list-package.err" +fi +contains "buildcoord-list-exact-target" \ + "$work/buildcoord-list-package.out" "//listpkg:exact" +contains "buildcoord-list-generated-target" \ + "$work/buildcoord-list-package.out" "//listpkg:foo.o" +contains "buildcoord-list-nested-package" \ + "$work/buildcoord-list-package.out" "//listpkg/subpkg:child" +not_contains "buildcoord-list-no-child-files-from-parent-rule" \ + "$work/buildcoord-list-package.out" "//listpkg:sub/bar.o" + +if run_build_tool buildcoord-list-scan-do list --scan-do //redo/...; then + ok "buildcoord-list-scan-do" +else + not_ok "buildcoord-list-scan-do" "$work/buildcoord-list-scan-do.err" +fi +contains "buildcoord-list-scan-do-default-provider" \ + "$work/buildcoord-list-scan-do.out" "default.o.do" + +run_fail "buildcoord-invalid-def-name-rejected" \ + "$KIT" build --store "$store" --root "$ws" --def-name ../BUILD.kit \ + //app:bundle +contains "buildcoord-invalid-def-name-diag" \ + "$work/buildcoord-invalid-def-name-rejected.err" "def" + +run_fail "buildcoord-invalid-label-rejected" \ + "$KIT" build --store "$store" --root "$ws" --def BUILD.kit //bad/../pkg:target +contains "buildcoord-invalid-label-diag" \ + "$work/buildcoord-invalid-label-rejected.err" "invalid" + +mkdir -p "$ws/badrecipe" +cat > "$ws/badrecipe/BUILD.kit" <<'EOF' +kit-build 1 +[target probe] +recipe ../recipes/argv.sh +EOF +run_fail "buildcoord-invalid-recipe-path-rejected" \ + "$KIT" build --store "$store" --root "$ws" --def BUILD.kit \ + --config "env.KIT=$KIT" //badrecipe:probe +contains "buildcoord-invalid-recipe-path-diag" \ + "$work/buildcoord-invalid-recipe-path-rejected.err" "recipe" + build_assert_ok buildcoord-workspace-default //wscfg:probe wscfg_default_path=$(tree_path_from "$work/buildcoord-workspace-default.out") contains "buildcoord-workspace-default-output" \ @@ -849,6 +994,81 @@ else kit_skip "buildcoord-external-targz" "host tar/gzip unavailable" fi +if run_build_tool buildcoord-repo-list repo list; then + ok "buildcoord-repo-list" +else + not_ok "buildcoord-repo-list" "$work/buildcoord-repo-list.err" +fi +contains "buildcoord-repo-list-ext" "$work/buildcoord-repo-list.out" "ext" +contains "buildcoord-repo-list-tree-format" \ + "$work/buildcoord-repo-list.out" "tree" + +if run_build_tool buildcoord-repo-fetch repo fetch ext; then + ok "buildcoord-repo-fetch" +else + not_ok "buildcoord-repo-fetch" "$work/buildcoord-repo-fetch.err" +fi +contains "buildcoord-repo-fetch-output" \ + "$work/buildcoord-repo-fetch.out" "ext" + +mkdir -p "$work/repoadd-src" +cat > "$work/repoadd-src/WORKSPACE.kit" <<'EOF' +kit-workspace 1 +name repoadd +version test +EOF +cat > "$work/repoadd-src/BUILD.kit" <<'EOF' +kit-build 1 +[target data] +recipe //build.sh +EOF +cat > "$work/repoadd-src/build.sh" <<'EOF' +#!/bin/sh +set -eu +mkdir -p "$KIT_BUILD_OUT" +printf 'repoadd\n' > "$KIT_BUILD_OUT/repoadd.txt" +EOF +chmod +x "$work/repoadd-src/build.sh" +if (cd "$work" && COPYFILE_DISABLE=1 tar --format ustar -czf \ + "$work/repoadd.tar.gz" repoadd-src) \ + > "$work/repoadd-pack.out" 2> "$work/repoadd-pack.err"; then + if run_build_tool buildcoord-repo-add repo add added \ + "file://$work/repoadd.tar.gz" --format tar.gz \ + --strip-prefix repoadd-src; then + ok "buildcoord-repo-add" + else + not_ok "buildcoord-repo-add" "$work/buildcoord-repo-add.err" + fi + contains "buildcoord-repo-add-workspace-entry" "$ws/WORKSPACE.kit" \ + "[external added]" +else + kit_skip "buildcoord-repo-add" "host tar unavailable" + kit_skip "buildcoord-repo-add-workspace-entry" "host tar unavailable" +fi + +if run_build_tool buildcoord-workspace-package workspace package \ + --format tar.gz -o "$work/workspace-package.tar.gz"; then + ok "buildcoord-workspace-package" +else + not_ok "buildcoord-workspace-package" \ + "$work/buildcoord-workspace-package.err" +fi +assert_file_exists "buildcoord-workspace-package-output" \ + "$work/workspace-package.tar.gz" +if tar -tzf "$work/workspace-package.tar.gz" \ + > "$work/workspace-package.list" 2> "$work/workspace-package-list.err"; then + ok "buildcoord-workspace-package-list" +else + not_ok "buildcoord-workspace-package-list" \ + "$work/workspace-package-list.err" +fi +contains "buildcoord-workspace-package-includes-workspace" \ + "$work/workspace-package.list" "WORKSPACE.kit" +contains "buildcoord-workspace-package-includes-build-def" \ + "$work/workspace-package.list" "BUILD.kit" +not_contains "buildcoord-workspace-package-excludes-store" \ + "$work/workspace-package.list" "/build/cache/" + build_assert_ok buildcoord-absent-cold //absent:probe absent_path=$(tree_path_from "$work/buildcoord-absent-cold.out") contains "buildcoord-absent-cold-output" "$absent_path/optional.txt" "absent" @@ -1077,6 +1297,9 @@ contains "buildcoord-config-override-change-deep" \ contains "buildcoord-config-override-change-parent-deep" \ "$work/buildcoord-config-override-change.err" \ "trace deep-hit target=//scope:parent-override" +contains "buildcoord-config-override-change-shielded-trace" \ + "$work/buildcoord-config-override-change.err" \ + "deep-config target=//scope:parent-override child=//scope:leaf key=mode scope=overlay-shielded" not_contains "buildcoord-config-override-change-parent-not-shallow" \ "$work/buildcoord-config-override-change.err" \ "trace shallow-hit target=//scope:parent-override" @@ -1133,6 +1356,15 @@ contains "buildcoord-config-shadow-parent-change-parent" \ contains "buildcoord-config-shadow-parent-change-leaf" \ "$shadow_parent2_path/leaf.txt" "leaf:child" +build_assert_ok buildcoord-config-shadow-parent-change-trace --trace --stats \ + --config mode=parent3 //shadow:parent +contains "buildcoord-config-shadow-parent-root-mismatch-trace" \ + "$work/buildcoord-config-shadow-parent-change-trace.err" \ + "deep-config target=//shadow:parent key=mode scope=root result=mismatch" +contains "buildcoord-config-shadow-parent-child-shielded-trace" \ + "$work/buildcoord-config-shadow-parent-change-trace.err" \ + "deep-config target=//shadow:parent child=//shadow:leaf key=mode scope=overlay-shielded" + build_assert_ok buildcoord-stats-argv-cold --stats //argv:echo -- stat-one contains "buildcoord-stats-argv-cold-run" "$work/buildcoord-stats-argv-cold.err" \ "recipes_run=1" @@ -1243,6 +1475,43 @@ fi contains "buildcoord-need-await-format-output" "$await_fmt_path/args.txt" \ "format-arg" +build_assert_ok buildcoord-exports-provider --stats //exports:provider +exports_provider_path=$(tree_path_from "$work/buildcoord-exports-provider.out") +contains "buildcoord-exports-file-created" \ + "$exports_provider_path/.kit/exports" "kit-build-exports 1" +contains "buildcoord-exports-cflags-stored" \ + "$exports_provider_path/.kit/exports" "DPROVIDER=1" + +build_assert_ok buildcoord-exports-consumer --stats //exports:consumer +exports_consumer_path=$(tree_path_from "$work/buildcoord-exports-consumer.out") +contains "buildcoord-export-get-cflags" \ + "$exports_consumer_path/cflags.txt" "Iinclude" +contains "buildcoord-export-get-libs" \ + "$exports_consumer_path/libs.txt" "lprovider" +contains "buildcoord-export-collect-provider-first" \ + "$exports_consumer_path/collect.txt" "Iinclude" +contains "buildcoord-export-collect-other-second" \ + "$exports_consumer_path/collect.txt" "Iother" + +cat > "$ws/recipes/exports_provider.sh" <<'EOF' +#!/bin/sh +set -eu +mkdir -p "$KIT_BUILD_OUT" +"$KIT" build export-set cflags -- -Iinclude -DPROVIDER=2 +"$KIT" build export-set libs -- -Llib -lprovider +printf 'provider-v2\n' > "$KIT_BUILD_OUT/provider.txt" +EOF +chmod +x "$ws/recipes/exports_provider.sh" +build_assert_ok buildcoord-exports-provider-change-invalidates-consumer \ + --stats //exports:consumer +contains "buildcoord-exports-consumer-reruns-on-provider-tree-change" \ + "$work/buildcoord-exports-provider-change-invalidates-consumer.err" \ + "recipes_run=" +exports_consumer_v2_path=$(tree_path_from \ + "$work/buildcoord-exports-provider-change-invalidates-consumer.out") +contains "buildcoord-export-get-cflags-updated" \ + "$exports_consumer_v2_path/cflags.txt" "DPROVIDER=2" + build_assert_ok buildcoord-defn-v1 --stats //defn:probe contains "buildcoord-defn-v1-run" "$work/buildcoord-defn-v1.err" \ "recipes_run=1" @@ -1297,6 +1566,35 @@ contains "buildcoord-material-missing-bytes-reruns-run" \ contains "buildcoord-material-missing-bytes-reruns-miss" \ "$work/buildcoord-material-missing-bytes-reruns.err" "materialize_misses=" +build_assert_ok buildcoord-storage-record-cold --stats //storage:probe +contains "buildcoord-storage-record-cold-run" \ + "$work/buildcoord-storage-record-cold.err" "recipes_run=1" +storage_record=$(target_record_for "//storage:probe") +if [ -n "$storage_record" ] && [ -f "$storage_record" ]; then + ok "buildcoord-storage-record-found" + printf 'not a kit-build-record\n' > "$storage_record" +else + printf 'record=%s\n' "$storage_record" > "$work/storage-record.diag" + not_ok "buildcoord-storage-record-found" "$work/storage-record.diag" +fi +build_assert_ok buildcoord-storage-corrupt-record-reruns --stats //storage:probe +contains "buildcoord-storage-corrupt-record-reruns-run" \ + "$work/buildcoord-storage-corrupt-record-reruns.err" "recipes_run=1" + +find "$store/build/trace" -type f -exec grep -l "target //storage:probe" {} + \ + > "$work/storage-traces.txt" 2> "$work/storage-traces.err" +if [ -s "$work/storage-traces.txt" ]; then + ok "buildcoord-storage-traces-found" + while IFS= read -r trace_file; do + printf 'not a kit-build-trace\n' > "$trace_file" + done < "$work/storage-traces.txt" +else + not_ok "buildcoord-storage-traces-found" "$work/storage-traces.err" +fi +build_assert_ok buildcoord-storage-malformed-trace-reruns --stats //storage:probe +contains "buildcoord-storage-malformed-trace-reruns-run" \ + "$work/buildcoord-storage-malformed-trace-reruns.err" "recipes_run=1" + build_assert_ok buildcoord-globset-cold --stats //globset:probe contains "buildcoord-globset-cold-run" "$work/buildcoord-globset-cold.err" \ "recipes_run=1" @@ -1480,6 +1778,40 @@ run_fail "buildcoord-cycle-indirect-fails" \ contains "buildcoord-cycle-indirect-diag" \ "$work/buildcoord-cycle-indirect-fails.err" "cycle" +build_assert_ok buildcoord-async-unawaited-cold --stats //async:unawaited +contains "buildcoord-async-unawaited-cold-stats" \ + "$work/buildcoord-async-unawaited-cold.err" "recipes_run=" +async_unawaited_path=$(tree_path_from "$work/buildcoord-async-unawaited-cold.out") +contains "buildcoord-async-unawaited-output" \ + "$async_unawaited_path/unawaited.txt" "unawaited" +find "$store/build/trace" -type f -exec grep -l "target //async:unawaited" {} + \ + > "$work/async-unawaited-traces.txt" 2> "$work/async-unawaited-traces.err" +if [ -s "$work/async-unawaited-traces.txt" ]; then + xargs grep -h "//async:dep" < "$work/async-unawaited-traces.txt" \ + > "$work/async-unawaited-deps.txt" 2> "$work/async-unawaited-deps.err" || true + if [ ! -s "$work/async-unawaited-deps.txt" ]; then + ok "buildcoord-async-unawaited-not-recorded" + else + not_ok "buildcoord-async-unawaited-not-recorded" \ + "$work/async-unawaited-deps.txt" + fi +else + not_ok "buildcoord-async-unawaited-traces-found" \ + "$work/async-unawaited-traces.err" +fi +cat > "$ws/recipes/async_dep.sh" <<'EOF' +#!/bin/sh +set -eu +mkdir -p "$KIT_BUILD_OUT" +printf 'async-dep:edited\n' > "$KIT_BUILD_OUT/dep.txt" +EOF +chmod +x "$ws/recipes/async_dep.sh" +build_assert_ok buildcoord-async-unawaited-dep-edit --stats //async:unawaited +contains "buildcoord-async-unawaited-dep-edit-hit" \ + "$work/buildcoord-async-unawaited-dep-edit.err" "deep_hits=1" +contains "buildcoord-async-unawaited-dep-edit-no-run" \ + "$work/buildcoord-async-unawaited-dep-edit.err" "recipes_run=0" + export AMBIENT=leaked build_assert_ok buildcoord-env-cold --stats --config env.DECLARED=one \ //env:probe @@ -1490,8 +1822,21 @@ contains "buildcoord-env-clean-ambient" "$env_one_path/env.txt" \ contains "buildcoord-env-declared-visible" "$env_one_path/env.txt" \ "declared:one" -# Spec checks below are intentionally red until these BUILD.md promises are -# implemented. +unset MISSING_BUILDCOORD_ENV +run_fail "buildcoord-env-shorthand-unset-rejected" \ + "$KIT" build --store "$store" --root "$ws" --def BUILD.kit \ + --env MISSING_BUILDCOORD_ENV --config "env.KIT=$KIT" //env:probe +contains "buildcoord-env-shorthand-unset-rejected-diag" \ + "$work/buildcoord-env-shorthand-unset-rejected.err" "MISSING_BUILDCOORD_ENV" + +run_fail "buildcoord-env-config-unset-rejected" \ + "$KIT" build --store "$store" --root "$ws" --def BUILD.kit \ + --config env.MISSING_BUILDCOORD_ENV --config "env.KIT=$KIT" //env:probe +contains "buildcoord-env-config-unset-rejected-diag" \ + "$work/buildcoord-env-config-unset-rejected.err" "MISSING_BUILDCOORD_ENV" + +# Spec checks below are intentionally red until these BUILD_COORDINATOR.md +# promises are implemented. build_assert_ok buildcoord-spec-env-change-invalidates --stats \ --config env.DECLARED=two //env:probe contains "buildcoord-spec-env-change-reruns" \