makefile简介

1.0 介绍

  为了解决编译一个工程非常繁琐这个问题,前人就给我们发明了编译辅助工具 make 工具,它的编译思路是非常简单的,它会在编译之前先比较哪个文件的时间发生了改变,如果说这个文件它修改的时间要晚于编译生成的文件,那么它就会按照要求重新构建这些文件,而不是说再浪费时间重新构建其他的文件了。
  make 工具是编译辅助工具,用来解决使用命令编译工程非常繁琐的问题。
  Makefile 就是描述了整个工程编译连接等规则的文件。
  我们在终端输入完 make 命令之后,调用 make工具,make 就会在当前目录按照文件名就会找 makefile 文件,Makefile 的命名必须是 makefile 或Makefile ,m 大写小写都是可以的。

2.0 语法

目标…... : 依赖文件集合……
    命令 1
    命令 2
……

  如:

main : main.o input.o calcu.o
    gcc -o main main.o input.o calcu.o

  这条规则的目标是 main,main.o、input.o 和 calcu.o 是生成 main 的依赖文件,如果要更新目标 main,就必须先更新它的所有依赖文件,如果依赖文件中的任何一个有更新,那么目标也必须更新,“更新”就是执行一遍规则中的命令列表。
  命令列表中的每条命令必须以 TAB 键开始,不能使用空格!
  make 命令会为 Makefile 中的每个以 TAB 开始的命令创建一个 Shell 进程去执行。
  Makefile 中可以写注释,注释开头要用符号“#”。

 clean:
    rm *.o
    rm main

  可通过make clean执行其下的命令。
make 的执行流程:
  make 命令会在当前目录下查找以 Makefile或makefile命名的文件。
  当找到 Makefile 文件以后就会按照 Makefile 中定义的规则去编译生成最终的目标文件。
  当发现目标文件不存在,或者目标所依赖的文件比目标文件新(也就是最后修改时间比目标文件晚)的话,就会执行后面的命令来更新目标。

3.0 变量

objects = main.o input.o calcu.o
main: $(objects)
    gcc -o main $(objects)

  其中将objects 赋值为"main.o input.o calcu.o", 后续通过$(变量名)来引用。

4.0 赋值

4.1 “=”

  “最后赋值”,指借助另外一个变量,可以将变量的真实值推到后面去定义。也就是变量的真实值取决于它所引用的变量的最后一次有效值。如:

name = zzk
curname = $(name)
name = zuozhongkai
print:
    @echo curname: $(curname)
# 结果为输出 curname: zuozhongkai

  “echo”前面加了个“@”符号,因为 Make 在执行的过程中会自动输出命令执行过程,在命令前面加上“@”的话就不会输出命令执行过程。

4.2 “:=”

  立即赋值,这是因为赋值符“:=”不会使用后面定义的变量,只能使用前面已经定义好的。如4.1中若使用:=则输出 curname: zzk。

4.3 “?=”

curname ?= zuozhongkai

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

4.4 “+=”

追加。如:

objects = main.o inpiut.o
objects += calcu.o

  一开始变量 objects 的值为“main.o input.o”,后面我们给他追加了一个“calcu.o”,因此变量 objects 变成了“main.o input.o calcu.o”,这个就是变量的追加。

5.0 模式匹配

5.1 %

  %通配符匹配,表示长度任意的非空字符串。

5.2 自动变量匹配

自动化变量 描述
$@ 规则中的目标集合,在模式规则中,如果有多个目标的话,“$@”表示匹配模式中定义的目标集合。
$% 当目标是函数库的时候表示规则中的目标成员名,如果目标不是函数库文件,那么其值为空
$< 依赖文件集合中的第一个文件,如果依赖文件是以模式(即“%”)定义的,那么“$<”就是符合模式的一系列的文件集合。
$? 所有比目标新的依赖目标集合,以空格分开。
$> 所有依赖文件的集合,使用空格分开,如果在依赖文件中有多个重复的文件,"$^”会去除重复的依赖文件,值保留一份。
$+ 和“$^”类似,但是当依赖文件存在重复的话不会去除重复的依赖文件。
$* 这个变量表示目标模式中"%"及其之前的部分,如果目标是 test/a.test.c,目标模式为 a.%.c,那么“$*”就是 test/a.test。

如:

1 objects = main.o input.o calcu.o
2 main: $(objects)
3    gcc -o main $(objects)
4 
5 %.o : %.c
6    gcc -c $<
7 
8 clean:
9    rm *.o
10    rm main

等价于

1 main: main.o input.o calcu.o
2    gcc -o main main.o input.o calcu.o
3 main.o: main.c
4    gcc -c main.c
5 input.o: input.c
6    gcc -c input.c
7 calcu.o: calcu.c
8    gcc -c calcu.c
9 
10 clean:
11    rm *.o
12    rm main

5.3 伪目标

  Makefile 有一种特殊的目标——伪目标,一般的目标名都是要生成的文件,而伪目标不代表真正的目标名,在执行 make 命令的时候通过指定这个伪目标来执行其所在规则的定义的命令。
  使用伪目标主要是为了避免 Makefile 中定义的执行命令的目标和工作目录下的实际文件出现名字冲突,有时候我们需要编写一个规则用来执行一些命令,但是这个规则不是用来创建文件的。
  使用伪目标时要防止伪目标的名称与当前目录下文件名称重复,否则会因为目标下没有依赖文件,所以目标被认为是最新的,因此后面的 命令也就不会执行。
  声明伪目标的方式如下:

.PHONY : clean

具体场景如下:
1 objects = main.o input.o calcu.o
2 main: $(objects)
3     gcc -o main $(objects)
4 
5 .PHONY : clean
6 
7 %.o : %.c
8     gcc -c $<
9 
10 clean:
11     rm *.o
12        rm main

  上述代码第 5 行声明 clean 为伪目标,声明 clean 为伪目标以后不管当前目录下是否存在名为“clean”的文件,输入“make clean”的话规则后面的 rm 命令都会执行。

5.4 条件判断

  语法格式有两种:

<条件关键字>
    <条件为真时执行的语句>
endif

以及:

<条件关键字>
    <条件为真时执行的语句>
else
    <条件为假时执行的语句>
endif

  条件关键字有 4 个、可分为2组:ifeq、ifneq一组,ifdef 和 ifndef一组,前一组表示是否相等,后一组表示是否非空。
  如:

ifeq (<参数 1>, <参数 2>)
# 用单双引号皆可
ifeq “<参数 1>”, “<参数 2>”
ifeq “<参数 1>”, ‘<参数 2>’

ifdef <变量名>

6.0 函数

6.1 wildcard

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

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

6.2 dir

  用函数dir来获取目录。
  此函数用来从文件名序列中提取出目录部分,返回值是文件名序列的目录部分,比如:

$(dir </src/a.c>) #提取 /src

6.3 notdir

  此函数用与从文件名序列中提取出文件名非目录部分,比如:

$(notdir </src/a.c>) #提取文件名“a.c”。

6.4 foreach函数

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

names := a b c d
files := $(foreach n, $(names), $(n).o)
上面的例子中,$(name)中的单词会被挨个取出,并存到变量“n”中,“$(n).o”每次根据“$(n)”计算出一个值,这些值以空格分隔,最后作为foreach函数的返回,所以,$(files)的值是“a.o b.o c.o d.o”。

6.5 subst

  字符串替换。形式如下:

$(subst <from>, <to>, <text>)
    此函数的功能是将字符串<text>中的<from>内容替换为<to>,函数返回被替换以后的字符串,比如如下示例:
$(subst  zzk, ZZK, my name is zzk)
    把字符串“my name is zzk”中的“zzk”替换为“ZZK”,替换完成以后的字符串为“my nameis ZZK "

6.6 patsubst

    此函数查找字符串<text>中的单词是否符合模式<pattern>,如果匹配就用<replacement>来替换掉,<pattern>可以使用通配符“%",表示任意长度的字符串,函数返回值就是替换后的字符串。如果<replacement>中也包涵“%”,那么<replacement>中的“%”将是<pattern>中的那个“%”所代表的字符串,比如:
$(patsubst %.c, %.o, a.c b.c c.c)
    将字符串“a.cb.cc.c”中的所有符合“%.c”的字符串,替换为“%.o”,替换完成以后的字符串为“a.o b.o c.o”。
posted @   平安顺遂233  阅读(2)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示