4

I have something like this that works:

target1.PREREQUISITES = file11 file12 file13
target2.PREREQUISITES = file21 file22 file23

$(myDir)target1.sfx : $(target1.PREREQUISITES)
    <same recipe here>

$(myDir)target2.sfx : $(target2.PREREQUISITES)
    <same recipe here>

and this is what I want to do but it not working:

target1.PREREQUISITES = file11 file12 file13
target2.PREREQUISITES = file21 file22 file23

$(myDir)%.sfx : $(%.PREREQUISITES)
    <commun recipe here>

It always says that there is nothing to do because the target is up to date.

I have the feeling that the problem could be what is done in each make phase, I mean, what is done first the % or $. Should it be working just fine?

Guasqueño
  • 405
  • 9
  • 20

3 Answers3

4

You'll need something like secondary expansion for this to work, otherwise the expansion of the variable in the prerequisites occurs before the pattern is substituted, and you haven't defined a variable called %.PREREQUISITES.

.SECONDEXPANSION:

$(mydir)%.sfx: $$($$*.PREREQUISITES)
    echo $^
user657267
  • 20,568
  • 5
  • 58
  • 77
  • Thanks. It works, however I get a secondary undesired effect: if one of the prerequisite files is missing, for instance file12, make complains that there is no rule to make the target, rather than complaining about a lack of rule to make file12. – Guasqueño Jul 18 '17 at 13:44
  • It is unfortunate that the make manual does not have an example of a variable used in a prerequisite with a `%` sign. I did see only one example with `%` in a prerequisite but it is not a variable, it is in a function, but it is not clear for me why that example works and mine not. This is what is in the manual: `%.o: $$(addsuffix /%.c,foo bar) foo.h` – Guasqueño Jul 18 '17 at 13:52
  • 1
    Actually I just tried a test file this way and worked as well as your proposal but I don't understand why: **`$(myDir)%.sfx : $$(%.PREREQUISITES)`** and it has the same side effect as yours: it does not tell you that a prerequisite file is missing. – Guasqueño Jul 18 '17 at 14:00
3

The second expansion proposed by user657267 works well. GNU make also supports a kind of loop mechanism that you can use to instantiate several rules with very similar forms:

target1.PREREQUISITES = file11 file12 file13
target2.PREREQUISITES = file21 file22 file23
TARGETS = target1 target2

# $(1) is a parameter to substitute. The $$ will expand as $.
define MY_rule
$$(myDir)$(1).sfx : $$($(1).PREREQUISITES)
    <same recipe here>
endef
$(foreach target,$(TARGETS),$(eval $(call MY_rule,$(target))))
  • foreach loops over all words in $(TARGETS) and assigns the current word to $(target).
  • call performs an expansion of MY_rule where it substitutes $(1) with the current value of $(target) and $$ with $.
  • eval instantiates the result of the call expansion as a regular rule.

The result of the first iteration of foreach, for instance, will be:

$(eval $(call MY_rule,target1))

The call will evaluate as:

$(myDir)target1.sfx : $(target1.PREREQUISITES)
    <same recipe here>

and eval will instantiate it as a rule. Important: do not forget that call performs a first expansion. So, if your <same recipe here> contains $ symbols, do not forget to double them, unless their expansion by call is fine. If your recipe makes use of shell variables, it is even possible that you end up with things like $$$$var.

This mechanism is slightly more powerful and generic that the second expansion. It even works with more than one parameter to substitute and with nested loops:

target1.PREREQUISITES = file11 file12 file13
target2.PREREQUISITES = file21 file22 file23
TARGETS = target1 target2
DIRS = myDir

# $(1): target
# $(2): directory
define MY_rule
$(2)$(1).sfx : $$($(1).PREREQUISITES)
    <same recipe here>
endef
$(foreach target,$(TARGETS),$(foreach dir,$(DIRS),$(eval $(call MY_rule,$(target),$(dir)))))

And you can even embed a foreach-eval-call inside a define-endef:

target1.PREREQUISITES = file11 file12 file13
target2.PREREQUISITES = file21 file22 file23
TARGETS = target1 target2
DIRS = myDir

# $(1): target
# $(2): directory
define MY_rule_1
$(2)$(1).sfx : $$($(1).PREREQUISITES)
    <same recipe here>
endef

# $(1): directory
define MY_rule_2
$$(foreach target,$$(TARGETS),$$(eval $$(call MY_rule_1,$$(target),$(1))))
endef
$(foreach dir,$(DIRS),$(eval $(call MY_rule_2,$(dir))))
Renaud Pacalet
  • 25,260
  • 3
  • 34
  • 51
-2

Thanks user657267 for your answer, it does work and it gave me a good lead. For those not too familiar with make notice the double dollar sign. I am including my answer here, however, because it still uses the % sign that was part of my original question and is working as well as yours. I am using the following solution.

.SECONDEXPANSION:

$(mydir)%.sfx: $$(%.PREREQUISITES)
    echo $^

I just noticed, and be aware of it, that when using a secondary expansion make does not tell you that a missing prerequisite has no rule, instead it displays a misleading message saying that there is no rule to make the target.

Guasqueño
  • 405
  • 9
  • 20