wcroughan
wcroughan

Reputation: 154

Makefile ignoring included rules

I'm trying to create a makefile for a very basic c++ program. I'm trying to implement the automatic generation of dependencies by running g++ with the -M flag, storing this output in a .d file, and then including those .d files in my main makefile. The makefile content is below

CC=g++
CPPFLAGS=-Wall -Wextra -g -std=c++11

SOURCEDIR=src
SOURCES = $(wildcard $(SOURCEDIR)/*.cpp)

BUILDDIR=build
OBJDIR=$(BUILDDIR)/objs
OBJS=$(SOURCES:$(SOURCEDIR)/%.cpp=$(OBJDIR)/%.o)
DEP_FILES = $(OBJS:.o=.d)

OUTFILE=hello.out


$(OUTFILE) : $(OBJS)
    $(CC) -o $@ $^ $(CPPFLAGS)

include $(DEP_FILES)
$(OBJDIR)/%.d : $(SOURCEDIR)/%.cpp
    $(CC) $(CPPFLAGS) $< -MM -MT $(@:.d=.o) > $@

$(DEP_FILES) : | $(OBJDIR)
$(OBJS): | $(OBJDIR)

$(OBJDIR):
    mkdir -p $(OBJDIR)


.PHONY: clean
clean:
    rm -f $(BUILDDIR) -r
    rm -f *~ 
    rm -f $(OUTFILE)

When I run make, the directory build/objs/ is generated and a .d file is generated with rules in it. Here's main.d file:

build/objs/main.o: src/main.cpp src/main.h

And here's the myfunc.d file:

build/objs/myfunc.o: src/myfunc.cpp src/main.h

Here's the issue Since I'm calling include on these .d files, I'd expect the .o files which they specify to then be created, and then the main outfile to be created as the main rule. However, make creates the .d files, and then skips directly to the main compilation step without creating any .o files:

g++ -o hello.out build/objs/myfunc.o build/objs/main.o -Wall -Wextra -g -std=c++11

This fails with the following error, since the .o files are never created:

g++: error: build/objs/myfunc.o: No such file or directory
g++: error: build/objs/main.o: No such file or directory
g++: fatal error: no input files

How can I use this makefile to generate the .o files necessary for g++? Thank you for any help in advance!

Upvotes: 0

Views: 1386

Answers (2)

I saw you got your makefile working but I just wanted to add a few things you might want to consider for future projects. I recommend using the vpath variable rather than specifying $(OBJDIR)/%.o in your makefile recipes. I actually read somewhere that it's not "cannon" to build object files in a separate directory, but in the cursory search I conducted before posting, I couldn't find the document.

That being said, I wrote a makefile that does what you wanted; it builds the output folder, generates the dependencies, and compiles the program. I specifically included the $(COMPILE.cpp) definition so you could see what it's composed of. $(CC) is specifically the C compiler, and $(CFLAGS) is specifically flags for the C compiler. They're just variables, obviously, so you can change them like you did and it will work fine, but the main think to keep in mind is that whoever uses your programs will expect to be able to configure the compilation as they see fit. This means they will set the $(CXX) and $(CXXFLAGS) expecting to set the C++ compiler and flags. $(CPPFLAGS) stands for C/C++ Preprocessor flags.

It's not the cleanest makefile, and if I was to change something, I would just compile the object files in place and save myself that headache. That cuts down on unnecessary make hacking, but for the purposes of answering your question, here it is. Anyways I hope this helps you somewhat, let me know if you have any questions.


Oh yea, I almost forgot; notice I changed your make clean script. I used $(RM) instead of simply rm -f. When you use utilities in your makefiles, you want to use them as variables. Again, this is to allow your users as much freedom and flexibility as possible when they're compiling your program.


vpath %.cpp src
vpath %.hpp include
vpath %.o build/objs
vpath %.d build/objs

.SUFFIXES:
.SUFFIXES: .cpp .hpp .o .d

SRCDIR          = src
INCLUDESDIR     = include
BUILDDIR        = build
OBJDIR          = $(BUILDDIR)/objs

SRCS            = $(wildcard $(SRCDIR)/*.cpp)
OBJS            = $(patsubst %.cpp, %.o, $(notdir $(SRCS)))
DEP_FILES       = $(patsubst %.o, %.d, $(OBJS))

INCLUDE_DIRS    = -I $(INCLUDESDIR)

CXX             = g++
CPPFLAGS        = 
CXXFLAGS        = -Wall -Wextra -g -std=c++11

PROGRAM         = hello.out

COMPILE.cpp     = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(INCLUDE_DIRS) $(TARGET_ARCH)

all: $(PROGRAM)

$(PROGRAM): %: $(OBJS)
    $(LINK.cpp) $(INCLUDE_DIRS) $(addprefix $(OBJDIR)/, $^) $(LOADLIBES) $(LDLIBS) -o $@

%.o: %.cpp
    $(COMPILE.cpp) -c -o $(OBJDIR)/$@ $<

%.d: %.cpp
    mkdir -p $(OBJDIR)
    $(COMPILE.cpp) $^ -MM -MT $(addprefix $(OBJDIR)/, $(@:.d=.o)) > $(OBJDIR)/$@

include $(DEP_FILES)

.PHONY: clean
clean:
    @echo $(RM)
    $(RM) $(BUILDDIR) -r
    $(RM) *~ 
    $(RM) $(PROGRAM)

Upvotes: 1

wcroughan
wcroughan

Reputation: 154

For anyone having a similar issue, here's the correct solution is in the comments. Here for convenience: The included .d files generate dependencies but not a recipe for making the .o files, and since I'm putting things in various directories the default rule doesn't work here, so the .o files aren't created. The solution was to add in the following rule to my main makefile.

$(OBJDIR)/%.o :
    $(CC) -c -o $@ $< $(CPPFLAGS) 

Thanks Matt and Renaud for your answers!

Upvotes: 0

Related Questions