AnJoElLp 4532110
AnJoElLp 4532110

Reputation: 51

Makefile patsubst apply pattern to filename not to full path

I am creating a Makefile to build an application with Qt using moc.

MOC_HEADERS=include/test.hpp include/test/test.hpp

MOC_SRCS=$(patsubst include/%.hpp,build/moc/moc_%.cpp,${MOC_HEADERS})

The first problem is that include/test/test.hpp will be converted to build/moc/moc_test/test.cpp and not to build/moc/test/moc_test.cpp

The second problem is that I don't know how to write this pattern target correctly:

build/moc/moc_%.cpp: include/%.hpp
    ${MOC} $< -o $@

Can someone help me?

Upvotes: 0

Views: 47

Answers (1)

Renaud Pacalet
Renaud Pacalet

Reputation: 28910

The following is for GNU make only.

To compute MOC_SRCS you could play with various built-in functions (patsubst, dir, notdir). You could also create your own user functions. Example where we define a function to convert a header filename to a source filename (HPP2CPP) and the opposite (CPP2HPP):

HPP2CPP = $(patsubst include%,build/moc%,$(dir $1))$(patsubst %.hpp,moc_%.cpp,$(notdir $1))
CPP2HPP = $(patsubst build/moc%,include%,$(dir $1))$(patsubst moc_%.cpp,%.hpp,$(notdir $1))

MOC_HEADERS := include/test.hpp include/test/test.hpp
MOC_SRCS    := $(foreach h,$(MOC_HEADERS),$(call HPP2CPP,$h))

For your rule, things are more complex because pattern rules do not allow multiple wildcards. You need something more powerful. There are basically 2 options:

Secondary expansion:
HPP2CPP = $(patsubst include%,build/moc%,$(dir $1))$(patsubst %.hpp,moc_%.cpp,$(notdir $1))
CPP2HPP = $(patsubst build/moc%,include%,$(dir $1))$(patsubst moc_%.cpp,%.hpp,$(notdir $1))

MOC_HEADERS := include/test.hpp include/test/test.hpp
MOC_SRCS    := $(foreach h,$(MOC_HEADERS),$(call HPP2CPP,$h))

.PHONY: all
.DEFAULT_GOAL := all

all: $(MOC_SRCS)

.SECONDEXPANSION:

$(MOC_SRCS): $$(call CPP2HPP,$$@)
    $(MOC) $< -o $@

All rules after the special .SECONDEXPANSION target have their list of prerequisites expanded twice: once during the parsing of the Makefile, and a second time when make checks the freshness of prerequisites versus targets. During this second expansion automatic variables are defined, while they are not during the first pass. This allows, for example, to programmatically compute prerequisites from target names ($@). Note the $$ to escape the first expansion.

foreach-eval-call:
HPP2CPP = $(patsubst include%,build/moc%,$(dir $1))$(patsubst %.hpp,moc_%.cpp,$(notdir $1))
CPP2HPP = $(patsubst build/moc%,include%,$(dir $1))$(patsubst moc_%.cpp,%.hpp,$(notdir $1))

MOC_HEADERS := include/test.hpp include/test/test.hpp
MOC_SRCS    := $(foreach h,$(MOC_HEADERS),$(call HPP2CPP,$h))

.PHONY: all
.DEFAULT_GOAL := all

all: $(MOC_SRCS)

define MOC_RULE
$1: $$(call CPP2HPP,$1)
    $$(MOC) $$< -o $$@
endef
$(foreach c,$(MOC_SRCS),$(eval $(call MOC_RULE,$c)))

The eval function dynamically instantiates other make statements. Here again $$ is needed to escape the first expansion. Note that with this second approach we could use only HPP2CPP and also compute MOC_SRCS iteratively with the MOC_RULE:

HPP2CPP = $(patsubst include%,build/moc%,$(dir $1))$(patsubst %.hpp,moc_%.cpp,$(notdir $1))

MOC_HEADERS := include/test.hpp include/test/test.hpp

define MOC_RULE
$1-cpp := $$(call HPP2CPP,$1)
MOC_SRCS += $$($1-cpp)

$$($1-cpp): $1
    $$(MOC) $$< -o $$@
endef
$(foreach h,$(MOC_HEADERS),$(eval $(call MOC_RULE,$h)))

.PHONY: all
.DEFAULT_GOAL := all

all: $(MOC_SRCS)

Upvotes: 1

Related Questions