From: Thomas Walker Lynch Date: Wed, 11 Nov 2026 06:53:41 +0000 (+0000) Subject: make hierarchy X-Git-Url: https://git.reasoningtechnology.com/?a=commitdiff_plain;h=b7e8d8147f27b791636ef38e139be6af08c8fa65;p=Man-In-Grey make hierarchy --- diff --git a/developer/authored/Man_In_Grey_gate.make/Man_In_Grey_gate.mk b/developer/authored/Man_In_Grey_gate.make/Man_In_Grey_gate.mk new file mode 100644 index 0000000..671ce9d --- /dev/null +++ b/developer/authored/Man_In_Grey_gate.make/Man_In_Grey_gate.mk @@ -0,0 +1,23 @@ +# Man_In_Grey_gate.mk +# +# The settings for building a gate on Man_In_Grey's general gate. Harmony.mk +# reads this file last when it builds this namespace, and stages it with +# libMan_In_Grey.a and Gate.lib.c, so promotion puts all three in +# consumer/Man_In_Grey_gate/. An application builds its own gate by including +# the promoted copy from a .mk beside its gate's .CLI.c, for example: +# +# include $(REPO_HOME)/shared/project-dependency/Man_In_Grey/Man_In_Grey_gate/Man_In_Grey_gate.mk +# +# The directory of this file: the last makefile make has read is the one it +# is reading now, and := takes the value before this file includes anything. +Man_In_Grey_gate_dp := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) + +# A gate runs setuid, so it is built hardened. +CFLAGS += -O2 -fstack-protector-strong -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 +LN_FLAGS += -Wl,-z,relro -Wl,-z,now + +# Where a gate built in another project finds Gate.lib.c and libMan_In_Grey.a. +# In Man_In_Grey's own build this is the source directory, and Harmony.mk +# finds both without it. +CFLAGS += -I $(Man_In_Grey_gate_dp) +LN_FLAGS += -L$(Man_In_Grey_gate_dp) -lMan_In_Grey diff --git a/developer/tool/build_component/make b/developer/tool/build_component/make index 30fb82d..82df53f 100755 --- a/developer/tool/build_component/make +++ b/developer/tool/build_component/make @@ -12,4 +12,6 @@ export KMOD_SOURCE_DIR="authored/$NAMESPACE_DIR" export LIBRARY_DIR="scratchpad/made/$NAMESPACE" export MACHINE_DIR="scratchpad/made/$NAMESPACE" -/bin/make -f tool/build_component/makefile "$CMD" +# Harmony.mk is the makefile run; it includes tool/build_component/makefile, +# the project's settings, and the namespace's *.mk files. +/bin/make -f "$REPO_HOME/shared/tool/makefile/Harmony.mk" "$CMD" diff --git a/developer/tool/build_component/makefile b/developer/tool/build_component/makefile index cba5ea4..ed4ec4a 100644 --- a/developer/tool/build_component/makefile +++ b/developer/tool/build_component/makefile @@ -1,61 +1,20 @@ # developer/tool/build_component/makefile -.SUFFIXES: -.EXPORT_ALL_VARIABLES: - -RT_MAKEFILE_DP := $(REPO_HOME)/shared/tool/makefile - -# the setuid gate is built hardened -ifeq ($(NAMESPACE),Man_In_Grey_gate) - CFLAGS_PROJECT_LOCAL := -O2 -fstack-protector-strong -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 - LN_FLAGS_PROJECT_LOCAL := -Wl,-z,relro -Wl,-z,now -endif - -.PHONY: usage -usage: - @printf "Usage: make [usage|version|information|all|lib|CLI|kmod|clean]\n" - -.PHONY: version -version: - @printf "local ----------------------------------------\n" - @echo tool/makefile version 2.0 - @printf "Harmony.mk ----------------------------------------\n" - @$(MAKE) -f $(RT_MAKEFILE_DP)/Harmony.mk version - @printf "target_kernel-module.mk ----------------------------------------\n" - @$(MAKE) -f $(RT_MAKEFILE_DP)/target_kernel-module.mk version - -.PHONY: information -information: - @printf "local ----------------------------------------\n" - -@echo CURDIR='$(CURDIR)' - @echo REPO_HOME="$(REPO_HOME)" - @echo NAMESPACE="$(NAMESPACE)" - @echo C_SOURCE_DIR="$(C_SOURCE_DIR)" - @echo KMOD_BUILD_DIR="/lib/modules/$(shell uname -r)/build" - @echo CURDIR="$(CURDIR)" - @printf "Harmony.mk ----------------------------------------\n" - @$(MAKE) -f $(RT_MAKEFILE_DP)/Harmony.mk information - @printf "target_kernel-module.mk ----------------------------------------\n" - @$(MAKE) -f $(RT_MAKEFILE_DP)/target_kernel-module.mk information - -.PHONY: all -all: library CLI kmod - -.PHONY: library lib -library lib: - @$(MAKE) -f $(RT_MAKEFILE_DP)/Harmony.mk library - -.PHONY: CLI -CLI: - @$(MAKE) -f $(RT_MAKEFILE_DP)/Harmony.mk CLI - -.PHONY: kmod -kmod: - @$(MAKE) -f $(RT_MAKEFILE_DP)/target_kernel-module.mk kmod - -.PHONY: clean -clean: - @printf "local ----------------------------------------\n" - @printf "Harmony.mk ----------------------------------------\n" - @$(MAKE) -f $(RT_MAKEFILE_DP)/Harmony.mk clean - @printf "target_kernel-module.mk ----------------------------------------\n" - @$(MAKE) -f $(RT_MAKEFILE_DP)/target_kernel-module.mk clean +# +# The project's make settings, for every namespace the project builds with +# make. Harmony.mk includes this file after its own defaults and before the +# namespace's *.mk files, which have the last say. This file holds settings, +# not rules for building: Harmony.mk has those. +# +# Add to a default with +=, replace one with :=, for example: +# +# CFLAGS += -O2 +# LN_FLAGS += -lm +# +# Settings for one namespace go in a .mk file beside its source instead, such +# as authored/.make/.mk. That file is also staged and +# promoted with the namespace's library, so a project built against the +# library reads the same settings. +# +# Harmony.mk's information and clean are double-colon rules, so a recipe +# added here as 'information::' or 'clean::' runs as well as Harmony.mk's. +# Make runs them in the order it read them, so this file's runs first. diff --git a/shared/tool/makefile/Harmony.mk b/shared/tool/makefile/Harmony.mk index 78db1a2..2f95a03 100644 --- a/shared/tool/makefile/Harmony.mk +++ b/shared/tool/makefile/Harmony.mk @@ -1,40 +1,50 @@ # 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 +# files have two suffixes by convention, e.g.: X.lib.c or Y.CLI.c +# +# One make reads three layers, each after the one before, so each can change +# what the layers before it set: +# +# 1. Harmony's defaults, in this file +# 2. the project's settings, tool/build_component/makefile +# 3. the namespace's settings, any *.mk beside its source +# +# A layer adds to a default with += and replaces one with :=. The flags the +# build itself depends on (dependency files, the global header, the source +# directory) are added after the layers, so replacing a default cannot remove +# them. +# +# The layers are read before the derived variables and the rules, because +# make expands those as it reads them. A rule a layer defines is read at that +# point too, so its targets and prerequisites cannot use the derived lists. .SUFFIXES: .DELETE_ON_ERROR: +.DEFAULT_GOAL := usage #-------------------------------------------------------------------------------- -# Harmony structure +# 1. Harmony's defaults -SHELL=/bin/bash +SHELL := /bin/bash ECHO := printf "%b\n" -C_SOURCE_DIR ?= authored -C ?= gcc -CFLAGS ?= -std=gnu11 -Wall -Wextra -Wpedantic -finput-charset=UTF-8 -CFLAGS += -MMD -MP -CFLAGS += -include "$(REPO_HOME)/shared/tool/makefile/RT_global.h" -CFLAGS += -I $(C_SOURCE_DIR) -CFLAGS += $(CFLAGS_PROJECT_LOCAL) - -# Project administrators can override this in their local makefile -LIBRARY_NAME ?= $(PROJECT) -LIBRARY_NAME := $(subst -,_,$(LIBRARY_NAME)) +RT_MAKEFILE_DP := $(REPO_HOME)/shared/tool/makefile -BUILD_DIR ?= scratchpad/build -OBJECT_DIR ?= $(BUILD_DIR)/object +# The build component supplies these on the environment, for the namespace +# being built; ?= keeps its values, and a layer can still replace them. +C_SOURCE_DIR ?= authored LIBRARY_DIR ?= scratchpad/made MACHINE_DIR ?= scratchpad/made -LIBRARY_FILE ?= $(LIBRARY_DIR)/lib$(LIBRARY_NAME).a +C := gcc +CFLAGS := -std=gnu11 -Wall -Wextra -Wpedantic -finput-charset=UTF-8 +LN_FLAGS := -L/lib64 -L/lib -# the project library is linked only when the namespace makes one -LN_FLAGS ?= -L$(LIBRARY_DIR) -L/lib64 -L/lib $(if $(object_lib),-l$(LIBRARY_NAME)) -# LN_FLAGS ?= -L$(LIBRARY_DIR) -L/lib64 -L/lib -l$(LIBRARY_NAME) -LN_FLAGS += $(LN_FLAGS_PROJECT_LOCAL) +LIBRARY_NAME := $(PROJECT) + +BUILD_DIR := scratchpad/build +OBJECT_DIR = $(BUILD_DIR)/object KMOD_SOURCE_DIR ?= authored KMOD_CCFLAGS ?= -I $(KMOD_SOURCE_DIR) @@ -42,9 +52,22 @@ KMOD_CCFLAGS ?= -I $(KMOD_SOURCE_DIR) KMOD_CCFLAGS += -include $(REPO_HOME)/shared/tool/makefile/RT_global.h KMOD_OUTPUT_DIR ?= scratchpad/kmod +#-------------------------------------------------------------------------------- +# 2. the project's settings + +include tool/build_component/makefile + +#-------------------------------------------------------------------------------- +# 3. the namespace's settings + +-include $(wildcard $(C_SOURCE_DIR)/*.mk) + #-------------------------------------------------------------------------------- # derived variables +LIBRARY_NAME := $(subst -,_,$(LIBRARY_NAME)) +LIBRARY_FILE := $(LIBRARY_DIR)/lib$(LIBRARY_NAME).a + # source discovery (single dir) c_source_lib := $(wildcard $(C_SOURCE_DIR)/*.lib.c) c_source_exec := $(wildcard $(C_SOURCE_DIR)/*.CLI.c) @@ -60,6 +83,20 @@ object_exec := $(patsubst %, $(OBJECT_DIR)/%.CLI.o, $(c_base_exec)) # executables are made from exec_ sources exec_ := $(patsubst %, $(MACHINE_DIR)/%, $(c_base_exec)) +# A library's product is its archive, its interface and its settings: in the +# single-file C module the .lib.c is its own header, and a .mk holds what a +# program built against the library needs. Both are staged beside the +# archive, so promotion hands a consumer all three. A namespace that makes no +# library stages neither. +interface_ := $(if $(c_source_lib),$(patsubst $(C_SOURCE_DIR)/%,$(LIBRARY_DIR)/%,$(c_source_lib) $(wildcard $(C_SOURCE_DIR)/*.mk))) + +# the flags the build depends on, after the layers +CFLAGS += -MMD -MP +CFLAGS += -include "$(REPO_HOME)/shared/tool/makefile/RT_global.h" +CFLAGS += -I $(C_SOURCE_DIR) +# the project library is linked only when the namespace makes one +LN_FLAGS += -L$(LIBRARY_DIR) $(if $(object_lib),-l$(LIBRARY_NAME)) + #-------------------------------------------------------------------------------- # pull in dependencies @@ -69,25 +106,31 @@ exec_ := $(patsubst %, $(MACHINE_DIR)/%, $(c_base_exec)) #-------------------------------------------------------------------------------- # 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 + @printf "Usage: make [usage|version|information|all|library|lib|CLI|kmod|clean]\n" .PHONY: version version: - @echo makefile version 8.0 + @echo Harmony.mk version 9.0 if [ ! -z "$(C)" ]; then $(C) -v; fi /bin/make -v + @$(MAKE) --no-print-directory -f $(RT_MAKEFILE_DP)/target_kernel-module.mk version +# information and clean are double-colon rules, so the project's settings or a +# namespace's .mk can add a recipe of its own; each recipe runs. .PHONY: information -information: +information:: @printf "· → Unicode middle dot - visible: [%b]\n" "·" + @echo "REPO_HOME: " $(REPO_HOME) + @echo "NAMESPACE: " $(NAMESPACE) @echo "C_SOURCE_DIR: " $(C_SOURCE_DIR) @echo "BUILD_DIR: " $(BUILD_DIR) + @echo "LIBRARY_DIR: " $(LIBRARY_DIR) + @echo "MACHINE_DIR: " $(MACHINE_DIR) + @echo "makefiles read: " $(MAKEFILE_LIST) + @echo "CFLAGS: " $(CFLAGS) + @echo "LN_FLAGS: " $(LN_FLAGS) @echo "c_source_lib: " $(c_source_lib) @echo "c_source_exec: " $(c_source_exec) @echo "c_base_lib: " $(c_base_lib) @@ -95,40 +138,50 @@ information: @echo "object_lib: " $(object_lib) @echo "object_exec: " $(object_exec) @echo "exec_: " $(exec_) + @echo "interface_: " $(interface_) + @$(MAKE) --no-print-directory -f $(RT_MAKEFILE_DP)/target_kernel-module.mk information + +.PHONY: all +all: library CLI kmod -.PHONY: library -library: $(LIBRARY_FILE) +.PHONY: library lib +library lib: $(LIBRARY_FILE) $(interface_) $(LIBRARY_FILE): $(object_lib) - @mkdir -p $(MACHINE_DIR) + @mkdir -p $(LIBRARY_DIR) @if [ -s "$@" ] || [ -n "$(object_lib)" ]; then \ echo "ar rcs $@ $^"; \ ar rcs $@ $^; \ else \ rm -f "$@"; \ - fi + fi -#.PHONY: CLI -#CLI: $(LIBRARY_FILE) $(exec_) +# each staged interface file depends on its source file of the same name +$(interface_): $(LIBRARY_DIR)/%: $(C_SOURCE_DIR)/% + @mkdir -p $(LIBRARY_DIR) + cp $< $@ .PHONY: CLI CLI: library $(exec_) +# The kernel modules are built by a make of their own, which drives Kbuild. +# It defines targets of the same names as this file, so it is run, not +# included. +.PHONY: kmod +kmod: + @$(MAKE) --no-print-directory -f $(RT_MAKEFILE_DP)/target_kernel-module.mk kmod # 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) +clean:: + rm -f $(LIBRARY_FILE) $(interface_) 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) + @$(MAKE) --no-print-directory -f $(RT_MAKEFILE_DP)/target_kernel-module.mk clean # 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)