Link to home
Start Free TrialLog in
Avatar of lbertacco
lbertacco

asked on

basic GNU makefile for small c++ prog

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=myprog

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

clean:
        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
Avatar of evilrix
evilrix
Flag of United Kingdom of Great Britain and Northern Ireland image

Various shortcuts and optimizations for makefiles are discussed here: -

http://www.eng.hawaii.edu/Tutor/Make/4.html
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?
SOLUTION
Avatar of cup
cup

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of yuy2002
yuy2002

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.
The GNU make manual is a good starting place for this. It's written in a very accessible way. See:

http://www.gnu.org/software/make/manual/make.html
Avatar of lbertacco

ASKER

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:
LINK.cc = $(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.
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)

clean:
        $(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.
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.

e.g.

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.
Incidentally, using += makes it more legible:

 MYLIBDIRLIBS  = /usr/blabla/lib,x:y
 MYLIBDIRLIBS += /usr/foofoo/lib,z
-DNDEBUG $(foreach dir,$(MYINCDIRS),-I$(dir))
should go in CPPFLAGS, because they are preprocessor directives
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
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
CFLAGS = -c -DWMVT_EXPORTS
LFLAGS = -lc -lm
CSFLAGS = -qmkshrobj -bnoentry
EXPCOPT = -bE:wmvt.exp
OBJS = xxxxxx.o \
       yyyyyy.o \
       zzzzzz.o \
       abcdefgh.o
             
# rule(s)
# creates object from source.
.c.o:
      $(CC) $(CFLAGS) $<

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

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

Note, the xlc wanted a <TAB> char preceeding any statement line following a target or rule.
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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
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...
Thanks for sharing, lbertacco. This has been educational.
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)
endif
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.
Yes as you can see from the origianal question, this whole discussion is gnu make specific.