I\'ve trying to learn the \"best practice\" makefile\'s for a project.
Please review my Makefile file below and suggest changes to enhance it.
The dir layout:
If you expect other people to use your Makefile, always include a help
target that prints out a message detailing the various targets that are sensible to call from the command line, and the various environment vars which can be reasonably set to do various things...
See this and that answers for examples about Makefile
. Also run make -p
to understand the builtin rules inside GNU make, so use $(LINK.cc)
for e.g. your bin/tengine
target.
For complex builds, consider upgrading to GNU make 4.0 and use its Guile ability.
You may want to generate automatically dependencies. Read automatic prerequisites and about autodependencies; read also about GCC preprocessor options like -M
or -MD
etc etc ....
Here is a suggestion for a revised makefile, tested slightly on a 7-year-old version of Linux (RHEL 5):
# Generic makefile
TARGETS=tengine test2
all: ${TARGETS}
help:
@echo ""
@echo "make - builds ${TARGETS}"
@echo "make tengine - builds tengine"
@echo "make test2 - builds test2"
@echo "make clean - deletes prior build"
@echo "make help - prints this help"
# Switches:
INC=-I/usr/include/hiredis
LIB=-lhiredis
SUBDIRS=obj deps bin
LNK=gcc -g -Wl,--warn-common
DEBUG=1
ifdef DEBUG
CFLAGS=-Wall -Winline -pipe -g -DDEBUG #-pedantic -pg
else
CFLAGS=-Wall -Winline -pipe -O3 -march=native -funroll-all-loops \
-finline-functions #-pedantic
endif
#CXXFLAGS=$(CFLAGS)
# Generic rules:
obj/%.o: src/%.c
@echo Compiling $@
@mkdir -p $(SUBDIRS)
$(CC) $(CFLAGS) $(INC) -MMD -MF '$(patsubst src/%.c,deps/%.d,$<)' -o $@ -c $<
obj/%.o: src/%.cc
@echo Compiling $@
@mkdir -p $(SUBDIRS)
$(CXX) $(CXXFLAGS) $(INC) -MMD -MF '$(patsubst src/%.c,deps/%.d,$<)' -o $@ -c $<
${TARGETS}: %:bin/%
# Specific target rules:
bin/tengine: obj/main.o obj/tengine.o
$(LNK) $^ $(LIB) -o $@
bin/test2: obj/main.o obj/test2.o
$(LNK) $^ $(LIB) -o $@
clean:
rm -f *~ src/*~ gmon.out
rm -fr $(SUBDIRS)
-include deps/*.d
Some notes:
A key problem with the original was that the dependency were generated, but not used. This has been fixed using -include deps/*.d
(at the end).
Now that deps/*.d is used, the makefile doesn't need to have the src/%.h
cases.
The original was also putting garbage into these files: in $(patsubst src/%,obj/%,%(patsubst %.cc,%.o,$<))
the third %
should have been a $
.
In the revised version, the dependencies are generated at the same time as the object, using -MMD
. This is quicker, shortens the makefile, and adds some DRY.
Shortened INC
: why bother including the standard system include directories? And in fact gcc will apparently ignore your -I /usr/include -I /usr/local/include
anyway.
Removed your two different definitions of OBJS
. Not needed, and potentially confusing. Used $^
instead.
It is always a good idea for make clean
to completely undo everything make does, so you are left with what you started. But the sub-directories obj/ and deps/ were being created, and never deleted. Also, bin/ was pre-supposed to exist.
For the linking, added $(LNK)
, with LNK=gcc -g -Wl,--warn-common
(but you may not want the warnings). AFAIK, all the other usual $(CFLAGS)
are ignored for links.
Removed comments, which were (mostly) distracting.
Repeated twice make;make
now gives make: Nothing to be done for ...
.
See also gcc dependency generation for a different output directory.