Makefile syntax and Writing Method (2)

Source: Internet
Author: User

3 makefile writing rules

--------------------------------------------------------------------------------
A rule contains two parts: dependency and target generation.
In makefile, the sequence of rules is very important, because only one final goal should be included in makefile, and other goals should be associated with this goal, make sure that make knows what your final goal is. In general, there may be many targets defined in makefile, but the targets in the first rule will be established as the final targets. If there are many targets in the first rule, the first target will become the final target. This is what make has done.

Well, let's take a look at how to write rules.

3.1 rule example
Foo. O: Foo. c defs. h # Foo Module
CC-C-G Foo. c
You may not be familiar with this example. As mentioned above, foo. O is our goal, foo. C and defs. H is the source file on which the target depends, and there is only one command "cc-C-G Foo. C "(starting with the tab key ). This rule tells us two things:

File dependency, foo. O depends on foo. C and defs. h file, if Foo. C and defs. the file date of H is later than that of foo. o file date should be new, or Foo. o does not exist, so the dependency occurs.
If the foo. o file is generated (or updated. That is, the CC command, which explains how to generate the file Foo. O. (Of course, the foo. c file includes the defs. h file)

3.2 rule syntax
Targets: prerequisites
Command
...
Or:
Targets: prerequisites; command
Command
...
Targets is a file name separated by spaces. Wildcards can be used. In general, our goal is basically one file, but it may also be multiple files.
Command is a command line. If it is not in a line with "target: prerequisites", it must start with the [tab key]. If it is in a line with prerequisites, you can use a semicolon to separate it. (See above)

Prerequisites is the object (or dependency target) on which the target depends ). If a file is newer than the target file, the target is regarded as "obsolete" and is considered to need to be regenerated. This has been mentioned earlier.

If the command is too long, you can use a backslash ('/') as a line break. Make has no limit on the number of characters in a row. The rule tells make two things: the dependency of the file and how to build the target file.

In general, make will execute commands using the UNIX standard shell, that is,/bin/sh.

3.3 use wildcards in rules
If we want to define a series of similar files, we naturally think of using wildcards. Make supports three wildcards: "*", "?" And "[...]". This is the same as the Unix B-shell.

Tilde ("~") Characters are also used in file names. For example, /Test ", which indicates the test directory under the $ home directory of the current user. And "~ "Hchen/test" indicates the test directory under the user's home directory. (These are UNIX knowledge, make also supports) and in Windows or MS-DOS, the user does not have a home directory, the directory indicated by the Tilde depends on the environment variable "home.

Wildcards replace your series of files. For example, "*. c" indicates a file with a suffix of C. Note that if the file name contains wildcards, such as "*", you can use the Escape Character "/". for example, "/*" represents the true "*" character, rather than any string of any length.

Well, let's take a look at several examples:

Clean:
Rm-f *. o
In the above example, I will not mention it much. This is a wildcard supported by the operating system shell. This is a wildcard in the command.
Print: *. c
LPR-p $?
Touch print
The preceding example shows that the wildcard can also be used in our Rules. The target print depends on all [. C] files. "$ ?" It is an automation variable. I will introduce it to you later.
Objects = *. o
The above example indicates that the token can also be used in variables. It does not mean that [*. O] will be opened, no! The value of objects is "*. O ". The variables in makefile are actually Macros in C/C ++. If you want to expand the wildcard in the variable, that is, to make the objects value a set of all [. O] file names, you can:
Objects: = $ (wildcard *. O)
This usage is pointed out by the keyword "wildcard". The keyword about makefile will be discussed later.
3.4 file search
In some large projects, there are a large number of source files. Our common practice is to classify these many source files and store them in different directories. Therefore, when make needs to find the file dependency, you can add a path before the file, but the best way is to tell make a path so that make can automatically find it.

The special variable "vpath" in the MAKEFILE file completes this function. If this variable is not specified, make will only search for the dependent and target files in the current directory. If this variable is defined, make will find the file in the specified directory if the current directory cannot be found.

Vpath = SRC: ../Headers
The preceding Definition specifies two directories, "src" and "../headers". Make searches in this order. Directories are separated by colons. (Of course, the current directory is always the top priority for search)
Another way to set the file search path is to use the "vpath" keyword of make (note that it is all in lower case). This is not a variable, but a keyword of make, this is similar to the vpath variable mentioned above, but it is more flexible. It can specify different files in different search directories. This is a flexible feature. It can be used in three ways:

Vpath <pattern> <directories>
Specify the search directory for the file that meets the pattern <pattern>.
Vpath <pattern>
Clear the search directory of the file that conforms to the pattern <pattern>.
Vpath
Clear all configured file search directories.
The <pattern> In the vapth usage must contain the "%" character. "%" Indicates matching zero or several characters. For example, "%. H" indicates all files ending with ". H. <Pattern> specifies the file set to be searched, while <directories> specifies the directory for searching the file set. For example:

Vpath %. h ../Headers
This statement indicates that make needs to search all files ending with ". H" in the "../headers" directory. (If a file is not found in the current directory)
We can use the vpath statement consecutively to specify different search policies. If the same <pattern> or repeated <pattern> exists in a continuous vpath statement, make executes the search in the order of the vpath statement. For example:

Vpath %. c foo
Vpath % blish
Vpath %. c bar
It indicates the file ending with ". c", first in the "foo" directory, then "blish", and finally the "bar" directory.
Vpath %. c FOO: bar
Vpath % blish
The preceding statement indicates the file ending with ". c". First in the "foo" directory, then the "bar" directory, and finally the "blish" directory.

 

3.5 pseudo targets
In the first example, we mentioned a "clean" target, which is a "pseudo target ",

Clean:
RM *. O temp
Just like "clean" in the previous example, when we generate many file compilation files, we should also provide a "goal" for clearing them for full re-compilation. (Use "make clean" to use this target)
Because we do not generate the "clean" file. "Pseudo-target" is not a file, but a tag. Because "pseudo-target" is not a file, make cannot generate its dependency and decide whether to execute it. Only by explicitly specifying this "target" can we make it take effect. Of course, the name of a "pseudo-target" cannot be the same as that of a file name, otherwise it will lose the meaning of "pseudo-target.

Of course, to avoid duplicate names with files, we can use a special tag ". "Phony" indicates that a target is a "pseudo target" and shows to make that whether or not this file exists is a "pseudo target ".

. Phony: clean
If this Declaration exists, whether or not the "clean" file exists, only make clean is required to run the "clean" object. The entire process can be written as follows:
. Phony: clean
Clean:
RM *. O temp
Generally, the pseudo-target has no dependent files. However, we can also specify the dependent files for the pseudo target. The pseudo-target can also be used as the "Default target", as long as it is placed in the first. For example, if your makefile needs to generate several executable files in one breath, but you just want to simply knock a make file and write all the target files in a makefile, then you can use the "pseudo-target" feature:
ALL: prog1 prog2 prog3
. Phony: All

Prog1: prog1.o utils. o
CC-O prog1 prog1.o utils. o

Prog2: prog2.o
CC-O prog2 prog2.o

Prog3: prog3.o sort. O utils. o
CC-O prog3 prog3.o sort. O utils. o
We know that the first target in makefile will be used as its default target. We declare a "all" pseudo target, which depends on the other three goals. Because the pseudo-target is always executed, the three targets on which it depends are always not as new as the target "all. Therefore, rules for the other three goals will always be determined. In this way, we can generate multiple goals in one breath. ". Phony: All" declares that "all" is a "pseudo target ".
The preceding example shows that the target can also be dependent. Therefore, pseudo targets can also become dependencies. See the following example:

. Phony: cleanall cleanobj cleandiff

Cleanall: cleanobj cleandiff
Rm Program

Cleanobj:
RM *. o

Cleandiff:
RM *. Diff
"Make clean" will clear all files to be cleared. The "cleanobj" and "cleandiff" pseudo targets are a bit like "subprograms. Run the "make cleanall", "Make cleanobj", and "make cleandiff" commands to clear different types of files.

3.6 targets
There can be more than one target in the makefile rule, which supports multiple targets. It is possible that multiple targets depend on one file at the same time, and the generated commands are similar. So we can combine them. Of course, the execution command for Generating Rules for multiple targets is the same, which may cause us trouble, fortunately, we can use an automation variable "$ @" (the automation variable will be described later), which represents the set of all goals in the current rule, this may be very abstract. Let's look at an example.

Bigoutput littleoutput: Text. g
Generate text. G-$ (SUBST output, $ @) >$ @
The above rules are equivalent:

Bigoutput: Text. g
Generate text. G-big> bigoutput
Littleoutput: Text. g
Generate text. G-little> littleoutput
"$" In-$ (SUBST output, $ @) indicates to execute a makefile function. The function name is SUBST, followed by a parameter. The functions will be described later. Here, this function is used to intercept strings. "$ @" indicates the set of targets, just like an array. "$ @" extracts the targets in sequence and runs the commands.

3.7 static mode
The static mode makes it easier to define rules with multiple targets and makes our rules more flexible and flexible. Let's take a look at the Syntax:

<Targets... >:< target-pattern >:< prereq-patterns...>
<Commands>
...Tar gets defines a series of target files with wildcards. Is a set of targets.

Target-parrtern indicates the targets mode, that is, the target set mode.

Prereq-parrterns is the dependency mode of the target. It defines the dependency mode formed by target-parrtern again.

The three things may not be clearly described in this way. Let's give an example. If our <target-parrtern> is defined as "%. "O" means that all of our collections are ". O ", and if our <prereq-parrterns> is defined as" %. C ", which means to perform a second definition of the target set formed by <target-parrtern>. The calculation method is, take "%" in the <target-parrtern> mode (that is, remove [. o]), and add [. c] A new set formed at the end.

Therefore, the "Target mode" or "dependency mode" must contain the character "%, if your file name contains "%", you can use the Backslash "/" to escape it to indicate the true "%" character.

Let's look at an example:

Objects = Foo. O Bar. o

ALL: $ (objects)

$ (Objects): %. O: %. c
$ (CC)-C $ (cflags) $ <-o $ @

In the above example, we specify that our target is obtained from $ object, "%. O "indicates that all o. O Bar. "O", that is, the mode of the variable $ object set, and the dependency mode "%. c. The format is "%. O "" % ", that is," foo bar ", and add". c, so our dependency target is "foo. c bar. C ". In the command, "$ <" and "$ @" are automation variables. "$ <" indicates all dependent target sets (that is, "foo. c bar. C ")," $ @ "indicates the target set (also fade away. O Bar. O "). As a result, the above rules are equivalent to the following rules:
Foo. O: Foo. c
$ (CC)-C $ (cflags) Foo. C-o Foo. o
Bar. O: bar. c
$ (CC)-C $ (cflags) bar. C-o Bar. o
Imagine that if we have hundreds of "%. O", it would be too efficient to write a bunch of rules as long as we use this simple "static mode rule. The usage of "static mode rules" is flexible. If used well, it will be very powerful. Let's look at another example:
Files = Foo. ELC bar. O lose. o

$ (Filter %. O, $ (Files): %. O: %. c
$ (CC)-C $ (cflags) $ <-o $ @
$ (Filter %. ELC, $ (Files): %. ELC: %. El
Emacs-F batch-byte-compile $ <
$ (Filter %. O, $ (Files) indicates that the filter function of makefile is called to filter the "$ filter" set, as long as the mode is "%. O. I don't have to say much about its content. This example shows greater elasticity in makefile.

3.8 automatic dependency generation
In makefile, our dependency may need to contain a series of header files, for example, if our main. C contains the "# include" defs. H "", then our dependency should be:

Main. O: Main. c defs. h
However, if it is a relatively large project, you must know which c files contain header files, and when you add or delete header files, you also need to carefully modify the makefile, this is a very difficult task to maintain. To avoid this heavy and error-prone issue, we can use a C/C ++ compilation function. Most C/C ++ compilers support a "-M" option, that is, to automatically find the header file contained in the source file and generate a dependency. For example, if we execute the following command:
CC-m main. c
The output is:
Main. O: Main. c defs. h
So the dependencies automatically generated by the compiler do not have to manually write the dependencies of several files, but are automatically generated by the compiler. Note that if you use the gnu c/C ++ compiler, you must use the "-Mm" parameter. Otherwise, the "-M" parameter also contains header files of some standard libraries.
The output of GCC-m main. C is:

Main. O: Main. c defs. h/usr/include/stdio. h/usr/include/features. h/
/Usr/include/sys/cdefs. h/usr/include/GNU/stubs. h/
/Usr/lib/GCC-lib/i486-suse-linux/2.95.3/include/stddef. h/
/Usr/include/bits/types. h/usr/include/bits/pthreadtypes. h/
/Usr/include/bits/sched. h/usr/include/libio. h/
/Usr/include/_ g_config.h/usr/include/wchar. h/
/Usr/include/bits/wchar. h/usr/include/gconv. h/
/Usr/lib/GCC-lib/i486-suse-linux/2.95.3/include/stdarg. h/
/Usr/include/bits/stdio_lim.h

The output of GCC-mm main. C is:
Main. O: Main. c defs. h
Then, how does the compiler function relate to our makefile. As a result, our makefile must be regenerated based on these source files so that the makefile itself depends on the source file? This feature is not realistic, but we can use other methods to achieve it. The GNU Organization recommends that you put the dependency automatically generated by the compiler for each source file in one file, for each "name. c files generate a "name. d "makefile ,[. d] the corresponding [. c] File dependency.
Therefore, we can write [. c] file and [. d] File dependency, and make auto update or self-contained [. d] file and include it in our main makefile. In this way, we can automatically generate dependencies for each file.

Here, we provide a Pattern Rule to generate the [. d] file:

%. D: %. c
@ Set-E; RM-F $ @;/
$ (CC)-M $ (cppflags) $ <>$ @. $ ;/
Sed's,/($ */)/. O [:] *,/1.o $ @:, G' <$ @. $ >$ @;/
Rm-F $ @. $
This rule means that all [. d] the file depends on [. c] file. "RM-F $ @" means to delete all objects, that is [. d] file. The second line indicates "$ <" for each dependent file, that is [. c] The dependency file generated by the file. "$ @" indicates the mode "%. d "file. If there is a C file named name. c. "%" is "name", and "$" indicates a random number. The file generated in the second line may be "name. d.12345 ", the third line uses the SED command to make a replacement. For details about the usage of the SED command, see the relevant user manual. The fourth line is to delete temporary files.

All in all, the thing to do with this mode is to add the dependency of the [. d] file to the dependencies generated by the compiler, that is, the dependency:

Main. O: Main. c defs. h
Convert:
Main. O main. D: Main. c defs. h
So our [. d] the file is automatically updated and generated. Of course, you can also [. d] add not only dependencies to the file, but also the generated commands [. d] all files contain a complete rule. Once this is done, we will put these automatically generated rules into our main makefile. We can use the "include" command of makefile to introduce other makefile files (as mentioned earlier), for example:
Sources = Foo. c bar. c

Include $ (sources:. c =. d)
In the preceding statement, "$ (sources :. C =. d) ". C =. d "means to replace all the variables $ (sources [. c] strings are replaced [. d]. I will give more details about this "Replacement. Of course, you have to pay attention to the order, because include is used to load files on a per-time basis, the targets in the first [. d] file will become the default targets.

 

4 makefile writing commands

--------------------------------------------------------------------------------

The command lines in each rule are the same as those in the operating system shell. Make will execute the command one by one in sequence. Each Command must start with a [Tab] key, unless the command is followed by a semicolon after the dependency rule. Spaces or blank lines between command lines are ignored. However, if the space or blank line starts with the tab key, make considers it an empty command.

We may use different shells in UNIX, but the make command is interpreted and executed by the standard shell of "/bin/sh" -- UNIX by default. Unless you specify another shell. In makefile, "#" is a annotator, which is similar to "//" in C/C ++. All subsequent characters of this line are annotated.

4.1 Display command
Generally, make will output the command line to be executed to the screen before the command is executed. When we use the "@" character before the command line, this command will not be displayed by make. The most representative example is that we use this function to display some information like the screen. For example:

@ ECHO is compiling the xxx module ......
When make is executed, the "Compiling XXX module..." string is output, but no command is output. If no "@" is displayed, make will output:
ECHO is compiling XXX module ......
Compiling XXX module ......
If the make parameter "-n" or "-- just-print" is included during the make operation, the command is only displayed, but the command is not executed, this function is very helpful for us to debug our makefile and see what the commands we write are like or in what order.
The Make parameter "-s" or "-- slient" indicates that the command is fully forbidden.

4.2 Command Execution
When the dependent target is new to the target, that is, when the target of the rule needs to be updated, make will execute the subsequent command one by one. Note that if you want to apply the result of the previous command to the next command, you should use semicolons to separate the two commands. For example, if your first command is the CD command and you want the second command to be run on the basis of the CD command, you cannot write the two commands on two lines, the two commands should be written on one line and separated by semicolons. For example:

Example 1:
Exec:
CD/home/hchen
PWD

Example 2:
Exec:
CD/home/hchen; pwd
When we execute "make EXEC", the CD in the first example does not work. pwd prints the current makefile directory, and the CD works in the second example, PWD prints "/home/hchen ".

Make generally uses the system shell defined in the environment variable shell to execute commands. By default, it uses the standard UNIX shell --/bin/sh to execute commands. But it is a little special in the MS-DOS, because there is no shell environment variable in the MS-DOS, of course you can also specify. If you specify a Unix-style directory, make will first find the command interpreter in the path specified by shell. If it cannot be found, it will be searched in the current directory of the current drive letter. If it cannot be found, it will find it in all the paths defined in the PATH environment variable. In ms-dos, if your command interpreter is not found, it will add suffixes such as cmd.exe,. com,. bat, and. Sh to your command interpreter.

4.3 A command error occurred.
After a command is run, make checks the return code of each command. If the command returns success, make executes the next command. When all the commands in the rule are successfully returned, this rule is successfully completed. If a command in a rule has an error (the exit code is not zero), make will terminate the execution of the current rule, which may terminate the execution of all rules.

Sometimes, a command error does not indicate that it is an error. For example, for the mkdir command, we must create a directory. If the directory does not exist, mkdir is successfully executed. Everything is fine. If the directory exists, an error occurs. The reason why we use mkdir is that we must have such a directory, so we do not want mkdir errors to terminate the rule operation.

To do this, ignore command errors, we can add a minus sign "-" (after the tab key) in front of the makefile command line ), it is regarded as successful no matter whether the command is output or not. For example:

Clean:
-Rm-f *. o
Another global method is to add the "-I" or "-- ignore-errors" parameter to make. Therefore, all commands in makefile will ignore the error. If a rule targets ". Ignore", all commands in the rule will ignore the error. These are different levels of methods to prevent Command Errors. You can set them according to your preference.
Another make parameter is "-k" or "-- keep-going". This parameter indicates that if a command in a rule fails, the rule is executed, but other rules are executed.

4.4 execute make in a nested way
In some big projects, we place the source files of different modules or different functions in different directories. we can write a makefile for this directory in each directory, this makes our makefile more concise, instead of writing everything in a makefile, which makes it difficult to maintain our makefile, this technology is of great benefit to our module compilation and multipart compilation.

For example, we have a subdirectory named subdir, which has a MAKEFILE file to specify the file compilation rules in this directory. The makefile of the general control can be written as follows:

Subsystem:
CD subdir & $ (make)

It is equivalent:

Subsystem:
$ (Make)-C subdir
The definition of $ (make) macro variable means that our make may need some parameters, so defining it as a variable is more conducive to maintenance. In both examples, the "subdir" directory is first entered, and then the make command is executed.

We call this makefile "Master makefile". the variables of the master makefile can be passed to the lower-level makefile (if you show the Declaration), but will not overwrite the variables defined in the lower-level makefile, unless the "-e" parameter is specified.

If you want to pass the variable to the lower-level makefile, you can use the following declaration:
Export <variable...>
If you do not want some variables to be passed to lower-level makefile, you can declare them as follows:
Unexport <variable...>
For example:

Example 1:

Export variable = Value

It is equivalent:

Variable = Value
Export variable

It is equivalent:

Export variable: = Value

It is equivalent:

Variable: = Value
Export variable

Example 2:

Export variable + = Value

It is equivalent:

Variable + = Value
Export variable

If you want to pass all the variables, you only need one export. You don't need to talk about anything next to it, indicating that all variables are passed.

Note that there are two variables: shell and makeflags. Whether export or not, these variables are always passed to the lower makefile, especially the makefiles variable, it contains the make parameter information. If the make parameter is defined in the upper-layer makefile when we execute the "general control makefile", The makefiles variable will be these parameters, it is passed to the lower-level makefile. This is a system-level environment variable.

But there are several parameters in the make command that are not passed down. They are "-c", "-F ", "-h", "-o", and "-W" (details about makefile parameters will be described later). If you do not want to pass parameters to the lower layer, you can do this:

Subsystem:
CD subdir & $ (make) makeflags =

If you define the environment variable makeflags, you must be sure that the options are used by everyone. If "-T", "-n ", and the "-Q" parameter will lead to unexpected results, and may cause you to panic abnormally.

There is also a useful parameter in "nested execution". "-W" or "-- print-directory" will output some information during the make process, let you see the current working directory. For example, if our lower-level make directory is "/home/hchen/GNU/make" and we use "make-W" for execution, when we enter this directory, we will see:

Make: Entering directory '/home/hchen/GNU/make '.

When you exit the directory after completing the lower-layer make, we will see:

Make: Leaving directory '/home/hchen/GNU/make'

When you use the "-c" parameter to specify the lower makefile of make, "-W" will be automatically opened. If the parameter contains "-s" ("-- slient") or "-- no-print-directory", "-W" is always invalid.
--- ++4.5 define a command package

If the makefile contains some identical command sequences, we can define a variable for these identical command sequences. The syntax for defining this command sequence starts with "Define" and ends with "endef", for example:
Define run-YACC
YACC $ (firstword $ ^)
Mv y. Tab. C $ @
Endef

Here, "Run-YACC" is the name of the command package. It should not be the same as the variable name in makefile. The two lines in "Define" and "endef" are command sequences. The first command in this command package is to run the YACC program. Because the YACC program always generates the "Y. Tab. c" file, the second command is to change the file name. Let's take a look at this command package in an example.
Foo. C: Foo. Y
$ (Run-YACC)

We can see that to use this command package, we are just like using variables. In the use of this command package, "$ ^" in the Command package "Run-YACC" is "foo. Y "," $ @ "is" foo. C "(for this special variable starting with" $ ", we will introduce it later). When executing the command package, make will execute each command in the Command package independently.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.