first, the experiment introduces--makefile variable
This experiment will introduce the variable definition style of make, the substitution reference of variables, the use of environment variables, command line variables, target-specified variables, and the use of automation variables. 1.1 Experimental content of different variable styles and assignment style variable substitution references, environment variables, command line variables using the target specified variables using the use of automation variables 1.2 Experiment knowledge point variable definition and launch time. The recursive expansion variables are defined using "=" or define, and are expanded when used. The definition of a recursive expansion variable has nothing to do with the writing order, but it also produces problems that are difficult to debug and function to call repeatedly. The direct expand variable uses the ": =" definition to expand immediately when make reads into the current row. The 5.+= operator can append to a variable in a way that is consistent with the original assignment of the variable. The 6.?= operator can be assigned when a variable is undefined. A substitution reference for a variable can replace the contents of a variable's expansion with a string. The system environment variable is visible to makefile, but a variable with the same name in the file overrides the environment variable and can be avoided by using the-e option. command-line variables have a higher priority than ordinary variables in makefile, and you can use the Override keyword to prevent a variable with the same name in makefile from being overridden by a command line designation. The target-specified variable is visible only in contexts that include dependencies, similar to local variables, with precedence over ordinary variables. Automation variables can automatically generate a list of files based on specific targets and dependencies. 1.3 Experimental Environment
Ubuntu system, GNU GCC Tool, GNU make tool 1.4 for crowd
This course is moderately difficult, suitable for students who have already learned the makefile rules. 1.5 Code Acquisition
You can get code from the following command:
$ git clone https://github.com/darmac/make_example.git
second, the experimental principle
According to the basic rules of makefile to conduct positive and negative experiments to learn and understand the use of rules. Iii. Development and preparation
Enter the experimental building course can be. IV. structure of the project document
.
├──auto: Use of automation variables
│ ├──add.c
│ ├──makefile
│ └──minus.c
├──readme.md
├── Rep: variable substitution
│ ├──envi.mk
│ ├──makefile
│ └──override.mk
├──style: Variable and assignment style
│ ├──append.mk
│ ├──direct.mk
│ └──makefile
└──target: Target specified variable
└── Makefile
v. Steps of the Experiment
recursive execution examples for 5.1 make
5.1.1 Crawl Source code
Use the following cmd to get the GitHub source code and enter the appropriate section:
CD ~/code/
git clone https://github.com/darmac/make_example.git
CD Make_example/chapter9
5.1.2 Recursive expansion variable
The makefile variable is a name that represents a text string. Variables are defined in two ways: recursive expansion variables and direct expansion variables. The variable is expanded into a string at the makefile read stage.
Recursive expansion variables can be defined by "=" and "define", and the definition of other variables does not expand immediately when the variable is defined, but only when the variable is used by the rule.
The makefile file in the chapter9/style/directory demonstrates the definition and use of recursive expansion variables.
The contents of the document are as follows:
#this makefile is for recursively vari test
. Phony:recur Loop a1 = ABC A2 = $ (a3) A3 = $ (a1) B1 = $ (b2) B2
= $ (B1)
recur:
@echo "A1:" $ (A1) c8/> @echo "A2:" $ (A2)
@echo "A3:" $ (A3)
loop:
@echo "B1:" $ (B1)
@echo "B2:" $ (B2)
The recur rule in the file uses 3 variables, A1 is a direct-defined string, and a3,a3 that is defined after the A2 reference refers to A1.
The loop rule uses b1,b22 variables, which refer to each other.
Enter the style directory to test the recur rule:
CD Style;make Recur
Terminal printing:
A1:ABC
a2:abc
a3:abc
Visible A1 A2 A3 values are consistent, and the expansion of the variable is independent of the defined order.
To test the Loop command again:
Make loop
Terminal printing:
Makefile:9: * * * recursive variable ' b1 ' references itself (eventually). Stop.
Make error exits because of infinite recursion for two variables.
From the above test you can see the advantages of a recursive expansion: This variable is independent of the order in which reference variables are defined. The disadvantage is that multiple variables can cause infinite recursion when they are referenced to each other.
In addition, if there is a function reference in a recursive expansion variable, each reference to the variable causes the function to be executed again, with less efficiency. 5.1.3 Direct Expansion Variable
The direct expansion variables are defined by ": =", and references to other variables and functions are expanded when defined.
File Direct.mk Replaces "=" in makefile with ": =", and then executes the recur and loop rules again:
Make-f direct.mk recur;make-f Direct.mk Loop
Terminal printing:
A1:ABC
A2:
a3:abc
B1:
B2:
As you can see from the test results, the A2,B1 is expanded to null because it references a variable that has not yet been defined.
Using the direct expansion variables can avoid the problem of infinite recursion and function repeated expansion of efficiency problems, and more in line with the general program design logic, easy to debug problems, so recommend users to use direct expansion of the variable as far as possible. 5.1.4 variable append and conditional assignment
You can append a variable with the + = assignment, and the assignment style when the variable is appended is the same as the variable definition, and if the append is undefined, the default is to be assigned in the recursive expanded style.
Use the? = Assignment symbol to assign values to a variable, or to assign a variable if it is not defined, otherwise the current definition of the variable is not changed.
The Append.mk file demonstrates how the append assignment and conditional assignment are used, as follows:
#this makefile is for + = Test
. Phony:dir recur
A1: = aa1
A1 + + _a1st
a2: = _a2
A1 + + $ (a2)
a1 + = $ (A3)
a3 + = $ (a1)
B1 = bb1
B1 + = _b1st
B2 = _b2
B1 = _b2
B1 + + $ (b3)
B3 +
+ = $ (B1) C1 + + $ (C2) C2 + = $ C1 DD1
d2 = dd2
d2? = dd3
dir:
@echo "A1:" $ (A1)
recur:
@echo "B1:" $ (B1)
def:
@ echo "C1:" $ (c1)
cond:
@echo "D1:" $ (D1)
@echo "D2:" $ (D2)
The Dir and recur rules demonstrate the difference between using an append assignment for a recursive expanded variable and a direct-unwind variable.
The DEF rule demonstrates the default style for undefined variable append assignments.
Cond demonstrates the use of conditional assignments.
Execute four rules separately:
Make-f append.mk dir;make-f append.mk recur;make-f append.mk def;make-f append.mk cond
Terminal printing:
A1:aa1 _a1st _a2
append.mk:16: * * Recursive variable ' b1 ' references itself (eventually). Stop.
APPEND.MK:19: * * * recursive variable ' c1 ' references itself (eventually). Stop.
D1:DD1
D2:DD2
Please analyze each line on its own and why it is printed.
The experimental process is shown in the following illustration:
5.2 Substitution of variables 5.2.1 Substitution Reference
For a variable that has already been defined, you can replace it with a string that you specify by using replace reference.
The substitution reference is in the form of $ (var:a=b), which replaces all a-ending characters in the variable VAR with the characters at the end of B.
You can also use pattern symbols to replace characters that conform to a mode with B mode.
Chapter9/rep/makefile demonstrates the substitution reference for the variable, which reads as follows:
. Phony:all
vari_a: = FA.O fb.o fc.o f.o.o vari_b:
= $ (vari_a:.o=.c) Vari_c: =
$ (vari_a:%.o=%.c)
vari_d: = $ (vari_a:f.o%=f.c%) all
:
@echo "vari_a:" $ (vari_a)
@echo "Vari_b:" $ (vari_b)
@echo "Vari_c:" $ (vari_ c)
@echo "Vari_d:" $ (vari_d)
Replace references and pattern substitution references for different variables in the file, enter the rep directory and test:
Cd.. /rep;make
Terminal printing:
vari_a: fa.o fb.o fc.o f.o.o
vari_b: fa.c fb.c fc.c f.o.c
vari_c: fa.c fb.c fc.c f.o.c
vari_d: fa.o fb.o fc . O F.C.O
The. o suffix in the vari_b is replaced with the. c suffix, f.o.o is replaced with no f.o.c, which indicates that only the suffix is replaced and the rest of the string remains unchanged.
Vari_c replaces the suffix with the pattern symbol, and the result is consistent with Vari_b.
Vari_d replaces the prefix F.O with the F.C using the pattern symbol. the use of 5.2.2 environment variables
For makefile, the environment variables under the system are visible. If the variable name in the file is the same as the environment variable name, the variable in the file is referenced by default.
File envi.mk demonstrates the execution of variable cc when it conflicts with environment variable cc:
. Phony:all
cc: = ABC all
:
@echo $ (cc)
The file defines a CC variable and assigns it to ABC, which prints the contents of the CC variable when performing the ultimate goal.
We first export an environment variable CC, then perform envi.mk to observe whether the two variables are different:
Export Cc=def;echo $CC; make-f envi.mk
Terminal printing:
DEF
ABC
Explains that the makefile custom variable precedence is higher than the environment variable. We can also cancel the definition of CC variables in makefile or modify the path variable definition to see what happens. 5.2.3 prevents environment variables from being overwritten
You can use the-e option to prevent an environment variable from being overwritten with a variable of the same name, such as the-e option:
Make-f ENVI.MK-E
Terminal printing:
Def
5.2.4 command line variable
Unlike environment variables, the command-line variables specified when you execute make will overwrite the definition of a variable with the same name in makefile.
If you want the variable to not be overwritten, you need to use the override keyword.
The Override.mk file demonstrates the override of command line arguments and the use of override keywords:
. Phony:all
vari_a = ABC
vari_b: = def
override Vari_c = Hij override vari_d
: = lmn
vari_c = XXX
va Ri_d + = xxx
override Vari_c + + zzz
override Vari_d + = zzz all
:
@echo "vari_a: $ (vari_a)
@echo" Vari _b: "$ (vari_b)
@echo" Vari_c: "$ (vari_c)
@echo" Vari_d: "$ (vari_d)
@echo" Vari_e: "$ (vari_e)
Vari_a and Vari_c are recursive expansion variables, Vari_b and vari_d are direct-expanded variables, and vari_e are undefined variables.
Now pass vari_a to vari_e from the command line and view the final expansion value of the variable:
Make-f override.mk vari_a=va vari_b=vb VARI_C=VC VARI_D=VD
Terminal printing:
Vari_a:va
vari_b:vb
vari_c:hij zzz
vari_d:lmn zzz
From printing you can see that regardless of the style of the variable, you need to use the override indicator to prevent the command line definition of the same name variable coverage.
At the same time, variables defined with override need to be override when they are modified, otherwise the modifications will not take effect and the authentication method is as follows:
Make-f override.mk
Terminal printing:
VARI_A:ABC
vari_b:def
vari_c:hij zzz vari_d:lmn zzz
:
The visible command line does not have incoming variables, but Vari_c and vari_d still cannot append "+ = zzz" without the override indicator.
The experimental process is shown in the following illustration:
5.3 Target Specify variable and pattern specified variable
Variables defined in makefile are usually valid for the entire file, similar to global variables. In addition to the ordinary variable definitions, there is a target that specifies the variable, defined at the target dependency, only visible to the target context. The target context also includes the rules for the target dependencies.
The target-specified variable can also be defined in the schema target, called the pattern-specified variable.
When a variable used in a target is defined in both the global and the target definition, the goal definition is higher, but note that the target-specified variable and the global variable are two variables, and their values do not affect each other.
Chapter9/target/makefile demonstrates the use of a target-specified variable, as follows:
. Phony:all
vari_a=abc
vari_b=def
all:vari_a:=all_target
all:pre_a pre_b file_c
@echo $@ ":" $ ( vari_a)
@echo $@ ":" $ (vari_b)
pre_%:vari_b:=pat
pre_%:
@echo $@ ":" $ (vari_a)
@echo $@ ":" $ ( Vari_b)
file_%:
@echo $@ ":" $ (vari_a)
@echo $@ ":" $ (Vari_b)
The vari_a and vari_b two global variables are defined in makefile, and the target all specifies a vari_a variable with the same name, and the pattern target pre_% specifies a ' vari_b variable with the same name.
The vari_a and Vari_b values that they can see are printed in the rules of each goal, and you can infer what information each goal will print according to the rules described earlier.
Go to target directory and execute make:
Cd.. /target;make
Terminal printing:
Pre_a:all_target
pre_a:pat
pre_b:all_target
pre_b:pat
file_c:all_target
All : All_target
All:def
Because the ultimate goal all specifies the vari_a as "All_target", the vari_a is in the form of a target-specified variable during the entire target reconstruction process. Vari_b is defined only in the pattern target pre_%, so Vari_b is pat for Pre_a and Pre_b, but for file_% and all targets, Vari_b is a global variable and is expanded to def.
We can also individually target pre_a and file_c to see what the difference is:
Make Pre_a
Terminal printing:
PRE_A:ABC
Pre_a:pat
Re-execution:
Make File_c
Terminal printing:
FILE_C:ABC
File_c:def
Because it is not in the context of the all target at this time, all the specified vari_a variable is invalidated, replaced by the original value "ABC", and pre_% specifies the Vari_b variable, so the Vari_b variable is still "Pat" for Pre_a.
The experimental process is shown in the following illustration:
5.4 Automation Variables
In a pattern rule, a pattern target can match several different target names, but in the process of engineering rebuilding, it is often necessary to specify an exact target name, in order to facilitate the acquisition of specific target names and dependencies in the rules, the makefile need to use the automation variables, The value of an Automation variable is determined by the specific rules that are executed, depending on the target of the rule being executed and the dependent file name.
There are seven kinds of automation variables:
$@: Target Name
$%: If the target name is a static library, represents a member name of the static library, otherwise null
$<: First dependency name
$?: all new dependency list than target file
$^: All dependencies list, duplicate name dependencies ignored
$+: Includes all list of dependencies for duplicate name dependencies
$*: A stem in a pattern rule or a static pattern rule, which is also the part represented by "%"
Chapter9/auto/makefile demonstrates the use of seven types of automation variables, and the contents of the file are as follows:
# $@ $^ $% $< $? $* $+
. Phony:clean
pre:=pre_a pre_b pre_a pre_c all:$
(PRE) lib-ladd
@echo "$$" "@:" $@
@echo "$$" "^:" $^
@ echo "$$" "+:" $+
@echo "$$" "," $<
@echo "$$" "?:" $?
@echo "$$" "*:" $*
@echo "$$" "%:" $%
@touch $@
$ (PRE):p re_%:d epen_%
@echo "$$" "* (in $@):" $*
Touch $@
depen_%:
@echo "use Depen rule to build:" $@ touch
$@ lib:libadd.a
(add.o minus.o)
LIBADD.A (ADD.O minus.o): add.o minus.o
@echo "$$" "% (in $@):" $%
$ (AR) r $@ $% clean
:
$ (RM) pre_* Depen_* *.a *.o Lib All
Ultimate goal all dependencies include the Pre_a Pre_b pre_c Lib and library file libadd.a, which repeats a pre_a dependency.
The pattern rule pre_% uses the static mode to rely on the corresponding depen_% rule, prints the matching stem, and generates the target file, the library file rule prints $% and packs the generation LIBADD.A.
Since the $ (cc) is used here to compile, and we previously assigned the environment variable CC to "Def", we now need to modify it back:
Export CC=GCC
Now go to the Auto directory and execute make:
Cd.. /auto;make
Terminal printing:
Makefile:17:target ' pre_a ' given more than once in the same rule.
Use depen the rule to build:depen_a the touch
depen_a
$* (in pre_a): A touch pre_a with Depen rule to
build:depen_b< C5/>touch Depen_b
$* (in pre_b): B Touch pre_b use depen the rule to
Build:depen_c touch
depen_c
$* (in Pre_c): C touch
pre_c
gcc -c-o add.o add.c
gcc -c-o minus.o minus.c
$? ( In libadd.a): ADD.O minus.o
$% (in libadd.a): add.o
ar r libadd.a add.o ar:creating libadd.a
$? ( In libadd.a): ADD.O minus.o
$% (in libadd.a): minus.o
ar r libadd.a minus.o
$? ( In Lib): ADD.O minus.o Touch
lib
$@:all
$^:p re_a pre_b pre_c lib libadd.a
$+:p re_a pre_b pre_a pre_c L IB libadd.a
$<:p re_a
$:p re_a pre_b pre_c lib libadd.a $*:
$%:
Make first rebuilds the pre_a pre_b pre_c dependencies and prints the matching stems a b C, then rebuilds the Lib rule, libadd.a prints the $% during the rebuild, and can be seen from the printing and packaging commands $% Only the ADD.O file is expanded, but the static file target is expanded several times according to the given list of files. Finally, make executes the list of commands for the ultimate goal all, prints its automation variables separately, and generates the all file.
Please carefully observe the changes in the automation variables under different rules. Since this is the first time the ultimate goal is established, the list of dependencies that the $? Gets is all dependencies. Use Touch command to update pre_a Pre_b test again:
Touch Pre_a Pre_b;make
Terminal printing:
Makefile:17:target ' pre_a ' given more than once in the same rule.
$@:all
$^:p re_a pre_b pre_c lib libadd.a
$+:p re_a pre_b pre_a pre_c lib libadd.a
$<:p re_a
$:p re_a Pre_b
$*:
$%:
Since Pre_a Pre_b has been manually updated, the $ content that is now printed is Pre_a pre_b.
In addition to the direct reference, the above seven automation variables can also be followed by the addition of the D or F characters to get the directory name and file name.
such as: $ (@D) represents the directory name of the destination file, and $ (@F) represents the file name of the destination file. This usage is very simple, also apply to all the automation variables, please test yourself.
The experimental process is shown in the following illustration:
Vi. Summary of the experiment
The experiment introduces the variable definition style of make, the substitution reference of variables, the use of environment variables, command line variables, target-specified variables and the use of automation variables. post-class exercises
Please design your own experiment to test the access to the directory name and file name of the automation variable.