2

I want to call rule inside another rule. So I tried to write the following test makefile

var = 11

a:
    echo $(var)
    $(eval var=22)
    echo $(var)
    $(MAKE) b

b:
    echo $(var)

The problem is that when I run make a, the $(MAKE) b will still output 11, but I wish it should be 22. So my problem is : Is there a way to inherit variable values across different rules?

user15964
  • 2,507
  • 2
  • 31
  • 57

3 Answers3

2

If you're in the same file, you should avoid this altogether -- stop thinking of make as some "advanced scripting tool". It is not. It's a tool for building files doing the minimum necessary work while respecting all dependencies and therefore, your job is to exactly state these dependencies using prerequisites.

Look at this example and what it does:

var = 11

printvar:
    echo $(var)

setvar:
    $(eval var=22)

a:  printvar setvar b

b:  
    echo $(var)

.PHONY: a b printvar setvar

Note that none of these rules create an actual file. Normally, a rule should create its target, if it isn't, it must be listed as a prerequisite of the special target .PHONY to let make know this. This should be an exception -- the primary use of make is that it can decide whether it has to apply a rule by comparing the timestamps of the prerequisites with that of the target. A rule's recipe is only executed if there's a prerequisite that is newer than the target. With a .PHONY rule, the recipe has to be executed each and every time.


When talking about recursive make, the question would make some more sense. One easy way to pass a variable from a parent make process to a child is to export it to the environment. In your example, the following would do:

var ?= 11      # only set var if it doesn't have a value yet
export var     # export var to the environment, so it's available to child make

a:
    echo $(var)
    $(eval var=22)
    echo $(var)
    $(MAKE) b

b:
    echo $(var)

.PHONY: a b

Of course, this only makes sense in practice when you have different Makefiles, so not just call $(MAKE) b, but e.g. $(MAKE) -C [some subdir] b. IMHO, recursive make should be avoided as it's very hard to get the dependencies correct with recursive make. But anyways, this would work.

  • Hi, Felix Palmen. Thank you so much for answer. Your solution works. But I don't understand why use `?=`, I tried `=` and `:=` both don't work. I especially don't understand the difference between `?=` and `:=`. According to https://stackoverflow.com/questions/448910/what-is-the-difference-between-the-gnu-makefile-variable-assignments-a?rq=1 they are somewhat similiar – user15964 Oct 23 '17 at 09:11
  • I suggest you [read the fine manual about that](https://www.gnu.org/software/make/manual/html_node/Setting.html#Setting) (because the distinction between `=` and `:=` is very important, but not relevant here). In short, `?=` only sets that value if it doesn't have a value yet, and `make` also takes variables from the environment. **But again:** If you have just one Makefile, **don't do that**. It's really a misuse of `make`. –  Oct 23 '17 at 09:17
  • Hi, @Felix Palmen. I found I still don't understand. I know `?=` only sets that value if it doesn't have a value yet. But I don't see any difference between using `?=` and `=` or `?=` in your last example, they all set var equal to 11, and there is no expansion issue. But the result is that only `?=` works for your last example – user15964 Oct 30 '17 at 02:35
  • @user15964 you are **calling** `$(MAKE)` in a recipe, which starts a **second** instance of make. In this second instance, `var` is already said because the "parent" `make` exported it to the environment. Avoid recursive make... –  Oct 30 '17 at 06:15
  • Oh, so do you mean when call `$(MAKE)`, all things outside recipes in current makefile will be reevaluated, right? – user15964 Oct 30 '17 at 08:29
  • @user15964 you are *calling `make`* -- what do you expect? This starts a new instance, so yes, of course. –  Oct 30 '17 at 08:44
1

When you invoke make again from the recipe of a, you really launch a new make process that will parse again your makefile and thus, its first line, that assigns value 11 to variable var. Next, this second make invocation builds target b and echoes 11...

If you want to pass a variable value to a sub-make invocation you can do it on the command line with the make VAR=VALUE ... syntax. In your example, you could, for instance:

$ cat Makefile
var = 11

a:
    echo $(var)
    $(eval var=22)
    echo $(var)
    $(MAKE) var=$(var) b

b:
    echo $(var)

$ make a
echo 11
11
echo 22
22
make var=22 b
make[1]: Entering directory 'foo'
echo 22
22
make[1]: Leaving directory 'foo'

It works because variables that are assigned on the command line, by default, override the definitions found in the makefile (see the make manual).

This command line assignment may look strange because it seems to assign the value of variable var to itself but it does not. It assigns the current value of variable var of the first (top) make invocation to variable var of the sub-make invocation.

Renaud Pacalet
  • 25,260
  • 3
  • 34
  • 51
0

Yes. IMHO you should revert the order: ‘a’ depends on ‘b’ var = 11 b:var =22 b: a echo b:$(var) a: echo: a:$(var)

Luca_65
  • 123
  • 9