Using BSD make, I wish to use different compiler flags for the debug and release versions of my program. The version of the program to build should be defined via a MODE
variable. I would like to only run the compiler when necessary, meaning that if I build the debug version and then build the release version, I would like to recompile all objects, even though they are technically up-to-date according to make.
A simplified version of my makefile would look like this:
MODE ?= DEBUG # DEBUG or RELEASE
CFLAGS = -std=c99 -Werror -Wall -Wextra
.if $(MODE) == DEBUG
CFLAGS += -g -fsanitize=undefined,address
.endif
a.o: a.c a.h
cc -c $(CFLAGS) -o $(.TARGET) $(.ALLSRC:M*.c)
b.o: b.c a.h b.h
cc -c $(CFLAGS) -o $(.TARGET) $(.ALLSRC:M*.c)
c.o: c.c c.h
cc -c $(CFLAGS) -o $(.TARGET) $(.ALLSRC:M*.c)
program: a.o b.o c.o
cc $(CFLAGS) -o $(.TARGET) $(.ALLSRC)
This example would build fine the first time, but if I change MODE
to RELEASE
, make would inform me that there is nothing to build, as everything is already up-to-date.
The only way I found to achieve the result I'm looking for is to print the MODE
variable into a plain text file at the end of the build process, check for equality with the current MODE
value, and force a rebuild on all "rebuildable" targets if MODE
has changed.
MODE ?= DEBUG # DEBUG or RELEASE
LAST_MODE != cat mode
CFLAGS = -std=c99 -Werror -Wall -Wextra
.if $(MODE) == DEBUG
CFLAGS += -g -fsanitize=undefined,address
.endif
.if $(MODE) != $(LAST_MODE)
rebuildable: .PHONY
.END:
printf "$(MODE)" > $(EXPORTS)/mode
.else
rebuildable: .USE
.endif
a.o: a.c a.h rebuildable
cc -c $(CFLAGS) -o $(.TARGET) $(.ALLSRC:M*.c)
b.o: b.c a.h b.h rebuildable
cc -c $(CFLAGS) -o $(.TARGET) $(.ALLSRC:M*.c)
c.o: c.c c.h rebuildable
cc -c $(CFLAGS) -o $(.TARGET) $(.ALLSRC:M*.c)
program: a.o b.o c.o rebuildable
cc $(CFLAGS) -o $(.TARGET) $(.ALLSRC:M*.o)
Is there a more idiomatic, less hacky way of doing this?
You have to have some way to memorialize in what mode targets have been built. I would suggest, though, that a more natural perspective on the problem is that
Target-A-Debug
is a logically different target fromTarget-A-Release
, even if they are ultimately built from the same sources. It follows that you should have different rules for them.That might work out something like this (but please forgive me if I mistake any
bmake
-specific syntax):I imagine it's possible to apply a bmake-ism or two to merge those two separate lists of rules into one -- this is left as an exercise :-)