basic GNU makefile for small c++ prog

Posted on 2007-11-27
Last Modified: 2009-08-08
This is under linux using GNU tools and in particular GNU make (I don't care about Makefile portability) What's the "best" makefile for a small c++ project made of 3 c++ files and using a third party module (with its includes and lib)?
Let's starting with this
MYINCDIR  = /usr/blabla/inc
MYLIBDIR  = /usr/blabla/lib

LDFLAGS = -L$(MYLIBDIR) -lmylib -lm -lpthread
CPPFLAGS = -m64 -O -fPIC -fexceptions -DNDEBUG -I$(MYINCDIR)

OBJS=a.o b.o c.o

$(PROG): $(OBJS)
        $(CXX) $(CXXFLAGS) $(CPPFLAGS) -o $@ $^ $(LDFLAGS)

        rm -f $(PROG) $(OBJS)
GNU make has several implicit rules and maybe tricks that I don't know. Can you make suggestions to improve this makefile with the following aims:
- keep it small and short, exploiting gnu make implicit rules and mechanisms (but keeping compilation and linking separate)
- avoid the explicit rule for linking. Note that by default gnu make use gcc (instead of g++) for linking .o files (but that wouldn't work on a c++ project) and also note that if the implicit rule passes the -l options before the OBJS list, that can also break the linking. That's why in the sample above here I had to use an explicit rule. But maybe there is a better way.
- I'd prefer to specify the list of source files, rather than the list of intermediate .o files
- is there a better way to specify additional include dirs (maybe using .INCLUDE_DIRS?) and library dirs?
- is there any automatic mechanism for handling .h dependencies
Question by:lbertacco
  • 6
  • 4
  • 3
  • +4
LVL 40

Expert Comment

ID: 20361304
Various shortcuts and optimizations for makefiles are discussed here: -
LVL 34

Expert Comment

by:Duncan Roe
ID: 20361540
You should have -m64 -O -fPIC -fexceptions in CXXFLAGS (or maybe CFLAGS - you'll have to try it) since they are not preprocessor options.
You don't need CPPFLAGS on the link line and you shouldn't need CXXFLAGS either. I've never managed to avoid having a link line - post how it's done when you find out.
"info make" is a very good source of documentation. I always use the automatic dependency generation from it - have a look. (You can post again if you don't find it).
Is this homework?
LVL 11

Assisted Solution

cup earned 200 total points
ID: 20361657
List of source files

SRCS=a.cxx b.cxx c.cxx

Include directories: there is no easy way to do it.  Just specifying a bunch of directories doesn't work.  You need the /I

MYINCDIR  = -I/usr/blabla/inc -I/usr/global/inc -I/usr/lablab
CPPFLAGS = -m64 -O -fPIC -fexceptions -DNDEBUG $(MYINCDIR)

Same with libraries.  It is better to specify path and library together

LIBXWIN=-L/usr/X11R6/lib -lXmu -lXm -lXt
LIBBOOST=-L/usr/boost/lib -lboostthread
LIBERATE=-L/home/erate -lerate

Expert Comment

ID: 20362899
your gcc options miss a very important option.
-g option,without this option you couldn't debug your programme and if your programme cored, you will be embarrassed to process.
LVL 17

Expert Comment

ID: 20363789
The GNU make manual is a good starting place for this. It's written in a very accessible way. See:
LVL 11

Author Comment

ID: 20363925
Some answers:
evilrix/duncan_roe I will check your pointers and post back if I find interesting things, however  I already  know of "info make" and also the make manual in other formats, but the reason of my question was to avoid to read the whole make manual just for this.

duncan_roe: I mistakenly believed cppflags standed for cplusplus not cpreprocessor. Changed that to CXXFLAGS; I put CXXFLAGS and CPPFLAGS in the link rule because my make default link rule is: = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)
so I tried to used the same rule with minimal changes
(you can see your make default rules running "make -p" in a directory without a makefile)
And no, this is no homework, I know how to use make quite well and used it for years but it might have evolved in ways I don't know so I'm just asking if there are better ways to use it for small projects. I mean, for example, having a default linking rule that doesn't work for c++ projects sounds suspicious to me, so maybe I'm missing something and there is a way to make it work (through a CXXFLAGS that make gcc behave like g++, or something else).

cup: good hint for the sources. Regarding inc/lib dirs, of course you can set and play with variables as you like as long as path dirs and lib dependencies end up in the final command, but I was asking if make has anything specific to handle this with its default rules.

yuy2002: we are not discussing compiler flags here, my flags were just an example

rstaveley: yes I know where to find the make manual in either pdf, html or info format, but see my first answer here.
LVL 11

Author Comment

ID: 20364151
Ok I have something better, here is it:
SRCS=a.cpp b.cpp c.cpp

MYINCDIRS  = /usr/blabla/inc /usr/foe/inc
MYLIBDIRS  = /usr/blabla/lib
MYLIBS = mylib1 m pthread
CXXFLAGS =  -m64 -O -fPIC -fexceptions -DNDEBUG $(foreach dir,$(MYINCDIRS),-I$(dir))
LDLIBS = $(foreach dir,$(MYLIBDIRS),-L$(dir)) $(foreach lib,stdc++ $(MYLIBS),-l$(lib))

PROG=$(firstword $(SRCS:.cpp=))
$(PROG): $(SRCS:.cpp=.o)

        $(RM) $(PROG) $(SRCS:.cpp=.o)
This would build a final executable with the name of the first source file (it is quite common to have a source file with the same name of the final exec).
The differences are: a) I use LDLIBS instead of LDFLAGS so that the -l dependecies end up AFTER the .o dependencies in the link command. Note that LDLIBS is strangely missing in the "Variables Used by Implicit Rules" section of the make manual
b) I added -lstdc++ to the lib deps since default linking rule uses gcc instead of g++ and gcc doesn't add stdc++ automatically (g++ does). Alternatively you could set CC=g++.
c) it uses the default link rule

The only missing part is .h dependencies. Will see tomorrow if I find something simple.
LVL 17

Expert Comment

ID: 20364435
Nifty, but...

> $(foreach dir,$(MYLIBDIRS),-L$(dir))

...doesn't specify path and lib together (as suggested by cup).

You might replace the directories in MYLIBDIRS with something that can be tokenised, using an inner patsubst invocation to split the directory from the list of libs and put an inner foreach to list libs within the directory.


MYLIBDIRLIBS  = /usr/blabla/lib,x:y /usr/foofoo/lib,z

(where the y and y libraries are in  /usr/blabla/lib and z is in /usr/foofoo/lib)

If I was a better person I'd attempt to write the function invocations, but I don't have the time to experiment right now and your Makefile is already a lot cleverer than any of mine.
LVL 17

Expert Comment

ID: 20364441
Incidentally, using += makes it more legible:

 MYLIBDIRLIBS  = /usr/blabla/lib,x:y
 MYLIBDIRLIBS += /usr/foofoo/lib,z
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

LVL 34

Expert Comment

by:Duncan Roe
ID: 20364881
-DNDEBUG $(foreach dir,$(MYINCDIRS),-I$(dir))
should go in CPPFLAGS, because they are preprocessor directives
LVL 11

Author Comment

ID: 20365110
Yes -D and -I are preproc options but in the implict rules the CPPCFLAGS is always used in pair with C/CXXFLAGS macro except when invoking the preprocessor alone. Since here I'm not using the preprocessor alone but only through the compiler, splitting options between CXX and CPP flags seemed a pointless complication
LVL 39

Expert Comment

ID: 20367645
Here is a makefile I made this summer for the AIX  and Visual Age compiler xlc. I was a newbee same as you regarding UNIX makefiles. You may have to exchange the compiler and linker flags (and the names of course). But look at the 'rule' section, where it was defined how to *make* a  .o file from a .c file.

Regards, Alex

# Makefile

# macro(s)
CC = xlc
LFLAGS = -lc -lm
CSFLAGS = -qmkshrobj -bnoentry
EXPCOPT = -bE:wmvt.exp
OBJS = xxxxxx.o \
       yyyyyy.o \
       zzzzzz.o \
# rule(s)
# creates object from source.
      $(CC) $(CFLAGS) $<

# target(s), first target is default target
wmvt: $(OBJS)
      $(CC) $(CSFLAGS) -o $(EXPCOPT) $(LFLAGS) $(OBJS)

      rm -f *.o
      rm -f *.so
      rm -f wmvt

LVL 39

Expert Comment

ID: 20367669
Note, the xlc wanted a <TAB> char preceeding any statement line following a target or rule.
LVL 34

Accepted Solution

Duncan Roe earned 300 total points
ID: 20368814
Ok this is how you do automatic dependency generation. Note that it WILL use the preprocessor in isolation so you'd better separate out CPPFLAGS and CXXFLAGS (IMHO it's never a "pointless complication" to do the right thing to start with:

# Following magic lifted from "info make"
%.d: %.cpp
        $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
        sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
        rm -f $@.$$$$
include $(SRCS:.cpp=.d)

The first time you run this, you get complaints from make because the .d files don't exist, but it goes ahead and generates them anyway.
The "clean" target should have the extra line "rm -f *.d"

The "magic" code should go right at the end (not sure if that's important, but I always do it)

Maximum debug information is obtained with options "-g3 -ggdb". With these options, gdb can see the values of #defined numeric constants, like EINVAL &c
LVL 11

Author Comment

ID: 20371617
Interesting. Reading gcc docs and simplifying it considerably I'm now with:
sources = a.cpp b.cpp c.cpp

incdirs  = /usr/blabla/inc /usr/foe/inc
libdirs  = /usr/blabla/lib
libdeps = mylib1 m pthread
CPPFLAGS =  -DNDEBUG $(foreach dir,$(incdirs),-I$(dir))
CXXFLAGS =  -m64 -O -fPIC -fexceptions

LDLIBS = $(foreach d,$(libdirs),-L$(d)) $(foreach l,stdc++ $(libdeps),-l$(l))

prog = $(firstword $(sources:.cpp=))
$(prog): $(sources=:cpp=.o)

-include $(sources:.cpp=.d)
%.d: %.cpp
        $(CC) -MM -MF $@ -MT $(<:.cpp=.o) -MT $@ $(CPPFLAGS) $<

.PHONY : clean
        -$(RM) $(prog) $(sources=:cpp=.o) $(sources=:cpp=.d)
Differences: this generates and uses include-dependencies (with just 3 more lines). I replaced the ugly multi-step gcc/sed way which also uses intermediate files, with the more elegant gcc -MM option (of course you need GNU for this). The dep include is actually a "-include" so you don't get the warning if the dep is missing (it is just regenerated), "clean" is declared .PHONY and the "rm" command in clean has the leading "-" to ignore errors. Only predefined macros are uppercase, others are lowercase.
I agree that if you have MANY lib dependencies it is probably more elegant to declare each lib together with its libdir, also if you have just 1 or 2 lib deps, its' probably cleaner to just set LDLIBS directly without using the 'foreach' stuff; if you have a few libs I think this is a good compromise.

I'm quite happy now with this makefile and ready to give points unless someone has more comments...
LVL 17

Expert Comment

ID: 20372387
Thanks for sharing, lbertacco. This has been educational.
LVL 11

Author Comment

ID: 25033367
Well, to avoid building dependencies when doing a "make clean", which deletes them, the "include" statement can be surrounded by the following conditional:

ifneq ($(MAKECMDGOALS),clean)
-include $(sources:.cpp=.d)
LVL 11

Expert Comment

ID: 25048342
The if..endif construct is specific to gnumake.  If that is your platform and you don't intend shifting to something else like AIX, Solaris, HPUX or Windows, then it isn't a problem.  If you do then you may get several variants of makefile.

One of the problems we used to have in dependency checking was that it dug deep into the includes from the C library.  These varied depening on the machine so the dependency table was being rebuilt whenever it was moved to a machine with different spec hardware.  To avoid this, we doctored the dependency files to stop once they got to files from the C library.
LVL 11

Author Comment

ID: 25048636
Yes as you can see from the origianal question, this whole discussion is gnu make specific.

Featured Post

Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

Join & Write a Comment

Entering a date in Microsoft Access can be tricky. A typo can cause month and day to be shuffled, entering the day only causes an error, as does entering, say, day 31 in June. This article shows how an inputmask supported by code can help the user a…
If you’re thinking to yourself “That description sounds a lot like two people doing the work that one could accomplish,” you’re not alone.
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.
In this fourth video of the Xpdf series, we discuss and demonstrate the PDFinfo utility, which retrieves the contents of a PDF's Info Dictionary, as well as some other information, including the page count. We show how to isolate the page count in a…

757 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now