Makefile

makefile基础知识

  • 只要依赖比目标,或是目标不存在,就会执行命令。
<目标> : <依赖>
	<命令>
  • %.o:表示所有的.o文件。

  • %.c:表示所有的.c文件。

  • $@:表示目标。

  • $^:表示所有的依赖文件。

  • $<:表示第一个依赖文件。

  • .PHONY:假象目标。

    • 当目录里面有名为“clean”的文件,目标文件是有的,并且没有依赖文件,没有办法判断依赖文件的时间。这种写法会导致:有同名的"clean"文件时,就没有办法执行make clean操作。解决办法:我们需要把目标定义为假象目标,用关键子PHONY。
      PHONY: clean //把clean定义为假象目标。他就不会判断名为“clean”的文件是否存在。
  • %格式和*格式的区别
    % *在 makefile 中都是用来匹配相似的文件,但它们的使用场景和具体含义有所不同。

    • %格式:模式匹配符,用于定义通用规则时使用。
      • 例如:%.c 代表任何以 .c 结尾的文件,并且可以与模式规则一起使用,以生成相应的目标文件。%.o : %.c 表示任何.o文件都可以由相应的.c文件生成。使用 %.c 时,make 会根据需要自动推导出依赖关系。

        %.o : %.c
            gcc  -c -o $@ $<
        

        在上面的规则中,%.o 依赖于 %.c,并使用 gcc 进行编译。这意味着 a.o 会由 a.c生成,b.o会由b.c 生成,以此类推。

    • *格式:通配符匹配,匹配相同形式的文件。
      • 例如:*.c 用于表示当前目录下所有以 .c 结尾的文件。它通常用于文件列表中,以表示一组文件。

变量

在makefile中有两种变量:

  1. 简单变量(即时变量): :=定义的时候已经被确定了。
  2. 延时变量:=只有在使用到的时候才确定。
 :=  # 即时变量
  =  # 延时变量
 ?=  # 延时变量, 如果是第1次定义才起效, 如果在前面该变量已定义则忽略这句
\+=  # 附加, 它是即时变量还是延时变量取决于前面的定义
 ?=: # 如果这个变量在前面已经被定义了,这句话就会不会起效果

函数

foreach

$(foreach var,list,text) #将 text 中的 var 替换为 list 中的每个元素,并对所有元素执行相同的操作,然后将结果合并成一个字符串
#var: 在每次迭代时,list 中的当前元素将被赋值给 var
#list: 需要遍历的列表或数据序列,可以是空格分隔的字符串
#text: 对 list 中的每个元素执行的文本或操作
输入:
A = a b c
B = $(foreach f, &(A), $(f).o)
all:
	@echo B = $(B)

输出:
B = a.o b.o c.o

filter/filter-out

$(filter pattern...,text)     # 从 text 中筛选出所有匹配 pattern 的项,返回。匹配的项用于从文件列表中选择特定的文件,或根据模式过滤变量内容
$(filter-out pattern...,text) # 从 text 中排除所有匹配 pattern 的项,返回不匹配的项
输入:
C = a b c d/
D = $(filter %/, $(C))
E = $(filter-out %/, $(C))
all:
        @echo D = $(D)
        @echo E = $(E)
        
输出:
D = d/
E = a b c

Wildcard

$(wildcard pattern) # 用于查找与指定模式匹配的文件列表。返回所有匹配 pattern 的文件名,以空格分隔的字符串形式
在目录下创建三个文件:a.c b.c c.c
输入:
files = $(wildcard *.c)
all:
        @echo files = $(files)
        
输出:
files = a.c b.c c.c

#也可以用wildcard函数来判断,真实存在的文件
输入:
files2 = a.c b.c c.c d.c e.c  abc
files3 = $(wildcard $(files2))
all:
        @echo files3 = $(files3)

输出:
files3 = a.c b.c c.c

patsubst

$(patsubst pattern,replacement,\$(var)) #从 var 变量里面取出每一个值,如果这个符合 pattern 格式,把它替换成 replacement 格式
输入:
files2  = a.c b.c c.c d.c e.c abc
dep_files = $(patsubst %.c,%.d,$(files2))
all:
        @echo dep_files = $(dep_files)

输出:
dep_files = a.d b.d c.d d.d e.d abc

其他

gcc -M c.c # 打印出依赖
gcc -M -MF c.d c.c  # 让 gcc 生成 c.c 文件的依赖关系,并将这些依赖信息输出到指定的文件 c.d 中,即把依赖写入文件c.d。-MF后面跟的是文件名
gcc -c -o c.o c.c -MD -MF c.d  # 编译c.o, 把依赖写入文件c.d
CFLAGS = -Werror #把所有的警告当成错误
CFLAGS = -Iinclude #加上-I参数,指定头文件路径,-Iinclude表示当前的inclue文件夹下

基础makefiel

  • 使用时把要编译的文件写进objs
objs := a.o b.o c.o

dep_files := $(foreach f, $(objs), .$(f).d)
dep_files := $(wildcard $(dep_files))

ifneq ($(dep_files),)
	include $(dep_files)
endif

test : $(objs)
	gcc -o $@ $^

%.o : %.c
	gcc -Wp,-MD,.$@.d -c -o $@ $<

.PHONY : clean distclean	
clean :
	rm *.o test -f
distclean :
	rm $(dep_files) *.o test -f

进阶Makefile

  1. 将警告当成错误。
  2. 指定头文件存放位置为include,在.c文件中使用< >包含头文件。
# 定义目标文件列表
objs := a.o b.o c.o

# 生成依赖文件列表,每个目标文件对应一个依赖文件
# 筛选实际存在的依赖文件
dep_files := $(foreach f, $(objs), .$(f).d)
dep_files := $(wildcard $(dep_files))

# 包含依赖文件,以便使用自动生成的依赖信息
ifneq ($(dep_files),)
	include $(dep_files)
endif

# 定义编译选项:将所有警告视为错误,指定包含路径为 include 目录
CFLAGS = -Werror -Iinclude

# 默认目标:链接目标文件生成可执行文件 test
test: $(objs)
	gcc -o $@ $^

# 编译规则:将 .c 文件编译为 .o 文件,并生成依赖文件
%.o: %.c
	gcc -Wp,-MD,.$@.d $(CFLAGS) -c -o $@ $<
	
# 指定伪目标:这些目标不生成文件,只是执行命令
.PHONY: clean distclean

# 清理目标:删除所有 .o 文件和可执行文件 test
clean:
	rm *.o test -f

# 完全清理目标:删除依赖文件、所有 .o 文件和可执行文件 test
distclean:
	rm $(dep_files) *.o test -f

解析:

objs := a.o b.o c.o
dep_files := $(foreach f, $(objs), .$(f).d)
dep_files := $(wildcard $(dep_files))
  • objs 定义了目标文件列表。
  • dep_files 根据目标文件列表生成依赖文件列表,并使用 wildcard 过滤出存在的依赖文件。
CFLAGS = -Werror -Iinclude
  • CFLAGS 定义了编译选项,包括启用所有警告作为错误和包含路径 include
test: $(objs)
    gcc -o $@ $^
  • test 目标依赖于所有目标文件,并使用 gcc 链接生成可执行文件。
ifneq ($(dep_files),)
    include $(dep_files)
endif
  • 如果存在依赖文件,则将它们包含进来。
%.o: %.c
    gcc -Wp,-MD,.$@.d $(CFLAGS) -c -o $@ $<
  • 通用的编译规则,将.c文件编译成 .o 文件,并生成依赖文件。
  • -Wp,-MD,.$@.d: 这部分选项告诉编译器预处理阶段的行为。
    • -Wp,option: 把 option 传递给预处理器。
    • -MD: 生成依赖文件。这会创建一个包含当前文件依赖关系的 .d 文件。
    • .$@.d: 指定依赖文件的名称。$@ 代表当前目标文件的名称(不含扩展名),.$@.d 通常用于生成文件名如 file.o.d
      • -Wp,-MD,.$@.d :生成依赖关系文件,并将其保存为以 . 开头并以 .d 结尾的文件。
    • -c: 只编译源文件,不进行链接。
    • -o $@: 指定输出文件的名称。$@ 代表当前目标文件的名称(通常是目标文件的路径和名称)。
    • $<: 代表第一个依赖文件的名称,通常是当前的源文件。
.PHONY: clean distclean
  • 指定 cleandistclean 作为伪目标,确保它们不会与实际文件名冲突。
clean:
    rm *.o test -f
  • clean 目标用于删除所有目标文件和可执行文件。
distclean:
    rm $(dep_files) *.o test -f
  • distclean 目标用于删除所有目标文件、可执行文件和依赖文件。

最终版本:

# 定义目标文件列表,表示需要编译的源文件对应的目标文件
objs := a.o b.o c.o d.o

# 生成依赖文件列表,每个目标文件将会有一个对应的依赖文件
# foreach 循环遍历目标文件列表 objs,为每个文件添加前缀 '.' 和后缀 '.d'
dep_files := $(foreach f, $(objs), .$(f).d)

# 筛选实际存在的依赖文件
# wildcard 函数返回当前目录下存在的匹配模式的文件列表
dep_files := $(wildcard $(dep_files))

# 包含依赖文件,以便使用自动生成的依赖信息
# ifneq 判断 dep_files 是否为空,如果不为空则包含这些依赖文件
ifneq ($(dep_files),)
	include $(dep_files)
endif

# 定义编译选项
# -Werror: 将所有警告视为错误,防止警告信息被忽视
# -Iinclude: 指定头文件搜索路径为 include 目录,方便查找头文件
CFLAGS = -Werror -Iinclude

# 默认目标,表示要生成的最终可执行文件 test
# $(objs) 表示目标文件列表 a.o b.o c.o
# gcc 编译命令
# -o $@ 表示输出文件为目标文件 test
# $^ 表示所有依赖文件,即 a.o b.o c.o
test: $(objs)
	gcc -o $@ $^

# %.o: %.c 表示所有 .o 文件都依赖于同名的 .c 文件
# -Wp,-MD,.$@.d 表示生成依赖文件 .[目标文件].d,例如 .a.o.d
# $(CFLAGS) 表示使用之前定义的编译选项
# -c 表示只进行编译,不进行链接
# -o $@ 表示输出文件为目标文件
# $< 表示第一个依赖文件,即对应的 .c 文件
%.o: %.c
	gcc -Wp,-MD,.$@.d $(CFLAGS) -c -o $@ $<

# 指定伪目标,表示这些目标不生成文件,只是执行命令
# 伪目标可以避免与实际文件同名冲突,并且在命令行调用时总是执行
.PHONY: clean distclean

# 清理目标,表示删除编译生成的中间文件和可执行文件
# 删除所有 .o 文件和可执行文件 test
clean:
	rm *.o test -f

# 完全清理目标,表示删除所有依赖文件、中间文件和可执行文件
# 删除依赖文件、所有 .o 文件和可执行文件 test
distclean:
	rm $(dep_files) *.o test -f

参考他人的makefile

# 定义变量
TARGET  = 100ask                    # 最终生成的可执行文件的名称
CC      = gcc                       # 使用的编译器
CFLAGS  = -Wall -g                  # 编译标志:开启所有警告并生成调试信息
INCLUDES = -I ./inc                 # 包含头文件的目录路径
VERSION = 1.0                       # 版本号(根据需要定义)

# 自动生成源文件列表和目标文件列表
SOURCE  = $(wildcard ./src/*.c)     # 查找 src 目录下所有的 .c 文件
OBJECT  = $(patsubst %.c, %.o, $(SOURCE))  # 将所有 .c 文件转换为对应的 .o 文件

# 定义可执行文件生成规则
$(TARGET): $(OBJECT)
	@mkdir -p output/             # 创建 output 目录(如果不存在的话)
	$(CC) $(OBJECT) $(CFLAGS) -o output/$(TARGET)_$(VERSION)  # 链接所有 .o 文件生成可执行文件

# 定义通用规则,将 .c 文件编译为 .o 文件
%.o: %.c
	$(CC) $(INCLUDES) $(CFLAGS) -c $< -o $@  # 编译 .c 文件为 .o 文件

# 定义 clean 规则,用于清理编译生成的文件
.PHONY: clean
clean:
	@rm -rf $(OBJECT) output/     # 删除所有 .o 文件和 output 目录

  1. 创建两个目录(scrinc)和makefile文件;
  2. 将所有的源文件(.c)放入src中,所有的头文件(.h)放入inc中;
  3. 使用make进行自动编译,生成的可执行文件也会放在output目录中;
  4. 最后使用make clean命令删除所有的.o文件和output目录。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值