0%

Makefile语法

Makefile 入门

三个重要变量

  • $@: 目标文件
  • $^:所有的依赖文件,
  • $<:第一个依赖文件

重点语法规则

  • 反斜杠(\)是换行符的意思
  • Shell命令,一定要以一个Tab键作为开头

make自动推导

make 具有隐晦规则,可自动推导,比如

make看到一个whatever.o文件,会自动的把whatever.c文件加在依赖关系,cc -c whatever.c 也会被推导出来

Makefile的构成

Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。

  • 显式规则:要生成的文件,文件的依赖文件,生成的命令。
  • 隐晦规则:make自动推导的。
  • 变量的定义:字符串,C语言中的宏,当Makefile被执行时,变量都会被扩展到相应的引用位置上
  • 文件指示
    • 引用另一个Makefile
    • 根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样
    • 定义一个多行的命令。
  • 注释:Makefile中只有行注释”#”

Makefile的文件名

  • 默认的情况下,make命令会在当前目录下按顺序找寻文件”GNUmakefile”、”makefile”、”Makefile”.
  • 可以指定特定的Makefile,使用make的”-f”和”—file”参数,如:make -f Make.Linux或make —file Make.AIX。

引用其他Makefile

include foo.make *.mk $(bar)

  • include 支持引入其他make,和变量,且支持通配符
  • make 是使用--include-dir或者--I指定,引用目录
  • 搜索路径:当前目录 -> 指定目录 -> \/include(一般是:/usr/local/bin 或/usr/include)
  • 兼容其他版本make使用sinclude

make是如何工作的(重点)

  • make程序会在当前目录下找名字叫”Makefile”或”makefile”的文件。
  • 读入被include 的其它Makefile
  • 初始化变量
  • 设置目标(一次make,只有一个最终目标),第一个目标为默认最终目标,如果要make非默认最终目标,需要显示执行,如 make clean
  • 如果目标文件所依赖的.o文件不存在,就先生成该.o
  • 然后层层递归,从而生成一条编译顺序链,非最终目标的的依赖对象不会在编译链中,所以不会被执行
  • 最后执行编译命令,层层返回直到生成目标文件
  • 没有和目标文件直接或间接关联,不会被自动执行,只是被定义。

不重要的知识点

环境变量MAKEFILES

如果存在环境变量MAKEFILES,make会自动引用,但与include不同的是,MAKEFILES不会主动执行

建议不要用,因为所有的Makefile都会受他影响

清空目标文件的规则

1
2
3
.PHONY : clean 
clean :
-rm edit $(objects)
  • .PHONY意思表示clean是一个”伪目标”。
  • rm命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事
  • 不成文的规矩是——“clean从来都是放在文件的最后”

如何编写规则

通配符

  • *: 略
  • ?:上一命令的结果
  • ~:家目录,windows下是环境变量HMOE

源文件搜寻

  • 特殊变量”VPATH”

    • VPATH = src:../headers
    • 给Makefile指定源文件所在目录
    • 目录间以冒号:隔开
  • 关键字”vpath”

    • vpath <pattern> <directories> #指定某个目录的某种文件
      • vpath %.c foo
    • vpath <pattern> #清除已指定的某种类型的文件
      • vpath %.c
    • vpath #清除所有已指定的文件
      • vpath
    • %:表示匹配0或若干个字符(同*)

伪目标

  • 顾名思义,具有目标的属性,但不是真正的文件,不会生成目标文件,并且需要指定依赖关系
  • 伪目标可不用声明,若伪目标与文件同名,则伪目标必须用.PHONY 声明,否则编译会报错
  • 伪目标可以作为Makefile的默认目标,比如声明一个名为”all”伪目标,依赖多个真实的目标,从而实现一次生成多个目标的目的

多目标(目标变量)

  • @ : 目标变量
  • 这一节书中说的有绕,我的理解是,有多个目标,不用每个目标都写一套指令,指令要复用,指令涉及到目标名的,用$@替代

静态模式

<targets ...> : <target-pattern> : <prereq-patterns ...>

1
2
3
4
objects = foo.o bar.o
all:$(objects)
$(objects): %.o: %.c #%.c表示.o同名的.c文件
$(CC) -c $(CFLAGS) $< -o $@

目前不大理解其用途,意思就是筛选目标集,并制定依赖关系

自动生成依赖关系

先说明一下,这个没啥用,现在的编译器可以不依赖头文件
利用 gcc/g++的”-M”的选项,获取源文件包含的头文件,然后把所有依赖文件放入一个.d中,编译时把.d文件引入

命令的书写

说明:这一章很多无用知识点,将不按说中描述进行记录,只列出我认为有用的点

  • 命令出错

    • 命令前加一个减号”-“(在Tab 键之后),标记为不管命令出不出错都认为是成功的
    • 给 make 加上”-i”,所有命令都会忽略错误,”-k”,出错则终止
  • 嵌套执行

    • $(MAKE) 执行子Makefile
    • make的-C选项可以先进入一个文件夹,在执行make
    • 嵌套时传递变量:export variable;拦截变量传递:unexport variable
    • 如果make有参数,除”-C”,”-f”,”-h””-o”和”-W” 以外的参数都将传递
    • make的-w选项:输出当前目录,-C时,默认-w
  • 定义命令包

    • 以”define”开始,以”endef”结束,如

      1
      2
      3
      4
      define run-yacc
      yacc $(firstword $^)
      mv y.tab.c $@
      endef
    • 执行:

      1
      2
      foo.c : foo.y
      $(run-yacc)

      在命令包的定义中run-yacc,”$^”就是”foo.y”,”$@”就是”foo.c”

      命令包的执行需要使用$()

变量的使用

变量的声明

  • 变量名可以是数字开头,但不应该含有”:”、”#”、”=”或是空字符(空格、回车等)。变量是大小写敏感的
  • 变量在声明时,需要给予初值
  • 变量在使用时,需要给在变量名前加上”$”符号,最好用小括号”()”或是大括号”{}”把变量给包括起来
  • MakeFile的变量类似C/C++中的宏,在指定为位置展开,不同的是可以重定义

变量给变量赋值

  • 变量可以先使用在定义,但相互嵌套会报错

    1
    2
    3
    foo = $(bar)
    bar = $(ugh)
    ugh = Huh?
  • := 表示不使用未声明的变量;?= 表示如果未定义则定义,已定义则跳过

  • 如果变量后面有n个空格,变量在展开时,也会附带一个空格

追加变量

  • 以使用”+=”操作符给变量追加值,如objects += another.o
  • +=
    • 变量未定义过,那么,”+=”会自动变成”=”,
    • 变量已定义,会继承于前次操作,如果前一次的是”:=”,那么”+=”会以”:=”作为其赋值符

系统环境变量

  • make在开始运行时,会载入系统环境变量
  • 默认系统环境变量是可覆盖的,make的”-e”参数,表示不覆盖

局部变量

局部变量分两种,目标变量和模式变量,区别在于作用不同:目标变量的作用域为目标生成的范围内,模式变量的作用域为符合模式的文件范围

目标变量

格式:<target ...> : <variable-assignment>

在目标后对变量赋值,如下

1
2
3
prog : CFLAGS = -g
prog : prog.o foo.o bar.o
$(CC) $(CFLAGS) prog.o foo.o bar.o

模式变量

格式:<pattern ...>; : <variable-assignment>

如:%.o : CFLAGS = -O

不重要的知识点

  • override 指示符:命令行定义的变量需要override来修改

  • 变量值的替换

    • ${var:a=b}:把变量”var”中所有以”a”字串”结尾”的”a”替换成”b”字串。这里的”结尾”意
      思是”空格”或是”结束符”
    • bar := $(foo:%.o=%.c):静态模式
  • 变量嵌套:把变量值当变量

    $($(x))

  • 多行变量:使用define&endef定义多行变量,无[Tab] 键开头为变量,有则为命令包

条件判断

  • 关键字:ifeq ifneq ifdef ifndef else endif

  • 说明make提供四种判断,相等,不相等,定义,未定义;未提供eles if

  • 格式:

    1
    2
    3
    4
    5
    ifdef foo
    frobozz = yes
    else
    frobozz = no
    endif

函数

函数调用:$(<fun> <arg>)

字符串处理函数

  • 模式替换 patsubst :$(patsubst <pattern>,<replacement>,<text>)
  • 字符串替换 subst:$(subst <from>,<to>,<text>)
  • 去头尾空格 strip:$(strip <string>)
  • 字符串查找 findstring:$(findstring <find>,<in>)
  • 过滤 filter:$(filter <pattern...>,<text>)
  • 反向过滤 filter-out:$(filter-out <pattern...>,<text>)
  • 单词排序 sort :$(sort <list>)
  • 取单词 word:$(word <n>,<text>)
  • 取单词串 wordlist:$(wordlist <ss>,<e>,<text>)
  • 单词个数统计 words:$(words <text>)
  • 首单词 firstword:$(firstword <text>)

文件名操作函数

  • 取目录 dir:$(dir <names...>)
  • 取文件 notdir:$(notdir <names...>)
  • 取后缀 suffix:$(suffix <names...>)
  • 取前缀 basename:$(basename <names...>)
  • 加后缀 addsuffix:$(addsuffix <suffix>,<names...>)
  • 加前缀 addprefix:$(addprefix <prefix>,<names...>)
  • 连接 join——两个list按下标结合,如a[1],b[1]结合:$(join <list1>,<list2>)
  • wildcard——替换 Bash 的通配符:$(wildcard PATTERN…)

其他函数

  • foreach ——遍历list,操作其中的每个元素:$(foreach <var>,<list>,<text>)

  • if——功能类似ifeq:$(if <condition>,<then-part>) 或者 $(if <condition>,<then-part>,<else-part>)

  • call——调用自定义函数变量

    1
    2
    reverse = $(1) $(2)
    foo = $(call reverse,a,b)
  • origin——获取变量来源:$(origin ;)

  • shell——执行shell命令,获取返回值:$(shell cat foo)

  • error——报错,make暂停(可以继续):$(error ;)

  • warning——报警,但make不会停:$(warning ;)

隐含规则

C/C++隐含规则

  • 编译 C 程序的隐含规则:”.o”的目标的依赖目标会自动推导为”.c”,并且其生成命令是$(CC) –c $(CPPFLAGS) $(CFLAGS)

  • 编译 C++ 程序的隐含规则:”.o”的目标的依赖目标会自动推导为”.cc”或是”.C”,并且其生成命令是$(CXX) –c $(CPPFLAGS) $(CFLAGS)(建议使用”.cc”作为 C++ 源文件的后缀,而不是”.C”)

隐含规则使用的变量

命令的变量

  • CC:C 语言编译程序。默认命令是”cc”
  • CXX:C++ 语言编译程序。默认命令是”g++”
  • AR: 函数库(Object文件)打包程序。默认命令是”ar”
  • CPP:C 程序的预处理器(输出是标准输出设备)。默认命令是”$(CC) –E”
  • RM: 删除文件命令。默认命令是”rm –f”

命令参数的变量

  • ARFLAGS: 函数库打包程序 AR 命令的参数。默认值是”rv”
  • ASFLAGS: 汇编语言编译器参数。(当明显地调用”.s”或”.S”文件时)
  • CFLAGS:C 语言编译器参数
  • CXXFLAGS:C++ 语言编译器参数
  • CPPFLAGS:C 预处理器参数

自定义模式规则

使用”%”来定义一个隐含规则 ,目标的”%”的意思是表示一个或多个任意字符,目标的”%”,取决于依赖。变量与函数的”%”在Makefile载入是被展开,模式则在运行时展开

使用自动化变量

一般而言,使用模式是为了实现自动化,所以在模式中需要使用自动化变量实现对具体的文件的”抽象”,自动化变量的定义见附录

  • $@、$<、$%、$*在扩展时只会有一个文件

  • $?、$^、$展开时一个文件列表

  • 这些变量使用”D”,”F”,可以获取目录或文件名,如$(<D), $(<F)

模式的匹配

​ “%”所匹配的内容叫做”茎”,当一个模式匹配包含有斜杠的文件时,目录部分会首先被移开,然后进行匹配,成功后,再把目录加回去。如”e%t”,”src/eat”匹配于该模式,%为a,但目录部分src/也会随之出现在目标中

函数库

​ 函数库文件(.a/.lib)也就是对 Object 文件(程序编译的中间文件)的打包文件,由命令“ar”来完成打包工作。隐含规则:如”foo.a(bar.o bar1.o)”中bar.o,bar1.o编译成功后,会自动调用ar命令打包,不过个人更喜欢显式的输入ar命令进行打包

附录

GUN制定的伪目标

伪目标 定义
all 编译所有的目标
clean 删除所有被 make 创建的文件
install 安装已编译好的程序,其实就是把目标执行文件拷贝到指定的目标中去
print 列出改变过的源文件
tar 源程序打包成tar 文件
dist 创建一个压缩文件,把 tar 文件压成 Z 文件或是 gz文件
TAGS 更新所有的目标,以备完整地重编译使用(没理解)
check 和 test 测试 makefile 的流程。

make 的参数

参数 含义
-b/-m 忽略和其它版本 make 的兼容性
-B 重编译
-C 指定读取 makefile 的目录
-debug[=] 输出 make 的调试信息
-e 环境变量的值覆盖 makefile 中定义的变量的值
-f=, 指定需要执行的 makefile
-h 显示帮助信息
-i 在执行时忽略所有的错误
-I 指定makefile 的搜索目录。可以使用多个“-I”参数来指定多个目录
-j [] 指定同时运行命令的个数
-k 出错也不停止运行
-l 指定 make 运行命令的负载
-n 仅输出执行过程中的命令序列,但并不执行。
-o 不更新生成的指定的
-p 输出所有的规则和变量
-q 是检查所指定的目标是否需要更新,0更新,2有错误发生
-r 禁止 make 使用任何隐含规则
-R 禁止 make 使用任何作用于变量上的隐含规则
-s 在命令运行时不输出命令的输出
-S 取消“-k”选项的作用
-t 把目标的修改日期变成最新的(导致目标不会更新)
-v 输出 make 程序的版本
-w 输出运行 makefile 之前和之后的信息
-W 假 定 目 标需要更新

自动化变量

变量 含义
$@ 目标文件集
$% 当目标是函数库文件时有效,表示目标成员名。如是”foo.a(bar.o)”,目标为foo.a,$%表示bar.o
$< 第一个依赖目标
$? 所有比目标新的依赖目标的集合(需要更新的依赖)
$^ 所有依赖目标的集合,去除重复
$+ 所有依赖目标的集合,不去除重复
$* 目标模式中”%”及其之前的部分,。如目标是”dir/a.foo.b”,模式是”a.%.b”,那么,”$*”就是”dir/a.foo”

特殊的目标

名称 功能
.PHONY: 这个目标的所有依赖被作为伪目标。伪目标是这样一个目标:当使用 make 命令行指定此目标时,这个目标所在的规则定义的命令、无论目标文件是否存在都会被无条件执行。
.SUFFIXES: 这个目标的所有依赖指出了一系列在后缀规则中需要检查的后缀名
.DEFAULT: Makefile 中,这个特殊目标所在规则定义的命令,被用在重建那些没有具体规则的目标,就是说一个文件作为某个规则的依赖,却不是另外一个规则的目标时,make 程序无法找到重建此文件的规则,这种情况就执行 “.DEFAULT” 所指定的命令。
.PRECIOUS: 这个特殊目标所在的依赖文件在 make 的过程中会被特殊处理:当命令执行的过程中断时,make 不会删除它们。而且如果目标的依赖文件是中间过程文件,同样这些文件不会被删除。
.INTERMEDIATE: 这个特殊目标的依赖文件在 make 执行时被作为中间文件对待。没有任何依赖文件的这个目标没有意义。
.SECONDARY: 这个特殊目标的依赖文件被作为中过程的文件对待。但是这些文件不会被删除。这个目标没有任何依赖文件的含义是:将所有的文件视为中间文件。
.IGNORE 这个目标的依赖文件忽略创建这个文件所执行命令的错误,给此目标指定命令是没有意义的。当此目标没有依赖文件时,将忽略所有命令执行的错误。
.DELETE_ON_ERROR: 如果在 Makefile 中存在特殊的目标 “.DELETE_ON_ERROR” ,make 在执行过程中,荣国规则的命令执行错误,将删除已经被修改的目标文件。
.LOW_RESOLUTION_TIME: 这个目标的依赖文件被 make 认为是低分辨率时间戳文件,给这个目标指定命令是没有意义的。通常的目标都是高分辨率时间戳。
.SILENT: 出现在此目标 “.SILENT” 的依赖文件列表中的文件,make 在创建这些文件时,不打印出此文件所执行的命令。同样,给目标 “SILENT” 指定命令行是没有意义的。
.EXPORT_ALL_VARIABLES: 此目标应该作为一个简单的没有依赖的目标,它的功能是将之后的所有变量传递给子 make 进程。
.NOTPARALLEL: Makefile 中如果出现这个特殊目标,则所有的命令按照串行的方式执行,即使是存在 make 的命令行参数 “-j” 。但在递归调用的子make进程中,命令行可以并行执行。此目标不应该有依赖文件,所有出现的依赖文件将会被忽略。