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 Dec 27 '20 at 14:13
  • 1
    I've edited the question to add a fail example. – JonnyRobbie Dec 27 '20 at 14:30
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. – JonnyRobbie Dec 27 '20 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 Dec 27 '20 at 15:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.