文章目录
Makefile介绍
Makefile:是一个由 make 工具使用的文件,用于自动化编译和构建过程。它定义了一些规则和目标,指定了如何从源代码文件生成目标文件(如可执行文件或库)。通过合理的配置,Makefile可以帮助开发者高效地管理项目的构建过程。
Makefile基本结构
一个典型的 Makefile 由目标、依赖和命令组成。
target: dependencies
command
目标:构建的最终目标,通常是可执行文件、对象文件、虚拟目标等。
第一条规则中的第一个目标成为终极目标,只要该目标更新了,那么 Makefile 的工作就完成了。
依赖文件:目标需要用到的文件,如果依赖文件更新,目标将重新构建。
命令:用来创建或更新目标,每个目标下面有一个或多个命令,命令必须以TAB开头(不能是空格)。
Makefile执行过程
解析 Makefile:make 读取 Makefile 文件中的内容,解析其中的目标、依赖关系和命令。
确定目标:make 确定终极目标(通常是 Makefile 中的第一个目标,或者指定的目标)。
检查依赖关系:make 检查目标所依赖的文件是否更新。如果依赖文件的时间戳晚于目标文件的时间戳,make会执行相应的命令来重新生成目标文件。
执行命令:如果目标需要更新,make 会执行与该目标相关联的命令。
递归构建:如果目标依赖于其他目标,make 会递归地构建这些依赖目标,直到所有目标都构建完毕。
make 会从目标的依赖关系开始,递归地检查每一个目标及其依赖,直到所有依赖项都已处理完毕。在执行每个目标时,make会确保依赖项按照正确的顺序构建。
make选项:
make -j4:并行执行构建任务
make -n:查看执行过程
make -d:显示make要执行的命令,但不实际执行
Makefile变量
Makefile中的变量都是字符串。
注释开头用符号#
。
变量的引用方法是$(变量名)
。
在命令前加上@
就不会输出命令执行过程.
要输出一串字符使用echo
CC = gcc
CFLAGS = -Wall -g
all:
@echo "Compiler is $(CC)"
@echo "Flags are $(CFLAGS)"
赋值符:
=
:在每次引用该变量时重新计算其值,即变量的值会在使用时根据当前的环境或上下文进行计算
:=
:立即计算右边的表达式并将其值赋给变量
?=
:用于在变量尚未定义时进行赋值。如果该变量已经定义,则不会进行赋值操作。这是一种常见的防止覆盖已经设置的变量值的方法。
+=
:将新的内容追加到现有的变量值中
Makefile模式规则
模式规则是允许通过使用通配符来定义规则。常见的模式符号是百分号 (%),它代表文件名的通用部分。
%.o: %.c
gcc -c $< -o $@
Makefile自动化变量
自动化变量在构建规则时会被make自动赋值,它们代表目标和依赖文件、命令执行过程中的其他信息。
Makefile伪目标
伪目标是一些不代表实际文件的目标。通常用于执行一些非构建操作,如清理中间文件、安装程序、构建文档等。伪目标不依赖于文件系统中的实际文件,因此它们总是会被执行,而不管是否存在同名的文件。
为什么使用伪目标?
在 Makefile 中,make会根据目标文件是否存在来决定是否执行规则。如果目标文件已经存在,且依赖文件没有更新,make将跳过该目标的构建过程。因此,如果你创建一个与某个实际文件同名的目标(例如 clean),make 会认为这个目标已经是最新的,而不会执行相应的命令。为了避免这种情况,可以使用伪目标,它们不会受到文件存在与否的影响,每次都会执行。
声明方式:.PHONY : clean
.PHONY: clean all install
all: program
program: main.o utils.o
gcc -o program main.o utils.o
clean:
rm -f *.o program
install:
cp program /usr/local/bin
Makefile条件判断
ifeq和ifneq
ifeq和ifneq用于测试两个值是否相等或不相等。
ifeq (value1, value2)
# 如果 value1 和 value2 相等,执行此块的命令
endif
CC = gcc
CFLAGS = -Wall
ifeq ($(DEBUG), true)
CFLAGS += -g
TARGET = debug_program
else
CFLAGS += -O2
TARGET = release_program
endif
$(TARGET): main.o utils.o
$(CC) $(CFLAGS) -o $@ $^
all: $(TARGET)
ifdef 和 ifndef
ifdef 和 ifndef 用于判断一个变量是否已定义。
ifdef VAR_NAME
# 如果 VAR_NAME 已定义,执行此块的命令
endif
Makefile内建函数
$(wildcard files...)
wildcard 函数用于查找符合通配符模式的文件,通常用来列出目录中的所有源代码文件。
#返回所有以 .c 结尾的文件名
SRCS := $(wildcard *.c)
$(patsubst pattern,replacement,text)
patsubst 函数用于根据给定的模式替换字符串中的部分内容。
#src/main.c 会变成 src/main.o
OBJS := $(patsubst %.c,%.o,$(SRCS))
$(suffix names...)
suffix 用于返回文件的扩展名部分。
#$(suffix $(SRCS)) 会返回所有源文件的扩展名部分
EXTS := $(suffix $(SRCS))
$(basename names...)
basename 用于去掉文件名中的扩展名,只保留文件名的主体部分。
#如果 SRCS 中包含 src/main.c 和 src/utils.c,那么 $(FILES) 会是 src/main 和 src/utils
FILES := $(basename $(SRCS))
$(dir names...)
dir 用于获取文件的目录部分,即去除文件名部分。
#src/main.c 会被替换为 src/,如果文件在根目录,返回空字符串。
DIRS := $(dir $(SRCS))
$(notdir names...)
notdir 用于获取文件的文件名部分,即去除路径。
#src/main.c 会变成 main.c
BASENAMES := $(notdir $(SRCS))
$(join list1,list2)
join 函数用于将两个列表合并成一个列表,返回一个包含所有元素的新列表。
#将 $(DIRS) 和 $(FILES) 两个列表合并为一个列表
LIST := $(join $(DIRS), $(FILES))
$(sort list)
sort 用于对列表进行排序。
#$(sort $(SRCS)) 会对 $(SRCS) 中的文件名进行排序,并返回排序后的列表。
SORTED := $(sort $(SRCS))
$(strip text)
strip 用于去除文本中的多余空格和制表符。
#$(strip -Wall -g ) 会将 CFLAGS 中的多余空格去除,得到 -Wall -g
CFLAGS := $(strip -Wall -g )
$(words list)
words 用于返回列表中元素的个数。
#$(words $(SRCS)) 会返回 $(SRCS) 中元素的个数。
NUM_SRCS := $(words $(SRCS))
$(findstring find,text)
findstring 用于在文本中查找子字符串,如果找到则返回子字符串,否则返回空字符串。
#如果 $(SRC_FILES) 中包含 foo,则返回 foo,否则返回空字符串。
FOUND := $(findstring foo, $(SRC_FILES))
$(filter pattern,text)
filter 用于筛选出符合模式的元素。
#$(filter %.o, $(FILES)) 会筛选出 $(FILES) 中所有以 .o 结尾的文件。
OBJECTS := $(filter %.o, $(FILES))
$(filter-out pattern,text)
filter-out 用于从列表中排除匹配的元素。
#$(filter-out %.o, $(FILES)) 会返回 $(FILES) 中所有不以 .o 结尾的文件。
NOT_OBJS := $(filter-out %.o, $(FILES))
$(call function,arguments...)
call 用于调用一个自定义函数,并传递参数。
define myfunc
echo "This is a custom function, argument: $(1)"
endef
all:
$(call myfunc, test)