commit 00b46f1426b781000d7beda4b4812737c12eca2c
parent 49b6fb2e582e31592612da787fc1af47163ab9cf
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Fri, 17 Jul 2026 15:32:07 -0700
driver: depend only on the public API; drop all relative includes
Enforce two boundary invariants and make the build itself uphold them.
No source uses a relative "../" include. The five offenders (src/build,
driver/env, driver/cmd) now include via their -I roots.
The driver depends only on the public include/ surface; it no longer reaches
into src/ or lang/:
* build_coord.c parsed WORKSPACE.kit through src/build/{defn,workspace}.h.
The workspace and config value types, their constants, and
kit_build_workspace_{init,parse,external_find} are now public in
kit/build_coord.h. The internal cfg.h/workspace.h typedef the short
Build* names to the public KitBuild* types (one definition site), and
src/api/build_coord.c adds thin public wrappers.
* cc.c's #include "c/c.h" was dead (its CC_*/cc_* knobs are all local, and
it never referenced kit_c_frontend_vtable). Removed it and dropped -Ilang
from DRIVER_CFLAGS/DRIVER_ENV_CFLAGS. The library keeps its own -Ilang for
lang_registry.c.
With neither -Ilang nor -Isrc on the driver build, a private-header include
now fails to compile, so the boundary cannot silently regress.
Tests: make test-build green (build_pure 62/0, buildcoord 352/0,
build-public-link); kit cc and kit build repo list smoked end-to-end.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cu5rjrydtNn6LAEwLYqQeB
Diffstat:
12 files changed, 162 insertions(+), 85 deletions(-)
diff --git a/driver/cmd/build_coord.c b/driver/cmd/build_coord.c
@@ -8,8 +8,6 @@
#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"
@@ -50,7 +48,7 @@ typedef struct BuildCli {
} BuildCli;
typedef struct BuildListTarget {
- char label[BUILD_TARGET_MAX];
+ char label[KIT_BUILD_TARGET_MAX];
} BuildListTarget;
void driver_help_build(void) {
@@ -218,7 +216,7 @@ static int build_load_file(DriverEnv* env, const char* path, DriverLoad* load,
}
static int build_load_workspace(DriverEnv* env, const char* root,
- BuildWorkspace* ws, int* present) {
+ KitBuildWorkspace* ws, int* present) {
char path[KIT_BUILD_PATH_MAX];
DriverLoad load;
KitSlice bytes;
@@ -226,13 +224,13 @@ static int build_load_workspace(DriverEnv* env, const char* root,
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);
+ kit_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) {
+ if (kit_build_workspace_parse(bytes.data, bytes.len, ws, err, sizeof err) !=
+ KIT_OK) {
driver_errf(BUILD_TOOL, "bad WORKSPACE.kit: %s", err);
driver_release_bytes(&env->file_io, &load);
return 1;
@@ -346,9 +344,9 @@ static int build_list_generated_from_projection(DriverEnv* env,
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];
+ char pat[KIT_BUILD_PATH_MAX], tmpl[KIT_BUILD_TARGET_MAX];
+ char dir_rel[KIT_BUILD_PATH_MAX], dir_abs[KIT_BUILD_PATH_MAX];
+ char prefix[128], suffix[128], label[KIT_BUILD_TARGET_MAX], local[KIT_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;
@@ -390,7 +388,7 @@ static int build_list_generated_from_projection(DriverEnv* env,
uint64_t ino, size, mtime;
uint8_t ftype;
size_t pn = driver_strlen(prefix), sn = driver_strlen(suffix);
- char stem[BUILD_TARGET_MAX];
+ char stem[KIT_BUILD_TARGET_MAX];
if (driver_read_dir_entry(dh, i, &name, &name_len, &ino, &size, &mtime,
&ftype) != 0)
break;
@@ -434,7 +432,7 @@ static int build_list_package(DriverEnv* env, const char* root,
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];
+ char local[KIT_BUILD_TARGET_MAX], label[KIT_BUILD_TARGET_MAX];
build_copy_slice0(local, sizeof local,
(KitSlice){.s = line.s + 8u,
.len = line.len - 9u});
@@ -467,7 +465,7 @@ static int build_list_package(DriverEnv* env, const char* root,
uint32_t name_len;
uint64_t ino, size, mtime;
uint8_t ftype;
- char child_pkg[BUILD_PATH_MAX], child_dir[KIT_BUILD_PATH_MAX],
+ char child_pkg[KIT_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)
@@ -707,7 +705,7 @@ static int build_export_set(DriverEnv* env, int argc, char** argv,
return 1;
}
{
- char line[BUILD_VAL_MAX + 32u];
+ char line[KIT_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");
@@ -715,7 +713,7 @@ static int build_export_set(DriverEnv* env, int argc, char** argv,
}
}
for (i = (size_t)first_arg + 2u; i < (size_t)argc; ++i) {
- char line[BUILD_VAL_MAX + 2u];
+ char line[KIT_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");
@@ -1270,7 +1268,7 @@ static int build_cmd_list(int argc, char** argv, int cmd_index) {
const char* pattern = "//...";
int scan_do = 0;
int i, recursive = 0;
- char pkg[BUILD_PATH_MAX];
+ char pkg[KIT_BUILD_PATH_MAX];
BuildListTarget targets[512];
size_t ntargets = 0, j;
(void)store_ignored;
@@ -1351,7 +1349,7 @@ static int build_cmd_list(int argc, char** argv, int cmd_index) {
static int build_cmd_repo(int argc, char** argv, int cmd_index) {
DriverEnv env;
const char *root, *def_name_ignored, *store_ignored;
- BuildWorkspace ws;
+ KitBuildWorkspace ws;
int present = 0;
size_t i;
(void)def_name_ignored;
@@ -1369,13 +1367,13 @@ static int build_cmd_repo(int argc, char** argv, int cmd_index) {
}
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
+ char hex[KIT_BUILD_HEX_LEN];
+ const char* fmt = ws.externals[i].format == KIT_BUILD_WS_EXT_TREE
? "tree"
- : ws.externals[i].format == BUILD_WS_EXT_TARGZ
+ : ws.externals[i].format == KIT_BUILD_WS_EXT_TARGZ
? "tar.gz"
: "kpkg";
- kit_hex_encode(hex, ws.externals[i].archive, BUILD_HASH_LEN);
+ kit_hex_encode(hex, ws.externals[i].archive, KIT_BUILD_HASH_LEN);
driver_printf("%s %s %s\n", ws.externals[i].name, fmt, hex);
}
} else if (driver_streq(argv[cmd_index + 1], "fetch")) {
@@ -1384,7 +1382,7 @@ static int build_cmd_repo(int argc, char** argv, int cmd_index) {
driver_env_fini(&env);
return 2;
}
- if (!build_workspace_external_find(&ws, kit_slice_cstr(argv[cmd_index + 2]))) {
+ if (!kit_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;
@@ -1396,7 +1394,7 @@ static int build_cmd_repo(int argc, char** argv, int cmd_index) {
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];
+ char ws_path[KIT_BUILD_PATH_MAX], line[2048], hex[KIT_BUILD_HEX_LEN];
DriverLoad load;
KitSlice bytes;
KitBlobInfo bi;
@@ -1422,7 +1420,7 @@ static int build_cmd_repo(int argc, char** argv, int cmd_index) {
}
kit_blob_info(&bi, bytes.data, bytes.len);
driver_release_bytes(&env.file_io, &load);
- kit_hex_encode(hex, bi.id, BUILD_HASH_LEN);
+ kit_hex_encode(hex, bi.id, KIT_BUILD_HASH_LEN);
if (build_join_path(ws_path, sizeof ws_path, root, "WORKSPACE.kit") != 0) {
driver_env_fini(&env);
return 1;
diff --git a/driver/cmd/cc.c b/driver/cmd/cc.c
@@ -1,5 +1,3 @@
-#include "c/c.h"
-
#include <kit/asm_emit.h>
#include <kit/build.h>
#include <kit/compile.h>
diff --git a/driver/cmd/targets.c b/driver/cmd/targets.c
@@ -1,4 +1,4 @@
-#include "../driver.h"
+#include "driver.h"
#include <kit/target.h>
#include <stdint.h>
diff --git a/driver/env/build_host_posix.c b/driver/env/build_host_posix.c
@@ -1,4 +1,4 @@
-#include "../env_build.h"
+#include "env_build.h"
#include "dist_host.h"
diff --git a/driver/env/build_host_stub.c b/driver/env/build_host_stub.c
@@ -1,4 +1,4 @@
-#include "../env_build.h"
+#include "env_build.h"
#include <string.h>
diff --git a/driver/env/build_host_windows.c b/driver/env/build_host_windows.c
@@ -1,4 +1,4 @@
-#include "../env_build.h"
+#include "env_build.h"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
diff --git a/include/kit/build_coord.h b/include/kit/build_coord.h
@@ -523,4 +523,92 @@ KIT_API KitStatus kit_build_client_need_await(KitBuildClient*,
KitBuildNeedToken token,
KitBuildResult* out);
+/* ------------------------------------------------------------------ *
+ * Workspace definition and configuration values
+ * ------------------------------------------------------------------ *
+ *
+ * A workspace (WORKSPACE.kit at the build root) names the project, declares
+ * named [config NAME] profiles, and pins external repositories. These types
+ * expose the parsed form for tooling that inspects or renders a workspace (for
+ * example `kit build repo list`); the coordinator itself consumes a workspace
+ * through KitBuildOptions.workspace_root/profile and resolves it internally, so
+ * a caller that only runs builds never needs them.
+ *
+ * All storage is inline and fixed-size (VLA-free), so a KitBuildWorkspace is
+ * safe to hold by value on the stack. Fixed string capacities match the
+ * canonical build text format. */
+#define KIT_BUILD_TARGET_MAX 256u /* label, e.g. "//app:server" */
+#define KIT_BUILD_KEY_MAX 64u /* config key name */
+#define KIT_BUILD_VAL_MAX 512u /* config value */
+#define KIT_BUILD_URL_MAX 1024u /* one fetch URL hint */
+#define KIT_BUILD_HEX_LEN (2u * KIT_BUILD_HASH_LEN + 1u) /* lowercase hex + NUL */
+
+/* One key=value entry of a propagated-config map. */
+typedef struct KitBuildConfigEntry {
+ char key[KIT_BUILD_KEY_MAX];
+ char value[KIT_BUILD_VAL_MAX];
+} KitBuildConfigEntry;
+
+/* A propagated-config map over caller-owned `entries` storage; kept sorted by
+ * key with no duplicates once canonicalized. */
+typedef struct KitBuildConfig {
+ KitBuildConfigEntry* entries;
+ size_t n;
+ size_t cap;
+} KitBuildConfig;
+
+/* How an external repository's pinned content is delivered. */
+typedef enum KitBuildWorkspaceExternalFormat {
+ KIT_BUILD_WS_EXT_NONE = 0,
+ KIT_BUILD_WS_EXT_TREE,
+ KIT_BUILD_WS_EXT_KPKG,
+ KIT_BUILD_WS_EXT_TARGZ,
+} KitBuildWorkspaceExternalFormat;
+
+/* One pinned external repository. */
+typedef struct KitBuildWorkspaceExternal {
+ char name[KIT_BUILD_KEY_MAX];
+ KitBuildWorkspaceExternalFormat format;
+ uint8_t archive[KIT_BUILD_HASH_LEN];
+ int has_archive;
+ uint8_t package[KIT_BUILD_HASH_LEN];
+ int has_package;
+ char strip_prefix[KIT_BUILD_PATH_MAX];
+ char urls[8][KIT_BUILD_URL_MAX];
+ size_t n_urls;
+} KitBuildWorkspaceExternal;
+
+/* One named [config NAME] profile: inline entry storage plus a KitBuildConfig
+ * view over it. */
+typedef struct KitBuildWorkspaceConfigProfile {
+ char name[KIT_BUILD_KEY_MAX];
+ char inherits[KIT_BUILD_KEY_MAX];
+ KitBuildConfigEntry entries[64];
+ KitBuildConfig cfg;
+} KitBuildWorkspaceConfigProfile;
+
+/* A parsed WORKSPACE.kit: identity, config profiles, and external repos. */
+typedef struct KitBuildWorkspace {
+ char name[KIT_BUILD_KEY_MAX];
+ char version[KIT_BUILD_VAL_MAX];
+ char def_name[KIT_BUILD_PATH_MAX];
+ KitBuildWorkspaceConfigProfile profiles[16];
+ size_t n_profiles;
+ KitBuildWorkspaceExternal externals[16];
+ size_t n_externals;
+} KitBuildWorkspace;
+
+/* Initialize an empty workspace (no profiles, no externals). */
+KIT_API void kit_build_workspace_init(KitBuildWorkspace* out);
+
+/* Parse canonical WORKSPACE.kit bytes into `out`. On a parse error, returns
+ * KIT_INVALID and writes a human-readable reason into `err` (errcap bytes). */
+KIT_API KitStatus kit_build_workspace_parse(const uint8_t* data, size_t len,
+ KitBuildWorkspace* out, char* err,
+ size_t errcap);
+
+/* Find an external repository by name; NULL if absent. */
+KIT_API const KitBuildWorkspaceExternal* kit_build_workspace_external_find(
+ const KitBuildWorkspace* ws, KitSlice name);
+
#endif
diff --git a/mk/flags.mk b/mk/flags.mk
@@ -135,17 +135,17 @@ FREESTANDING_CFLAGS = $(CFLAGS_COMMON) $(FREESTANDING_CONFIG_CFLAGS) -ffreestand
LIB_VISIBILITY_CFLAGS = -fvisibility=hidden
LIB_CFLAGS = $(FREESTANDING_CFLAGS) $(LIB_VISIBILITY_CFLAGS) -Iinclude -Isrc -Ivendor
-# Driver: mostly freestanding CLI binary. Sees only the public include/ tree —
-# that's what makes the driver the first consumer of libkit. -Ilang lets `cc`
-# reach the C frontend's public header ("c/c.h") for the JIT REPL; it
-# deliberately does NOT get -Isrc, so internal headers ("core/...", "link/...")
-# are unreachable from the driver.
+# Driver: mostly freestanding CLI binary. Sees ONLY the public include/ tree —
+# that's what makes the driver the first consumer of libkit, and the build
+# itself enforces it: with neither -Ilang nor -Isrc, a driver source
+# physically cannot include a frontend (lang/) or libkit-internal (src/)
+# header, so the driver depends on nothing but the public API.
#
# driver/env/ holds all hosted OS/libc adapter code and is compiled with
# DRIVER_ENV_CFLAGS. The per-OS feature-test macros and the exact source
# list (DRIVER_ENV_OS_CFLAGS / DRIVER_ENV_SRCS) come from mk/env.mk.
-DRIVER_CFLAGS = $(FREESTANDING_CFLAGS) -Iinclude -Ilang -Idriver -Idriver/lib
-DRIVER_ENV_CFLAGS = $(HOST_CFLAGS) -Iinclude -Ilang -Idriver -Idriver/lib
+DRIVER_CFLAGS = $(FREESTANDING_CFLAGS) -Iinclude -Idriver -Idriver/lib
+DRIVER_ENV_CFLAGS = $(HOST_CFLAGS) -Iinclude -Idriver -Idriver/lib
TEST_HOST_CFLAGS = $(HOST_CFLAGS) -Iinclude -Ilang
# `kit update` release trust is generated per build. Development binaries keep
diff --git a/src/api/build_coord.c b/src/api/build_coord.c
@@ -5,6 +5,7 @@
#include "build/coord.h"
#include "build/protocol.h"
#include "build/resolve.h"
+#include "build/workspace.h"
#include <string.h>
@@ -495,3 +496,25 @@ KitStatus kit_build_client_need_await(KitBuildClient* c,
req.token = token.id;
return build_client_need_result(c, &req, out);
}
+
+/* ------------------------------------------------------------------ *
+ * Workspace inspection (public wrappers over the internal parser)
+ * ------------------------------------------------------------------ */
+
+void kit_build_workspace_init(KitBuildWorkspace* out) {
+ build_workspace_init(out);
+}
+
+KitStatus kit_build_workspace_parse(const uint8_t* data, size_t len,
+ KitBuildWorkspace* out, char* err,
+ size_t errcap) {
+ if (!data || !out) return KIT_INVALID;
+ return build_workspace_parse(data, len, out, err, errcap) == BUILD_OK
+ ? KIT_OK
+ : KIT_INVALID;
+}
+
+const KitBuildWorkspaceExternal* kit_build_workspace_external_find(
+ const KitBuildWorkspace* ws, KitSlice name) {
+ return build_workspace_external_find(ws, name);
+}
diff --git a/src/build/cfg.h b/src/build/cfg.h
@@ -26,18 +26,11 @@
* and the api layer owns sizing.
*/
-typedef struct BuildConfigEntry {
- char key[BUILD_KEY_MAX];
- char value[BUILD_VAL_MAX];
-} BuildConfigEntry;
-
-/* A propagated config map. `entries` is caller-owned; kept sorted by key with
- * no duplicates once canonicalized. */
-typedef struct BuildConfig {
- BuildConfigEntry* entries;
- size_t n;
- size_t cap;
-} BuildConfig;
+/* The propagated-config value types are part of the public build model
+ * (<kit/build_coord.h>, included above); keep the internal short names as
+ * aliases so src/build code is unchanged and there is one definition site. */
+typedef KitBuildConfigEntry BuildConfigEntry;
+typedef KitBuildConfig BuildConfig;
/* A local argv vector. `args` is caller-owned; order is significant and
* preserved (argv is positional, not a map). */
diff --git a/src/build/coord.c b/src/build/coord.c
@@ -1,7 +1,7 @@
#include "coord.h"
#include "bundle.h"
-#include "../dist/tar.h"
+#include "dist/tar.h"
#include <kit/compress.h>
#include <kit/package.h>
diff --git a/src/build/workspace.h b/src/build/workspace.h
@@ -1,6 +1,7 @@
#ifndef KIT_BUILD_WORKSPACE_H
#define KIT_BUILD_WORKSPACE_H
+#include <kit/build_coord.h>
#include <kit/core.h>
#include <stddef.h>
#include <stdint.h>
@@ -8,41 +9,17 @@
#include "build.h"
#include "cfg.h"
-typedef enum BuildWorkspaceExternalFormat {
- BUILD_WS_EXT_NONE = 0,
- BUILD_WS_EXT_TREE,
- BUILD_WS_EXT_KPKG,
- BUILD_WS_EXT_TARGZ,
-} BuildWorkspaceExternalFormat;
-
-typedef struct BuildWorkspaceConfigProfile {
- char name[BUILD_KEY_MAX];
- char inherits[BUILD_KEY_MAX];
- BuildConfigEntry entries[64];
- BuildConfig cfg;
-} BuildWorkspaceConfigProfile;
-
-typedef struct BuildWorkspaceExternal {
- char name[BUILD_KEY_MAX];
- BuildWorkspaceExternalFormat format;
- uint8_t archive[BUILD_HASH_LEN];
- int has_archive;
- uint8_t package[BUILD_HASH_LEN];
- int has_package;
- char strip_prefix[BUILD_PATH_MAX];
- char urls[8][BUILD_URL_MAX];
- size_t n_urls;
-} BuildWorkspaceExternal;
-
-typedef struct BuildWorkspace {
- char name[BUILD_KEY_MAX];
- char version[BUILD_VAL_MAX];
- char def_name[BUILD_PATH_MAX];
- BuildWorkspaceConfigProfile profiles[16];
- size_t n_profiles;
- BuildWorkspaceExternal externals[16];
- size_t n_externals;
-} BuildWorkspace;
+/* The workspace value types are part of the public build model
+ * (<kit/build_coord.h>); keep the internal short names as aliases so src/build
+ * code is unchanged and there is one definition site. */
+typedef KitBuildWorkspaceExternalFormat BuildWorkspaceExternalFormat;
+typedef KitBuildWorkspaceExternal BuildWorkspaceExternal;
+typedef KitBuildWorkspaceConfigProfile BuildWorkspaceConfigProfile;
+typedef KitBuildWorkspace BuildWorkspace;
+#define BUILD_WS_EXT_NONE KIT_BUILD_WS_EXT_NONE
+#define BUILD_WS_EXT_TREE KIT_BUILD_WS_EXT_TREE
+#define BUILD_WS_EXT_KPKG KIT_BUILD_WS_EXT_KPKG
+#define BUILD_WS_EXT_TARGZ KIT_BUILD_WS_EXT_TARGZ
void build_workspace_init(BuildWorkspace*);
int build_workspace_parse(const uint8_t* data, size_t len, BuildWorkspace* out,