8
\$\begingroup\$

I am looking for improvements for this basic makefile and directory structure. I know this is overkill and could be compiled with one line in a flat directory structure, but I want to use this as a learning exercise.

CC=g++
CFLAGS=-Wl,--no-as-needed -std=c++11 
LIBS=-lpthread
SRC=$(PWD)/src
BUILDDIR=$(PWD)/build
OUTDIR=$(BUILDDIR)/bin
TEMPDIR=$(BUILDDIR)/tmp
MKDIR_P = mkdir -p
.PHONY: directories clean run
all: directories $(OUTDIR)/terminal
$(OUTDIR)/terminal: $(TEMPDIR)/main.o $(TEMPDIR)/terminal.o
 $(CC) $(CFLAGS) $(TEMPDIR)/terminal.o $(TEMPDIR)/main.o $(LIBS) -o $(OUTDIR)/terminal
$(TEMPDIR)/main.o: $(SRC)/main.cpp $(SRC)/terminal.hpp
 $(CC) -c $(CFLAGS) $(SRC)/main.cpp -o $(TEMPDIR)/main.o
$(TEMPDIR)/terminal.o: $(SRC)/terminal.cpp $(SRC)/terminal.hpp
 $(CC) -c $(CFLAGS) $(SRC)/terminal.cpp -o $(TEMPDIR)/terminal.o
directories: ${OUTDIR} $(TEMPDIR)
$(OUTDIR): 
 ${MKDIR_P} $(OUTDIR)
$(TEMPDIR):
 ${MKDIR_P} $(TEMPDIR)
clean:
 rm -rf $(BUILDDIR)
run:
 $(OUTDIR)/terminal
toolic
14.9k5 gold badges29 silver badges206 bronze badges
asked Jan 10, 2015 at 6:05
\$\endgroup\$
2
  • \$\begingroup\$ GNU make, I assume? \$\endgroup\$ Commented Jan 10, 2015 at 7:12
  • \$\begingroup\$ Yes. <10 more characters for yes/no question.> \$\endgroup\$ Commented Jan 10, 2015 at 17:43

2 Answers 2

8
\$\begingroup\$
  • Stem rules:

    Spelling out compilation rule for each object file is tedious and error prone.

    $(TEMPDIR)/%.o: $(SRC)/%.c
     $(CXX) $(CFLAGS) -c $< -o $@
    

    takes care about all of them.

  • Autodependencies:

    Spelling out dependencies for each object file is tedious and error prone. gcc can do it for you:

    $(TEMPDIR)/main.d: $(SRC)/main.c
     $(CXX) $(CFLAGS) -MM -MT $< -o $@
    -include $(TEMPDIR)/main.d
    

    Of course you we don't want to spell it out for each source file, which naturally leads us to the next step.

  • Use macros. Shall you add more source files, only FILES needs to be modified.

    FILES = main.c terminal.c
    SOURCES = $(patsubst %,$(SRC)/%,$(FILES))
    OBJECTS = $(patsubst %.c,$(TEMPDIR)/%.o,$(FILES))
    DEPS = $(patsubst %.c.$(TEMPDIR)/%.d,$(FILES))
    $(TEMPDIR)/%.o: $(SRC)/%.c
     $(CXX) $(CFLAGS) -c $< -o $@
    $(TEMPDIR)/%.d: $(SRC)/%.c
     $(CXX) $(CFLAGS) -MM -MT $< -o $@
    -include $(DEPS)
    $(TARGET): $(OBJECTS)
     $(CXX) $(LDFLAGS) $(OBJECTS) -o $(TARGET)
    
answered Jan 10, 2015 at 22:49
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Good review. The only point I think you may wish to add is that rm -rf is rather dangerous for clean. Better would be to remove only the generated files. \$\endgroup\$ Commented Jan 11, 2015 at 14:55
  • \$\begingroup\$ @Edward Agreed. It is even better to define RM = /bin/rm. \$\endgroup\$ Commented Jan 12, 2015 at 5:34
8
\$\begingroup\$

You should pick a single make variable usage style and use it consistently. Personally I use $(...) to avoid confusing it with shell variable ${...} (as I use that much more frequently than command substitution).

You probably want to make all as phony also.

Your run target will fail if run first thing. Using run: $(OUTDIR)/terminal will cause it to bootstrap the binary before using it.

GNU Make has built-in rules for building .c files into .o files which, if you want, you can use. Which means you just need to add the .h files as prerequisites to the .o files (if you don't autogenerate them) like this $(TEMPDIR)/main.o: $(SRC)/terminal.hpp with no recipe.

As to autogenerating dependencies (which @vnp also mentions) you should read Auto-Dependency Generation.

These two rules

$(OUTDIR): 
 ${MKDIR_P} $(OUTDIR)
$(TEMPDIR):
 ${MKDIR_P} $(TEMPDIR)

can be combined into

$(OUTDIR) $(TEMPDIR):
 $(MKDIR_P) $@

You can avoid needing to manually run the directories target by using order-only prerequisites on the needed directories.

$(OUTDIR)/terminal: $(TEMPDIR)/main.o $(TEMPDIR)/terminal.o | $(OUTDIR)
 ....
$(TEMPDIR)/main.o: $(SRC)/main.cpp $(SRC)/terminal.hpp | $(TEMPDIR)
 ....
$(TEMPDIR)/terminal.o: $(SRC)/terminal.cpp $(SRC)/terminal.hpp | $(TEMPDIR)
 ....
answered Jan 11, 2015 at 0:47
\$\endgroup\$
1
  • \$\begingroup\$ Well done review, especially the note about the order-only-prerequisites. I had not recalled that feature of gnu make. \$\endgroup\$ Commented Jan 11, 2015 at 15:26

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.