[Top][All Lists]

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

Re: Rule-specific variables

From: Brian Dessent
Subject: Re: Rule-specific variables
Date: Mon, 28 Apr 2008 10:26:47 -0700

Jeffrey Ratcliffe wrote:

> I'm trying to optionally add arguments to a command, depending on the
> target, as shown by my (obviously non-working) example:
> %.xdb : %.bdfi
>         var=1
>         [ $(basename $(@F)) = part_of_filename ] && var=2
>         command --var=$(var) $(basename $(@F)).bdfi

Everything that is part of the recipe (i.e. starts with a TAB) is passed 
directly to the shell after undergoing variable expansion, so any 
variable assignments that occur are in the domain of the shell and not 
make.  Try the following two commands to understand why this won't work:

sh -c 'var=1'
sh -c 'echo var is: $var'

This is essentially what you are telling make to invoke with your 
makefile, and as you can see any assignment to var in the first command 
is invisible to the second one, because any changes to 'var' in the 
child process effectively disappear when that child terminates. 
Therefore if you want to use shell variables you need to write the 
recipe so that it uses a single shell invocation, e.g.

sh -c 'var=1; echo var is: $var'

You can still write the recipe on multiple lines but you must use '\' at 
the end of each line and separate each command with ';' so that 
logically it's still one single command.

Moreover, using this technique 'var' is a shell variable not a 'make' 
variable so you cannot refer to it as $(var).  You need for the shell to 
see it as $var (or ${var}) so you must write it as $$var (or $${var}) in 
the makefile, so that make passes the $ through to the shell rather than 
trying to expand it as an (empty) make variable.  See section 5.1.2 
("Using Variables in Recipes") of the manaul.  You might end up with 
something like:

%.xdb : %.bdfi
        var=1; \
        [ $(basename $(@F)) = part_of_filename ] && var=2; \
        command --var=$$var $<

There is another way to handle this, however.  You can do all the logic 
using 'make' functions:

%.xdb : %.bdfi
        command --var=$(if $(findstring part_of_filename,$(basename 
$(@F))),2,1) $<

This is potentially more efficient since 'make' expands the $(if ...) 
expression before passing it to the shell, avoiding the need for the 
shell to have to fork/exec the 'test' binary to evaluate the expression 
(although most common Bourne shell implementations do have a 'test' 
builtin to avoid this cost.)  But even still it will likely be faster to 
use $(if ...) since make has an optimization where it can skip the shell 
and invoke the command directly if the command is simple, e.g. if after 
expansion it contains no shell metacharacters or shell constructs like 
redirection or quoting or &&.

The flip side of the coin is that using functions like $(if ...) is GNU 
make specific and so your makefile will not be usable with other 'make' 
implementations, whereas doing it in the shell does not depend on any 
'make' features.  However, since you are already using $(basename ...) 
you most likely don't care about portability since that's specific to 
GNU make too, AFAIK.


reply via email to

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