Here's a basic way to do this (although I'm convinced it's a terrible idea, see below):
-include CFLAGS.save
CFLAGS := -O2 -g
all: foo
CFLAGS.save:
echo 'CFLAGS_SAVE := $(CFLAGS)' > $@
ifeq ($(CFLAGS),$(CFLAGS_SAVE))
%.o: %.c CFLAGS.save
gcc $(CFLAGS_SAVE) -c -o $@ $<
else
.PHONY: CFLAGS.save
%.o: %.c CFLAGS.save
$(MAKE) $@
endif
foo: foo.o
gcc -o $@ $^
Here's what happens: Before any compilation is done, the current CFLAGS are written out to CFLAGS.save. If CFLAGS isn't equal to CFLAGS_SAVE, then the user must've changed them. If so, we declare CFLAGS.save to be phony so make will rebuild it. Note also that if CFLAGS has changed, we'll update it but we'll still have the old value in memory. Therefore, we have to recursively invoke make for every source file. Not cool on a big project.
The other problem is that if you neglect to specify CFLAGS on the command line, it will go back and rebuild everything with the default. You could work around this by testing the $(origin) of CFLAGS, but seriously, no. My professional ethics won't allow me to stand for this.
make is meant to be simple. Distributors already have enough trouble understanding packagers' abuses of build tools (sadly, most of the blame levelled at automake is due to this). Please, just say no to Cthulhoid build systems.
Besides, make clean all CFLAGS='-Whatever -foo' will work just as well.