kit

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

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:
Adriver/cmd/targets.c | 211+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdriver/lib/target.c | 21+++++++++++++++++++++
Minclude/kit/config.h | 1+
Minclude/kit/target.h | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/api/target.c | 286+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/api/target_test.c | 120+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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);