5_嵌入式Linux驱动开发篇_Makefile的初步使用_语法|变量|自动化变量|伪目标_一个案例让你搞懂Makefile_笔记

Makefile定义

Makefile 文件描述了整个工程的编译、连接等规则。其中包括:
①工程中的哪些源文件需要编译以及如何编译
②需要创建哪些库文件以及如何创建这些库文件
③如何最后产生我们想要的可执行文件。
为工程编写Makefile 的好处是能够使用一行命令来完成“自动化编译”。
终端输入make命令启用Makefile文件。make是一个命令工具,它解释Makefile 中的指令。
Makefile 有自己的书写格式、关键字、函数。像C 语言有自己的格式、关键字和函数一样。以下是 Makefile 的基本规则和语法:

Makefile基本语法

基本语法:

target: prerequisites
    command

①目标(Target):目标是 Makefile 中的名称,它指定了要构建的输出文件的名称。目标可以是可执行文件、中间文件、或者其他文件。
②依赖(Prerequisites):依赖是目标生成所需的文件或者其他目标。如果目标所依赖的文件发生了变化,Make 将重新生成该目标。
③命令(Command):命令是 Makefile 中的一条指令,它告诉 Make 如何生成目标文件。命令必须以 Tab 键开始,不能用空格。

Makefile变量

Makefile 支持变量,可以使 Makefile 更易于维护和更具可读性。
变量定义之后,使用$使用变量。类似于#define 宏定义。
例如:

CC=gcc
CFLAGS=-Wall
hello: hello.o
    $(CC) $(CFLAGS) -o hello hello.o
hello.o: hello.c
    $(CC) $(CFLAGS) -c hello.c

在这个例子中,CC 是 C 编译器的名称,CFLAGS 是编译选项,使编译器显示所有警告。

Makefile通配符

在 Makefile 中,通配符用于匹配文件名或文件路径中的多个字符,以便在规则中批量处理文件。通配符常见的有以下几种:

*:匹配零个或多个字符。
?:匹配一个任意字符。
[...]:匹配方括号内的任意一个字符。
[!...]:匹配除了方括号内的字符之外的任意一个字符。
在 Makefile 中,通配符通常与模式规则结合使用,以便自动化地处理一类文件。
例:

program: *.c
    gcc $^ -o $@

在这个 Makefile 中,*.c 通配符匹配当前目录下所有以 .c 结尾的文件。$^ 表示所有匹配到的文件,$@ 表示目标文件。因此,program: *.c 规则会将所有的 .c 文件编译链接成一个名为 program 的可执行文件。

Makefile模式规则|自动化变量

模式规则

模式规则(Pattern Rule)是 Makefile 中的一种特殊规则,它用于定义一种模式,告诉 Make 工具如何将一类文件转换成另一类文件。通常,模式规则用于编译源文件到目标文件的转换过程。
例子:

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

这个模式规则告诉 Make 工具,如果存在以 .c 结尾的源文件,则可以使用 gcc 编译器将其编译成对应的 .o 目标文件。$< 表示第一个依赖文件(即源文件),$@ 表示目标文件。

自动化变量

自动化变量是 Makefile 中的一种特殊变量,它们在规则的命令中使用,代表了与规则相关联的文件名。这些变量使得 Makefile 编写更加简洁和灵活。

以下是常用的自动化变量:

$@:表示规则中的目标文件名。
$<:表示规则中的第一个依赖文件名。
$^:表示规则中的所有依赖文件名,以空格分隔。
$?:表示比目标文件更新的所有依赖文件名,以空格分隔。
$(@D):表示目标文件所在的目录名。
$(@F):表示目标文件的文件名(不包含路径)。
下面是一个示例 Makefile,演示了如何使用自动化变量:

all: program
program: main.o func1.o func2.o
    gcc -o $@ $^
main.o: main.c
    gcc -c $< -o $@
func1.o: func1.c
    gcc -c $< -o $@
func2.o: func2.c
    gcc -c $< -o $@
clean:
    rm -f *.o program

在这个示例中,使用了 $@ 表示目标文件,$^ 表示所有依赖文件,$< 表示第一个依赖文件。通过这些自动化变量,可以使 Makefile 更加简洁和易于维护。

Makefile伪目标

伪目标是 Makefile 中的一种特殊目标,它不对应任何实际的文件。伪目标通常用于定义一些命令或者规则,用于执行一些特殊的操作,例如清理、安装、测试等。
在 Makefile 中,伪目标通常以 .PHONY 声明,以告诉 Make 工具这些目标不是文件名,而是一个命令序列。

.PHONY: clean

clean:
    rm -f *.o

在这个示例中,clean 是一个伪目标,它的作用是删除所有 .o 文件。.PHONY 声明告诉 Make 工具,clean 并不对应任何实际的文件,只是一个执行命令的目标。

一个裸机开发使用Makefile案例

例:
目的是编写点亮一个LED的c语言程序。
这个项目包含了几个目录和文件:
加粗样式目录:
①imx6ul: 存放了一些头文件。
②bsp/clk: 存放了与时钟相关的源文件。
③bsp/led: 存放了与 LED 相关的源文件。
④bsp/delay: 存放了与延时相关的源文件。
⑤project: 存放了项目的主要源文件。
文件:
①start.s启动文件。
②main.c主函数、led.c、clk.c、delay.c
③imx6ul.h 手搓寄存器的结构体定义文件。
链接脚本文件(imx6ul.lds):定义了链接器如何将目标文件链接成可执行文件。
最终,这个 Makefile 的目标是生成以下几个文件:
①bsp.bin:二进制可执行文件,可以在 ARM 平台上运行。
②bsp.elf:ELF 格式的可执行文件,用于调试和分析。
③bsp.dis:反汇编文件,包含了可执行文件的汇编代码。

目录展示:
目录

Makefile:

CROSS_COMPILE 	?= arm-linux-gnueabihf-
TARGET		  	?= bsp

CC 				:= $(CROSS_COMPILE)gcc
LD				:= $(CROSS_COMPILE)ld
OBJCOPY 		:= $(CROSS_COMPILE)objcopy
OBJDUMP 		:= $(CROSS_COMPILE)objdump

INCDIRS 		:= imx6ul \
				   bsp/clk \
				   bsp/led \
				   bsp/delay 
				   			   
SRCDIRS			:= project \
				   bsp/clk \
				   bsp/led \
				   bsp/delay 		   
				   
INCLUDE			:= $(patsubst %, -I %, $(INCDIRS))

SFILES			:= $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S))
CFILES			:= $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))

SFILENDIR		:= $(notdir  $(SFILES))
CFILENDIR		:= $(notdir  $(CFILES))

SOBJS			:= $(patsubst %, obj/%, $(SFILENDIR:.S=.o))
COBJS			:= $(patsubst %, obj/%, $(CFILENDIR:.c=.o))
OBJS			:= $(SOBJS) $(COBJS)

VPATH			:= $(SRCDIRS)

.PHONY: clean
	
$(TARGET).bin : $(OBJS)
	$(LD) -Timx6ul.lds -o $(TARGET).elf $^
	$(OBJCOPY) -O binary -S $(TARGET).elf $@
	$(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis

$(SOBJS) : obj/%.o : %.S
	$(CC) -Wall -nostdlib -c -O2  $(INCLUDE) -o $@ $<

$(COBJS) : obj/%.o : %.c
	$(CC) -Wall -nostdlib -c -O2  $(INCLUDE) -o $@ $<
	
clean:
	rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)

解释
CROSS_COMPILETARGET 是两个变量,分别用于设置交叉编译工具链的前缀和生成的目标文件名。它们使用了赋值运算符 ?=,表示如果这些变量没有在命令行中指定,就使用后面的默认值。
CC、LD、OBJCOPY、OBJDUMP 分别是编译器、链接器、目标文件复制工具和目标文件反汇编工具的路径。
INCDIRSSRCDIRS 分别指定了包含头文件和源文件的目录。
INCLUDE 使用了函数 patsubst 将每个目录名转换成了 -I 选项的形式,用于指定头文件搜索路径。
SFILESCFILES 分别使用了 wildcard 函数匹配所有汇编语言文件和 C 语言文件。
SFILENDIRCFILENDIR 使用了 notdir 函数获取文件名(不包含路径)。
SOBJSCOBJS 使用了 patsubst 函数将每个源文件名转换成了对应的目标文件名,并加了 obj/ 前缀表示目标文件存放的目录。
VPATH 再次设置了 Makefile 中文件的搜索路径,以便 Make 可以在这些目录中找到源文件。
.PHONY: clean 声明了 clean 目标是一个伪目标,不对应实际文件。主要用来删除/清理编译生成的文件。
$(TARGET).bin 是构建目标,依赖于 $(OBJS) 中定义的所有目标文件。该规则使用了链接器 $(LD) 将所有目标文件链接成一个.elf可执行文件,并使用 $(OBJCOPY) 将其转换成二进制文件。生成了一个 .dis 文件,包含了可执行文件的反汇编结果。
$(SOBJS)$(COBJS) 分别是汇编文件和 C 文件的编译规则,将源文件编译成目标文件。

解析每一个变量定义的实际含义:

LD				= arm-linux-gnueabihf-ld
OBJCOPY			= arm-linux-gnueabihf-objcopy
OBJDUMP			= arm-linux-gnueabihf-objdump

INC_DIRS		=  imx6ul bsp/clk bsp/led bsp/delay					#头文件所在目录
SRC_DIRS		= project bsp/clk bsp/led bsp/delay					#源文件所在目录

INCLUDE			= -I imx6ul  -I bsp/clk  -I bsp/led -I bsp/delay 	# Makefile语法要求指明头文件目录的时候前面需要加上-I

S_FILE			= project/start.s     								# $(foreach dir, $(SRC_DIRS), $(wildcard $(dir)/*.s)) 
																	# 这个函数将寻找每个源文件目录下的.s汇编文件
C_FILE			= project/main.c  bsp/clk/bsp_clk.c  \
					bsp/led/bsp_led.c  bsp/delaybsp_delay.c   		# 同上,得出所有源文件目录下的.c文件
S_FILE_NDIR		= start.s       									# $(notdir $(S_FILE))这个函将把参数中的文件路径去掉,只剩下文件
C_FILE_NDIR		= main.c  bsp_clk.c  bsp_led.c  bsp_delay.c			# 同上,只剩下文件

S_OBJS			= obj/start.o										# $(S_FILE_NDIR:.s=.o)将S_FILE_NDIR中所有.s文件替换为.o文件;
																	# $(patsubst %, obj/%, $(S_FILE_NDIR:.s=.o))把
																	# $(S_FILE_NDIR:.s=.o)得到的所有文件变为obj/$(S_FILE_NDIR:.s=.o),
																	# 也就是在前面加上obj/
C_OBJS			= obj/main.o  obj/bsp_clk.o  obj/bsp_led.o  obj/bsp_delay.o
OBJS			= obj/start.o obj/main.o  obj/bsp_clk.o  obj/bsp_led.o  obj/bsp_delay.o
VPATH			= project bsp/clk bsp/led bsp/delay

解析编译命令

$(TARGET).bin : $(OBJS)   
【等价于】==>  bsp.bin : obj/start.o obj/main.o  obj/bsp_clk.o  obj/bsp_led.o  obj/bsp_delay.o

	$(LD) -Timx6ul.lds -o $(TARGET).elf $^  
	【等价于】==> arm-linux-gnueabihf-ld -Timx6ul.lds -o bsp.elf  obj/start.o obj/main.o \
	 obj/bsp_clk.o obj/bsp_led.o  obj/bsp_delay.o                	 #链接所有.o文件生成.elf文件
	 
	$(OBJCOPY) -O binary -S $(TARGET).elf $@ 
	【等价于】==> arm-linux-gnueabihf-objcopy -O binary -S bsp.elf  bsp.bin    #生成bin文件

	$(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis  
	【等价于】==> arm-linux-gnueabihf-objdump -D -m arm bsp.elf > bsp.dis		 #生成反汇编



$(S_OBJS) : obj/%.o : %.s  
【等价于】==> obj/start.o : start.s

	$(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ $<  
	【等价于】==>  arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -I imx6ul  -I bsp/clk  -I bsp/led -I bsp/delay -o obj/start.o start.s
	
	
	
$(C_OBJS) : obj/%.o : %.c 
【等价于】==>     obj/main.o : main.c 
		obj/bsp_clk.o : bsp_clk.c  
		obj/bsp_led.o :  bsp_led.c   
		obj/bsp_delay.o : bsp_delay.c
			
	$(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ $<  
	【等价于】==>
		arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -I imx6ul  -I bsp/clk  -I bsp/led -I bsp/delay -o obj/main.o main.c 
		arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -I imx6ul  -I bsp/clk  -I bsp/led -I bsp/delay -o obj/bsp_clk.o bsp_clk.c 
		arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -I imx6ul  -I bsp/clk  -I bsp/led -I bsp/delay -o obj/bsp_led.o bsp_led.c 
		arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -I imx6ul  -I bsp/clk  -I bsp/led -I bsp/delay -o obj/bsp_delay.o bsp_delay.c


rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)    
【等价于】==>rm -rf bsp.elf bsp.dis bsp.bin obj/main.o  obj/bsp_clk.o  obj/bsp_led.o  obj/bsp_delay.o obj/start.o

部分解析参考该博客。

终端运行:
运行

  • 23
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

善于伴随

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值