--- /dev/null
+#ifndef ExampleGreet·Greeter·ONCE
+#define ExampleGreet·Greeter·ONCE
+
+#include "Math.lib.c"
+
+void ExampleGreet·Greeter·hello_loop(int count);
+
+#ifdef ExampleGreet·Greeter
+ #include <stdio.h>
+
+ void ExampleGreet·Greeter·hello_loop(int count){
+ for(int TM = 0; TM < count; ++TM){
+ int current_count = ExampleGreet·Math·add(TM ,1);
+ printf("Hello iteration: %d\n" ,current_count);
+ }
+ }
+#endif // ExampleGreet·Greeter
+
+#endif // ExampleGreet·Greeter·ONCE
--- /dev/null
+#ifndef ExampleGreet·Math·ONCE
+#define ExampleGreet·Math·ONCE
+
+int ExampleGreet·Math·add(int a ,int b);
+
+#ifdef ExampleGreet·Math
+ int ExampleGreet·Math·add(int a ,int b){
+ return a + b;
+ }
+#endif // ExampleGreet·Math
+
+#endif // ExampleGreet·Math·ONCE
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "Math.lib.c"
+#include "Greeter.lib.c"
+
+void CLI(void){
+ int base_count = ExampleGreet·Math·add(1 ,2);
+ printf("Calculated base loop count: %d\n" ,base_count);
+ ExampleGreet·Greeter·hello_loop(base_count);
+}
+
+int main(int argc ,char **argv){
+ (void)argc;
+ (void)argv;
+
+ CLI();
+
+ return EXIT_SUCCESS;
+}
+++ /dev/null
-#ifndef ExampleGreet·Greeter·ONCE
-#define ExampleGreet·Greeter·ONCE
-
-#ifndef ExampleGreet·Greeter
- #define ExampleGreet·Greeter
-#endif
-
-#include "Math.lib.c"
-
-void ExampleGreet·Greeter·hello_loop(int count);
-
-#ifdef ExampleGreet·Greeter
-
-#include <stdio.h>
-
-void ExampleGreet·Greeter·hello_loop(int count){
- for(int TM = 0; TM < count; ++TM){
- int current_count = ExampleGreet·Math·add(TM ,1);
- printf("Hello iteration: %d\n" ,current_count);
- }
-}
-
-#endif // ExampleGreet·Greeter
-#endif // ExampleGreet·Greeter·ONCE
+++ /dev/null
-#ifndef ExampleGreet·Math·ONCE
-#define ExampleGreet·Math·ONCE
-
-#ifndef ExampleGreet·Math
- #define ExampleGreet·Math
-#endif
-
-int ExampleGreet·Math·add(int a ,int b);
-
-#ifdef ExampleGreet·Math
-
-int ExampleGreet·Math·add(int a ,int b){
- return a + b;
-}
-
-#endif // ExampleGreet·Math
-#endif // ExampleGreet·Math·ONCE
+++ /dev/null
-#include <stdlib.h>
-#include <stdio.h>
-
-#include "Math.lib.c"
-#include "Greeter.lib.c"
-
-void CLI(void){
- int base_count = ExampleGreet·Math·add(1 ,2);
- printf("Calculated base loop count: %d\n" ,base_count);
- ExampleGreet·Greeter·hello_loop(base_count);
-}
-
-int main(int argc ,char **argv){
- (void)argc;
- (void)argv;
-
- CLI();
-
- return EXIT_SUCCESS;
-}
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <title>C single file modules and namespaces</title>
+ <script src="setup.js"></script>
+ <script>
+ window.StyleRT.include('RT/theme');
+ window.StyleRT.include('RT/layout/article_tech_ref');
+ </script>
+ </head>
+ <body>
+ <RT-article>
+ <RT-title
+ author="Thomas Walker Lynch"
+ date="2026-03-10"
+ title="C single file modules and namespaces">
+ </RT-title>
+
+ <RT-TOC level="1"></RT-TOC>
+
+ <h1>Single file C source</h1>
+ <p>
+ The RT C-culture abandons the traditional separation of declarations into <RT-code>.h</RT-code> header files and definitions into <RT-code>.c</RT-code> source files. Instead, a developer integrates the interface and the implementation into a single file.
+ </p>
+ <p>
+ When a person includes the file using an <RT-code>#include</RT-code> directive, the file exposes only its interface. When the build system compiles the file into an object, a preprocessor macro acts as an implementation gate to compile the definitions. This keeps the source tree clean and ensures the interface and implementation never fall out of synchronization.
+ </p>
+
+ <h1>Ad hoc namespaces</h1>
+ <p>
+ The C language lacks native namespaces. To prevent symbol collisions in large projects, RT code format uses a center dot (<RT-code>·</RT-code>) to denote ad hoc namespaces within identifiers.
+ </p>
+ <p>
+ A programmer maps the directory structure directly to these namespaces. For example, a file located at <RT-code>authored/ExampleGreet/Math.lib.c</RT-code> belongs to the <RT-code>ExampleGreet</RT-code> namespace and defines the <RT-code>Math</RT-code> module.
+ </p>
+ <p>
+ Types and modules use <RT-code>PascalCase</RT-code>, while functions and variables use <RT-code>snake_case</RT-code>. An exported function from this module carries the full namespace and module prefix, such as <RT-code>ExampleGreet·Math·add</RT-code>.
+ </p>
+
+ <h1>The implementation gate</h1>
+ <p>
+ To achieve the single-file source pattern, the code relies on two preprocessor constructs. The entire file is wrapped in an include guard using the <RT-code>·ONCE</RT-code> suffix. This prevents redeclaration errors if the file is included multiple times.
+ </p>
+ <p>
+ The definitions are wrapped in a single <RT-code>#ifdef</RT-code> block using the exact namespace and module name. The build system dynamically injects this macro via a <RT-code>-D</RT-code> compiler flag when building the module's specific object file.
+ </p>
+
+ <h2>Example: Math.lib.c</h2>
+ <p>
+ The following example demonstrates the complete structure.
+ </p>
+
+ <RT-code>
+ #ifndef ExampleGreet·Math·ONCE
+ #define ExampleGreet·Math·ONCE
+
+ int ExampleGreet·Math·add(int a ,int b);
+
+ #ifdef ExampleGreet·Math
+
+ int ExampleGreet·Math·add(int a ,int b){
+ return a + b;
+ }
+
+ #endif // ExampleGreet·Math
+ #endif // ExampleGreet·Math·ONCE
+ </RT-code>
+
+ <h2>Build system mechanics</h2>
+ <p>
+ When a consumer file, such as <RT-code>hello.CLI.c</RT-code>, contains <RT-code>#include "Math.lib.c"</RT-code>, the compiler processes the file without the <RT-code>ExampleGreet·Math</RT-code> macro defined. It skips the implementation and reads only the function prototype.
+ </p>
+ <p>
+ When the orchestrator compiles the library object, it evaluates the target name and explicitly passes <RT-code>-DExampleGreet·Math</RT-code> to the compiler. This unlocks the gate, compiling the machine code for the definitions into <RT-code>Math.lib.o</RT-code>.
+ </p>
+
+ </RT-article>
+ </body>
+</html>
-# developer/tool/makefile — Orchestrator (Hybrid)
+# developer/tool/makefile - Orchestrator (Hybrid)
.SUFFIXES:
.EXPORT_ALL_VARIABLES:
version:
@printf "local ----------------------------------------\n"
@echo tool/makefile version 2.0
- @printf "target_library_CLI.mk ----------------------------------------\n"
- @$(MAKE) -f $(RT_MAKEFILE_DP)/target_kmod.mk version
+ @printf "Harmony.mk ----------------------------------------\n"
+ @$(MAKE) -f $(RT_MAKEFILE_DP)/Harmony.mk version
@printf "target_kmod.mk ----------------------------------------\n"
- @$(MAKE) -f $(RT_MAKEFILE_DP)/target_library_CLI.mk version
+ @$(MAKE) -f $(RT_MAKEFILE_DP)/target_kmod.mk version
.PHONY: information
information:
@echo C_SOURCE_DIR="$(C_SOURCE_DIR)"
@echo KMOD_BUILD_DIR="/lib/modules/$(shell uname -r)/build"
@echo CURDIR="$(CURDIR)"
- @printf "target_library_CLI.mk ----------------------------------------\n"
- @$(MAKE) -f $(RT_MAKEFILE_DP)/target_library_CLI.mk information
+ @printf "Harmony.mk ----------------------------------------\n"
+ @$(MAKE) -f $(RT_MAKEFILE_DP)/Harmony.mk information
@printf "target_kmod.mk ----------------------------------------\n"
@$(MAKE) -f $(RT_MAKEFILE_DP)/target_kmod.mk information
.PHONY: library lib
library lib:
- @$(MAKE) -f $(RT_MAKEFILE_DP)/target_library_CLI.mk library
+ @$(MAKE) -f $(RT_MAKEFILE_DP)/Harmony.mk library
.PHONY: CLI
CLI:
- @$(MAKE) -f $(RT_MAKEFILE_DP)/target_library_CLI.mk CLI
+ @$(MAKE) -f $(RT_MAKEFILE_DP)/Harmony.mk CLI
.PHONY: kmod
kmod:
.PHONY: clean
clean:
@printf "local ----------------------------------------\n"
- @printf "target_library_CLI.mk ----------------------------------------\n"
- @$(MAKE) -f $(RT_MAKEFILE_DP)/target_library_CLI.mk clean
+ @printf "Harmony.mk ----------------------------------------\n"
+ @$(MAKE) -f $(RT_MAKEFILE_DP)/Harmony.mk clean
@printf "target_kmod.mk ----------------------------------------\n"
@$(MAKE) -f $(RT_MAKEFILE_DP)/target_kmod.mk clean
-# tool/shared/Harmony.mk
-# makefile environment variable defaults.
-# cc is the name of the C compiler, a file called <name>.c is C source code.
-# RT uses header integrated C source files, i.e. the source and the header are the same file
+# make/Harmony.mk - build *.lib.c and *.CLI.c
+# written for the Harmony skeleton, always invoked from cwd $REPO_HOME/<role>
+# files have two suffixes by convention, e.g.: X.lib.c or Y.CLI.c
+
+.SUFFIXES:
+.DELETE_ON_ERROR:
+
+#--------------------------------------------------------------------------------
+# Harmony structure
SHELL=/bin/bash
KMOD_CCFLAGS += -include $(REPO_HOME)/shared/tool/makefile/RT_global.h
KMOD_OUTPUT_DIR ?= scratchpad/kmod
+#--------------------------------------------------------------------------------
+# derived variables
+
+# source discovery (single dir)
+c_source_lib := $(wildcard $(C_SOURCE_DIR)/*.lib.c)
+c_source_exec := $(wildcard $(C_SOURCE_DIR)/*.CLI.c)
+
+# remove suffix to get base name
+c_base_lib := $(sort $(patsubst %.lib.c,%, $(notdir $(c_source_lib))))
+c_base_exec := $(sort $(patsubst %.CLI.c,%, $(notdir $(c_source_exec))))
+
+# two sets of object files, one for the lib, and one for the CLI programs
+object_lib := $(patsubst %, $(OBJECT_DIR)/%.lib.o, $(c_base_lib))
+object_exec := $(patsubst %, $(OBJECT_DIR)/%.CLI.o, $(c_base_exec))
+
+# executables are made from exec_ sources
+exec_ := $(patsubst %, $(MACHINE_DIR)/%, $(c_base_exec))
+
+#--------------------------------------------------------------------------------
+# pull in dependencies
+
+-include $(object_lib:.o=.d) $(object_exec:.o=.d)
+
+
+#--------------------------------------------------------------------------------
+# targets
+
+# when no target is given make uses the first target, this one
+.PHONY: usage
+usage:
+ @echo example usage: make clean
+ @echo example usage: make library
+ @echo example usage: make CLI
+ @echo example usage: make library CLI
+
+.PHONY: version
+version:
+ @echo makefile version 8.0
+ if [ ! -z "$(C)" ]; then $(C) -v; fi
+ /bin/make -v
+
+.PHONY: information
+information:
+ @printf "· → Unicode middle dot - visible: [%b]\n" "·"
+ @echo "C_SOURCE_DIR: " $(C_SOURCE_DIR)
+ @echo "BUILD_DIR: " $(BUILD_DIR)
+ @echo "c_source_lib: " $(c_source_lib)
+ @echo "c_source_exec: " $(c_source_exec)
+ @echo "c_base_lib: " $(c_base_lib)
+ @echo "c_base_exec: " $(c_base_exec)
+ @echo "object_lib: " $(object_lib)
+ @echo "object_exec: " $(object_exec)
+ @echo "exec_: " $(exec_)
+
+.PHONY: library
+library: $(LIBRARY_FILE)
+
+$(LIBRARY_FILE): $(object_lib)
+ @mkdir -p $(MACHINE_DIR)
+ @if [ -s "$@" ] || [ -n "$(object_lib)" ]; then \
+ echo "ar rcs $@ $^"; \
+ ar rcs $@ $^; \
+ else \
+ rm -f "$@"; \
+ fi
+
+#.PHONY: CLI
+#CLI: $(LIBRARY_FILE) $(exec_)
+
+.PHONY: CLI
+CLI: library $(exec_)
+
+
+# generally better to use the project local clean scripts, but this will make it so that the make targets can be run again
+
+.PHONY: clean
+clean:
+ rm -f $(LIBRARY_FILE)
+ for obj in $(object_lib) $(object_exec); do rm -f $$obj $${obj%.o}.d || true; done
+ for i in $(exec_); do [ -e $$i ] && rm $$i || true; done
+ rm -rf $(BUILD_DIR)
+
+
+# recipes
+#$(OBJECT_DIR)/%.o: $(C_SOURCE_DIR)/%.c
+# @mkdir -p $(OBJECT_DIR)
+# $(C) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<
+
+$(OBJECT_DIR)/%.o: $(C_SOURCE_DIR)/%.c
+ @mkdir -p $(OBJECT_DIR)
+ $(C) $(CFLAGS) $(if $(findstring .lib,$@),-D$(if $(NAMESPACE),$(NAMESPACE)·,)$(basename $*)) -o $@ -c $<
+
+$(MACHINE_DIR)/%: $(OBJECT_DIR)/%.CLI.o
+ @mkdir -p $(MACHINE_DIR)
+ $(C) -o $@ $< $(LN_FLAGS)
+++ /dev/null
-# make/target_cli_lib.mk — build *.lib.c and *.CLI.c
-# written for the Harmony skeleton, always invoked from cwd $REPO_HOME/<role>
-# files have two suffixes by convention, e.g.: X.lib.c or Y.CLI.c
-
-.SUFFIXES:
-.DELETE_ON_ERROR:
-
-#--------------------------------------------------------------------------------
-# defaults for environment variables
-
-C ?= cc
-CFLAGS ?= -O2 -Wall -Wextra -pedantic
-C_SOURCE_DIR ?= src
-OBJECT_DIR ?= obj
-LIBRARY_DIR ?= lib
-MACHINE_DIR ?= bin
-LN_FLAGS ?= -L$(LIBRARY_DIR) -L/usr/local/lib -L/usr/lib
-
-#--------------------------------------------------------------------------------
-# derived variables
-
-# source discovery (single dir)
-c_source_lib := $(wildcard $(C_SOURCE_DIR)/*.lib.c)
-c_source_exec := $(wildcard $(C_SOURCE_DIR)/*.CLI.c)
-
-# remove suffix to get base name
-c_base_lib := $(sort $(patsubst %.lib.c,%, $(notdir $(c_source_lib))))
-c_base_exec := $(sort $(patsubst %.CLI.c,%, $(notdir $(c_source_exec))))
-
-# two sets of object files, one for the lib, and one for the CLI programs
-object_lib := $(patsubst %, $(OBJECT_DIR)/%.lib.o, $(c_base_lib))
-object_exec := $(patsubst %, $(OBJECT_DIR)/%.CLI.o, $(c_base_exec))
-
-# executables are made from exec_ sources
-exec_ := $(patsubst %, $(MACHINE_DIR)/%, $(c_base_exec))
-
-#--------------------------------------------------------------------------------
-# pull in dependencies
-
--include $(object_lib:.o=.d) $(object_exec:.o=.d)
-
-
-#--------------------------------------------------------------------------------
-# targets
-
-# when no target is given make uses the first target, this one
-.PHONY: usage
-usage:
- @echo example usage: make clean
- @echo example usage: make library
- @echo example usage: make CLI
- @echo example usage: make library CLI
-
-.PHONY: version
-version:
- @echo makefile version 8.0
- if [ ! -z "$(C)" ]; then $(C) -v; fi
- /bin/make -v
-
-.PHONY: information
-information:
- @printf "· → Unicode middle dot — visible: [%b]\n" "·"
- @echo "C_SOURCE_DIR: " $(C_SOURCE_DIR)
- @echo "BUILD_DIR: " $(BUILD_DIR)
- @echo "c_source_lib: " $(c_source_lib)
- @echo "c_source_exec: " $(c_source_exec)
- @echo "c_base_lib: " $(c_base_lib)
- @echo "c_base_exec: " $(c_base_exec)
- @echo "object_lib: " $(object_lib)
- @echo "object_exec: " $(object_exec)
- @echo "exec_: " $(exec_)
-
-.PHONY: library
-library: $(LIBRARY_FILE)
-
-$(LIBRARY_FILE): $(object_lib)
- @mkdir -p $(MACHINE_DIR)
- @if [ -s "$@" ] || [ -n "$(object_lib)" ]; then \
- echo "ar rcs $@ $^"; \
- ar rcs $@ $^; \
- else \
- rm -f "$@"; \
- fi
-
-#.PHONY: CLI
-#CLI: $(LIBRARY_FILE) $(exec_)
-
-.PHONY: CLI
-CLI: library $(exec_)
-
-
-# generally better to use the project local clean scripts, but this will make it so that the make targets can be run again
-
-.PHONY: clean
-clean:
- rm -f $(LIBRARY_FILE)
- for obj in $(object_lib) $(object_exec); do rm -f $$obj $${obj%.o}.d || true; done
- for i in $(exec_); do [ -e $$i ] && rm $$i || true; done
- rm -rf $(BUILD_DIR)
-
-
-# recipes
-#$(OBJECT_DIR)/%.o: $(C_SOURCE_DIR)/%.c
-# @mkdir -p $(OBJECT_DIR)
-# $(C) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<
-
-# The local logic checks if NAMESPACE is defined, and if the target is a .lib.o file
-$(OBJECT_DIR)/%.o: $(C_SOURCE_DIR)/%.c
- @mkdir -p $(OBJECT_DIR)
- $(C) $(CFLAGS) $(if $(and $(NAMESPACE),$(findstring .lib,$@)),-D$(NAMESPACE)·$(basename $*),) -o $@ -c $<
-
-
-$(MACHINE_DIR)/%: $(OBJECT_DIR)/%.CLI.o
- @mkdir -p $(MACHINE_DIR)
- $(C) -o $@ $< $(LN_FLAGS)