views:

39

answers:

2

Hi!

I have a directory in which I keep adding different C++ source files, and generic Makefile to compile them. This is the content of the Makefile:

.PHONY: all clean

CXXFLAGS = -pipe -Wall -Wextra -Weffc++ -pedantic -ggdb

SRCS = $(wildcard *.cxx)
OBJS = $(patsubst %.cxx,%.out,$(SRCS))

all: $(OBJS)

clean:
    rm -fv $(OBJS)

%.out: %.cxx
    $(CXX) $(CXXFLAGS) $^ -o $@

NOTE: As is obvious from above, I am using *.out for executable file extensions (and not for object file).

Also, there are some files which are compiled together:

g++ file_main.cxx file.cxx -o file_main.out

To compile such files, until now I have been adding explicit rules in the Makefile:

file_main.out: file_main.cxx file.cxx

file.out: file_main.out
    @echo "Skipping $@"

But now my Makefile has a lot of explicit rules, and I would like to replace them with a simpler implicit rule.

Any idea how to do it?

A: 

It seems that you are putting the source code for multiple different programs in the same folder, and this is really the source of your problems. If you separate the source code for your libraries and programs into separate folders (or, better yet, separate projects), then you can skirt this issue by depending on all source files in the given folder. When you have everything intermixed, it is necessary to be explicit.

That said, if your dependencies have consistent, predictable names, then it is possible to eliminate this redundancy by using the eval function. For example, based on the example above:

#
# I'm going to use standard file extensions here,
# slightly deviating from your conventions. I am also
# assuming that there is a variable named PROGNAMES,
# which gives a list of all the programs to be built.
#
define ADD_EXECUTABLE
     $(1): $(1).o $(1)_main.o
         $(LINK.cc) $(1).o $(1)_main.o -o $(1)
endef

$(foreach progname,$(PROGNAMES),$(eval $(call ADD_EXECUTABLE,$(progname)))) 

Also, just a few suggestions... you should append to CXXFLAGS rather than overwrite it and you would be better off using standard file extensions (".cpp" for C++ source files, ".o" for object files, no extension for executables). See my Makefile tutorial for tips on making things easier with Make (no pun intended).

Michael Aaron Safyan
Why not just use a pattern rule: `$(PROGNAMES): % : %.o %_main.o ; $(LINK.cc) $^ -o $@`? (And I think you mean `endef`.)
Beta
@Beta, thanks. Fixed the endef. I think my version is clearer, but what you have suggested is also valid.
Michael Aaron Safyan
+1  A: 

First, this method of compiling several source files directly into an executable is not a terribly good idea. The more common compile-then-link approach will save a lot of unnecessary compilation.

That said, the way to replace many explicit rules with a simpler rule depends on what the explicit rules have in common. You already have a pattern rule:

%.out: %.cxx
    $(CXX) $(CXXFLAGS) $^ -o $@

and if all you want to do is add another source file to a particular target, you don't have to do this:

g++ file_main.cxx file.cxx -o file_main.out

you can get the effect just by adding a prerequisite (in a line by itself):

file_main.out: file.cxx

If you have several targets with that pattern, you can use a pattern rule:

file_main.out another_main.out a_third_main.out: %_main.out : %.cxx

If you have many such targets, you can use a variable:

MAIN_THINGS = file another a_third a_fourth and_yet_another
MAIN_TARGETS = $(addsuffix _main.out, $(MAIN_THINGS))
$(MAIN_TARGETS): %_main.out : %.cxx

And you can add other patterns for other target sets, even overlapping sets. Does that cover your situation?

Beta