Home > Mobile >  Reducing two similar make rules to one general rule
Reducing two similar make rules to one general rule

Time:12-12

Let's say I have two rules for making object files (each in obj/), one that depends on the .cpp files in directory A - let's call it src - and one that depends on the .cpp files directory B, which is a subdirectory of A - call it src/demos. Their rules look like this:

$(ODIR)/%.o : $(SRC)/%.cpp $(DEPS)
    $(CXX) -c -o $@ $< $(CXXFLAGS)

$(ODIR)/%.o : $(DEMODIR)/%.cpp $(DEPS)
    $(CXX) -c -o $@ $< $(CXXFLAGS)

As you can see, I am repeating a recipe in making two rules for these directories. Is there a way I can "compress" these two rules into one? I thought about using a wildcard in some way to match to both src/*.cpp and src/demos/*.cpp, but I am not sure how that would be implemented in a rule.

Thanks in advance!

CodePudding user response:

There are two solutions to this.

  1. The simple one that places object files in a different directory and is robust against name clashes.
  2. The one that preserves your exact directory structure but is a bit more complicated and not robust against name clashes.

Preamble variables used in both solutions (they will partly look differently in your real setting)

SRC=src/
DEMODIR=src/demos/
ODIR=out/
DEPS=

sources=$(wildcard $(SRC)*.cpp) $(wildcard $(DEMODIR)*.cpp)

Solution 1 (my recommendation).

Object files follow the directory structure of source files.

objects=$(patsubst %.cpp,$(ODIR)%.o,$(sources))
$(objects): $(ODIR)%.o: %.cpp $(DEPS)
    $(CXX) -c -o $@ $< $(CXXFLAGS)

Solution 2 (your directory structure).

objects=$(patsubst %.cpp,$(ODIR)%.o,$(sources))
flatobjects=$(patsubst $(ODIR)$(SRC)%, $(ODIR)%, $(objects))
f_retrieve=$$(findstring $(join $(SRC),$(1:.o=.cpp)),$(sources)) $$(findstring $(join $(DEMODIR),$(1:.o=.cpp)),$(sources))

.SECONDEXPANSION:
$(flatobjects): $(ODIR)%.o: $(call f_retrieve,%.o) $(DEPS)
    $(CXX) -c -o $@ $< $(CXXFLAGS)

This will likely break the second you have both src/a.cpp and src/demos/a.cpp, so Solution 1 is recommended on that basis alone.

The trick is to search for each target's prequisite in the $(sources) variable for a matching source file (via findstring). Note the .SECONDEXPANSION section and the double $ before findstring. Doing this directly should work in theory, but GNU make doesn't like it inside the rule. Relaxing the expression to be expanded twice then works.

Demo (solution 2)

./src: a.cpp b.cpp c.cpp
./src/demos: x.cpp y.cpp z.cpp

Additional helper rule: flatobjects: $(flatobjects)

$ make flatobjects 
gcc -c -o out/a.o src/a.cpp 
gcc -c -o out/b.o src/b.cpp 
gcc -c -o out/c.o src/c.cpp 
gcc -c -o out/x.o src/demos/x.cpp 
gcc -c -o out/y.o src/demos/y.cpp 
gcc -c -o out/z.o src/demos/z.cpp 
  • Related