Dependencies
The Makefile uses dependencies to automatically determine whether a file needs to be recompiled. For example, <span>main.o: main.c</span>, when <span>main.c</span> changes, <span>main.o</span> will be recompiled, and then relinked to generate the target file <span>app</span>.
main.o: main.c $(CC) $(CFLAGS) -c main.c -o main.o
app: main.o $(CC) $(CFLAGS) main.o -o app
Pattern Rules
Using the wildcard <span>%</span> represents a class of files, such as <span>%.o: %.c</span> which indicates that all <span>.c</span> files are compiled into their corresponding <span>.o</span> files, simplifying the writing of Makefiles and avoiding duplicate rules.
%.o represents all files ending with .o, representing the first file in the dependency list, < represents the first file in the dependency list, and @ represents the target file.
%.o: %.c $(CC) $(CFLAGS) -c $< -o $@
app: main.o func.o $(CC) $(CFLAGS) main.o func.o -o app
Built-in Functions
The Makefile provides various built-in functions, such as <span>wildcard</span> for matching files, <span>patsubst</span> for pattern replacement, and <span>subst</span> for string replacement, which help in handling filenames, generating commands, etc.
(objects) represents all filenames in the variable objects, rm−f(objects) app means deleting all .o files and the app file.
objects = main.o func.o
app: $(objects) $(CC) $(CFLAGS) $(objects) -o app
clean: rm -f $(objects) app
Implicit Rules
The Makefile has built-in implicit rules that automatically select the compiler and commands based on file extensions. For example, <span>.c</span> files will be compiled into <span>.o</span> files using <span>cc</span>, reducing the need for explicit command writing.
$^ represents all filenames in the dependency list. The Makefile will automatically select the correct compiler based on the file extension and generate the corresponding target file.
app: main.c func.c $(CC) $(CFLAGS) $^ -o $@
Phony Targets
When using RM, if an error occurs with make clean: make: ‘clean’ is up to date, you need to add a phony directive: .PHONY: clean or .PHONY: $(C_CLEAN), where C_CLEAN is a variable.
Variables
The Makefile supports the definition and referencing of variables. Variables can help simplify the writing of Makefiles. The format for defining a variable is: VARNAME = value. For example:
CC = gcc
CFLAGS = -Wall -O2
The format for referencing a variable is: $(VARNAME). For example:
$(CC) $(CFLAGS) -c main.c -o main.o
Custom Variables
Numbers, letters, underscores, cannot start with a number, case-sensitive, do not use # when referencing variables, need to add $ when accessing, if unsuccessful, add () around the variable, spaces are allowed around the equals sign, all variable types are strings, no need for "" when using.
System Variable Environment
CC: the name of the compiler, default is gcc, CC = arm-linux-gcc
RM: remove files, equivalent to rm -f
Automatic Variables
$^: represents all dependency files
$@: represents the target file
Multi-directory Projects
Source file directory src, place *.c files
Header file directory include, place *.h files
Library file directory lib, place *.so files
Executable file bin, the final generated binary
gcc ./src/*.c -o main -I ./include/ -lm, -lm is required for sqrt, generally can write -I for header file linking, capital I -L for library directory linking -l for library access, lowercase l.
C_SRC = $(wildcard ./src/*.c)
#C_OBJ = $(patsubst %c,%o,$(C_SRC)) ,# is a comment
I_DIR = -I ./include
L_DIR = -L ./lib -lm
C_BIN = ./bin/main
C_CLEAN = clean
$(C_BIN): $(C_OBJ) $(CC) $^ -o $@ $(I_DIR) $(L_DIR)
.PHONY: $(C_CLEAN)
$(C_CLEAN): $(RM) $(C_BIN)
Complex Files
(wildcard) function: searches for matching files in a given path, usage (wildcard arg1,arg2,arg3)
patsubst function: converts all *.c files to *.o files, usage $(patsubst%c,%o, dependency files)
Library Creation
Static libraries (<span>.a</span>) package multiple object files, created using the <span>ar</span> command, linked to the program at compile time, and do not require the library file at runtime. Dynamic libraries (<span>.so</span>) are loaded at runtime, and the system must be able to find the library file.
Libraries are stored in binary form and need to be linked during compilation.
Static library lib*.a, libadd.a
During compilation, static linking is required to include library functions in the executable program.
After compilation, the static library is not needed for execution.
Dynamic library lib*.so, linadd.so
During program compilation, no linking is done, and when functions are needed, they are handled by the library at runtime, returning results.
Steps to Create a Static Library
Main function test data: main.c
Implementation of functional functions: add.c, add.h
Compile functional functions into a library file (binary file)
Compile functional files *.c into *.o files
gcc add.c -o add.o -c
Pack *.o files into an .a file, the filename must start with lib
ar rcs libadd.a add.o
Combined Compilation
gcc main.c libadd.a -o main
Test ./main
Steps to Create a Dynamic Library
Main function test data: main.c
Implementation of functional functions: add.c, add.h
Compile functional functions into a library file (binary file)
Compile functional files *.c into *.o files
gcc add.c -o add.o -fPIC -c
Pack *.o files into a .so file, the filename must start with lib
gcc -shared -fPIC -o libadd.so add.o
Combined Compilation
gcc main.c -o main -L -ladd
Test ./main
Dynamic Library Errors
./main: error while loading shared libraries: libadd.so: cannot open shared object file: No such file or directory
Solution: Copy the corresponding dynamic library to the lib directory in the Linux system
sudo cp libadd.so /lib
Conclusion
The Makefile efficiently manages the compilation process through dependencies and rules, with variables, functions, and implicit rules simplifying the writing process. Phony targets are used for cleanup operations. Multi-directory projects require attention to file references, and library creation is divided into static and dynamic, with dynamic libraries needing correct path settings. The Makefile is an efficient tool for software development, and proper use can optimize the compilation process and improve development efficiency.