kit

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

commit c04f2488cc1875033b4abeab995d2bdb10b40cc0
parent 82ef995c9c7a61e8fed52f30e3eb019bd08bf95d
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu, 18 Jun 2026 02:52:18 -0700

build: replay need overlays during shallow hits

Diffstat:
Msrc/build/bundle.c | 8+++++++-
Msrc/build/bundle.h | 8++++----
Msrc/build/cfg.c | 14++++++++++++++
Msrc/build/cfg.h | 5+++++
Msrc/build/resolve.c | 8++++++--
Msrc/build/runner.c | 38++++++++++++++++++++++++++++----------
Msrc/build/runner.h | 1+
Msrc/build/trace.c | 21++++++++++++++-------
Msrc/build/trace.h | 11+++++++----
Mtest/build/build_pure_test.c | 11+++++++----
Mtest/buildcoord/run.sh | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
11 files changed, 190 insertions(+), 32 deletions(-)

diff --git a/src/build/bundle.c b/src/build/bundle.c @@ -655,7 +655,9 @@ static int BUILD_MAYBE_UNUSED export_trace_refs(KitBuildCoordinator* c, payload_write_blob_from_cas(c, t.argv, root, seen) != BUILD_OK) goto shallow_out; for (i = 0u; i < t.n_deps; ++i) { - if (payload_write_blob_from_cas(c, t.deps[i].config_id, root, seen) != + if (payload_write_blob_from_cas(c, t.deps[i].overlay_id, root, seen) != + BUILD_OK || + payload_write_blob_from_cas(c, t.deps[i].config_id, root, seen) != BUILD_OK || payload_write_blob_from_cas(c, t.deps[i].argv_id, root, seen) != BUILD_OK) @@ -726,6 +728,10 @@ static int BUILD_MAYBE_UNUSED import_trace_refs(KitBuildCoordinator* c, goto shallow_out; release_file(c, &fd); for (i = 0u; i < t.n_deps; ++i) { + if (payload_install_blob(c, t.deps[i].overlay_id, root, seen, NULL, NULL, + &fd) != BUILD_OK) + goto shallow_out; + release_file(c, &fd); if (payload_install_blob(c, t.deps[i].config_id, root, seen, NULL, NULL, &fd) != BUILD_OK) goto shallow_out; diff --git a/src/build/bundle.h b/src/build/bundle.h @@ -18,10 +18,10 @@ * <kit/package.h> manifest + minisign + trust machinery wholesale: a signed * `kit-build-traces 1` manifest lists (target, kind, trace-id, output-tree-id) * claims, carried in a `.kpkg` alongside the trace bodies and the CAS blobs they - * reference — the serialized config-map / argv blobs (required, else an imported - * shallow trace cannot replay its `need`s) AND the deepset closure blobs a deep - * trace points at (required, else an imported deep trace cannot refresh) — plus - * optionally the referenced output trees/blobs. + * reference — the serialized config-map / argv blobs and need-overlay blobs + * (required, else an imported shallow trace cannot replay its `need`s) AND the + * deepset closure blobs a deep trace points at (required, else an imported deep + * trace cannot refresh) — plus optionally the referenced output trees/blobs. * * The security split is precise: a local build can deep/shallow-HIT on a remote * builder's trace (obtaining the output without running the recipe) while diff --git a/src/build/cfg.c b/src/build/cfg.c @@ -170,6 +170,20 @@ int build_config_overlay(const BuildConfig* base, const KitBuildKV* overrides, return BUILD_OK; } +int build_config_overlay_map(const BuildConfig* base, + const BuildConfig* overrides, BuildConfig* out) { + size_t i; + if (!overrides || (overrides->n && !overrides->entries)) return BUILD_ERR; + if (build_config_overlay(base, NULL, 0, out) != BUILD_OK) return BUILD_ERR; + for (i = 0; i < overrides->n; ++i) { + if (build_config_set(out, kit_slice_cstr(overrides->entries[i].key), + kit_slice_cstr(overrides->entries[i].value)) != + BUILD_OK) + return BUILD_ERR; + } + return BUILD_OK; +} + static int config_validate(const BuildConfig* cfg) { size_t i; if (!cfg || (cfg->n && !cfg->entries)) return BUILD_ERR; diff --git a/src/build/cfg.h b/src/build/cfg.h @@ -73,6 +73,11 @@ int build_config_get(const BuildConfig*, KitSlice key, KitSlice* value, int build_config_overlay(const BuildConfig* base, const KitBuildKV* overrides, size_t noverrides, BuildConfig* out); +/* Produce `out` = `base` with every entry in `overrides` applied. This is the + * replay form for a recorded need overlay recovered from its config-map blob. */ +int build_config_overlay_map(const BuildConfig* base, + const BuildConfig* overrides, BuildConfig* out); + /* Emit the canonical, byte-stable serialized form (BUILD_CONFIG_MAGIC version * line, then sorted `key value` rows). Sorting/dedup happens here. */ int build_config_emit(const BuildConfig*, KitWriter* out); diff --git a/src/build/resolve.c b/src/build/resolve.c @@ -105,16 +105,20 @@ static int try_shallow_trace(KitBuildCoordinator* c, KitSlice target, return BUILD_ERR; for (i = 0; i < st->n_deps; ++i) { BuildConfigEntry dep_cfg_entries[128]; + BuildConfigEntry overlay_entries[64]; char dep_argv_entries[64][BUILD_VAL_MAX]; - BuildConfig dep_cfg; + BuildConfig dep_cfg, overlay_cfg; BuildArgv dep_argv; BuildResolved dep_r; build_config_init(&dep_cfg, dep_cfg_entries, sizeof dep_cfg_entries / sizeof dep_cfg_entries[0]); + build_config_init(&overlay_cfg, overlay_entries, + sizeof overlay_entries / sizeof overlay_entries[0]); build_argv_init(&dep_argv, dep_argv_entries, sizeof dep_argv_entries / sizeof dep_argv_entries[0]); - if (build_coord_config_by_id(c, st->deps[i].config_id, &dep_cfg) != + if (build_coord_config_by_id(c, st->deps[i].overlay_id, &overlay_cfg) != BUILD_OK || + build_config_overlay_map(cfg, &overlay_cfg, &dep_cfg) != BUILD_OK || build_coord_argv_by_id(c, st->deps[i].argv_id, &dep_argv) != BUILD_OK || build_resolve(c, kit_slice_cstr(st->deps[i].name), &dep_cfg, &dep_argv, diff --git a/src/build/runner.c b/src/build/runner.c @@ -71,12 +71,13 @@ static int append_glob(BuildDepLog* log, KitSlice pattern, } static int append_dep(BuildDepLog* log, KitSlice target, + const uint8_t overlay_id[BUILD_HASH_LEN], const uint8_t config_id[BUILD_HASH_LEN], const uint8_t argv_id[BUILD_HASH_LEN], const BuildResolved* r) { BuildDepEdge* row; if (!log || !target.s || target.len == 0u || target.len >= BUILD_TARGET_MAX || - !config_id || !argv_id || !r || !r->leafset) + !overlay_id || !config_id || !argv_id || !r || !r->leafset) return BUILD_ERR; if (log->n_deps >= log->cap_deps || log->n_children >= log->cap_children) @@ -84,6 +85,7 @@ static int append_dep(BuildDepLog* log, KitSlice target, row = &log->deps[log->n_deps++]; memcpy(row->name, target.s, target.len); row->name[target.len] = '\0'; + memcpy(row->overlay_id, overlay_id, BUILD_HASH_LEN); memcpy(row->config_id, config_id, BUILD_HASH_LEN); memcpy(row->argv_id, argv_id, BUILD_HASH_LEN); memcpy(row->output_tree, r->output_tree, BUILD_HASH_LEN); @@ -412,24 +414,32 @@ int build_runner_service(KitBuildCoordinator* c, KitBuildConn* conn, if (write_resp(c, conn, &req, &resp) != BUILD_OK) return BUILD_ERR; } } else if (req.cmd == BUILD_CMD_NEED) { - BuildConfigEntry cfg_entries[128]; + BuildConfigEntry cfg_entries[128], overlay_entries[64]; char argv_entries[64][BUILD_VAL_MAX]; - BuildConfig dep_cfg; + BuildConfig dep_cfg, overlay_cfg, empty_cfg; BuildArgv dep_argv; BuildResolved r; - uint8_t cfg_id[BUILD_HASH_LEN], argv_id[BUILD_HASH_LEN]; + uint8_t overlay_id[BUILD_HASH_LEN], cfg_id[BUILD_HASH_LEN], + argv_id[BUILD_HASH_LEN]; build_config_init(&dep_cfg, cfg_entries, sizeof cfg_entries / sizeof cfg_entries[0]); + build_config_init(&overlay_cfg, overlay_entries, + sizeof overlay_entries / sizeof overlay_entries[0]); + build_config_init(&empty_cfg, NULL, 0); build_argv_init(&dep_argv, argv_entries, sizeof argv_entries / sizeof argv_entries[0]); if (build_config_overlay(cfg, req.overrides, req.noverrides, &dep_cfg) != BUILD_OK || + build_config_overlay(&empty_cfg, req.overrides, req.noverrides, + &overlay_cfg) != BUILD_OK || + store_config_blob(c, &overlay_cfg, overlay_id) != BUILD_OK || build_argv_set(&dep_argv, req.argv, req.argc) != BUILD_OK || build_config_id(c->ctx->heap, &dep_cfg, cfg_id) != BUILD_OK || build_argv_id(c->ctx->heap, &dep_argv, argv_id) != BUILD_OK || build_resolve(c, req.arg, &dep_cfg, &dep_argv, chain, &r) != BUILD_OK || - append_dep(log, req.arg, cfg_id, argv_id, &r) != BUILD_OK) { + append_dep(log, req.arg, overlay_id, cfg_id, argv_id, &r) != + BUILD_OK) { resp_error(&resp, KIT_ERR, "need failed"); } else { memcpy(resp.id, r.output_tree, BUILD_HASH_LEN); @@ -437,21 +447,28 @@ int build_runner_service(KitBuildCoordinator* c, KitBuildConn* conn, } if (write_resp(c, conn, &req, &resp) != BUILD_OK) return BUILD_ERR; } else if (req.cmd == BUILD_CMD_NEED_SUBMIT) { - BuildConfigEntry cfg_entries[128]; + BuildConfigEntry cfg_entries[128], overlay_entries[64]; char argv_entries[64][BUILD_VAL_MAX]; - BuildConfig dep_cfg; + BuildConfig dep_cfg, overlay_cfg, empty_cfg; BuildArgv dep_argv; BuildTargetFuture* f = NULL; BuildPendingNeed* p; - uint8_t cfg_id[BUILD_HASH_LEN], argv_id[BUILD_HASH_LEN]; + uint8_t overlay_id[BUILD_HASH_LEN], cfg_id[BUILD_HASH_LEN], + argv_id[BUILD_HASH_LEN]; char err[128]; build_config_init(&dep_cfg, cfg_entries, sizeof cfg_entries / sizeof cfg_entries[0]); + build_config_init(&overlay_cfg, overlay_entries, + sizeof overlay_entries / sizeof overlay_entries[0]); + build_config_init(&empty_cfg, NULL, 0); build_argv_init(&dep_argv, argv_entries, sizeof argv_entries / sizeof argv_entries[0]); if (log->n_pending >= log->cap_pending || build_config_overlay(cfg, req.overrides, req.noverrides, &dep_cfg) != BUILD_OK || + build_config_overlay(&empty_cfg, req.overrides, req.noverrides, + &overlay_cfg) != BUILD_OK || + store_config_blob(c, &overlay_cfg, overlay_id) != BUILD_OK || build_argv_set(&dep_argv, req.argv, req.argc) != BUILD_OK || build_config_id(c->ctx->heap, &dep_cfg, cfg_id) != BUILD_OK || build_argv_id(c->ctx->heap, &dep_argv, argv_id) != BUILD_OK || @@ -463,6 +480,7 @@ int build_runner_service(KitBuildCoordinator* c, KitBuildConn* conn, memset(p, 0, sizeof *p); p->token = log->next_token++; target_copy(req.arg, p->dep); + memcpy(p->overlay_id, overlay_id, BUILD_HASH_LEN); memcpy(p->config_id, cfg_id, BUILD_HASH_LEN); memcpy(p->argv_id, argv_id, BUILD_HASH_LEN); p->future = f; @@ -473,8 +491,8 @@ int build_runner_service(KitBuildCoordinator* c, KitBuildConn* conn, BuildPendingNeed* p = find_pending(log, req.token); BuildResolved r; if (!p || build_coord_target_await(c, p->future, &r) != BUILD_OK || - append_dep(log, kit_slice_cstr(p->dep), p->config_id, p->argv_id, - &r) != BUILD_OK) { + append_dep(log, kit_slice_cstr(p->dep), p->overlay_id, p->config_id, + p->argv_id, &r) != BUILD_OK) { resp_error(&resp, KIT_ERR, "need-await failed"); } else { memcpy(resp.id, r.output_tree, BUILD_HASH_LEN); diff --git a/src/build/runner.h b/src/build/runner.h @@ -31,6 +31,7 @@ typedef struct BuildPendingNeed { uint64_t token; char dep[BUILD_TARGET_MAX]; + uint8_t overlay_id[BUILD_HASH_LEN]; uint8_t config_id[BUILD_HASH_LEN]; uint8_t argv_id[BUILD_HASH_LEN]; BuildTargetFuture* future; /* from build_coord_dispatch */ diff --git a/src/build/trace.c b/src/build/trace.c @@ -112,6 +112,8 @@ static int glob_cmp(const BuildGlobLeaf* a, const BuildGlobLeaf* b) { static int dep_cmp(const BuildDepEdge* a, const BuildDepEdge* b) { int c = strcmp(a->name, b->name); if (c != 0) return c; + c = id_cmp(a->overlay_id, b->overlay_id); + if (c != 0) return c; c = id_cmp(a->config_id, b->config_id); if (c != 0) return c; return id_cmp(a->argv_id, b->argv_id); @@ -224,12 +226,16 @@ static int emit_sorted_globs(const BuildGlobLeaf* rows, size_t n, KitWriter* out } static int emit_dep_row(KitWriter* out, const BuildDepEdge* row) { - char cfg[BUILD_HEX_LEN], argv[BUILD_HEX_LEN], tree[BUILD_HEX_LEN]; + char ovr[BUILD_HEX_LEN], cfg[BUILD_HEX_LEN], argv[BUILD_HEX_LEN], + tree[BUILD_HEX_LEN]; + hex_encode(ovr, row->overlay_id); hex_encode(cfg, row->config_id); hex_encode(argv, row->argv_id); hex_encode(tree, row->output_tree); if (emit_bytes(out, row->name) != BUILD_OK) return BUILD_ERR; if (emit_bytes(out, " ") != BUILD_OK) return BUILD_ERR; + if (emit_bytes(out, ovr) != BUILD_OK) return BUILD_ERR; + if (emit_bytes(out, " ") != BUILD_OK) return BUILD_ERR; if (emit_bytes(out, cfg) != BUILD_OK) return BUILD_ERR; if (emit_bytes(out, " ") != BUILD_OK) return BUILD_ERR; if (emit_bytes(out, argv) != BUILD_OK) return BUILD_ERR; @@ -423,7 +429,7 @@ typedef struct TraceParser { size_t len; size_t pos; int first; - char line[BUILD_PATH_MAX + 4u * BUILD_HEX_LEN + BUILD_TARGET_MAX + 16u]; + char line[BUILD_PATH_MAX + 5u * BUILD_HEX_LEN + BUILD_TARGET_MAX + 16u]; } TraceParser; static int parser_next(TraceParser* p, char** line, char* err, size_t errcap) { @@ -602,15 +608,16 @@ static int append_deepset_glob(BuildDeepSet* out, char** fields, size_t n, static int append_dep(BuildShallowTrace* out, char** fields, size_t n, char* err, size_t errcap) { BuildDepEdge* row; - if (expect_field_count(n, 4u, err, errcap) != BUILD_OK) return BUILD_ERR; + if (expect_field_count(n, 5u, err, errcap) != BUILD_OK) return BUILD_ERR; if (out->n_deps >= out->cap_deps) return set_err(err, errcap, "too many dep rows"); if (!valid_target(fields[0])) return set_err(err, errcap, "bad dep target"); row = &out->deps[out->n_deps]; snprintf(row->name, sizeof row->name, "%s", fields[0]); - if (hex_decode_strict(fields[1], row->config_id) != BUILD_OK || - hex_decode_strict(fields[2], row->argv_id) != BUILD_OK || - hex_decode_strict(fields[3], row->output_tree) != BUILD_OK) + if (hex_decode_strict(fields[1], row->overlay_id) != BUILD_OK || + hex_decode_strict(fields[2], row->config_id) != BUILD_OK || + hex_decode_strict(fields[3], row->argv_id) != BUILD_OK || + hex_decode_strict(fields[4], row->output_tree) != BUILD_OK) return set_err(err, errcap, "bad dep hash"); if (out->n_deps && dep_cmp(&out->deps[out->n_deps - 1u], row) >= 0) return set_err(err, errcap, "non-canonical dep rows"); @@ -687,7 +694,7 @@ int build_shallow_parse(const uint8_t* data, size_t len, BuildShallowTrace* out, p.first = 1; for (;;) { char* line; - char* fields[4]; + char* fields[5]; size_t n = 0; int r = parser_next(&p, &line, err, errcap); if (r < 0) return BUILD_ERR; diff --git a/src/build/trace.h b/src/build/trace.h @@ -42,12 +42,15 @@ typedef struct BuildGlobLeaf { uint8_t result_hash[BUILD_HASH_LEN]; } BuildGlobLeaf; -/* One target-dep edge (shallow only): replays one `need`. Carries the dep's - * config-id AND argv-id (so it re-resolves under the exact (config, argv) it - * used — the full resolution identity) and the output tree-id it produced. - * Sorted by (name, config-id, argv-id). */ +/* One target-dep edge (shallow only): replays one `need`. Carries the explicit + * need overlay-id, the dep's effective config-id and argv-id, and the output + * tree-id it produced. Shallow replay recomputes the dep config as the current + * parent config overlaid by overlay-id; config-id is retained for sharing, + * exact trace description, and bundle closure. Sorted by + * (name, overlay-id, config-id, argv-id). */ typedef struct BuildDepEdge { char name[BUILD_TARGET_MAX]; + uint8_t overlay_id[BUILD_HASH_LEN]; uint8_t config_id[BUILD_HASH_LEN]; uint8_t argv_id[BUILD_HASH_LEN]; uint8_t output_tree[BUILD_HASH_LEN]; diff --git a/test/build/build_pure_test.c b/test/build/build_pure_test.c @@ -210,9 +210,10 @@ static void test_trace(void) { st.n_deps = 1; st.cap_deps = 1; strcpy(deps[0].name, "//lib:core"); - fill_id(deps[0].config_id, 6); - fill_id(deps[0].argv_id, 7); - fill_id(deps[0].output_tree, 8); + fill_id(deps[0].overlay_id, 6); + fill_id(deps[0].config_id, 7); + fill_id(deps[0].argv_id, 8); + fill_id(deps[0].output_tree, 9); EXPECT(kit_writer_mem(&g_u.heap, &w) == KIT_OK && w, "trace writer"); EXPECT(build_shallow_emit(&st, w, err, sizeof err) == BUILD_OK, @@ -234,7 +235,9 @@ static void test_trace(void) { "parse shallow"); EXPECT(strcmp(parsed.config_keys[0].name, "a") == 0 && strcmp(parsed.sources[0].path, "src/a.c") == 0 && - parsed.sources[0].absent, + parsed.sources[0].absent && + memcmp(parsed.deps[0].overlay_id, deps[0].overlay_id, + BUILD_HASH_LEN) == 0, "shallow canonical sections"); EXPECT(build_shallow_parse((const uint8_t*)dup_source, sizeof dup_source - 1u, &parsed, err, sizeof err) == BUILD_ERR, diff --git a/test/buildcoord/run.sh b/test/buildcoord/run.sh @@ -40,6 +40,14 @@ recipe recipes/lib.sh recipe recipes/needargv.sh [target //recipe:stamp] recipe recipes/recipe.sh +[target //scope:leaf] +recipe recipes/scope_leaf.sh +[target //scope:parent-inherit] +recipe recipes/scope_parent_inherit.sh +[target //scope:parent-override] +recipe recipes/scope_parent_override.sh +[target //scope:plain] +recipe recipes/scope_plain.sh [target //stable:dep] recipe recipes/stable_dep.sh [target //stable:parent] @@ -118,6 +126,37 @@ mkdir -p "$KIT_BUILD_OUT" printf 'recipe:v1\n' > "$KIT_BUILD_OUT/stamp.txt" EOF +cat > "$ws/recipes/scope_leaf.sh" <<'EOF' +#!/bin/sh +set -eu +mkdir -p "$KIT_BUILD_OUT" +mode=$("$KIT" build config-get mode || true) +printf 'mode:%s\n' "$mode" > "$KIT_BUILD_OUT/mode.txt" +EOF + +cat > "$ws/recipes/scope_parent_inherit.sh" <<'EOF' +#!/bin/sh +set -eu +mkdir -p "$KIT_BUILD_OUT" +dep_dir=$("$KIT" build need //scope:leaf) +cat "$dep_dir/mode.txt" > "$KIT_BUILD_OUT/mode.txt" +EOF + +cat > "$ws/recipes/scope_parent_override.sh" <<'EOF' +#!/bin/sh +set -eu +mkdir -p "$KIT_BUILD_OUT" +dep_dir=$("$KIT" build need --config mode=forced //scope:leaf) +cat "$dep_dir/mode.txt" > "$KIT_BUILD_OUT/mode.txt" +EOF + +cat > "$ws/recipes/scope_plain.sh" <<'EOF' +#!/bin/sh +set -eu +mkdir -p "$KIT_BUILD_OUT" +printf 'plain\n' > "$KIT_BUILD_OUT/plain.txt" +EOF + cat > "$ws/recipes/stable_dep.sh" <<'EOF' #!/bin/sh set -eu @@ -324,6 +363,64 @@ cfg_change_path=$(tree_path_from "$work/buildcoord-stats-cfg-change.out") contains "buildcoord-stats-cfg-change-output" "$cfg_change_path/mode.txt" \ "mode:release" +build_assert_ok buildcoord-config-unread-cold --stats --config flavor=one \ + //scope:plain +contains "buildcoord-config-unread-cold-run" \ + "$work/buildcoord-config-unread-cold.err" "recipes_run=1" +build_assert_ok buildcoord-config-unread-change --stats --config flavor=two \ + //scope:plain +contains "buildcoord-config-unread-change-shallow" \ + "$work/buildcoord-config-unread-change.err" "shallow_hits=1" +contains "buildcoord-config-unread-change-no-run" \ + "$work/buildcoord-config-unread-change.err" "recipes_run=0" +plain_change_path=$(tree_path_from "$work/buildcoord-config-unread-change.out") +contains "buildcoord-config-unread-change-output" "$plain_change_path/plain.txt" \ + "plain" + +build_assert_ok buildcoord-config-inherit-cold --stats --config mode=debug \ + //scope:parent-inherit +inherit_debug_path=$(tree_path_from "$work/buildcoord-config-inherit-cold.out") +contains "buildcoord-config-inherit-debug-output" "$inherit_debug_path/mode.txt" \ + "mode:debug" +build_assert_ok buildcoord-config-inherit-change --stats --config mode=release \ + //scope:parent-inherit +contains "buildcoord-config-inherit-change-runs" \ + "$work/buildcoord-config-inherit-change.err" "recipes_run=2" +inherit_release_path=$(tree_path_from "$work/buildcoord-config-inherit-change.out") +contains "buildcoord-config-inherit-release-output" \ + "$inherit_release_path/mode.txt" "mode:release" +if [ "$inherit_release_path" != "$inherit_debug_path" ]; then + ok "buildcoord-config-inherit-new-tree" +else + printf 'unchanged tree path=%s\n' "$inherit_release_path" \ + > "$work/config-inherit-tree.diag" + not_ok "buildcoord-config-inherit-new-tree" \ + "$work/config-inherit-tree.diag" +fi + +build_assert_ok buildcoord-config-override-cold --stats --config mode=debug \ + //scope:parent-override +override_debug_path=$(tree_path_from "$work/buildcoord-config-override-cold.out") +contains "buildcoord-config-override-debug-output" \ + "$override_debug_path/mode.txt" "mode:forced" +build_assert_ok buildcoord-config-override-change --stats --config mode=release \ + //scope:parent-override +contains "buildcoord-config-override-change-shallow" \ + "$work/buildcoord-config-override-change.err" "shallow_hits=1" +contains "buildcoord-config-override-change-no-run" \ + "$work/buildcoord-config-override-change.err" "recipes_run=0" +override_release_path=$(tree_path_from "$work/buildcoord-config-override-change.out") +contains "buildcoord-config-override-release-output" \ + "$override_release_path/mode.txt" "mode:forced" +if [ "$override_release_path" = "$override_debug_path" ]; then + ok "buildcoord-config-override-reuses-tree" +else + printf 'debug=%s\nrelease=%s\n' "$override_debug_path" \ + "$override_release_path" > "$work/config-override-tree.diag" + not_ok "buildcoord-config-override-reuses-tree" \ + "$work/config-override-tree.diag" +fi + 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"