7
votes

I have a Makefile for a C++ program that uses automatic dependency generation. The %.d recipe is taken from the GNU Make manual.

The problem is that somehow "Makefile" is being added as a target and then an implicit rule is causing it to assume it's an executable and using my src/%.cpp rule to try to compile src/Makefile.cpp. When looking at the debug info, this always happens right after the include is executed.

    No need to remake target `build/Sprite.d'.
 Considering target file `Makefile'.
  Looking for an implicit rule for `Makefile'.
  ...
  Trying pattern rule with stem `Makefile'.
  Trying implicit prerequisite `Makefile.o'.
  Looking for a rule with intermediate file `Makefile.o'.

I know include causes the given Makefiles to be rebuilt if necessary. Does it also try to rebuild the current Makefile? If so how do I stop it, and if not, then why is "Makefile" being added as a target?

Also, the include is executed, causing the .d files to be remade even if I specify a target on the command line, such as make clean. Is there any way to stop that from happening?



# $(call setsuffix,newsuffix,files)
# Replaces all the suffixes of the given list of files.
setsuffix = $(foreach file,$2,$(subst $(suffix $(file)),$1,$(file)))

# $(call twinfile,newdir,newsuffix,oldfile)
# Turns a path to one file into a path to a corresponding file in a different
# directory with a different suffix.
twinfile = $(addprefix $1,$(call setsuffix,$2,$(notdir $3)))

MAIN = main

SOURCE_DIR = src/
INCLUDE_DIR = include/
BUILD_DIR = build/

SOURCES = $(wildcard $(SOURCE_DIR)*.cpp)
OBJECTS = $(call twinfile,$(BUILD_DIR),.o,$(SOURCES))
DEPENDENCIES = $(call twinfile,$(BUILD_DIR),.d,$(SOURCES))

CXX = g++
LIBS = -lpng
CXXFLAGS = -I $(INCLUDE_DIR)


.PHONY: all
all: $(MAIN)

$(MAIN): $(OBJECTS)
 $(CXX) $(LIBS) $^ -o $(MAIN)

include $(DEPENDENCIES)

%.o: $(BUILD_DIR)stamp
 $(CXX) $(CXXFLAGS) -c $(call twinfile,$(SOURCE_DIR),.cpp,$@) -o $@

$(BUILD_DIR)%.d: $(SOURCE_DIR)%.cpp $(BUILD_DIR)stamp
 @ echo Generate dependencies for $ $@.$$$$; \
 sed 's,\($*\)\.o[ :]*,$(BUILD_DIR)\1.o $@ : ,g'  $@; \
 rm -f $@.$$$$

$(BUILD_DIR)stamp:
 mkdir -p $(BUILD_DIR)
 touch $@

.PHONY: clean
clean:
 rm -rf $(BUILD_DIR)

.PHONY: printvars
printvars:
 @ echo $(SOURCES)
 @ echo $(OBJECTS)
 @ echo $(DEPENDENCIES)


1

1 Answers

13
votes

Make will always try to remake the Makefile before executing the Makefile. To do so, make will look for rules which can be used to recreate the Makefile. Make will look for quite a few implicit rules and other obscure methods to (re)create the Makefile.

In your case, make somehow decided that the pattern rule %.o: $(BUILD_DIR)/stamp should be used to recreate the Makefile, which failed.

To prevent make from remaking the Makefile you can write a rule with an empty recipe:

Makefile: ;

Read the chapter Remaking Makefiles in the make manual for more explanation.

About the included Makefiles: Included Makefiles will always be included, regardless of the target. If the included makefiles are missing (or older than their prerequisites) then they will first be (re)created. That means a make clean will first generate the .d Makefiles, only to delete them again.

You can prevent the including for specific goals by wraping the include directive in a conditional:

ifneq ($(MAKECMDGOALS),clean)
include $(DEPENDENCIES)
endif

Here is your entire Makefile with some fixes. I marked the places where I changed something.

# Makefile

# $(call setsuffix,newsuffix,files)
# Replaces all the suffixes of the given list of files.
setsuffix = $(foreach file,$2,$(subst $(suffix $(file)),$1,$(file)))

# $(call twinfile,newdir,newsuffix,oldfile)
# Turns a path to one file into a path to a corresponding file in a different
# directory with a different suffix.
twinfile = $(addprefix $1/,$(call setsuffix,$2,$(notdir $3)))

MAIN = main

SOURCE_DIR = src
INCLUDE_DIR = include
BUILD_DIR = build

SOURCES = $(wildcard $(SOURCE_DIR)/*.cpp)
OBJECTS = $(call twinfile,$(BUILD_DIR),.o,$(SOURCES))
DEPENDENCIES = $(call twinfile,$(BUILD_DIR),.d,$(SOURCES))

CXX = g++
LIBS = -lpng
CXXFLAGS = -I $(INCLUDE_DIR)


.PHONY: all
all: $(MAIN)

$(MAIN): $(OBJECTS)
    $(CXX) $(LIBS) $^ -o $(MAIN)

# -------> only include if goal is not clean <---------
ifneq ($(MAKECMDGOALS),clean)
include $(DEPENDENCIES)
endif

# ---------> fixed this target <--------------
$(BUILD_DIR)/%.o: $(SOURCE_DIR)/%.cpp $(BUILD_DIR)/stamp
    $(CXX) $(CXXFLAGS) -c $(call twinfile,$(SOURCE_DIR),.cpp,$@) -o $@

# ---------> and this target <---------------
$(BUILD_DIR)/%.d: $(SOURCE_DIR)/%.cpp $(BUILD_DIR)/stamp
    @ echo Generate dependencies for $@;
    @set -e; rm -f $@; \
    $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
    sed 's,\($*\)\.o[ :]*,$(BUILD_DIR)\1.o $@ : ,g' < $@.$$$$ > $@; \
    rm -f $@.$$$$

$(BUILD_DIR)/stamp:
    mkdir -p $(BUILD_DIR)
    touch $@

.PHONY: clean
clean:
    rm -rf $(BUILD_DIR)

.PHONY: printvars
printvars:
    @ echo $(SOURCES)
    @ echo $(OBJECTS)
    @ echo $(DEPENDENCIES)