commit 977cc8c48aacd19bd104c05e46851cd5724acce5
parent 68162c4ade84c734b1bbaecc249b14ce28cc95ab
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Tue, 23 Jun 2026 07:48:22 -0700
Add reusable C build rules
Diffstat:
19 files changed, 636 insertions(+), 178 deletions(-)
diff --git a/BUILD.kit b/BUILD.kit
@@ -1,7 +1,33 @@
-kit-build 1
+kit-build 2
+
+[default c.executable]
+recipe //do/default.exe.do
+
+[default c.object]
+recipe //do/default.o.do
+
+[default c.objects]
+recipe //do/default.objs.do
+
+[default c.static-library]
+recipe //do/default.a.do
+
+[rule c-object]
+match *.o
+type c.object
+
+[rule c-objects]
+match *.objs
+type c.objects
+
[target driver]
-recipe recipes/buildkit/driver.sh
+type c.objects
[target kit]
-recipe recipes/buildkit/kit.sh
+type c.executable
[target libkit]
-recipe recipes/buildkit/libkit.sh
+type c.static-library
+
+[redo-defaults]
+enabled true
+search do
+walk-parents true
diff --git a/do/c.sh b/do/c.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+set -eu
+
+: "${KIT:=./build/kit}"
+
+common=$("$KIT" build source recipes/buildkit/common.sh)
+. "$common"
diff --git a/do/default.a.do b/do/default.a.do
@@ -0,0 +1,6 @@
+#!/bin/sh
+set -eu
+
+: "${KIT:=./build/kit}"
+rule=$("$KIT" build source rules/c/default.a.do)
+. "$rule"
diff --git a/do/default.exe.do b/do/default.exe.do
@@ -0,0 +1,6 @@
+#!/bin/sh
+set -eu
+
+: "${KIT:=./build/kit}"
+rule=$("$KIT" build source rules/c/default.exe.do)
+. "$rule"
diff --git a/do/default.o.do b/do/default.o.do
@@ -0,0 +1,6 @@
+#!/bin/sh
+set -eu
+
+: "${KIT:=./build/kit}"
+rule=$("$KIT" build source rules/c/default.o.do)
+. "$rule"
diff --git a/do/default.objs.do b/do/default.objs.do
@@ -0,0 +1,6 @@
+#!/bin/sh
+set -eu
+
+: "${KIT:=./build/kit}"
+rule=$("$KIT" build source rules/c/default.objs.do)
+. "$rule"
diff --git a/doc/BUILD_COORDINATOR.md b/doc/BUILD_COORDINATOR.md
@@ -288,6 +288,15 @@ tracked inputs (`source`, `glob`, `config-get`, `need`, or a future equivalent);
the resolver itself remains a client-side organization layer, not hidden cache
identity.
+Reusable language defaults should therefore be ordinary recipe libraries, not
+implicit catalog semantics. For example, a generic C `.do` recipe can implement
+the mechanics of compiling, archiving, linking, depfile logging, and profile
+flag merging, while project-local shell hooks compute the actual sources,
+target-level flags, dependency labels, and outputs by using `source`, `glob`,
+`config-get`, and `need`. The coordinator-provided target pieces
+(`KIT_BUILD_REPO`, `KIT_BUILD_PACKAGE`, `KIT_BUILD_LOCAL`) keep such shell rule
+libraries from reparsing labels themselves.
+
### Workspace manifest, defaults, and external repositories
A workspace may also have a root `WORKSPACE.kit` manifest. `BUILD.kit` files
@@ -1032,7 +1041,7 @@ run_recipe(T, cfg, argv, chain):
spawn recipe (process argv = argv, the request's argv or empty when none given) with a
CLEAN env (host inherits nothing): { KIT_BUILD_SOCK, KIT_BUILD_OUT=out,
KIT_BUILD_TARGET=T, KIT_BUILD_REPO=<repo>, KIT_BUILD_PACKAGE=<package>,
- current workspace root } + cfg's `env.*` keys as declared env vars
+ KIT_BUILD_LOCAL=<local-name> } + cfg's `env.*` keys as declared env vars
service the recipe's protocol requests, logging each as a dep (next section);
config-get reads cfg; source returns the live current-workspace path + pinned blob-id;
need releases this recipe's job slot, recurses resolve(...) under cfg ⊕ overrides
diff --git a/include/kit/build_coord.h b/include/kit/build_coord.h
@@ -40,11 +40,14 @@
* creates a per-recipe endpoint (KitBuildTransport.listen) and passes its name
* in KIT_BUILD_SOCK; the recipe dials that name back to the coordinator and
* writes its output tree under KIT_BUILD_OUT. KIT_BUILD_TARGET names the target
- * being built. */
+ * being built; KIT_BUILD_REPO, KIT_BUILD_PACKAGE, and KIT_BUILD_LOCAL expose
+ * the canonical label's pieces for recipe rule libraries. */
#define KIT_BUILD_ENV_SOCK "KIT_BUILD_SOCK"
#define KIT_BUILD_ENV_OUT "KIT_BUILD_OUT"
#define KIT_BUILD_ENV_TARGET "KIT_BUILD_TARGET"
+#define KIT_BUILD_ENV_REPO "KIT_BUILD_REPO"
#define KIT_BUILD_ENV_PACKAGE "KIT_BUILD_PACKAGE"
+#define KIT_BUILD_ENV_LOCAL "KIT_BUILD_LOCAL"
/* Propagated-config keys under this prefix declare the recipe's pass-through
* environment: a key "env.PATH" sets $PATH (to its config value) in the recipe's
diff --git a/recipes/buildkit/common.sh b/recipes/buildkit/common.sh
@@ -7,6 +7,9 @@ export PATH
: "${KIT:=./build/kit}"
+c_rules=$("$KIT" build source rules/c/common.sh)
+. "$c_rules"
+
bk_source() {
"$KIT" build source "$1"
}
@@ -33,6 +36,13 @@ bk_depfile() {
fi
}
+c_depfile_filter() {
+ sed -e 's@driver/cmd/\.\./\.\./@@g' \
+ -e 's@driver/env/\.\./@driver/@g' \
+ -e 's@src/build/\.\./@src/@g' \
+ "$1"
+}
+
bk_host_detect() {
uname_s=$(uname -s)
uname_m=$(uname -m)
@@ -217,12 +227,41 @@ bk_flat_obj() {
printf '%s/%s/%s/%s\n' "$KIT_BUILD_OUT" "$dest" "$dir" "$flat"
}
+bk_flat_obj_rel() {
+ src=$1
+ root=$2
+ dest=$3
+ rel=${src#"$root"/}
+ dir=$(dirname "$rel")
+ base=${rel%.c}
+ flat=$(printf '%s' "$base" | tr '/' '_').o
+ if [ "$dir" = "." ]; then
+ printf '%s/%s\n' "$dest" "$flat"
+ else
+ printf '%s/%s/%s\n' "$dest" "$dir" "$flat"
+ fi
+}
+
bk_driver_obj() {
src=$1
rel=${src#driver/}
printf '%s/driver/%s.o\n' "$KIT_BUILD_OUT" "${rel%.c}"
}
+bk_driver_obj_rel() {
+ src=$1
+ rel=${src#driver/}
+ printf 'driver/%s.o\n' "${rel%.c}"
+}
+
+bk_target_local() {
+ printf '%s\n' "${KIT_BUILD_TARGET##*:}"
+}
+
+bk_obj_target() {
+ printf '%s\n' "$1" | sed 's@/@_@g; s@[.]c$@.o@'
+}
+
bk_sort_words() {
printf '%s\n' "$@" | awk 'NF' | sort -u
}
@@ -652,3 +691,180 @@ bk_version_defs() {
host_triple=$(bk_config host.triple "$triple_arch-$triple_os")
printf '%s\n' "-DKIT_VERSION_STR=\"$version\" -DKIT_BUILD_ID=\"$build_id\" -DKIT_HOST_TRIPLE=\"$host_triple\""
}
+
+bk_libkit_object_plan() {
+ for src in $(bk_lib_sources); do
+ flags=$BK_LIB_CFLAGS
+ root=src
+ dest=lib
+ case "$src" in
+ src/api/lang_registry.c)
+ flags="$BK_FREESTANDING_CFLAGS $BK_LIB_VISIBILITY_CFLAGS -Iinclude -Ilang -Isrc"
+ ;;
+ lang/cpp/*)
+ flags="$BK_FREESTANDING_CFLAGS $BK_LIB_VISIBILITY_CFLAGS -Iinclude -Ilang/cpp"
+ root=lang
+ dest=lang
+ ;;
+ lang/c/*)
+ flags="$BK_FREESTANDING_CFLAGS $BK_LIB_VISIBILITY_CFLAGS -Iinclude -Ilang/cpp -Ilang/c"
+ root=lang
+ dest=lang
+ ;;
+ lang/wasm/*)
+ flags="$BK_FREESTANDING_CFLAGS $BK_LIB_VISIBILITY_CFLAGS -Iinclude -Isrc -Ilang/wasm"
+ root=lang
+ dest=lang
+ ;;
+ lang/toy/*)
+ flags="$BK_FREESTANDING_CFLAGS $BK_LIB_VISIBILITY_CFLAGS -Iinclude -Ilang/toy"
+ root=lang
+ dest=lang
+ ;;
+ vendor/*)
+ root=vendor
+ dest=vendor
+ ;;
+ esac
+ printf '%s|%s|%s|%s\n' \
+ "$(bk_obj_target "$src")" "$src" \
+ "$(bk_flat_obj_rel "$src" "$root" "$dest")" "$flags"
+ done
+}
+
+bk_driver_object_plan() {
+ version_defs=$(bk_version_defs)
+ for src in $(bk_driver_sources); do
+ flags=$BK_DRIVER_CFLAGS
+ case "$src" in
+ driver/env/*)
+ flags="$BK_DRIVER_ENV_CFLAGS $BK_DRIVER_ENV_OS_CFLAGS"
+ ;;
+ esac
+ case "$src" in
+ driver/version.c)
+ flags="$flags $version_defs"
+ ;;
+ esac
+ printf '%s|%s|%s|%s\n' \
+ "$(bk_obj_target "$src")" "$src" "$(bk_driver_obj_rel "$src")" \
+ "$flags"
+ done
+}
+
+bk_object_plan() {
+ case "$1" in
+ driver) bk_driver_object_plan ;;
+ libkit) bk_libkit_object_plan ;;
+ libkit-*.objs)
+ set_id=${1#libkit-}
+ set_id=${set_id%.objs}
+ bk_libkit_object_plan | awk -F '|' -v set_id="$set_id" '
+ BEGIN { limit = 60; first = set_id * limit + 1; last = first + limit - 1 }
+ NR >= first && NR <= last { print }
+ '
+ ;;
+ *)
+ echo "unknown object plan: $1" >&2
+ return 1
+ ;;
+ esac
+}
+
+bk_libkit_object_sets() {
+ bk_libkit_object_plan | awk '
+ BEGIN { limit = 60 }
+ (NR - 1) % limit == 0 { printf "libkit-%u.objs\n", int((NR - 1) / limit) }
+ '
+}
+
+bk_object_plan_find() {
+ if bk_libkit_object_plan | awk -F '|' -v target="$1" '
+ $1 == target { print; found = 1; exit }
+ END { if (!found) exit 1 }
+ '; then
+ return 0
+ fi
+ bk_driver_object_plan | awk -F '|' -v target="$1" '
+ $1 == target { print; found = 1; exit }
+ END { if (!found) exit 1 }
+ '
+}
+
+bk_copy_runtime_support() {
+ find rt -type f | sort | while IFS= read -r rt_file; do
+ src=$(bk_source "$rt_file")
+ dst="$KIT_BUILD_OUT/support/rt/$rt_file"
+ mkdir -p "$(dirname "$dst")"
+ cp "$src" "$dst"
+ done
+}
+
+c_flags_init() {
+ bk_flags_init
+ C_CC=$BK_CC
+ C_AR=$BK_AR
+ C_CFLAGS=""
+ C_LDFLAGS=$BK_HOST_LDFLAGS
+ C_LDLIBS=$BK_HOST_LDLIBS
+}
+
+c_object_plan() {
+ bk_object_plan "$1"
+}
+
+c_object_plan_find() {
+ bk_object_plan_find "$1"
+}
+
+c_archive_output() {
+ case "$1" in
+ libkit) printf '%s\n' libkit.a ;;
+ *)
+ echo "no static-library plan for target: $KIT_BUILD_TARGET" >&2
+ return 1
+ ;;
+ esac
+}
+
+c_archive_object_sets() {
+ case "$1" in
+ libkit) bk_libkit_object_sets ;;
+ *)
+ echo "no static-library object sets for target: $KIT_BUILD_TARGET" >&2
+ return 1
+ ;;
+ esac
+}
+
+c_executable_output() {
+ case "$1" in
+ kit) printf '%s\n' kit ;;
+ *)
+ echo "no executable plan for target: $KIT_BUILD_TARGET" >&2
+ return 1
+ ;;
+ esac
+}
+
+c_executable_object_sets() {
+ case "$1" in
+ kit) printf '%s\n' //:driver ;;
+ esac
+}
+
+c_executable_libraries() {
+ case "$1" in
+ kit) printf '%s\n' '//:libkit|libkit.a' ;;
+ esac
+}
+
+c_executable_after_link() {
+ case "$1" in
+ kit)
+ lib_dir=$("$KIT" build need //:libkit)
+ cp "$lib_dir/libkit.a" "$KIT_BUILD_OUT/libkit.a"
+ bk_copy_runtime_support
+ ;;
+ esac
+}
diff --git a/recipes/buildkit/driver.sh b/recipes/buildkit/driver.sh
@@ -1,30 +0,0 @@
-#!/bin/sh
-set -eu
-
-: "${KIT:=./build/kit}"
-common=$("$KIT" build source recipes/buildkit/common.sh)
-. "$common"
-
-bk_flags_init
-mkdir -p "$KIT_BUILD_OUT"
-
-objects="$KIT_BUILD_OUT/objects.list"
-: > "$objects"
-version_defs=$(bk_version_defs)
-
-for src in $(bk_driver_sources); do
- flags=$BK_DRIVER_CFLAGS
- case "$src" in
- driver/env/*)
- flags="$BK_DRIVER_ENV_CFLAGS $BK_DRIVER_ENV_OS_CFLAGS"
- ;;
- esac
- case "$src" in
- driver/version.c)
- flags="$flags $version_defs"
- ;;
- esac
- obj=$(bk_driver_obj "$src")
- bk_compile "$src" "$obj" "$flags"
- printf '%s\n' "${obj#"$KIT_BUILD_OUT"/}" >> "$objects"
-done
diff --git a/recipes/buildkit/kit.sh b/recipes/buildkit/kit.sh
@@ -1,30 +0,0 @@
-#!/bin/sh
-set -eu
-
-: "${KIT:=./build/kit}"
-common=$("$KIT" build source recipes/buildkit/common.sh)
-. "$common"
-
-bk_flags_init
-mkdir -p "$KIT_BUILD_OUT"
-
-driver_dir=$("$KIT" build need //:driver)
-lib_dir=$("$KIT" build need //:libkit)
-
-objs=
-while IFS= read -r obj; do
- [ -n "$obj" ] || continue
- objs="$objs $driver_dir/$obj"
-done < "$driver_dir/objects.list"
-
-# shellcheck disable=SC2086
-"$BK_CC" $BK_HOST_LDFLAGS -o "$KIT_BUILD_OUT/kit" $objs \
- "$lib_dir/libkit.a" $BK_HOST_LDLIBS
-cp "$lib_dir/libkit.a" "$KIT_BUILD_OUT/libkit.a"
-
-find rt -type f | sort | while IFS= read -r rt_file; do
- src=$(bk_source "$rt_file")
- dst="$KIT_BUILD_OUT/support/rt/$rt_file"
- mkdir -p "$(dirname "$dst")"
- cp "$src" "$dst"
-done
diff --git a/recipes/buildkit/libkit.sh b/recipes/buildkit/libkit.sh
@@ -1,56 +0,0 @@
-#!/bin/sh
-set -eu
-
-: "${KIT:=./build/kit}"
-common=$("$KIT" build source recipes/buildkit/common.sh)
-. "$common"
-
-bk_flags_init
-mkdir -p "$KIT_BUILD_OUT"
-
-objects="$KIT_BUILD_OUT/objects.list"
-: > "$objects"
-
-for src in $(bk_lib_sources); do
- flags=$BK_LIB_CFLAGS
- root=src
- dest=lib
- case "$src" in
- src/api/lang_registry.c)
- flags="$BK_FREESTANDING_CFLAGS $BK_LIB_VISIBILITY_CFLAGS -Iinclude -Ilang -Isrc"
- ;;
- lang/cpp/*)
- flags="$BK_FREESTANDING_CFLAGS $BK_LIB_VISIBILITY_CFLAGS -Iinclude -Ilang/cpp"
- root=lang
- dest=lang
- ;;
- lang/c/*)
- flags="$BK_FREESTANDING_CFLAGS $BK_LIB_VISIBILITY_CFLAGS -Iinclude -Ilang/cpp -Ilang/c"
- root=lang
- dest=lang
- ;;
- lang/wasm/*)
- flags="$BK_FREESTANDING_CFLAGS $BK_LIB_VISIBILITY_CFLAGS -Iinclude -Isrc -Ilang/wasm"
- root=lang
- dest=lang
- ;;
- lang/toy/*)
- flags="$BK_FREESTANDING_CFLAGS $BK_LIB_VISIBILITY_CFLAGS -Iinclude -Ilang/toy"
- root=lang
- dest=lang
- ;;
- vendor/*)
- root=vendor
- dest=vendor
- ;;
- esac
- obj=$(bk_flat_obj "$src" "$root" "$dest")
- bk_compile "$src" "$obj" "$flags"
- printf '%s\n' "$obj" >> "$objects"
-done
-
-rm -f "$KIT_BUILD_OUT/libkit.a"
-# shellcheck disable=SC2046
-"$BK_AR" rcs "$KIT_BUILD_OUT/libkit.a" $(cat "$objects")
-find "$KIT_BUILD_OUT" -type f ! -name libkit.a -delete
-find "$KIT_BUILD_OUT" -type d -empty -delete
diff --git a/rules/c/common.sh b/rules/c/common.sh
@@ -0,0 +1,212 @@
+#!/bin/sh
+
+set -eu
+
+: "${KIT:=./build/kit}"
+
+c_source() {
+ "$KIT" build source "$1"
+}
+
+c_config() {
+ key=$1
+ def=$2
+ "$KIT" build config-get --default "$def" "$key"
+}
+
+c_config_merge() {
+ key=$1
+ def=$2
+ profile=$(c_config profile "")
+ base=$(c_config "$key" "$def")
+ prof=""
+ extra=$(c_config "$key.extra" "")
+ if [ -n "$profile" ]; then
+ prof=$(c_config "profile.$profile.$key" "")
+ fi
+ printf '%s\n' "$base $prof $extra" | awk '{$1=$1; print}'
+}
+
+c_toolchain_init() {
+ C_CC=$(c_config tool.c.cc "$(c_config cc "${CC:-cc}")")
+ C_AR=$(c_config tool.c.ar "$(c_config ar "${AR:-ar}")")
+ C_CFLAGS=$(c_config_merge tool.c.cflags "")
+ C_LDFLAGS=$(c_config_merge tool.c.ldflags "")
+ C_LDLIBS=$(c_config_merge tool.c.ldlibs "")
+}
+
+c_glob() {
+ "$KIT" build glob "$1"
+}
+
+c_depfile() {
+ if [ -s "$1" ]; then
+ dep=$1
+ if command -v c_depfile_filter >/dev/null 2>&1; then
+ norm="$dep.norm"
+ c_depfile_filter "$dep" > "$norm"
+ dep=$norm
+ fi
+ "$KIT" build depfile "$dep" >/dev/null
+ rm -f "$1" "${1}.norm"
+ fi
+}
+
+c_compile() {
+ src=$1
+ obj=$2
+ flags=$3
+ mkdir -p "$(dirname "$obj")"
+ dep="$obj.d"
+ "$C_CC" $C_CFLAGS $flags -MMD -MP -MF "$dep" -c "$src" -o "$obj"
+ c_depfile "$dep"
+}
+
+c_need() {
+ label=$1
+ case "$label" in
+ :*|//*|@*) "$KIT" build need "$label" ;;
+ *) "$KIT" build need ":$label" ;;
+ esac
+}
+
+c_target_local() {
+ if [ -n "${KIT_BUILD_LOCAL:-}" ]; then
+ printf '%s\n' "$KIT_BUILD_LOCAL"
+ else
+ printf '%s\n' "${KIT_BUILD_TARGET##*:}"
+ fi
+}
+
+c_require_hook() {
+ if ! command -v "$1" >/dev/null 2>&1; then
+ echo "missing C rule hook: $1" >&2
+ exit 1
+ fi
+}
+
+c_flags_init_default() {
+ if command -v c_flags_init >/dev/null 2>&1; then
+ c_flags_init
+ else
+ c_toolchain_init
+ fi
+}
+
+c_default_object() {
+ c_flags_init_default
+ c_require_hook c_object_plan_find
+ local=$(c_target_local)
+ line=$(c_object_plan_find "$local") || {
+ echo "no C object plan for target: $KIT_BUILD_TARGET" >&2
+ exit 1
+ }
+
+ target=${line%%|*}
+ rest=${line#*|}
+ src=${rest%%|*}
+ rest=${rest#*|}
+ obj_rel=${rest%%|*}
+ flags=${rest#*|}
+
+ [ "$target" = "$local" ] || exit 1
+
+ mkdir -p "$KIT_BUILD_OUT/obj"
+ c_compile "$src" "$KIT_BUILD_OUT/obj/$local" "$flags"
+ printf '%s\n' "obj/$local" > "$KIT_BUILD_OUT/object.list"
+ printf '%s\n' "$obj_rel" > "$KIT_BUILD_OUT/object.rel"
+ printf '%s\n' "$src" > "$KIT_BUILD_OUT/source.txt"
+}
+
+c_default_objects() {
+ c_flags_init_default
+ c_require_hook c_object_plan
+ local=$(c_target_local)
+ mkdir -p "$KIT_BUILD_OUT"
+ objects="$KIT_BUILD_OUT/objects.list"
+ : > "$objects"
+
+ c_object_plan "$local" | while IFS='|' read -r _target src obj_rel flags; do
+ [ -n "$src" ] || continue
+ dst="$KIT_BUILD_OUT/$obj_rel"
+ mkdir -p "$(dirname "$dst")"
+ c_compile "$src" "$dst" "$flags"
+ printf '%s\n' "$obj_rel" >> "$objects"
+ done
+}
+
+c_default_archive() {
+ c_flags_init_default
+ c_require_hook c_archive_output
+ c_require_hook c_archive_object_sets
+ local=$(c_target_local)
+ mkdir -p "$KIT_BUILD_OUT"
+ archive=$(c_archive_output "$local")
+ objects="$KIT_BUILD_OUT/archive-objects.list"
+ : > "$objects"
+
+ c_archive_object_sets "$local" | while IFS= read -r set_target; do
+ [ -n "$set_target" ] || continue
+ dep=$(c_need "$set_target")
+ while IFS= read -r obj; do
+ [ -n "$obj" ] || continue
+ printf '%s/%s\n' "$dep" "$obj" >> "$objects"
+ done < "$dep/objects.list"
+ done
+
+ rm -f "$KIT_BUILD_OUT/$archive"
+ # shellcheck disable=SC2046
+ "$C_AR" rcs "$KIT_BUILD_OUT/$archive" $(cat "$objects")
+ rm -f "$objects"
+}
+
+c_default_executable() {
+ c_flags_init_default
+ c_require_hook c_executable_output
+ local=$(c_target_local)
+ mkdir -p "$KIT_BUILD_OUT"
+ exe=$(c_executable_output "$local")
+ objs=
+ libs=
+
+ if command -v c_executable_object_sets >/dev/null 2>&1; then
+ for set_target in $(c_executable_object_sets "$local"); do
+ dep=$(c_need "$set_target")
+ while IFS= read -r obj; do
+ [ -n "$obj" ] || continue
+ objs="$objs $dep/$obj"
+ done < "$dep/objects.list"
+ done
+ fi
+
+ if command -v c_executable_libraries >/dev/null 2>&1; then
+ c_executable_libraries "$local" | while IFS='|' read -r label file; do
+ [ -n "$label" ] || continue
+ dep=$(c_need "$label")
+ printf '%s/%s\n' "$dep" "$file" >> "$KIT_BUILD_OUT/.link-libs"
+ done
+ if [ -f "$KIT_BUILD_OUT/.link-libs" ]; then
+ while IFS= read -r lib; do
+ libs="$libs $lib"
+ done < "$KIT_BUILD_OUT/.link-libs"
+ rm -f "$KIT_BUILD_OUT/.link-libs"
+ fi
+ fi
+
+ target_ldflags=""
+ target_ldlibs=""
+ if command -v c_executable_ldflags >/dev/null 2>&1; then
+ target_ldflags=$(c_executable_ldflags "$local")
+ fi
+ if command -v c_executable_ldlibs >/dev/null 2>&1; then
+ target_ldlibs=$(c_executable_ldlibs "$local")
+ fi
+
+ # shellcheck disable=SC2086
+ "$C_CC" $C_LDFLAGS $target_ldflags -o "$KIT_BUILD_OUT/$exe" \
+ $objs $libs $target_ldlibs $C_LDLIBS
+
+ if command -v c_executable_after_link >/dev/null 2>&1; then
+ c_executable_after_link "$local"
+ fi
+}
diff --git a/rules/c/default.a.do b/rules/c/default.a.do
@@ -0,0 +1,11 @@
+#!/bin/sh
+set -eu
+
+: "${KIT:=./build/kit}"
+rules=$("$KIT" build source rules/c/common.sh)
+. "$rules"
+project=$("$KIT" build config-get --default do/c.sh tool.c.project)
+project_src=$("$KIT" build source "$project")
+. "$project_src"
+
+c_default_archive
diff --git a/rules/c/default.exe.do b/rules/c/default.exe.do
@@ -0,0 +1,11 @@
+#!/bin/sh
+set -eu
+
+: "${KIT:=./build/kit}"
+rules=$("$KIT" build source rules/c/common.sh)
+. "$rules"
+project=$("$KIT" build config-get --default do/c.sh tool.c.project)
+project_src=$("$KIT" build source "$project")
+. "$project_src"
+
+c_default_executable
diff --git a/rules/c/default.o.do b/rules/c/default.o.do
@@ -0,0 +1,11 @@
+#!/bin/sh
+set -eu
+
+: "${KIT:=./build/kit}"
+rules=$("$KIT" build source rules/c/common.sh)
+. "$rules"
+project=$("$KIT" build config-get --default do/c.sh tool.c.project)
+project_src=$("$KIT" build source "$project")
+. "$project_src"
+
+c_default_object
diff --git a/rules/c/default.objs.do b/rules/c/default.objs.do
@@ -0,0 +1,11 @@
+#!/bin/sh
+set -eu
+
+: "${KIT:=./build/kit}"
+rules=$("$KIT" build source rules/c/common.sh)
+. "$rules"
+project=$("$KIT" build config-get --default do/c.sh tool.c.project)
+project_src=$("$KIT" build source "$project")
+. "$project_src"
+
+c_default_objects
diff --git a/src/build/runner.c b/src/build/runner.c
@@ -28,6 +28,51 @@ static int target_copy(KitSlice target, char out[BUILD_TARGET_MAX]) {
return BUILD_OK;
}
+typedef struct BuildRunScratch {
+ BuildConfigLeaf configs[BUILD_RECIPE_CONFIG_CAP];
+ BuildSourceLeaf sources[BUILD_RECIPE_SOURCE_CAP];
+ BuildGlobLeaf globs[BUILD_RECIPE_GLOB_CAP];
+ BuildBlobLeaf blobs[BUILD_RECIPE_BLOB_CAP];
+ BuildDepEdge deps[BUILD_RECIPE_DEP_CAP];
+ const BuildLeafSet* child_leafsets[BUILD_RECIPE_DEP_CAP];
+ BuildPendingNeed pending[BUILD_RECIPE_DEP_CAP];
+} BuildRunScratch;
+
+static BuildRunScratch* build_run_scratch_new(KitBuildCoordinator* c) {
+ BuildRunScratch* s;
+ if (!c || !c->ctx || !c->ctx->heap) return NULL;
+ s = (BuildRunScratch*)c->ctx->heap->alloc(c->ctx->heap, sizeof *s,
+ _Alignof(BuildRunScratch));
+ if (s) memset(s, 0, sizeof *s);
+ return s;
+}
+
+static void build_run_scratch_free(KitBuildCoordinator* c,
+ BuildRunScratch* s) {
+ if (c && c->ctx && c->ctx->heap && s)
+ c->ctx->heap->free(c->ctx->heap, s, sizeof *s);
+}
+
+static void build_dep_log_init(BuildDepLog* log, BuildRunScratch* scratch) {
+ memset(log, 0, sizeof *log);
+ log->configs = scratch->configs;
+ log->cap_configs = sizeof scratch->configs / sizeof scratch->configs[0];
+ log->sources = scratch->sources;
+ log->cap_sources = sizeof scratch->sources / sizeof scratch->sources[0];
+ log->globs = scratch->globs;
+ log->cap_globs = sizeof scratch->globs / sizeof scratch->globs[0];
+ log->blobs = scratch->blobs;
+ log->cap_blobs = sizeof scratch->blobs / sizeof scratch->blobs[0];
+ log->deps = scratch->deps;
+ log->cap_deps = sizeof scratch->deps / sizeof scratch->deps[0];
+ log->child_leafsets = scratch->child_leafsets;
+ log->cap_children =
+ sizeof scratch->child_leafsets / sizeof scratch->child_leafsets[0];
+ log->pending = scratch->pending;
+ log->cap_pending = sizeof scratch->pending / sizeof scratch->pending[0];
+ log->next_token = 1;
+}
+
static void hash_slice(KitSlice s, uint8_t out[BUILD_HASH_LEN]) {
KitBlobInfo bi;
static const uint8_t empty = 0;
@@ -374,18 +419,12 @@ static int build_run_recipe_impl(KitBuildCoordinator* c, KitSlice target,
char sandbox[BUILD_PATH_MAX];
char out_dir[BUILD_PATH_MAX];
KitSlice proc_argv[129];
- KitBuildKV env[132];
+ KitBuildKV env[140];
char env_keys[128][BUILD_KEY_MAX];
- size_t argc = 0, nenv = 0, i;
+ size_t argc = 0, nenv = 0, i, base_nenv = 0;
int exit_code = 1;
int ok = BUILD_ERR;
- BuildConfigLeaf configs[BUILD_RECIPE_CONFIG_CAP];
- BuildSourceLeaf sources[BUILD_RECIPE_SOURCE_CAP];
- BuildGlobLeaf globs[BUILD_RECIPE_GLOB_CAP];
- BuildBlobLeaf blobs[BUILD_RECIPE_BLOB_CAP];
- BuildDepEdge deps[BUILD_RECIPE_DEP_CAP];
- const BuildLeafSet* child_leafsets[BUILD_RECIPE_DEP_CAP];
- BuildPendingNeed pending[BUILD_RECIPE_DEP_CAP];
+ BuildRunScratch* scratch = NULL;
BuildDepLog log;
if (!c || !cfg || !argv || !out || !c->host.exec || !c->host.transport)
@@ -404,22 +443,9 @@ static int build_run_recipe_impl(KitBuildCoordinator* c, KitSlice target,
sizeof out_dir) != BUILD_OK)
goto out_cleanup;
- memset(&log, 0, sizeof log);
- log.configs = configs;
- log.cap_configs = sizeof configs / sizeof configs[0];
- log.sources = sources;
- log.cap_sources = sizeof sources / sizeof sources[0];
- log.globs = globs;
- log.cap_globs = sizeof globs / sizeof globs[0];
- log.blobs = blobs;
- log.cap_blobs = sizeof blobs / sizeof blobs[0];
- log.deps = deps;
- log.cap_deps = sizeof deps / sizeof deps[0];
- log.child_leafsets = child_leafsets;
- log.cap_children = sizeof child_leafsets / sizeof child_leafsets[0];
- log.pending = pending;
- log.cap_pending = sizeof pending / sizeof pending[0];
- log.next_token = 1;
+ scratch = build_run_scratch_new(c);
+ if (!scratch) goto out_cleanup;
+ build_dep_log_init(&log, scratch);
proc_argv[argc++] = kit_slice_cstr(recipe.recipe_abspath);
for (i = 0; i < argv->n && argc < sizeof proc_argv / sizeof proc_argv[0];
@@ -433,18 +459,26 @@ static int build_run_recipe_impl(KitBuildCoordinator* c, KitSlice target,
env[nenv++].value = kit_slice_cstr(out_dir);
env[nenv].key = KIT_SLICE_LIT(KIT_BUILD_ENV_TARGET);
env[nenv++].value = target;
+ env[nenv].key = KIT_SLICE_LIT(KIT_BUILD_ENV_REPO);
+ env[nenv++].value = kit_slice_cstr(recipe.repo);
env[nenv].key = KIT_SLICE_LIT(KIT_BUILD_ENV_PACKAGE);
env[nenv++].value = kit_slice_cstr(recipe.package);
- for (i = 0; i < cfg->n && nenv < sizeof env / sizeof env[0]; ++i) {
+ env[nenv].key = KIT_SLICE_LIT(KIT_BUILD_ENV_LOCAL);
+ env[nenv++].value = kit_slice_cstr(recipe.local_name);
+ base_nenv = nenv;
+ for (i = 0; i < cfg->n && nenv < sizeof env / sizeof env[0] &&
+ nenv - base_nenv < sizeof env_keys / sizeof env_keys[0];
+ ++i) {
const char* key = cfg->entries[i].key;
size_t prefix_len = sizeof(KIT_BUILD_ENV_PREFIX) - 1u;
if (strncmp(key, KIT_BUILD_ENV_PREFIX, prefix_len) != 0) continue;
if (append_config_observation(&log, cfg, kit_slice_cstr(key),
KIT_SLICE_NULL, 0) != BUILD_OK)
goto out_cleanup;
- snprintf(env_keys[nenv - 4u], sizeof env_keys[nenv - 4u], "%s",
+ snprintf(env_keys[nenv - base_nenv],
+ sizeof env_keys[nenv - base_nenv], "%s",
key + prefix_len);
- env[nenv].key = kit_slice_cstr(env_keys[nenv - 4u]);
+ env[nenv].key = kit_slice_cstr(env_keys[nenv - base_nenv]);
env[nenv].value = kit_slice_cstr(cfg->entries[i].value);
++nenv;
}
@@ -481,6 +515,7 @@ out_cleanup:
if (listener)
c->host.transport->close_listener(c->host.transport->user, listener);
if (sandbox[0]) build_store_sandbox_done(&c->store, sandbox);
+ build_run_scratch_free(c, scratch);
return ok;
}
@@ -500,18 +535,12 @@ static int build_run_test_recipe_impl(KitBuildCoordinator* c, KitSlice target,
char stdout_path[BUILD_PATH_MAX];
char stderr_path[BUILD_PATH_MAX];
KitSlice proc_argv[129];
- KitBuildKV env[132];
+ KitBuildKV env[140];
char env_keys[128][BUILD_KEY_MAX];
- size_t argc = 0, nenv = 0, i;
+ size_t argc = 0, nenv = 0, i, base_nenv = 0;
int exit_code = 1;
int ok = BUILD_ERR;
- BuildConfigLeaf configs[BUILD_RECIPE_CONFIG_CAP];
- BuildSourceLeaf sources[BUILD_RECIPE_SOURCE_CAP];
- BuildGlobLeaf globs[BUILD_RECIPE_GLOB_CAP];
- BuildBlobLeaf blobs[BUILD_RECIPE_BLOB_CAP];
- BuildDepEdge deps[BUILD_RECIPE_DEP_CAP];
- const BuildLeafSet* child_leafsets[BUILD_RECIPE_DEP_CAP];
- BuildPendingNeed pending[BUILD_RECIPE_DEP_CAP];
+ BuildRunScratch* scratch = NULL;
BuildDepLog log;
if (!c || !cfg || !argv || !out || !c->host.exec || !c->host.transport ||
@@ -538,22 +567,9 @@ static int build_run_test_recipe_impl(KitBuildCoordinator* c, KitSlice target,
BUILD_OK)
goto out_cleanup;
- memset(&log, 0, sizeof log);
- log.configs = configs;
- log.cap_configs = sizeof configs / sizeof configs[0];
- log.sources = sources;
- log.cap_sources = sizeof sources / sizeof sources[0];
- log.globs = globs;
- log.cap_globs = sizeof globs / sizeof globs[0];
- log.blobs = blobs;
- log.cap_blobs = sizeof blobs / sizeof blobs[0];
- log.deps = deps;
- log.cap_deps = sizeof deps / sizeof deps[0];
- log.child_leafsets = child_leafsets;
- log.cap_children = sizeof child_leafsets / sizeof child_leafsets[0];
- log.pending = pending;
- log.cap_pending = sizeof pending / sizeof pending[0];
- log.next_token = 1;
+ scratch = build_run_scratch_new(c);
+ if (!scratch) goto out_cleanup;
+ build_dep_log_init(&log, scratch);
proc_argv[argc++] = kit_slice_cstr(recipe.recipe_abspath);
for (i = 0; i < argv->n && argc < sizeof proc_argv / sizeof proc_argv[0];
@@ -567,18 +583,26 @@ static int build_run_test_recipe_impl(KitBuildCoordinator* c, KitSlice target,
env[nenv++].value = kit_slice_cstr(out_dir);
env[nenv].key = KIT_SLICE_LIT(KIT_BUILD_ENV_TARGET);
env[nenv++].value = target;
+ env[nenv].key = KIT_SLICE_LIT(KIT_BUILD_ENV_REPO);
+ env[nenv++].value = kit_slice_cstr(recipe.repo);
env[nenv].key = KIT_SLICE_LIT(KIT_BUILD_ENV_PACKAGE);
env[nenv++].value = kit_slice_cstr(recipe.package);
- for (i = 0; i < cfg->n && nenv < sizeof env / sizeof env[0]; ++i) {
+ env[nenv].key = KIT_SLICE_LIT(KIT_BUILD_ENV_LOCAL);
+ env[nenv++].value = kit_slice_cstr(recipe.local_name);
+ base_nenv = nenv;
+ for (i = 0; i < cfg->n && nenv < sizeof env / sizeof env[0] &&
+ nenv - base_nenv < sizeof env_keys / sizeof env_keys[0];
+ ++i) {
const char* key = cfg->entries[i].key;
size_t prefix_len = sizeof(KIT_BUILD_ENV_PREFIX) - 1u;
if (strncmp(key, KIT_BUILD_ENV_PREFIX, prefix_len) != 0) continue;
if (append_config_observation(&log, cfg, kit_slice_cstr(key),
KIT_SLICE_NULL, 0) != BUILD_OK)
goto out_cleanup;
- snprintf(env_keys[nenv - 4u], sizeof env_keys[nenv - 4u], "%s",
+ snprintf(env_keys[nenv - base_nenv],
+ sizeof env_keys[nenv - base_nenv], "%s",
key + prefix_len);
- env[nenv].key = kit_slice_cstr(env_keys[nenv - 4u]);
+ env[nenv].key = kit_slice_cstr(env_keys[nenv - base_nenv]);
env[nenv].value = kit_slice_cstr(cfg->entries[i].value);
++nenv;
}
@@ -626,6 +650,7 @@ out_cleanup:
if (listener)
c->host.transport->close_listener(c->host.transport->user, listener);
if (sandbox[0]) build_store_sandbox_done(&c->store, sandbox);
+ build_run_scratch_free(c, scratch);
return ok;
}
diff --git a/test/buildcoord/run.sh b/test/buildcoord/run.sh
@@ -353,6 +353,9 @@ set -eu
mkdir -p "$KIT_BUILD_OUT"
printf 'ambient:%s\n' "${AMBIENT-unset}" > "$KIT_BUILD_OUT/env.txt"
printf 'declared:%s\n' "${DECLARED-unset}" >> "$KIT_BUILD_OUT/env.txt"
+printf 'repo:%s\n' "${KIT_BUILD_REPO-unset}" >> "$KIT_BUILD_OUT/env.txt"
+printf 'package:%s\n' "${KIT_BUILD_PACKAGE-unset}" >> "$KIT_BUILD_OUT/env.txt"
+printf 'local:%s\n' "${KIT_BUILD_LOCAL-unset}" >> "$KIT_BUILD_OUT/env.txt"
EOF
cat > "$ws/recipes/exports_provider.sh" <<'EOF'
@@ -1821,6 +1824,10 @@ contains "buildcoord-env-clean-ambient" "$env_one_path/env.txt" \
"ambient:unset"
contains "buildcoord-env-declared-visible" "$env_one_path/env.txt" \
"declared:one"
+contains "buildcoord-env-target-repo" "$env_one_path/env.txt" "repo:"
+contains "buildcoord-env-target-package" "$env_one_path/env.txt" \
+ "package:env"
+contains "buildcoord-env-target-local" "$env_one_path/env.txt" "local:probe"
unset MISSING_BUILDCOORD_ENV
run_fail "buildcoord-env-shorthand-unset-rejected" \