[Linux development—Makefile tool]

1. Background

  • A project usually has multiple source files. If you only modify one of them, it would be a waste of time to re-compile and link all the source files. Therefore, it is very necessary to introduce the Makefile tool: the Makefile tool can automatically find those source files that need to be recompiled and linked based on file dependencies, and perform corresponding actions on them.

Two, understanding

1, make give makefile

  • Makefile defines a series of rules to specify which files need to be compiled first, which files need to be compiled later, which files need to be recompiled, and even more complex functional operations, because the makefile is like a Shell script, which can also be executed Operating system commands.
  • The most important and basic function of the Make tool is to describe the relationship between source programs through makefile files and automatically maintain the compilation work .
  • The makefile file needs to be written according to a certain syntax. The file needs to explain how to compile each source file and connect it to generate an executable file, and it is required to define the dependencies between the source files.
  • Makefile files are a common method used by many compilers - including compilers under Windows - to maintain compilation information. In the integrated development environment, users only modify the makefile files through a friendly interface.
  • In UNIX systems, it is customary to use Makefile as the makefile file. If you want to use another file as a makefile, you can specify the makefile using a similar make command option.
  • A file that instructs a program how to compile and link the program. The default name of the makefile is literally Makefile, but a command line option name can be specified.
  • The make program helps you when developing large programs to keep track of the entire program, parts of which have changed, and only those parts of the program compiled since the last time it was compiled.

2. About compilation

Compiling a small C program requires at least a single file .h file (if applicable). Although the command to perform this task is simply CC file.c, there are 3 steps to get the final executable program as follows:

  • Compilation phase : All C language code in the .c file is converted into a low-level assembly language; making the .s file.

  • Assembly stage : The assembly language code made in the previous stage is then converted into target code code fragments that the computer can directly understand. Object code file .o ends.

  • Linking Phase : The final phase of the compilation process involves linking the object code to the code base, which contains certain "built-in" functions such as printf. This stage produces an executable program, which by default is named a.out.

3. Compilation case:

Assume the following source files.

main.cpp
hello.cpp
factorial.cpp
functions.h
  • Contents of main.cpp file
#include <iostream.h>

#include "functions.h"

int main(){
    
    
    print_hello();
    cout << endl;
    cout << "The factorial of 5 is " << factorial(5) << endl;
    return 0;
}
  • Contents of hello.cpp file
#include <iostream.h>

#include "functions.h"

void print_hello(){
    
    
   cout << "Hello World!";
}
  • Contents of factorial.cpp file
#include "functions.h"

int factorial(int n){
    
    
    if(n!=1){
    
    
	return(n * factorial(n-1));
    }
    else return 1;
}
  • functions.h content
void print_hello();
int factorial(int n);

Trivial way to compile the file and get an executable, by running the following command:

CC  main.cpp hello.cpp factorial.cpp -o hello
  • CC: stands for compiler (gcc), compiles C++ files,
  • The above command will generate a binary Hello. In our case we only have four files and we know the sequence of function calls, so it might be feasible to write the above commands by hand and prepare the final binary. But for big projects, where we will have thousands of source code files, it will be difficult to keep binary versions.
  • The make command allows you to manage large programs or groups of programs . When you start writing larger programs, you will find that recompiling larger programs takes longer than recompiling short programs. Also found are usually only a small part of the program (such as a single function being debugged), leaving the rest of the program unchanged.

3. Makefile macro

The make program allows you to use macros, which are similar to variables. = One pair of macros defined in a Makefile. For example:

MACROS=  -me
PSROFF=  groff -Tps
DITROFF= groff -Tdvi
CFLAGS= -O -systype bsd43
LIBS := "-lncurses -lm -lsdl"
LIBS += -lstdc++
MYFACE = ":*)"
PROJ_HOME = /home/moon/projects
$(MACROS)
$(MYFACE) :*)

1. Special macros ( $@, $?, $<, $*, $^)

1,$@$?

There are some special predefined macros before issuing any commands to the target ruleset.
- $@represents the target file.
-Indicates a list of dependent files that are newer$? than the target .

So, for example, we could use a rule:

hello: main.cpp hello.cpp factorial.cpp
	$(CC) $(CFLAGS) $? $(LDFLAGS) -o $@

alternatively:

hello: main.cpp hello.cpp factorial.cpp
        $(CC) $(CFLAGS) $@.cpp $(LDFLAGS) -o $@
  • In this example $@ represents hello, ? or ? or? or @.cpp will pick up all changed source files.

2,$<, $*

There are two special macros used in implicit rules. They are:

  • $<Represents the first dependent file .
  • $*This variable represents the "%" and the part before it in the target pattern . .ofile equivalent to %.ofile
    • For $*if the target is "dir/a.foo.b" and the target's mode is "a.%.b", then $*the value of " " is "dir/a.foo". This variable is useful for constructing associated file names. If there is no pattern definition in the target, then " $*" cannot be deduced. However, if the suffix of the target file is recognized by make, then " $*" is the part except the suffix. For example: If the target is "foo.c", because ".c" is a suffix name recognized by make, $*the value of " " is "foo". 这个特性是GNU make的,很有可能不兼容于其它版本的make, so you should try to avoid using " $*" unless it is in implicit rules or static mode. If the suffix in the target is not recognized by make, then " $*" is a null value. .
      Common implicit rules are constructed for .o (object) files, .cpp (source files).
.o:.cpp
        $(CC) $(CFLAGS) -c $<

alternatively

.o:.cpp
        $(CC) $(CFLAGS) -c $*.cpp

3,$^

$^Show all dependencies:

LDFLAGS = -lstdc++
hello:main.o factorial.o hello.o
	$(CC) $^ $(LDFLAGS ) -o $@

2. Traditional macro

There are many default macros (typing " make -p " prints out the defaults). Most of the rules for using them are obvious:
these predefined variables, ie. Macros used in implicit rules fall into two broad categories: those with program names (such as CC) and those with parameters (such as CFLAGS).

Here is a table of some of the more common variables used as built-in rules: makefile program names.

  • AR Archive-maintaining program; default `ar’.
  • AS Program for compiling assembly files; default `as’.
  • CC Program for compiling C programs; default `cc’.
  • CO Program for checking out files from RCS; default `co’.
  • CXX Program for compiling C++ programs; default `g++'.
  • CPP Program for running the C preprocessor, with results to standard output; default `$(CC) -E’.
  • FC Program for compiling or preprocessing Fortran and Ratfor programs; default `f77’.
  • GET Program for extracting a file from SCCS; default `get’.
  • LEX Program to use to turn Lex grammars into source code; default `lex’.
  • YACC Program to use to turn Yacc grammars into source code; default `yacc’.
  • LINT Program to use to run lint on source code; default `lint’.
  • M2C Program to use to compile Modula-2 source code; default `m2c’.
  • PC Program for compiling Pascal programs; default `pc’.
  • MAKEINFO Program to convert a Texinfo source file into an Info file; default `makeinfo’.
  • TEX Program to make TeX dvi files from TeX source; default `tex’.
  • TEXI2DVI Program to make TeX dvi files from Texinfo source; default `texi2dvi’.
  • WEAVE Program to translate Web into TeX; default `weave’.
  • CWEAVE Program to translate C Web into TeX; default `cweave’.
  • TANGLE Program to translate Web into Pascal; default `tangle’.
  • CTANGLE Program to translate C Web into C; default `ctangle’.
  • RM Command to remove a file; default `rm -f’.

Here is a variable whose value is an additional parameter list for the above program. The default value for all of these is the empty string unless otherwise specified.

  • ARFLAGS Flags to give the archive-maintaining program; default `rv’.
  • ASFLAGS Extra flags to give to the assembler (when explicitly invoked on a .s' or .S’ file).
  • CFLAGS Extra flags to give to the C compiler.
  • CXXFLAGS Extra flags to give to the C compiler.
  • COFLAGS Extra flags to give to the RCS co program.
  • CPPFLAGS Extra flags to give to the C preprocessor and programs that use it (the C and Fortran compilers).
  • FFLAGS Extra flags to give to the Fortran compiler.
  • GFLAGS Extra flags to give to the SCCS get program.
  • LDFLAGS Extra flags to give to compilers when they are supposed to invoke the linker, `ld’.
  • LFLAGS Extra flags to give to Lex.
  • YFLAGS Extra flags to give to Yacc.
  • PFLAGS Extra flags to give to the Pascal compiler.
  • RFLAGS Extra flags to give to the Fortran compiler for Ratfor programs.
  • LINTFLAGS Extra flags to give to lint.

Note: You can uncheck -Ror --no-builtin-variablesoption all variables used by the implicit rule.
For example, you can also define macros on the command line

          make CPP=/home/moon/projects

4. Makefile defines dependencies

新版的gcc编译器引入头文件不再需要后缀名,eg:# include<iostream>,不再需要.h后缀。

It is very common that the final binary will depend on various source code and header files from the source code. Dependencies are important because they tell the source of any target. Please see the example below

hello: main.o factorial.o hello.o
	$(CC) main.o factorial.o hello.o -o hello
  • Here, we tell the object file hello that it depends on main.o, factorial.o and hello.o, so whenever there is any change, these object files will take action. You can also use the compile rule rule: $(CC) $? -o $@instead $(CC) main.o factorial.o hello.o -o hello.

At the same time we will tell how to prepare the .o file, so we must define these dependencies also as follows:

main.o: main.cpp functions.h
	$(CC) -c main.cpp

factorial.o: factorial.cpp functions.h
	$(CC) -c factorial.cpp

hello.o: hello.cpp functions.h
	$(CC) -c hello.cpp

If there are many dependencies, use += in LDFLAGS to connect
Insert image description here

5. Makefile definition rules

General syntax of a Makefile target rule

    target [target...] : [dependent ....]
    [ command ...]
  • Items in square brackets are optional, and ellipses refer to one or more. Note the label, required before each command.

Given below is a simple example that defines a rule that makes your target hello from three other files.

hello: main.o factorial.o hello.o
	$(CC) $? -o $@
  • NOTE: In this example you have to drop the rule so that all objects read the file from the source file

The semantics are quite simple. When "make target" finds that the target rules apply, if there is a dependent new target, it causes the commands to be executed one at a time (after macro substitution). If any dependencies occur, that happens first (allowing you to have a recursion).
If any command returns a failure status, MAKE will terminate. That's why you see rules like:

clean:hello
        -rm *.o *~ core paper
  • Make ignores the status returned by a command line that begins with a dash. For example. If there are no core files, who cares? , after defining clean, execute: make clean, clean all files

Make will echo the macro string after the replacement command, telling what happened as it happened. Sometimes you may want to turn them off. For example:

install:hello
        @echo You must be root to install
  • @ is added before the echo command to indicate silent output and will not print anything unrelated to the information. For example, echo will not be printed, but You must be root to install will be printed.
  • Some of the goals of Makefiles that everyone expects. You should always browse first, but it's reasonably intended to target (or just do), install, clean.
    • make all - Compile everything so you can test locally, previously installed things, all can be used together with rules, eg:all:hello install
    • make install - stuff that should be installed in the right place. But it looks like everything is installed in the right place for the system.
    • make clean - something that should be cleaned. Get rid of the executable file, any temporary files, object files, etc.

6. Implicit rules of Makefile

1. The command should be used in all cases where we build an executable x with the source code x.cpp as an implicit rule, which can be said:

.cpp:
        $(CC) $(CFLAGS) $@.cpp $(LDFLAGS) -o $@
  • This implicit rule says that if make c, xc runs the xc call and outputs x. The rules are implicit because no specific goal is mentioned. It can be used in all situations.

2. Another common implicit rule is the construction of .o (object) files and .cpp (source files).

.o:.cpp
        $(CC) $(CFLAGS) -c $<

alternatively

.o:.cpp
        $(CC) $(CFLAGS) -c $*.cpp

3. $^ indicates all dependencies

#LDFLAGS 表示依赖库
LDFLAGS = -lstdc++ 
hello:main.o factorial.o hello.o
        $(CC) $^ $(LDFLAGS) -o $@
.o:.cpp functions.h
        $(CC) -c $<
clean:
        -rm *.o hello
install:hello
        @echo "You need build hello!"
        echo "no @ used"
all:hello install

7. Makefile custom suffix rules

For its part, make already knows that in order to create a .o file, it must use cc-c the corresponding c file. These rules built into MAKE can take advantage of this to shorten the Makefile. If a Makefile dependency line simply represents a .h file that the current target is dependent on, MAKE will know that the corresponding file has been specified. You don't even need the compiler include command.
This reduces our Makefile even more to the following:

OBJECTS = main.o hello.o factorial.o
hello: $(OBJECTS)
        cc $(OBJECTS) -o hello
hello.o: functions.h
main.o: functions.h 
factorial.o: functions.h 

1. .SUFFIXESAllow custom suffixes

Make uses a special target, hence the name, which .SUFFIXESallows you to define your own suffixes . For example, dependencies:

.SUFFIXES: .foo .bar
  • Tell make that these special suffixes will be used to make your own rules.

How to make make already know how to generate .o files from .c files. Similarly rules can be defined in the following way:

.foo:.bar
        tr '[A-Z][a-z]' '[N-Z][A-M][n-z][a-m]' < $< > $@
.c:.o
        $(CC) $(CFLAGS) -c $<
  • The first rule allows you to create a .bar file from a .foo file. (don't worry about what it does, it basically scrambles the file)
  • The second rules .c file creates a .o file using the default rules.

2, *is a wildcard

*Used to represent arbitrary words, which can greatly simplify the makefile:

LDFLAGS = -lstdc++

all:hello install

hello:*.cpp *.h
        $(CC) $^ $(LDFLAGS) -o $@

clean:
        -rm *.o hello

install:hello
        @echo "You need build hello!"
        echo "no @ used"

8. Makefile instructions

There are several instructions in different forms. The program may not support all commands. So please check if make supports the directive, here we explore some of the directives supported by GNU make

1. Conditional instructions

Conditional instructions:

  • The condition for starting the ifeq instruction, the specified condition. It contains two parameters separated by commas and enclosed in parentheses. Variable substitution is performed on the two parameters, and then they are compared. The line in the makefile following IFEQ is obeyed if the two parameters match, otherwise it is ignored. Are they equal?
  • The condition for starting the ifneq instruction, the specified condition. It contains two parameters separated by commas and enclosed in parentheses. Variable substitution is performed on the two parameters, and then they are compared. makefile ifneq respects if the two arguments do not match, otherwise they are ignored. Doesn't wait
  • The condition for starting the ifdef directive, the specified condition. It contains a single parameter. The condition is true if the given arguments are true. Is it defined?
  • The condition for starting the ifndef directive, the specified condition. It contains a single parameter. If the given is false, then the condition is true. Is it undefined?
  • The else directive will result in the following line if the previous condition is not respected. In the example above, this means that the second option to connect to the command is not used when the first option is used. It is optional and has an else if conditional.
  • The end condition of the endif directive. Every condition must end with endif.

1. Syntax of conditional instructions

A simple conditional with
no other syntax is as follows:

conditional-directive
text-if-true
[else
text-if-false]
endif
  • text if true can be any line of text that is considered part of the makefile if the condition is true. If the condition is false, no text is substituted.
  • Square brackets []indicate that the corresponding content is ignored

The syntax for a complex condition
is as follows:

conditional-directive
text-if-true
else
text-if-false
endif
  • If the condition is true, text if true is used, otherwise if false text is used instead. The text, if the number of errors, can be any line of text.

The syntax of conditional directives is the same whether the condition is simple or complex. There are four different instructions for testing different conditions .
Here is a table:

ifeq (arg1, arg2)
ifeq 'arg1' 'arg2'
ifeq "arg1" "arg2"
ifeq "arg1" 'arg2'
ifeq 'arg1' "arg2" 

The instructions opposite to the above conditions are as follows:

ifneq (arg1, arg2)
ifneq 'arg1' 'arg2'
ifneq "arg1" "arg2"
ifneq "arg1" 'arg2'
ifneq 'arg1' "arg2" 

2. Example of conditional instructions

Conditional instructions, cross-platform compilation.

libs_for_gcc = -lgnu
normal_libs =

foo: $(objects)
ifeq ($(CC),gcc)
        $(CC) -o foo $(objects) $(libs_for_gcc)
else
        $(CC) -o foo $(objects) $(normal_libs)
endif

2. include directive

The include directive tells make to pause reading the current makefile and read one or more other makefiles before continuing. The directive is a single line in the makefile and looks like this:

include filenames...

Filenames can contain shell filename patterns. Extra spaces are allowed at the beginning of the line to be ignored, but a label is not allowed. For example, if there are three .mk'文件, a.mk', b.mk', and c.mk', and a $(bar) expanded in bash, then the following expression.

include foo *.mk $(bar)

is equivalent to

include foo a.mk b.mk c.mk bash

When MAKE processes the include directive, it pauses reading of the containing makefile and reads from each listed file in turn. When this process is complete, make the read directive appear in the resume of the makefile.

3. override instruction

If a variable is already set as a command argument, it is ignored in the makefile for ordinary tasks. If you want to set a makefile variable even if it is set as a command argument, you can use an override directive, which is a line that looks like this:

override variable = value

or

override variable := value

4,=、+=、:=、?=

= Direct assignment
:= Ignore the previous assignment
+= Append
?= If there is no previous definition, the definition will take effect

9. Recompile the Makefile

The make program is an intelligent utility and works based on changes in source files. If there are four files main.cpp, hello.cpp, factorial.cpp and functions.h. All reamining files here depend on functions.h, and main.cpp depends on hello.cpp and factorical.cpp. So if you make any changes to functions.h then all source files will be recompiled to generate new object files. But if any changes are made to main.cpp, since this is not dependent on any other filtering, then in this case only main.cpp file will be recompiled and hellp.cpp factorial.cpp will not be recompiled.

1. Conditions for recompiling

While compiling a file, MAKE checks the target file and compares the time stamps, if the source file has an update timestamp that is newer than the target file, then a new object file will be generated, assuming the source file has been changed.

2. Avoid recompiling

It is possible that the project includes thousands of files. Sometimes you may have changed a source file but don't want to recompile all the files that depend on it. For example, suppose you add a macro to a header file or declaration that many other files depend on. It is assumed that any changes in the header files will require recompiling all related files, but be aware that they do not need to be recompiled and it is better not to waste time waiting for them to be compiled.

  • -tFlags can be used if changes to header files are expected before problems occur . This flag tells the make command not to run the rule, but to mark the target so far by changing its last modified date. Follow these steps:

    • 1. Use the command 'make' to recompile if you really need to recompile the source files.
    • 2. Make changes in the header file.
    • 3. Use the command -tto record all target files as up-to-date. The next time you run make, changes in the header files will not cause any recompilation.
  • If the header files have been changed, some files will need to be recompiled, and it will be too late to do this. Instead, you can use -ofile flags, which mark a specified file as old. This means that the file itself will not be reproduced and nothing else its instructions will be reproduced. Follow these steps:

    • 1. Recompiling source files requires the preparation of independent specific header files make -o headerfile. If several header files are involved, use a separate -ooption for each header file.
    • 2. Touch all target files make -tto avoid recompiling.

3,touch

Touch updates the library to the latest time to avoid the problem of projects not being updated due to too short time intervals.
for example:

touch  xxx.cpp

10. Other functions of Makefile

1. Use make recursively

Recursive use means using make as a command in a makefile. This technique is very useful when you want various subsystems of makefiles to compose a larger system. For example, let's say you have a subdirectory that has its own makefile, and you want to run make in the subdirectory's makefile. This can be done as follows:

#方式一:先切换目录再去make编译
subsystem:
        cd subdir && $(MAKE)

or, equivalently
#方式二:编译时去寻找切换目录 	
subsystem:
        $(MAKE) -C subdir

It is possible to write recursive copies of this example just through the make command, but there are a lot of things to understand about how and why they work, and how sub-top-level make is involved.

2. Communication variables to sub-make

The values ​​of top-level make variables can be passed to sub-environments, by explicit request. These variables are defined in the submake as default values, but will not override the ones specified in the submakefile unless the `-e' switch is used

Passing down, or exporting, a variable, the variable and its value are added to the environment in which each command is run. Submake In turn, make uses environment variable values ​​to initialize its tables

The special variables SHELL and MAKEFLAGS are always exported (unless exported is cancelled). MAKEFILES exports if you set it to anything.

If you want to export a sub-manufacturer of a specific variable, use the export directive, like this:

export variable ...

If you want to prevent a variable from being exported, use the undo export directive, like this:

unexport variable ...

3. MAKEFILES variable

MAKEFILES, environment predefined macro, if an environment variable is defined, make a list of additional makefile names (separated by spaces) before others consider their values ​​to be read. This is much like the include directive: it looks for these files in different directories.
The main purpose of MAKEFILES is to communicate between MAKE recursive calls without reporting errors, which easily complicates the project.但没有必要,最好不用定义和使用该变量

LDFLAGS=-lstdc++
all:hello install
	@echo "MAKEFILES=$(MAKEFFILES)"

4. The header files are included in different directories

If you have put your header files in a different directory and run make in a different directory, then it needs to tell the path to the header files. This can be done using -Ioptions in the makefile. Assuming that the functions.h file is available in /home/yidaoyun/header and other files in /home/yidaoyun/src/ then the files will be written as follows.

INCLUDES = -I "/home/yidaoyun/header"
CC = gcc
LIBS =  -lm
CFLAGS = -g -Wall
OBJ =  main.o factorial.o hello.o

hello: ${
    
    OBJ}
   ${
    
    CC} ${
    
    CFLAGS} ${
    
    INCLUDES} -o $@ ${
    
    OBJS} ${
    
    LIBS}
.o:.cpp
   ${
    
    CC} ${
    
    CFLAGS} ${
    
    INCLUDES} -c $<

5. Append more text variables

Typically, it is used to add more text to the value of an already defined variable.
The make line contains +=, like this:

objects += another.o

This takes the value of the variable object and adds the text another.o(preceded by a single space). therefore:

objects = main.o hello.o factorial.o
objects += another.o

set 文件main.o hello.o factorial.o another.oobject.
Usage +=is similar to:

objects = main.o hello.o factorial.o
objects := $(objects) another.o

6. Line continuation in Makefile

If you don't like too long lines in the Makefile, then you can use backslashes \, 但反斜杠后面不能有任何字符(空格,tab建,字母,下划线等)as shown below

OBJ =  main.o factorial.o \
	hello.o

is equivalent to

OBJ =  main.o factorial.o hello.o

7. Run the Makefile from the command prompt

If you have prepared a Makefile with the name "Makefile" then simply write in the command prompt it will run the Makefile. But if there is any other name for the Makefile then use the following command

make -f your-makefile-name

11. Makefile case

This is an example of compiling the hello program Makefile. This program contains three files main.cpp, factorial.cpp, hello.cpp.

# Define required macros here
SHELL = /bin/sh

OBJS =  main.o factorial.o hello.o
#CFLAG编译参数,-Wall:开启所有警告,-g:开启调试信息
CFLAG = -Wall -g
CC = gcc
#INCLUDES:依赖文件,没有就空,-I:当前文件
INCLUDES = -I .
#LIBS 依赖库
LIBS = -lstdc++

hello:${
    
    OBJS}
	${
    
    CC} ${
    
    CFLAGS} -o $@ $^ ${
    
    LIBS}

clean:
	-rm -f *.o hello

.o:.cpp
	${
    
    CC} ${
    
    CFLAGS} ${
    
    INCLUDES} -c $<
  • To build the hello program use the "make" hello project. If you issue the command "make clean", then it will delete all objects available in the makefile in the current directory.

Summarize

1. Shortcut keys in vim command mode:
u: Undo all input before the last save.
control+r: Restore all input before the last save.
Press d twice: delete a line.

Guess you like

Origin blog.csdn.net/MOON_YZM/article/details/131098875