0%

Makefile收集

代码变成可执行文件,叫做编译(compile);先编译这个还是先编译那个(即编译的安排),叫做构建(build)。Make是最常用的构建工具,诞生于1977年,主要用于C语言的项目。

Makefile基础

一、Make

      make命令执行时需要一个makefile文件,以告诉make命令需要怎么样的去编译和链接程序。它是一个根据指定的Shell命令进行构建的工具,规则很简单,你规定要构建哪个文件、它依赖哪些源文件,当那些文件有变动时,如何重新构建它。

二、Makefile

  1. Makefile文件由一系列规则rules构成,每条规则就明确两件事:构建目标的前置条件是什么,以及如何构建。每条规则的形式如下
1
2
<target> : <prerequisites> 
[tab] <commands>
  • 目标target:一个目标target就构成一条规则,它通常是文件名(一个或多个,多个用空格分开),指明Make命令所要构建的对象目标是一/多个文件名。
    • target除了可以是文件名,还可以是某个操作的名字,这称为”伪目标”(phony target)。
  • 前置条件prerequisites:它通常是一组文件名,之间用空格分隔。
    • 前置条件指定了目标是否重新构建的判断标准:只要有一个前置文件不存在,或者有过更新(前置文件的last-modification时间戳比目标的时间戳新)目标就需要重新构建。
  • 制表键tab:每行命令之前必须有一个制表键tab
    • 如果想用其他键可以用内置变量.RECIPEPREFIX声明。
  • 命令commands:表示如何更新目标文件,由一行或多行的shell命令组成。它是构建目标的具体指令,它的运行结果通常就是生成目标文件。
    • 每行命令在一个单独的shell中执行,这些shell之间没有继承关系。若要一次性执行执行多行shell有三种方案:
      • 将多行shell写在一行,中间用逗号分隔
      • 在每行shell后加反斜杠\转义
      • 使用内置变量.ONESHELL
  1. Makefile语法
    • 注释:用#,跟shell脚本一样
    • 回声echoing:正常情况下make会打印每条命令,然后再执行,这就叫做回声。
      • 在命令的前面加上@可以关闭回声。
    • 通配符wildcard:用来指定一组符合条件的文件名。
      • Makefile的通配符与Bash一致,主要有*?[...],如*.o表示所有后缀名为o的文件。
    • 模式匹配:Make命令允许对文件名进行类似正则运算的匹配,主要用到的匹配符是%
      • 使用匹配符%可以将大量同类型的文件只用一条规则就完成构建。
    • 变量和赋值符:和大多数变成语言类似,使用等号=进行赋值给变量
      • 调用shell变量需要在美元符号前再加一个美元符号,如echo $$PATH
1
2
3
4
VARIABLE = value # 在执行时扩展,允许递归扩展
VARIABLE := value # 在定义时扩展
VARIABLE ?= value # 只有在该变量为空时才设置值
VARIABLE += value # 将值追加到变量的尾端
  • 内置变量Implicit Variables:Make命令提供一系列内置变量,比如$(CC)指向当前使用的编译器,$(MAKE)指向当前使用的Make工具。
  • 自动变量Automatic Variables:自动变量的值与当前规则有关,常用的如下:
    • $@指代当前目标,就是Make命令当前构建的目标。
    • $<指代第一个前置条件,如规则为t: p1 p2,那么$<就指代p1
    • $^指代所有前置条件,之间以空格分隔。
    • $+表示所有依赖文件的集合(不去重)。
    • $?指代比目标更新的所有前置条件,之间以空格分隔。
    • $*指代匹配符%匹配到的部分。
    • $(@D)$(@F)分别指向$@的目录名和文件名。
    • $(<D)$(<F)分别指向$<的目录名和文件名。
  • 判断和循环:Makefile使用Bash语法完成判断和循环。
  • 函数:Makefile还可以使用函数,格式为:$(function arguments)或${function arguments},一些常用的函数如下:
    • shell函数用来执行shell命令。
    • wildcard函数用来在Makefile中替换Bash的通配符。
    • subst函数用来文本替换。
    • patsubst函数用于模式匹配的替换。
    • strip函数用于去除头部和尾部的空格。
    • filter函数用于过滤特定模式的字符串。
    • word函数用于取单词。

三、参考

  1. 参考一
  2. 参考二

Makefile中条件判断

一、介绍

      使用条件判断可以让make根据运行时的不同情况选择不同的执行分支,条件表达式可以是比较变量的值,或是比较变量和常量的值。

  1. 语法
1
2
3
4
5
ifxxx (arg1,arg2)
#do true
else #可省略
#do false #可省略
endif
  1. 条件常用格式

    • ifxxx (arg1,arg2)
    • ifxxx “arg1” “arg2”
    • ifxxx ‘arg1’ ‘arg2’
    • ifxxx “arg1” ‘arg2’
    • ifxxx ‘arg1’ “arg2”
  2. 分类

判断 作用
ifeq 判断参数是否相等,相等为true,否则为false
ifneq 判断参数是否不相等,不相等为true,否则为false
ifdef 判断参数是否有值,有值为true,否则为false
ifndef 判断参数是否没有值,没有值为true,否则为false

二、使用

  1. 定义makefile文件
1
2
3
4
5
6
7
8
9
10
11
.PHONY : test

var1 := 123
var2 := $(var1)

test:
ifeq ($(var1),$(var2))
@echo success
else
@echo fail
endif
  1. 执行makemake test,正常会输出success

  2. 注意:

    • ifxxx 后面有个空格
    • ifxxx:当ifxxx没有缩进时make会正确识别它们,将其作为分支选择的标识;当ifxxx有缩进时,make将它们当做普通的shell script
      • 意思就是ifxxx前不用加tab

三、参考

  1. 参考一

Makefile中自动变量与隐晦规则

一、自动变量

      makefile中的自动变量实质上是对一类变量的简写,当我们在模式规则中对这类变量处理的时候可以直接使用自动变量简化makefile代码的编写。自动变量包括如目标文件,依赖文件等,常用的自动变量:

  1. $@指代当前目标,就是Make命令当前构建的那个目标。

    • makefile内容如下
    1
    2
    test:
    @echo $@
    • 执行make得到结果test,即目标本身
  2. $<指代第一个前置条件。

    • makefile内容如下
1
2
3
4
5
6
test.txt: source.txt result.txt
@echo $<
source.txt:
@echo "this is a test" > source.txt
result.txt:
@echo "this is also a test" > result.txt
  • 执行makemake test.txt得到结果source.txt
  1. $^指代所有前置条件,之间以空格分隔。
    • makefile内容如下
1
2
3
4
5
6
test.txt: source.txt result.txt
@echo $^
source.txt:
@echo "this is a test" > source.txt
result.txt:
@echo "this is also a test" > result.txt
  • 执行makemake test.txt得到结果source.txt result.txt
  1. $+表示所有依赖文件的集合(不去重),$^会去重。

  2. $?指代比目标更新的所有前置条件,之间以空格分隔。

  3. $*指代匹配符%匹配到的部分。

  4. $(@D)$(@F)分别指向$@的目录名(相对路径)和文件名。

  5. $(<D)$(<F)分别指向$<的目录名(相对路径)和文件名。

二、隐晦规则

      使用makefile隐晦规则可以省去为每一个类似的规则生成都去写类似的规则,makefile会自动推导依赖文件,并根据隐含规则推导出生成当前目标的命令。

  1. 不使用隐晦规则
1
2
3
4
5
6
7
8
target: a.o b.o c.o
g++ -o $@ $^ $(LIB) $(INC) $(LINKER)
a.o: a.c
g++ -c $(CFLAGS) -o a.o a.c
b.o: b.c
g++ -c $(CFLAGS) -o b.o b.c
c.o: c.c
g++ -c $(CFLAGS) -o c.o c.c
  1. 使用隐晦规则
1
2
3
4
5
6
SOURCE := $(shell find ./ -type f -name *.c)
OBJECTS := $(patsubst *.c,*.o,$(SOURCE))
target : $(OBJECTS)
g++ -o $@ $^ $(LIB) $(INC) $(LINKER)
%.o:%.c
$(CC) -c $(CFLAGS) -o $@ $<

执行过程:

makefile中首先声明了变量SOURCEOBJECTS分别代表当前文件夹下的.c.o文件集合。目标target依赖所有的目标文件.o,即a.o,b.o,c.o。当需要依赖a.omakefile会根据隐晦规则自动推导出生成a.o文件的命令g++ -c $(CFLAGS) -o a.o a.c生成a.o,同理会根据隐晦规则生成b.o、c.o,三个.o文件生成后再根据上述规则生成target

三、参考

  1. 参考一

  2. 参考二


Makefile中伪目标与PHONY

一、概念

      Makefile文件由一系列规则rules构成,规则是有格式要求的,其中最主要的就是目标文件target,其次就是前置条件prerequisites和命令commands(二者至少存在一个)。当目标文件target不是一个文件时就称此target为伪目标。

      .PHONY是一个特殊的目标(special target),也可称为伪目标,它用来指定一个假想的工作目标,其后面并不是一个实际文件,而是一个必须要执行的命令名。即存在.PHONY时,其后面的命令一定会执行,可以有效防止在Makefile文件中定义的可执行命令的目标规则和工作目录下的实际文件出现名称冲突,并提高Makefile的执行性能。

二、使用

  1. 不带.PHONY
    • 不存在同名文件clean,执行make clean会正常删除result.txt
    • 存在同名文件clean,执行make clean会报错
1
2
clean:
rm result.txt
  1. .PHONY
1
2
3
4
clean:
rm result.txt

.PHONY:clean

三、参考

  1. 参考一