makefile

目录

1、Makefile 格式

2、变量

3、赋值符

①“ = ”

②“ := ”

③“?=”

④“+=”

4、模式规则“%”

5、自动化变量

①$^:表示所有的依赖文件,使用空格分开

②$@:表示规则中的目标文件

③$<:表示依赖文件中的第一个文件名

6、伪目标

综合实战


——这篇文章主要是记录常用的

1、Makefile 格式

目标  :  依赖文件....

        命令

        注意:每条命令必须以 TAB 键开始,不能使用空格!

举例1:

main : main.o 1.o 2.o

        gcc main.o 1.o 2.o -o main

        目标是 main, main.o、 1.o 和 2.o 是生成 main 的依赖文件

        那么这只是将.o文件链接,如果还没生成呢?下面继续看例子2:(#为注释符)

main: main.o 1.o 2.o                        #第一条规则
    gcc main.o 1.o 2.o -o main
main.o: main.c                                #第二条规则
    gcc -c main.c
1.o: 1.c                                            #第三条规则
    gcc -c 1.c
2.o: 2.c                                            # 第四条规则
    gcc -c 2.c

clean:                                                #第五条规则
    rm *.o
    rm main

make执行步骤如下:
        第一条规则的目标成为默认目标,在第一次编译的时候由于 main 还不存在,因此第一条规则会执行,第一条规则依赖于文件 main.o、 1.o 和 2.o 这个三个.o 文件,这三个.o 文件目前还都没有,因此必须先更新这三个文件。
        make 会查找以这三个.o 文件为目标的规则并执行,发现更新 main.o 的是第二条规则,因此会执行第二条规则,规则的命令就是不链接编译 main.c,生成 main.o,接着同理,发现第三条和第四条规则后也陆续生成了1.o和2.o文件

        最后一个规则目标是 clean,它没有依赖文件,因此会默认为依赖文件都是最新的,所以这条规则的命令不会执行,当使用命令“make clean”的时候,才会执行,会删除当前目录下所有的.o 文件以及 main,完成工程的清理。

2、变量

继续用前面例子1

main : main.o 1.o 2.o

        gcc main.o 1.o 2.o -o main

        三个依赖文件输入了两遍,这种重复输入的工作就会非常费时间,且会出现输错的风险

        为了解决这个问题,Makefile加入了变量支持,变量都是字符串,类似 C 语言中的宏

直接上例子

objects = main.o 1.o 2.o        #定义了一个变量 objects,并赋值为字符串“main.o 1.o 2.o"
main: $(objects)
    gcc -o main $(objects)

         Makefile 中变量的引用方法是“$(变量名)”,$(objects)等于 main.o 1.o 2.o ,这样方便多了

3、赋值符

        在Makefile中有4种不同意义的赋值符,下面一一分析一下

①“ = ”

        在上面例子中也有出现,下面讨论一下细节

#假如在makefile中有下面这几句,请问getname是123还是456?

name = 123
getname = $(name)
name = 456

        getname是456,赋值符“="可以将变量的真实值推到后面去定义,也就是引用的真实值取决于所引用变量的最后一次赋值

②“ := ”

        用上面的例子修改分析

#假如在makefile中有下面这几句,请问getname是123还是456?

name = 123
getname := $(name)
name = 456

          getname是123,“:=”不会使用后面定义的变量,只能使用前面已经定义好的

③“?=”

        getname ?= 789

         如果变量 getname 前面没有被赋值,那么此变量就是“789”;如果前面已经赋过值了,那么就使用前面赋的值。

④“+=”

#变量追加

objects = main.o 1.o
objects += 2.o

        变量 objects 的值为“main.o 1.o”,后面我们给他追加了一个“2.o”,因此变量 objects 变成了“main.o 1.o 2.o”

4、模式规则“%”

        上面写的都是一般规则的例子,模式规则中,至少在规则的目标定定义中要包涵“%”

%.o : %.c
        命令

         ”%.o”就是所有的以.o 结尾的文件;“%.c”就是所有的以.c 结尾的文件,类似与通配符,比如a.%.c 就表示以 a.开头,以.c 结束的所有文件

这样我们就可以修改优化上面的例子2了,这里称为例子3

objects = main.o 1.o 2.o                    #定义变量
main: $(objects)                               #引用变量
    gcc  $(objects) -o main
%.o: %.c                                            #模式规则
    #要执行的命令,这里先不写,下面再优化

clean:                                                
    rm *.o
    rm main 

5、自动化变量

        在 Makefile 中自动化变量是一种特殊的变量,它们具有特殊的含义,可以在 Makefile 的编写中使用它们来简化代码的编写和提高代码的可读性。以下是一些常用的自动化变量及其含义:

①$^:表示所有的依赖文件,使用空格分开

假设有以下 Makefile 规则如下:

```
object_files: file1.c file2.c file3.c
        gcc $^ -o object_files
```

         $^ 表示所有的依赖文件 file1.c、file2.c 和 file3.c,make命令将把这些文件编译并链接为一个输出文件 object_files

②$@:表示规则中的目标文件

假设有以下 Makefile 规则如下:

```

hello: main.c 1.c
        gcc   $^ -o $@
```

         在执行make命令时,当hello目标需要被构建时,它会执行上述规则,即编译main.c和1.c,并将它们链接起来生成可执行文件hello。$@就代表了目标文件hello,因此可以用$@来代表目标文件的名字,而不必手动输入它,这提高了makefile的可读性和灵活性

        在这就可以把两个自动化变量合起来用,修改第一个例子如下

(例1)假设有以下 Makefile 规则如下:

```
object_files: file1.c file2.c file3.c
        gcc $^ -o $@
```

        这样就方便了一些,不用再输入长的字符串

③$<:表示依赖文件中的第一个文件名

        如果依赖文件是以模式(即“%” )定义的,那么“$<”就是符合模式的一系列的文件集合

假设有以下 Makefile 规则如下:

```
main.o: main.c header.h
        gcc -c $<
```

         `$<` 表示依赖文件中的第一个文件名 `main.c`,所以最后一句等价于:gcc -c main.c

下面继续优化例子3

main:  main.o 1.o 2.o                          
    gcc  $^ -o $@                                   #自动化变量
%.o: %.c                                            #模式规则
    gcc -c $<                                        #自动化变量

clean:                                                
    rm *.o
    rm main 

        这里将三个 C 语言源文件 main.c1.c2.c 编译成可执行文件,并将输出文件名设置为 main, 规则使用"$^"表示所有依赖项,"-o $@” 表示输出文件名

        main目标文件依赖.o文件,没有.o文件就会找到“%.o: %.c”这条规则,全部.o文件依赖.c文件,通过该规则下的命令来编译出.o文件,这样就可以输出main目标文件了

6、伪目标

        伪目标主要是为了避免 Makefile 中定义的执行命令的目标和工作目录下的实际文件出
现名字冲突,比如我们清理功能

clean:                                                
    rm *.o
    rm main 

         输入“make clean”以后,后面的“rm *.o”和“rm main”总是会执行。可是如果在工作目录下创建一个名为“clean”的文件,那就不一样了,当执行“make clean”的时候,规则因为没有依赖文件,所以目标被认为是最新的,因此后面的 rm 命令也就不会执行,为了避免这个问题,我们可以将 clean 声明为伪目标:

.PHONY : clean

clean:                                                
    rm *.o
    rm main 

         就加一行声明就可以了,声明 clean 为伪目标,声明 clean 为伪目标以后不管当前目录下是否存在名为“clean”的文件,输入“make clean”的话规则后面的 rm 命令都会执行

综合实战

        假如文件工程目录如下

 利用makefile编译出一个bin文件,编写的makefile如下,详细解释都在包含在内容里面

#自定义变量
CROSS_COMPILE ?= arm-linux-gnueabihf-
TARGET ?= bsp
#默认的编译器名称变量
CC := $(CROSS_COMPILE)gcc
#默认的链接器名称变量
LD := $(CROSS_COMPILE)ld
#ELF文件转换为二进制bin文件
OBJCOPY := $(CROSS_COMPILE)objcopy
#反汇编器和调试器
OBJDUMP := $(CROSS_COMPILE)objdump

#常用的默认头文件变量,指定编译器在编译过程中需要搜索的头文件目录列表,每个目录之间用空格分隔
INCDIRS := imx6ull \
			bsp/clk \
			bsp/led \
			bsp/delay

#常用的默认源文件变量,指定编译器在编译过程中需要搜索的源文件目录列表,每个目录之间用空格分隔
SRCDIRS := project \
			bsp/clk \
			bsp/led \
			bsp/delay

#自定义变量
#patsubst 字符串替换函数:$( patsubst 原模式, 目标模式, 文件列表)
#从文件列表中查找出符合原模式文件类型的文件,然后一一替换成目标模式,“%”,表示任意长度的字符串
#也就是把全部路径抽出来加上-I
##-I 是编译器的一个选项,用于告诉编译器在编译过程中需要搜索头文件的目录
INCLUDE := $(patsubst %, -I %, $(INCDIRS))

#自定义变量
#dir:dir是一个内置变量,默认情况下make会将工作目录设置为当前目录
#$(dir)/*.S:代表在当前执行make命令的工作目录下所有以.S为后缀名的文件
#wildcard函数:$(wildcard 指定文件类型),用于查找指定目录下指定类型的文件,函数参数:目录+文件类型
#$(wildcard $(dir)/*.S):查找当前执行make命令的工作目录下所有以.S为后缀名的文件
#foreach循环函数:$(foreach var,list,text):把参数<list>中的单词逐一取出来放到参数<var>中,
#										然后再执行<text>所包含的表达式,
#										每次<text>都会返回一个字符串,循环的过程中,
#										<text>中所包含的每个字符串会以空格隔开,
#										最后当整个循环结束时, <text>所返回的每个字符串组成 foreach 函数的返回值
#也就是(当前工作目录下,每一个目录路径,查找这个路径下.S文件),获取到.S文件存在的路径
SFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S))

#同理,这次获取保存工程中的是所有的.c文件保存的路径
CFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))

#自定义变量
#notdir提取函数:$(notdir names ...),从names中提取出文件名非目录部分,就是把路径去掉就保留文件名
#把上面获取到.S文件的路径去掉
SFILENDIR := $(notdir $(SFILES))

#同理,把上面获取到.c文件的路径去掉
CFILENDIR := $(notdir $(CFILES))

#自定义变量
#patsubst 字符串替换函数:$( patsubst 原模式, 目标模式, 文件列表)
#从文件列表中查找出符合原模式文件类型的文件,然后一一替换成目标模式,“%”,表示任意长度的字符串
#$(SFILENDIR:.S=.o):进行变量替换,会将这些.S文件编译成对应的.o目标文件(注意替换不是修改文件后缀的意思,可以理解为编译)
#obj/%:把.o文件的都添加obj路径,SOBJS就是这些文件的文件路径
SOBJS := $(patsubst %, obj/%, $(SFILENDIR:.S=.o))

#同理,把.c文件的都替换成.c文件,添加obj路径
COBJS := $(patsubst %, obj/%, $(CFILENDIR:.c=.o))

#自定义变量
#一个包含所有编译目标文件的列表集合,把全部.o文件都并成一个总的编译目标文件列表
OBJS := $(SOBJS) $(COBJS)

#VPATH是一个特殊的Makefile变量,代表了Makefile的搜索路径
#将所有的源代码路径添加到Makefile的搜索路径中,以便在编译时能够正确地找到需要的头文件和源文件
#指定make在进行依赖文件搜索时,需要搜索的路径列表
VPATH := $(SRCDIRS)

#单独一个冒号是一个规则的开始,表示目标文件和依赖关系的声明
#$(TARGET).bin 是目标文件,$(OBJS) 是依赖文件
#这个规则告诉make如何生成目标文件$(TARGET).bin
#告诉make当$(OBJS)中任何一个文件被修改时,重新生成$(TARGET).bin
$(TARGET).bin : $(OBJS)

#目的:将所有输入文件编译成一个名为 .elf 的目标文件
#"-T"通常是一个gcc的命令行选项,用于指定链接器使用的链接脚本文件
#"-Timx6ull.lds"表示将链接器脚本文件指定为"Timx6ull.lds"
#$^:表示所有的依赖文件
	$(LD) -Timx6ull.lds -o $(TARGET).elf $^

#目的:将 elf 文件转换为二进制文件,并去除elf 文件中的代码段和数据段等信息删除,从而生成一个更小的二进制可执行文件
#-O binary:将目标文件转换为纯二进制文件格式,即可执行bin文件,意味着去掉所有目标文件的元数据和头部信息,只保留代码和数据部分
#-S:去掉所有的符号表和调试信息
#$(TARGET).elf:指定了要操作的目标文件
#$@:表示规则中的目标文件,即$(TARGET).bin
	$(OBJCOPY) $(TARGET).elf -O binary -S $@

#将生成的 ELF 文件反汇编成可读的汇编代码并输出到一个.dis文本文件中
#-D: 参数表示要进行反汇编
#-m arm: 参数表示使用 ARM 体系结构进行反汇编
#$(TARGET).elf:指定了要操作的目标文件
#>:重定向到$(TARGET).dis文件中
#$(TARGET).dis:输出的汇编代码的文件名
	$(OBJDUMP) $(TARGET).elf -D -m arm > $(TARGET).dis

#将所有以.S结尾的汇编文件编译成.o文件,放在obj目录下
$(SOBJS) : obj/%.o : %.S

#CC:指定的编译器进行编译
#$<:表示依赖文件中的第一个文件名, 即 %.S
#$(INCLUDE):指定头文件路径的变量
#Wall:开启编译器的所有警告提示
#nostdlib:不使用标准库,可以减小可执行文件的大小
#O2:幅度更大的优化,生成的可执行效率更高,但是整个编译过程会很慢
#-c:编译源文件为目标文件,而不进行链接操作
#-o: 表示指定输出文件名
#$@:表示规则中的目标文件,即 obj/%.o
	$(CC) $< $(INCLUDE) -Wall -nostdlib -O2 -c -o $@
#同理,将所有以.c结尾的源文件编译成.o文件,放在obj目录下
$(COBJS) : obj/%.o : %.c
	$(CC) $< $(INCLUDE) -Wall -nostdlib -O2 -c -o $@

.PHONY:clean
clean:
	rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)

执行结果,已经生成了bin文件和反汇编文件

 

 执行清理,就把编译生成的全部文件删除了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值