Reputation: 6606
Is there a simple way to apply a single recipe to multiple pattern rules?
Consider this directory structure
|- src
| - file1.cpp
|- test
| - file2.cpp
| - file3.cpp
| - metrics
| - file4.cpp
| - file5.cpp
I would like to write a single pattern rule to compile all the .cpp files in the test directory. Here is what I have right now:
$(OBJS)/%.o: test/%.cpp
@mkdir -p $(OBJS)
g++ $(FLAGS) $(CPPFLAGS) -c $< -o $@
$(OBJS)/%.o: test/metrics/%.cpp
@mkdir -p $(OBJS)
g++ $(FLAGS) $(CPPFLAGS) -I test -c $< -o $@
TEST_CPP := $(wildcard test/*.cpp) $(wildcard test/**/*.cpp)
TEST_OBJ := $(addprefix $(OBJS)/,$(notdir $(TEST_CPP:.cpp=.o)))
$(BIN)/testRunner: $(TEST_OBJ)
@mkdir -p $(BIN)
g++ $(FLAGS) $(CPPFLAGS) $^ $(LIBS) -o $@
I would like to avoid repeating the recipe for the object files. I imagine the solution would look something like this:
$(OBJS)/%.o: test/%.cpp
$(OBJS)/%.o: test/metrics/%.cpp
@mkdir -p $(OBJS)
g++ $(FLAGS) $(CPPFLAGS) -c $< -o $@
(At the moment, the objs directory is flat; but, I don't have a problem duplicating the source directory structure if that simplifies the makefile.)
Upvotes: 4
Views: 2176
Reputation: 100836
There is no way to do what you want. The closest you can get is to put the recipe into a variable and use that same variable in each recipe, like this:
define BUILD_O
@mkdir -p $(OBJS)
g++ $(FLAGS) $(CPPFLAGS) -c $< -o $@
endef
$(OBJS)/%.o : test/%.cpp
$(BUILD_O)
$(OBJS)/%.o : test/metrics/%.cpp
$(BUILD_O)
As an alternative to that, because you want all your objects to go into the same directory but find sources from different directories, you could use VPATH instead and write just a single pattern rule, like this:
VPATH = test test/metrics
$(OBJS)/%.o : %.cpp
@mkdir -p $(@D)
g++ $(FLAGS) $(CPPFLAGS) -c $< -o $@
I strongly urge you to rework your makefiles to use the standard variable names, though; use $(CXX)
, not g++
, for the C++ compiler name and $(CXXFLAGS)
, not $(FLAGS)
, for the C++ compiler options.
EDIT
If you need to customize flags, then the normal way to do this is either through target-specific variables, or through constructed macro names
Target-specific variables would look like this:
test: $(test_targets)
test: FLAGS += -Dtest
production: $(production_targets)
production: FLAGS += -Dproduction
then when you run make test
you'll get the -Dtest
added; when you run make production
you'll get the -Dproduction
added. There are issues here though: if you run make myfoo.o
then you won't get these added (see the documentation).
Constructed macro names would look like this:
test_FLAGS = -Dtest
production_FLAGS = -Dproduction
rootdir = $(firstword $(subst /, ,$1))
VPATH = test test/metrics production production/widgets
$(OBJS)/%.o : %.cpp
@mkdir -p $(@D)
g++ $(FLAGS) $(CPPFLAGS) $($(call rootdir,$(<D))_FLAGS) -c $< -o $@
Upvotes: 4