# call once recursively so we get rid of default rules and variables ifndef RECURSIVE all: Makefile: ; %: @$(MAKE) -rR --no-print-directory RECURSIVE=1 $(MAKECMDGOALS) else # default target all: libs progs @: .SECONDEXPANSION: # default directories DESTDIR ?= SBINDIR ?= /usr/sbin CONFDIR ?= /etc/$(PACKAGE) MANDIR ?= /usr/share/man DOCDIR ?= /usr/share/doc/$(PACKAGE) STATEDIR ?= /var/run # utilities and default flags for them. CROSS_COMPILE ?= CC := $(CROSS_COMPILE)gcc AR := $(CROSS_COMPILE)ar RANLIB := $(CROSS_COMPILE)ranlib LD := $(CROSS_COMPILE)ld INSTALL := install INSTALLDIR := $(INSTALL) -d CFLAGS := -Wall -Wstrict-prototypes -D_GNU_SOURCE -std=gnu99 CFLAGS_ALL ?= -g -O2 LDFLAGS := $(LDFLAGS) LDFLAGS_ALL ?= -g # some helpers PHONY:=all libs progs allobjdirs arguments-changed build-prologue:=$(TFBUILD)TFbuild.prologue build-epilogue:=$(TFBUILD)TFbuild.epilogue comma:=, squote:=' empty:= space:=$(empty) $(empty) separator:=-- ifdef V ifeq ("$(origin V)", "command line") VERBOSE = $(V) endif endif ifndef VERBOSE VERBOSE = 0 endif ifeq ($(VERBOSE),1) quiet = Q = else quiet=quiet_ Q = @ endif ifneq ($(findstring s,$(MAKEFLAGS)),) quiet=silent_ endif # recursion related stuff srctree := objtree := obj/ current-dir := current-dirc := recursion-level = $(words $(current-dirc)) next-recursion-level = $(words $(current-dirc) dummy) objdir = $(objtree)$(current-dir) # globals all-progs := all-libs := local-vars := CFLAGS LDFLAGS LIBS # helper macros for targets escsq = $(subst $(squote),'\$(squote)',$1) printable-target = $(patsubst $(objtree)%,%,$@) depfile = $(subst $(comma),_,$(@).d) #arg-check-prereq = $(if $(arg-check),arguments-changed \ #$(warning arg-check $(arg-check))\ #$(warning old $@ - $(cmd_$(@)))\ #$(warning new $1 - $(cmd_$(1)))\ #) arg-check-prereq = $(if $(arg-check),arguments-changed) arg-check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$(@))) \ $(filter-out $(cmd_$(@)), $(cmd_$(1))) ) echo-cmd = $(if $($(quiet)cmd_$(1)),\ echo ' $(call escsq,$($(quiet)cmd_$(1)))$(echo-why)';) make-cmd = $(subst \#,\\\#,$(subst $$,$$$$,$(call escsq,$(cmd_$(1))))) cmd = @$(echo-cmd) $(cmd_$(1)) primary_source = $(firstword $^) #target-objects2=$(addprefix $(objdir),$(if $(filter-out $(origin $(1)-objs-y),undefined),$($(1)-objs-y),$(1).o)) #target-objects=$(warning target-objects $(1)=$(target-objects2))$(target-objects2) target-objects=$(addprefix $(objdir),$(if $(filter-out $(origin $(1)-objs-y),undefined),$($(1)-objs-y),$(1).o)) # why - tell all prerequisites that have changed ifeq ($(VERBOSE),2) why = - due to $(filter-out $(PHONY),$?) $(filter $(PHONY),$^) echo-why = $(call escsq, $(strip $(why))) endif # rules for targets define CreateDirectory ifeq ($(filter createdir-$(1),$(PHONY)),) PHONY+=createdir-$(1) ifneq ($(shell [ -d $(1) ] && echo yes), yes) createdir-$(1): $(Q)mkdir -p $(1) allobjdirs: createdir-$(1) endif endif endef define CallRule @set -e; \ $(rule_$(1)) endef define ExecuteCommand @set -e; \ $(echo-cmd) $(cmd_$(1)); \ echo 'cmd_$@ := $(make-cmd)' > $(@).cmd endef define MemoizeVariable ifneq ($($(1)),) $(current-dir)--$(1):=$($(1)) $(1):= endif endef define PushVariable inherit-$(1)-$(recursion-level):=$($(1)) endef define PopVariable $(current-dir)--$(1):=$($(1)) $(1):=$(inherit-$(1)-$(recursion-level)) endef define MemoizeAndPopVariable $(current-dir)--$(1):=$($(1)) $(1):=$(inherit-$(1)-$(recursion-level)) endef # GCC C-compiler c_flags = -Wp,-MD,$(depfile),-MT,$@ $(CFLAGS_ALL) \ $($(dir $(printable-target))--CFLAGS) \ $($(dir $(printable-target))--CFLAGS_$(notdir $(primary_source))) quiet_cmd_cc_o_c = CC $(printable-target) cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $(primary_source) define rule_cc_o_c $(call echo-cmd,cc_o_c) $(cmd_cc_o_c); \ (echo 'cmd_$@ := $(call make-cmd,cc_o_c)'; \ echo; cat $(depfile) ; echo ; \ sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ -e '/^$$/ d' -e 's/$$/ :/' < $(depfile) ) \ > $@.cmd ; \ rm $(depfile) endef $(objtree)%.o: $(srctree)%.c $$(call arg-check-prereq,cc_o_c) |allobjdirs $(call CallRule,cc_o_c) # AR static library archiver quiet_cmd_ar = AR $(printable-target) cmd_ar = $(AR) -r $@ $? 2> /dev/null && \ $(RANLIB) $@ define CreateLibrary all-libs += $(objdir)$(1).a $(objdir)$(1).a: $(target-objects) $(LIBS) | createdir-$(objdir) -include $(addsuffix .cmd,$(target-objects)) $(call CreateDirectory,$(objdir)) endef # Linker ld_flags = $(LDFLAGS_ALL) \ $($(dir $(printable-target))--LDFLAGS) \ $($(dir $(printable-target))--LDFLAGS_$(notdir $(primary_source))) quiet_cmd_ld = LD $(printable-target) cmd_ld = $(CC) $(ld_flags) -o $@ $(filter-out $(PHONY),$^) $($(@D)--LIBS) $($@--LIBS) define CreateProgram all-progs += $(objdir)$(1) $(objdir)$(1): $(target-objects) $(LIBS) | createdir-$(objdir) -include $(addsuffix .cmd,$(objdir)$(1) $(target-objects)) $(call CreateDirectory,$(objdir)) endef # version string GIT_REV := $(shell test -d .git && git describe 2> /dev/null || echo exported) ifneq ($(GIT_REV), exported) FULL_VERSION := $(patsubst v%,%,$(GIT_REV)) else FULL_VERSION := $(VERSION) endif # include the main directory's TFbuild include $(build-prologue) TFbuild $(build-epilogue) # debug dump all variables #$(foreach VAR,$(sort $(.VARIABLES)),$(warning $(VAR)=$($(VAR)))) progs: $(all-progs) libs: $(all-libs) $(all-libs): %: $(call ExecuteCommand,ar) $(all-progs): %: $$(call arg-check-prereq,ld) $(call ExecuteCommand,ld) .PHONY: $(PHONY) $(PHONY): endif