Makefile入门详解

提示:以下是本篇文章正文内容,下面案例可供参考

一、Makefile简介

makefile主要使用在没有集成开发环境时,对工程文件进行自动化编译的工具。

makefile 的本质是一个文件,需要配合make指令进行自动化编译。

make是一个命令工具,用来解释makefile文件中的代码,从而实现自动化编译。编译使用的编译器本质上还是gcc。

makefile文件中定义了一系列的规则来指定, 哪些文件需要先编译, 哪些文件需要后编译, 哪些文件需要重新编译, 甚至于进行更复杂的功能操作。

makefile文件的命名:makefile或者Makefile。(也可通过其他方式,不使用这两个名字,通常我们使用这两个即可)

二、makefile 原理

基本原理:若想生成目标, 检查规则中的所有的依赖文件是否都存在。

1、当有依赖文件不存在

  1. 如果有的依赖文件不存在, 则向下搜索规则, 看是否有生成该依赖文件的规则:
  2. 如果有规则用来生成该依赖文件,则执行规则中的命令生成依赖文件;
  3. 如果没有规则用来生成该依赖文件, 则报错

也就是如下图所示:
在这里插入图片描述

2、当所有依赖文件存在

如果所有依赖都存在, 检查规则中的目标是否需要更新, 必须先检查它的所有依赖,依赖中有任何一个被更新, 则目标必须更新.(检查的规则是哪个时间大哪个最新)

如下图所示:
在这里插入图片描述

三、makefile基本规则

1.makefile规则三要素

  1. 目标: 要生成的目标文件
  2. 依赖: 目标文件由哪些文件生成
  3. 命令: 通过执行该命令由依赖文件生成目标

2.基本规则

规则如下:

目标:依赖
	命令		#注意命令需要以tab键开始
main:main.c test.c
	gcc main.c test.c -o main

3、makfile中的变量

makefile中使用变量有点类似于C语言中的宏定义, 使用该变量相当于内容替换, 使用变量可以使makefile易于维护, 修改起来变得简单。
makefile中有三种变量:

  1. 普通变量
  2. 自动变量
  3. 自带变量(不太了解)

3.1普通变量

变量定义是用 = 号即可,例如:

OBJS = main					//定义并赋值(通常不这样使用),会配合: 进行使用,后面会详解
$(OBJS)						//使用变量

3.2自动变量

自动变量主要有,如下所示:

  1. $@: 规则中的目标集合,在模式规则中,如果有多个目标的话,“ $@”表示匹配模式中定义的目标集合。
  2. $<:依赖文件集合中的第一个文件,如果依赖文件是以模式(即“ %” )定义的,那么“ $<”就是符合模式的一系列的文件集合
  3. $^:所有依赖文件的集合,使用空格分开,如果在依赖文件中有多个重复的文件,会去除重复的依赖文件,只保留一份。
  4. $%:当目标是函数库的时候表示规则中的目标成员名,如果目标不是函数库文件,那么其值为空。
  5. $?:所有比目标新的依赖目标集合,以空格分开。
  6. $+:和“ $^”类似,但是当依赖文件存在重复的话不会去除重复的依赖文件。

3.3例程

CC := gcc #arm-linux-gnueabihf-gcc
TARGET := main
object := main.o test.o

.PHONY : clean

$(TARGET):$(object)
	$(CC) -o $@ $^
%.o:%.c
	$(CC) -o -c $@ $<
clean:
	rm main main.o test.o #rm main %.o

上面就是一个简单的makefile文件

4、makefile中的伪目标

Makefile 有一种特殊的目标——伪目标,一般的目标名都是要生成的文件,而伪目标不代表真正的目标名,在执行 make 命令的时候通过指定这个伪目标来执行其所在规则的定义的命令。

注意:使用伪目标的主要是为了避免 Makefile 中定义的只执行命令的目标和工作目录下的实际文件出现名字冲突

上面例程中使用的伪目标clean,是用来清理编译产生的文件。

四、makefile其他常用的规则

  1. := :一个变量在定义并赋值后,不会使用后面给该变量赋值的值,只能使用前面已经定义好的
  2. ?= :如果该变量在前面没有被赋值,就给该变量赋值。如果赋值过了,就使用之前赋值的值
  3. += :需要给前面已经定义好的变量添加一些字符串进去,类似x +=1;
  4. %:类似通配符,例:%.c,也就是以 .c 结尾的所有文件
  5. \:是makefile中行的分隔符。

五、makefile中的函数

注意:此处只说下面综合例程中使用的函数和常用的函数

5.1函数 subst

函数作用:完成字符串的替换操作。
调用形式如下:

$(subst <from>,<to>,<text>)
#from:字符串中被替换的字符串
#to:替换的字符串
#text:字符串

#举例
$(subst aaa,AAA,aaabbb)
#也就是将字符串aaa替换成AAA,替换后字符串为AAAbbb

5.2函数 patsubst

函数作用:用来完成模式字符串替换
调用形式如下:

$(patsubst <pattern>,<replacement>,<text>)

$(patsubst %.c,%.o,a.c b.c c.c)
#将字符串“ a.c b.c c.c”中的所有符合“ %.c”的字符串,替换为“ %.o”,替换完成以后的字符串为“ a.o b.o c.o”

5.3函数 dir

函数作用: 用来获取目录
调用形式如下:

$(dir <names…>)

$(dir </src/a.c>)
#提取文件“ /src/a.c”的目录部分,也就是“ /src”

5.4函数 notdir

函数作用:除文件中的目录部分,提取文件名
调用形式如下:

$(notdir <names…>)

$(notdir </src/a.c>)
#提取文件“ /src/a.c”中的非目录部分,也就是文件名“ a.c”。

5.5函数 foreach

函数作用:函数用来完成循环
调用形式如下:

$(foreach <var>, <list>,<text>)
#把参数<list>中的单词逐一取出来放到参数<var>中,然后再执行<text>所包含的表达式。每次<text>都会返回一个字符串,循环的过程中,
# <text>中所包含的每个字符串会以空格隔开,最后当整个循环结束时, <text>所返回的每个字符串所组成的整个字符串将会是函数 foreach
# 函数的返回值。

5.6函数 wildcard

函数作用:通配符“ %”只能用在规则中,只有在规则中它才会展开,如果在变量定义和函数使用时,通配符不会自动展开,这个时候就要用到函数 wildcard
调用形式如下:

$(wildcard PATTERN…)

$(wildcard *.c)
#上面的代码是用来获取当前目录下所有的.c 文件,类似“%”

六、综合例程

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/delay	\
				bsp/LED

SRCDIRS		:=	project		\
				bsp/clk 	\
				bsp/delay	\
				bsp/LED 	


INCLUDE		:=	$(patsubst %, -I %, $(INCDIRS))

#SFILES		:= 	$(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S))
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)

print :
		@echo	INCLUDE	 =	$(INCLUDE)
		@echo 	SFILES = $(SFILES)
		@echo 	CFILES = $(CFILES)
		@echo 	SFILENDIR	=	$(SFILENDIR)
		@echo	CFILENDIR	=	$(CFILENDIR)
		@echo	SOBJS	=	$(SOBJS)
		@echo	COBJS	=	$(COBJS)
		@echo	OBJS	=	$(OBJS)

例程中的makefile适用于大型工程管理,只需要添加源文件路径和头文件路径,不需要改动太多的makefile代码,即可实现工程代码的自动化编译。

おすすめ

転載: blog.csdn.net/weixin_51363326/article/details/121127034