Using variables
————
The variable defined in makefile, like a macro in the C + + language, represents a text string that, when executed in makefile, automatically expands to the original mode as it is used. It differs from C/s + + in that you can change its value in makefile. In makefile, variables can be used in "targets", "dependent targets", "commands" or other parts of makefile.
The name of a variable can contain characters, numbers, underscores (which can be the beginning of a number), but should not contain ":", "#", "=", or null characters (spaces, carriage returns, etc.). The variables are case-sensitive, and "foo", "foo", and "foo" are three different variable names. The traditional makefile variable name is all-uppercase naming, but I recommend using the name of the variable with the casing, such as: Makeflags. This avoids collisions with the system's variables, and unexpected things happen.
Some variables are very strange strings, such as "$<", "[email protected]" and so on, these are the automation variables, I will introduce later.
First, the basis of the variable
Variables need to be given an initial value at declaration, and when used, they need to be preceded by a "$" symbol in the name of the variable, but it is best to include the variable with the parentheses "()" or the curly brace "{}". If you want to use the real "$" character, then you need to "$$" to indicate.
Variables can be used in many places, such as "target", "dependent", "command", and new variables in the rules. Let's look at an example:
objects = PROGRAM.O foo.o UTILS.O
Program: $ (objects)
Cc-o Program $ (objects)
$ (objects): Defs.h
The variable expands exactly where it is used, just like a macro in C + +, such as:
Foo = c
prog.o:prog.$ (foo)
$ (foo) $ (foo)-$ (foo) prog.$ (foo)
Expanded to get:
Prog.o:prog.c
Cc-c prog.c
Of course, do not do this in your makefile, here is just an example to show that the variables in the makefile are used to unfold the actual appearance. It can be seen as an "alternative" principle.
In addition, the parentheses are used to make the variable more secure, and in the example above, if you do not want to enclose the variable in parentheses, it is also possible, but I strongly recommend that you enclose the variable in parentheses.
Ii. Variables in variables
When defining the value of a variable, we can use other variables to construct the value of the variable, and in makefile there are two ways to define the value of the variable with a variable.
The first way, that is, simply using the "=" number, the left side of the "=" is a variable, the right side is the value of the variable, the value of the right variable can be defined at any place in the file, that is, the right side of the variable is not necessarily a defined value, it can also use the value defined later. Such as:
Foo = $ (bar)
Bar = $ (ugh)
Ugh = Huh?
All
echo $ (foo)
We execute "make all" will be typed the value of the variable $ (foo) is "Huh?" The value of ($ (foo) is $ (bar), the value of $ (bar) is $ (ugh), and the value of $ (ugh) is "Huh?" Visible, variables can be defined using the following variables.
There are good places and bad places for this function, and the good part is that we can push the real value of the variable to the back to define it, such as:
CFLAGS = $ (include_dirs)-O
Include_dirs =-ifoo-ibar
When "CFLAGS" is expanded in the command, it will be "-ifoo-ibar-o". But this form also has a bad place, that is, recursive definition, such as:
CFLAGS = $ (CFLAGS)-O
Or:
A = $ (B)
B = $ (A)
This allows make to go into infinite variable expansion, and of course, our make is capable of detecting such a definition and will give an error. What's more, if you use a function in a variable, that way makes our make run very slow and, worse, he will use the two make function "wildcard" and "shell" to cause unpredictable errors. Because you don't know how many times these two functions will be called.
To avoid this approach, we can use another method in make to define variables using variables. This method uses the ": =" operator, such as:
x: = foo
Y: = $ (x) bar
x: = Later
It is equivalent to:
Y: = foo bar
x: = Later
It is worth mentioning that this method, the preceding variable cannot use the following variables, can only use the previously defined variables. If this is the case:
Y: = $ (x) bar
x: = foo
So, the value of y is "bar", not "foo bar".
Here are some simple variables to use, let's take a look at a complex example, including the use of make functions, conditional expressions, and a system variable "Makelevel":
Ifeq (0,${makelevel})
Cur-dir: = $ (shell pwd)
WHOAMI: = $ (Shell whoami)
Host-type: = $ (Shell arch)
Make: = ${make} host-type=${host-type} Whoami=${whoami}
endif
As for the conditional expressions and functions, we'll say later, for the system variable "makelevel", which means that if our make has a nested execution (see the previous "nesting use make"), then this variable will record the number of calls to our current makefile.
Let's take a look at an example, if we want to define a variable whose value is a space, let's say that we need to know about the two variables defined here:
NullString: =
Space: = $ (nullstring) # End of the line
NullString is an empty variable, where nothing is, and the value of our space is a space. Because it is difficult to describe a space on the right side of the operator, the technique used here is very useful, first using an empty variable to indicate the value of the variable, and then using the "#" notation to denote the termination of the variable definition, so that we can define a variable whose value is a space. Note here about the use of "#", The Notation "#" which deserves our attention if we define a variable like this:
Dir: =/foo/bar # directory to put the frobs in
Dir The value of this variable is "/foo/bar", followed by 4 spaces, if we use such a variable to specify a different directory-"$ (dir)/file" then it is finished.
There is also a more useful operator is "? =", first look at the example:
FOO? = Bar
The implication is that if Foo is not defined, then the value of the variable foo is "bar", and if Foo was previously defined, then the phrase will do nothing, which is equivalent to:
Ifeq ($ (Origin FOO), undefined)
FOO = Bar
endif
Iii. Advanced usage of variables
This article describes the advanced usage of the two variables, the first of which is the substitution of variable values.
We can replace the common part of the variable in the form of "$ (var:a=b)" or "${var:a=b}", which means replacing all "a" in the variable "var" with "a" in the "a" string to "B" string. The "end" here means "space" or "Terminator".
Let's look at an example:
Foo: = A.O b.o C.O
Bar: = $ (FOO:.O=.C)
In this example, we first define a "$ (foo)" Variable, and the second line means to replace all ". O" Strings with ". C" in "$ (foo)", so our value for "$ (bar)" is "A.C B.C c.c".
Another technique for variable substitution is defined in "static mode" (see previous chapters), such as:
Foo: = A.O b.o C.O
Bar: = $ (FOO:%.O=%.C)
This depends on the same pattern in the substituted string, and the pattern must contain a "%" character, and this example also allows the value of the $ (bar) variable to be "A.C B.C c.c".
The second high-level usage is-"Take the value of a variable again as a variable." Let's look at an example:
x = y
y = Z
A: = $ ($ (x))
In this example, the value of $ (x) is "Y", so $ ($ (x)) is $ (y), so the value of $ (a) is "Z". (Note that it is "x=y", not "x=$ (y)")
We can also use more layers:
x = y
y = Z
z = u
A: = $ ($ ($ (x)))
The value of $ (a) here is "U", and the relevant derivation is left to the reader to do it himself.
Let's be more complicated by using the first way of "using variables in variable definitions" to look at an example:
x = $ (y)
y = Z
z = Hello
A: = $ ($ (x))
The $ ($ (x)) here is replaced by $ ($ (y)), because the $ (Y) value is "Z", so the end result is: a:=$ (z), which is "Hello".
More complicated, let's add the function:
x = Variable1
Variable2: = Hello
y = $ (subst 1,2,$ (x))
z = y
A: = $ ($ ($ (z)))
In this example, "$ ($ ($ ($))" expands to "$ ($ (Y))" and is again extended to "$ ($ (subst 1,2,$ (x))"). The value of $ (x) is "Variable1", and the SUBST function replaces all "1" strings in "Variable1" with the "2" string, so that "variable1" becomes "variable2" and then takes its value, so finally, the value of $ (a) is $ ( VARIABLE2) The value-"Hello". (Oh, very hard)
In this way, or you can use multiple variables to form the name of a variable, and then take its value:
First_second = Hello
A = First
B = Second
All = $ ($a _$b)
The "$a _$b" here consists of "First_second", so the value of $ (all) is "Hello".
Take a look at examples of the first technique:
A_objects: = A.O b.o C.O
1_objects: = 1.o 2.o 3.o
Sources: = $ ($ (A1) _OBJECTS:.O=.C)
In this example, if the value of $ (A1) is "A", then the value of $ (sources) is "A.C B.C c.c", and if the value of $ (A1) is "1", then the value of $ (sources) is "1.c 2.c 3.c".
Take another look at this technique and the example used with "function" and "conditional statement":
Ifdef do_sort
Func: = sort
Else
Func: = Strip
endif
Bar: = a d b g Q c
Foo: = $ ($ (func) $ (bar))
In this example, if "Do_sort" is defined, then: foo: = $ (sort a d B g Q C), so the value of $ (foo) is "a b c D g Q", and if "do_sort" is not defined, then: foo: = $ (sort a d B g Q c), the strip function is called.
Of course, the technique of "using the value of a variable again as a variable" can also be used on the left side of the operator:
DIR = foo
$ (dir) _sources: = $ (Wildcard $ (dir)/*.c)
Define $ (dir) _print
LPR $ ($ (dir) _sources)
Endef
In this example, three variables are defined: "dir", "Foo_sources" and "Foo_print".
Four, append variable value
We can use the "+ =" operator to append a value to a variable, such as:
objects = MAIN.O foo.o bar.o utils.o
Objects + = ANOTHER.O
So our $ (objects) value becomes: "MAIN.O foo.o bar.o utils.o another.o" (ANOTHER.O is appended)
Using the "+ =" operator, you can simulate this example for the following:
objects = MAIN.O foo.o bar.o utils.o
Objects: = $ (objects) ANOTHER.O
The difference is that "+ =" is more concise.
If the variable is not defined before, then "+ =" will automatically become "=", if there is a variable definition, then "+ =" will inherit from the previous operation of the assignment. If the previous time is ": =", then "+ =" will be ": =" as its assignment, such as:
Variable: = value
Variable + = more
Equivalent to:
Variable: = value
Variable: = $ (variable) more
But if this is the case:
Variable = value
Variable + = more
Since the previous assignment is "=", so "+ =" will also be assigned as "=", then it will not happen to the variable by-definition, which is very bad, so make will automatically solve this problem for us, we do not have to worry about this problem.
V. Override indicator
If a variable is usually set by the command-line argument of make, then the assignment of the variable in makefile is ignored. If you want to set the values of such parameters in makefile, you can use the "override" indicator. Its syntax is:
Override <variable> = <value>
Override <variable>: = <value>
Of course, you can also add:
Override <variable> + = <more text>
For multi-line variable definitions, we can use the define indicator, before the define indicator, as well as using the ovveride indicator, such as:
Override define Foo
Bar
Endef
Six, multi-line variables
There is also a way to set the value of a variable by using the Define keyword. The use of the Define keyword to set the value of a variable can have a newline, which facilitates the definition of a series of commands (the technique we've talked about "command packs" earlier is to use this keyword).
The define indicator is followed by the name of the variable, and the value of the variable is defined by a line that ends with the ENDEF keyword. It works the same way as the "=" operator. The value of a variable can contain functions, commands, literals, or other variables. Because the command needs to start with the [tab] key, if the command variable you define with define does not start with the [tab] key, then make does not think of it as a command.
The following example shows the use of define:
Define Two-lines
echo Foo
echo $ (BAR)
Endef
Seven, environment variables
The system environment variables of the make runtime can be loaded into the makefile file while the makes is running, but if the variable is already defined in makefile, or if the variable is brought in by the Make command line, the value of the system's environment variable will be overwritten. (If make specifies the "-e" parameter, the system environment variable overrides the variable defined in the makefile)
Therefore, if we set the "CFLAGS" environment variable in the environment variable, then we can use this variable in all the makefile. This is a great benefit for us to use uniform compilation parameters. If cflags is defined in makefile, then the variable in makefile is used, and if there is no definition then the value of the system environment variable is used, a commonality and a unity of personality, much like the attributes of "global variables" and "local variables".
When make nested calls (see the preceding "Nested calls" section), the variables defined in the upper makefile are passed to the lower makefile in the manner of the system environment variables. Of course, by default, only variables that are set through the command line are passed. Variables defined in the file, if passed to the lower makefile, need to be declared using the Exprot keyword. (See previous chapters)
Of course, I do not recommend that many variables be defined in the system environment, so that when we do not use the makefile, we have the same set of system variables, which can lead to more trouble.
Viii. Target Variables
The variables defined in makefile above are all "global variables", which we can access throughout the file. Of course, except for "automation variables", such as "$<", such as the amount of automation variables such as "Regular variable", the value of this variable depends on the goal of the rule and the definition of dependent targets.
Of course, I can also set a local variable for a target, which is called "Target-specific Variable", it can be the same name as "global variable", because its scope is only in this rule and the rules and regulations, so its value is only valid within the scope of action. The values of global variables other than the rule chain are not affected.
Its syntax is:
<target ...>: <variable-assignment>
<target ...>: Overide <variable-assignment>
<variable-assignment> can be a variety of assignment expressions previously mentioned, such as "=", ": =", "+ =" or "? = ". The second syntax is for variables that are brought into the make command line, or for system environment variables.
This feature is very useful when we set up a variable that will act on all the rules that are thrown by the target. Such as:
Prog:cflags = g
PROG:PROG.O foo.o BAR.O
$ (CC) $ (CFLAGS) PROG.O foo.o BAR.O
Prog.o:prog.c
$ (CC) $ (CFLAGS) prog.c
Foo.o:foo.c
$ (CC) $ (CFLAGS) foo.c
Bar.o:bar.c
$ (CC) $ (CFLAGS) bar.c
In this example, regardless of what the global $ (CFLAGS) value is, the value of $ (BAR.O) is "-G" in the prog target, and in all the rules that it throws (PROG.O foo.o CFLAGS rule)
Nine, mode variables
In GNU make, schema variables (pattern-specific Variable) are also supported, and through the target variables above, we know that variables can be defined on a target. The advantage of a pattern variable is that we can give a "pattern" that defines a variable in all targets that conform to that pattern.
We know that the "mode" of make generally contains at least one "%", so we can define target variables for all targets ending with [. O] as follows:
%.o:cflags =-O
Similarly, the syntax of a pattern variable is the same as the "Target variable":
<pattern ...>: <variable-assignment>
<pattern ...>: Override <variable-assignment>
Override is also for variables passed in to the system environment, or variables specified by the Make command line.