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?
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:
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)