# 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/<namespace>.make/<namespace>.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.
# 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
+# 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)
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)
# 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
#--------------------------------------------------------------------------------
# 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)
@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)