Question

I have a make file with a number of phony targets, they all compile the same code just with different compilation flags.

EXECUTABLE=ecis

#debug build
.PHONY: debug
debug: FLAGS=-g
debug: $(EXECUTABLE)

#No optimization
.PHONY: opt0
opt0: FLAGS=
opt0: $(EXECUTABLE)

#level 1 optimization
.PHONY: opt1
opt1: FLAGS=-O1
opt1: $(EXECUTABLE)

#level 2 optimization
.PHONY: opt2
opt2: FLAGS=-O2
opt2: $(EXECUTABLE)

...

$(EXECUTABLE):$(FORTRAN_OBJECTS) $(CPP_OBJECTS)
    $(CPP) $(FLAGS) $(FORTRAN_OBJECTS) $(CPP_OBJECTS) -lgfortran -o $@
...

When I first run the makefile with one build make opt2 option it runs just fine. If I subsequently want to run make with another build option say make debug it claims that the target is up to date. I understand why it is doing this, make doesn't realize that the flags changed and so as far as make is concerned nothing has changed if the files haven't changed.

That said, is there an easy way around it other than calling make cleanall (which deletes the .o files and the executable)? Is there some way for make to recognize the different flags as changing the compilation? Are there any other paths to have it "do the right thing"?

Was it helpful?

Solution

Here's an example that should work:

.compile_flags: Makefile
        [ "`cat $@`" = '$(FLAGS)' ] || echo '$(FLAGS)' > $@

$(FORTRAN_OBJECTS) $(CPP_OBJECTS) $(EXECUTABLE): .compile_flags

An alternative is to generate the object files and executable into a separate subdirectory for each different base target. Then they won't overlap. This has the added benefit that you don't have to recompile the world for each type of build (only since the last time you did that build). But it uses more disk space and may cause other issues if you have other parts of the system expecting things to live where they do now.

To put things in other directories can't be done with target-specific variables. The simplest way to do it is to use one instance of recursive make instead, like this:

EXECUTABLE=ecis

#debug build
.PHONY: debug
debug: FLAGS=-g

#No optimization
.PHONY: opt0
opt0: FLAGS=

#level 1 optimization
.PHONY: opt1
opt1: FLAGS=-O1

#level 2 optimization
.PHONY: opt2
opt2: FLAGS=-O2

debug opt0 opt1 opt2:
        $(MAKE) OUTDIR=obj_$@ FLAGS=$(FLAGS) obj_$@/$(EXECUTABLE)
...
FORTRAN_OBJECTS := $(addprefix $(OUTDIR)/,$(FORTRAN_OBJECTS))
CPP_OBJECTS := $(addprefix $(OUTDIR)/,$(CPP_OBJECTS))

$(OUTDIR)/$(EXECUTABLE):$(FORTRAN_OBJECTS) $(CPP_OBJECTS)
    $(CPP) $(FLAGS) $(FORTRAN_OBJECTS) $(CPP_OBJECTS) -lgfortran -o $@
...

And, you'll have to create pattern rules for your object files like:

$(OUTDIR)/%.o : %.cpp
        ...

and ditto for FORTRAN.

OTHER TIPS

If you're limited to GNU make itself, you'll have trouble achieving your goal. As @MadScientist suggested, you can basically get what you want with some shenanigans. John Graham-Cumming wrote up a good explanation in his old "Ask Mr. Make" column: Rebuilding when CPPFLAGS Changes.

If you can use other make implementations, you might check out Electric Make, a reimplementation of GNU make designed for performance and reliability. It includes a feature called "ledger" which provides precisely this functionality.

Disclaimer: I'm the architect and lead developer of Electric Make

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top