How to generate a Makefile with source in sub-directories using just one makefile

Viewed 148302

I have source in a bunch of subdirectories like:

src/widgets/apple.cpp
src/widgets/knob.cpp
src/tests/blend.cpp
src/ui/flash.cpp

In the root of the project I want to generate a single Makefile using a rule like:

%.o: %.cpp
   $(CC) -c $<

build/test.exe: build/widgets/apple.o build/widgets/knob.o build/tests/blend.o src/ui/flash.o
   $(LD) build/widgets/apple.o .... build/ui/flash.o -o build/test.exe

When I try this it does not find a rule for build/widgets/apple.o. Can I change something so that the %.o: %.cpp is used when it needs to make build/widgets/apple.o ?

7 Answers

The reason is that your rule

%.o: %.cpp
       ...

expects the .cpp file to reside in the same directory as the .o your building. Since test.exe in your case depends on build/widgets/apple.o (etc), make is expecting apple.cpp to be build/widgets/apple.cpp.

You can use VPATH to resolve this:

VPATH = src/widgets

BUILDDIR = build/widgets

$(BUILDDIR)/%.o: %.cpp
      ...

When attempting to build "build/widgets/apple.o", make will search for apple.cpp in VPATH. Note that the build rule has to use special variables in order to access the actual filename make finds:

$(BUILDDIR)/%.o: %.cpp
        $(CC) $< -o $@

Where "$<" expands to the path where make located the first dependency.

Also note that this will build all the .o files in build/widgets. If you want to build the binaries in different directories, you can do something like

build/widgets/%.o: %.cpp
        ....

build/ui/%.o: %.cpp
        ....

build/tests/%.o: %.cpp
        ....

I would recommend that you use "canned command sequences" in order to avoid repeating the actual compiler build rule:

define cc-command
$(CC) $(CFLAGS) $< -o $@
endef

You can then have multiple rules like this:

build1/foo.o build1/bar.o: %.o: %.cpp
    $(cc-command)

build2/frotz.o build2/fie.o: %.o: %.cpp
    $(cc-command)

This does the trick:

CC        := g++
LD        := g++

MODULES   := widgets test ui
SRC_DIR   := $(addprefix src/,$(MODULES))
BUILD_DIR := $(addprefix build/,$(MODULES))

SRC       := $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.cpp))
OBJ       := $(patsubst src/%.cpp,build/%.o,$(SRC))
INCLUDES  := $(addprefix -I,$(SRC_DIR))

vpath %.cpp $(SRC_DIR)

define make-goal
$1/%.o: %.cpp
    $(CC) $(INCLUDES) -c $$< -o $$@
endef

.PHONY: all checkdirs clean

all: checkdirs build/test.exe

build/test.exe: $(OBJ)
    $(LD) $^ -o $@


checkdirs: $(BUILD_DIR)

$(BUILD_DIR):
    @mkdir -p $@

clean:
    @rm -rf $(BUILD_DIR)

$(foreach bdir,$(BUILD_DIR),$(eval $(call make-goal,$(bdir))))

This Makefile assumes you have your include files in the source directories. Also it checks if the build directories exist, and creates them if they do not exist.

The last line is the most important. It creates the implicit rules for each build using the function make-goal, and it is not necessary write them one by one

You can also add automatic dependency generation, using Tromey's way

Usually, you create a Makefile in each subdirectory, and write in the top-level Makefile to call make in the subdirectories.

This page may help: http://www.gnu.org/software/make/

Related