Variables defined in makefile are like Macros in C/C ++. They represent a text string, when executed in makefile, the original mode is automatically expanded in the used place. Unlike C/C ++, you can change the value in makefile. In makefile, variables can be used in "target", "dependency target", "command", or other parts of makefile.
The name of a variable can contain characters, numbers, and underscores (can start with a number), but cannot contain ": "," # "," = ", or an empty character (space, carriage return, etc ). Variables are case sensitive. "foo", "foo", and "foo" are three different variable names. In traditional makefile, variable names are all named in uppercase. However, we recommend that you use a combination of uppercase and lowercase variables, such as makeflags. This avoids conflicts with system variables and unexpected events.
Some variables are very strange strings, such as "$ <" and "$ @". These are automated variables. I will introduce them later.
I. Basics of Variables
When declaring a variable, you need to give the initial value. When using the variable, you need to add the "$" symbol before the variable name, but it is best to use parentheses "() or braces "{}" to include the variable. If you want to use the true "$" character, you need to use "$" to represent it.
Variables can be used in many places, such as the "target", "dependency", "Command" in the rule, and new variables. Let's take a look at an example:
Objects = program. O Foo. O utils. o
Program: $ (objects)
CC-O program $ (objects)
$ (Objects): defs. h
Variables are precisely expanded where they are used, like Macros in C/C ++. For example:
Foo = C
Prog. O: Prog. $ (FOO)
$ (FOO)-$ (FOO) Prog. $ (FOO)
Expand and get:
Prog. O: Prog. c
CC-C Prog. c
Of course, do not do this in your makefile. Here is an example to show how variables in makefile are actually expanded in use. It can be seen that it is an alternative principle.
In addition, brackets are added to variables to make it safer to use them. In the above example, if you do not want to add parentheses to variables, you can also, however, I strongly recommend that you add parentheses to the variables.
2. Variables in Variables
When defining the value of a variable, we can use other variables to construct the value of the variable. There are two methods in makefile to define the value of the variable.
First, let's look at the first method, that is, simply using the "=" sign, on the left side of "=" is the variable, and on the right side is the value of the variable, the value of the variable on the right can be defined in any part of the file. That is to say, the variable on the right is not necessarily a defined value. It can also use the value defined later. For example:
Foo = $ (bar)
Bar = $ (ugh)
Ugh = huh?
ALL:
Echo $ (FOO)
When we execute "make all", the value of the variable $ (FOO) is "huh ?" ($ (FOO) is $ (bar), $ (bar) is $ (ugh), and $ (ugh) is "huh ?") It can be seen that variables can be defined using the following variables.
This function has both good and bad features. The good thing is that we can push the real value of the variable to the end for definition, such:
Cflags = $ (include_dirs)-O
Include_dirs =-ifoo-Ibar
When "cflags" is expanded in the command, it will be "-ifoo-Ibar-o ". But there is also a bad thing about this form, that is, recursive definition, such:
Cflags = $ (cflags)-O
Or:
A = $ (B)
B = $ ()
This will bring make into the infinite variable expansion process. Of course, our make is capable of detecting such a definition and reporting an error. In addition, if a function is used in a variable, this method will slow our make runtime. What's worse, he will use two make functions "wildcard" and "shell" to make unpredictable errors. Because you don't know how many times these two functions will be called.
To avoid this method, we can use another method in make to define variables. This method uses the ": =" operator, for example:
X: = foo
Y: = $ (x) bar
X: = later
It is equivalent:
Y: = Foo bar
X: = later
It is worth mentioning that, in this method, the preceding variables cannot use the following variables, but can only use the previously defined variables. If so:
Y: = $ (x) bar
X: = foo
Then, the value of Y is "bar" instead of "foo bar ".
The above are some simple variables used. Let's take a look at a complicated example, including the make function, conditional expression, and the use of 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
We will talk about conditional expressions and functions later. For the system variable "makelevel", it means, if make has a nested execution action (see the previous "use make nested"), this variable records the number of calls to the current makefile.
Next we need to know about two variables. Let's take a look at an example. If we want to define a variable whose value is a space, we can do this:
Nullstring: =
Space: = $ (nullstring) # End of the line
Nullstring is an empty variable with nothing in it, and our space value is a space. It is difficult to describe a space on the right side of the operator. The technology used here is very useful. First, use an empty variable to indicate the value of the variable, the "#" annotator is used later to indicate the termination of variable definition. In this way, we can define a variable whose value is a space. Please note that the usage of "#" here is worth noting for the feature of the annotator "#". If we define a variable like this:
Dir: =/Foo/bar # directory to put the frobs in
The value of the Dir variable is "/Foo/bar", followed by four spaces. If we use this variable to specify another directory -- "$ (DIR) /file.
Another useful operator is "? = ", First look at the example:
Foo? = Bar
The meaning is that if Foo has not been defined, the value of the variable foo is bar. If Foo has been previously defined, then this language will do nothing, which is equivalent:
Ifeq ($ (origin Foo), undefined)
Foo = bar
Endif
Iii. Advanced variable usage
This section describes the advanced usage of the two variables. The first is the replacement of the variable value.
We can replace the common parts of the variable in the format of "$ (VaR: A = B)" or "$ {var: a = B}", which means, replace all "a" in the variable "var" with "B" string ending with ". Here the "end" 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 put all the values in "$ (FOO)" as ". O "string" end "is replaced with". C ", so the value of" $ (bar) "is". c B. c. C ".
Another technique for variable replacement is defined as "static mode" (see the previous section), for example:
Foo: = A. o B. O C. O
Bar: = $ (FOO: %. O = %. c)
This relies on the same pattern in the replaced string. The pattern must contain a "%" character. In this example, the value of the $ (bar) variable is also set to ". c B. c. C ".
The second advanced usage is to "treat the value of a variable as a variable ". Let's take a 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 is "Z ". (Note: "x = y" instead of "X = $ (y )")
We can also use more layers:
X = y
Y = z
Z = u
A: =$ ($ (X )))
Here, the value of $ (a) is "U". Let the readers do the derivation.
Let's make it more complicated. Let's look at an example using the first method of "using variables in variable definition:
X = $ (y)
Y = z
Z = Hello
A: =$ ($ (x ))
Here $ (x) is replaced with $ (y), because the $ (y) value is "Z", the final result is:: = $ (z), that is, "hello ".
To be more complex, we add the following functions:
X = variable1
Variable2: = Hello
Y = $ (SUBST 1, 2, $ (x ))
Z = y
A: =$ ($ (z )))
In this example, "$ (z)" is extended to "$ (y ))", it is expanded to "$ (SUBST, $ (x)" again )))". The value of $ (X) is "variable1". The SUBST function replaces all "1" strings in "variable1" with "2". Therefore, "variable1" is changed to "variable2 ", then, the value of $ (a) is the value of $ (variable2) -- "hello ". (Oh, It's hard)
In this way, you can use multiple variables to form the name of a variable, and then obtain its value:
First_second = Hello
A = first
B = Second
All = $ ($ A _ $ B)
Here, "$ A _ $ B" forms "first_second", so the value of $ (all) is "hello ".
Let's take a look at the example of combining the first Technology:
A_objects: = A. o B. O C. O
Export 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 ". c B. c. C "; if the value of $ (A1) is" 1 ", then the value of $ (sources) is" 1.C 2.c 3. C ".
Let's take a look at an example of this technology and the use of "functions" and "conditional statements:
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), then $ (FOO) the value 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 technology of "taking the value of a variable 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 ".
Iv. append variable values
We can use the "+ =" operator to append a value to the variable, for example:
Objects = Main. O Foo. O Bar. O utils. o
Objects + = another. o
So our $ (objects) value is changed to "Main. O Foo. O Bar. O utils. o another. O" (another. O is appended)
Using the "+ =" operator, we can simulate the following example:
Objects = Main. O Foo. O Bar. O utils. o
Objects: = $ (objects) Another. o
The difference is that "+ =" is more concise.
If the variable has not been defined before, "+ =" is automatically changed to "=". If there is a variable definition before, "+ =" is inherited from the value assignment of the previous operation. If the previous value is ": =", "+ =" uses ": =" as its value assignment, for example:
Variable: = Value
Variable + = more
It is equivalent:
Variable: = Value
Variable: = $ (variable) More
However, in this case:
Variable = Value
Variable + = more
Because the previous value assignment is "=", "+ =" will also use "=" as the value assignment, so it is difficult to implement the progressive population definition of variables, so make will automatically solve this problem for us and we don't have to worry about it.
V. Override indicator
If a variable is set by the command line parameter of make, the value of this variable in makefile is ignored. If you want to set the value of this type of parameter in makefile, you can use the "Override" indicator. Its syntax is:
Override <variable >;=< value>;
Override <variable >;:=< value>;
Of course, you can also append:
Override <variable >;+=< more text>;
For variable definitions of multiple rows, we use the define indicator. We can also use the ovveride indicator before the define indicator, for example:
Override define foo
Bar
Endef
6. multiline Variables
Another way to set the variable value is to use the define keyword. You can use the define keyword to set variable values with line breaks, which helps to define a series of commands (the "command package" technology we mentioned earlier is to use this keyword ).
The define indicator is followed by the variable name, and the variable value is defined in a new line. The definition ends with the endef keyword. It works in the same way as the "=" operator. Variable values can include functions, commands, text, or other variables. Because the command must start with the [Tab] key, if the command variable defined by define does not start with the [Tab] key, make will not regard it as a command.
The following example shows how to use define:
Define two-Lines
Echo foo
Echo $ (bar)
Endef
VII. Environment Variables
The system environment variable during the make operation can be loaded into the MAKEFILE file when make starts running. However, if the makefile has defined this variable, or the variable is imported by the make command line, then the system environment variable value will be overwritten. (If make specifies the "-e" parameter, the system environment variable overwrites the variable defined in makefile)
Therefore, if we set the "cflags" environment variable in the environment variable, we can use this variable in all makefiles. This is of great benefit to the use of unified compilation parameters. If cflags is defined in makefile, this variable in makefile will be used. If no definition is made, the value of the system environment variable will be used, which is a unity of commonality and individuality, it is similar to the features of "global variables" and "local variables.
When you make a nested call (see the previous "nested call" section), the variables defined in the upper makefile are passed to the lower makefile as system environment variables. Of course, by default, only variables set through the command line will be passed. Variables defined in files must be declared using the exprot keyword if they are to be passed to lower-level makefile. (See the previous chapter)
Of course, I do not recommend defining many variables in the system environment. In this way, when we execute makefiles that are not used, some of them have the same set of system variables, this may cause more trouble.
8. target variables
The variables defined in makefile are all "global variables", which can be accessed throughout the file. Of course, except for "automation variables", for example, "$ <" and other types of automation variables belong to "rule variables ", the value of this variable depends on the rule's target and the target's definition.
Of course, I can also set a local variable for a target. This variable is called "target-specific variable" and can have the same name as "global variable, because its scope is only in this rule and associated rules, its value is only valid within the scope. The value of global variables other than the rule chain is not affected.
Its syntax is:
<Target... >;:< variable-assignment>;
<Target...>;: overide <variable-assignment>;
<Variable-assignment>; can be a variety of value assignment expressions, such as "=", ": =", "+ =", or "? = ". The second syntax is for variables or system environment variables brought in by the make command line.
This feature is very useful. When we set such a variable, it will apply to all the rules caused by this target. For example:
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, no matter what the global $ (cflags) value is. O Foo. O Bar. O rules), $ (cflags) values are "-G"
9. Mode Variables
In GNU make, pattern-specific variable is also supported. Through the above target variables, we know that variables can be defined on a specific target. The advantage of pattern variables is that we can give a "pattern" to define variables on all targets that conform to this pattern.
We know that the "pattern" of make generally contains at least one "%", so we can define the target variable for all targets ending with [. O] in the following way:
%. O: cflags =-o
Similarly, the syntax of pattern variables is the same as that of "target variables:
<Pattern... >;:< variable-assignment>;
<Pattern...>;: override <variable-assignment>;
Override is also for variables passed in the system environment or variables specified by the make command line.