Overview -
What is makefile? Perhaps many of WinodWs's programmers don't know this, because those Windows IDE has done this job, but I think it is necessary to make a good and Professional programmer, makefile still wants. This seems to have so many HTML editors, but if you want to be a professional, you still have to understand the meaning of HTML's identity. Especially in UNIX software, you can't write makefile yourself, will not write makefile, from one side, explain whether a person has the ability to complete large-scale projects.
Because Makefile is related to the compilation rules of the entire project. Source files in an engineering are not countful, which are placed in a number of directorys according to the type, function, and modules. Makefile defines a series of rules to specify, which files need to be compiled first, which files need to be compiled, which files need to be re- Compile, even more complex functional operations, because Makefile is like a shell script, which can also perform commands of the operating system.
The benefits brought by makefile are - "Automated Compilation", once written, only one make command, complete automatic compilation, greatly improve the efficiency of software development. Make is a command tool that is a command tool that explains the command in Makefile. In general, most of the IDE has this command, such as: Delphi Make, Visual C NMAKE, Linux GNU Make. It can be seen that Makefile has become a compilation method in engineering.
Now telling how to write Makefile's article is relatively small, this is the reason I want to write this article. Of course, the Make of different organizers is different, and there are different grammar, but their essence is in "document-dependent", here, I only tell the GNU's Make, my environment is Redhat Linux 8.0, Make's version is 3.80. By, this make is the most widely used and the most used. And it is still the most follow-in (POSIX.2) of IEEE 1003.2-1992.
In this document, it will be based on the source code of C / C as our foundation, so it will inevitably involve knowledge of compilation of C / C , related to this, please check the relevant compiler documents. The compiler default here is GCC and CC under UNIX.
About the compilation and link of the program ----------
Here, I think more about some norms and methods of program compilation. In general, both C, C , or PAS, first compile the source file into an intermediate code file, it is .OBJ file under Windows, UNIX The next is .o file, Object File, this action is called compilation. Then use a lot of Object File synthesizes the execution file, this action is a link (LINK).
When compiling, the compiler needs the correctness of the syntax, the function of the function and the variable declaration. For the latter, it is usually the location you need to tell the compiler header file (the header should just declare, and the definition should be placed in the C / C file), as long as all the syntax is correct, the compiler can compile the intermediate target. file. In general, each source file should correspond to an intermediate target file (O file or OBJ file). When linking, primarily link functions and global variables, so we can use these intermediate target files (O files or OBJ files) to link our applications. The linker does not manage the source file where the function is located, only the intermediate target file (Object file) of the function, in most, because the source file is too much, there are too many intermediate target files generated, and the middle is clearly pointed out in the link. The target file name, this is very inconvenient, so we have to make a package to the middle target file, which is called "library file" under Windows, that is, the .lib file, under UNIX, is Archive file, that is, .a file.
Summary, the source file will generate an intermediate target file and then generate an execution file by the intermediate target file. At compiler, the compiler only detects the program syntax, and the function, whether the variable is declared. If the function is not declared, the compiler gives a warning, but can generate Object File. When the linker, the linker finds the implementation of a function in all Object File. If you can't find it, you will report the link error code (Linker Error), under VC, this error is generally: Link 2001 Error, meaning, the linker failed to find the implementation of the function. You need to specify the Object File of the function.
Ok, the words come true, GNU's make has a lot of content, idle, or let us start.
Makefile introduction -------
When the make command is executed, you need a Makefile file to tell the make command to compile and link programs.
First, we use an example to illustrate the writing rules of Makefile. To give you a sense of sensibility. This example comes from the GNU's Make User Manual, in this example, our project has 8 C files, and 3 head files, we have to write a Makefile to tell the make command how to compile and link these files. Our rules are: 1) If this project is not compiled, all of our C files should be compiled and linked. 2) If a few C files in this project are modified, then we only build the modified C file and link the target program. 3) If the header file of this project is changed, then we need to compile C files that reference these header files and link the target program.
As long as our makefile is well written enough, all of this, we can complete with a make command, and the make command will automatically intelligently determine which files need to be recompiled according to the current file modification, so that they need to compile it. Files and link target programs.
I. Makefile rules
Before telling this makefile, let's take a look at the rules of Makefile first.
Target ...: prerequisites ... command ... Target is also a target file, which can be Object File or a executable file. It can also be a label, which is a narrative in subsequent "Pseudo Target" chapters for labels.
Prerequisites is to generate the files required by Target or goals.
Command is also a command that make Make needs to be executed. (Any shell command)
This is a dependency of a file, that is, Target This or more target files depends on the file in Prerequisites, which generates rules defined in Command. To put it bluntly, if there is more than one file in Prerequisites, the command defined by Command is executed more than the TARGET file. This is the rule of Makefile. That is, the core content in Makefile.
When I said, Makefile's thing is like this, as if I have this document. Ha ha. Not too, this is the main line and core of Makefile, but it is not enough to write a makefile. I will slowly come to you later. The content is more. :)
Second, one example
As mentioned earlier, if a project has 3 head files, and 8 C files, we should be the following this look in order to complete the three rules mentioned earlier.
Edit: main.o kbd.o command.o display.o / insert.o search.o files.o utils.o cc -o edit main.o kbd.o command.o display.o / insert.o search.o FILES.O Utils.o
Main.o: main.c defs.h cc -c main.c kbd.o: kbd.c defs.h command.h cc -c kbd.c command.o: command.c defs.h command.h cc - C command.c display.o: display.c defs.h buffer.h cc -c display.c insert.o: insert.c defs.h buffer.h cc -c insert.c search.o: search.c defs .h buffer.h cc -c search.c files.o: files.c defs.h buffer.h command.h cc -c files.c utils.o: utils.c defs.h cc -c utils.c clean : Rm edit main.o kbd.o command.o display.o / insert.o search.o files.o utils.o
The backslash (/) is the meaning of change. This is more convenient for Makefile easy to read. We can save this content in a file that "makefile" or "makefile", and then enter the command "make" directly in this directory to generate an execution file Edit. If you want to delete the execution file and all intermediate target files, then just make "make clean" as long as you do it. In this makefile, the target file (target) contains: execute file edit and intermediate target file (*.), Dependent file (prerequisites) is those .c files and .h files behind the colon. Each .o file has a set of dependencies, and these .o files are also performing file edit reliance on files. The dependence is essentially indicated by which files are generated by the target file, in other words, which files are updated.
After defining the dependency, subsequent rows define how to generate the operating system command of the target file, be sure to start with a Tab key. Remember, Make does not care how the command works, he only does the defined command. Make will compare the modification date of the targets file and prerequisites file. If the date of the Prerequisites file is more than the date of the targets file, or the target does not exist, Make will perform a subsequent command.
Here, it will be that Clean is not a file, it is just a one action name, it is a bit like Lable in the C language, and there is nothing after his colon. So, Make will not automatically find the dependency of the file. You will not automate the commands defined later. To perform the subsequent command, you will clearly indicate the name of this Lable after the make command. Such methods are very useful, we can define unused compiles or compilers in a makefile, such as program packaging, procedures, and so on.
Third, how is Make work?
In the default mode, that is, we only enter the Make command. Then
1. Make will find a file called "makefile" or "makefile" in the current directory. 2, if you find it, it will find the first target file in the file. In the above example, he will find the "Edit" file and use this file as the final target file. 3, if the Edit file does not exist, or the file modification time behind the EDIT is better than the Edit file, then he will perform the commands defined later to generate the Edit file. 4, if the EDIT is dependent on the .o file, Make will find the dependence of the target as the .o file in the current file, if you find it, generate the .o file according to the rule. (This is a bit like a stack process) 5. Of course, your C file and H file exist, so Make will generate .o file, then use .o file life make the ultimate task, that is, the execution file Edit .
This is the dependence of the entire make, and make a layer of relying on the document again until the first target file is finally compiled. In the process of finding, if an error occurs, if the last dependent file cannot be found, Make will exit directly and report the error, and the error of the defined command, or the compilation is unsuccessful. Make only the dependence of the document, that is, if I find the dependencies, the file behind the colon is still, then I am sorry, I will not work. Through the above analysis, we know that like Clean, it is not directly or indirectly associated with the first target file, then the commands that will be later defined will not be executed automatically, however, we can display to execute it as you want. That is, command - "make clean" to clear all target files to recompile.
So in our programming, if this project has been compiled, when we modify one of the source files, such as file.c, then according to our dependence, our target file.o will be compiled (that is The commands defined later later), so File.o's files are also the latest, so file.o's file modification time is more new than Edit, so Edit will be resinted (see Edit target file for details) The command that is defined).
And if we changed "command.h", then kdb.o, command.o and files.o will be recompiled, and Edit will be linked.
Fourth, use variables in Makefile
In the above example, let's take a look at the EDIT rules:
Edit: main.o kbd.o command.o display.o / insert.o search.o files.o utils.o cc -o edit main.o kbd.o command.o display.o / insert.o search.o FILES.O Utils.o
We can see the [.o] file string is repeated twice, if our project needs to join a new [.o] file, then we need to add (it should be three places, One place in Clean). Of course, our makefile is not complicated, so it is not tired in two places, but if Makefile is complicated, then we may have forgetting a place you need to join, but causing compilation to fail. So, for the easy maintenance of Makefile, we can use variables in Makefile. Makefile variables are also a string, understanding the macros in C language may be better.
For example, we declare a variable called Objects, Objects, Objs, Objs, Obj, or Obj, anyway, no matter what, you can represent the OBJ file. We define this at the beginning of Makefile:
Objects = main.o kbd.o command.o display.o / insert.o search.o files.o utils.o
So, we can easily use this variable in our Makefile, so our improvement version makefile will become the following look:
Objects = main.o kbd.o command.o display.o Utils.oEdit: $ (Objects) CC -O Edit $ (Objects) main.o: main.c defs.h Cc -c main.c kbd.o: kbd.c defs.h command.h cc -c kbd.c command.o: command.c defs.h command.h cc - Command.c Display.O: Display. C Defs.h buffer.h cc - C Display.c INSERT.O: INSERT.C Defs.h buffer.h cc -c insert.c search.O: search.c defs.h buffer.h cc -c search. C Files.o: files.c defs.h buffer.h command.h cc -c files.c utils.o: utils.c defs.h cc -c utils.c clean: RM Edit $ (Objects)
So if there is a new .o file join, we only need to simply modify the Objects variable.
With regard to the topic of variables, I will give you a way.
5. Let Make automatically derive
The GNU's makers are very powerful. It can automatically derive the files and the commands behind the document dependencies, so we don't have to write similar orders after each [.o] file, because our make will automatically identify. And derive the order yourself.
As long as Make sees an [.o] file, it will automatically put the [.c] file in the dependencies, if Make finds a wh dayver.o, then wherever.c will be the dependency of whatver.o . And cc -c whatver.c will also be derived, so our makefile will never write so complicated. We are new Makefile and released.
Objects = main.o kbd.o command.o display.o / insert.o search.o files.o utils.o
Edit: $ (Objects) cc -o edit $ (Objects)
Main.o: defs.h kbd.o: Defs.h command.h command.o: defs.h command.h Display.O: Defs.h buffer.h insert.o: Defs.h buffer.h search.o : Defs.h buffer.h files.o: Defs.h buffer.h command.h utils.o: defs.h
.Phony: Clean Clean: RM Edit $ (Objects)
This method is that Make's "murgee rules". In the above file content, ". Phony" means that Clean is a pseudo target file.
With more detailed "hidden rules" and "pseudo target files", I will give you a way.
Six, alternative Makefile
Others, our makers can be automatically derived, then I see that the bunch of [.o] and [.h] is a bit uncomfortable, so much repetitive [.h], can you get it more, okay. No problem, this is easy for MAKE, who told it to provide automatic derivation of commands and file features? Let's take a look at the latest style makefile. Objects = main.o kbd.o command.o display.o / insert.o search.o files.o utils.o
Edit: $ (Objects) cc -o edit $ (Objects)
$ (Objects): Defs.h kbd.o command.o files.o: command.h display.o insert.o search.o files.o: buffer.h
.Phony: Clean Clean: RM Edit $ (Objects)
This style makes our makefile very simple, but our document reliance is a bit messy. You can not have it both ways. I still see your preference. I don't like this style. First, the dependency of the document is unclear. Second, if there are more files, join a few new .o files, it will not be clear.
7. Rules for emptying target documents
Each Makefile should write a rule that empty the target file (.o and execution file), which is not only easy to compile, but also the cleaning of the file. This is a "cultivation" (huh, I still remember my "programming cultivation"). The general style is:
Clean: RM Edit $ (Objects)
More robust practices are:
.Phony: Clean Clean: -rm Edit $ (Objects)
As mentioned earlier, .phony means that Clean is a "pseudo-target". The meaning of adding a small minus sign in front of the RM command is that maybe some files have problems, but do not manage, continue to do. Of course, the rules of Clean do not place the beginning of the file, otherwise, this will become the default target of Make, I believe who is not willing to do this. The rules of incomplete are - "Clean has never placed the last file".
The above is the profile of makefile, is also the foundation of Makefile, and there are many Makefile related details, is it ready? Ready to get ready.
Makefile's summary -------
First, what is Makefile?
Makefile mainly includes five things: explicit rules, murgee rules, variable definitions, file instructions, and comments.
1. Explicit rules. Explicit rules illustrate how to generate one or more target files. This is clearly pointed out by Makefile's writing, the file, dependent file, the generation of the file, the resulting command.
2, the macious rules. Since our makers have automatic derived features, the concealed rules allow us to write Makefile relatively roughly, which is supported by Make.
3, the definition of the variable. In Makefile, we want to define a series of variables, the variables are generally strings, this a bit in your C language macro, when Makefile is executed, the variables are extended to the corresponding reference location.
4, file instructions. It includes three parts, one is to reference another Makefile in a makefile, just like include in the C language; the other means that the effective part in the makefile is specified according to some cases, just like the precompilation in the C language. #iF is the same; there is a multi-line command. I will talk about this part of the content. 5, comment. Makefile is only a row of comments, like UNIX's shell script, which comes with "#" characters, which is like "//" in C / C . If you want to use the "#" characters in your makefile, you can use the backstelvers to escape, such as "/ #".
Finally, it is also worth mentioning that in the Makefile command, you must start with the [Tab] button.
Second, Makefile's file name
By default, the make command finds the file name "gnumakefile", "makefile", "makefile", "makefile", "makefile", and found the file. In these three file names, it is best to use "makefile" file name, because this file name is the first character for uppercase, which has a dedicated feeling. It is best not to use "gnumakefile", this file is the GNU's Make recognition. There are additional Make only sensitive to the full-written "Makefile" file name, but basically, most of the Make supports "makefile" and "makefile" two default file names.
Of course, you can use other file names to write makefile, such as "make.linux", "make.solaris", "make.aix", etc. If you want to specify a specific makefile, you can use Make's "-f" And "--file" parameters such as: make -f make.linux or make --file make.aix.
Third, reference other Makefile
In Makefile Use the include keyword to include other makefiles, which is like a C language #include, the file being included in the original model is placed in the current file. Include's grammar is:
INCLUDE
FILENAME can be the file mode of the current operating system shell (you can save the path and wildcard)
There may be some empty characters in front of Include, but must never start with the [Tab] button. Include and
INCLUDE foo.make * .mk $ (bar)
Equivalent to:
Include foo.make a.mk B.mk C.MK E.MK F.MK
When the make command starts, the other makefile indicated by INCLUDE is placed in the current position. Just like the #include directive of C / C . If the file does not specify an absolute path or relative path, make will first look for the current directory, if you are not found in the current directory, then make it will find: Look at the following directory:
1. If Make is executed, there is a "-i" or "-include-dir" parameter, Make will find the directory specified by this parameter. 2, if the directory
-include
Fourth, environment variable Makefiles
If you define environment variables Makefiles in your current environment, Make will make a value in this variable similar to the Include action. The value in this variable is the other makefile, separated by spaces. However, it is different from the include that "target" from the Makefile introduced from this environment will not work. If the file defined in the environment variable finds an error, Make will ignore.
But here I still recommend not using this environment variable, because as long as this variable is defined, when you use Make, all Makefile will be affected by it, this is not what you want to see. Here, this is just to tell you, maybe sometimes your makefile has a strange thing, then you can see if this variable is defined in the current environment.
V. Make's work
The execution steps of GNU's Make work will be entered: (I want to come to other Make.)
1. Read all Makefile. 2, read into other Makefiles of include. 3. Initialize the variables in the file. 4. Divide the murchest rules and analyze all rules. 5. Create a dependency chain for all target files. 6. Depending on the dependencies, decide which goals should be regenerated. 7, execute the generated command.
1-5 Steps to the first stage, 6-7 is the second phase. In the first phase, if the defined variable is used, Make will expand it in the location where it is used. But Make will not completely start immediately. Make uses delay tactics. If the variable appears in the rules of dependencies, then the variable is only expanded within it only when this reliance is determined.
Of course, this way you don't have to be clear, but you know this way you will be more familiar with Make. With this foundation, the subsequent part is easy to understand.
Writing rules ----
The rules contain two parts, one is a dependency, one is a way to generate the target.
In Makefile, the order of the rules is very important, because only one ultimate goal is in Makefile, other goals are coming from this target, so be sure to let Make know what your ultimate goal is. In general, it may be a lot of targets defined in makefile, but the target in the first rule will be established as the final goal. If there are many objectives in the first rule, then the first goal will become the final goal. This goal is made by Make.
Ok, let's take a look at how to write rules.
First, rule example
Foo.O: foo.c defs.h # foo module cc -c -g foo.c
Seeing this example, you should not be very strange. I have said before, foo.o is our goal, foo.c and defs.h are the source files dependent on the target, and only one command "cc -c - g foo.c "(beginning with a Tab). This rule tells us two things:
1, the dependency of the file, foo.o depends on the file of foo.c and defs.h, if foo.c and defs.h's file date should be new, or foo.o does not exist Then, the dependency occurs. 2, how to generate (or update) the foo.o file. That is the CC command, which explains how to generate foo.o file. (Of course, foo.c files include DEFS.H file)
Second, the syntax of the rules
Targets: Prerequisites Command ...
Or this:
Targets: prerequisites; Command Command ...
Targets is the file name, separated by space, and can use wildcard. In general, our goal is basically a file, but it may be multiple files.
Command is a command line if it is not with "Target: prerequisites", then, must start with the [Tab key], if Prerequisites are in one line, then the semicolon is separated by sections. (See)
Prerequisites is also a file (or dependent objective) that is dependent on the target. If a file is to be new than the target file, then the goal is considered "Out of", it is considered to be reborn. This has been told in front.
If the command is too long, you can use a backslash box ('/') as a newline. Make has no restrictions on how many characters on a line. The rule tells make two things, dependencies, and how to become a target file.
In general, Make will execute commands with UNIX standard shell, that is, / bin / sh.
Third, using 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 UNIX's B-shell.
Wave number ("~") characters also have a relatively special purpose in the file name. If it is "~ / test", this means the TEST directory under the $ HOME directory of the current user. And "~ hchen / test" represents the Test directory under the host directory of the user's hCHEN. (These are small knowledge under UNIX, Make also supports) and under Windows or MS-DOS, the user does not have a host directory, then the directory referred to by the Wave is determined according to the environment variable "home".
Wildcard replaces your series of files, such as "* .c" means that the suffix is C. One requires us to pay attention, if there is a wildcard in our file name, such as "*", you can use the escape character "/", such as "*" to represent the true "*" character, not any length String.
Ok, let's take a few examples first:
Clean: rm -f * .o
I don't have much to say this above, this is the wildcard supported by the operating system shell. This is a wildcard in the command. Print: * .c lpr -p $? Touch Print
The above example illustrates the wildcards or in our rules, the target Print relies on all [.c] files. Where "$?" Is an automated variable, I will tell you later.
Objects = * .o
The above example, indicated, the harness can also be used in the variable. Not to say [* .o] will start, no! The value of Objects is "* .o". The variable in makefile is actually a macro in C / C . If you want a wildcard to expand in the variable, that is, the value of Objects is all the collection of file names of [.o], then you can:
Objects: = $ (Wildcard * .o)
This method is pointed out by keyword "wildcard", and we will discuss later about MAKEFILE.
Fourth, file search
In some big projects, there are a lot of source files, and our usual approach is to classify this many source files and store them in different directories. So, when Make needs to find the dependencies of the document, you can add a path before the file, but the best way is to tell Make with a path, let Make go automatically.
The special variable "vpath" in the makefile file is to complete this feature. If this variable is not specified, Make will only find dependencies and destination files in the current directory. If this variable is defined, Make will find a file in the specified directory when the current directory is not found.
Vpath = src: ../ Headers
The above definition specifies two directories, "src" and "../headers" ,make will search in this order. The directory is separated by a "colon". (Of course, the current directory is always the highest priority search)
Another way to set the file search path is to use the "vPath" keyword (note that it is a lot of writing), this is not a variable, this is a keyword, which 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 very flexible function. There are three ways to use:
1, vpath
Specify search directory
2, vpath
Clear the search directory of files that match the pattern
3, vpath
Clear all file search directories that have been set.
vpath% .h ../headers
This statement is indicated that Make is required to search all files ending with ".h" in the ".h" directory. (If a file is not found in the current directory)
We can use the vPath statement continuously to specify different search strategies. If the same
It means ".C" file, first in the "foo" directory, then "BLISH", and finally the "bar" directory.
vpath% .c foo: bar vpath% blish
The above statement indicates ".C" file, first in the "foo" directory, then "Bar" directory, and finally the "blish" directory.
V. Pseudo
In an early first example, we mentioned a "clean" goal, this is a "pseudo-objective",
Clean: rm * .o Temp
Just like "clean" in our previous example, we have generated many file compilation files, and we should also provide a "target" to clear their "target" to prepare to compile completely. (Use this goal with "make clean")
Because we don't generate "clean" files. "Pseudo Target" is not a file, just a label, because "pseudo-target" is not a file, so Make cannot generate its dependencies and decide whether it is to be executed. We only indicate this "goals" by displaying to take effect. Of course, "pseudo-target" is named with the file name, otherwise it will lose the meaning of "pseudo-target".
Of course, in order to avoid this situation to be renowned, we can use a special tag ".phony" to display a "pseudo-target", explain to make, regardless of this file, this goal is " Pseudo-target.
.Phony: Clean
As long as there is this statement, no matter whether there is a "clean" file, you have to run the "clean" target, only "make clean". So the whole process can write this:
.Phony: Clean Clean: Rm * .o Temp
Pseudo-targets generally have no dependencies. However, we can also specify the dependencies dependent on the pseudo target. The pseudo target can also be used as a "default target" as long as it is placed in the first one. One example is that if your makefile needs to generate a number of executable files, you just want to simply knock an opportunity, and all the target files are written in a makefile, then you can use "PseudoT" This 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 goal in makefile will be used as its default target. We declare a "all" pseudo-target that relies on the other three goals. Since the characteristics of the pseudo objective are always being executed, the three goals depending on the dependence are always like "all". Therefore, the rules of the other three goals will always be resolution. Just achieve the goal of us to generate multiple goals. ".Phony: all" declares that "all" is "pseudo-target".
Just mention, from the above example we can see that the goal can also be dependent. Therefore, the pseudo target can also be dependent. Look at the example below: .phony: CleanAll Cleanobj Cleandiff
Cleanall: Cleanobj Cleandiff RM Program
Cleanobj: rm * .o
CleanDiff: rm * .diff
"Make Clean" will clear all files to be cleared. "Cleanobj" and "Cleandiff" are a bit like "subroutine". We can enter the "make cleanl" and "make clean cleanobj" and "make cleandiff" commands to achieve the purpose of clearing different kinds of files.
Sixth, multi-objective
Makefile's goals can be more than one, which supports multi-objective, there is a possible number of targets to depend on a file, and the resulting command is generally similar. So we can merge them. Of course, the execution command of the generation rules of multiple targets is the same, which may have to bring trouble, but in our use of an automated variable "$ @" (about automated variables, it will be described later), this Variables show a collection of all goals in the current rules, which may be abstract, or look at an example.
BIGOUTPUT LITTLEOUT: TEXT.G Generate Text.g - $ (Subst Output, $ @)> $ @
The above rules are equivalent to:
Bigoutput: text.g generate text.g -big> Bigoutput LittleOutput: text.g generate text.g -little> LittleOutput
Among them, "$" in-$ (subst output, $ @) means executing a Makefile function, the function is called Subst, which is the parameter. About the function will be described later. This function here is the meaning of the string, "$ @" indicates the collection of the target, just like an array, "$ @" takes out the goal, and it is committed to the command.
Seven, static mode
Static mode can be easier to define multi-objective rules that make our rules become more flexible and flexible. Let's first look at the syntax first:
Targets defines a range of target files that can have wildcards. Is a collection of goals.
Target-Parrtern is a model of Targets, that is, the target set mode.
Prereq-Parrterns is the dependency mode of the target, which is defined for the mode of Target-Parrtern.
This three things may still be clear, or an example to explain it. If we define "% .o", it means that our
Look at an example:
Objects = foo.o bar.o
All: $ (Objects)
$ (Objects):% .o:% .C $ (cc) -c $ (cflags) $ <-o $ @
In the above example, it indicates that our goal is obtained from $ Object, "%. O" indicates all the goals ending with ".o", that is, "foo.o bar.o", that is, the variable $ Object Collection The mode, and the dependency "% .C" is taken "% .o" "%", that is, "foo bar", and add ".c" suffix, so our dependency objective is "Foo.c bar.c". The "$ <" and "$ @" are automated variables in the command, "$ <" indicates that all dependence target sets (that is, "foo.c bar.c"), "$ @" indicates the target set (also It is "foo.o bar.o"). Therefore, the above rule is equivalent to the following rules:
FOO.O: FOO.C $ (cc) -c $ (cflags) foo.c -o foo. bar.o: bar.c $ (cc) -c $ (cflags) bar.c -o bar.o
Imagine if our "% .o" has hundreds, then we only use this very simple "static mode rule" to write a bunch of rules, it is too efficient. The Usage of Static Mode Rules is flexible, if you are used, it will be a very powerful feature. Look at an example:
FILES = foo.elc bar.o lose.o
$ (Filter% .o, $ (files)):% .o:% .c $ (cc) -c $ (cflags) $ <-o $ @ $ (file% .elc, $ (files):% .elc:% .el emacs -f batCh-byte-compile $
$ (Filter% .o, $ (files)) indicates that the Makefile's Filter function is filtered, and the "$ FILTER" set is filtered, as long as it is "% .o". Its content, I don't have to say more. This example shows greater flexibility in makefile.
Eight, automatically generate dependencies
In Makefile, our dependencies may need to include a series of headers, such as if there is a "#include" defs.h "in our main.c, then our dependency should be:
Main.o: main.c defs.h
However, if it is a relatively large engineering, you must know which C files contain which header files, and when you join or delete the header file, you need carefully to modify makefile, which is a very maintenance of work. In order to avoid this heavy and easy error, we can use a function of compilation of C / C . Most C / C compilers support a "-m" option, that is, automatically find the header files contained in the source file and generate a dependency. For example, if we do the following command: cc -m main.c
Its output is:
Main.o: main.c defs.h
Therefore, the dependency of the compiler is automatically generated, so that you don't have to manually write the dependencies of several files, and the compiler is automatically generated. It is necessary to remind a sentence that if you use the GNU's C / C compiler, you have to use the "-mm" parameter, otherwise, "-m" parameters will also include some standard libraries' header files.
GCC -M main.c output 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
So, how this feature of the compiler is associated with our makefile. Because in this way, our makefile should also regenerate according to these source files, let Makefile dependent on the source file? This feature is not realistic, but we can have other means to go back to this function. The GNU organization recommends putting the compiler to automatically generated dependencies of each source file in a file, generate a "name.d" Makefile file for each "name.c" file, [D] file Store the dependency of the [.c] file in place.
Thus, we can write the [.c] file and [.d] file dependency, and let the make automatically update or self-contained [.d] files, and contain it in our master makefile, so we will It can automatically generate the dependence of each file.
Here, we give a pattern rule to generate [.d] files:
% .d:% .c @set -E; RM -F $ @; / $ (cc) -m $ (cppflags) $> $ @. $$$$; / sed 's, /( ($ *) ) /. O:] *, / 1.o $ @:, g '<$ @. $$$$> $ @; / rm -f $ @. $$$$ This rule means, all [.d] files depend on the [.c] file, "RM -F $ @" means deleting all the goals, that is, [.d] file, the second line means, for each dependency file "$ <", That is, the [.c] file generation dependencies," $ @ "represents the"% .d "file, if there is a C file is name.c, then"% "is" name "," $$$ " $ "Means a random number, the second line generated files may be" name.d.12345 ", the third line uses the SED command to do a replacement, please refer to the related use documentation for the SED command. The fourth line is to delete a temporary file.
All in all, this mode is to do is to join the "dependency of the [.d] file in the dependency generated by the compiler, that is, depending on the relationship:
Main.o: main.c defs.h
transform:
Main.o main.d: main.c Defs.h
So, our [.d] file will be automatically updated, and will be automatically generated. Of course, you can also join in this [.d] file not just dependencies, including the generated commands can also join Let each [.d] file contain a decree rule. Once we finish this job, then we have to put these automatically generated rules into our master makefile. We can use the Makefile "Include" command to introduce other Makefile files (previously speaking), for example:
Sources = foo.c bar.c
INCLUDE $ (Sources: .c = .d)
".C = .d" in the above statement ".c = .d" means to make a replacement, replace all the strings of the variable $ (Sources) [.c] .d], about this "replace" content, I will have more detailed descriptions later. Of course, you have to pay attention to the order, because INCLUDE is filed as a file, the target in the [.D] file that is first loaded will become the default target.
Writing command ----
Command lines of each rule and the command line of the operating system shell are consistent. Make will execute a command in order, each command must begin with the [Tab] key, unless, the command is followed by the semicolon behind the dependency rule. The space in the command line or the blank line will be ignored, but if the space or the blank line is opened with a Tab key, Make will think it is an empty command.
We can use different shells under UNIX, but make the make commands are performed by the "/ Bin / SH" - UNIX standard shell. Unless you specifically specify a further shell. Makefile, "#" is an comment, very like "//" in C / C , and the subsequent bank characters are commented.
First, display command
Typically, Make will output the command line to be executed before the command is executed on the screen. When we use the "@" character before the command line, then this command will not be displayed by Make, the most representative example, we use this feature to display some information on the screen. Such as:
@echo is compiling XXX module ... When Make is executed, it will output "Compiling XXX Modules ..." strings, but will not output commands, if there is no "@", then Make will Output:
Echo is compiling XXX modules ... is compiling XXX modules ...
If Make is executed, bring the Make parameter "-n" or "--just-print", then it just displays the command, but does not execute the command, this feature is very good for us to debug our makefile, see what we write The command is what is executed or what is the order.
The Make parameter "-s" or "--slient" is a full disable command display.
Second, the command execution
When relying on the target is new in the target, the Make will execute the subsequent command when the target of the rule needs to be updated. It should be noted that if you want to apply the result of the previous command to the next command, you should use the semicolon to separate these two commands. For example, your first command is a CD command, you want the second command to run on the CD, then you can't write these two commands on both lines, but should write these two commands. On a row, separated by a semicolon. Such as:
Example 1: EXEC: CD / HOME / HCHEN PWD
Example 2: EXEC: CD / Home / Hchen; PWD
When we perform "make exec", the CD in the first example does not work. The PWD will print the current makefile directory, and in the second example, the CD works, and the PWD will print "/ HOME / HCHEN ".
Make is typically executing commands using the system shell defined in the environment variable shell, by default, using UNIX standard shell - / bin / sh to execute the command. But it is a bit special under MS-DOS because there is no shell environment variable under MS-DOS, of course you can also specify. If you specify the form of a UNIX style, first, Make will find a command interpreter in the path specified by Shell. If you can't find it, you will find it in the current directory in the current drive, if you can't find it, It will be found in all paths defined in the PATH environment variable. In MS-DOS, if your defined command interpreter is not found, it will give you a command interpreter, such as ".exe", ". Com", ".", ". Sh", etc.
Third, the order is wrong
Whenever the command is running, make detects the return code of each command. If the command returns success, then make will perform the next command. When all the commands in the rules have been successfully returned, this rule is completed. If a command in a rule is wrong (the command exits is not zero), the make will terminate the execution of the current rule, which will be possible to terminate all rules.
Sometimes, the error of the command does not mean that it is wrong. For example, the mkdir command, we must build a directory, if the directory does not exist, then MKDIR will be executed successfully, and all the best, if the directory exists, then it is wrong. The reason why we use MKDIR is to have such a directory, so we do not want MKDIR to go wrong and terminate the rules.
In order to do this, ignore the error of the command, we can add a minus "-" (after the Tab key) in the Makefile command line, the tag is not that the command is not wrong. Such as:
Clean: -rm -f * .o has a global way to add "-i" or "--ignore-errors" parameters to make, then all commands in makefile ignore the error. And if a rule is ".ignore" as a target, all commands in this rule will ignore the error. These are methods of preventing an error in different levels, you can set it according to your different.
There is also a "-k" or "--keep-going", which is "-k" or "--keep-going", this parameter is that if the command in a rule is wrong, then the execution of the rule, But continue to perform other rules.
Fourth, nested to execute Make
In some big projects, we will put our different modules or different functional files in different directories, we can write a directory of Makefile in each directory, which is conducive to let our makefile change It is more concise, not to write all things in a makefile, which will hardly maintain our makefile, which has a very benefit of our module compilation and segmentation compilation.
For example, we have a subdirectory called subdir. There is a Makefile file in this directory to indicate the compilation rules for the files in this directory. Then our total control Makefile can write like this:
Subsystem: CD Subdir && $ (MAKE)
It is equivalent to:
Subsystem: $ (MAKE) -C Subdir
Defining $ (Make) macro variable means that perhaps our make needs some parameters, so defined as a variable is also conducive to maintenance. These two examples means to enter the "subdir" directory first, then execute the make command.
We call this Makefile ", the total control Makefile" can be passed to the subkey Makefile (if you show the declaration), but will not cover the variable defined in the subkefile of the next layer, unless specified "-e "parameter.
If you want to pass a variable into a sub-Makefile, then you can use this statement:
EXPORT
If you don't want some variables to be delivered to the sub-Makefile, then you can declare this:
UNEXPORT
Such as: Example 1:
Export variable = value
It is equivalent to:
Variable = Value Export Variable
It is equivalent to:
Export variable: = value
It is equivalent to:
Variable: = Value Export Variable
Example 2:
Export variable = Value
It is equivalent to:
Variable = Value Export Variable
If you want to pass all variables, then just an export. There is no need to follow, indicating that all variables are passed.
It should be noted that there are two variables, one is the shell, one is makeflags, these two variables don't care if you export, it is always necessary to pass to the next Makefile, especially the makefiles variable, which contains the parameter information of Make, If we have a Make parameter or define this variable in the upper Makefile when performing "Total Makefile", the makefiles variable will be these parameters and will be transferred to the lower Makefile, which is a system-level environment variable. However, several parameters in the make command are not transmitted down, they are "-c", "- f", "- h" "- o" and "-w" (detail about the Makefile parameter will be described later. ), If you don't want to pass parameters to the lower layer, then you can come:
Subsystem: CD Subdir && $ (Make) Makeflags =
If you define environment variables makeflags, then you have to be confident that everyone will be used, if there are "-t", "- n", and "-q" parameters, then there will be unexpected The result may make you panic.
There is also a more useful parameter, "- w" or "-print-directory" in "Nesting Execution", which will output some information in the opportunity of Make, so that you see the current working directory. For example, if our subordinate Make directory is "/ home / hchen / gnu / make", if we use "make -w" to execute, we will see when entering the directory, we will see:
Make: Entering Directory `/ Home / Hchen / GNU / Make '.
When you leave the directory after completing the subkey, we will see:
LEAVING DIRECTORY `/ Home / Hchen / GNU / Make '
When you use the "-c" parameter to specify the next Makefile, "- W" will be turned on automatically. If there is "-s" ("- slient") or "--no-print-directory" in the parameter, "- w" is always invalid.
V. Define the command package
If there are some same command sequences in Makefile, we can define a variable for these same command sequences. Defining the syntax of this command sequence begins with "Define", end with "endef", such as:
Define Run-Yacc Yacc $ (Firstword $ ^) mv y.tab.c $ @ Endef
Here, "run-yacc" is the name of this command package, which does not reintegrate with the variables in Makefile. Two lines in "define" and "endef" are command sequences. The first command in this command package is running the YACC program because the YACC program always generates "Y. Tab.c" files, so the second line command is to change this file. Or put this command to a sample to see it.
FOO.c: foo.y $ (run-yacc)
We can see that we want to use this command package, we seem to use variables. In this command package, "$ ^" in the command package "Run-Yacc" is "foo.y", "$ @" is "foo.c" (related to this special variable starting with "$" We will introduce later), Make is executing each command in the command package when executing the command package.
Use variables ----
In Makefile, it is like a macro in the C / C language. He represents a text string. It will automatically start the original model when it is executed in Makefile. It is different from C / C that you can change its value in makefile. In Makefile, variables can be used in "target", "dependency", "command", or other parts of Makefile. The name word of the variable can contain characters, numbers, and underscore (can be digital start), but should not contain ":", "#", "=" or empty characters (spaces, Enter, etc.). Variables are sensitive, "foo", "foo", and "foo" are three different variable names. The traditional Makefile variable name is a new name method, but I recommend using a variable name with case, such as: Makeflags. This avoids conflicts with the system's variables, and accidents occur.
Some variables are very strange strings, such as "$ <", "$ @", etc., these are automated variables, I will introduce later.
First, the basis of variables
Variables need to give initial values when they declare, while in use, need to add "$" symbol before the variable name, but it is best to use small brackets "()" or parentheses "{}" gives the variables. If you want to use a real "$" character, you need to use "$$".
Variables can be used in many places, such as "depends", "dependencies", "command", and new variables. First look at an example:
Objects = program.o foo.o utils.o program: $ (bjects) cc -o program $ (Objects)
$ (Objects): Defs.h
Variables are exactly exactly using it, just like the macro in C / C , for example:
Foo = C Prog.O: PROG. $ (FOO) $ (FOO) $ (Foo) PROG. $ (foo)
After you get:
Prog.o: prog.c cc -c prog.c
Of course, don't do this in your makefile, here is just an example to indicate the true look of the variable in the Makefile. It can be seen that it is a "alternative" principle.
In addition, the variable plus parentheses is completely used for more securely using this variable, in the above example, if you don't want to add parentheses, it can, but I still recommend that you add parentheses to the variable.
Second, variables in variables
When defining the value of the variable, we can use other variables to construct the value of the variable, there are two ways to define variables with variables in both Makefile.
First, look at the first way, that is, the "=" number, "=" is the variable, the right side is the value of the variable, the value of the right variable can be defined anywhere in the file, that is, The variables in the right side are not necessarily a value that has been defined, and it can also use the value of the following definition. Such as:
Foo = $ (bar) bar = $ (ugh) ugh = huh?
All: Echo $ (foo)
We execute "make all" will play the value of the variable $ (foo) is "huh?" ($ (Foo) value is $ (bar), $ (bar) value is $ (UGH), $ (UGH) The value is "huh?") It can be seen that the variable can be defined using the rear variables.
This feature has a good place, and there is a bad place. Well, we can push the true value of the variable to the back to define, 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 will let Make fall into the unlimited variable expansion process, of course, our make is the ability to detect such definitions and will report an error. There is also if you use a function in a variable, then this way makes our maket time very slow, worse, he will use two Make functions "Wildcard" and "shell" unpredictable error. Because you won't know how many times the two functions will be called.
To avoid this method, we can use another variable to define variables in another variable. This method is used ": =" 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 previous variable cannot use the rear variable, and 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 is a relatively simple variable is used, let us look at a complex example, including the use of Make functions, conditional expressions and a system variable "MakeElevel":
Ifeq (0, $ {MakeElevel}) Cur-Dir: = $ (Shell PWD) Whoami: = $ (Shell WhoAmi) Host-type: = $ (shell arch) make: = $ {make} host-type = $ { Host-type} whoami = $ {whoami} Endif
About conditional expressions and functions, we will say later, for system variable "MakeElevel", it means that if our make has a nested action (see "Nesting Make" in front of "Nesting Make"), then this variable Will record our current Makefile call layers.
Let's introduce two definition variables, please see an example, if we want to define a variable, its value is a space, then we can come:
nullstring: = Space: = $ (nullstring) # end of the line
nullstring is an EMPTY variable, where there is no, and 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 used, first using an EMPTY variable to indicate the value of the variable start, and the "#" comment is used to indicate the termination of the variable definition, so We can define the variables of a space. Please note that this feature of "#" is worthy of our attention, if we define a variable if we do this:
DIR: = / foo / bar # Directory to put the frobs Indir This value of this variable is "/ foo / bar", followed by 4 spaces, if we use this variable to specify other directories - "$ Dir) / file "Then it will be finished.
There is also a more useful operator "? =", First see example:
Foo? = Bar
The meaning is that if the foo is not defined, the value of the variable foo is "bar". If Foo is previously defined, then this language will not do anything, its equivalent:
IFEQ ($ (Origin foo), undefined) foo = bar endif
Third, the variable advanced usage
Here, the advanced use of two variables is introduced, the first is the replacement of variable values.
We can replace the common parts in variables, "$ (var: a = b)" or "$ = b)", it means that all of the variables "VAR" "A "" A "" "A" "end" is replaced with "B" string. The "end" here means "space" or "ending".
Still watching an example:
Foo: = a.O B.O C.O bar: = $ (foo: .o = .c)
In this example, we define a "$ (foo) variable, and the second line means all" end "all in" .o "string" "" "" .c " Therefore, our "$ (bar)" is "AC BC CC".
Another variable replacement technology is defined in "static mode" (see the previous chapter), such as:
Foo: = a.O B.O C.O Bar: = $ (foo:%. o =%. c)
This depends on the same mode in the replaced string, and a "%" character must contain a "%" character. This example also allows the value of the $ (bar) variable to "A.c B.C C.C".
The second advanced utility is - "The value of the variable is changed as a variable." First look at an example:
X = y y = z A: = $ ($ (x)))
In this example, the value of $ (x) is "y", so $ ($ (x)) is $ (y), and the value of $ (a) is "Z". (Note, "x = y", not "x = $ (y)")
We can also use more levels:
x = y y = z z = u A: = $ ($ ($ (x)))))))
The value of $ (a) here is "U", the relevant derivation leaves the reader to do itself.
Let us be more complicated, use the first way to "use variables in variable definition" to see an example:
X = $ (y) y = z z = Hello A: = $ ($ (x))
The $ ($ (x)) is replaced by $ ($ (y)) because the $ (y) value is "Z", so the end result is: A: = $ (z), that is, "Hello ".
More complicated, we plus functions:
x = variable1 variable2: = Hello Y = $ (Subst 1, 2, $ (x)) z = y A: = $ ($ (z)))))
In this example, "$ ($ ($ (z)))" Extension is "$ ($ (y)", and it is once again extended to "$ ($ (SUBST 1, 2, $ (x)))))) ". The value of $ (x) is "variable1", the SUBST function replaces all the "1" strings in "variable1" into "2" strings, "Variable1" becomes "variable2", then takes the value, so , Finally, the value of $ (a) is the value of $ (variable2) - "Hello". (Oh, it is easy to say in this way, or you can use multiple variables to form a variable name, 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".
Let's take a look at the example of combining the first technology:
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", the value of $ (Sources) is "AC BC CC"; if the value of $ (a1) is "1", then $ (Sourcees) The value is "1.c 2.c 3.c".
Let's take a look at an example of using this technology and "function" 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 adbgqc), the value of $ (foo) is "abcdgq", and if "do_sort" is not defined, then: foo: = $ (Sort ADBGQC), calling the Strip function.
Of course, "the value of the variable is changed as a variable", which 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
Three variables are defined in this example: "DIR", "foo_sources" and "foo_print".
Fourth, add variable value
We can use the " =" operator to add value to the variable, such as:
Objects = main.o foo.o bar.o utils.o Objects = another.o
Thus, our $ (objects) value becomes: "main.o foo.o bar.o utils.o noother.o" (Another.o is added)
Use the " =" operator to simulate this example below:
Objects = main.o foo.o bar.o utils.o Objects: = $ (Objects) another.o
The difference is that it is more concise with " =".
If the variable is not defined before, " =" will automatically turn "=", if there is a variable definition in front, " =" will inherit the assassifier of the previous operation. If the previous one is ": =", " =" will assign a value in ": =" 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 "=", " =" will also be assigned as "=", then there will be a credit definition of the variable, which is very bad, so Make will automatically We solve this problem, we don't have to worry about this problem.
V. Override indicator
If there is a variable is usually set, the assignment of this variable will be ignored in the Makefile. If you want to set the value of such parameters in makefile, then you can use the "OVERRIDE" indicator. Its syntax is:
OVERRIDE
OVERRIDE
Of course, you can also add:
OVERRIDE
For multi-line variable definitions, we use the Define indicator, before the define indicator, you can also use the Ovveride indicator, such as:
Override Define Foo Bar Endef 6, multi-line variables There is also a way to set variable values to use the define keyword. The value of setting the variable using the define keyword can have a wrap, which facilitates the definition of a series of commands (before we told the "command package" technology to use this keyword).
The define indicator is followed by the name of the variable, and restarts the value of the defined variable, the definition is the end of the Endef keyword. Its work mode is the same as the "=" operator. The value of the variable can contain functions, commands, text, or other variables. Because the command needs to begin with the [Tab] key, if you don't start with the [Tab] button with the defined command variable, Make will not think it is a command.
The following example shows the usage of Define:
Define Two-Lines Echo Foo Echo $ (BAR) Endef
Seven, environment variables
Make runtime system environment variables can be loaded into the Makefile file while Make starts, but this variable has been defined in Makefile, or this variable is brought by the Make command line, then the value of the environmental variable of the system will Covered. (If Make specifies the "-e" parameter, the system environment variable will override the variables defined in Makefile)
Therefore, if we set the "cflags" environment variable in the environment variable, we can use this variable in all Makefiles. This has a relatively good benefit to our compilation parameters. If you define cflags in makefile, this variable in makefile will use the value of the system environment variable, a common and personalized unity, which is very similar to the "global variable" and "local variable" if it is not defined. When Make nested (see chapter "Nested Call chapter), the variable defined in the upper Makefile will be passed to the lower Makefile in the way system environment variables. Of course, by default, the variables set by the command line will be passed. And define the variables in the file, if you want to pass down MakeFile, you need to use the exprot keyword to declare. (See the previous section)
Of course, I don't recommend that many variables are defined in the system environment, so that when we don't have to make Makefile, it has the same system variable, which may bring more trouble.
Eight, target variables
The variables we defined in Makefile before we are talking about are "global variables", and we can access these variables throughout the file. Of course, except "automated variables", such as "$ <", such as "$ <" belongs to the "rule variable", and the value of this variable depends on the rules and definitions of dependent targets.
Of course, I can also set local variables for a target. This variable is called "target-specific variable", which can be with "global variable", because its role range is only in this rule and the joint rules Therefore, its value is only valid within the scope of action. The value of global variables other than the rule chain will not affect the rule chain.
Its syntax is:
This feature is very useful, when we set a variable, this variable will act to all rules caused by this goal. Such as:
PROG: CFLAGS = -g PROG: PROG.O FOO. BAR.O $ (CC) $ (cflags) prog.o foo. 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 the value of the global $ (cflags), in the PROG target, and all the rules that it raind (PROG. O foo.o bar.o rules), the value of $ (cflags) is "-g"
Nine, pattern variable
In the GNU's Make, the pattern variable is also supported. In the above target variable, we know that variables can be defined on a certain target. The advantage of mode variables is that we can give a "mode" to define variables on all targets that meet this mode.
We know that Make's "mode" is typically at least one "%", so we can define target variables in the end of [.o] in the following way:%. O: cflags = -O
Similarly, the syntax and "target variable" of the mode variable:
OVERRIDE is also a variable that is incorporated on the system environment, or the variable specified by the Make command line.
Conditions judgment ------
Using conditional judgment, Make can make Make selection different execution branches depending on the time of runtime. The conditional expression can be a value of a comparison variable, or a value of comparison variables and constants.
First, example
The following example, determine if the $ (cc) variable "GCC", if yes, use the GNU function to compile the target.
Libs_for_gcc = -lnu normal_libs =
Foo: $ (Objects) IFEQ ($ (CC), GCC) $ (CC) -O Foo $ (Objects) $ (Libs_for_GCC) ELSE $ (CC) -o Foo $ (Objects) $ (NORMAL_LIBS) ENDIF
It can be seen that in this rule in the example above, the target "foo" can select different libraries to compile the program according to the variable "$ (cc) value value.
We can see three keywords from the above example: IFEQ, Else, and Endif. IFEQ means the beginning of the conditional statement, and specifies a conditional expression, the expression contains two parameters, separated by commas, and the expression is enclosed in parentheses. Else indicates the condition of the conditional expression. Endif indicates the end of a conditional statement, and any conditional expression should end with ENDIF.
When our variable $ (cc) value is "GCC", the rules of the target foo are:
Foo: $ (OBJECTS) $ (CC) -o foo $ (ibjects) $ (libs_for_gcc)
And when our variable $ (cc) value is not "GCC" (such as "cc"), the rules of the target foo are:
Foo: $ (Objects) $ (CC) -o Foo $ (Objects) $ (Normal_Libs)
Of course, we can also write the above example more concise:
Libs_for_gcc = -lnu normal_libs =
IFEQ ($ (CC), GCC) libs = $ (libs_for_gcc) Else Libs = $ (NORMAL_LIBS) ENDIF
Foo: $ (Objects) $ (CC) -O Foo $ (Objects) $ (LIBS)
Second, grammar
The syntax of the conditional expression is:
as well as:
Where
The first is "IFEQ" you have seen in front.
IFEQ (
IFEQ ($ (Strip $),)
The "strip" function is used in this example. If the return value of this function is empty, then
The second condition key is "ifneq". The syntax is:
IFNEQ (
Its comparison parameter "arg1" and "arg2" are the same, if different, it is true. Similar to "IFEQ".
The third condition key is "IFDEF". The syntax is:
IFDEF
If the value of the variable
Example 1: BAR = foo = $ (bar) ifdef foo frobozz = yes else frobozz = no endif
Example 2: foo = ifdef foo frobozz = yes else frobozz = no endif
In the first example, the "FROBOZZ" value is "YES", and the second is "NO".
The fourth condition key is "ifndef". Its syntax is:
IFNDEF
I don't have much to say this, and "ifdef" is the opposite.
On
In particular, Make is the value of the conditional expression when reading Makefile, and selects the statement according to the value of the conditional expression, so don't put the automated variables (such as "$ @", etc.) In the conditional expression, since the automation variable is only running.
Moreover, in order to avoid confusion, MAKE does not allow the entire condition statement into two parts in different files. Use functions ----
You can use a function to process variables in makefile, so that our commands or rules are more flexible and intelligent. The function supported by Make is not much, but it is enough for our operation. After the function is called, the return value of the function can be used as a variable.
First, the function of the function call grammar
The function call, very like the use of variables, is also identified by "$", its syntax is as follows:
$ (
Or
$ {
Here,
Still come and see an example:
Comma: =, EMPTY: = Space: = $ (EMPTY) $ (EMPTY) foo: = a b c bar: = $ (Subst $ (Space), $ (Comma), $ (Foo))
In this example, the value of $ (COMMA) is a comma. $ (space) Download a space, $ (foo) value is "ABC", $ (bar) defined, calling the function "subst", this is a replacement function, this function has three Parameters, the first parameter is replaced by the string, the second parameter is replacing the string, the third parameter is a string of the operation. This function is also replacing the space in $ (foo) into a comma, so the value of $ (bar) is "A, B, C".
Second, string processing functions
$ (Subst
Name: String Replacement Function - Subst. Function: Replace the
Example: $ (Subst Ee, EE, Feet on The Street), replacing "EE" in "Feet on The Street" into "EE", the return result is "Feet on the street".
$ (Patsubst
Name: Mode string replacement function - Patsubst. Function: Find words in
$ (PATSUBST% .c,%. O, X.c.c bar.c)
Replace the string "x.c.c bar.c" Comply with the word [%. C] to replace [% .o], return the result is "x.c.o bar.o"
Remarks:
This is a bit similar to the relevant knowledge that we have said in front of the "variable chapter". Such as: "$ (var:
$ (Strip
Name: Go to Net Gift Function - Strip. Function: Remove the empty characters that start and end in the
Take the string "a b c" to the beginning and end space, the result is "a b C".
$ (Findstring
Name: Find string functions - Findstring. Function: Find
$ (FindString A, A B C) $ (Findstring A, B C)
The first function returns "A" string, the second return "" string (empty string)
$ (Filter
Name: Filter function - Filter. Function: Filter the words in the
Sourcees: = foo.c bar.c baz.s Ugh.h foo: $ (Sources) CC $ (Filter% .C%. S, $ (Sources)) -O Foo $ (Filter% .C% .s, The value returned by $ (SOURCES) is "foo.c bar.c baz.s".
$ (Filter-Out
Name: Reverse filter function - Filter-out. Function: Filter the words in the
Objects = main1.o foo.o main2.o bar.o mains = main1.o main2.o $ (Filter-Out $ (MAINS), $ (Objects)) The return value is "foo.o bar.o". $ (Sort )
Name: Sort Function - Sort. Function: Sort by words in string (ascending). Returns: Returns the sorted string. Example: $ (Sort Foo Bar LOSE) Returns "Bar Foo Lose". Note: The Sort function removes the same words in
.
$ (Word
Name: Take a word function - Word. Function: Take the string
$ (WordList ,
Name: Take a string string function --wordList. Function: Take the word string from to and to is larger than the number of words in start, to
$ (Words
Name: Word Number Statistics - Words. Function: Counting the number of words in strings in
$ (Firstword
Name: First single word function --firstword. Function: Take the first word in string
Override cflags = $ (PatSubst%, - I%, $ (Subst:,, $ (vPath)))
If our "$ (vPath) value is" src: ../ Headers "," $ (PatSubst%, - I%, $ (Subst:,, $ (vPath)) "will return" -isrc - I ../ Headers, this is the parameter of the CC or GCC search header file path.
Third, file name operation function
Below we want to introduce the function to process the file name. The parameter string of each function will be treated as one or a series of file names.
$ (DIR
Name: Take a directory function - DIR. Function: Take out the directory section from the file name sequence
$ (notdir
Name: Take the file function - NOTDIR. Function: Take an unlibular part from the file name sequence
$ (BaseName
Name: Take the prefix function - Basename. Function: Take out the prefix section of each file name from the file name sequence
$ (Addsuffix
Name: Add the assales - ADDSUFFIX. Function: Adding the suffix
Name: Add the prefix function - ADDPREFIX. Function: Adding the prefix
$ (Join
Name: Connection function - Join. Function: Add the words in
FOREACH function
The Foreach function is very different from other functions. Because this function is used for looping, the Foreach function in the makefile is almost in the FOR statement in the UNIX standard shell (/ bin / sh), or the Foreach statement in the c-shell (/ bin / csh). Built. Its syntax is:
$ (Foreach , ,
This function means that the words in the parameter are taken out one by one on the variable specified by the parameter , and then the expression included in
Therefore, is preferably a variable name, can be an expression, and in is usually enumerated in ["words in [TEXT". for example:
Names: = a b c d
Files: = $ (Foreach N, $ (Names), $ (N) .o)
In the above example, the words in the $ (name) will be taken out, and save the variable "n", "$ (n) .o" calculates a value each time according to "$ (N)", these values Space separation, finally the return as a Foreach function, so (files) value is "AO BO CO DO".
Note that parameters in Foreach are a temporary local variable. After the foreach function is executed, the variables of parameter will not function, and its scope is only in the foreach function. V. IF functions
The IF function is very similar to the conditional statement supported by the GNU's Make - IFEQ (see the chapter described earlier), the syntax of the IF function is:
$ (if
Or
$ (if
It can be seen that the IF function can contain "ELSE" sections, or not. That is, the parameter of the IF function can be two or three. The
The return value of the IF function is if
Therefore, Sixth, Call function The Call function is the only function that can be used to create new parameters. You can write a very complex expression. In this expression, you can define many parameters, then you can use the Call function to pass parameters to this expression. Its syntax is: $ (Call When Make performs this function, the variables in the Foo = $ (Call Reverse, A, B) So, the value of foo is "a b". Of course, the order of the parameters can be customized, not necessarily the order, such as: REVERSE = $ (2) $ (1) Foo = $ (Call Reverse, A, B) The value of the FOO at this time is "B A". Seven, Origin functions The Origin function is not like other functions, he does not operate the value of the variable, he just tells you where your variable is coming? Its syntax is: $ (Origin Note that "Undefined" If If "Environment" If File If "Command Line" If "OVERRIDE" If Automatic " If This information is very useful for we write makefile, for example, suppose we have a Makefile package that packs a definition file Make.def, defined a variable "Bletch" in make.def, and there is an environment variable in our environment "BLETCH", at this time, we want to judge, if the variable comes from the environment, then we will redefine it, if you come from the Make.DEF or the command line, then we don't redefine it. So, in our makefile, we can write this: IFDEF BLETCH Ifeq "$ (Origin Bletch)" Environment " Bletch = BARF, GAG, ETC. ENDIF ENDIF Of course, you may say that you can redefine the variables in the environment using the Override keyword? Why do you need this step? Yes, we use Override to achieve such an effect, but Override is too rude, it will overwrite the variables defined from the command line, and we just want to redefine the environment, and do not want to redefine the command line Come. Eight, shell function The shell function is not like other functions. As the name suggests, its parameters should be the command of the operating system shell. It and the reverse "` "are the same function. That is to say, the shell function returns the output after executing the operating system command as a function. Thus, we can generate a variable with an operating system command and a string handle command awk, sed, etc., such as: Contents: = $ (Shell Cat Foo) Files: = $ (shell echo * .c) Note that this function will generate a shell program to execute the command, so you have to pay attention to its run performance, if you have some complicated rules in your makefile, and use this function, so you are harmful to your system performance. . Especially the macked rules of makefile may make your shell function to do much more than what you imagine. Nine, control the function of MAKE Make provides some functions to control the operation of Make. Typically, you need to detect some runtime information when running makefile, and determine this information, you let Make continue, or stop. $ (Error Generate a fatal error, IFDef Error_001 $ (Error Error IS $ (Error_001)) ENDIF Example 2: Err = $ (Error Found An Error!) .Phony: ERR Err:; $ (ERR) The example will generate an Error call when the variable error_001 is defined, and the example two will occur when the directory ERR is executed. $ (WARNING This function is like an Error function, but it does not let Make exits, just output a warning message, and make continues. Make's run ------ In general, the easiest is to enter the make command directly on the command line, and the make command will be executed by the current directory Makefile, everything is automatic. But sometimes you may just want Make to recompile certain files, not the entire project, and sometimes you have several compilation rules, you want to use different compilation rules in different times, and so on. This chapter is to tell how to use the make command. First, Make's exit code There are three exit code after the board command is executed: 0 - means successfully executed. 1 - Any error occurs if the Make is running, it returns 1. 2 - If you use the "-q" option of make, and make make some targets don't need to be updated, then returns 2. Make's related parameters we will tell in subsequent chapters. Second, specify Makefile As we said, GNU Make finds the default Makefile rule to find three files in the current directory - "gnumakefile", "makefile", and "makefile". It is found in order to find these three files, once found, start reading this file and execute. Currently, we can also assign a special name for Makefile to the Make command. To achieve this feature, we want to use the "-f" or "-file" parameter ("--makefile" parameter). For example, we have a Makefile name is "hchen.mk", then we can let Make to perform this file: Make -f hchen.mk If the command line in Make is, you use the "-f" parameter at once, then all specified Makefile will be delivered together to Make. Third, designated goals In general, MAKE's ultimate goal is the first target in Makefile, while other goals are generally brought by this target. This is the default behavior of Make. Of course, in general, your Makefile's first goal is made up of many targets, you can indicate Make to complete the target you specify. To achieve this, it is very simple. You need to direct the name directly with the target after the make command (as shown in the "Make Clean" form mentioned above) Any goals in makefile can be specified as the ultimate target, but except "-" heads, or contains "=" target, because there are targets of these characters, they are parsed into command line parameters or variables. Even the goals we have clearly written can also become the ultimate goal of Make, that is, as long as Make can find its implicit rules, this implicit target can also be specified as the ultimate goal. There is a Make environment variable called "makecmdgoals". The list of the ultimate target you specify is stored in this variable. If you don't specify the target, this variable is null. This variable allows you to use in some particular situations. Such as the example below: Sourcees = foo.c bar.c ifneq ($ (Makecmdgoals), Clean) Include $ (Sources: .c = .d) endif Based on this example, as long as the command we entered is not "Make Clean", Makefile will automatically contain two Makefiles "foo.d" and "bar.d". Methods using the specified ultimate goal can easily compile our programs, such as the following example: .Phony: All All: PROG1 PROG2 PROG3 PROG4 From this example, we can see that there are four programs that need to be compiled - "PROG1", "PROG2", "PROG3", and "PROG4", we can compile all the "make all" commands. Goal (if the AL is set to the first goal, simply perform "make"), we can also use "make prog2" to compile the target "PROG2" separately. Often Make can specify all the goals in all makefile, then "pseudo-target", so we can make our makefile to complete different things based on this nature. In UNIX world, when the software is released, especially the release of this open source software, its Makefile contains compilation, installation, packaging and other functions. We can refer to this rule to write the goals in our makefile. "All" pseudo-objective is the goal of all goals, and its feature is generally compiled all the goals. "Clean" pseudo-objective function is to delete all files created by Make. "Install" pseudo-objective function is to install the compiled program, in fact, copy the target execution file to the specified target. "Print" The function of this pseudo-target is to illustrate the changed source file. "TAR" pseudoactive function is to package the source package backup. That is a TAR file. "DIST" pseudo-target function is to create a compressed file, which is generally pressed into the z file. Or GZ file. "Tags" pseudo-objective function is to update all the goals for complete recompilation. "Check" and "Test" are generally used to test the process of Makefile. Of course, a project's makefile does not have to write such a goal, these things are gnu's things, but I think the GNU has a certain thing (waiting for the program files under your UNIX. You will find these features very useful), it is just a description. If you want to write this function, it is best to use this name to name your goals, so that some, the normative benefits is - do not explain, Everyone understands. And if you have these features in your makefile, one is very practical, and the other is that you can look very professional (not the work of the beginner). Fourth, check rules Sometimes, we don't want our rules in our makefile, we just want to check our commands or execute the sequence. So we can use the following parameters of the make command: "-N" "--just-print" "--dry-run" "--Recon" does not perform parameters, these parameters are just print commands, regardless of whether the target is updated, print the rules and the commands under the joint rules, but Do not execute, these parameters are very useful for us to debug Makefile. "-T" "--Touch" means that the time update of the target file, but does not change the target file. That is, Make pretend to be compiled, but is not a real compilation target, just turn the target into a compiled state. The behavior of "-q" "--question" is the meaning of finding the target, that is, if the target exists, then what else does not output, and of course, it will not perform compilation, if the target does not exist, it will print An error message. "-W Another interesting usage is to combine "-P" and "-v" to output the information when Makefile is executed (this will be described later). V. Make parameters The parameters definitions of all GNU Make version 3.80 are listed below. Other versions of the MAKE of the manufacturer are similar, but the specific parameters of other manufacturers make specific parameters, please refer to the respective product documents. The role of "-b" "- m" these two parameters is to ignore compatibility with other version of Make. "-B" "- always-make" thinks that all goals need to be updated (recompile). "-C
"-Debug [=
"-D" is equivalent to "--debug = a".
"-E" "- Environment-overrides" indicates the value of the value of the environment variable overrides the variable defined in the Makefile.
"-F =
"-I" "- ignore-errors" ignores all errors during execution.
"-I
"-J [
"-K" "- keep-going" error does not stop running. If generated a target fail, it will not be executed if the goal is dependent on it.
"-L
"-N" "- Just-print" "- DRY-RUN" "- Recon" only outputs the command sequence during execution, but does not execute.
"-O
"-P" "- print-data-based" outputs all data in Makefile, including all rules and variables. This parameter will make a simple makefile output a bunch of information. If you just want to output information, you can use the "Make -QP" command without performing Makefile. If you want to see the preset variables and rules before performing makefile, you can use "make -f / dev / null". The information of this parameter output will contain the file name and line number of your makefile file, so use this parameter to debug your makefile will be very useful, especially when your environment variable is very complicated.
"-Q" "- question" does not run the command, nor does it output. Just check if the specified target needs to be updated. If it is 0, it means to update, if it is 2, an error occurs.
"-R" "- no-builtin-rules" is forbidden to use Make to use any implicit rules.
"-R" "- no-builtin-variabes" prohibits make from using any implicit rules that do on variables.
"-S" "- Silent" - Quiet "does not output the output of the command at the command.
"-S" "- no-keyp-going" "- STOP" cancels the "-k" option. Because some, make inherited from the environment variable "makeflags". So you can use this parameter in the command line to make the "-k" option in the environment variables failed.
"-T" "- touch" is equivalent to Unix's touch command, just turning the dates of the target into the latest, that is, blocking the command to generate the target from running.
"-V" "- version" outputs the version of the Make program, copyright, etc. About Make.
"-W" "- print-directory" outputs information before and after running Makefile. This parameter is useful for tracking nested calls Make.
"--NO-print-Directory" prohibits "-w" option.
"-W
Implied rules ----
When we use makefile, there are some frequent uses, and use frequent frequencies. For example, we compile the C / C source program as an intermediate target file (UNIX is [.o] file, Windows is [. OBJ] file). This chapter tells some "implied" in makefile, earlier, no need to write the rules.
"Implied rules" is also a practice. Make will run according to this "convention" heart, even if there is no writing rules in our makefile. For example, compiling the [.c] file into the rule of [.o] file, you don't have to write it all, make it automatically derived this rule and generates the [.o] file we need.
The "Implied Rule" will use some of our system variables, we can change the value of these system variables to customize the parameters of the rules of the rules. If the system variable "cflags" can control compiler parameters when compiling.
We can also write down your implicit rules through the Mode Rules. There will be many restrictions with "Suffix Rules" to define implicit rules. Using Mode Rules will be more intelligent and clear, but the Suffix Rules can be used to ensure our makefile compatibility. We understand the "implicit rules", allowing us to better serve us, will let us know something "convention and customary", and not so that we feel inexplicable when we are running Makefile. Of course, anything is contradictory, water can be boat, can also come to the boat, so sometimes the "implied rules" will also cause non-small troubles. Only by knowing it, we can use it better.
First, use implied rules If you want to use an implicit rule to generate the goals you need, you need to do this is not to write the rules of this goal. So, make attempts to automatically derive the rules and commands that generate this goal. If Make can automatically derive the rules and commands of this goal, then this behavior is the automatic derivation of implicit rules. Of course, the implicit rules are some things that make prior approximately. For example, we have a Makefile below:
Foo: foo.o bar.o cc -o foo foo.o bar.o $ (cflags) $ (ldflags)
We can notice that this makefile has not written on how to generate rules and commands for both objects of foo.o and bar.o. Because Make's "Implied Rule" feature automatically automatically derives the dependence targets and generation commands of these two goals.
Make will find the rules that can be used in your own Implicit Rules library. If you find it, you will use it. If you can't find it, you will report it wrong. In the example above, the implied rule called for the Make call is to set the dependency file of [.o] to [.c], and use C Compile command "CC -C $ (cflags) [.c] "To generate the goal of [.o]. In other words, we don't have to write down the following two rules:
Foo.O: foo.c cc -c foo.c $ (cflags) bar.o: bar.c cc -c bar.c $ (cflags) Because this is already a "agreement" good thing, Make and We agree to generate the rules of the [.o] file with the CC "CC", which is implicit rules.
Of course, if we write your own rules for the [.o] file, Make will not automatically derive and call the implicit rules, which will be faithfully executed in accordance with our write.
Also, in the "implied rules library" of Make, each implied rule has its order in the library, the more often used, so this will cause us to show us even if we show The target dependence is specified, and MAKE will not be tubed. Such a rule (no command):
FOO.O: foo.p
Relying on the file "foo.p" (source file of the Pascal program) may become meaningless. If there is a "foo.c" file in the directory, then our implicit rules will take effect, and will generate a foo.o file through the compiler of C. Because, in the implicit rule, the rule of PASCAL appears after the rule of C, so Make finds the rules that can generate foo.o will no longer look for the next rule. If you really don't want any implicit rules to derive, then you don't just write the "dependency rules" without writing the command.
Second, the implicit rule list here We will tell all the implicit rules of all pre-set (which is Make built). If we don't know the rules, Make will find the required rules and commands in these rules. Of course, we can also use Make's parameters "-r" or "--no-builtin-rules" option to cancel all pre-set implicit rules.
Of course, even if we specify the "-r" parameter, some implicit rules will take effect, because there are many implicit rules that use the Suix Rules to define, so as long as there is implicit rules Suffix List (also defined in the target .Suffixes dependent target), the implicit rules will take effect. The default suffix list is: .out, .a, .ln, .o, .c, .cc, .c, .p, .f, .f, .r, .y, .l, .s, .s , .mod, .ssym, .def, .h, .info, .dvi, .tex, .texinfo, .Texi, .txinfo, .w, .ch .web, .sh, .elc, .l. For details, we will talk later.
Still let's take a look at the common implicit rules.
1. Compile the implied rules of the C program. The dependent target of "
2, compile the implicit rules for C programs. The dependent target of "
3, compile the implicit rules for the PASCAL program. The dependent target of "
4, compile the implicit rules for the Fortran / RatFor program. The dependent target of "
6. Compile the implicit rules for the MODULA-2 program. The dependent target of "
7, assembly and assembly pre-processing implicit rules. The dependent target of "
8, link the implicit rules for the Object file. "
x: y.o z.o
And "x.c", "y.c" and "z.c" exist, the implicit rule will execute the following command:
Cc -c x.c -o x.o cc -c y.c -o y.O cc -c z.c -o z.O CC x.o y.O z.O -O X RM-F x.o rm -f y.O rm -f z.o
If there is no source file (such as X.c in the above example) and your target name (as X) in the above example, you'd better write your own generation rules, otherwise, implicit rules will report an error.
9. Implicit rules when YACC C procedures. "
10. Implicit rules when Lex C procedures. "
Third, the variables used by the implicit rules are basically used in the commands in the implicit rules, basically use some pre-set variables. You can change these variables in your makefile, or incorporate these values in the command line of make, or set these values in your environment variable, no matter how, just set these specific variables, Then it will work for implicit rules. Of course, you can also use Make's "-r" or "--no-builtin-variables" parameter to cancel the role of the variable you defined on the implicit rule.
For example, the first implicit rule-compiling the implied rule of the C program is "$ (cc) -c $ (cflags) $ (cppflags). Make default compile command is "CC", if you redefine the variable "$ (cc)" to "GCC", turn the variable "$ (cflags)" to "-g", then implicit rules The command will be executed in "GCC -C -G $ (cppflags).
We can divide the variables used in the implicit rules into two: one is a command, such as "CC"; one is the parameter phase, such as "cflags". Below is the variables used in all implicit rules:
1. The variables of the command.
AR function library package program. The default command is "ar". AS assembly language compiler. The default command is "AS". CC C language compiler. The default command is "CC". CXX C language compiler. The default command is "G ". CO expands the file program from the RCS file. The default command is "CO". The preprocessor of the CPP C program (output is a standard output device). The default command is "$ (cc) -e". FC FORTRAN and RATFOR compiler and pre-processes. The default command is "F77". GET extension files from the SCCS file. The default command is "get". Lex Lex method analyzer program (for C or Ratfor). The default command is "lex". PC Pascal language compiler. The default command is "PC". YACC YACC Grammar Analyzer (for C procedures). The default command is "YACC". Yaccr Yacc Grammar Analyzer (for the Ratfor program). The default command is "Yacc -R". MakeInfo converts the TexInfo source file (.TEXI) to the INFO file program. The default command is "makeinfo". Tex creates a program from the Tex DVI file from the TEX source file. The default command is "tex". Texi2DVI creates a program from the TexInfo source file to create an army TEX DVI file. The default command is "texi2dvi". Weave converts web to tex program. The default command is "weave". CWEAVE converts C Web to TEX program. The default command is "CWEAVE". Tangle converts the web to the Pascal language program. The default command is "tangle". Ctangle converts C Web to C. The default command is "ctangle". RM deletes the file command. The default command is "RM -F". 2. These variables below the command parameters are the parameters of the relevant order. If there is no default value, its default value is empty. Arflags Function Library Package Ar Command Parameters. The default is "RV". Asflags assembly language compiler parameters. (When ".s" or ".s" file is displayed clearly). CFLAGS C language compiler parameters. CXXFLAGS C language compiler parameters. COFLAGS RCS command parameters. CPPFLAGS C Preprocessor parameters. (C and Fortran compilers are also used). FFlags Fortran language compiler parameters. GFLAGS SCCS "Get" program parameters. LDFLAGS linker parameters. (Such as "LD") LFLAGS LEX Grammar Analyzer Parameters. Pflags Pascal language compiler parameters. The Fortran compiler parameter of the RFLAGS Ratfor program. YFLAGS YACC Grammar Analyzer Parameters.
Fourth, the implicit rules chain sometimes, one goal may be rooted by a series of implicit rules. For example, an [.o] file generation may be first included with the [.y] file of YACC first [.c], then generate it again by the compiler of C. We call this series of implicit rules "implicit rules chain".
In the above example, if the file [.c] exists, then directly call the implied rules of the compiler, if there is no [.c] file, but there is a [.y] file, then YACC implicit rules Will be called, generate the [.c] file, then call the impact rules for C-compilation ultimately by [.c] to generate the [.o] file to achieve the goal.
We call this [.c] file (or target) called an intermediate goal. Anyway, Make will work hard to automatically derive all methods of generating the goals, regardless of the intermediate targets, they will take all implicit rules and the rules you write, and try to achieve goals, so sometimes May make you feel strange, how do my goal be generated? How is my makefile be crazy? By default, there are two places in the intermediate target, it is different from the general goals: the first difference is that the intermediate rule is triggered unless the intermediate target does not exist. The second difference is that as long as the target is successful, then the resulting intermediate target file will be deleted in "RM -F" during the final goal.
Typically, a file that is specified by Makefile to target or dependent the target cannot be treated as an intermediary. However, you can clearly illustrate a file or a target is an intermediary target, you can use pseudo-target ". InterMediate" to force the declaration. (Such as: .intermediate: MID)
You can also prevent Make from automatically deleting intermediate targets. To do this, you can use pseudo-target ". Secondary" to force statements (such as: .secondary: sec). You can also specify your goals to specify (eg:%. O) into the target ".precious" dependent object to save the intermediate file generated by the implicit rule.
In the "Implied Rules Chain", it is forbidden to appear twice or more, so that in order to prevent unlimited recursive conditions when Make is automatically derived.
Make will optimize some special implicit rules without generating an intermediate file. For example, from the file "foo.c" to generate the target program "foo", according to the truth, make it compiles the generation of the intermediate file "foo.o", then links into "foo", but in actual conditions, this action can be by one The "cc" command is completed (cc -o foo foo.c), so optimized rules will not generate an intermediate file.
V. Define mode rules
You can use the pattern rules to define an implicit rule. A mode rule seems to have a general rule, just in rules, the definition of the target needs to have a "%" character. "%" Means that one or more arbitrary characters. In the dependent target, "%" can also be used, but only the value of "%" in the target is dependent on its objectives.
One thing to note is that the "%" deployment occurs after the expansion of variables and functions, the expansion of variables and functions occurs when Make loads Makefile, while "%" in the mode rule occurs at runtime.
1, mode rule introduction
In the mode rule, at least in the target definition of the rule, "%" is included, otherwise, it is a general rule. The "%" definition in the target represents the matching of the file name, "%" represents any non-empty string of length. For example: "%. C" indicates the file name ending with ".c" (the file name is at least 3), and "s.%. C" is indicated by "s." Start, ". C" ending The file name (the file name is at least 5).
If "%" is defined in the target, then "%" value in the target determines the value of "%" in the target, that is, the "%" "%" in the target determines the dependency target "%" " For example, there is a pattern rule as follows:%. O:% .c;
Its meaning is, pointing out how to generate the rules of the [.o] file from all [.c] files. If the goal to be generated is "a.o B.O", "% C" is "a.c B.c".
Once the "%" mode in the target is determined, the make will be asked to match all the file names in the current directory. Once the Make is found, the Make will rule, so in the mode rules, the target may be Multiple, if there is a mode matching multiple targets, Make will generate all mode destination. At this time, make cares about the dependencies of the dependent file name and the generation of the target.
2, mode rule example
The following example is expressed, compiling all [.c] files into the [.o] file.
% .O:%. C $ (cc) -c $ (cflags) $ (cppflags) $ <-O $ @
Among them, "$ @" represents all the targets of the target, "$ <" indicates all the values that depend on the target. These strange variables are called "automated variables" and will be detailed later.
There are two objects in this example below:
%. Tab.c% .tab.h:% .y bison -d $
This rule tells Make to perform all [.y] files in "Bison -D
3, automation variable
In the above mode rules, the targets and dependent files are files, so how do we write a command to complete the corresponding goal from different dependency files? Because every time the pattern rule is parsed, it will be different targets and dependencies.
Automated variables are completed. In front, we have already proposed by the automated variables, I believe that you have seen it here has a sense of sensibility. The so-called automated variable is that this variable will automatically remove a series of files defined in the mode until all the modes of matching are finished. This automation variable should only appear in the rule command. Below is all automation variables and their description:
$ @ Represents the target file set in the rule. In the mode rule, if there are multiple targets, "$ @" is a collection of mode definitions that matches the target.
$% Is only when the goal is in a library file, indicating the target member name in the rule. For example, if a goal is "foo.a (bar.o)", then "$%" is "bar.o", "$ @" is "foo.a". If the target is not a library file (UNIX is [.a], Windows is [.lib] under [.lib]), then its value is empty.
$ $? All collection of new dependent targets than the target. Separate in space. $ ^ All relying on the target collection. Separate in space. If there are multiple repetitions in the dependent target, the variable will remove the duplicate dependency target, only one copy. $ This variable is very similar to "$ ^", and it is also a collection of dependent targets. It is just that it does not remove duplicate depends on the target. $ * This variable indicates "%" and its previous part in the target mode. If the target is "dir / a.foo.b", and the target mode is "a.%. B", then "$ *" value is "Dir / a.foo". This variable is more compared to constructing associated file names. If there is no mode definition in the target, "$ *" cannot be derived, but if the edix of the target file is made by Make, "$ *" is part of the suffix. For example, if the target is "foo.c", because ".c" is the suffix name that make can identify, "$ *" is "foo". This feature is GNU Make, it is likely not compatible with other versions of Make, so you should try to avoid using "$ *", unless you impose rules or static mode. If the suffix in the target is made by Make, "$ *" is a null value. When you want to operate only the updated dependencies, "$?" Is useful in explicit rules, for example, suppose having a library file called "lib", which is updated by several other Object files. Then the more efficient Makefile rules package the Object file package are: LIB: foo. bar.o lose.o win.o ar r lib $? In the autointer variables listed above. Four variables ($ @, $ <, $%, $ *) will only have a file when extension, and the other three values are a list of files. These seven automation variables can also obtain the directory name of the file or the file name in the current directory, just match "D" or "F". This is the characteristics of the old version of GNU Make. In the new version, we can use the function "DIR" or "notdir". The meaning of "D" is Directory, which is the directory, "f" meaning is File, that is, the file. Here is the meaning of "D" or "f" for the seven variables of the above: $ (@D) represents the directory section of "$ @" (not the end of the slash), if the "$ @" value is "dir / foo.o", then "$ (@d" is "DIR", If there is no slash in "$ @", its value is "." (Current directory). $ (@ F) means "$ @" file part, if the "$ @" value is "dir / foo.o", then "$ (@ f)" is "foo.o", "$ (@f) "It is equivalent to function" $ (notdir $ @). "$ (* D)" "$ (* f)" and the same as described above, is also the directory section and file part of the file. For the example above, "$ (* d)" returns "DIR", and "$ (* f)" returns "foo" "$ (% D)" "$ (% f)" indicates the directory section and the file part of the function package file member. This contains different directories in "Member" in the form of "Archive" forms, "MEMBER". "$ ( "$ (^ D)" "$ (^ f)" represents all directory sections and file parts of the dependencies. (No identical) "$ ( D)" "$ ( f)" represents all directory sections and file parts of all dependencies. (Can have the same) "$ (? D)" "$ (? F)" represents the directory section and file part of the updated dependencies. Finally, I want to remind it that for "$ <", in order to avoid unnecessary trouble, we will add parentheses to the specific characters behind $, for example, "$ (<)" is more than "$ <"It is better. It is important to note that these variables are only used in rules, and they are generally "Explicit Rules" and "Static Mode Rules" (see chapters of the "Writing Rules"). It doesn't make sense in implicit rules. 4, mode match In general, a target mode has a "%" with a prefix or suffix, or there is no prefix, which is directly a "%". Because "%" represents one or more characters, in the defined mode, we call the contents of "%" match "stem", such as "% .c", "Test.c" "Test" is "stem". Because when there is "%" in the target and dependent target, "stem" depends on the target will pass to the target, and "stem" in the target. When a mode match contains a file with a slash (actually not often included), then the directory portion will be first removed, then matched, then add the directory back. When the transmission of "stem", we need to know this step. For example, there is a mode "E% T", the file "src / EAT" matches this mode, so "SRC / A" is its "stem", if this mode is defined in the dependent target, and is dependent on this mode There is also a mode "C% R", then the target is "src / car". ("Stem" is transmitted) 5, overload built-in implicit rules You can overruun built-in implicit rules (or define a new), for example, you can re-construct and build different hidden rules, such as: % .O:% .C $ (CC) -C $ (CPPFLAGS) $ (cflags) -d $ (date) You can cancel the built-in implicit rules, as long as you don't write the command later. Such as: % .O:% .s Similarly, you can also redefine a new implicit rule, which in the hidden rules depends on where you write this rule. The front position is before. Sixth, "suffix rules" Suffix rules are a comparison method for implicit rules. Suffix rules will be gradually replaced by mode rules. Because pattern rules are stronger clearer. GNU Make is equally compatible with the old version of Makefile. There are two ways to suffix rules: "Double Suffix" and "Single Human Supper". Double complications define a pair of suffixes: the suffix of the target file and the suffix on the destination (source file). Such as ".c.o" is equivalent to "% o:% C". Single suffix rules only define a suffix, that is, the suffix of the source file. Such as ".c" is equivalent to "%:% .c". The suffix defined in the suffix rule should be made by Make. If a suffix is made by Make, then this rule is a single binding rule, and if the two consecutive suffixes are recognized by Make, it is a double suffix rule. For example: ". C" and ".o" are made by Make. Thus, if you define a rule is ".c.o" then it is the two suffix rules, meaning ".c" is the suffix of the source file, ". O" is the suffix of the target file. The following example: .c.o: $ (CC) -C $ (CFLAGS) $ (CPPFLAGS) -O $ @ $ < Suffix rules do not allow any dependencies, if there is a dependent file, it is not a suffix rule, and those subjectedures are considered to be file names, such as: .co: foo.h $ (cc) -c $ (cflags) $ (cppflags) -o $ @ $ % .O:% .c foo.h $ (cc) -c $ (cflags) $ (cppflags) -o $ @ $ < In the suffix rule, it is meaningless if there is no command. Because he doesn't remove the built-in implicit rules. To let Make know some specific suffixes, we can use pseudo-target ".suffixes" to define or delete, such as: . Suffixes: .hack .win Add a suffix .hack and .win Add to the end of the suffix list. .Suffixes: # Deletes the default suffix.suffixes: .c .o .h # Defines your suffix First clear the default suffix, define your own suffix list. Make's parameters "-r" or "-no-builtin-rules" also use the default suffix list empty. The variable "SUFFIXE" is used to define the default suffix list, you can use ".suffixes" to change the suffix list, but do not change the value of the variable "suffixe". Seven, implicit rules search algorithm For example, we have a target called T. Below is an algorithm for searching for the rules of target T. Note that in the following, we did not mention the suffix rule, because all suffix rules were converted into a pattern rule when Makefile was loaded into memory. If the target is "Archive (MEMBER)" function library file mode, then this algorithm will run twice, the first time I find the target T. If I haven't found it, then I will enter the second time, I will put "Member" Take T comment. 1. Separate T 's directory part. Call D, and the remaining part is called N. (, If T is "src / foo.o", then D is "src /", n is "foo.o") 2. Create all the list of pattern rules that matches T or N. 3. If there is a mode in which all files are matched in the pattern rule list, such as "%", remove other modes from the list. 4. Remove the rules that have no commands in the list. 5. For the first mode rule in the list: 1) derive its "stem" S, S should be t or n matched in "%" non-empty portions in the mode. 2) Calculate dependencies. Replace "%" in the dependency file into "stem" S. If there is no flap character in the target mode, the D is added to the beginning of the first dependency file. 3) Test if all dependencies are exist or are existed. (If there is a file being defined as a target file for another rule, or an explicit rule dependent file, then this file is called "reasonable") 4) If all dependencies exists or correct, or There is no dependency file. Then this rule will be adopted and exit the algorithm. 6. If you have passed through step 5, there is no mode rule to be found, then you will do something a further search. For the first mode rule existing in the list: 1) If the rule is termination rule, then ignore it, continue the next mode rule. 2) Calculate dependencies. (Step 5) 3) Test if all dependencies exist or correct. 4) For non-existent dependent files, recursive calls this algorithm lookup that he can be found by implicit rules. 5) If all dependencies exists or properly, or there is no dependency file. Then this rule is used to exit the algorithm. 7. If there is no implicit rule, you can use, view ".default" rules, if there is, use, use the ".default" command to T. Once the rules are found, they will execute their quite commands, while this time, our automation variables will be generated. Update the library file using the make ----------- The library file is also a package file for the Object file (intermediate file compiled). Under UNIX, it is generally made by the command "AR" to complete the package. First, the member of the library file A library file consists of multiple files. You can specify a library file and its composition in the following format: Archive (MEMBER) This is not a command, and a definition of a goal and dependencies. In general, this usage is basically to serve "AR" commands. Such as: FOOLIB (HACK.O): Hack.o Ar Cr Foolib Hack.o If you want to specify multiple member, then separate space, such as: FOOLIB (Hack.o Kludge.o) It is equivalent to: FOOLIB (Hack.o) Foolib (Kludge.o) You can also use the shell's file wildcard to define, such as: FOOLIB (*. o) Second, the implicit rules of the function library member When the Make search for an implicit rule, a special feature is that if this goal is "a (m)" form, it will turn the target "(m)". So, if our member is "% .o" mode definition, and if we use "make foo.a" to call Makefile form, the implicit rule will go to "bar.o" rules. If there is no rule of bar.o, then the internal implicit rules take effect. Make will go to Bar.c file to generate bar.o. If you find it, the command executed by Make is as follows: Cc -c bar.c -o bar.o ar r foo.a bar.o rm -f bar.o There is also a variable to pay attention to "$%", which is an automated variable of the exclusive library file. For its instructions, see the Automation Variables. Third, the suffix rule of the function library file You can use the Suffix Rules and Incontrol Rules to generate a log library package file, such as: .c.a: $ (cc) $ (cflags) $ (cppflags) -c $ <- 6 *. o $ (ar) R $ @ $ *. o $ (rm) $ *. o It is equivalent to: (% .O):%. C $ (cc) $ (cppflags) -c $ <-o $ *. o $ (ar) R $ @ $ *. o $ (rm) $ *. o Fourth, pay attention Carefully use the Make's parallel mechanism ("-j" parameter) when performing a function library packaging file. If multiple ar commands run on the same function library package at the same time, you can damage this library file. Therefore, in the future version of the Make, a mechanism should be provided to avoid parallel operations to occur on a function package file. But as far, you should not try not to use the "-j" parameter as much as possible. Subsequent sequence - Finally, when I wrote a concclusion, the above basically is all the details of GNU Make's makefile. The Make's Make is basically like this, no matter what kind of make, it is based on the dependence of documents. It is basically following a standard. 80% of the technical details in this document apply to any make, I guess the content of the "function" may not be supported by other make, and I think different makers will have different implementations. I have no energy to view the GNU's make and VC's nmake, BCB Make, or what difference is made under other Unix, one is not enough, the second is because I basically use it under UNIX. Make, previously in Sco Unix and IBM AIX, now in Linux, Solaris, HP-UX, AIX, and Alpha, LINUX and Solaris are more. However, I can be sure that Make under UNIX, whether it is a platform, almost all the Make and CC / GCC compilers developed by Richard Stallman, and basically all GNU Make (all The UNIX machine is installed on the GNU, so there are more programs that use the GNU). The GNU's thing is still very good, especially after use, more and more sense of the power of the GNU software, and more and more feel that GNU is "killed" in the operating system (mainly Unix or even windows). For all the details of all the makers, we can not only use the Make tool to compile our programs, but also use Make to complete other work, because the commands in the rules can be under any shell, so in Unix You don't necessarily just use the compiler of the program language, you can also write other commands in Makefile, such as: Tar, AWK, Mail, Sed, CVS, Compress, LS, RM, YACC, RPM, FTP ..., etc. , Etc., to complete the function, file operation, file management, file operation, file management, file operation, and file management, such as "Program Packaging", "Program Backup", "Production Program Installation Pack", "Submit Code", "User Template", "Merge File", etc. Programming development design, or some other whimsical things. For example, when you write a bank trading process, because the bank's trading procedure is basically the same, you will see some general program templates for some transactions, and write some network communication, database operation, business operation. In one file, in these files, in these files, such as "@@ n, ### n" strange string labeled some locations, then write a transaction, just writing a specific process according to a specific rule, and finally When Make, use awk and sed to replace the "@@ n, ### n" in the template to a specific program, form a C file, and then compile. This action is very similar to the "extended C" language of the database (ie, "Exec SQL" in the C language executes the SQL statement. Before compiling CC / GCC, you need to use "extended C" translation, such as CPRE, It translates into standard C). If you have some more wonderful methods when using Make, please remember to tell me. Looking back, look at the whole document, I don't think I have just started to develop in Unix a few years ago, someone asked if I would not write Makefile, I didn't know what to say. At first, I saw that someone turned "! Make" after writing the program in the VI, I thought it was the function of VI. Later, I learned that there was a makefile in my monster, so I checked it. At that time, I didn't want to read English, I found it. Introducing Makefile if there is no Chinese documentation, I have to see Makefile written by others, I have accumulated a little knowledge, but in many places, I don't know if I don't know. Later, I started working on UNIX product software. I saw a 400-year-old year, nearly 2,000 lines of code, I found that it is necessary to compile such a huge thing. If there is no Makefile, how horror is the same. So I came to my heart, I read a bunch of English documents, I feel that I have mastered it. But it is found that the article is still so pitiful, so I want to write such an article, share it for everyone, I hope to help you. Now I finally finished, I saw the creation time of the document, and this technical document has also been written for more than two months. I found that I know it is a matter, I have to write down, telling others is another thing, and now there is no time to study technology details, so when I write, it is difficult to explain some details. Doing rigorous and refined, and telling what you are telling what you first say, it is still referring to some foreign sites, and the language style of some technical books is completed. The outline of the entire document is written by the Outline of the GNU-based Makefile Technical Manual and combines its own work experience and its own learning. Because there is never written such a long, such a thin document, there will be many places where there is a problem, language ambiguity or error. Because of some, I am in urgent to wait to give me an indication and suggestion, as well as any feedback. Finally, use this in this back, introduce yourself. I have currently engaged in software development under all UNIX platforms, mainly to do distributed computing / grid computing system product software, and I am very interested in the next-generation computer revolution - grid computing, for distributed calculations , P2P, Web Service, J2EE technical direction is also very interesting, and at the same time, for project implementation, team management, project management is also small, I hope to fight the young generation on the "technology and management of" technology and management ", A lot of exchanges with me. My MSN is: haoel@hotmail.com (common), QQ is: 753640 (not common). (Note: Do not send me MSN's mailbox, because Hotmail's spam causes me to refuse this mailbox.) I welcome any form of communication, whether it is discussing technology or management, or other hairdryerous things. I don't care about politics and entertainment news, other things I am welcome! In the end, I also want to introduce the design and developer of the Make program. The first rush is: Richard Stallman Open source software leaders and pioneers have never received a day of salary, never used a Windows operating system. For his deeds and his software and his thoughts, I don't have to say too much, I believe that everyone is not strange to this person, this is his home page: http://www.stallman.org/. Only a recent photo is posted here: Computer, music, butterfly is his favorite The second is: Roland McGrath The personal home page is: http://www.frob.com/~roland/, below is some of his deeds: 1) Cooperation and maintain GNU Make. 2) Write GNU Hurd with Thomas Bushnell. 3) Write and maintain GNU C Library. 4) Cooperation and maintain part of GNU Emacs. Here, the fighter of the two open source projects is the most authentic tribute. (Full text)