Search code examples
makefilecompilationgnu-make

What causes a conflict between pattern rules in the Makefile below?


Consider the following Makefile as an MWE:

###################################################################
# first bunch
###################################################################
FORTRAN1 = ifort -c
PATH1 = sub/
FILE1 = first.f90
SOURCE1 = $(FILE_TEST:%=$(PATH1)%)
OBJECT1 = $(addprefix lnk/,$(addsuffix .o,$(basename $(FILE1))))
###################################################################
#second bunch
###################################################################
FORTRAN2 = ifort -traceback -c
PATH2 = sub/
FILE2 = second.f90
SOURCE2 = $(FILE2:%=$(PATH2)%)
OBJECT2 = $(addprefix lnk/,$(addsuffix .o,$(basename $(FILE2))))
###################################################################
# first rule
###################################################################
all: $(OBJECT1)
lnk/%.o: $(PATH1)%.f90
   $(FORTRAN1) -o $@ $<
###################################################################
# second rule
###################################################################
all: $(OBJECT2)
lnk/%.o: $(PATH2)%.f90
   $(FORTRAN2) -o $@ $<

When I run this script, the following commands are executed:

ifort -traceback -c -o lnk/first.o sub/first.f90
ifort -traceback -c -o lnk/second.o sub/second.f90

In other words, both the first and second rules use FORTRAN2=ifort -traceback -c, while the first rule should use FORTRAN1=ifort -c. Could you please help me what is responsible for this strange behavior? How can I achieve that $(FORTRAN1)$ should be used by the first rule?

EDIT #1:

Following MadScientist's suggestion, I tried the following:

###################################################################
# first bunch
###################################################################
FORTRAN1 = ifort -c
PATH1 = sub/
FILE1 = first.f90
SOURCE1 = $(FILE_TEST:%=$(PATH1)%)
OBJECT1 = $(addprefix lnk/,$(addsuffix .o,$(basename $(FILE1))))
###################################################################
#second bunch
###################################################################
FORTRAN2 = ifort -traceback -c
PATH2 = sub/
FILE2 = second.f90
SOURCE2 = $(FILE2:%=$(PATH2)%)
OBJECT2 = $(addprefix lnk/,$(addsuffix .o,$(basename $(FILE2))))
###################################################################
# first rule
###################################################################
$(OBJECT1): lnk/%.o: $(PATH1)%.f90
   $(FORTRAN1) -o $@ $<
###################################################################
# second rule
###################################################################
$(OBJECT2): lnk/%.o: $(PATH2)%.f90
   $(FORTRAN2) -o $@ $<

However, it executes only one (that is, the first) command:

ifort -c -o lnk/first.o sub/first.f90

EDIT #2:

The problem was that I removed the all: targets. The correct file content should be:

###################################################################
# first bunch
###################################################################
FORTRAN1 = ifort -c
PATH1 = sub/
FILE1 = first.f90
SOURCE1 = $(FILE_TEST:%=$(PATH1)%)
OBJECT1 = $(addprefix lnk/,$(addsuffix .o,$(basename $(FILE1))))
###################################################################
#second bunch
###################################################################
FORTRAN2 = ifort -traceback -c
PATH2 = sub/
FILE2 = second.f90
SOURCE2 = $(FILE2:%=$(PATH2)%)
OBJECT2 = $(addprefix lnk/,$(addsuffix .o,$(basename $(FILE2))))
###################################################################
# first rule
###################################################################
all: $(OBJECT1)
$(OBJECT1): lnk/%.o: $(PATH1)%.f90
   $(FORTRAN1) -o $@ $<
###################################################################
# second rule
###################################################################
all: $(OBJECT2)
$(OBJECT2): lnk/%.o: $(PATH2)%.f90
   $(FORTRAN2) -o $@ $<

Solution

  • Make is not a scripting language: it's not the case that make reads the makefile and "executes" it while it is processed. Make reads the entire makefile completely, plus all included files, then after it's done it starts processing.

    There can only be one pattern rule with the same set of targets and prerequisites. In your makefile you have:

    PATH1 = sub/
    
    PATH2 = sub/
    
    lnk/%.o: $(PATH1)%.f90
       ...
    
    lnk/%.o: $(PATH2)%.f90
       ...
    

    Since PATH1 and PATH2 variables have the same value, make sees this:

    lnk/%.o: sub/%.f90
       ...
    
    lnk/%.o: sub/%.f90
       ...
    

    the second rule is identical to the first rule, and so it overwrites it and the first rule is lost. Then after all this parsing is complete, THEN make starts to try to build objects and see which rule should be used to match which target. Since there's only one pattern rule that matches, it's used for all objects.

    There are a few different ways you can do this. One is by using static pattern rules instead of full pattern rules:

    $(OBJECT1) : lnk/%.o: $(PATH1)%.f90
       ...
    
    $(OBJECT2) : lnk/%.o: $(PATH2)%.f90
       ...
    

    Now those rules match only those exact objects.