Home > Net >  How to create a multiple recipes by looping over a Makefile list?
How to create a multiple recipes by looping over a Makefile list?

Time:02-26

I have the following makefile that has multiple cp commands to copy directories from source to destination.

process:
   cp dir/level/2000/config.json output/2000/config.json
   cp dir/level/2001/config.json output/2001/config.json
   cp dir/level/2002/config.json output/2002/config.json

stage:
   mkdir -p output/2000 output/2001 output/2002
   cp dir/common/2000/esc output/2000/esc
   cp dir/common/2001/esc output/2001/esc
   cp dir/common/2002/esc output/2002/esc

apply: stage process

I want to make the above Makefile more dynamic by introducing a variable list of 2000 2001 2002 and it would then loop over the variable and run the rule every iteration. Something like...

var := 2000 2001 2002
process:
   cp dir/level/${var}/config.json output/${var}/config.json

stage:
   mkdir -p output/${var}
   cp dir/common/${var}/esc output/${var}/esc

apply: stage process

Now if I run make apply it should recreate the same steps as the first makefile. I tried using multiple targets as ${var} and it worked the way I wanted but it can only be used in place of one of the targets, not for both stage and process. I did

process:
   cp dir/level/2000/config.json output/2000/config.json
   cp dir/level/2001/config.json output/2001/config.json
   cp dir/level/2002/config.json output/2002/config.json

${var}:
   mkdir -p output/$@ 
   cp dir/common/$@/esc output/$@/esc

apply: ${var} process

Now if I run make apply, it will run as I expected but how to use the same multiple targets in place of process too?

CodePudding user response:

You would use pattern rules most likely:

apply: stage process

var := 2000 2001 2002

process: $(foreach V,$(var),output/$V/config.json)
stage: $(foreach V,$(var),output/$V/esc)

output/%/config.json: dir/level/%/config.json
        cp $< $@

output/%/esc : dir/common/%/esc
        mkdir -p $(@D)
        cp $< $@

CodePudding user response:

The simplest solution is probably to use a shell for loop in your recipe:

var := 2000 2001 2002

process:
  for year in $(var); do \
    cp dir/level/$$year/config.json output/$$year/config.json; \
  done

Note that within the recipe we need to write $$year to refer to the shell variable named year because a single $ would introduce a makefile variable reference.


Using just make syntax, you could do this:

var = 2000 2001 2002
outputs = $(patsubst %,output/%/config.json,$(var))

output/%/config.json: dir/level/%/config.json
    echo cp $< $@

all: $(outputs)

This creates a variable outputs that has the content:

output/2000/config.json output/2001/config.json output/2002/config.json

And then uses a pattern rule to implicitly create recipes for creating those outputs from the corresponding inputs.

  • Related