命名

文件保存以Makefilemakefile为名.当在当前目录下直接输入make即可执行

如果不是默认的文件名, 可以使用-f参数

1
make -f make.some

变量

变量基本用法

  • 变量以=赋值 左侧是变量, 右侧是变量的值

    1
    
    objects = main.o kbd.o command.o display.o \

    也可以使用变量来构造变量

    1
    2
    
    A = $(B)
    B = $(A)

    如上所示, 会让make陷入无限的变量展开过程中, 报错. 故可使用下面的:=

  • 变量以$(objects)取值 变量使用时要在前面加上$, 最好用()或者{}把变量括起来. 如果要使用真实的的$, 用$$

  • 变量以:=定义 =有可能导致递归定义, 故使用该表达式, 前面的变量不使用后面的变量, 只使用已经定义好的变量

  • 变量以?=定义

    1
    
    foo ?= bar

    如果foo没有被定义过, 那么变量foo的值是bar, 否则, 这条语句什么也不做.

  • 追加变量+= 如果变量之前没有定义过, +=自动变为=, 反之则继承于前次的操作的赋值符. 前次是:=, 那么此次为:=; 前次为=, 此次为=

  • override指示符变量 用override指示符的变量, 在make命令行参数中可以设置, 而Makefile中对这个变量的赋值会被忽略

    1
    2
    
    override <varriable> = <value>
    override <varriable> := <value>

    追加,

    1
    
    override <variable> += <more value>
  • 多行变量 define指示符后面跟的是变量名称, 而重起一行定义变量的值, 变量可以换行, 定义以endef关键字结束. 变量的值可以包含函数,命令,文字或者其他变量

  • 环境变量 make 运行时的系统环境变量可以在 make 开始运行时被载入到 Makefile 文件中,但是 如果 Makefile 中已定义了这个变量,或是这个变量由 make 命令行带入,那么系统的环 境变量的值将被覆盖。如果 make 指定了-e参数,那么,系统环境变量将覆盖 Makefile 中定义的变量

变量高级用法

  • 变量值的替换 替换变量中的共有部分, 格式为$(var:a=b)或者${var:a=b}, 把var中所有以结尾的字串替换成以b结尾 如下

    1
    2
    
    foo := a.o b.o c.o
    bar := $(foo:.o=.c)

    定义了\${foo}变量,把\${foo}中所有以.o结尾的子串全部替换为以.c结尾,$(bar)的值是a.c b.c c.c

  • 把变量值再再当成变量

    1
    2
    3
    
    x = y
    y = z
    a := $($(x))

    如上, \$(x)的值是y, \$(\$(x))就是\$(y), 那\$(a)就是z

依赖规则

规则语法

1
2
targets : prerequisites
    command

targes是文件名, 以空格分开, 可以使用通配符. 目标可以是一个文件, 也可能是多个文件.

command是命令行, 如果不与targets : prerequisites一行, 必须以[Tagb键]开头, 如果在一行, 可用;分割, 如下

1
targets : prerequisites ; command

prerequisites是目标所依赖的文件(或者依赖目标), 如果有一个以上的文件比 target 文件要新的话,command 所定义的命令就会被执行

伪目标

伪目标的规则类似上面的规则, 但是伪目标不是文件, 只是一个标签, 所以make无法生成它的依赖关系和决定它是否执行, 只有通过显示指定这个目标才能让其生效. 比如, make clean科学可以执行下面的命令.

1
2
clean :
    rm *.o temp

为了避免和文件重命的情况, 使用特殊的额标记.PHONY来显示的指明一个伪目标, 不管是否有同名文件

1
2
3
.PHONY : clean
clean : 
    rm *.o temp

伪目标没有依赖文件 ,但是可以为伪目标指定所依赖的文件. 伪目标可以作为默认目标

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
.PHONY : all cleanall cleanobj
all : prog1 prog2

prog1 : prog1.o utils.o
    cc -o prog1 prog1.o util.o

prog2 : proj2.o 
    cc -o prog2 prog2.o

cleanall : cleanobj cleandiff
    rm program

cleanobj :
    rm *.o

dleandiff : 
    rm *.diff

Makefile中的第一目标会被当做默认目标. all伪目标依赖于其他的三个目标

目标和伪目标都可以成为依赖.

如上, make cleanall, make cleanobj都可以显示伪目标执行命令

多目标

Makefile的规则中的目标可以不止一个, 其支持多目标. 当让, 有自动化变量$@吧表示目标的集合, 依次取出目标, 并执行命令.如下

1
2
bigoutput littleoutput : text.g
generate text.g $@ > $@

静态模式定义的多目标

语法如下

1
2
<targets...> : <target-pattern> : <prereq-pattern>
<commmands>
  • targets 定义了一系列目标, 可以有通配符, 是目标的一个集合
  • targets-pattern 指明了目标集模式, 从目标集中选出一部分目标
  • prereq-pattern 是目标的依赖模式, 对target-pattern形成的模式再次进行依赖目标的定义, 对选出的目标进行再定义

For example.

1
2
3
4
objects = foo.o bar.o
all : $(objects)
$(objects) : %.o : %.c
$(CC) -c $< -o $@

从目标集foo.o bar.o中通过%.o的目标集模式选出之后, 通过%.c进行二次定义. - %<自动化变量, 表示所有的依赖目标, 用%c表示 - %@表示目标集, 即%.o

规则中使用通配符

同bash一样, 支持三种通配符 |符号|意义| |:—:|:—| |*|0到无穷多个任意字符| |?|一个任意字符| |[]|一个[]内的字符(非任意字符). 例如, [abcd], a,b,c,d中的任意一个字符| |[-]|编码顺序内的字符. 例如, [0-9]代表0到9之间的所有数字| |[^]|反向选择. 例如, [^abc]非a,b,c外的其他的任意一个字符|

文件搜索

VPATH(大写)变量指定搜索目录

make默认在当前目录搜索文件, 然后再到VPATH指定的目录去搜索.

1
VPATH = src:../headers

上面的定义指定两个目录,, 用:分割, “src”和”../headers”, make按照这个顺序进行搜索, 当然, 当前目录是最高优先级.

vpath(小写)关键字设定搜索规则和路径

三种使用方法 - vpath <pattern> <directories> 为符合模式<patern>的文件指定搜索目录<directories>

  • vpath 清楚设置的符合模式<pattern>的文件的搜索目录

  • vpath 清楚所有已被设置好的文件的搜索目录

vpath使用的<pattern>中需要包含%字符, 表示匹配零或者若干字符

1
2
3
vpath %c foo:bar
vpath %c usr
vpath % blish

上面的语句表示.c结尾的文件, 依次搜索”foo”, “bar”, “user”, “blish”目录

条件判断

ifeq 条件语句开始, else条件表达式为假, endif表示一个条件语句的结束.

  • ifeq, 比较参数arg1和arg2是否相同,相同则为真

    1
    2
    3
    4
    
    ifeq '<arg1>' '<arg2>'
    ifeq "<arg1>" "<arg2>"
    ifeq '<arg1>' "<arg2>"
    ifeq "<arg1>" '<arg2>'
  • ifneq是否相同, 不同则为真

  • ifdef <variable-name>判断变量的值非空, 则为真.只是测量变量是否有值, 并不会把变量扩展到当前位置

函数

函数调用以$开头, 以圆括号或花括号把函数名和参数括起.参数以,分隔, 函数名和参数之间以空格分隔;

1
$(<function> <arguments1,arguments2>) #小括号可换为大括号

常用的字符串处理函数

  • $(subst <from>,<to>,<text>) 把子串<text>中的<from>字符串替换为<to>, 返回替换后的子串

  • (strip <string>) 去掉<string>子串中开头和结尾的空字符,返回被去掉空格的字符串

  • (findstring <find>,<in>) 查找字符串函数, 在子串<in>中查找<find>子串, 如果找到, 返回<find>, 否则返回空字符串

  • $(word <n>,<text>) 取单词函数, 取字符串<text>中的第<n>的单词, 从1开始.返回子串<text>中的第<n>个单词. 如果<n>比<text>中的单词要大, 返回空字符串

  • $(wordlist <s>,<e>,<text>) 取字符串函数 从字符串<text>中取<s>开始到<e>的单词串, <s>和<e>是一个数字

特定字符

-放在命令前, 无论出现什么错误, 继续执行

注释用#字符

命令以[Tab]键开始

\ 换行符, 类似c语言, 表示连接上一行

Makefile与vim

vim内置了autocmd, 所以可以使用:make, make当前目录:pwd下的Makefile

1
:make           #执行当前目录下的Makefile

Use the following commands to examine the compile errors with vim

1
2
3
4
:copen          #open a mini-window with list of errors, hit enter on an error to jump to line
:cclose         #close the mini-window
:cn[ext]          #jump to the next errror
:cp[revious]

具体用法, 可以在vim下:h copen:h cnext

Makefile经验总结

  • 整个Makefile文件都不要使用空格键最为安全,你看到的所有空格其实是有Tab键产生的,一旦使用了空格键,文件是默认空格是编译语言的其中一部分,从而会导致错误的出现。包括在编写注释的时候,也不要用空格
  • 使用-l连接库模块时,不要直接写文件名,要把文件名中开头的lib去掉。例如,源文件是libopencv_core.so,在连接时使用-lopencv_core.
  • 多个连接模块,使用空格分开。

参考: 陈皓的Makefile教程