Makefile Engineering Management

This article comprehensively and deeply explains the make project management tool, which lays a foundation for subsequent reading of complex source codes such as U-Boot and kernel and project development

The principle of Make and the basic knowledge of Makefile

Introduction to Make

Project manager, as its name suggests, refers to managing more files

Make project manager is also an "automatic compilation manager". Here, "automatic" means that it can automatically find updated files according to the file timestamp to reduce the workload of compilation. At the same time, it performs a lot of compilation by reading the contents of Makefile files

Make will compile only the changed code files, not the full compilation.

Makefile basic structure

Makefile is the only configuration file read in by Make

  • The target created by the make tool is usually a target file or an executable file
  • dependency_file of the target body to be created
  • Commands to run when creating each target body
  • Note: the command line must be preceded by a TAB key, otherwise the compilation error is: * * * missing separator Stop.

Makefile format

target: dependency_files
<TAB>  command

example

hello.o: hello.c hello.h
gcc –c hello.c –o hello.o

Makefile variable

A more complex example:

  • sunq: kang.o yul.o
        gcc kang.o yul.o -o sunq
    kang.o: kang.c kang.h 
        gcc –Wall –O -g –c kang.c -o kang.o
    yul.o: yul.c 
        gcc - Wall –O -g –c yul.c -o yul.o
    

    notes:

    • -Wall allows you to send gcc all useful alarm messages
    • -c just compiles without linking and generates the target file o
    • -Output o to file

    More about using man tools

Create and use variables

Purpose of creating variable: to replace a text string:

  • Name of the series file
  • Parameters passed to the compiler
  • Program to run
  • Need to find the directory of source code
  • The directory where you need to output information
  • Other things you want to do

Two ways to define variables

  • Recursive expansion method VAR=var
  • Simple method VAR:=var
    • Use $(VAR) for variables
    • Use $$, then use $$
    • Similar to macros in programming languages

The example just now

OBJS = kang.o yul.o
CC = gcc
CFLAGS = -Wall -O -g
sunq : $(OBJS)
	$(CC) $(OBJS) -o sunq
kang.o : kang.c kang.h
	$(CC) $(CFLAGS) -c kang.c -o kang.o
yul.o : yul.c yul.h
	$(CC) $(CFLAGS) -c yul.c -o yul.o

Recursive expansion method VAR=var; example:

foo = $(bar) 
bar = $(ugh) 
ugh = Huh? 

The value of $(foo) is?

echo $(foo) to view

  • Advantage: it can refer back to variables
  • Disadvantages: this variable cannot be extended in any way, for example:
    CFLAGS = $(CFLAGS) -O
    It will cause a dead cycle

Simple method VAR:=var

m := mm 
x := $(m) 
y := $(x) bar 
x := later 
echo $(x) $(y) # See what information is printed?
  • Variables defined in this way will be expanded according to the current value of the referenced variable at the definition point of the variable
  • This way of defining variables is more suitable for large programming projects because it is more like our general programming language

Use= Define variables

dir := /foo/bar
FOO ?= bar # FOO is?
  • It means that if FOO has not been defined, the value of variable FOO is bar. If FOO has been defined previously, this phrase will do nothing, which is equivalent to:

    ifeq ($(origin FOO), undefined)
    FOO = bar
    endif
    

Add values to variables; You can add a new value to a defined variable with + =

Main=hello.o hello-1.o
Main+=hello-2.o

Predefined variables

  • The name of the AR library file maintenance program. The default value is ar.
  • The name of the as assembler. The default value is as.
  • cc is the name of the C compiler. The default value is cc.
  • The name of CPP C precompiler. The default value is $(CC) – E.
  • The name of CXX C + + compiler. The default value is g + +.
  • The name of the FC FORTRAN compiler. The default value is f77
  • The name of the RM file deletion program. The default value is rm -f

example:

Hello: main.c main.h 
<tab> $(CC) –o hello main.c
clean:
<tab> $(RM) hello

Predefined variables

  • There is no default value for the option of ARFLAGS library file maintenance program.
  • ASFLAGS assembler option, no default value.
  • CFLAGS C compiler options, no default value.
  • CPPFLAGS C precompiled option, no default value.
  • Cxxflags is an option of the C + + compiler. There is no default value.
  • Options of FFLAGS FORTRAN compiler, no default value.

The example just now

  • OBJS = kang.o yul.o
    CC = gcc	
    CFLAGS = -Wall -O -g
    sunq : $(OBJS)
    	$(CC) $(OBJS) -o sunq
    kang.o : kang.c kang.h
    	$(CC) $(CFLAGS) -c kang.c -o kang.o
    yul.o : yul.c yul.h
    	$(CC) $(CFLAGS) -c yul.c -o yul.o
    

Automatic variable

  • $* target file name without extension
  • $+ all dependent files, separated by spaces and in the order of occurrence, may contain duplicate dependent files
  • $< name of the first dependent file
  • $? All dependent files whose timestamp is later than the target file are separated by spaces
  • Full name of $@ target file
  • $^ all non duplicate target dependent files, separated by spaces
  • $% if the target is an archive member, this variable represents the archive member name of the target

Just now:

OBJS = kang.o yul.o
CC = gcc
CFLAGS = -Wall -O -g
sunq : $(OBJS)
	$(CC) $^ -o $@
kang.o : kang.c kang.h 
	$(CC) $(CFLAGS) -c $< -o $@
yul.o : yul.c yul.h
	$(CC) $(CFLAGS) -c $< -o $@

environment variable

  • make will automatically read the environment variables currently defined by the system when starting, and will create variables with the same name and value
  • If the user defines a variable with the same name in Makefile, the user-defined variable will override the environment variable with the same name

Options of Make command and implicit rules in Makefile

Make use

Run make directly

option

  • -C dir reads the Makefile in the specified directory
  • -f file read in the file file in the current directory as a Makefile
    make -f Makefile.debug
    make -f Makefile.debug clean
  • -i ignore all command execution errors
  • -I dir specifies the directory of the included Makefile
  • -n print only the commands to be executed, but do not execute them
  • -p displays the make variable database and implicit rules
  • -s does not display commands when executing commands
  • -If make changes the name of the current directory during the printing process
  • -j \nproc ` ` compile according to the number of computer cores

Implied rules of Makefile

Implicit rule 1: implicit rules for compiling C programs

  • <n>. O's goal dependency goal is automatically derived as < n > C and its generation command is $(CC) -c $(CPPFLAGS) $(CFLAGS)

Implicit rule 2: implicit rules for linking Object files

  • <n> The target depends on < n > o. Run the linker generation (generally ld) by running the C compiler. The generation command is $(CC) (ldflags) < n > o

  • $(LOADLIBES) $(LDLIBS). This rule is valid for projects with only one source file and for multiple Object files (generated from different source files). For example:

    • Rules:
      x : x.o y.o z.o

    • When x.c, y.c and z.c exist, the implicit rule will execute the following commands:

      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
      
    • If no source file (such as x.c in the above example) is associated with your target name (such as x in the above example), you'd better write your own generation rules, otherwise, the implicit rules will report an error

Example 1:

  • $ ls -R
    Makefile	clean		f2.c		include
    Makefile2	f1.c		head.h		main.c
    
    ./include:
    myinclude.h
    
  • CFLAGS=-c -Wall -I include
    f1:f1.o f2.o main.o	
    
    .PHONY:clean
    clean:
    	rm *.o f1
    

Example 2:

  • ls -R
    Makefile	clean		f1.c		head.h		main.c
    Makefile2	config.mk	 f2.c		include
    
    ./include:
    myinclude.h
    
    OBJS=f1.o f2.o
    OBJS+=main.o
    CFLAGS=-c -Wall -I include
    ~           
    ~           
    ~ 
    ~ 
    ~ 
    "config.mk" 3L, 55C
    
    
  • #OBJS=f1.o f2.o
    #OBJS+=main.o
    #CFLAGS=-c -Wall -I include
    include config.mk
    test:$(OBJS)
    	gcc $(OBJS) -o test
    #f1.o:f1.c
    #f2.o:f2.c
    #main.o:main.c
    .PHONY:clean
    clean:
    	rm *.o test
    
    

VPATH and nested Makefile

Usage of VPATH

VPATH: virtual path

  • In some large projects, there are a large number of source files. Our usual practice is to classify these source files and store them in different directories. Therefore, when make needs to find the dependency of the file, you can add a path in front of the file, but the best way is to tell make a path and let make find it automatically.
  • The special variable VPTH in the Makefile file is written into this function. If this variable is not specified, make will only find the dependent files and target files in the current directory. If this variable is defined, make will look for files in the specified directory when the current directory cannot be found.
  • VPATH = src:../headers
  • The above definition specifies two directories, src and/ headers and make will search in this order. Directories are separated by colons. (of course, when the current directory is always the highest priority search place)

Example 3:

  • $ ls -R
    Makefile	Makefile2	main		src2
    Makefile1	include		src1
    
    ./include:
    head.h		myinclude.h
    
    ./main:
    main.c
    
    ./src1:
    f1.c
    
    ./src2:
    f2.c
    
  • FLAGS=-c -Wall -I include
    VPATH=src1 src2 main
    f1:f1.o f2.o main.o
    
    .PHONY:clean
    clean:
            find ./ -name "*.o" -exec rm {} \;;rm f1
    

Nested Makefile

Case:

ls -R
Makefile	f2		main
f1		include		obj

./f1:
Makefile	f1.c

./f2:
Makefile	f2.c

./include:
myinclude.h

./main:
Makefile	main.c

./obj:
Makefile
CC=gcc
SUBDIRS=f1 \
		f2 \
		main \
		obj
OBJS=f1.o f2.o main.o
BIN=myapp
OBJS_DIR=obj
BIN_DIR=bin
export CC OBJS BIN OBJS_DIR BIN_DIR

all:CHECK_DIR $(SUBDIRS)
CHECK_DIR:
	mkdir -p $(BIN_DIR)
$(SUBDIRS):ECHO
	make -C $@
ECHO:
	@echo $(SUBDIRS)
	@echo begin compile
CLEAN:
	@$(RM) $(OBJS_DIR)/*.o
	@rm -rf $(BIN_DIR)
  • We noticed a sentence @ echo $(SUBDIRS)
  • @(RM) is not a variable defined by ourselves. Where does it come from?
  • make -C $@
  • export CC OBJS BIN OBJS_DIR BIN_DIR

Keywords: Makefile

Added by Tokunbo on Sat, 05 Mar 2022 02:54:44 +0200