24

I'm using UnitTest++ to allow me to create unit tests for some C++ code (that should build on Linux or Mac OS X). I have a directory structure like this:

src
- Foo.cpp
- Bar.cpp
test
- FooTest.cpp
- BarTest.cpp
- Main.cpp
- Makefile
UnitTest++
- libUnitTest++.a

And this Makefile (adapted from the UnitTest++ Makefile) works nicely (with GNU make):

test = TestFooAndBar

src = ../src/Foo.cpp \
    ../src/Bar.cpp

test_src = Main.cpp \
    FooTest.cpp \
    BarTest.cpp

lib = ../UnitTest++/libUnitTest++.a

objects = $(patsubst %.cpp,%.o,$(src))
test_objects = $(patsubst %.cpp,%.o,$(test_src))


.PHONY: all
all: $(test)
    @echo Running unit tests...
    @./$(test)

$(test): $(lib) $(test_objects) $(objects)
    @echo Linking $(test)...
    @$(CXX) $(LDFLAGS) -o $(test) $(test_objects) $(objects) $(lib)

.PHONY: clean
clean:
    -@$(RM) -f $(objects) $(test_objects) $(test) 2> /dev/null

%.o : %.cpp
    @echo $<
    @$(CXX) $(CXXFLAGS) -c $< -o $(patsubst %.cpp,%.o,$<)

But I want to put all the .o files in an "obj" subdirectory of the "test" directory. How do I modify this Makefile to do so?

I've tried adding "obj/" to the objects and test_objects variables, but I can't figure out how to modify the %.o rule so it knows where the .o files are and refers to the correct .cpp files. Do I need to create two separate rules, one for each set of .cpp files?

Would it be simpler if instead of defining the src and test_src variables, I just have the Makefile build a .o (into obj/) for all .cpp files (both in the same directory as the Makefile and in ../src/)?

Daryl Spitzer
  • 143,156
  • 76
  • 154
  • 173

1 Answers1

37

There's more than one way to do it, but this is a pretty good one (I really should have that hotkeyed).

vpath %.cpp ../src

src = Foo.cpp Bar.cpp 
test_src = Main.cpp FooTest.cpp BarTest.cpp 

objects = $(patsubst %.cpp,obj/%.o,$(src)) 
test_objects = $(patsubst %.cpp,obj/%.o,$(test_src)) 

$(objects): | obj

obj:
  @mkdir -p $@

obj/%.o : %.cpp
  @echo $< 
  @$(CXX) $(CXXFLAGS) -c $< -o $@
Pancho
  • 506
  • 1
  • 4
  • 9
Beta
  • 96,650
  • 16
  • 149
  • 150
  • 7
    What does the "|" in the line `$(objects): | obj` stands for? – Anton Daneyko Jan 17 '13 at 13:36
  • 16
    @mezhaka, it means that the prerequisites that follow (in this case `obj`) are [order-only prerequisites](http://www.gnu.org/software/make/manual/make.html#Prerequisite-Types). This means that if any `$(objects)` must be built then `obj` must be built first, but if `obj` is out of date (or doesn't exist), that does *not* force `$(objects)` to be built. – Beta Jan 17 '13 at 14:57
  • Uh, it's so dense... where do the results of vpath go? how do I get access to it's contents? What does this even mean? "objects = $(patsubst %.cpp,obj/%.o,$(src))" – MarcusJ Jun 30 '17 at 00:45
  • 1
    @MarcusJ I'm also new to Makefiles, so I don't know about the vpath questions... I'm wondering that myself. The `objects=...` bit looks at every filename in the source file string, and replaces all occurrences of `%.cpp` with `obj/%.o`, where `%` is a wildcard. You can find a good explanation for this usage in the [patsubst function manual](https://www.gnu.org/software/make/manual/html_node/Text-Functions.html#Text-Functions). Also, see the [GNU make manual](https://www.gnu.org/software/make/manual/html_node/index.html#Top). – drmuelr Jun 30 '17 at 17:24