I have a setup like the following one (using GNU Make 4.2.1):
Makefile
main.cpp
subfolder/
a.cpp
b.cpp
etc...
I want to compile all of those .cpp into their corresponding .o in _build but preserving the directory structure, so that after compilation I have:
Makefile
_build
final_binary
main.o
subfolder/
a.o
b.o
main.cpp
subfolder/
a.cpp
b.cpp
I haven't been able to make this work properly. My makefile is something like:
OBJS := $(patsubst %.cpp,_build/%.o,main.cpp $(wildcard subfolder/*.cpp))
TARGET := _build/final_binary
DIRS := $(sort $(dir $(OBJS) $(TARGET)))
$(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) $^ $(LIBS) -o $@ $(LDPATHS)
$(OBJS) $(TARGET): %: | $(dir %)
_build/%.o: %.cpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
$(DIRS):
mkdir -p $@
But I can't make it work, and the problem seems related to the surprising fact that %
is not a regular wildcard that consumes everything against the matching target, but that removes directories and then paste the steams in very unintuitive ways, and I'm having trouble at every attempt. Another approach I tried was:
### $(OBJS) $(TARGET): %: | $(dir %)
_build/%.o: %.cpp | $(dir _build/%.o)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
but that's just me improvising honestly. The deal with %
ignoring directories makes me not fully trust anything that contains a %
.
What is the proper way to create the required folders by demand? (and by demand I mean, not writing any order-only prerequisite for $(TARGET) that creates all folders at once, but creating only the subfolders that are required for a particular .o
).
If it's worth generalizing the question at this point, this problem is really an instance of the general problem of doing cross-folder pattern matching in GNU Make.
Your problem doesn't have anything to do with how patterns match directories.
The problem is that expansion of prerequisites happens immediately when the rule is parsed, but expansion of patterns happens later. So when you write % : | $(dir %)
you are not running dir
on the thing that the pattern expands to, you are running on the literal value %
and the dir
of a word with no /
is always ./
. So you are writing % : | ./
.
Similarly in your pattern rule: _build/%.o: %.cpp | $(dir _build/%.o)
here the $(dir ..)
is expanded when the pattern rule is parsed and so you are writing _build/%.o: %.cpp | _build/
which is not what you want.
I really don't see any point in this complexity: the mkdir
solution is by far the simplest and doesn't require any specialized make features, and the build time difference cannot be measured in my opinion.
But if you do want to do this you'll have to make use of even MORE specialized make features, you'll need to use secondary expansion:
.SECONDEXPANSION:
_build/%.o: %.cpp | $$(@D)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
(note the extra $
).