0

I have the following file tree (for example):

foo/1213.csv
foo/7789.csv

far/8471.csv
far/7811.csv

boo/8109.csv

bar/2778.csv
bar/3334.csv
bar/1337.csv

Now, I want for each foo far boo bar directory to create a qux.json inside of it, which is dependant on all of *.csv files in that directory.

So far, it shouldn't be to difficult - I believe something like this would work (correct me if I'm wrong):

%/qux.json: %/*.csv
    my-script.py -o $@ $^

The point where I get stuck is when there are few "classes" of rules and for each class have a different recipe, for example something like:

CLASS1=foo far
CLASS2=boo bar

$(patsubst %,%/qux.json,$(CLASS1)): $(patsubst %,%/*.csv,$(CLASS1))
    my-script1.py -o $@ $^

$(patsubst %,%/qux.json,$(CLASS2)): $(patsubst %,%/*.csv,$(CLASS2))
    my-script2.py -o $@ $^

But running make foo/qux.json fails with make: *** No rule to make target 'far/*.csv', needed by 'foo/qux.json'. Stop..

I tried many different things, like:

$(patsubst %,%/qux.json,$(CLASS1)): $(wildcard $(patsubst %/qux.json,%/*.csv,$@))
    my-script1.py -o $@ $

No dice.

Essentially, I have several types of subdirectories. I can enumerate each member to a makefile variable. Each subdirectory contains random number of input csv files. I want to create a json file inside each directory from all its csv files. But each class of directories needs to run a different script.

I tried reading through man files, I've tries searching on the internet and SO, but this s too specific for any search result and too difficult for me to figure out through manual.

2
  • But this fails. "Fails" how exactly? Does the csv files exists before the rule is run?
    – KamilCuk
    Commented Dec 27, 2020 at 14:13
  • 1
    I've edited the question to add a fail example. Commented Dec 27, 2020 at 14:30

1 Answer 1

1

Functions always looked clearer to me then %:

CLASS1=foo far
CLASS2=boo bar

define f
$1/qux.json: $(wildcard $1/*.csv)
    $2
endef

$(foreach i, $(CLASS1), $(eval $(call f, $i,my-script1.py -o $$@ $$^)))
$(foreach i, $(CLASS2), $(eval $(call f, $i,my-script2.py -o $$@ $$^)))

But really, writing the following takes less time:

foo/qux.json: $(wildcard foo/*.csv)
     my-script1.py -o $@ $^
boo/qux.json: $(wildcard boo/*.csv)
     my-script2.py -o $@ $^
far/qux.json: $(wildcard far/*.csv)
     my-script1.py -o $@ $^
bar/qux.json: $(wildcard bar/*.csv)
     my-script2.py -o $@ $^
2
  • I guess this would work. However is there no other way then using defines and for each's? Few additional questions though: what is the eval do here? But regarding the "unrolling" a second example, wouldn't it matter that there's a code duplication? There would be as many same rules as to how many are there in each class. Commented Dec 27, 2020 at 14:35
  • is there no other way then using defines and for each's? The problem is you can't use %/qux.json: $(wildcard %/*.csv), because wildcard is expanded before the % is replaced. And you can't use $@ in rule dependencies either - it's empty. what is the eval do here? Parses the target: files stuff, I do not remember to be honest. Try without. wouldn't it matter that there's a code duplication? Is a question to you, I don't care for 8 lines. Typical automation of short tasks.
    – KamilCuk
    Commented Dec 27, 2020 at 15:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .