Extended variable inheritance in GNU make

I am reading the GNU make manual, and am confused about the mechanism of variable inheritance. Let me learn the basics first.


I quote from the chapter in manual 6.10 Variables from the environment:

Variables in make can come from the environment in which make is run. Each environment variable that make sees when it starts is converted to a make variable with the same name and value.

So, imagine that I open a shell (name it "shell 1"), and I define two variables. Then I run make with two options: "op1" and "op2". The make program itself reads the makefile and creates a third variable called "varC". We get the situation as shown in the figure below:

enter image description here


I continue with a quote from the manual:

When make runs the recipe, the variables defined in the makefile are placed in each shell environment.

So what am I going to do now. The first line of the recipe is executed for the purpose for which make opens a temporary shell (name it "shell 2"). I assume that all the variables "varA", "varB" and "varC" are present in this shell and, therefore, can be used in the recipe line. Although I'm not 100% sure.

enter image description here


In the manual on the case when a recipe calls to do recursively:

By default, only variables that come from the environment or the command line are passed for recursive calls. You can use the export directive to pass other variables.

The next line of the recipe is a recursive call to $(MAKE) . Toplevel make opens a temporary shell (name it "shell 3") to run this sub-make instance. Since varC was not explicitly exported, I believe it is not in shell 3 or in sub-make . Am I right?

enter image description here

I posted this topic to get clarifications from experienced script writers. I am new to this topic, but I am working hard to study the manual and get started after that. All help is appreciated :-)

PS: if you are sending a response, indicate whether your answer is for Linux, Windows, or both.

+1
source share
3 answers

I will only answer for Windows, since there is no Unix environment here. It should give a good idea of ​​how it works in GNU make .

First, I assume that the environment variables you are talking about have a life cycle associated with the life cycle of the shell, so this is not a system environment variable.

Windows has two programs for setting a variable: SET and SETX . There are probably more subtleties, but to make it simple, SET will set the variable only for the current shell and its subprocesses, and SETX set the system environment variable. I use only SET , since I do not want to deal with system environment variables.

I will give an empirical answer. I checked this setting:

 \---level1 | Makefile | \---level2 | Makefile | \---level3 Makefile 

level1 - Makefile

 LEVEL = LEVEL1 LEVEL1VAR = VAR1 varB = 12 export varB .PHONY: foo foo: @echo $(LEVEL) var level 1 : $(LEVEL1VAR) @echo $(LEVEL) varA is $(varA) @echo $(LEVEL) varB is $(varB) cd level2 & $(MAKE) foo 

level2 - Makefile

 LEVEL = LEVEL2 LEVEL2VAR = VAR2 MKID = MKID2 varC = 13 export varC .PHONY: foo foo: @echo $(LEVEL) var level 1 : $(LEVEL1VAR) @echo $(LEVEL) var level 2 : $(LEVEL2VAR) @echo $(LEVEL) varA is $(varA) @echo $(LEVEL) varB is $(varB) cd level3 & $(MAKE) foo 

level3 - Makefile

 LEVEL = LEVEL3 LEVEL3VAR = VAR3 .PHONY: foo foo: @echo $(LEVEL) var level 1 : $(LEVEL1VAR) @echo $(LEVEL) var level 2 : $(LEVEL2VAR) @echo $(LEVEL) var level 3 : $(LEVEL3VAR) @echo $(LEVEL) varA is $(varA) @echo $(LEVEL) varB is $(varB) @echo $(LEVEL) varC is $(varC) 

At the beginning of the test, I open the shell (Windows command line) in the folder level1. I create a varA variable with a value of 11:

SET varA=11

Then I call the first Makefile, which will call the second, which will call the third.

make foo

Here is the result:

 LEVEL1 var level 1 : VAR1 LEVEL1 varA is 11 LEVEL1 varB is 12 cd level2 & make foo LEVEL2 var level 1 : LEVEL2 var level 2 : VAR2 LEVEL2 varA is 11 LEVEL2 varB is 12 cd level3 & make foo LEVEL3 var level 1 : LEVEL3 var level 2 : LEVEL3 var level 3 : VAR3 LEVEL3 varA is 11 LEVEL3 varB is 12 LEVEL3 varC is 13 

So we can see that:

  • Access to the shell variable can be obtained from all the sub-headings of make
  • The exported Makefile variable is available from all make subframes of this Makefile
  • A non-exported Makefile variable cannot be accessed from the make subframes of this Makefile

You can easily reproduce this example to run more tests.

Note that you can actually include another Makefile and therefore get its variables and rules, see GNU make: Enable . I do not recommend using this if you do not pay close attention to what is happening, because you can redefine the rules if they have the same name in the included Makefile, as in what it includes.

+1
source

I think you overdid the paragraph:

When "make" runs the script command, the variables defined in the makefile are placed in the environment of this command. This allows you to pass values ​​for subqueries' invocations. By default, only variables that come from the environment or the command line are passed for recursive calls. You can use the export directive to pass in other variables.

All this happens together, so ONLY environment variables set in the input environment or on the command line, or explicitly "exported" to the Makefile, are placed in the environment of the called commands (whether this command is $(MAKE) or something else).

One interesting corner case is a variable set both in the input environment and in the Makefile (but not explicitly exported). Then the Makefile value overrides the value of the incoming environment, AND is also exported (since it was in the incoming environment, although with a different value).

Makefile:

 TEST = test default: @echo TEST="\"$$TEST\"" 

result:

 $ make TEST="" $ TEST=xx make TEST="test" $ make TEST=xx TEST="xx" 
+2
source

One thing to be careful about is that the rules for $ (shell) seem to be slightly different from the rules when executing commands as part of a recipe.

In the "interesting corner case" of Chris Dodd, it might seem that modifications are not passed when using $ (shell). With make file from

 IN_ENV = hello $(info $(shell echo IN_ENV is $$IN_ENV)) all: @echo IN_ENV is $$IN_ENV 

if you run:

 export IN_ENV=goodbye make 

you will get the output:

 IN_ENV is goodbye IN_ENV is hello 
0
source

All Articles