working ExampleGreet with implementation gate
authorThomas Walker Lynch <eknp9n@reasoningtechnology.com>
Tue, 10 Mar 2026 11:34:51 +0000 (11:34 +0000)
committerThomas Walker Lynch <eknp9n@reasoningtechnology.com>
Tue, 10 Mar 2026 11:34:51 +0000 (11:34 +0000)
developer/authored/ExampleGreet/Greeter.lib.c [new file with mode: 0644]
developer/authored/ExampleGreet/Math.lib.c [new file with mode: 0644]
developer/authored/ExampleGreet/hello.CLI.c [new file with mode: 0644]
developer/authored/Greeter/Greeter.lib.c [deleted file]
developer/authored/Greeter/Math.lib.c [deleted file]
developer/authored/Greeter/hello.CLI.c [deleted file]
developer/document/C_single_file_modules_and_namespaces.html [new file with mode: 0644]
developer/tool/makefile
shared/tool/makefile/Harmony.mk
shared/tool/makefile/target_library_CLI.mk [deleted file]

diff --git a/developer/authored/ExampleGreet/Greeter.lib.c b/developer/authored/ExampleGreet/Greeter.lib.c
new file mode 100644 (file)
index 0000000..ec41cb2
--- /dev/null
@@ -0,0 +1,19 @@
+#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
diff --git a/developer/authored/ExampleGreet/Math.lib.c b/developer/authored/ExampleGreet/Math.lib.c
new file mode 100644 (file)
index 0000000..6f1880e
--- /dev/null
@@ -0,0 +1,12 @@
+#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
diff --git a/developer/authored/ExampleGreet/hello.CLI.c b/developer/authored/ExampleGreet/hello.CLI.c
new file mode 100644 (file)
index 0000000..8427efb
--- /dev/null
@@ -0,0 +1,20 @@
+#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;
+}
diff --git a/developer/authored/Greeter/Greeter.lib.c b/developer/authored/Greeter/Greeter.lib.c
deleted file mode 100644 (file)
index 4c9b9db..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-#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
diff --git a/developer/authored/Greeter/Math.lib.c b/developer/authored/Greeter/Math.lib.c
deleted file mode 100644 (file)
index 99dec03..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#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
diff --git a/developer/authored/Greeter/hello.CLI.c b/developer/authored/Greeter/hello.CLI.c
deleted file mode 100644 (file)
index 8427efb..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#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;
-}
diff --git a/developer/document/C_single_file_modules_and_namespaces.html b/developer/document/C_single_file_modules_and_namespaces.html
new file mode 100644 (file)
index 0000000..18284b2
--- /dev/null
@@ -0,0 +1,80 @@
+<!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>
index 98d86a0..6c14627 100644 (file)
@@ -1,4 +1,4 @@
-# developer/tool/makefile  Orchestrator (Hybrid)
+# developer/tool/makefile - Orchestrator (Hybrid)
 .SUFFIXES:
 .EXPORT_ALL_VARIABLES:
 
@@ -19,10 +19,10 @@ usage:
 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:
@@ -33,8 +33,8 @@ 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
 
@@ -43,11 +43,11 @@ all: library CLI kmod
 
 .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:
@@ -56,7 +56,7 @@ 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
index f46a73d..eaf1ce8 100644 (file)
@@ -1,7 +1,12 @@
-# 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
 
@@ -33,3 +38,98 @@ KMOD_CCFLAGS     ?= -I $(KMOD_SOURCE_DIR)
 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)
diff --git a/shared/tool/makefile/target_library_CLI.mk b/shared/tool/makefile/target_library_CLI.mk
deleted file mode 100644 (file)
index 65b71ad..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-# 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)