Makefile 讲解

一、为什么要有Makefile

          Makefile可以很好的组织和管理程序和文件。特别是对一个大型工程而言,Makefile 关系到了整个工程的编译规则。一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,Makefile 定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为Makefile 就像一个 shell 脚本一样,其中也可以执行操作系统的命令。

          Makefile 带来的好处是“自动化编译”,一旦写好,只需要一个 make 命令,整个工程完全自动编译,极大的提高了软件开发的效率。
 

一、Makefile的基本规则

目标:依赖1 依赖2
[tab]命令
上面就是Makefile的基本语法规则

当依赖比目标新时,它就会执行下面的命令。

 

二、使用

下面我以arm的交叉编译工具为例来说明make的使用

假设我们有连个文件start.c和main.c。现在我们要使用arm的交叉编译工具编译他们

1.先看一下我们的最初版本,没有makefile时我们怎么做?

#预处理+编译+汇编   其中gcc的-c参数是指明,只编译不连接 -o参数是指明输出文件的名字
          arm-none-linux-gnueabi-gcc -c start.S -o start.o
          arm-none-linux-gnueabi-gcc -c main.c -o main.o
#连接,生成可执行文件  -Ttext 是指明.text段的起始地址
          arm-none-linux-gnueabi-ld -Ttext 0xd0020010  -o main start.o main.o
直接命令行敲,每次更改文件编译都要敲这么多命令,这还是在只有两个文件的情况下,如果有成百上千个文件,这明显不可能让人做。

2.把上面的命令行写成一个shell脚本过makefile文件,每次执行前都执行一次shell脚本或make指令

用shell脚本的实现

为了满足make规则,编写Makefile文

main.elf:start.o main.o
        arm-none-linux-gnueabi-gcc -c start.S -o start.o
        arm-none-linux-gnueabi-gcc -c main.c -o main.o
        arm-none-linux-gnueabi-ld -Ttext 0xd0020010  -o main start.o main.o


 

可以对比看到shell脚本和make文件都可以生成最终的main.elf文件。

但唯一有一点可以从上面看到,如果main.elf的依赖是最新的话,再次执行make不会再执行命令。

而shell则会无论是否是最新的都执行命令。

当文件 特别多的时候,make会编译有改动过的文件,而shell是 所有都编译,通常我们该软件,都只会该很小一部分,这样就能节约大量的时间。

 

突然发现,即使改了一个文件,也会编译两个文件。所以我们的makefile文件继续改进。

改进为如下:每个文件都有自己的依赖,当它的依赖没有改动时,则不指行命令。

main.elf: start.o main.o
    arm-none-linux-gnueabi-ld -Ttext 0xd0020010  -o main start.o main.o
start.o:start.S
    arm-none-linux-gnueabi-gcc -c start.S -o start.o
main.o: main.c
    arm-none-linux-gnueabi-gcc -c main.c -o main.o


 

3.接下来我们增加两个文件

main.elf:start.o main.o a.o b.o
     arm-none-linux-gnueabi-ld -Ttext 0xd0020010  -o main start.o main.o a.o b.o
start.o:start.S
        arm-none-linux-gnueabi-gcc -c start.S -o start.o
main.o:main.c
        arm-none-linux-gnueabi-gcc -c main.c -o main.o
a.o:a.S
        arm-none-linux-gnueabi-gcc -c a.c -o a.o
b.o:b.S
        arm-none-linux-gnueabi-gcc -c b.c -o b.o


可以发现,随着文件数量的增加,我们增加命令,又要增加依赖关系,还得增加 ld后面的链接个数

能不能有更简便的方法,减少重复性的工作呢?

肯定是有的。

4.自动化变量

4.1、%  作为统配符,可以表示所有和它文件一类的文件如 %.c代表当前目录下的所有.c文件

make中可以有如下命令

          %.o: %.c
即.o文件依赖于当前文件下的所有.c文件

类似的也有.S

          %.o : %.S
4.2、变量

简单变量:如下,即A的值即刻被确定,比如下一行使用了A,则A的值就是xxx

A := xxx   
延时变量:如下,A的值在整Makefile文件浏览一遍后,使用最后一个定义的即下面的使用A的值为ddd

A = xxx
使用A
A = ddd
通常绝大多数都是使用简单变量

4.3、变量的使用

A := xxx   
 
C = $A
C = $(A)
4.4、目标的通配符

@

4.5、第一个依赖的通配符

<

4.6上面变量和通配符的结合

%.o    : %.c
    arm-linux-gcc -o $@ $< -c
即用过gcc命令,根据输入的依赖所有的.c文件生成对应的.o文件

相应的也有.S

%.o    : %.S
    arm-linux-gcc -o $@ $< -c
5.伪目标

即该目标不存在,但可以方便使用,比如下面的all:

all:    start.o main.o a.o b.o
     arm-none-linux-gnueabi-ld -Ttext 0xd0020010  -o main start.o main.o a.o b.o
 
%.o    : %.c
    arm-linux-gcc -o $@ $< -c
 
%.o    : %.S
    arm-linux-gcc -o $@ $< -c


增加.PHONY声明为伪目标

.PHONY:all
all:    start.o main.o a.o b.o
     arm-none-linux-gnueabi-ld -Ttext 0xd0020010  -o main start.o main.o a.o b.o
 
%.o    : %.c
    arm-linux-gcc -o $@ $< -c
 
%.o    : %.S
    arm-linux-gcc -o $@ $< -c


增加清除操作

.PHONY:all
all:    start.o main.o a.o b.o
    arm-none-linux-gnueabi-ld -Ttext 0xd0020010  -o main start.o main.o a.o b.o
 
%.o    : %.c
    arm-none-linux-gnueabi-gcc -o $@ $< -c
 
%.o    : %.S
    arm-none-linux-gnueabi-gcc -o $@ $< -c
 
.PHONY:clean
clean:
    *.o *.elf -f


6.上面的已经比较完美了,但任然有缺点,比如all的依赖和ld命令后的东西重复了,每增加一个文件,都要改两个地方。

所以继续优化:

使用$^表示,它前面最近的目标的所有依赖即:all后面的start.o main.o a.o b.o

 

.PHONY:all
all:    start.o main.o a.o b.o
    arm-none-linux-gnueabi-ld -Ttext 0xd0020010  -o main $^
 
%.o    : %.c
    arm-none-linux-gnueabi-gcc -o $@ $< -c
 
%.o    : %.S
    arm-none-linux-gnueabi-gcc -o $@ $< -c
 
.PHONY:clean
clean:
    *.o *.elf -f


7.方便增加文件,继续优化

增加变量obj方便增加文件或增加其它目录下的文件

 

obj += start.o main.o a.o 
obj += b.o
obj += ./xxxx/cccc.o
obj += ./aaaa/dddd.o
 
.PHONY:all
all:  $(obj)
    arm-none-linux-gnueabi-ld -Ttext 0xd0020010  -o main $^
 
%.o    : %.c
    arm-none-linux-gnueabi-gcc -o $@ $< -c
 
%.o    : %.S
    arm-none-linux-gnueabi-gcc -o $@ $< -c
 
.PHONY:clean
clean:
    *.o *.elf -f


8.上面的已经算可以常规使用了

发现增加伪目标后,连接命令每次都要执行。

所以看来还有可以继续优化的地方。

分析上面的Makefile,发现,all是一个伪目标,所以它下面的命令一点会被执行,而向想要ld命令不被执行,即它的目标必须不是伪目标,我们让这里的真实目标main做目标。

如下:all并不包含任何命令,make后,all它会首先检查main,是不是最新的,进而mian的依赖obj检查他们的依赖是不是最新的。

 

obj += start.o main.o a.o 
obj += b.o
obj += ./xxxx/cccc.o
obj += ./aaaa/dddd.o
 
.PHONY:all
all: main 
 
main:$(obj)
    arm-none-linux-gnueabi-ld -Ttext 0xd0020010  -o main $^
 
%.o    : %.c
    arm-none-linux-gnueabi-gcc -o $@ $< -c
 
%.o    : %.S
    arm-none-linux-gnueabi-gcc -o $@ $< -c
 
.PHONY:clean
clean:
    *.o *.elf -f


9.标准化,增加可移植性

CC := arm-none-linux-gnueabi-gcc
LD := arm-none-linux-gnueabi-ld
 
 
obj += start.o main.o a.o 
obj += b.o
obj += ./xxxx/cccc.o
obj += ./aaaa/dddd.o
 
.PHONY:all
all: main 
 
main:$(obj)
    $(LD) -Ttext 0xd0020010  -o main $^
 
%.o    : %.c
    $(CC) -o $@ $< -c
 
%.o    : %.S
    $(CC) -o $@ $< -c
 
.PHONY:clean
clean:
    *.o *.elf -f


 
————————————————
版权声明:本文为CSDN博主「to_run_away」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_16777851/article/details/81588193

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SPDK(Storage Performance Development Kit)是一个开源的高性能存储软件开发工具包,提供了一组用于开发高性能存储系统的API和工具。SPDK的Makefile文件是一个非常复杂的文件,以下是对SPDK的Makefile进行简要讲解: 1. 定义变量 Makefile首先会定义一系列变量,用于设置编译器、编译选项、目标文件、依赖库等等。例如: ``` CC = gcc CFLAGS += -g -O2 LIBS += -lpthread -lrt TARGET = spdk ``` 在上述代码中,CC变量定义了编译器为gcc,CFLAGS变量定义了编译选项为-g -O2,LIBS变量定义了依赖库为pthread和rt,TARGET变量定义了目标文件为spdk。 2. 定义规则 Makefile接下来会定义一系列规则,用于指定如何编译源代码、如何链接目标文件、如何清除编译生成的文件等等。例如: ``` $(TARGET): $(OBJS) $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< ``` 在上述代码中,$(TARGET)规则指定了如何链接目标文件,依赖于$(OBJS)变量中定义的一组目标文件,使用$(LDFLAGS)变量中定义的链接选项和$(LIBS)变量中定义的依赖库进行链接。%.o规则指定了如何编译C源代码文件,使用$(CFLAGS)变量中定义的编译选项进行编译,并将生成的目标文件保存为.o文件。 3. 定义依赖 Makefile接下来会定义一系列依赖关系,用于指定哪些源代码文件依赖于哪些头文件、哪些目标文件依赖于哪些源代码文件等等。例如: ``` spdk.o: spdk.c spdk_internal.h spdk/env.h spdk/event.h spdk/queue.h spdk/thread.h ``` 在上述代码中,spdk.o目标文件依赖于spdk.c、spdk_internal.h、spdk/env.h、spdk/event.h、spdk/queue.h和spdk/thread.h等一组源代码文件和头文件。 4. 定义清理规则 Makefile最后会定义一系列清理规则,用于删除编译生成的文件。例如: ``` clean: rm -f $(TARGET) $(OBJS) ``` 在上述代码中,clean规则指定了如何删除$(TARGET)变量和$(OBJS)变量中定义的一组文件。 以上是SPDK的Makefile文件的基本结构和内容,由于SPDK是一个非常复杂的软件工具包,其Makefile文件很复杂,还包括了很多其他的规则和变量定义等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值