[Switch] make command (1)

--------------------------------------------------------------------------------------------------------
reprint
Write Makefile with me: Introduction to MakeFile                                                                           
http://wiki.ubuntu.com.cn/index.php?title=Write a Makefile with me: Introduction to MakeFile&variant=en-us            
--------------------------------------------------------------------------------------------------------
1. Introduction to makefiles
    When the make command is executed, a makefile is required to tell the make command how to compile and link the program.
    First, we use an example to illustrate the writing rules of makefile. In order to give you a perceptual understanding. This example comes from the gnu make manual. In this example, our project has 8 c files and 3 header files. We need to write a makefile to tell the make command how to compile and link these files. Our rules are:
    1) If the project has not been compiled, then all our c files must be compiled and linked.
    2) If some c files of this project are modified, then we only compile the modified c files and link the target program.
    3) If the header file of this project has been changed, then we need to compile the c file that references these header files and link the target program.
    As long as our makefile is well written, all of this can be done with only one make command. The make command will automatically and intelligently determine which files need to be recompiled according to the current file modification, so as to compile the required files by ourselves. files and link target programs.
    makefile rules
        Before talking about this makefile, let's take a rough look at the rules of makefile.
        -------------------------------------------------------------------------------------------------------------------
        target ... : prerequisites ...
            command
            ...
            ...
        -------------------------------------------------------------------------------------------------------------------
        A target can be an object file, an executable file, or a label. For this feature of tags, it will be described in the subsequent "Pseudo-target" chapter.
        prerequisites are the files or targets required to generate that target.
        command is the command that make needs to execute. (arbitrary shell command)
        This is a file dependency, that is, one or more target files of the target depend on the files in prerequisites, and their generation rules are defined in command.
        To put it bluntly, if more than one file in prerequisites is newer than the target file, the command defined by command will be executed. This is the rule of the makefile. That is, the core content of the makefile.
        After all, the makefile thing is just like this, it seems that this document of mine should end.
        Ha ha. Not really, this is the main line and core of makefiles, but it is not enough to write a good makefile. I will tell you slowly with my work experience bit by bit later. There is more content. :)
    an example
        As mentioned earlier, if a project has 3 header files and 8 c files, in order to complete the three rules mentioned above, our makefile should look like this.
        -------------------------------------------------------------------------------------------------------------------------------------------
        edit : main.o kbd.o command.o display.o \
                insert.o search.o files.o utils.o /*Note: If the latter .o files are newer than the edit executable file, then the following command will be executed*/
            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
        ----------------------------------------------------------------------------------------------------------------------------------------------
    A backslash (\) means a newline. This makes the makefile easier to read. We can save this content in a file named "makefile" or "Makefile", and then directly enter the command "make" in this directory to generate the executable file edit. If you want to remove the executable and all intermediate object files, then simply do a "make clean".
    In this makefile, the target file (target) contains: the executable file edit and the intermediate target file (*.o), and the dependency files (prerequisites) are those .c files and .h files after the colon. Each .o file has a set of dependent files, and these .o files are the dependent files for executing file edit. The essence of the dependency relationship is to indicate which files are generated by the target file, in other words, which files are updated to the target file.
    After defining the dependencies, the following line defines the operating system command for how to generate the object file, which must start with a tab key. Remember, make doesn't care how the command works, it just executes the defined command. make will compare the modification date of the targets file and the prerequisites file. If the date of the prerequisites file is newer than the date of the targets file, or if the target does not exist, then make will execute the subsequently defined commands.
    It should be noted here that clean is not a file, it is just an action name, a bit like lable in c language, there is nothing after the colon, then make will not automatically find its dependencies, It will not automatically execute the commands defined after it. To execute subsequent commands (not only for clean, but also for other labels), it is necessary to clearly point out the name of the label after the make command. This method is very useful. We can define commands that are not compiled or not related to compilation in a makefile, such as program packaging, program backup, and so on.
2. How does make work
    In the default mode, that is, we only enter the make command. So,
    make will look for a file named "Makefile" or "makefile" in the current directory.
    If found, it will look for the first target file (target) in the file, in the above example, it will find the "edit" file and use this file as the final target file.
    If the edit file does not exist, or the modification time of the subsequent .o file that edit depends on is newer than the edit file, then he will execute the command defined later to generate the edit file.
    If the .o file that edit depends on does not exist, then make will find the dependency of the target .o file in the current file, and if found, generate the .o file according to that rule. (It's kind of like a stack process)
    Of course, your C files and H files exist, so make will generate .o files, and then use the .o files to generate make's ultimate task, which is to execute file edit.
    This is the dependency of the entire make. Make will find the dependencies of the files layer by layer until the first object file is finally compiled. In the process of searching, if there is an error, such as the last dependent file cannot be found, then make will exit directly and report an error, and for the error of the defined command or the unsuccessful compilation, make simply ignores it. make only cares about the file's dependencies, i.e. if the file after the colon is still not there after I look for the dependencies, then sorry, I don't 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 defined after it will not be automatically executed, but we can explicitly ask make to execute. That is, the command - "make clean", in order to clear all object files for recompilation.
    So in our programming, if the project has been compiled, when we modify one of the source files, such as file.c, then according to our dependencies, our target file.o will be recompiled (that is, in The command defined after this dependency), so the file of file.o is also the latest, so the modification time of the file of file.o is newer than that of edit, so edit will also be re-linked (see edit target file for details). command defined after).
    And if we change "command.h", then kdb.o, command.o and files.o will be recompiled, and edit will be relinked.
3. Use variables in makefiles
    In the above example, let's first look at the rules for edit:
    --------------------------------------------------------------------
    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 that the string of the [.o] file is repeated twice, if our project needs to add a new [.o] file, then we need to add it in two places (it should be three places, and A place is in clean). Of course, our makefile is not complicated, so it is not tiring to add it in two places, but if the makefile becomes complicated, then we may forget a place that needs to be added and cause the compilation to fail. So, in order to make the makefile easier to maintain, we can use variables in the makefile. The variable of the makefile is also a string, which may be better understood as a macro in the C language.
    For example, we declare any variable name, called objects, OBJECTS, objs, OBJS, obj, or OBJ, as long as it can represent the obj file. We define this variable at the beginning of the makefile as follows:
    ---------------------------------------------------------------------
    objects = main.o kbd.o command.o display.o \
            insert.o search.o files.o utils.o
    ---------------------------------------------------------------------
    Therefore, we can easily use this variable in our makefile in the form of "$(objects)", so our improved makefile becomes as follows:
    -------------------------------------------------------------------------------------------
    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 : 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 $(objects)
    -------------------------------------------------------------------------------------------------------
    If a new .o file is added, we simply modify the variable objects.
    More on the topic of variables, which I will cover in detail later.
4. Let make automatically deduce
    GNU's make is very powerful. It can automatically deduce the commands behind files and file dependencies, so we don't need to write similar commands after each [.o] file, because our make will automatically recognize, and deduce the command yourself.
    As long as make sees a [.o] file, it will automatically add the [.c] file to the dependencies. If make finds a whatever.o, then whatever.c will be the dependency file of whatever.o .
    And cc -c whatever.c will also be deduced, so our makefiles don't have to be so complicated anymore. Our new makefile is out again.
    -------------------------------------------------------------------------------------------------
    objects = main.o kbd.o command.o display.o \
            insert.o search.o files.o utils.o
     cc = gcc
    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 the "implicit rule" of make. In the above file content, ".PHONY" indicates that clean is a pseudo target file.
    For more detailed "obscure rules" and "pseudo-object files", I will give you one by one in the follow-up.
5. Alternative style makefiles
    Since our make can automatically deduce commands, I am a little uncomfortable seeing the dependencies of [.o] and [.h], so many repeated [.h], can you close it up, okay, No problem, this is easy for make, who told it to provide the function of automatically deriving commands and files?
    Let's take a look at the latest style of 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 file dependencies are a bit messy. You can not have it both ways. Also depends on your preferences.
    I don't like this style, one is that the dependencies of the files are not clear, and the other is that if there are too many files and several new .o files need to be added, it will be unclear.
6. Rules for emptying target files
    Every Makefile should write a rule to empty the object files (.o and executable files), which is not only easy to recompile, but also helps to keep the files clean. This is a "cultivation" (hehe, remember my "programming training"). The general styles are:
    -------------------------------------
    clean:
        rm edit $(objects)
    -------------------------------------
    A more robust approach is:
    -------------------------------------
    .PHONY : clean
    clean :
        -rm edit $(objects)
    -------------------------------------
    As mentioned earlier, .PHONY means that clean is a "pseudo target". And adding a small minus sign in front of the rm command means that maybe there is a problem with some files, but don't care, continue to do the following things.
    Of course, the rules of clean should not be placed at the beginning of the file, otherwise, this will become the default target of make, and I believe no one wants to do this. The unwritten rule is - "clean is always at the end of the file".
The above is an overview of a makefile, and it is also the basis of a makefile. There are many details of a makefile below. Are you ready? Come when you're ready.
1. What is in the Makefile?
    Makefile mainly contains five things: explicit rules, implicit rules, variable definitions, file instructions and comments.
        1. Explicit rules. Explicit rules describe how to generate one or more object files. This is clearly pointed out by the author of the Makefile, the file to be generated, the file's dependencies, the generated command.
        2. Obscure rules. Since our make has the function of automatic deduction, the implicit rules allow us to write Makefiles relatively simply, which is supported by make.
        3. Definition of variables. In the Makefile, we need to define a series of variables. The variables are generally strings. This is a bit like a macro in your C language. When the Makefile is executed, the variables in it will be expanded to the corresponding reference position.
        4. Documentation instructions. It includes three parts, one is to refer to another Makefile in one Makefile, just like include in C language; the other is to specify valid parts in Makefile according to certain situations, just like precompile in C language #if is the same; there is also the definition of a multi-line command. I will talk about this part in the next part.
        5. Notes. There are only line comments in the Makefile. Like the UNIX Shell script, the comments are made with the "#" character, which is like "//" in C/C++. If you want to use the "#" character in your Makefile, escape it with a backslash, eg: "\#".
        Finally, it is also worth mentioning that the commands in the Makefile must start with the [Tab] key.
2. The file name of the Makefile
    By default, the make command will look for files named "GNUmakefile", "makefile", and "Makefile" in the current directory in order, and find the file that explains it. Among the three file names, it is best to use the file name "Makefile", because the first character of this file name is capitalized, which has a conspicuous feeling. It is best not to use "GNUmakefile", this file is recognized by GNU make. There are other makes that are only sensitive to the all-lowercase "makefile" filename, but basically most make supports the default filenames "makefile" and "Makefile".
    Of course, you can use other file names to write Makefiles, 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" parameter, such as: make -f Make.Linux or make --file Make.AIX.
3. Reference other Makefiles
    1. Use the include keyword in the Makefile to include other Makefiles, which is very similar to #include in the C language. The included file will be placed in the include location of the current file as it is. The syntax for include is:
    ------------------------------------------
    include <filename>;
    ------------------------------------------
    filename can be the file mode of the current operating system shell (can contain paths and wildcards)
    There can be some null characters before include, but it must not start with the [Tab] key. include and <filename>; can be separated by one or more spaces.
    For example, you have several Makefiles: a.mk, b.mk, c.mk, and a file called foo.make, and a variable $(bar), which contains e.mk and f.mk , then, the following statement:
    ------------------------------------------
    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, it will look for other Makefiles indicated by include and place their contents in the current location. Just like the C/C++ #include directive.
    If the file does not specify an absolute path or a relative path, make will first look in the current directory. If it is not found in the current directory, then make will also look in the following directories:
        1. If there is a "-I" or "--include-dir" parameter when make is executed, then make will search in the directory specified by this parameter.
        2. If the directory <prefix>;/include (usually: /usr/local/bin or /usr/include) exists, make will also look for it.
    make generates a warning if any files are not found, but not an immediate fatal error. It will continue to load other files. Once the makefile is read, make will retry the files it did not find or cannot read.
    If that doesn't work, make will display a fatal message. If you want make to ignore unreadable files and continue execution, you can add a minus sign "-" before include. Such as:
    -----------------------------------------------------
    -include <filename>;
    -----------------------------------------------------
    It means that no matter what error occurs during the include process, do not report an error and continue to execute. A related command that is compatible with other versions of make is sinclude, which does the same thing as this one.
4. Environment variable MAKEFILES
    If the environment variable MAKEFILES is defined in your current environment, then make will do an action similar to include with the value in this variable.
    The values ​​in this variable are other Makefiles, separated by spaces. However, it is different from include in that the "target" of the Makefile imported from this environment variable will not work. If the file defined in the environment variable finds an error, make will ignore it.
    But here I still recommend not to use this environment variable, because as long as this variable is defined, then when you use make, all Makefiles will be affected by it, which is never what you want to see.
    I mention this here just to tell everyone that sometimes something strange happens to your Makefile, so you can see if this variable is defined in the current environment.
5. How make works
    The execution steps of GNU make when working are as follows: (I think other make is similar)
        1. Read in all Makefiles.
        2. Read in other Makefiles that are included.
        3. Initialize the variables in the file.
        4. Derive implicit rules, and analyze all rules.
        5. Create dependency chains for all object files.
        6. Based on dependencies, decide which targets to regenerate.
        7. Execute the build command.
    Steps 1-5 are the first stage, and steps 6-7 are the second stage. In the first phase, if the defined variable is used, then make will expand it in the used position.
    But make doesn't expand immediately. Make uses a delay tactic. If a variable appears in the rules of a dependency, the variable will only be expanded inside the dependency when it is decided to be used.
    Of course, you don't have to know how this works, but knowing this way you'll be more familiar with make. With this foundation, the subsequent sections are easy to understand.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325222296&siteId=291194637