8

I have duplicated pattern rules for several extensions (eg: cpp and cc):

$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
    @$(CXX) $(CPPFLAGS) -I. -o $@ -c $?

$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cc
    @$(CXX) $(CPPFLAGS) -I. -o $@ -c $?

Is there a way to have one pattern rule which matches on both extensions, rather than having to have two rules?

Steve Lorimer
  • 27,059
  • 17
  • 118
  • 213

2 Answers2

7

No, you can't combine the two rules. Prerequisites all have to match.

But you can avoid needing to have the recipe specified twice.

Either by using a define for the recipe:

define COMPILE
@$(CXX) $(CPPFLAGS) -I. -o $@ -c $?
endef

$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
        $(COMPILE)

$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cc
        $(COMPILE)

Or by using a loop and eval to define the recipes (untested I may have gotten some of the escaping wrong but I prefer the define approach anyway):

$(foreach prereq,cc cpp,$(eval $(OBJ_DIR)/%.o: $(SRC_DIR)/%.$(prereq) ; @$(CXX) $(CPPFLAGS) -I. -o $@ -c $?))
Etan Reisner
  • 77,877
  • 8
  • 106
  • 148
  • Accepted @Louis's answer, as it is technically correct, even though the syntax requirements for `.SECONDEXPANSION` mean I'll use your approach. – Steve Lorimer Jan 15 '15 at 03:10
  • The advantage of the "foreach" approach, is that it can be generalized for multiple extensions (think: image formats). I expanded on this [in this answer](https://stackoverflow.com/a/47857821/193789). – kebs Dec 17 '17 at 22:57
3

You can use .SECONDEXPANSION for this. Here's an executable example:

OBJ_DIR:=obj
SRC_DIR:=src

TARGETS:=obj/foo.o obj/bar.o

all: $(TARGETS)

.SECONDEXPANSION:
$(OBJ_DIR)/%.o: $$(wildcard $(SRC_DIR)/%.cpp) $$(wildcard $(SRC_DIR)/%.cc)
    @$(CXX) $(CPPFLAGS) -I. -o $@ -c $<

Create src/foo.cc and src/bar.cpp, and issue make -n, and you'll get:

g++  -I. -o obj/foo.o -c src/foo.cc
g++  -I. -o obj/bar.o -c src/bar.cpp

If you have both foo.cc and foo.cpp this will behave just like the version in the question asked here (foo.cpp will have precedence over foo.cc, which will be ignored).

The $ signs are doubled for $$(wildcard... to prevent evaluation during the first expansion. You could have $$(SRC_DIR) just as well as $(SRC_DIR) since it does not matter (in the code above) when this variable is expanded.

Louis
  • 146,715
  • 28
  • 274
  • 320
  • 4
    I don't kn ow about you, but this looks like black magic to me, and I've been using `make` on and off for the past 30 years. – reinierpost Jan 15 '15 at 00:44
  • Does this actually work? I'd have expected this to need to be more like `$(OBJ_DIR)/%.o: $$(wildcard $$(SRC_DIR)/$$*.cpp) $$(wildcard $$(SRC_DIR)/$$*.cc)` To wildcard on the matched stem. Also I wonder if this might have problems in more complicated scenarios. But it is clever. – Etan Reisner Jan 15 '15 at 12:23
  • 1
    @EtanReisner Did you try it? I did. – Louis Jan 15 '15 at 12:24
  • No, hence the question. I don't have a good model for when patterns are matched... oh, I see what this is doing I think. The pattern expands first and then wildcard just drops a path that doesn't exist. I was thinking about it the other way (which didn't make sense). – Etan Reisner Jan 15 '15 at 12:26