commit 1a1d67691c24338bdec361ea3ffe2a3f11982c11
parent 209cfaa3f8102565c021bddf4fc346126551535e
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Thu, 16 Jul 2026 10:10:31 -0700
target: add public profile registry and targets command
Diffstat:
6 files changed, 720 insertions(+), 0 deletions(-)
diff --git a/driver/cmd/targets.c b/driver/cmd/targets.c
@@ -0,0 +1,211 @@
+#include "../driver.h"
+
+#include <kit/target.h>
+#include <stdint.h>
+
+enum TargetsFormat {
+ TARGETS_FORMAT_HUMAN,
+ TARGETS_FORMAT_TRIPLE,
+ TARGETS_FORMAT_TSV_V1,
+};
+
+static void targets_print_capabilities(uint32_t caps, const char* separator) {
+ struct CapabilityName {
+ uint32_t bit;
+ const char* name;
+ };
+ static const struct CapabilityName names[] = {
+ {KIT_TARGET_CAP_COMPILE, "compile"},
+ {KIT_TARGET_CAP_ASSEMBLE, "assemble"},
+ {KIT_TARGET_CAP_LINK, "link"},
+ {KIT_TARGET_CAP_SHARED, "shared"},
+ {KIT_TARGET_CAP_EXECUTE, "execute"},
+ {KIT_TARGET_CAP_SELFHOST, "selfhost"},
+ };
+ unsigned i;
+ int first = 1;
+ for (i = 0; i < (unsigned)(sizeof names / sizeof names[0]); ++i) {
+ if (!(caps & names[i].bit)) continue;
+ if (!first) driver_printf("%s", separator);
+ driver_printf("%s", names[i].name);
+ first = 0;
+ }
+ if (first) driver_printf("none");
+}
+
+void driver_help_targets(void) {
+ driver_printf(
+ "kit targets — inspect the compiled target-profile registry\n"
+ "\n"
+ "USAGE\n"
+ " kit targets [--format=human|triple|tsv|tsv-v1] [SELECTOR|TRIPLE]\n"
+ "\n"
+ "DESCRIPTION\n"
+ " With no target argument, list every profile compiled into libkit.\n"
+ " A detail argument accepts either the stable orchestration selector or\n"
+ " its canonical -target triple. Capability and provisioning are separate:\n"
+ " provisioning describes the required local sysroot/SDK/VM/QEMU lane,\n"
+ " not a claim that it is installed on this machine.\n"
+ "\n"
+ "FORMATS\n"
+ " human Readable table or key/value detail (default)\n"
+ " triple Canonical triples, one per line\n"
+ " tsv Versioned kit-targets-v1 TSV records\n"
+ " tsv-v1 Explicit spelling for the same stable TSV schema\n"
+ "\n"
+ "EXAMPLES\n"
+ " kit targets\n"
+ " kit targets --format=triple\n"
+ " kit targets --format=tsv linux-musl-aa64\n"
+ " kit targets aarch64-linux-android21\n"
+ "\n"
+ "EXIT CODES\n"
+ " 0 Registry/list/detail emitted\n"
+ " 1 No profile matches the requested selector or triple\n"
+ " 2 Invalid command-line usage\n");
+}
+
+static int targets_parse_format(const char* value, enum TargetsFormat* out) {
+ if (driver_streq(value, "human")) {
+ *out = TARGETS_FORMAT_HUMAN;
+ } else if (driver_streq(value, "triple")) {
+ *out = TARGETS_FORMAT_TRIPLE;
+ } else if (driver_streq(value, "tsv") || driver_streq(value, "tsv-v1")) {
+ *out = TARGETS_FORMAT_TSV_V1;
+ } else {
+ return 1;
+ }
+ return 0;
+}
+
+static void targets_unknown_format(const char* value) {
+ const char* const formats[] = {"human", "triple", "tsv", "tsv-v1"};
+ DriverSuggestion suggestions[3];
+ size_t n = driver_suggest_values(value, formats,
+ sizeof formats / sizeof formats[0],
+ suggestions, 3);
+ if (n)
+ driver_errf("targets", "unknown format: %s; did you mean '%s'?", value,
+ suggestions[0].value);
+ else
+ driver_errf("targets", "unknown format: %s", value);
+}
+
+static void targets_print_tsv_header(void) {
+ driver_printf("# kit-targets-v1\n");
+ driver_printf("selector\ttriple\tarch\tos\tobject\thosted\tlibc\t"
+ "sysroot\tcapabilities\tprovisioning\n");
+}
+
+static void targets_print_tsv(const KitTargetProfile* p) {
+ driver_printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t", p->selector,
+ p->triple, kit_target_arch_name(p->spec.arch),
+ kit_target_os_name(p->spec.os),
+ kit_target_obj_name(p->spec.obj),
+ p->hosted ? "hosted" : "freestanding",
+ kit_target_libc_name(p->libc),
+ kit_target_sysroot_name(p->sysroot));
+ targets_print_capabilities(p->capabilities, ",");
+ driver_printf("\t%s\n",
+ kit_target_provisioning_name(p->provisioning));
+}
+
+static void targets_print_human_row(const KitTargetProfile* p) {
+ driver_printf("%-22s %-29s %-8s %-13s %-6s %-15s ", p->selector,
+ p->triple, kit_target_arch_name(p->spec.arch),
+ kit_target_os_name(p->spec.os),
+ kit_target_obj_name(p->spec.obj),
+ kit_target_libc_name(p->libc));
+ targets_print_capabilities(p->capabilities, ",");
+ driver_printf(" [%s]\n",
+ kit_target_provisioning_name(p->provisioning));
+}
+
+static void targets_print_detail(const KitTargetProfile* p) {
+ driver_printf("selector: %s\n", p->selector);
+ driver_printf("triple: %s\n", p->triple);
+ driver_printf("architecture: %s\n", kit_target_arch_name(p->spec.arch));
+ driver_printf("os: %s\n", kit_target_os_name(p->spec.os));
+ driver_printf("object-format: %s\n", kit_target_obj_name(p->spec.obj));
+ driver_printf("pointer: %u-bit %s-endian\n",
+ (unsigned)p->spec.ptr_size * 8u,
+ p->spec.big_endian ? "big" : "little");
+ driver_printf("hosted-model: %s\n",
+ p->hosted ? "hosted" : "freestanding");
+ driver_printf("libc-model: %s\n", kit_target_libc_name(p->libc));
+ driver_printf("sysroot-model: %s\n", kit_target_sysroot_name(p->sysroot));
+ driver_printf("capabilities: ");
+ targets_print_capabilities(p->capabilities, ", ");
+ driver_printf("\nprovisioning: %s (requirement, local state not inferred)\n",
+ kit_target_provisioning_name(p->provisioning));
+}
+
+int driver_targets(int argc, char** argv) {
+ enum TargetsFormat format = TARGETS_FORMAT_HUMAN;
+ const char* detail = NULL;
+ const KitTargetProfile* profiles;
+ size_t count = 0;
+ size_t i;
+
+ if (driver_argv_wants_help(argc, argv, 1)) {
+ driver_help_targets();
+ return 0;
+ }
+ for (i = 1; i < (size_t)argc; ++i) {
+ const char* arg = argv[i];
+ if (driver_strneq(arg, "--format=", 9)) {
+ if (targets_parse_format(arg + 9, &format) != 0) {
+ targets_unknown_format(arg + 9);
+ return 2;
+ }
+ } else if (driver_streq(arg, "--format")) {
+ if (++i >= (size_t)argc) {
+ driver_errf("targets", "--format requires an argument");
+ return 2;
+ }
+ if (targets_parse_format(argv[i], &format) != 0) {
+ targets_unknown_format(argv[i]);
+ return 2;
+ }
+ } else if (arg[0] == '-') {
+ driver_errf("targets", "unknown option: %s", arg);
+ return 2;
+ } else if (detail) {
+ driver_errf("targets", "expected at most one selector or triple");
+ return 2;
+ } else {
+ detail = arg;
+ }
+ }
+
+ profiles = kit_target_profiles(&count);
+ if (detail) {
+ const KitTargetProfile* p = kit_target_profile_lookup(detail);
+ if (!p) {
+ driver_err_unknown_target("targets", detail);
+ return 1;
+ }
+ if (format == TARGETS_FORMAT_TRIPLE) {
+ driver_printf("%s\n", p->triple);
+ } else if (format == TARGETS_FORMAT_TSV_V1) {
+ targets_print_tsv_header();
+ targets_print_tsv(p);
+ } else {
+ targets_print_detail(p);
+ }
+ return 0;
+ }
+
+ if (format == TARGETS_FORMAT_TSV_V1) {
+ targets_print_tsv_header();
+ for (i = 0; i < count; ++i) targets_print_tsv(&profiles[i]);
+ } else if (format == TARGETS_FORMAT_TRIPLE) {
+ for (i = 0; i < count; ++i) driver_printf("%s\n", profiles[i].triple);
+ } else {
+ driver_printf("SELECTOR TRIPLE ARCH "
+ "OS OBJECT LIBC CAPABILITIES "
+ "[PROVISION]\n");
+ for (i = 0; i < count; ++i) targets_print_human_row(&profiles[i]);
+ }
+ return 0;
+}
diff --git a/driver/lib/target.c b/driver/lib/target.c
@@ -20,6 +20,27 @@ int driver_arch_from_name(const char* name, KitArchKind* arch_out,
return kit_arch_from_name(name, arch_out, ptr_size_out) ? 0 : 1;
}
+void driver_err_unknown_target(const char* tool, const char* value) {
+ const KitTargetProfile* profiles;
+ const char* candidates[64];
+ DriverSuggestion suggestions[3];
+ size_t count = 0, candidate_count = 0, i, nsuggestions;
+ profiles = kit_target_profiles(&count);
+ for (i = 0; i < count && candidate_count < 64; ++i)
+ candidates[candidate_count++] = profiles[i].triple;
+ for (i = 0; i < count && candidate_count < 64; ++i)
+ candidates[candidate_count++] = profiles[i].selector;
+ nsuggestions = driver_suggest_values(value, candidates, candidate_count,
+ suggestions, 3);
+ if (nsuggestions)
+ driver_errf(tool, "unrecognized target: %s; did you mean '%s'?", value,
+ suggestions[0].value);
+ else
+ driver_errf(tool, "unrecognized target: %s; run `kit targets` to list "
+ "supported profiles",
+ value);
+}
+
KitPic driver_default_pic(KitObjFmt obj, KitOSKind os) {
return kit_target_default_pic(obj, os);
}
diff --git a/include/kit/config.h b/include/kit/config.h
@@ -152,5 +152,6 @@
#define KIT_TOOL_MC_ENABLED 1
#define KIT_TOOL_GRAM_ENABLED 1
#define KIT_TOOL_UPDATE_ENABLED 1
+#define KIT_TOOL_TARGETS_ENABLED 1
#endif /* KIT_CONFIG_H */
diff --git a/include/kit/target.h b/include/kit/target.h
@@ -107,4 +107,85 @@ KIT_API int kit_target_needs_sysroot_libdir(KitTargetSpec spec);
* (Windows-COFF, given a sysroot and no -nostdlib). Returns 1/0. */
KIT_API int kit_target_default_hosted_profile(KitTargetSpec spec);
+/* -------------------------------------------------------------------------
+ * Supported target-profile registry
+ *
+ * A target triple describes an ABI, but it does not say which hosted libc or
+ * provisioning lane a distribution exercises. For example,
+ * aarch64-linux-gnu and aarch64-linux-musl share the same KitTargetSpec while
+ * requiring different sysroots. The profile registry is the public,
+ * versioned inventory used by `kit targets` and portability orchestration.
+ *
+ * `capabilities` describes what this Kit build can produce. `provisioning`
+ * describes the independent local prerequisite model; it deliberately does
+ * not claim that a sysroot, VM, emulator, or SDK is installed on this host.
+ * Consumers may inspect local state separately without changing this stable
+ * registry.
+ */
+#define KIT_TARGET_PROFILE_REGISTRY_VERSION 1u
+
+typedef enum KitTargetLibcModel {
+ KIT_TARGET_LIBC_NONE,
+ KIT_TARGET_LIBC_GLIBC,
+ KIT_TARGET_LIBC_MUSL,
+ KIT_TARGET_LIBC_FREEBSD,
+ KIT_TARGET_LIBC_UCRT,
+ KIT_TARGET_LIBC_BIONIC,
+ KIT_TARGET_LIBC_DARWIN,
+ KIT_TARGET_LIBC_WASI,
+} KitTargetLibcModel;
+
+typedef enum KitTargetSysrootModel {
+ KIT_TARGET_SYSROOT_NONE,
+ KIT_TARGET_SYSROOT_EXTERNAL,
+ KIT_TARGET_SYSROOT_NATIVE_SDK,
+} KitTargetSysrootModel;
+
+typedef enum KitTargetProvisioningModel {
+ KIT_TARGET_PROVISION_BUILTIN,
+ KIT_TARGET_PROVISION_SYSROOT,
+ KIT_TARGET_PROVISION_NATIVE,
+ KIT_TARGET_PROVISION_VM,
+ KIT_TARGET_PROVISION_QEMU,
+} KitTargetProvisioningModel;
+
+typedef enum KitTargetCapability {
+ KIT_TARGET_CAP_COMPILE = 1u << 0,
+ KIT_TARGET_CAP_ASSEMBLE = 1u << 1,
+ KIT_TARGET_CAP_LINK = 1u << 2,
+ KIT_TARGET_CAP_SHARED = 1u << 3,
+ KIT_TARGET_CAP_EXECUTE = 1u << 4,
+ KIT_TARGET_CAP_SELFHOST = 1u << 5,
+} KitTargetCapability;
+
+typedef struct KitTargetProfile {
+ /* Stable orchestration selector (for example "linux-musl-aa64"). */
+ const char* selector;
+ /* Canonical user-facing triple accepted by -target. */
+ const char* triple;
+ KitTargetSpec spec;
+ uint8_t libc; /* KitTargetLibcModel */
+ uint8_t sysroot; /* KitTargetSysrootModel */
+ uint8_t provisioning; /* KitTargetProvisioningModel */
+ uint8_t hosted; /* nonzero when an OS/libc profile is required */
+ uint32_t capabilities; /* KitTargetCapability bits */
+} KitTargetProfile;
+
+/* Borrow the immutable registry compiled into this libkit. `count` may be
+ * NULL. The returned records remain valid for the process lifetime. */
+KIT_API const KitTargetProfile* kit_target_profiles(size_t* count);
+
+/* Lookup by exact selector or canonical triple. Several profiles can share a
+ * semantic KitTargetSpec but triples remain distinct across libc models. */
+KIT_API const KitTargetProfile* kit_target_profile_lookup(const char* name);
+
+/* Stable diagnostic/serialization spellings for registry enum values and
+ * KitTargetSpec's architecture/OS/object-format axes. */
+KIT_API const char* kit_target_arch_name(KitArchKind arch);
+KIT_API const char* kit_target_os_name(KitOSKind os);
+KIT_API const char* kit_target_obj_name(KitObjFmt obj);
+KIT_API const char* kit_target_libc_name(uint8_t libc);
+KIT_API const char* kit_target_sysroot_name(uint8_t sysroot);
+KIT_API const char* kit_target_provisioning_name(uint8_t provisioning);
+
#endif
diff --git a/src/api/target.c b/src/api/target.c
@@ -9,6 +9,7 @@
#include <kit/target.h>
+#include <kit/config.h>
#include <kit/os.h>
#include <stddef.h>
#include <stdint.h>
@@ -209,6 +210,114 @@ static const struct OsSpec os_specs[] = {
static const size_t os_specs_count = sizeof os_specs / sizeof os_specs[0];
+const char* kit_target_arch_name(KitArchKind arch) {
+ switch (arch) {
+ case KIT_ARCH_X86_32:
+ return "x86_32";
+ case KIT_ARCH_X86_64:
+ return "x86_64";
+ case KIT_ARCH_ARM_32:
+ return "arm32";
+ case KIT_ARCH_ARM_64:
+ return "aarch64";
+ case KIT_ARCH_RV32:
+ return "riscv32";
+ case KIT_ARCH_RV64:
+ return "riscv64";
+ case KIT_ARCH_WASM:
+ return "wasm32";
+ }
+ return "unknown";
+}
+
+const char* kit_target_os_name(KitOSKind os) {
+ switch (os) {
+ case KIT_OS_FREESTANDING:
+ return "freestanding";
+ case KIT_OS_LINUX:
+ return "linux";
+ case KIT_OS_MACOS:
+ return "macos";
+ case KIT_OS_IOS:
+ return "ios";
+ case KIT_OS_IOS_SIMULATOR:
+ return "ios-simulator";
+ case KIT_OS_WINDOWS:
+ return "windows";
+ case KIT_OS_FREEBSD:
+ return "freebsd";
+ case KIT_OS_ANDROID:
+ return "android";
+ case KIT_OS_WASI:
+ return "wasi";
+ }
+ return "unknown";
+}
+
+const char* kit_target_obj_name(KitObjFmt obj) {
+ switch (obj) {
+ case KIT_OBJ_ELF:
+ return "elf";
+ case KIT_OBJ_COFF:
+ return "coff";
+ case KIT_OBJ_MACHO:
+ return "macho";
+ case KIT_OBJ_WASM:
+ return "wasm";
+ }
+ return "unknown";
+}
+
+const char* kit_target_libc_name(uint8_t libc) {
+ switch ((KitTargetLibcModel)libc) {
+ case KIT_TARGET_LIBC_NONE:
+ return "none";
+ case KIT_TARGET_LIBC_GLIBC:
+ return "glibc";
+ case KIT_TARGET_LIBC_MUSL:
+ return "musl";
+ case KIT_TARGET_LIBC_FREEBSD:
+ return "freebsd-libc";
+ case KIT_TARGET_LIBC_UCRT:
+ return "ucrt";
+ case KIT_TARGET_LIBC_BIONIC:
+ return "bionic";
+ case KIT_TARGET_LIBC_DARWIN:
+ return "darwin-libsystem";
+ case KIT_TARGET_LIBC_WASI:
+ return "wasi-libc";
+ }
+ return "unknown";
+}
+
+const char* kit_target_sysroot_name(uint8_t sysroot) {
+ switch ((KitTargetSysrootModel)sysroot) {
+ case KIT_TARGET_SYSROOT_NONE:
+ return "none";
+ case KIT_TARGET_SYSROOT_EXTERNAL:
+ return "external";
+ case KIT_TARGET_SYSROOT_NATIVE_SDK:
+ return "native-sdk";
+ }
+ return "unknown";
+}
+
+const char* kit_target_provisioning_name(uint8_t provisioning) {
+ switch ((KitTargetProvisioningModel)provisioning) {
+ case KIT_TARGET_PROVISION_BUILTIN:
+ return "built-in";
+ case KIT_TARGET_PROVISION_SYSROOT:
+ return "sysroot";
+ case KIT_TARGET_PROVISION_NATIVE:
+ return "native";
+ case KIT_TARGET_PROVISION_VM:
+ return "vm";
+ case KIT_TARGET_PROVISION_QEMU:
+ return "qemu";
+ }
+ return "unknown";
+}
+
static const struct OsSpec* os_spec_for(KitOSKind os) {
size_t i;
for (i = 0; i < os_specs_count; ++i) {
@@ -449,3 +558,180 @@ bool kit_target_code_model_from_name(const char* val, KitCodeModel* out) {
if (out) *out = cm;
return true;
}
+
+/* The registry is intentionally data-only. Triple parsing remains the
+ * authority for arbitrary user spellings; these rows describe the supported
+ * release/provisioning profiles (which additionally distinguish libc lanes).
+ */
+#define PROFILE_CAP_HOSTED \
+ (KIT_TARGET_CAP_COMPILE | KIT_TARGET_CAP_ASSEMBLE | KIT_TARGET_CAP_LINK | \
+ KIT_TARGET_CAP_SHARED | KIT_TARGET_CAP_EXECUTE | KIT_TARGET_CAP_SELFHOST)
+#define PROFILE_CAP_CROSS \
+ (KIT_TARGET_CAP_COMPILE | KIT_TARGET_CAP_ASSEMBLE | KIT_TARGET_CAP_LINK | \
+ KIT_TARGET_CAP_SHARED)
+#define PROFILE_CAP_BARE \
+ (KIT_TARGET_CAP_COMPILE | KIT_TARGET_CAP_ASSEMBLE | KIT_TARGET_CAP_LINK | \
+ KIT_TARGET_CAP_EXECUTE)
+#define PROFILE(sel_, triple_, arch_, os_, obj_, ptr_, libc_, sysroot_, \
+ provision_, hosted_, caps_) \
+ { \
+ .selector = (sel_), .triple = (triple_), \
+ .spec = {.arch = (arch_), \
+ .os = (os_), \
+ .obj = (obj_), \
+ .ptr_size = (ptr_), \
+ .ptr_align = (ptr_), \
+ .big_endian = false, \
+ .pic = ((os_) == KIT_OS_FREESTANDING ? KIT_PIC_NONE \
+ : KIT_PIC_PIE), \
+ .code_model = KIT_CM_DEFAULT, \
+ .float_abi = KIT_FLOAT_ABI_DEFAULT}, \
+ .libc = (libc_), .sysroot = (sysroot_), .provisioning = (provision_), \
+ .hosted = (hosted_), .capabilities = (caps_) \
+ }
+
+static const KitTargetProfile target_profiles[] = {
+#if KIT_OBJ_ELF_ENABLED && KIT_ARCH_AA64_ENABLED
+ PROFILE("linux-glibc-aa64", "aarch64-linux-gnu", KIT_ARCH_ARM_64,
+ KIT_OS_LINUX, KIT_OBJ_ELF, 8, KIT_TARGET_LIBC_GLIBC,
+ KIT_TARGET_SYSROOT_EXTERNAL, KIT_TARGET_PROVISION_SYSROOT, 1,
+ PROFILE_CAP_HOSTED),
+ PROFILE("linux-musl-aa64", "aarch64-linux-musl", KIT_ARCH_ARM_64,
+ KIT_OS_LINUX, KIT_OBJ_ELF, 8, KIT_TARGET_LIBC_MUSL,
+ KIT_TARGET_SYSROOT_EXTERNAL, KIT_TARGET_PROVISION_SYSROOT, 1,
+ PROFILE_CAP_HOSTED),
+ PROFILE("freebsd-aa64", "aarch64-freebsd", KIT_ARCH_ARM_64,
+ KIT_OS_FREEBSD, KIT_OBJ_ELF, 8, KIT_TARGET_LIBC_FREEBSD,
+ KIT_TARGET_SYSROOT_EXTERNAL, KIT_TARGET_PROVISION_VM, 1,
+ PROFILE_CAP_HOSTED),
+ PROFILE("android-aa64", "aarch64-linux-android21", KIT_ARCH_ARM_64,
+ KIT_OS_ANDROID, KIT_OBJ_ELF, 8, KIT_TARGET_LIBC_BIONIC,
+ KIT_TARGET_SYSROOT_EXTERNAL, KIT_TARGET_PROVISION_SYSROOT, 1,
+ PROFILE_CAP_CROSS),
+ PROFILE("freestanding-aa64", "aarch64-none-elf", KIT_ARCH_ARM_64,
+ KIT_OS_FREESTANDING, KIT_OBJ_ELF, 8, KIT_TARGET_LIBC_NONE,
+ KIT_TARGET_SYSROOT_NONE, KIT_TARGET_PROVISION_QEMU, 0,
+ PROFILE_CAP_BARE),
+#endif
+#if KIT_OBJ_ELF_ENABLED && KIT_ARCH_X64_ENABLED
+ PROFILE("linux-glibc-x64", "x86_64-linux-gnu", KIT_ARCH_X86_64,
+ KIT_OS_LINUX, KIT_OBJ_ELF, 8, KIT_TARGET_LIBC_GLIBC,
+ KIT_TARGET_SYSROOT_EXTERNAL, KIT_TARGET_PROVISION_SYSROOT, 1,
+ PROFILE_CAP_HOSTED),
+ PROFILE("linux-musl-x64", "x86_64-linux-musl", KIT_ARCH_X86_64,
+ KIT_OS_LINUX, KIT_OBJ_ELF, 8, KIT_TARGET_LIBC_MUSL,
+ KIT_TARGET_SYSROOT_EXTERNAL, KIT_TARGET_PROVISION_SYSROOT, 1,
+ PROFILE_CAP_HOSTED),
+ PROFILE("freebsd-x64", "x86_64-freebsd", KIT_ARCH_X86_64,
+ KIT_OS_FREEBSD, KIT_OBJ_ELF, 8, KIT_TARGET_LIBC_FREEBSD,
+ KIT_TARGET_SYSROOT_EXTERNAL, KIT_TARGET_PROVISION_VM, 1,
+ PROFILE_CAP_HOSTED),
+ PROFILE("freestanding-x64", "x86_64-none-elf", KIT_ARCH_X86_64,
+ KIT_OS_FREESTANDING, KIT_OBJ_ELF, 8, KIT_TARGET_LIBC_NONE,
+ KIT_TARGET_SYSROOT_NONE, KIT_TARGET_PROVISION_QEMU, 0,
+ PROFILE_CAP_BARE),
+#endif
+#if KIT_OBJ_ELF_ENABLED && KIT_ARCH_RV64_ENABLED
+ PROFILE("linux-glibc-rv64", "riscv64-linux-gnu", KIT_ARCH_RV64,
+ KIT_OS_LINUX, KIT_OBJ_ELF, 8, KIT_TARGET_LIBC_GLIBC,
+ KIT_TARGET_SYSROOT_EXTERNAL, KIT_TARGET_PROVISION_SYSROOT, 1,
+ PROFILE_CAP_HOSTED),
+ PROFILE("linux-musl-rv64", "riscv64-linux-musl", KIT_ARCH_RV64,
+ KIT_OS_LINUX, KIT_OBJ_ELF, 8, KIT_TARGET_LIBC_MUSL,
+ KIT_TARGET_SYSROOT_EXTERNAL, KIT_TARGET_PROVISION_SYSROOT, 1,
+ PROFILE_CAP_HOSTED),
+ PROFILE("freebsd-rv64", "riscv64-freebsd", KIT_ARCH_RV64,
+ KIT_OS_FREEBSD, KIT_OBJ_ELF, 8, KIT_TARGET_LIBC_FREEBSD,
+ KIT_TARGET_SYSROOT_EXTERNAL, KIT_TARGET_PROVISION_VM, 1,
+ PROFILE_CAP_HOSTED),
+ PROFILE("freestanding-rv64", "riscv64-none-elf", KIT_ARCH_RV64,
+ KIT_OS_FREESTANDING, KIT_OBJ_ELF, 8, KIT_TARGET_LIBC_NONE,
+ KIT_TARGET_SYSROOT_NONE, KIT_TARGET_PROVISION_QEMU, 0,
+ PROFILE_CAP_BARE),
+#endif
+#if KIT_OBJ_ELF_ENABLED && KIT_ARCH_RV32_ENABLED
+ PROFILE("freestanding-rv32", "riscv32-none-elf", KIT_ARCH_RV32,
+ KIT_OS_FREESTANDING, KIT_OBJ_ELF, 4, KIT_TARGET_LIBC_NONE,
+ KIT_TARGET_SYSROOT_NONE, KIT_TARGET_PROVISION_QEMU, 0,
+ PROFILE_CAP_BARE),
+#endif
+#if KIT_OBJ_ELF_ENABLED && KIT_ARCH_ARM32_ENABLED
+ PROFILE("freestanding-arm32", "arm-none-eabi", KIT_ARCH_ARM_32,
+ KIT_OS_FREESTANDING, KIT_OBJ_ELF, 4, KIT_TARGET_LIBC_NONE,
+ KIT_TARGET_SYSROOT_NONE, KIT_TARGET_PROVISION_QEMU, 0,
+ PROFILE_CAP_BARE),
+#endif
+#if KIT_OBJ_COFF_ENABLED && KIT_ARCH_AA64_ENABLED
+ PROFILE("windows-aa64", "aarch64-windows", KIT_ARCH_ARM_64,
+ KIT_OS_WINDOWS, KIT_OBJ_COFF, 8, KIT_TARGET_LIBC_UCRT,
+ KIT_TARGET_SYSROOT_EXTERNAL, KIT_TARGET_PROVISION_VM, 1,
+ PROFILE_CAP_HOSTED),
+#endif
+#if KIT_OBJ_COFF_ENABLED && KIT_ARCH_X64_ENABLED
+ PROFILE("windows-x64", "x86_64-windows", KIT_ARCH_X86_64,
+ KIT_OS_WINDOWS, KIT_OBJ_COFF, 8, KIT_TARGET_LIBC_UCRT,
+ KIT_TARGET_SYSROOT_EXTERNAL, KIT_TARGET_PROVISION_VM, 1,
+ PROFILE_CAP_HOSTED),
+#endif
+#if KIT_OBJ_MACHO_ENABLED && KIT_ARCH_AA64_ENABLED
+ PROFILE("macos-aa64", "aarch64-apple-darwin", KIT_ARCH_ARM_64,
+ KIT_OS_MACOS, KIT_OBJ_MACHO, 8, KIT_TARGET_LIBC_DARWIN,
+ KIT_TARGET_SYSROOT_NATIVE_SDK, KIT_TARGET_PROVISION_NATIVE, 1,
+ PROFILE_CAP_HOSTED),
+ PROFILE("ios-aa64", "aarch64-apple-ios", KIT_ARCH_ARM_64, KIT_OS_IOS,
+ KIT_OBJ_MACHO, 8, KIT_TARGET_LIBC_DARWIN,
+ KIT_TARGET_SYSROOT_NATIVE_SDK, KIT_TARGET_PROVISION_SYSROOT, 1,
+ PROFILE_CAP_CROSS),
+ PROFILE("ios-simulator-aa64", "aarch64-apple-ios-simulator",
+ KIT_ARCH_ARM_64, KIT_OS_IOS_SIMULATOR, KIT_OBJ_MACHO, 8,
+ KIT_TARGET_LIBC_DARWIN, KIT_TARGET_SYSROOT_NATIVE_SDK,
+ KIT_TARGET_PROVISION_SYSROOT, 1, PROFILE_CAP_CROSS),
+#endif
+#if KIT_OBJ_MACHO_ENABLED && KIT_ARCH_X64_ENABLED
+ PROFILE("macos-x64", "x86_64-apple-darwin", KIT_ARCH_X86_64,
+ KIT_OS_MACOS, KIT_OBJ_MACHO, 8, KIT_TARGET_LIBC_DARWIN,
+ KIT_TARGET_SYSROOT_NATIVE_SDK, KIT_TARGET_PROVISION_NATIVE, 1,
+ PROFILE_CAP_HOSTED),
+ PROFILE("ios-simulator-x64", "x86_64-apple-ios-simulator",
+ KIT_ARCH_X86_64, KIT_OS_IOS_SIMULATOR, KIT_OBJ_MACHO, 8,
+ KIT_TARGET_LIBC_DARWIN, KIT_TARGET_SYSROOT_NATIVE_SDK,
+ KIT_TARGET_PROVISION_SYSROOT, 1, PROFILE_CAP_CROSS),
+#endif
+#if KIT_OBJ_WASM_ENABLED && KIT_ARCH_WASM_ENABLED
+ PROFILE("wasm32-wasi", "wasm32-wasi", KIT_ARCH_WASM, KIT_OS_WASI,
+ KIT_OBJ_WASM, 4, KIT_TARGET_LIBC_WASI,
+ KIT_TARGET_SYSROOT_EXTERNAL, KIT_TARGET_PROVISION_SYSROOT, 1,
+ KIT_TARGET_CAP_COMPILE | KIT_TARGET_CAP_LINK |
+ KIT_TARGET_CAP_EXECUTE),
+ PROFILE("wasm32-freestanding", "wasm32-none", KIT_ARCH_WASM,
+ KIT_OS_FREESTANDING, KIT_OBJ_WASM, 4, KIT_TARGET_LIBC_NONE,
+ KIT_TARGET_SYSROOT_NONE, KIT_TARGET_PROVISION_BUILTIN, 0,
+ KIT_TARGET_CAP_COMPILE | KIT_TARGET_CAP_LINK |
+ KIT_TARGET_CAP_EXECUTE),
+#endif
+ /* A sentinel keeps the definition valid for component-minimal builds. */
+ {0},
+};
+
+const KitTargetProfile* kit_target_profiles(size_t* count) {
+ size_t n = sizeof target_profiles / sizeof target_profiles[0] - 1u;
+ if (count) *count = n;
+ return target_profiles;
+}
+
+const KitTargetProfile* kit_target_profile_lookup(const char* name) {
+ size_t i, n;
+ if (!name) return NULL;
+ n = sizeof target_profiles / sizeof target_profiles[0] - 1u;
+ for (i = 0; i < n; ++i) {
+ if (strcmp(name, target_profiles[i].selector) == 0 ||
+ strcmp(name, target_profiles[i].triple) == 0)
+ return &target_profiles[i];
+ }
+ return NULL;
+}
+
+#undef PROFILE
+#undef PROFILE_CAP_BARE
+#undef PROFILE_CAP_CROSS
+#undef PROFILE_CAP_HOSTED
diff --git a/test/api/target_test.c b/test/api/target_test.c
@@ -1,7 +1,9 @@
/* target_test - public <kit/core.h> target creation and feature API. */
#include <kit/core.h>
+#include <kit/os.h>
#include <kit/target.h>
+#include <stdlib.h>
#include <string.h>
#include "lib/kit_unit.h"
@@ -252,6 +254,122 @@ static void check_wasm_features(void) {
"wasm64 ISA diagnostic");
}
+static void check_target_profile_registry(void) {
+ static const char* const audited[] = {
+ "linux-glibc-aa64", "linux-glibc-x64", "linux-glibc-rv64",
+ "linux-musl-aa64", "linux-musl-x64", "linux-musl-rv64",
+ "freebsd-aa64", "freebsd-x64", "freebsd-rv64",
+ "windows-aa64", "windows-x64", "android-aa64",
+ "macos-aa64", "macos-x64", "freestanding-aa64",
+ "freestanding-x64", "freestanding-rv64", "freestanding-rv32",
+ "freestanding-arm32",
+ };
+ const KitTargetProfile* profiles;
+ const KitTargetProfile* glibc;
+ const KitTargetProfile* musl;
+ size_t count = 0;
+ size_t i;
+
+ profiles = kit_target_profiles(&count);
+ EXPECT(profiles != NULL, "profile registry returned");
+ EXPECT(count >= sizeof audited / sizeof audited[0],
+ "profile registry covers all audited cells");
+ for (i = 0; i < sizeof audited / sizeof audited[0]; ++i) {
+ const KitTargetProfile* p = kit_target_profile_lookup(audited[i]);
+ EXPECT(p != NULL, "audited profile selector is registered");
+ if (!p) continue;
+ EXPECT(p->selector != NULL && p->triple != NULL,
+ "profile has selector and triple");
+ EXPECT((p->capabilities & KIT_TARGET_CAP_COMPILE) != 0,
+ "profile advertises compile capability");
+ EXPECT(strcmp(kit_target_arch_name(p->spec.arch), "unknown") != 0 &&
+ strcmp(kit_target_os_name(p->spec.os), "unknown") != 0 &&
+ strcmp(kit_target_obj_name(p->spec.obj), "unknown") != 0,
+ "profile axes have stable names");
+ EXPECT(kit_target_profile_lookup(p->triple) == p,
+ "profile triple lookup returns same record");
+ }
+
+ glibc = kit_target_profile_lookup("aarch64-linux-gnu");
+ musl = kit_target_profile_lookup("aarch64-linux-musl");
+ EXPECT(glibc && musl && glibc->libc == KIT_TARGET_LIBC_GLIBC &&
+ musl->libc == KIT_TARGET_LIBC_MUSL,
+ "registry distinguishes Linux libc profiles");
+ EXPECT(kit_target_profile_lookup("wasm32-wasi") != NULL,
+ "compiled-in Wasm profile is registered");
+ EXPECT(kit_target_profile_lookup("definitely-not-a-target") == NULL,
+ "unknown profile is absent");
+ EXPECT(KIT_TARGET_PROFILE_REGISTRY_VERSION == 1u,
+ "registry schema version is pinned");
+}
+
+static void* hosted_test_alloc(void* user, size_t size) {
+ (void)user;
+ return malloc(size);
+}
+
+static void hosted_test_free(void* user, void* ptr, size_t size) {
+ (void)user;
+ (void)size;
+ free(ptr);
+}
+
+static int hosted_test_path_exists(void* user, const char* path) {
+ (void)user;
+ (void)path;
+ return 0;
+}
+
+static void hosted_test_clobber_stack(void) {
+ volatile unsigned char bytes[256];
+ size_t i;
+ for (i = 0; i < sizeof bytes; ++i) bytes[i] = 0xa5u;
+}
+
+static void check_android_api_define_lifetime(void) {
+ static const KitOsHostedHost host = {
+ .path_exists = hosted_test_path_exists,
+ .alloc = hosted_test_alloc,
+ .free = hosted_test_free,
+ };
+ const KitOsImpl* os = kit_os_lookup(KIT_OS_ANDROID);
+ KitOsHostedRequest req;
+ KitOsHostedDirs dirs;
+ KitOsHostedPlan plan;
+ uint32_t i;
+ int found = 0;
+
+ memset(&req, 0, sizeof req);
+ memset(&dirs, 0, sizeof dirs);
+ memset(&plan, 0, sizeof plan);
+ req.host = &host;
+ req.tool = "target_test";
+ req.target = target_spec(KIT_ARCH_ARM_64, KIT_OS_ANDROID, KIT_OBJ_ELF);
+ req.target.os_version_major = 24;
+ dirs.host = &host;
+ dirs.libdirs[0] = (char*)"/ndk/lib";
+ dirs.nlibdirs = 1;
+
+ EXPECT(os && os->hosted && os->hosted->resolve,
+ "Android hosted resolver is registered");
+ if (!os || !os->hosted || !os->hosted->resolve) return;
+ EXPECT(os->hosted->resolve(&req, &dirs, &plan) == 0,
+ "Android hosted plan resolves");
+ hosted_test_clobber_stack();
+ for (i = 0; i < plan.ndefines; ++i) {
+ if (plan.defines[i].name.len == strlen("__ANDROID_API__") &&
+ memcmp(plan.defines[i].name.s, "__ANDROID_API__",
+ strlen("__ANDROID_API__")) == 0) {
+ found = 1;
+ EXPECT(plan.defines[i].body.len == 2 &&
+ memcmp(plan.defines[i].body.s, "24", 2) == 0,
+ "Android API define owns the exact resolved API integer");
+ }
+ }
+ EXPECT(found, "Android API define is present");
+ kit_os_hosted_plan_fini(&host, NULL, &plan);
+}
+
static void check_errors(void) {
KitTargetSpec spec = target_spec(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF);
KitTargetFeature bad_feature[] = {
@@ -281,6 +399,8 @@ int main(void) {
check_x64_defaults_and_isa();
check_rv64_isa_and_overrides();
check_wasm_features();
+ check_target_profile_registry();
+ check_android_api_define_lifetime();
check_errors();
kit_unit_summary(&g_u, "target_test");
return kit_unit_status(&g_u);