[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Checking alternatives for a dynamic make rule construction

From: Paul Smith
Subject: Re: Checking alternatives for a dynamic make rule construction
Date: Sat, 17 Jun 2017 10:23:50 -0400

On Sat, 2017-06-17 at 16:05 +0200, SF Markus Elfring wrote:
> my_test_command?=cat
> define my_rule=
> ${1:.in=.txt}: ${1}
>         $${my_test_command} $$< > $$@
> endef
> $(eval $(call my_rule,MOTD.in))
> This result is fine. Please try the following approach out again.
> my_test_command?=cat
> define my_broken_rule=
> name::=${1}
> ${name:.in=.txt}: ${1}
>         $${my_test_command} $$< > $$@
> endef
> $(eval $(call my_broken_rule,MOTD.in))

Yes, this will not work.  The reason is the combination of "eval" and
"call" together.

The inner function ("call") will expand its argument as a
straightforward string: it has no concept of makefile syntax and has no
idea that in some contexts variables are to be expanded immediately and
in some they should be deferred.  That's why you have to escape the
variables in the recipe, like $$@ etc.

The best way to debug eval issues is to replace the eval function with
"info".  Then make will print out the string that eval would have
parsed.  In your first example if we do that:

  define my_rule=
  ${1:.in=.txt}: ${1}
          $${my_test_command} $$< > $$@

  $(info $(call my_rule,MOTD.in))

we see that the result of "call", before "eval", is what we expect; the
variable $1 is replaced with the value MOTD.in and the pattern
substitutions take effect:

  MOTD.txt: MOTD.in
          ${my_test_command} $< > $@

In your second example if we make the "info" substitution we see:

  define my_broken_rule=
  ${name:.in=.txt}: ${1}
          $${my_test_command} $$< > $$@

  $(info $(call my_broken_rule,MOTD.in))

We get this:

  : MOTD.in
          ${my_test_command} $< > $@

Why is this?  This happens because "call" doesn't actually evaluate any 
make commands, so the variable assignment in the first line
("name::=...") has not taken effect.  So in the second line where we
have "${name:.in=.txt}" the expanded value of the "name" variable here
will not be the assignment on the previous line, which has not been
evaluated as a make command: it will be the value of the variable "name"
which already exists... in this case, there is no existing value so you
get the empty string.

If you want to consider a much more confusing situation consider this:

  define my_broken_rule=
  ${name:.in=.txt}: ${1}
          $${my_test_command} $$< > $$@

  $(eval $(call my_broken_rule,MOTD.in))
  $(eval $(call my_broken_rule,FOO.in))
  $(eval $(call my_broken_rule,BAR.in))

In this situation, rules are properly defined for the first two
values (MOTD.in and FOO.in) but NOT the last one (BAR.in)...!!

If you can see why that is the case then you'll be well on your way to
really grokking eval and call (which is a pretty advanced topic).

Generally I recommend that people DO NOT try to create local variables
inside define'd makefile sections and instead just write out the
reference you want each time.  It's more verbose, but has many fewer

reply via email to

[Prev in Thread] Current Thread [Next in Thread]