1. Makefile的使用
Makefile基础 - Makefile教程 - 廖雪峰的官方网站
规则的基本格式
Makefile 由若干条规则构成,每条规则的基本格式如下:
目标文件: 依赖文件1 依赖文件2 ...
命令1
命令2
...
- 目标文件:是要生成的文件或执行的操作的名称,可以是实际的文件名,也可以是伪目标(如
clean)。 - 依赖文件:是生成目标文件所需的文件或其他目标的列表,
如果依赖文件的修改时间晚于目标文件,则目标文件需要重新生成。 - 命令:是用于生成目标文件的具体操作,必须以 Tab 键开头,可以是编译命令、复制命令等任意命令。
make执行时,默认执行第一条规则
伪目标
伪目标是不代表实际文件的目标,而是用于执行特定操作的标记。例如:
.PHONY: clean
clean:
rm -f m.txt
rm -f x.txt
在执行 make clean 时,clean 不会被视为文件名,而是直接执行其对应的命令。
伪目标的作用
- 避免与同名文件冲突:
- 如果当前目录下存在一个名为
clean的文件,make clean会认为clean目标已经是最新的,从而不会执行clean规则中的命令。 - 通过将
clean声明为伪目标,make会忽略当前目录下是否存在名为clean的文件,直接执行clean规则中的命令。
- 如果当前目录下存在一个名为
- 提高执行效率:
- 当目标被声明为伪目标后,
make不会试图寻找该目标的隐含规则,从而提高执行效率。
- 当目标被声明为伪目标后,
- 确保规则执行:
- 伪目标确保每次执行
make clean时,都会执行clean规则中的命令,而不会因为目标文件的存在而跳过。
- 伪目标确保每次执行
执行多条命令
一个规则可以包含多条命令,但需要注意以下几点:
每条命令必须以 Tab 键开头。- 如果希望多条命令在同一个 Shell 环境中执行,可以使用分号
;将命令连接起来,或者使用反斜杠\将命令换行。 - 使用
#来注释
控制打印
默认情况下,make 会打印出它执行的每一条命令。如果不想打印某一条命令,可以在命令前加上 @,例如:
no_output:
@echo 'not display'
echo 'will display'
控制错误
make 在执行命令时,会检查每一条命令的返回值,如果返回错误(非 0 值),就会中断执行。如果希望忽略错误,继续执行后续命令,可以在需要忽略错误的命令前加上 -,例如:
ignore_error:
-rm zzz.txt
echo 'ok'
变量的使用
Makefile 支持使用变量,变量的定义和使用方式如下:
- 定义变量:
变量名 = 变量值,例如CC = gcc。 - 使用变量:使用
$(变量名)或${变量名}来引用变量的值,例如$(CC)。
依赖关系的自动推导
Makefile 可以根据文件的后缀名自动推导出一些常见的编译规则,例如:
- 如果目标文件是
.o文件,依赖文件是.c文件,则默认使用$(CC) -c $< -o $@命令进行编译。 $<是 Makefile 中的一个自动变量,表示依赖列表中的第一个依赖文件。$@是 Makefile 中的一个自动变量,表示当前规则的目标文件。
文件名模式匹配
Makefile 支持使用通配符和模式匹配来简化规则的编写,例如:
使用
%.o: %.c可以匹配所有.c文件生成.o文件的规则。在
make的规则中,%用于表示文件名的模式匹配,例如%.o可以匹配所有以.o结尾的文件。
包含其他 Makefile 文件
可以使用 include 指令来包含其他 Makefile 文件,例如:
include common.mk
这可以将 common.mk 文件中的规则和变量包含到当前 Makefile 中。
条件语句
Makefile 支持条件语句,可以根据条件来执行不同的规则或命令,例如:
ifeq ($(DEBUG), 1)
CFLAGS = -g
else
CFLAGS = -O2
endif
这可以根据变量 DEBUG 的值来设置不同的编译选项。
Makefile内置函数
在 Makefile 中,OFILES = $(CFILES:.c=.o) 是一个常用的语法,用于将变量 CFILES 中的每个 .c 文件名替换为对应的 .o 文件名。这种语法是 Makefile 特有的,称为替换引用。
替换引用的语法
$(VAR:A=B)
VAR是变量名。A是要被替换的字符串。B是替换后的字符串。
假设 CFILES 的值为:
CFILES = main.c utils.c
那么执行以下替换操作后:
OFILES = $(CFILES:.c=.o)
OFILES 的值将变为:
OFILES = main.o utils.o
替换引用是 patsubst 函数的一个简化形式。patsubst 函数的功能更强大,可以处理更复杂的模式匹配和替换。例如:
OFILES = $(patsubst %.c,%.o,$(CFILES))
这与 OFILES = $(CFILES:.c=.o) 的效果完全相同。
1. wildcard 函数
- 功能:匹配文件名模式,并返回匹配的文件列表。
- 语法:
$(wildcard pattern) - 示例:
这会将当前目录下所有以CFILES = $(wildcard *.c).c结尾的文件名赋值给CFILES。
2. patsubst 函数
- 功能:模式替换,将符合模式的字符串替换为指定的字符串。
- 语法:
$(patsubst PATTERN,REPLACEMENT,TEXT) - 示例:
这会将OFILES = $(patsubst %.c,%.o,$(CFILES))CFILES中的每个.c文件名替换为.o。
3. addprefix 函数
- 功能:给列表中的每个元素添加前缀。
- 语法:
$(addprefix PREFIX,NAMES) - 示例:
这会将SRC_FILES = file1.c file2.c ALL_FILES = $(addprefix src/,$(SRC_FILES))SRC_FILES中的每个文件名添加前缀src/。
4. addsuffix 函数
- 功能:给列表中的每个元素添加后缀。
- 语法:
$(addsuffix SUFFIX,NAMES) - 示例:
这会将FILES = file1 file2 OBJ_FILES = $(addsuffix .o,$(FILES))FILES中的每个文件名添加后缀.o。
5. basename 函数
- 功能:去掉文件名中的后缀。
- 语法:
$(basename NAMES) - 示例:
这会将OBJS = ./build/main.o ./build/func.o BASE_OBJS = $(basename $(OBJS))OBJS中的每个文件名去掉后缀,结果为./build/main ./build/func。
6. dir 函数
- 功能:获取文件名中的目录部分。
- 语法:
$(dir NAMES) - 示例:
这会将OBJS = ./build/main.o ./build/func.o DIR_OBJS = $(dir $(OBJS))OBJS中的每个文件名的目录部分提取出来,结果为./build/ ./build/。
7. notdir 函数
- 功能:获取文件名中的非目录部分。
- 语法:
$(notdir NAMES) - 示例:
这会将OBJS = ./build/main.o ./build/func.o NOTDIR_OBJS = $(notdir $(OBJS))OBJS中的每个文件名的非目录部分提取出来,结果为main.o func.o。
8. shell 函数
- 功能:执行 Shell 命令,并返回其输出结果。
- 语法:
$(shell command) - 示例:
这会将CURRENT_TIME := $(shell date)date命令的输出结果赋值给CURRENT_TIME。
作业
- 解释
CFILES = $(wildcard *.c )是什么意思?
回答:
wildcard函数:wildcard是 Makefile 中的一个内置函数,用于匹配符合特定模式的文件名。它的语法是$(wildcard pattern),其中pattern是一个文件名模式,可以包含通配符*和?。所以这行的意思是将
当前目录下所有的.c文件名列表赋值给变量CFILES
- 解释下列语句
.c.o :
$(CC) -c $<
在 Makefile 中,
.c.o是一个模式规则,用于定义如何从.c文件生成.o文件。具体来说,.c.o规则告诉make如何将一个.c文件编译成对应的.o文件。
- **
.c.o**:这是一个模式规则,表示从.c文件生成.o文件。- **
$(CC)**:这是编译器变量,默认值通常是gcc或g++。- **
-c**:这是编译器选项,表示只编译源文件,生成目标文件,但不进行链接。- **
$<**:这是自动变量,表示依赖列表中的第一个依赖文件。在这个规则中,$<表示.c文件。- **
$@**:这是自动变量,表示目标文件。在这个规则中,$@表示.o文件。
2.库文件的区别及用途
.o 文件(目标文件)
- 定义:.o 文件是编译器将源代码文件(如
.c或.cpp)编译后生成的目标文件。它包含了编译后的机器代码和符号信息,但尚未被链接成可执行文件。 - 用途:.o 文件是编译过程中的中间产物,通常用于多文件项目。在链接阶段,多个 .o 文件会被链接器组合成一个可执行文件。
- 生成方法:
这条命令将gcc -c hello.c -o hello.ohello.c编译成hello.o文件。
.a 文件(静态库)
- 定义:.a 文件是静态链接库文件,也称为归档文件。它是一组目标文件(.o 文件)的集合,通常包含多个函数和变量的实现。
- 用途:静态库在程序编译时被链接到目标程序中,程序运行时不再需要该静态库。它适用于需要将库代码直接嵌入到可执行文件中的场景。
- 生成方法:
这条命令将ar rcs libmyhello.a hello.ohello.o文件打包成静态库libmyhello.a。 - 使用方法:
这条命令将gcc -o hello main.c -L. -lmyhellomain.c与静态库libmyhello.a链接,生成可执行文件hello。
.so 文件(共享库)
- 定义:.so 文件是共享对象文件(Shared Object),也称为动态链接库。它是一种在程序运行时动态加载的库文件,可以被多个程序共享使用。
- 用途:共享库在程序运行时被动态加载,减少了程序的体积,提高了代码的复用性。它适用于需要在运行时动态加载和更新库的场景。
- 生成方法:
这条命令将gcc -shared -fPIC -o libmyhello.so hello.ohello.o文件编译成共享库libmyhello.so。 - 使用方法:
这条命令将gcc -o hello main.c -L. -lmyhellomain.c与共享库libmyhello.so链接,生成可执行文件hello。运行时,需要确保共享库在系统的库路径中,或者通过设置LD_LIBRARY_PATH环境变量来指定库路径。
总结
- .o 文件:编译后的目标文件,用于链接阶段。
- .a 文件:静态链接库,编译时链接到目标程序中。
- .so 文件:共享库,运行时动态加载。



