# a demo is a name that can be make'd into an a.out, # that designates a collection of compile units, # each with a corresponding .src.c + .o file pair SOURCES_IN_DEMO=$(wildcard $1/*.src.c) OBJECTS_IN_DEMO=$(patsubst %.src.c,%.o,$(call SOURCES_IN_DEMO,$1)) ALL_OBJECTS=$(foreach demo,$(DEMOS),$(call OBJECTS_IN_DEMO,$(demo))) # each recursive subdir gives a demo # a demo name may have slashes in middle, but not at start or end (foo, foo/bar) DIRS_REC=$(shell find ./* -type d) DEMOS_FROM_DIRS=$(patsubst ./%,%,$1) DEMO_OF_FILE=$(patsubst %/,%,$(dir $1)) DEMOS=$(call DEMOS_FROM_DIRS,$(DIRS_REC)) # demos of working code build under target `all` # default assumption = demo is working # demos named err-% recursively opt out ERR_DIRPATS=err-% DEMOS_NONERR=$(filter-out $(ERR_DIRPATS),$(DEMOS)) DEMO_EXECS_NONERR=$(DEMOS_NONERR:=/a.out) # a recourse is a designated sub-demo of an error case # that is expected to build successfully RECOURSE_DIRPATS=%/recourse-classic DEMOS_RECOURSE=$(filter $(RECOURSE_DIRPATS),$(DEMOS)) DEMO_EXECS_RECOURSE=$(DEMOS_RECOURSE:=/a.out) # non-err and recourse demos try to build by default ALL=$(DEMO_EXECS_NONERR) $(DEMO_EXECS_RECOURSE) # a shred is a *.c or *.h file generated from a *.src.c SHRED_EXTS=.impl.c .defn.h .tdcl.h SHREDS_OF_FILE=$(foreach st,$(SHRED_EXTS),$(1:=$(st))) SHREDS_IN_DEMO=$(foreach src,$(call SOURCES_IN_DEMO,$1),$(call SHREDS_OF_FILE,$(src:.src.c=))) SHREDS_IN_DEMO_OF_FILE=$(call SHREDS_IN_DEMO,$(call DEMO_OF_FILE,$1)) # a 'classic' demo builds without shredding CLASSIC_DIRPATS=%-classic DEMOS_CLASSIC=$(filter $(CLASSIC_DIRPATS),$(DEMOS)) DEMOS_NONSHRED=$(DEMOS_CLASSIC) OBJECTS_NONSHRED=$(foreach demo,$(DEMOS_NONSHRED),$(call OBJECTS_IN_DEMO,$(demo))) # everything else builds via shredding DEMOS_SHRED=$(filter-out $(RECOURSE_DIRPATS),$(DEMOS)) OBJECTS_SHRED=$(foreach demo,$(DEMOS_SHRED),$(call OBJECTS_IN_DEMO,$(demo))) ALL_SHREDS=$(foreach demo,$(DEMOS),$(call SHREDS_IN_DEMO,$(demo))) CC=gcc CPPP=./cfa-cppp CQFLAGS=-Wall -Wextra -Werror .SECONDEXPANSION: # enable dynamic prereqs from $$ .SUFFIXES: # disable traditional rules like make .o from .c by calling $(CC) all: $(ALL) # troubleshooting, e.g. `make echo_DEMOS` runs `echo $(DEMOS)` echo_% : @echo '$($(@:echo_%=%))' # # demo # %/a.out : DEMO=$(@:/a.out=) %/a.out : OBJECTS=$(call OBJECTS_IN_DEMO,$(DEMO)) %/a.out : $$(OBJECTS) $(CC) $(OBJECTS) -o $@ $(CFLAGS) $(CQFLAGS) $(DEMOS) : $$@/a.out # # object # # object of regular, shredded, demo # each CU depends on all other CUs' shreds # conservative for rebuild (obviates -MMD), but helps do initial build $(OBJECTS_SHRED) : CC_SRC=$(@:.o=.impl.c) $(OBJECTS_SHRED) : SHREDS=$(call SHREDS_IN_DEMO_OF_FILE,$@) $(OBJECTS_SHRED) : $$(SHREDS) # of nonshredded demo $(OBJECTS_NONSHRED) : CC_SRC=$(@:.o=.src.c) $(OBJECTS_NONSHRED) : CQFLAGS+= -MMD -include $(OBJECTS_NONSHRED:.o=.d) $(ALL_OBJECTS) : $(CC) $(CC_SRC) -c -o $@ $(CFLAGS) $(CQFLAGS) # # shred # define SINGLE_SHREDEXT_RULE %$1: %.src.c $(CPPP) $(CPPP) $$@ endef $(foreach sfx,$(call SHREDS_OF_FILE,@),$(eval $(call SINGLE_SHREDEXT_RULE,$(sfx:@%=%)))) # # clean, misc # # 2-line body is significant; \n ending 1st line is literal define newline endef CLEANPATS=$(call SHREDS_OF_FILE,*) *.o *.d a.out clean: $(foreach p,$(CLEANPATS),find . -name '$p' -type f -print -delete$(newline)) .PRECIOUS: %.tdcl.h %.defn.h %.impl.c .PHONY: all echo_% clean