#ifndef ST_EXT_ABI_H #define ST_EXT_ABI_H /* * The extension ABI (plan todo 13). * * Everything a language module can do goes through this header. Core * knows NOTHING language-specific: no compiler names, no CC/CFLAGS * variables, nothing C/C++-specific anywhere outside the module files * (lang_c.c / lang_cpp.c and their private shared header). * * Three generic facilities: * 1. a variable registry (st_registry_set_var / st_registry_get_var) * -- the configure generator (todo 16) substitutes from this BY * NAME; CC/CFLAGS/CXX/CXXFLAGS are registered here by the C/C++ * modules, never hardcoded in core; * 2. a language registry (st_ext_register_language + detect); * 3. a check registry (st_ext_register_check). * * Lua-friendliness (todo 19/21): everything is a plain C function * pointer taking an explicitly passed `struct st_ext_ctx *`. There is * NO static state hidden inside the .c files -- a Lua binding only * needs to wrap these functions; the ctx is a userdata it passes back. * Borrowed results stay valid until the next mutation of the ctx (the * registries are linked lists, so node addresses are stable across * registrations). */ #include #include "error.h" /* ---------------- generic variable registry ---------------- */ struct st_registry; /* opaque; owns name/value pairs in insertion order */ struct st_registry *st_registry_new(void); void st_registry_free(struct st_registry *r); /* NULL is a no-op */ /* Set or update a variable. `value == NULL` UNSETS (removes) it. * Returns NULL on success, or a heap st_error on allocation failure. * Values are duplicated. */ struct st_error *st_registry_set_var(struct st_registry *r, const char *name, const char *value); /* Borrowed value, or NULL when unset. */ const char *st_registry_get_var(const struct st_registry *r, const char *name); /* Ordered iteration (insertion order) for the substitution engine. */ size_t st_registry_var_count(const struct st_registry *r); const char *st_registry_var_name(const struct st_registry *r, size_t i); const char *st_registry_var_value(const struct st_registry *r, size_t i); /* ---------------- toolchain result ---------------- */ /* What a successful detection produced. `path` is the command as * invoked (env override value or the candidate that worked); `id` is * derived from the compiler's own `--version` output (vendor name, or * "unknown" when unrecognizable); `version` is likewise ("16.2.1") or * "" when unparsable. All three fields are heap-allocated and owned by * the ctx. */ struct st_toolchain { char *path; char *id; char *version; }; /* ---------------- extension context ---------------- */ struct st_ext_ctx; /* opaque; owns languages, checks, variables */ /* Detects a language's toolchain. MUST fill `out` completely on * success (all three fields freshly strdup'd); on failure it returns a * heap st_error and may leave `out` partially filled -- the ctx frees * partial fields automatically. */ typedef struct st_error *(*st_lang_detect_fn)(struct st_ext_ctx *ctx, void *module_ctx, struct st_toolchain *out); /* A registered language. Borrowed view into the ctx; stable until the * ctx is freed. */ struct st_language { const char *name; /* "c", "cxx", ... (owned by the ctx) */ st_lang_detect_fn detect; /* how to find the toolchain */ void *module_ctx; /* opaque module state, passed back */ const char *const *var_names; /* NULL-terminated vars this language registers on detection (CC, CFLAGS…) */ const struct st_toolchain *toolchain; /* filled on detect; NULL before */ }; /* A registered check kind ("header" is universal; per-language kinds * arrive with todo 11/12). Borrowed view; stable until ctx free. */ struct st_check { const char *language; /* which language this check applies to */ const char *kind; /* "header", "function", ... */ void *probe_spec; /* opaque module data; may be NULL for now */ }; struct st_ext_ctx *st_ext_ctx_new(void); /* NULL on allocation failure */ void st_ext_ctx_free(struct st_ext_ctx *ctx); /* NULL is a no-op */ /* The ctx's variable registry (same instance the language modules * write into; what todo 16 substitutes from). */ struct st_registry *st_ext_var_registry(struct st_ext_ctx *ctx); /* Registers a language. `var_names` is copied (NULL-terminated array). * Returns NULL on success, or an error for bad arguments / duplicates. */ struct st_error *st_ext_register_language(struct st_ext_ctx *ctx, const char *name, st_lang_detect_fn detect, void *module_ctx, const char *const *var_names); size_t st_ext_language_count(const struct st_ext_ctx *ctx); const struct st_language *st_ext_language(const struct st_ext_ctx *ctx, size_t i); /* NULL past end */ const char *st_ext_language_name(const struct st_ext_ctx *ctx, size_t i); const struct st_toolchain *st_ext_language_toolchain( const struct st_ext_ctx *ctx, size_t i); const struct st_toolchain *st_ext_language_toolchain_named( const struct st_ext_ctx *ctx, const char *name); /* Runs the registered detect fn for the named language and stores the * result. Returns NULL on success, or an error (unknown language / * detection failure -- e.g. no compiler found). */ struct st_error *st_ext_detect_language(struct st_ext_ctx *ctx, const char *name); /* Registers a check kind for a language. Duplicate (language, kind) * pairs are errors. Returns NULL on success. */ struct st_error *st_ext_register_check(struct st_ext_ctx *ctx, const char *language, const char *kind, void *probe_spec); size_t st_ext_check_count(const struct st_ext_ctx *ctx); const struct st_check *st_ext_check(const struct st_ext_ctx *ctx, size_t i); /* NULL past end */ const char *st_ext_check_language(const struct st_ext_ctx *ctx, size_t i); const char *st_ext_check_kind(const struct st_ext_ctx *ctx, size_t i); void *st_ext_check_probe_spec(const struct st_ext_ctx *ctx, size_t i); /* ---------------- builtin module registry ---------------- */ /* Runs the builtin module entry points (lang_c, lang_cpp). The modules * register themselves through the generic ABI above -- the table here * names entry points only; every compiler string lives in the modules. * Returns NULL on success. */ struct st_error *st_ext_init_builtins(struct st_ext_ctx *ctx); /* ---------------- process helper for module authors ---------------- */ /* Runs argv (NULL-terminated; argv[0] looked up via execvp on PATH) * and captures stdout+stderr into *out (malloc'd; caller frees; may be * NULL to discard) with *out_len. Returns the child's exit status * (0-255), or -1 when it could not be spawned (e.g. ENOENT) or was * killed by a signal. No timeout: intended for short commands like * `cc --version`. */ int st_ext_run_capture(char *const argv[], char **out, size_t *out_len); #endif /* ST_EXT_ABI_H */