From e97fe422d7976a52983448685da06ed72db3f042 Mon Sep 17 00:00:00 2001 From: Thomas Walker Lynch Date: Tue, 10 Mar 2026 11:34:51 +0000 Subject: [PATCH] working ExampleGreet with implementation gate --- developer/authored/ExampleGreet/Greeter.lib.c | 19 +++ .../{Greeter => ExampleGreet}/Math.lib.c | 13 +- .../{Greeter => ExampleGreet}/hello.CLI.c | 0 developer/authored/Greeter/Greeter.lib.c | 24 ---- .../C_single_file_modules_and_namespaces.html | 80 ++++++++++++ developer/tool/makefile | 20 +-- shared/tool/makefile/Harmony.mk | 108 +++++++++++++++- shared/tool/makefile/target_library_CLI.mk | 115 ------------------ 8 files changed, 217 insertions(+), 162 deletions(-) create mode 100644 developer/authored/ExampleGreet/Greeter.lib.c rename developer/authored/{Greeter => ExampleGreet}/Math.lib.c (61%) rename developer/authored/{Greeter => ExampleGreet}/hello.CLI.c (100%) delete mode 100644 developer/authored/Greeter/Greeter.lib.c create mode 100644 developer/document/C_single_file_modules_and_namespaces.html delete mode 100644 shared/tool/makefile/target_library_CLI.mk diff --git a/developer/authored/ExampleGreet/Greeter.lib.c b/developer/authored/ExampleGreet/Greeter.lib.c new file mode 100644 index 0000000..ec41cb2 --- /dev/null +++ b/developer/authored/ExampleGreet/Greeter.lib.c @@ -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 + + 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/ExampleGreet/Math.lib.c similarity index 61% rename from developer/authored/Greeter/Math.lib.c rename to developer/authored/ExampleGreet/Math.lib.c index 99dec03..6f1880e 100644 --- a/developer/authored/Greeter/Math.lib.c +++ b/developer/authored/ExampleGreet/Math.lib.c @@ -1,17 +1,12 @@ #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; -} - + 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/ExampleGreet/hello.CLI.c similarity index 100% rename from developer/authored/Greeter/hello.CLI.c rename to developer/authored/ExampleGreet/hello.CLI.c diff --git a/developer/authored/Greeter/Greeter.lib.c b/developer/authored/Greeter/Greeter.lib.c deleted file mode 100644 index 4c9b9db..0000000 --- a/developer/authored/Greeter/Greeter.lib.c +++ /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 - -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/document/C_single_file_modules_and_namespaces.html b/developer/document/C_single_file_modules_and_namespaces.html new file mode 100644 index 0000000..18284b2 --- /dev/null +++ b/developer/document/C_single_file_modules_and_namespaces.html @@ -0,0 +1,80 @@ + + + + + C single file modules and namespaces + + + + + + + + + + +

Single file C source

+

+ The RT C-culture abandons the traditional separation of declarations into .h header files and definitions into .c source files. Instead, a developer integrates the interface and the implementation into a single file. +

+

+ When a person includes the file using an #include 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. +

+ +

Ad hoc namespaces

+

+ The C language lacks native namespaces. To prevent symbol collisions in large projects, RT code format uses a center dot (·) to denote ad hoc namespaces within identifiers. +

+

+ A programmer maps the directory structure directly to these namespaces. For example, a file located at authored/ExampleGreet/Math.lib.c belongs to the ExampleGreet namespace and defines the Math module. +

+

+ Types and modules use PascalCase, while functions and variables use snake_case. An exported function from this module carries the full namespace and module prefix, such as ExampleGreet·Math·add. +

+ +

The implementation gate

+

+ 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 ·ONCE suffix. This prevents redeclaration errors if the file is included multiple times. +

+

+ The definitions are wrapped in a single #ifdef block using the exact namespace and module name. The build system dynamically injects this macro via a -D compiler flag when building the module's specific object file. +

+ +

Example: Math.lib.c

+

+ The following example demonstrates the complete structure. +

+ + + #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 + + +

Build system mechanics

+

+ When a consumer file, such as hello.CLI.c, contains #include "Math.lib.c", the compiler processes the file without the ExampleGreet·Math macro defined. It skips the implementation and reads only the function prototype. +

+

+ When the orchestrator compiles the library object, it evaluates the target name and explicitly passes -DExampleGreet·Math to the compiler. This unlocks the gate, compiling the machine code for the definitions into Math.lib.o. +

+ +
+ + diff --git a/developer/tool/makefile b/developer/tool/makefile index 98d86a0..6c14627 100644 --- a/developer/tool/makefile +++ b/developer/tool/makefile @@ -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 diff --git a/shared/tool/makefile/Harmony.mk b/shared/tool/makefile/Harmony.mk index f46a73d..eaf1ce8 100644 --- a/shared/tool/makefile/Harmony.mk +++ b/shared/tool/makefile/Harmony.mk @@ -1,7 +1,12 @@ -# tool/shared/Harmony.mk -# makefile environment variable defaults. -# cc is the name of the C compiler, a file called .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/ +# 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 index 65b71ad..0000000 --- a/shared/tool/makefile/target_library_CLI.mk +++ /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/ -# 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) -- 2.20.1