diff --git a/scripts/nmk/.gitignore b/scripts/nmk/.gitignore new file mode 100644 index 000000000..9166105d2 --- /dev/null +++ b/scripts/nmk/.gitignore @@ -0,0 +1,3 @@ +*.swp +*.swo +.git-ignore diff --git a/scripts/nmk/Documentation/Makefile b/scripts/nmk/Documentation/Makefile new file mode 100644 index 000000000..981449979 --- /dev/null +++ b/scripts/nmk/Documentation/Makefile @@ -0,0 +1,42 @@ +ASCIIDOC := asciidoc +A2X := a2x +XMLTO := xmlto +PS2PDF := ps2pdf + +SRC += nmk.txt +XMLS := $(patsubst %.txt,%.xml,$(SRC)) +MANS := $(patsubst %.txt,%.8,$(SRC)) + +GROFF := groff +PAPER := $(shell paperconf 2>/dev/null || echo letter) +GROFF_OPTS := -Tps -t -dpaper=$(PAPER) -P-p$(PAPER) -man -msafer -rC1 -rD1 -rS11 +PSS := $(MANS:%.8=%.ps) +PDFS := $(MANS:%.8=%.pdf) + +ps: $(PSS) +pdf: $(PDFS) +all: check $(MANS) + +.PHONY: all ps pdf check clean + +check: + $(Q) for B in $(ASCIIDOC) $(A2X) $(XMLTO); do \ + $$B --version > /dev/null || exit 1; \ + done + +%.8: %.txt + $(call msg-gen, $@) + $(Q) $(ASCIIDOC) -b docbook -d manpage -o $(patsubst %.8,%.xml,$@) $< + $(Q) $(XMLTO) man --skip-validation $(patsubst %.8,%.xml,$@) 2>/dev/null + +%.ps: %.8 + $(call msg-gen, $@) + $(Q) $(GROFF) $(GROFF_OPTS) $^ > $@ + +%.pdf: %.ps + $(call msg-gen, $@) + $(Q) $(PS2PDF) $< $@ + +clean: + $(call msg-clean, "docs") + $(Q) $(RM) $(XMLS) $(MANS) $(PSS) $(PDFS) diff --git a/scripts/nmk/Documentation/nmk.txt b/scripts/nmk/Documentation/nmk.txt new file mode 100644 index 000000000..8e97864d0 --- /dev/null +++ b/scripts/nmk/Documentation/nmk.txt @@ -0,0 +1,70 @@ +nmk(8) +====== + +NAME +---- +nmk - a framework to minimize Makefile code needed for simple projects + + +SYNOPSIS +-------- +*make* -f main.mk makefile=Makefile obj= + + +OVERVIEW +-------- +Most of projects have similar source code structure: + + * Toplevel 'Makefile' + * Source code itself in directory '' + * Headers are gathered into directory '' + +so that building procedure is invoking *make* to read toplevel 'Makefile', +compile sources and link a final executable program. Taking this into account +*nmk* is trying to minimize efforts needed to write 'Makefile'. + + +USAGE +----- +First of all the *nmk* scripts are to be placed into some known place so the +*make* would be able to read them from a command line. Internally *nmk* uses +*__nmk_dir* variable to find own sources. Thus one can export + +---------- + export __nmk_dir=/ +---------- + +in a makefile or do it via environment variables. Note the ending slash is mandatory. + +As been mentioned earlier source code tree should include toplevel 'Makefile' +and source code in '' directory. Source code '' should provide own +'Makefile' (secondlevel) where files to be compiled are enumerated. + +A typical source code tree will look like + +---------- + Makefile # toplevel Makefile + # directory with nmk scripts + # source code directory + Makefile # secondlevel Makefile + src1.c # source code + src2.c + ... +---------- + +In toplevel 'Makefile' we should plug in *nmk* itself + +---------- + export __nmk_dir=scripts/ + include $(__nmk_dir)include.mk +---------- + +In secondlevel 'Makefile' we should enumerate files to be compiled. + +---------- + obj-y += src1.o + obj-y += src2.o + ... +---------- + +That is basically all one need to build a program. diff --git a/scripts/nmk/Makefile b/scripts/nmk/Makefile new file mode 100644 index 000000000..d9885c23a --- /dev/null +++ b/scripts/nmk/Makefile @@ -0,0 +1,40 @@ +__nmk_dir=scripts/ +export __nmk_dir + +include $(__nmk_dir)include.mk + +MAKEFLAGS := -r -R --no-print-directory + +.PHONY: all help test docs clean install + +help: + @echo ' Targets:' + @echo ' install dir= - Install scripts into directory ' + @echo ' docs - Build documentation' + @echo ' clean - Clean everything' + +test: + $(Q) $(MAKE) -C tests all + +docs: + $(Q) $(MAKE) -C Documentation all + +install: + @echo 'Copying scripts into $(dir)' + @cp scripts/build.mk $(dir) + @cp scripts/include.mk $(dir) + @cp scripts/macro.mk $(dir) + @cp scripts/main.mk $(dir) + @cp scripts/rules.mk $(dir) + @cp scripts/tools.mk $(dir) + @cp scripts/utils.mk $(dir) + +all: + @true + +clean: + $(call msg-clean, "nmk") + $(Q) $(MAKE) -C Documentation clean + $(Q) $(MAKE) -C tests clean + +.DEFAULT_GOAL ?= all diff --git a/scripts/nmk/README.md b/scripts/nmk/README.md new file mode 100644 index 000000000..3c0b570b9 --- /dev/null +++ b/scripts/nmk/README.md @@ -0,0 +1,5 @@ +NMK +=== + +NMK stands for NetMaKe -- is a very simple framework for make build system. +Most ideas are taken from the Linux kernel kbuild system. diff --git a/scripts/nmk/scripts/build.mk b/scripts/nmk/scripts/build.mk new file mode 100644 index 000000000..d976390cc --- /dev/null +++ b/scripts/nmk/scripts/build.mk @@ -0,0 +1,184 @@ +ifndef ____nmk_defined__build + +# +# General helpers for simplified Makefiles. +# +src := $(obj) +obj-y := +lib-y := +target := +deps-y := +all-y := +builtin-name := +lib-name := +ld_flags := +cleanup-y := + +MAKECMDGOALS := $(call uniq,$(MAKECMDGOALS)) + +ifndef obj + $(error obj is undefined) +endif + +include $(call objectify,$(makefile)) + +ifneq ($(strip $(target)),) + target := $(sort $(call uniq,$(target))) +endif + +# +# Prepare the unique entries. +obj-y := $(sort $(call uniq,$(obj-y))) +lib-y := $(filter-out $(obj-y),$(sort $(call uniq,$(lib-y)))) + +# +# Add subdir path +obj-y := $(call objectify,$(obj-y)) +lib-y := $(call objectify,$(lib-y)) + +# +# Strip custom names. +lib-name := $(strip $(lib-name)) +builtin-name := $(strip $(builtin-name)) + +# +# Link flags. +ld_flags := $(strip $(LDFLAGS) $(ldflags-y)) + +# +# Prepare targets. +ifneq ($(lib-y),) + lib-target := + ifneq ($(lib-name),) + lib-target := $(obj)/$(lib-name) + else + lib-target := $(obj)/lib.a + endif + cleanup-y += $(lib-target) + all-y += $(lib-target) +endif + +ifneq ($(obj-y),) + builtin-target := + ifneq ($(builtin-name),) + builtin-target := $(obj)/$(builtin-name) + else + builtin-target := $(obj)/built-in.o + endif + cleanup-y += $(builtin-target) + all-y += $(builtin-target) +endif + +# +# Helpers for targets. +define gen-ld-target-rule +$(1): $(3) + $$(call msg-link, $$@) + $$(Q) $$(LD) $(2) -r -o $$@ $(4) +endef + +define gen-ar-target-rule +$(1): $(3) + $$(call msg-link, $$@) + $$(Q) $$(AR) -rcs$(2) $$@ $(4) +endef + +# +# Predefined (builtins) targets rules. +ifdef builtin-target + $(eval $(call gen-ld-target-rule, \ + $(builtin-target), \ + $(ld_flags), \ + $(obj-y),$(obj-y) $(call objectify,$(obj-e)))) +endif + +ifdef lib-target + $(eval $(call gen-ar-target-rule, \ + $(lib-target), \ + $(ARFLAGS) $(arflags-y), \ + $(lib-y),$(lib-y) $(call objectify,$(lib-e)))) +endif + +# +# Custom targets rules. +define gen-custom-target-rule + ifneq ($($(1)-obj-y),) + $(eval $(call gen-ld-target-rule, \ + $(obj)/$(1).built-in.o, \ + $(ld_flags) $($(1)-ldflags-y), \ + $(call objectify,$($(1)-obj-y)), \ + $(call objectify,$($(1)-obj-y)) \ + $(call objectify,$($(1)-obj-e)))) + all-y += $(obj)/$(1).built-in.o + cleanup-y += $(obj)/$(1).built-in.o + endif + ifneq ($($(1)-lib-y),) + $(eval $(call gen-ar-target-rule, \ + $(obj)/$(1).lib.a, \ + $(ARFLAGS) $($(1)-arflags-y), \ + $(call objectify,$($(1)-lib-y)) \ + $(call objectify,$($(1)-lib-e)), \ + $(call objectify,$($(1)-lib-y)))) + all-y += $(obj)/$(1).lib.a + cleanup-y += $(obj)/$(1).lib.a + endif +endef + +$(foreach t,$(target),$(eval $(call gen-custom-target-rule,$(t)))) + +# +# Figure out if the target we're building needs deps to include. +define collect-deps + ifneq ($(filter-out %.d,$(1)),) + ifneq ($(filter %.o %.i %.s,$(1)),) + deps-y += $(addsuffix .d,$(basename $(1))) + endif + endif + ifeq ($(builtin-target),$(1)) + deps-y += $(obj-y:.o=.d) + endif + ifeq ($(lib-target),$(1)) + deps-y += $(lib-y:.o=.d) + endif + ifneq ($(filter all $(all-y) $(target),$(1)),) + deps-y += $(obj-y:.o=.d) + deps-y += $(lib-y:.o=.d) + deps-y += $(foreach t,$(target),$($(t)-lib-y:.o=.d) $($(t)-obj-y:.o=.d)) + endif +endef + +ifneq ($(MAKECMDGOALS),) + ifneq ($(MAKECMDGOALS),clean) + $(foreach goal,$(MAKECMDGOALS),$(eval $(call collect-deps,$(goal)))) + deps-y := $(call uniq,$(deps-y)) + ifneq ($(deps-y),) + $(eval -include $(deps-y)) + endif + endif +endif + +# +# Main phony rule. +all: $(all-y) + @true +.PHONY: all + +# +# List of object files. +objlist: + $(E) $(obj-y) $(lib-y) +.PHONY: objlist + +# +# Clean everything up. +clean: + $(call msg-clean, $(obj)) + $(Q) $(RM) $(obj)/*.o $(obj)/*.d $(obj)/*.i $(obj)/*.s $(cleanup-y) +.PHONY: clean + +# +# Footer. +$(__nmk_dir)scripts/build.mk: + @true +____nmk_defined__build = y +endif diff --git a/scripts/nmk/scripts/include.mk b/scripts/nmk/scripts/include.mk new file mode 100644 index 000000000..6216fead9 --- /dev/null +++ b/scripts/nmk/scripts/include.mk @@ -0,0 +1,100 @@ +ifndef ____nmk_defined__include + +# +# Silent make rules. +ifeq ($(strip $(V)),) + E := @echo + Q := @ +else + E := @\# + Q := +endif + +export E Q + +# +# Message helpers. +define msg-gen + $(E) " GEN " $(1) +endef + +define msg-clean + $(E) " CLEAN " $(1) +endef + +define msg-cc + $(E) " CC " $(1) +endef + +define msg-dep + $(E) " DEP " $(1) +endef + +define msg-link + $(E) " LINK " $(1) +endef + +define msg-ar + $(E) " AR " $(1) +endef + +define msg-build + $(E) " BUILD " $(1) +endef + +# +# Common vars. +SUBARCH := $(shell uname -m | sed \ + -e s/i.86/x86/ \ + -e s/x86_64/x86/ \ + -e s/sun4u/sparc64/ \ + -e s/arm.*/arm/ \ + -e s/sa110/arm/ \ + -e s/s390x/s390/ \ + -e s/parisc64/parisc/ \ + -e s/ppc.*/powerpc/ \ + -e s/mips.*/mips/ \ + -e s/sh[234].*/sh/ \ + -e s/aarch64.*/arm64/) + +ARCH ?= $(SUBARCH) +SRCARCH := $(ARCH) + +export SUBARCH ARCH SRCARCH + +ifndef ____nmk_defined__tools + include $(__nmk_dir)tools.mk +endif + +# Do not use make's built-in rules and variables +# (this increases performance and avoids hard-to-debug behaviour). +MAKEFLAGS += -rR --no-print-directory +export MAKEFLAGS + +# Avoid funny character set dependencies. +unexport LC_ALL +LC_COLLATE=C +LC_NUMERIC=C +export LC_COLLATE LC_NUMERIC + +# Avoid interference with shell env settings. +unexport GREP_OPTIONS + +# Shorthand for build. +build := -r -R -f $(__nmk_dir)main.mk makefile=Makefile obj +export build + +# With specified Makefile +build-as := -r -R -f $(__nmk_dir)main.mk makefile=$$(1) obj=$$(2) +export build-as + +# +# Dummy target for force building. +FORCE: ; + +# +# Footer. +$(__nmk_dir)scripts/include.mk: + @true +____nmk_defined__include = y +endif diff --git a/scripts/nmk/scripts/macro.mk b/scripts/nmk/scripts/macro.mk new file mode 100644 index 000000000..be6bf0a5e --- /dev/null +++ b/scripts/nmk/scripts/macro.mk @@ -0,0 +1,27 @@ +ifndef ____nmk_defined__macro + +# +# Helper to include makefile only once. +# +define include-once + ifndef $(join ____nmk_defined__,$(1:.mk=)) + include $(__nmk_dir)$(1) + endif +endef + +# Helper to build built-in target in directory. +# $(eval $(call gen-built-in,)) +define gen-built-in +$(1)/%: + $$(Q) $$(MAKE) $$(build)=$(1) $$@ +$(1): + $$(Q) $$(MAKE) $$(build)=$(1) all +$(1)/built-in.o: $(1) +endef + +# +# Footer. +$(__nmk_dir)scripts/macro.mk: + @true +____nmk_defined__macro = y +endif diff --git a/scripts/nmk/scripts/main.mk b/scripts/nmk/scripts/main.mk new file mode 100644 index 000000000..7c86f95e7 --- /dev/null +++ b/scripts/nmk/scripts/main.mk @@ -0,0 +1,31 @@ +ifndef ____nmk_defined__main + +# +# Genaral inclusion statement + +ifndef ____nmk_defined__include + include $(__nmk_dir)include.mk +endif + +ifndef ____nmk_defined__macro + include $(__nmk_dir)macro.mk +endif + +# +# Anything else might be included with +# +# $(eval $(call include-once,)) +# +# Note the order does matter! + +$(eval $(call include-once,tools.mk)) +$(eval $(call include-once,utils.mk)) +$(eval $(call include-once,rules.mk)) +$(eval $(call include-once,build.mk)) + +# +# Footer +$(__nmk_dir)scripts/main.mk: + @true +____nmk_defined__main = y +endif diff --git a/scripts/nmk/scripts/rules.mk b/scripts/nmk/scripts/rules.mk new file mode 100644 index 000000000..e589e4499 --- /dev/null +++ b/scripts/nmk/scripts/rules.mk @@ -0,0 +1,66 @@ +ifndef ____nmk_defined__rules + +# +# Accumulate common flags. +define nmk-ccflags + $(CFLAGS) $(ccflags-y) $(CFLAGS_$@) +endef + +define nmk-asflags + $(CFLAGS) $(ASFLAGS) $(asflags-y) $(ASFLAGS_$@) +endef + +# +# General rules. +define gen-rule-o-from-c-by-name +$(1).o: $(2).c + $$(call msg-cc, $$@) + $$(Q) $$(CC) -c $$(strip $$(nmk-ccflags)) $$< -o $$@ +endef +define gen-rule-i-from-c-by-name +$(1).i: $(2).c + $$(call msg-cc, $$@) + $$(Q) $$(CC) -E $$(strip $$(nmk-ccflags)) $$< -o $$@ +endef +define gen-rule-s-from-c-by-name +$(1).s: $(2).c + $$(call msg-cc, $$@) + $$(Q) $$(CC) -S -fverbose-asm $$(strip $$(nmk-ccflags)) $$< -o $$@ +endef +define gen-rule-o-from-S-by-name +$(1).o: $(2).S + $$(call msg-cc, $$@) + $$(Q) $$(CC) -c $$(strip $$(nmk-asflags)) $$< -o $$@ +endef +define gen-rule-d-from-c-by-name +$(1).d: $(2).c + $$(call msg-dep, $$@) + $$(Q) $$(CC) -M -MT $$@ -MT $$(patsubst %.d,%.o,$$@) $$(strip $$(nmk-ccflags)) $$< -o $$@ +endef +define gen-rule-d-from-S-by-name +$(1).d: $(2).S + $$(call msg-dep, $$@) + $$(Q) $$(CC) -M -MT $$@ -MT $$(patsubst %.d,%.o,$$@) $$(strip $$(nmk-asflags)) $$< -o $$@ +endef +define gen-rule-i-from-S-by-name +$(1).i: $(2).S + $$(call msg-cc, $$@) + $$(Q) $$(CC) -E $$(strip $$(nmk-asflags)) $$< -o $$@ +endef + +# +# Expand early which matched all implicits. +$(eval $(call gen-rule-o-from-c-by-name,%,%)) +$(eval $(call gen-rule-i-from-c-by-name,%,%)) +$(eval $(call gen-rule-s-from-c-by-name,%,%)) +$(eval $(call gen-rule-o-from-S-by-name,%,%)) +$(eval $(call gen-rule-d-from-c-by-name,%,%)) +$(eval $(call gen-rule-d-from-S-by-name,%,%)) +$(eval $(call gen-rule-i-from-S-by-name,%,%)) + +# +# Footer. +$(__nmk_dir)scripts/rules.mk: + @true +____nmk_defined__rules = y +endif diff --git a/scripts/nmk/scripts/tools.mk b/scripts/nmk/scripts/tools.mk new file mode 100644 index 000000000..6e7ba6725 --- /dev/null +++ b/scripts/nmk/scripts/tools.mk @@ -0,0 +1,32 @@ +ifndef ____nmk_defined__tools + +# +# System tools shorthands +RM := rm -f +LD := $(CROSS_COMPILE)ld +CC := $(CROSS_COMPILE)gcc +CPP := $(CC) -E +AS := $(CROSS_COMPILE)as +AR := $(CROSS_COMPILE)ar +STRIP := $(CROSS_COMPILE)strip +OBJCOPY := $(CROSS_COMPILE)objcopy +OBJDUMP := $(CROSS_COMPILE)objdump +NM := $(CROSS_COMPILE)nm +MAKE := make +MKDIR := mkdir -p +AWK := awk +PERL := perl +PYTHON := python +SH := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \ + else if [ -x /bin/bash ]; then echo /bin/bash; \ + else echo sh; fi ; fi) + +export RM LD CC CPP AS AR STRIP OBJCOPY OBJDUMP +export NM SH MAKE MKDIR AWK PERL PYTHON SH + +# +# Footer. +$(__nmk_dir)scripts/tools.mk: + @true +____nmk_defined__tools = y +endif diff --git a/scripts/nmk/scripts/utils.mk b/scripts/nmk/scripts/utils.mk new file mode 100644 index 000000000..50ceb7af5 --- /dev/null +++ b/scripts/nmk/scripts/utils.mk @@ -0,0 +1,28 @@ +ifndef ____nmk_defined__utils + +# +# Usage: option = $(call try-cc,source-to-build,cc-options,cc-defines) +try-cc = $(shell sh -c \ + 'TMP="$(OUTPUT)$(TMPOUT).$$$$"; \ + echo "$(1)" | \ + $(CC) $(3) -x c - $(2) -o "$$TMP" > /dev/null 2>&1 && echo y; \ + rm -f "$$TMP"') + +# pkg-config-check +# Usage: ifeq ($(call pkg-config-check, library),y) +pkg-config-check = $(shell sh -c 'pkg-config $(1) && echo y') + +# +# Remove duplicates. +uniq = $(strip $(if $1,$(firstword $1) $(call uniq,$(filter-out $(firstword $1),$1)))) + +# +# Add $(obj)/ for paths that are not relative +objectify = $(foreach o,$(sort $(call uniq,$(1))),$(if $(filter /% ./% ../%,$(o)),$(o),$(obj)/$(o))) + +# +# Footer. +$(__nmk_dir)scripts/utils.mk: + @true +____nmk_defined__utils = y +endif