make tool and makefile file foundation of Linux tools

Why use the make tool and makefile file

  it is very simple for us to compile a helloWorld program with gcc command, because it has only one file. However, when multiple files are compiled into an executable file, the linker is required to link the target file. However, when our project is very large and there are many source files designed, the compilation is not only cumbersome, but also leads to repeated compilation of unchanged files, wasting a lot of time.
   the make auxiliary compilation tool will compare the time stamps of all files before compilation. It will selectively compile the modified files instead of compiling them all, which greatly saves the time of compilation. For the target file of the file that has not been modified, because the linker works to link the target file together finally, it will not be affected by other files.

How to use the make tool

  call the make command to call the make tool

make
	#After calling this command, an error will be reported
	#make: *** No targets specified and no makefile found.  Stop.
	#The reason is that we do not specify how the make tool works, and the guidance is implemented through the makefile file

What is a makefile

  makefile is a file that describes the compilation, linking and other rules of the whole project. The name of makefile file must be makefile or makefile.

touch makefile
make
	#Error: Make: * * * no targets. Stop
	#Reason: Although the makefile file is found, it is empty and does not contain any rules.

makefile syntax

Syntax format, note that all line leading spaces are used tab Generated! Cannot use spaces:
	target:rely on
		command

give an example:

all:
	gcc hello.c -o hello

Target: all
Dependency: empty
Command: gcc hello.c -o hello
The above example can also be written as:

all:hello.o
	gcc hello.o -o hello
hello.o:hello.c
	gcc -c hello.c

Target: all and hello.o
Dependencies: hello.o and hello.c
Commands: gcc hello.c -o hello.o and gcc -c hello.c
    because all depends on the hello.o file, you need to execute gcc hello.c -o hello.o to get the hello.o file before you can execute gcc hello.c -o hello. Therefore, the execution order after entering the make command is the same as the above dependencies, rather than the top-down order in the makefile file.

Execute the make command using the makefile file

#Suppose that the makefile file has been written, as in the second example above
make all #Set the compilation target all, or you can obtain the first compilation target by default without specifying the obtained compilation target

    we will find that after executing the above Makefile, we will get the intermediate files of. o. of course, we can delete them manually (rm command), but it is very troublesome to operate every time. We can add the following commands to the makefile file:

clean:
	rm -rf *.o

  then execute the command make clean to quickly delete the. o file. Wildcards are used here in combination with the rm command to delete the matching files.

Pseudo target of makefile

  consider the following questions: if there is also a clean file in the current directory, what happens when you execute make clean?

    we can find that when there is a file with the same name as the makefile target name in the current path, the make command will report an error. In addition to deleting the relevant files before calling the make command, what other methods are available? This will use the pseudo target of makefile.
   pseudo target mode:. PHONY: target name. Adding this statement on the line above the target name of the file with the same name can avoid errors in the file with the same name.

-PHONY:clean
clean:
	rm -rf *.o

Makefile variable and variable assignment

  • Variables can be used in many places, such as targets, dependencies, or commands
  • Variables can be copied using: =,? =, + =,:, =.
  • Use of variables: use variables through $(variable name)

Example 1:

Use: = to assign values immediately:

var:=aaa
var:=$(var1)bbb
var1:=ccc
all:
	echo $(var2)

   use: = to assign a value to the variable immediately. The variable value has been determined while executing var:=aaa, so the final print should be aaabbb instead of cccbbb.

Example 2:

Use = to delay assignment:

var=aaa
var=$(var1)bbb
var1=ccc
all:
	echo $(var2)

    using = to assign a value to a variable is a delayed assignment. If it is used to assign a value, the final value of the referenced variable will be referenced when referring to the variable, so it should be cccbbb instead of aaabbb.

Example 3

Use= Default assignment

var:=aaa
var?=ccc
var:=$(var1)bbb
all:
	echo &(var2)

Example 4

Use + = to append assignments

var:=aaa
var=$(var1)bbb
var1+=ccc
var1+=ddd
all:
	echo $(var2)

  use= To assign a value to a variable is the default assignment, which means that if the variable has not been assigned before, it will default to the assigned value. Therefore, aaabbb will be printed instead of cccbbb.
   here, it is equivalent to direct: = assignment (the \ symbol is used here for multi line writing):

var:=aaa\
		ccc\
		bbb

Automation variables for makefile

  variables that are not defined and change with the context.

  • %@: indicates all targets
  • $<: represents the first dependent file. If the dependency mode is%, it represents a series of files. (% is a wildcard, similar to *) on Linux)
  • $^ indicates all dependencies.

Problem introduction

  we experience the following concepts through the following procedures:
Here are the following documents

//The hello.c file is as follows
#include<stdio.h>
#include"hello.h"
void hello()
{
	printf("hello world!\n);
}
//The hello.h file is as follows
#ifndef _HELLO_
#define _HELLO_
void hello(void);
#endif
//The main.c file is as follows
#include<stdio.h>
#include"hello.h"
void main(void)
{
	hello();
}

The makefile file is as follows

hello:hello.o main.o
	gcc hello.o main.o -o hello
hello.o:hello.c
	gcc -c hello.c -o hello.o
main.o:main.c
	gcc -c main.c -o main.o

clean:
	rm -rf *.o hello

    we can compile the executable file through the make command, but if we want to add some additional include files, we need to modify the dependency and the following commands in the makefile file, which is very troublesome when there are many files.

Use variables to optimize makefile s

Variable to replace the file name

  try the following methods:

var:= hello.o main.o
hello:$(var)
	gcc $(var) -o hello
hello.o:hello.c
	gcc -c hello.c -o hello.o
main.o:main.c
	gcc -c main.c -o main.o

  it can be found that if we need to increase the target file dependency in the future, we can directly modify the var variable instead of modifying the following statements twice, which reduces our workload.

Automation variables $< and $@ replace dependencies and targets with the same name

  observe the following makefile files:

var:= hello.o main.o
hello:$(var)
	gcc $(var) -o hello
%.o:%.c
	gcc -c $< -o $@

  it can be found that we use automation variables to integrate two groups of dependencies and goals with the same name into one place.

Automated variable $^ to replace all dependencies

  observe the following makefile files:

var:= hello.o main.o
hello:$(var)
	gcc $^ -o hello
%.o:%.c
	gcc -c $< -o $@

   there's a question: it's almost the same to use $(var) directly. Why do you want to introduce $^?

Function of makefile

wildcard function

Wildcard: meaning of wildcard
Format: $(wildcard string)
Function: expand the specified directory
give an example:
There is a C source file of a.c and a test folder in the directory of / home/test, and a b.c file in the folder of / home/test/test. We create the makefile file in the / home/test directory as follows:

var:=$(wildcard ./*.c ./test/*.c)
all:
	@echo $(var)

   after @ is added before the shell command here, the shell command after @ will not be displayed on the terminal by using the make command. The echo command will not be displayed here. We execute the makefile file and get the results:

It can be found that:

  1. The echo command is not displayed
  2. The wildcard path after the wildcard keyword is expanded into the matching c source file.

notdir function

Format: $(notdir path string)
Function: extract the file name in the path string
  assume that there is only the C source file of test.c in the current path

dir:=$(wildcard ./*.c)
var2:=$(notdir $(dir))
var:=$(notdir ./test.o)
chartest:
    echo $(dir)
    echo $(var)
    echo $(var2)

   executing the above file will output the following results:

It can be found that the var2 variable extracts the wildcard. The file name part of the result. / test.c is test.c.

dir function

Format: $(dir file name string)$
Function: extract the path name in the file name string (that is, all characters before the last / last)

dir:=$(wildcard ./*.c)
#var2:=$(notdir $(dir))
var:=$(notdir ./test.o)
var3:=$(dir $(dir))
chartest:
    echo $(dir)
    echo $(var)
    #echo $(var2)
    echo $(var3)

   executing the above file will output the following results:

It can be found that:

  1. #The makefile statement after is not executed, but actually # is the comment symbol of the makefile
  2. The # before the echo statement doesn't work, so you can know that the indented statement will be treated as a shell statement instead of a makefile statement.
  3. The var3 variable extracts the path part. / in the wildcard result. / test.c.

patsubst function

Format: $(patsubst), source character, target character, character list
Function: replace the source character to the target character in the character list

var2:=$(notdir $(dir))
var:=$(notdir ./test.o)
#var3:=$(dir $(dir))
var4:=$(patsubst %.c,%.o,$(var2))
chartest:
    echo $(dir)
    echo $(var)
    echo $(var2)
    @echo $(var3)
    echo $(var4)

   executing the above file will output the following results:

It can be found that the. c part of var2 is replaced with. o and stored in var4 (var2 has not been changed).

Replace characters with $(var: source string = destination string)

Format: $(var:a=b)
Function: replace the source string in the string with the target string

dir:=$(wildcard ./*.c)
var2:=$(notdir $(dir))
var:=$(notdir ./test.o)
#var3:=$(dir $(dir))
#var4:=$(patsubst %.c,%.o,$(var2))
var5:=$(dir:%.c=%.o)
chartest:
    echo $(dir)
    echo $(var)
    echo $(var2)
    @echo $(var3)
    @echo $(var4)
    echo $(var5)

   executing the above file will output the following results:

It can be found that. c in the variable dir is replaced by. o

foreach function

Format: $(foreach < var >, < list >, < text >)
Function: take out the words in the parameter < list > one by one, put them into the variable specified by the parameter < var >, and then execute the expression contained in < text >. Each time < text > will return a string.

dir:=$(wildcard ./*.c)
#var2:=$(notdir $(dir))
var:=$(notdir ./test.o)
var3:=$(dir $(dir))
#var4:=$(patsubst %.c,%.o,$(var2))
#var5:=$(dir:%.c=%.o)
var6:=$(foreach n,$(var3),$(wildcard $(n)*.c))
chartest:
    echo $(dir)
    echo $(var)
    @echo $(var2)
    echo $(var3)
    @echo $(var4)
    @echo $(var5)
    echo $(var6)

   executing the above file will output the following results:

It can be found that:

  1. foreach takes a value from var3, puts it into variable n, and executes the statement $(wildcard $(n)*.c). Then it takes out the next value and executes the corresponding statement until the values in var3 are exhausted.
  2. It can be found that the dir function will not remove the duplicate path. For example,. / test2. c. / test. c becomes. /. / after being processed by the dir function, so it will be executed twice in var6. Each time, two test2.c test.c file names will be matched because of the wildcard *. c. Therefore, a total of two groups of duplicate file names are entered.

makefile related information manual

    for space reasons, this article only lists some basic usage. Readers can find some relevant information manuals for further study on this basis. Here is a recommended book "write makefile with me", which is very good.

Sets the tab indent width of the vim editor

#Open the vim configuration file first
vi /etc/vim/vimrc	#The etc folder is used to store configuration files. The files at the end of rc are generally configuration files

Then add a statement on the last line (the configuration principle here is similar to the. bashrc file principle of PATH):

set tabstop=4

   here we set the indent width of tab to 4. Similarly, we can add the statement set number to set the line number of each line when it is opened. set shiftwidth=4 means that the automatic indent length of line feed is set to 4 spaces.

Tags: Linux Makefile Ubuntu

Posted on Wed, 01 Dec 2021 01:05:51 -0500 by waseembari1985