# vim:syntax=make:ts=8:noet:sts=8:sw=8 .SECONDEXPANSION: programs:: .PHONY: programs clean # Provide a way to echo (or not) depending on the flags passed to make: # Turn off echos for -s (user asked for silence) and -n (in this context, # it's just noise) ifeq "$(findstring s,$(MAKEFLAGS))$(findstring n,$(MAKEFLAGS))" "" DOECHO=@echo $(1) DOVECHO=echo $(1) else DOECHO= DOVECHO= endif # Certain targets are "trivial": when they are the only targets, it is not # necessary to include (and therefore generate) dependency information. TRIVIAL_TARGET_NAMES += clean ifeq ($(MAKECMDGOALS),) TRIVIAL_BUILD:=no else ifeq ($(filter-out $(TRIVIAL_TARGET_NAMES),$(MAKECMDGOALS)),) TRIVIAL_BUILD:=yes else TRIVIAL_BUILD:=no endif endif TOOBJS = $(patsubst %.c,.obj/%.o,$(1)) TODEPS = $(patsubst %.c,.obj/%.d,$(1)) # $(eval ...) allows creation of new rules, so when nothing else quite fits, # this is your escape valve -- though probably declare_program could also be # done with secondary expansion if it was thought through PROGRAM = $(eval $(call declare_program,$(1),$(2))) define declare_program ifneq ($(CFLAGS_$(1)),) $(1): CFLAGS := $(CFLAGS_$(1)) endif ifneq ($(LFLAGS_$(1)),) $(1): LFLAGS := $(LFLAGS_$(1)) endif $(1): $(call TOOBJS, $(SRCS_$(1))) $$(call DOECHO,Linking $$@) @$(CC) -o $$@ $$^ $$(LFLAGS) ifneq ($(TRIVIAL_BUILD),yes) -include $(call TODEPS, $(SRCS_$(1))) endif clean:: -rm -f $(1) programs:: $(1) endef %/.exists: @if [ ! -d $* ]; then $(call DOVECHO,Creating output directory $*;) true; fi @mkdir -p $* @touch "$@" .PRECIOUS: %/.exists # second expansion makes a dependency file depend on the .exists of its output # directory, so that make creates the directory just once, instead of once per # target as I've seen (and written!) in other systems .obj/%.d: %.c $$(dir $$@).exists $(call DOECHO,Depending $<) @mkdir -p $(dir $@) @$(CC) $(CFLAGS) $(DEPCFLAGS) -MM -MT "$@ $(patsubst %.d,%.o,$@)" $< -o $@.tmp \ && mv $@.tmp $@ .obj/%.o: %.c $$(dir $$@).exists $(call DOECHO,Compiling $<) @$(CC) $(CFLAGS) -c $< -o $@ clean:: -rm -rf .obj $(foreach p,$(PROGRAMS), $(call PROGRAM,$(p)))