GNU make笔记

是看跟我一起写makefile做的一点笔记。

编译和链接

在Makefile中定义整个编译流程以及各个目标文件与源文件之间的依赖关系,并且只重新编译新的修改会影响到的部分,从而降低编译的时间。

编译只检查函数、变量是否被声明,并生成.o(Linux)或.obj(Windows)文件。由于编译生成的中间目标文件太多,因此通常会将其打包成为.a(Linux)或.lib(Windows)文件,以进行链接。

依赖关系

makefile 复制代码
target ... : prerequisites ...
	command
	...
  • target:可以是一个目标文件,可执行文件,或一个标签。
  • prerequisites:target所依赖的文件和target。
  • command:该target要执行的shell命令。

注意makefile中的command必须以Tab开头。

另外,在执行有顺序性要求的命令时,不应该分写两行,而应该用分号隔开,例如:

makefile 复制代码
	# 错误写法!pwd将在本目录执行
	cd ../
	pwd

	# 正确写法
	cd ../; pwd

makefile会检查文件的更新时间,当prerequisites中有文件比target新时,command所定义的命令就会被执行。

以1个.h文件 + 2个.c文件为例:

makefile 复制代码
# myProcess是可执行文件
myProcess : main.o somec.o
	gcc -o myProcess main.o somec.o

main.o : main.c somec.h
	gcc -c main.c
somec.o : somec.c somec.h
	gcc -c somec.c

clean :
	rm myProcess main.o somec.o

注意,这里的clean是一个标签而非文件,使用类似make clean这样的形式来执行标签后的命令。

makefile会以完成第一个target为目标,逐次展开依赖,去更新涉及到的依赖的文件。

变量

将上例改为用变量的形式:

makefile 复制代码
OBJ = main.o somec.o
TARGET = myProcess
CC = gcc

$(TARGET) : $(OBJ)
	$(CC) -o $(TARGET) $(OBJ)

main.o : main.c somec.h
	$(CC) -c main.c
somec.o : somec.c somec.h
	$(CC) -c somec.c

clean :
	rm $(TARGET) $(OBJ)

也可以将clean改为下面这种写法:

makefile 复制代码
.PHONY : clean
clean :
	-rm $(TARGET) $(OBJ)

其中,.PHONY显式地表示clean是一个伪目标 ,即表明clean不是需要检查依赖的目标。

另外,rm前的-表示执行时即使出错,也继续执行命令。

一般更推荐使用:=来赋值变量,与=的区别在于,前者立即展开,而后者在使用时才会展开。

定义空格变量

使用如下的方法定义空格变量:

makefile 复制代码
nullstring :=
space := $(nullstring) #end of line

nullstring是空变量,其后加一个空格,在用注释表示结束,即可将space展开为一个空格变量。

变量的替换

例如:

makefile 复制代码
var1 := a.o b.o
var2 := $(var1:.o=.c)

表示var2是将var1中的.o替换为.c,即var2为a.c b.c。

override

override强制修改变量,例如:

makefile 复制代码
var := var1
override var1 := var2

多行变量

makefile 复制代码
define multi_line_var
xxx
xxx
endef

目标变量

在目标后的依赖里写变量,表示不管全局上该变量如何,在此目标引发的行为中变量都是这样的,例如:

makefile 复制代码
CFLAGS := -o
# 由prog引发的行为中CFLAGS都是-g
prog : CFLAGS = -g

自动变量

使用模式规则 来定义一个隐含规则,需要在目标中使用%。目标中的%取决于依赖中的值。

而自动化变量,就会把模式中所定义的一系列的文件自动地挨个取出。

下面是一些常用的自动变量:

自动变量 含义
$@ 规则中的目标文件名
$< 第一个依赖文件
$^ 所有依赖文件
$* 匹配通配符%的部分

make自动推导

make看到xx.o,可以自动地推导出它依赖同名的xx.c,并且gcc -c xx.c也会被自动加上。

由此,上例又可以省略为:

makefile 复制代码
OBJ = main.o somec.o
TARGET = myProcess
CC = gcc

$(TARGET) : $(OBJ)
	$(CC) -o $(TARGET) $(OBJ)

main.o : somec.h
somec.o : somec.h

clean :
	rm $(TARGET) $(OBJ)

包含其他makefile文件

默认情况下,make会寻找命名为makefile和Makefile的文件,当然也可以是其他任意命名的makefile文件,不过需要使用make -f <filename>来显式地指定。

在一个makefile中使用include <filenames>来包含其他makefile文件,

文件搜索

默认情况下,makefile只会在当前目录搜索文件。

  • 使用VPATH变量指明其他的目录用于搜索。
    例如VPATH = src:../headers,其中src和headers都是搜索目录,它们之间用:隔开。
  • 使用vpath关键字。
    例如vpath %.h ../headers表示在headers下搜索所有以.h结尾的文件。

一次生成多个程序

按基本的写法,makefile只会从第一行,即第一个生成程序开始查找依赖,如果后面还有程序要生成则可能被忽略。

可以使用伪目标来达成这一目的:

makefile 复制代码
all : prog1 prog2
.PHONY : all

prog1 : prog1.o
	gcc -o prog1 prog1.o

prog2 : prog2.o
	gcc -o prog2 prog2.o

声明一个伪目标all,它依赖于两个程序,但由于伪目标不会被生成文件,因此all总是需要更新其依赖,也就总是生成这多个程序。

条件判断

使用ifeq, ifneq, ifdef, ifndef来进行条件判断,并且都以endif结尾。

make读取Makefile时就计算条件表达式的值,并根据其值来选择语句,所以,不要把自动化变量放入条件表达式中,因为自动化变量是在运行时才有的。

函数

格式为$(function argument1,argument2)。函数和参数以空格隔开,参数见以逗号隔开。

下面介绍一些常用的函数。

字符串函数

  • subst
    $(subst from,to,text),把字符串text中的from替换为to。
  • patsubst
    $(patsubst pattern,replacement,text),模式替换,把text中的单词pattern替换为replacement。
  • strip
    $(strip string),去空格
  • filter
    $(filter pattern,text),只保留text中符合pattern模式的单词。
    例如```
makefile 复制代码
sources := a.c b.o c.s
$(filter %.c %.o,$(sourecs))
返回a.c b.o
  • sort
    $(sort string),给string中的单词排序,注意会去重。
  • word
    $(word n,string),取string中的第n个单词。
  • words
    $(words string),统计单词个数。

文件函数

  • dir
    $(dir names),从文件名列表names中取出目录,例如$(dir /src/foo.c temp)返回/src/ ./
  • suffix
    $(suffix names),从names中取出各个文件的后缀。
  • basename
    同上,只不过取的是前缀名。

控制流函数

  • foreach
    $(foreach var,list,expr),把list中的单词拿出来赋给var,然后执行expr。expr每次的返回会以空格隔开,最后组成整个函数的返回值。
    例如:$(foreach n,a b,$(n).o)返回a.o b.o
    注意var是一个局部变量,其作用域只在foreach中。
  • if
    $(if condition,then-part,else-part),else-part可以省略。
  • shell
    $(shell expr),执行一个shell命令作为返回值。
  • error
    $(error text),停止make并报错text。
  • warning
    同上,不过不退出make而是继续执行。

总结

GNU make的执行步骤如下:

  1. 读入所有的Makefile。
  2. 读入被include的其它Makefile。
  3. 初始化文件中的变量。
  4. 推导隐式规则,并分析所有规则。
  5. 为所有的目标文件创建依赖关系链。
  6. 根据依赖关系,决定哪些目标要重新生成。
  7. 执行生成命令。

第三步并非一开始就将所有变量展开,而是其出现在依赖关系中且要被使用了才展开。

相关推荐
我要出家当道士3 天前
Nginx单向链表 ngx_list_t
数据结构·nginx·链表·c
张飞的猪3 天前
Makefile文件中,两个$的变量变量$$Xxx 与一个$的变量 $Xxx的区别
makefile
LIKEYYLL5 天前
GNU Octave:特性、使用案例、工具箱、环境与界面
服务器·gnu
学习前端的小z8 天前
【C++】小乐乐求和问题的高效求解与算法对比分析
c
集大周杰伦10 天前
C语言指针与数组深入剖析及优化示例 指针解读 数组与指针的关系
c语言·c
学习前端的小z10 天前
【C++ 】for 循环系统深入解析与实现法比较
c
无为扫地僧11 天前
五、windows上vscode构建c/c++环境
c++·vscode·c
学习前端的小z11 天前
【C++】小乐乐求和题目分析n变量类型讨论
c
学习前端的小z12 天前
【C++】青蛙跳跃问题解析与解法
c
枫の准大一13 天前
【C++游记】Vector的使用和模拟实现
c