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.o
hello.c
编译成hello.o
文件。
.a 文件(静态库)
- 定义:.a 文件是静态链接库文件,也称为归档文件。它是一组目标文件(.o 文件)的集合,通常包含多个函数和变量的实现。
- 用途:静态库在程序编译时被链接到目标程序中,程序运行时不再需要该静态库。它适用于需要将库代码直接嵌入到可执行文件中的场景。
- 生成方法:
这条命令将ar rcs libmyhello.a hello.o
hello.o
文件打包成静态库libmyhello.a
。 - 使用方法:
这条命令将gcc -o hello main.c -L. -lmyhello
main.c
与静态库libmyhello.a
链接,生成可执行文件hello
。
.so 文件(共享库)
- 定义:.so 文件是共享对象文件(Shared Object),也称为动态链接库。它是一种在程序运行时动态加载的库文件,可以被多个程序共享使用。
- 用途:共享库在程序运行时被动态加载,减少了程序的体积,提高了代码的复用性。它适用于需要在运行时动态加载和更新库的场景。
- 生成方法:
这条命令将gcc -shared -fPIC -o libmyhello.so hello.o
hello.o
文件编译成共享库libmyhello.so
。 - 使用方法:
这条命令将gcc -o hello main.c -L. -lmyhello
main.c
与共享库libmyhello.so
链接,生成可执行文件hello
。运行时,需要确保共享库在系统的库路径中,或者通过设置LD_LIBRARY_PATH
环境变量来指定库路径。
总结
- .o 文件:编译后的目标文件,用于链接阶段。
- .a 文件:静态链接库,编译时链接到目标程序中。
- .so 文件:共享库,运行时动态加载。