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.
- The simple one that places object files in a different directory and is robust against name clashes.
- 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