jetc: rename --shared build output convention from .so to .jtc

Purely a naming-convention change per user request, not a format
change - the output is still a real ELF shared object (-shared -fPIC),
still dlopen()-able exactly as before. .jtc ("jet compiled") marks it as
an APIServer-loadable content module rather than a generic library.

build-shared.sh: usage text and STEM derivation
(basename "$OUTPUT" .so -> .jtc) both updated - the STEM change is the
one functional piece here, everything else in this commit is comments/
usage strings. Doc comments in jetc.cpp/JetCodegen.h/host_test.cpp/
api_test.cpp updated to match.

Verified live: rebuilt jetc and all five demo*.jtc modules, reran
api_test/host_test against demo3.jtc (still resolves jetc_api_demo3/
jetc_render_demo3 and renders correctly) - the extension is purely
cosmetic to dlopen(), as expected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Brad ArantandClaude Sonnet 5 committed 2026-08-25 11:41:44 -07:00
1 parent 3f273b0a95
commit 9bbb95cc57
6 files changed
+28 -22

No files matched your search

+3 -3
View File
@@ -10,7 +10,7 @@ namespace jetc {
// One `#api METHOD /path` directive from the top of the source template
// (see jetc.cpp) - a compile-time-known route this template wants to
// serve once loaded as a .so. Purely descriptive data handed to Codegen;
// serve once loaded as a .jtc. Purely descriptive data handed to Codegen;
// jetc itself never interprets the method/path beyond storing them.
struct ApiRoute {
std::string method;
@@ -34,7 +34,7 @@ namespace jetc {
//
// sharedMode == true: emits no main() at all. Instead the render
// logic is exported as `extern "C" void <entrySymbol>(jet::Global
// &global, coreutils::MString &out)`, meant to be built into a .so
// &global, coreutils::MString &out)`, meant to be built into a .jtc
// (see build-shared.sh) and dlsym()'d by a long-running host server -
// one call renders one request into the caller-owned `out`, using
// whatever Global the host passes in (its lifecycle, cgi/session
@@ -45,7 +45,7 @@ namespace jetc {
// letting coreutils::Exception propagate to the caller) across the
// dlopen() boundary is safe; this is a same-ABI plugin boundary, not
// a foreign C ABI one. entrySymbol should be unique per compiled
// template (see jetc.cpp) so multiple .so's can coexist even if a
// template (see jetc.cpp) so multiple .jtc's can coexist even if a
// host ever dlopen()s them RTLD_GLOBAL instead of keeping separate
// handles.
//
+2 -2
View File
@@ -1,7 +1,7 @@
// Minimal dlsym() check for the jetc_api_<name> route-metadata export added
// alongside jetc_render_<name> in --shared mode (see JetCodegen.cpp). Proves
// a host can discover a compiled module's declared routes purely from the
// .so itself, with no separate manifest file - the actual point of putting
// .jtc itself, with no separate manifest file - the actual point of putting
// routing metadata in an exported symbol instead of a sidecar file.
#include <dlfcn.h>
#include <iostream>
@@ -12,7 +12,7 @@ typedef const JetApiRoute *(*ApiFn)(int *count);
int main(int argc, char **argv) {
if (argc < 3) {
std::cerr << "usage: api_test <module.so> <api-symbol>" << std::endl;
std::cerr << "usage: api_test <module.jtc> <api-symbol>" << std::endl;
return 1;
}
void *handle = dlopen(argv[1], RTLD_NOW);
+10 -8
View File
@@ -1,12 +1,14 @@
#!/bin/bash
# Compiles a .jet file into a shared object exporting a stable, dlsym()-able
# render entry point - `jetc_render_<basename>(jet::Global &global,
# coreutils::MString &out)` - for a long-running server to load and call
# directly instead of spawning a jet-2.0 process per request.
# Compiles a .jet file into a shared object (conventionally named .jtc -
# "jet compiled" - to mark it as an APIServer-loadable content module, not
# a generic .so) exporting a stable, dlsym()-able render entry point -
# `jetc_render_<basename>(jet::Global &global, coreutils::MString &out)`
# - for a long-running server to load and call directly instead of
# spawning a jet-2.0 process per request.
#
# Usage: ./build-shared.sh <input.jet> <output.so>
# Usage: ./build-shared.sh <input.jet> <output.jtc>
#
# The host server links/dlopen()s the .so and calls the exported symbol
# The host server links/dlopen()s the .jtc and calls the exported symbol
# (see the "entry symbol:" line jetc prints) with its own jet::Global -
# this script does not decide that lifecycle, only builds the module.
# coreutils::Exception propagates out of the call uncaught; the host
@@ -18,13 +20,13 @@ CU="$JC/../CoreUtils"
HERE="$JC/jetc"
if [ $# -ne 2 ]; then
echo "usage: $0 <input.jet> <output.so>"
echo "usage: $0 <input.jet> <output.jtc>"
exit 1
fi
INPUT="$1"
OUTPUT="$2"
STEM="$(basename "$OUTPUT" .so)"
STEM="$(basename "$OUTPUT" .jtc)"
if [ ! -x "$HERE/jetc" ]; then
echo "Building jetc..."
Executable → Regular
+4 -4
View File
@@ -1,5 +1,5 @@
// Minimal stand-in for the not-yet-built server: dlopen()s a compiled
// template .so and calls its exported render entry point directly, the
// Minimal stand-in for a server: dlopen()s a compiled
// template .jtc and calls its exported render entry point directly, the
// way a persistent content-provider process would (no process spawn per
// request, no re-parsing the .jet source - both the compile step AND
// the shared-object load happen once, ahead of time).
@@ -14,7 +14,7 @@ typedef void (*RenderFn)(jet::Global &, coreutils::MString &);
int main(int argc, char **argv, char **envp) {
if (argc < 3) {
std::cerr << "usage: host_test <module.so> <symbol>" << std::endl;
std::cerr << "usage: host_test <module.jtc> <symbol>" << std::endl;
return 1;
}
void *handle = dlopen(argv[1], RTLD_NOW);
@@ -31,7 +31,7 @@ int main(int argc, char **argv, char **envp) {
}
// Simulate the host calling the SAME module twice, for two different
// "requests", to prove the .so is reusable across calls without being
// "requests", to prove the .jtc is reusable across calls without being
// reloaded - the actual point of a persistent content provider.
for (int i = 0; i < 2; ++i) {
jet::Global global(envp);
+3 -3
View File
@@ -63,7 +63,7 @@ int main(int argc, char **argv) {
// is stripped - a plain directive line, not a new tag/attribute concept,
// since jetc's parser has no existing notion of the enclosing <jet> tag's
// attributes to hang this off of. Each becomes one route this template
// wants to serve once loaded as a .so (see JetCodegen's apiRoutes).
// wants to serve once loaded as a .jtc (see JetCodegen's apiRoutes).
std::vector<jetc::ApiRoute> apiRoutes;
while (src.rfind("#api ", 0) == 0) {
size_t nl = src.find('\n');
@@ -88,8 +88,8 @@ int main(int argc, char **argv) {
jetc::Codegen codegen;
// Both symbols are derived from the input filename (not the output
// path) so they stay stable and predictable for whatever builds and
// dlsym()'s the resulting .so - unique per template, so multiple
// compiled templates' .so's can coexist in one process even under
// dlsym()'s the resulting .jtc - unique per template, so multiple
// compiled templates' .jtc's can coexist in one process even under
// dlopen(..., RTLD_GLOBAL) instead of requiring isolated handles.
std::string baseIdent = sanitizeIdent(baseNameNoExt(inputPath));
std::string symbol = "jetc_render_" + baseIdent;