• makefile文件中定义了一系列的规则来指定, 哪些文件需要先编译, 哪些文件需要后编译, 哪些文件需要重新编译, 甚至于进行更复杂的功能操作, 因为makefile就像一个Shell脚本一样, 其中也可以执行操作系统的命令. makefile带来的好处就是——“自动化编译”, 一旦写好, 只需要一个make命令, 整个工程完全自动编译, 极大的提高了软件开发的效率.
  • make是一个命令工具, 是一个解释makefile中指令的命令工具, 一般来说, 大多数的IDE都有这个命令, 比如:Visual C++的nmake, Linux下GNU的make. 可见, makefile都成为了一种在工程方面的编译方法.
  • makefile文件中会使用gcc编译器对源代码进行编译, 最终生成可执行文件或者是库文件.

makefile文件的命名:makefile或者Makefile

1. makefile的基本规则

makefile由一组规则组成,规则如下:

  1. 目标:依赖
  2. tab)命令

makefile基本规则三要素:

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

当前目录下有main.c fun1.c fun2.c sum.c, 根据这个基本规则编写一个简单的makefile文件, 生成可执行文件main.

第1个版本的makefile:

  1. main:main.c fun1.c fun2.c sum.c
  2. gcc main.c fun1.c fun2.c sum.c -I./ -o main

image.png
缺点: 效率低, 修改一个文件, 所有的文件会全部重新编译.

2. makefile工作原理

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

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

image.png

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

image.png

总结:

  • 分析各个目标和依赖之间的关系
  • 根据依赖关系自底向上执行命令
  • 根据依赖文件的时间和目标文件的时间确定是否需要更新
  • 如果目标不依赖任何条件, 则执行对应命令, 以示更新(如:伪目标)

第2个版本的makefile:

  1. main:main.o fun1.o fun2.o sum.o
  2. gcc main.o fun1.o fun2.o sum.o -I./ -o main
  3. main.o:main.c
  4. gcc -c main.c -I./ -o main.o
  5. fun1.o:fun1.c
  6. gcc -c fun1.c -I./ -o fun1.o
  7. fun2.o:fun2.c
  8. gcc -c fun2.c -I./ -o fun2.o
  9. sum.o:sum.c
  10. gcc -c sum.c -I./ -o sum.o

image.png
缺点: 冗余, 若.c文件数量很多, 编写起来比较麻烦.

3. makefile中的变量

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

makefile有三种类型的变量:

  1. 普通变量
  2. 内置变量
  3. 自动变量

普通变量

  • 变量定义直接用 =
  • 使用变量值用 $(变量名)

如:下面是变量的定义和使用,定义了两个变量: foo、bar, 其中bar的值是foo变量值的引用。

  1. foo = abc // 定义变量并赋值
  2. bar = $(foo) // 使用变量, $(变量名)

内置变量

除了使用用户自定义变量, makefile中也提供了一些变量(变量名大写)供用户直接使用, 我们可以直接对其进行赋值:

  1. CC = gcc #arm-linux-gcc
  2. CPPFLAGS : C预处理的选项 -I
  3. CFLAGS: C编译器的选项 -Wall -g -c
  4. LDFLAGS : 链接器选项 -L -l

自动变量

  1. $@: 表示规则中的目标
  2. $<: 表示规则中的第一个条件
  3. $^: 表示规则中的所有条件, 组成一个列表, 以空格隔开, 如果这个列表中有重复的项则消除重复项。

特别注意:自动变量只能在规则的命令中使用

模式规则

至少在规则的目标定义中要包含’%’, ‘%’表示一个或多个, 在依赖条件中同样可以使用’%’, 依赖条件中的’%’的取值取决于其目标:
比如: main.o:main.c fun1.o: fun1.c fun2.o:fun2.c, 说的简单点就是: xxx.o:xxx.c

第3个版本的makefile:

  1. target=main
  2. object=main.o fun1.o fun2.o sum.o
  3. CC=gcc
  4. CPPFLAGS=-I./
  5. $(target):$(object)
  6. $(CC) $^ -o $@
  7. %.o:%.c
  8. $(CC) -c $< $(CPPFLAGS) -o $@

image.png

4. makefile函数

在makefile中所有的函数都是有返回值的。

makefile中最常用的两个函数

  1. wildcard – 查找指定目录下的指定类型的文件

例:src=$(wildcard *.c) //找到当前目录下所有后缀为.c的文件,赋值给src

  1. patsubst – 匹配替换

例:obj=$(patsubst %.c,%.o, $(src)) //把src变量里所有后缀为.c的文件替换成.o

当前目录下有main.c fun1.c fun2.c sum.c
src=$(wildcard *.c) 等价于src=main.c fun1.c fun2.c sum.c
obj=$(patsubst %.c,%.o, $(src))等价于obj=main.o fun1.o fun2.o sum.o

第4个版本的makefile:

  1. target=main
  2. str=$(wildcard *.c)
  3. object=$(patsubst %.c,%.o, $(str))
  4. CC=gcc
  5. CPPFLAGS=-I./
  6. $(target):$(object)
  7. $(CC) $^ -o $@
  8. %.o:%.c
  9. $(CC) -c $< $(CPPFLAGS) -o $@

缺点: 每次重新编译都需要手工清理中间.o文件和最终目标文件

5. makefile的清理操作

用途: 清除编译生成的中间.o文件和最终目标文件
make clean 如果当前目录下有同名clean文件,则不执行clean对应的命令

伪目标声明:
.PHONY:clean
声明目标为伪目标之后, makefile将不会检查该目标是否存在或者该目标是否需要更新

clean命令中的特殊符号:

  1. -此条命令出错,make也会继续执行后续的命令。如:“-rm main.o

    rm -f: 强制执行,比如若要删除的文件不存在使用-f不会报错

  2. @不显示命令本身, 只显示结果。如:@echo clean done

命令:
make:默认执行第一个出现的目标, 可通过make 目标指定要执行的目标
make -f : -f 执行一个makefile文件名称, 使用make执行指定的makefile: make -f mainmak

  1. target=main
  2. str=$(wildcard *.c)
  3. object=$(patsubst %.c,%.o, $(str))
  4. CC=gcc
  5. CPPFLAGS=-I./
  6. $(target):$(object)
  7. $(CC) $^ -o $@
  8. %.o:%.c
  9. $(CC) -c $< $(CPPFLAGS) -o $@
  10. .PHONY:clean
  11. clean:
  12. rm $(object) $(target)

image.png

在makefile的第5个版本中, 综合使用了变量, 函数, 模式规则和清理命令, 是一个比较完善的版本.