Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x
?
Solved

basic GNU makefile for small c++ prog

Posted on 2007-11-27
19
Medium Priority
?
1,128 Views
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=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
0
Comment
Question by:lbertacco
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 6
  • 4
  • 3
  • +4
19 Comments
 
LVL 40

Expert Comment

by:evilrix
ID: 20361304
Various shortcuts and optimizations for makefiles are discussed here: -

http://www.eng.hawaii.edu/Tutor/Make/4.html
0
 
LVL 35

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?
0
 
LVL 11

Assisted Solution

by:cup
cup earned 600 total points
ID: 20361657
List of source files

SRCS=a.cxx b.cxx c.cxx
OBJS=$(SRCS:.cxx=.o)

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
...
LDFLAGS=$(LIBXWIN) $(LIBBOOST) $(LIBERATE) ...
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
LVL 4

Expert Comment

by:yuy2002
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.
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 20363789
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
0
 
LVL 11

Author Comment

by:lbertacco
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:
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.
0
 
LVL 11

Author Comment

by:lbertacco
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)

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.
0
 
LVL 17

Expert Comment

by:rstaveley
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.

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.
0
 
LVL 17

Expert Comment

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

 MYLIBDIRLIBS  = /usr/blabla/lib,x:y
 MYLIBDIRLIBS += /usr/foofoo/lib,z
0
 
LVL 35

Expert Comment

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

Author Comment

by:lbertacco
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
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
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
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

0
 
LVL 39

Expert Comment

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

Accepted Solution

by:
Duncan Roe earned 900 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
0
 
LVL 11

Author Comment

by:lbertacco
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
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...
0
 
LVL 17

Expert Comment

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

Author Comment

by:lbertacco
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)
endif
0
 
LVL 11

Expert Comment

by:cup
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.
0
 
LVL 11

Author Comment

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

Featured Post

NFR key for Veeam Agent for Linux

Veeam is happy to provide a free NFR license for one year.  It allows for the non‑production use and valid for five workstations and two servers. Veeam Agent for Linux is a simple backup tool for your Linux installations, both on‑premises and in the public cloud.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Basic understanding on "OO- Object Orientation" is needed for designing a logical solution to solve a problem. Basic OOAD is a prerequisite for a coder to ensure that they follow the basic design of OO. This would help developers to understand the b…
The SignAloud Glove is capable of translating American Sign Language signs into text and audio.
Progress
Starting up a Project

722 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