3

I have to convert a set of file (let's say format fa) into another format (fb) by a command (fa2fb). Each target fb depends only on one fa file.

Data structure is in a format like this:

source:

./DATA/L1/fa/L1.fa
./DATA/L2/fa/L2.fa
...
./DATA/Ln/fa/Ln.fa

target:

./DATA/L1/fb/L1.fb
./DATA/L2/fb/L2.fb
...
./DATA/Ln/fb/Ln.fb

How can I implement it with make?

I have tried this but of course it did not work:

./DATA/%/fb/%.fb :  ./DATA/%/fa/%.fb

    @fa2fb $< $@

Is there any simple solution without changing the data directories?

Many thanks!

gio10
  • 31
  • 1

3 Answers3

2

Use secondary expansion and the subst function to create a rule where the prerequisites are constructed as a more complex function of the target name:

.SECONDEXPANSION:
DATA/%.fb: $$(subst fb,fa,$$@)
    @fa2fb $< $@

Note that this approach assumes that fb will not occur anywhere else in the filename (which holds true if all of your filenames are of the form DATA/Ln/fb/Ln.fb, for some integer n).

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • Thank you. It works fine in my case. I'll find out how to check that fa (or fb?) is not present in the file names. In real case it may happen. – gio10 Jul 13 '11 at 10:04
1

This may be the sloppiest makefile I have ever written.

define template
 $(2) : $(1)
        echo hi
endef

sources=DATA/L1/fa/L1.fa DATA/L2/fa/L2.fa
$(foreach source,$(sources),$(eval $(call template,$(source),$(subst /fa/,/fb/,$(subst .fa,.fb,$(source))))))

The idea is to define a macro to generate your rules, then use foreach and eval+call to invoke it once for each source. The source is the first argument to the call, so it becomes $(1) in the macro. The second argument is just the transformation from a source file name to a destination file name; it becomes $(2) in the macro.

Replace echo hi with your own rule and you should be good to go. And be sure to write a nice big clear comment or someday someone will surely show up at your door with a baseball bat.

Nemo
  • 70,042
  • 10
  • 116
  • 153
  • Thank you for the suggestion. It's a bit complex but probably more robust than the other one. For the moment I'll stay with the first suggestion. The message about comments is clear! – gio10 Jul 13 '11 at 10:07
0

This is basically the same as Nemo's answer. I just tried to make the foreach call a bit more readable, by creating a list of modules, containing simply L1 L2 ... Ln, instead of the list of full source names.

MODULES := $(notdir $(wildcard ./DATA/L*))

define rule
./DATA/$(1)/fb/$(1).fb: ./DATA/$(1)/fa/$(1).fa
    @fa2fb $< $@
endef

$(foreach module, $(MODULES), $(eval $(call rule,$(module))))
eriktous
  • 6,569
  • 2
  • 25
  • 35