larsks
larsks

Reputation: 312038

DRY out my Makefile: avoid duplicating Makefile recipes when commands differ by a single derivable parameter

I am using a Makefile to generate templates from a set of parameter files. The parameter files are named values-<something>.yml, and the resulting templates are named template-<something>-<network_mode>.yml, where <network_mode> can be either pod or bridge.

The command to generate the two templates is identical with the exception of the network mode parameter (which can be derived from the target filename).

I was hoping I could write a rule like this:

%-template-bridge.yml %-template-pod.yml: values-%.yml
    NM=$@; NM=$${NM#*template-}; NM=$${NM%.yml}; \
    echo "something something network mode $$NM" > $@ || {rm -f $@; exit 1}

Unfortunately, while this works great when asking for individual files:

$ make cirros-template-bridge.yml
touch "cirros-template-bridge.yml"
$ make cirros-template-pod.yml
touch "cirros-template-pod.yml"

It doesn't work when asking for multiple files:

$ make cirros-template-bridge.yml cirros-template-pod.yml
touch "cirros-template-bridge.yml"
make: Nothing to be done for 'cirros-template-pod.yml'.
$ make cirros-template-bridge.yml cirros-template-pod.yml
make: 'cirros-template-bridge.yml' is up to date.
touch "cirros-template-pod.yml"

Note the first time it generate cirros-template-bridge.yml, but refused to generate cirros-template-pod.yml until it was invoked a second time. I assume this is because the syntax I'm trying to use means "the following recipe will generate both of these files", which isn't true.

What's the solution here? I can obviously create two separate pattern rules, like this...

%-template-pod.yml: values-%.yml
    echo "something something network mode pod" > $@ || {rm -f $@; exit 1}

%-template-bridge.yml: values-%.yml
    echo "something something network mode bridge" > $@ || {rm -f $@; exit 1}

...but this means I need to keep the command line for the two recipes in sync. I really want something like a Make "subroutine". Is there a better option?

Upvotes: 0

Views: 54

Answers (1)

MadScientist
MadScientist

Reputation: 100956

If you had lots of these you could do something like create a foreach loop with eval etc. but since you just have two rules my opinion is that the simplest solution is to put the recipe into a variable and use it in both rules like this:

MAKE_TEMPLATE = NM=$@; NM=$${NM\#*template-}; NM=$${NM%.yml}; \
    echo "something something network mode $$NM" > $@ || { rm -f $@; exit 1; }

%-template-pod.yml: values-%.yml
        $(MAKE_TEMPLATE)
%-template-bridge.yml: values-%.yml
        $(MAKE_TEMPLATE)

Upvotes: 1

Related Questions