makefile 学习

Posted by jabin on May 24, 2016

学习资料:《跟我一起写Makefile 》–陈皓

makefile 概述

我们都知道对于一个c/c++程序大概要经过编译、链接,然后生成可执行的文件,而makefile文件规定了编译和链接的一系列规则,试图解决文件间的依赖关系,makefile需要用make命令来执行。

一个最简单的Makefile

#in Makefile
include ./Makefile.def
test : $(object)
        g++ -o test $(object)
$(object) : a.cpp
        g++ -c a.cpp
clean:
        -rm test $(object)
#in Makefile.def
object = a.o

在这个Makefile文件中,”test”是我们的第一个目标文件,make会把这个文件作为最终的目标文件,然后去一层一层地寻找其依赖文件(a.o,a.cpp),直到编译出第一个目标文件。 这样,因为clean这一目标没有和“test”直接或间接关联,所以不会被自动执行,若要显式执行,需要make clean 来清除所有的目标文件。 从这个栗子,我们可以看到Makefile所包含的五个部分:

  1. 显示规则—clean
  2. 隐晦规则—自动推导依赖关系
  3. 变量定义—object
  4. 文件指示—include
  5. 注释

规则

规则的书写包括两部分:依赖关系和生成目标文件的方法。

通配符

主要有*和?这两个,用法和shell一致。

文件搜寻

应用场景:make寻找文件的依赖关系时,其它文件存放在不同的目录中,主要在较大的工程项目中。 我们可以使用VPATH这一特殊变量来指定make的搜寻目录,如:VPATH = src : ../headers。 另外关键字 vpath 则提供了更灵活的make搜寻方式,即指定文件在指定目录搜索。

伪目标

我们回到上文那个简单的Makefile文件,最后的clean就是makefile中的伪目标, 伪目标需要我们在执行make命令时,显式地执行,即make clean。 同时,为了避免有文件和我们的伪目标同名,通常需要显式地指明一个目标是伪目标—.PHONY, 因此栗子中更保险的写法是:

.PHONY : clean
clean:
	-rm test $(object)

根据伪目标的特性,我们还可以吧伪目标放在Makefile的开头,并指定该伪目标的依赖文件们,这样我们只要敲一个make命令就可以一次生成多个可执行文件all : test1 test2 test3,多么有趣,right?

多目标

所谓多目标,其实就是如果有多个目标依赖于相同的文件,我们可以将多个目标写在同一个规则中。 通常我们需要用到一个自动化变量—$@,其表示当前规则中所有的目标变量。

静态模式

静态模式可以让我们很方便地书写多目标和多依赖文件的规则,其本质是模式匹配。

objects = foo.o bar.o

.PHONY : all
all : objects

# 目标文件们 : 目标文件的匹配模式 : 依赖文件的匹配模式 
$objects : %.o : %.c
	g++ -c $< -o $@

这里我们用到了另一个自动化变量—$<, 表示所有的依赖目标集。

命令

命令和shell命令一样。

变量

定义变量时,通常采用:=, 这样前面的变量不能使用后面的变量。

变量的高级用法

变量替换

#替换方式一---$(var:a=b)  分割符为空格或结束符 
foo := a.o b.o c.o
bar := $(foo:.o=.c)

#替换方式二---模式匹配 
foo := a.o b.o c.o
bar := $(foo:%.o=%.c)

追加变量值

objects += anoter.o

环境变量

这里的环境变量指的是系统设置的环境变量,如#root: export CFLAGS=-o, 那么我们在Makefile中$(CFLAGS)则为’-o’。

目标变量

test : CFLAGS = -g 这样在目标test和其所引发的所有规则中,CFLAGS 的值 都是 -g, 覆盖环境变量的值。

模式变量

%.o : CFLAGS = -o 类似目标变量。

条件分支

if ($(CFLAGS),-o)
	do sth
else
	do oth
endif

函数

用法:$(<func> <arguments>)

常用字符串处理函数

subst–字符串替换

$(subst <from>,<to>,<test>)

patsubst–模式字符串替换

$(patsubst <patt>,<replace>,<test>)

strip–去首尾空格

$(strip <test>)

findstring–字符串查找

$(findstring <target>,<test>)

filter/filter-out–模式过滤/剔除

$(filter <filter patt>,<test>)

word*–单词操作相关

文件名操作函数

dir/notdir/suffix/basename/addsuffix/addprefix,顾名思义,对路径文件名进行对应操作

foreach函数

$(foreach <var>,<list>,<operation>)

#遍历name中的值,加后缀
name := a b c d
files := $(foreach n,$(name),$(n).c)

shell函数

$(shell <shell command>) 顾名思义,shell函数将shell命令的输出作为函数的返回。