Clickthe blue text
Follow us
In the previous article, we discussed some concepts and principles of Makefile. Next, let’s talk about some key points regarding Makefile.
make and make clean
Rules for generating target files (make command):
Executing the make command will generate the corresponding target files based on the rules defined in the Makefile in the current directory.
If the Makefile has a different name, such as makefile.linux, you need to use the make parameters (-f or –file) to execute the corresponding Makefile, for example:
make -f makefile.linux
Rules for cleaning target files (make clean command):
Every Makefile should include a rule to clean target files (such as .o
and other target files), which not only facilitates recompilation but also helps keep the files clean. It is advisable to develop the habit of writing such a rule when creating Makefiles. A common style is:
clean: rm $(obj) *.o
A more robust approach is (reason: if a clean file exists in the current directory, this command will fail), the solution is to add a phony target: .PHONY:clean:
.PHONY:clean
clean: rm $(obj) *.o
Note:
-
<span>clean</span>
rules should not be placed at the beginning of the file; otherwise, it will become the default target for make. Generally, clean is always placed at the end of the file.
Writing Rules
Display rules (@ character):
When using the @ character before a command, that command will not be displayed when executed. For comparison: Example of a command with @ character:
rice@rice:~/rice_file/mkfile$ cat Makefile
exec: @echo "rice makefile"
rice@rice:~/rice_file/mkfile$ make
rice makefile
Example of a command without @ character:
rice@rice:~/rice_file/mkfile$ cat Makefile
exec: echo "rice makefile"
rice@rice:~/rice_file/mkfile$ make
echo "rice makefile"
rice makefile
Note:
The make command parameters <span>-s</span>
or <span>--silent</span>
or <span>--quiet</span>
will completely suppress command display.
Command execution rules:
When a dependent target is newer than the target, make will execute the subsequent commands one by one. To apply the result of the previous command to the next command, use semicolon separation between the two commands, and do not write both commands on the same line.
Example 1:
Makefile:
exec: @cd /home/rice @pwd
Result:
/home/rice/rice_file/mkfile
Example 2:
Makefile:
exec: @cd /home/rice;pwd
Result:
/home/rice
From the above two examples, we can demonstrate the rules of command dependencies.
Command error rules (- symbol):
When a command finishes running, make checks the return code of each command. If the return is successful, make will execute the next command. When all commands return successfully, make completes execution. If a command in a rule fails (non-zero exit code), make will terminate the execution of the current rule, which may terminate the execution of all rules.
Sometimes, a command error does not indicate a failure. For example, the mkdir command creates a directory; if the directory does not exist, mkdir will not produce an error. If the directory already exists, an error will occur. From the example, the error from mkdir does not affect other commands because I only need the directory to exist, so the error from mkdir should not terminate the command rule’s execution.
To solve the above problem, simply add a symbol – before the command in the Makefile, so that even if the command fails, subsequent commands will still be executed.
.PHONY:clean
clean: -rm $(obj) *.o
Variables
Variable Definition
Makefile also supports variable definitions, which simplify and make our Makefile more reusable. Variable definitions are generally in uppercase letters, and the assignment method is similar to that in C language.
DIR = ./src/
To use the variable, enclose it in parentheses and add a dollar sign.
FOO = $(DIR)
Makefile supports other assignment methods besides using ‘=’ such as ‘:=’ and ‘?=’. Next, let’s compare the differences between these methods:
Assignment symbol ‘=’:
PARA = RICE
CURPARA = $(PARA)
PARA = rice
print: @echo $(CURPARA)
Result:
rice
The value of variable CURPARA is not “RICE. Its value is the last assigned value of PARA. This shows that the assignment symbol “= can leverage another variable, allowing the real value of the variable to be defined later. In other words, the real value of the variable depends on the last effective value of the variable it references.
Assignment symbol ‘:=:
PARA = RICE
CURPARA := $(PARA)
PARA = rice
print: @echo $(CURPARA)
Result:
RICE
The value of variable CURPARA is “RICE. The difference between “= and “:= is that “:= only takes the first assigned value.
Assignment symbol ‘?=:
PARA = RICE
PARA ?= rice
print: @echo $(PARA)
Result:
RICE
If the first line is removed, the result will be:
rice
This indicates that if the variable PARA has not been assigned a value before, then this variable will be “rice. If it has been assigned a value before, it will use the previously assigned value.
Assignment symbol ‘+=:In Makefile, variables are strings, and sometimes we need to add some strings to an already defined variable. In this case, we use the symbol “+=:
OBJ = main1.o main2.o
OBJ += main3.o
OBJ will have the value: “main1.o, main2.o, main3.o”. This shows that “+= is used for appending to variables.
Built-in Variables:
Some built-in variables are predefined, usually including CC, PWD, CLFAG, etc. Some have default values, while others do not. For example:
-
CPPFLAGS: Options needed by the preprocessor, such as:-l
-
CFLAGS: Parameters used during compilation-Wall -g -c
-
LDFLAGS: Options used for linking libraries-L -l
Among them, default values can be modified. For example, CC has a default value of cc, but can be changed to gcc: CC=gcc
Automatic Variables:
Makefile syntax provides some automatic variables that allow us to complete the writing of Makefile more quickly. These automatic variables can only be used in the commands of rules. Common automatic variables include:
-
$@: The target in the rule
-
$: The first dependency file in the rule
-
$^: All dependency files in the rule
CC = gcc
OBJ = main.o add.o
output: $(OBJ) $(CC) -o $@ $^
main.o: main.c $(CC) -c $<
add.o: add.c $(CC) -c $<
.PHONY:clean
clean: @rm $(OBJ) output
Pattern Rules
Pattern rules use % in the target and dependencies to match corresponding files. We can still use the previous example in pattern rule format as follows:
CC = gcc
OBJ = main.o add.o
output: $(OBJ) $(CC) -o $@ $^
%.o: %.c $(CC) -c $<
.PHONY:clean
clean: @rm $(OBJ) output
Where:
main.o is generated from main.c and add.o is generated from add.c.Functions
Makefile provides a variety of functions, among which the two most commonly used are (wildcard, patsubst).wildcard function: Used to find files of a specified type in a specified directory. Function parameters: directory + file type, usage:
SRC = $(wildcard ./src/*.c)
print: @echo $(SRC)
Result:
./src/add.c ./src/main.c
Indicates: Find all files with the suffix.c in the directory ./src and assign them to the variable SRC. After the command is executed, the value of the SRC variable is: ./src/add.c ./src/main.c
patsubst function: Used for matching and replacing. Function parameters: original pattern + target pattern + file list, usage::
SRC = $(wildcard ./src/*.c)
OBJ = $(patsubst %.c, %.o, $(SRC))
print: @echo $(OBJ)
Result:
./src/add.o ./src/main.o
Indicates: Replace all files with the suffix.c in the variable with.o. After the command is executed, the value of the OBJ variable is: ./src/add.o ./src/main.o
I will continue to update articles and learning materials.
You can add the author’s WeChat to exchange and learn together.
—Author’s WeChat ID: wueroo1314—
To join the discussion group, please add the author’s WeChat
Fan DIY
WeChat ID: Rice_DIY
Technology | Open Source | Sharing
Long press to follow…