Makefile
通常来说Makefile和make命令是配合使用的。大型项目通过Makefile来组织编译,通过Makefile清晰各种库和代码的依关系
Makefile文件中的命令有一定规范,在文件编写好之后即可执行一条make命令编译整个工程。不同的make可能会有所不同,在语法上也会有所区别,但是基本思想类似,最广泛的是GUNmake。
GUN make的工作方式
- 读入主Makefile(主Makefile中可以引用其他的Makefile)
- 读入被include的其他Makefile
- 初始化文件中的变量
- 推导隐晦规则,并分析所有规则
- 为所有的目标文件创建依赖关系链
- 根据依赖关系,决定那些目标要重新生成
- 执行生成命令
语法规则
目标 . . . :依赖 . . .
命令1
命令2
或者
目标 . . . :依赖 . . .;命令1
命令2
命令3
Makefile不会关心命令如何执行的,仅仅是会去执行所有定义的命令,和输入命令行是一样的效果
- 目标即生成的文件:如果文件的更新时间晚于依赖文件更新时间,说明依赖文件没有改动,目标文件不需要重新编译,否则会重新编译并更新目标文件。
- 终极文件:默认情况下Makefile的第一个目标即为终极文件
- 依赖:即目标文件由那些文件生成
- 命令:即通过执行命令由依赖文件生成目标文件。注意每条命令之前必须有一个tab保持缩进,这是语法要求。
- all:Makefile文件默认只生成第一个目标文件即完成翻译,但是可以通过all指定需要生成的目标文件
- 显示规则:说明如何生成一个或者多个目标文件(包括生成的文件,文件的依赖文件,生成的命令)
- 隐晦规则:make的自动推导功能所执行的规则
- 变量定义:Makefile中定义的变量
- 文件指示:Makefile中引用其他的Makefile;指定Makefile中有效的部分;定义一个多行命令
- 注释:Makefile只有行注释“#”,如果要使用或者输出“#”字符,需要进行转义,“\#”
变量覆盖override
作用是使Makefile中定义的变量能够覆盖make命令参数中指定的变量
语法:
override variable = value
# Makefile内容 (没有用override)
SRCS := programA.c programB.c programC.c
all:
@echo "SRCS: " $(SRCS)
# bash中运行make
$ make SRCS=nothing
SRCS: nothing
#################################################
# Makefile内容 (用override)
override SRCS := programA.c programB.c programC.c
all:
@echo "SRCS: " $(SRCS)
# bash中运行make
$ make SRCS=nothing
SRCS: programA.c programB.c programC.c
变量
$符号表示取变量的值,当变量名多于一个字符时,使用"( )"
$^表示所有的依赖文件
$@表示生成的目标文件
$<表示第一个依赖文件
目标变量
作用是使变量的作用域仅限于这个目标(target),而不像之前例子中定义的变量,对整个Makefile都有效
target :: variable-assignment
# Makefile 内容
SRCS := programA.c programB.c programC.c
target1: TARGET1-SRCS := programD.c
target1:
@echo "SRCS: " $(SRCS)
@echo "SRCS: " $(TARGET1-SRCS)
target2:
@echo "SRCS: " $(SRCS)
@echo "SRCS: " $(TARGET1-SRCS)
# bash中执行make
$ make target1
SRCS: programA.c programB.c programC.c
SRCS: programD.c
$ make target2 <-- target2中显示不了 $(TARGET1-SRCS)
SRCS: programA.c programB.c programC.c
SRCS:
变量赋值
- “=”是最普通的等号,在Makefile中使用“=”进行赋值,变量的值是整个Makefile中最后被指定的值
- “:=”表示直接赋值,赋予当前位置的值
- “?=”表示如果该变量没有被赋值,则进行赋值
- “+=”和平时写代码的理解是一样的,表示将符号后面的值添加到前面的变量上
预定义变量
CC:c编译器的名称,默认值为cc
回显问题:Makefile中的命令都会被打印出来。如果不想打印命令部分,可以使用@去除回显
函数
规则中的通配符
* ::表示一个或者多个字符
? ::表示任意一个字符
-
[…] ::ex.[abcd]表示a,b,c,d中任意一个字符[ ^ abcd]表示除a,b,c,d以外的字符,[0-9]表示 0~9中任意一个数字
- :: 表示用户的home目录
通配符
SRC = $(wildcard ./*.c)
匹配目录下所有.c文件,并赋值给SRC变量
OBJ = $(patsubst %.c, %.o, $(SRC))
函数有三个参数,是取出SRC中的所有值,然后将.c转成.o最后赋值给OBJ变量
路径搜索
当一个Makefile中涉及了大量的源文件时(这些源文件和Makefile极有可能不在同一个目录中)
这是,最好将源文件的路径明确在Makefile中,便于编译时直接查找,Makefile中国有一个特殊的变量VPATH就是完成这个功能的指定VPATH之后,如果当前目录中没有找到响应文件或者依赖的文件,Makefile回到VPATH指定的路径中再去查找
VPATH使用方法
vpath <directories> :: 当前目录中找不到文件时, 就从<directories>中搜索
vpath <pattern> <directories> :: 符合<pattern>格式的文件, 就从<directories>中搜索
vpath <pattern> :: 清除符合<pattern>格式的文件搜索路径
vpath :: 清除所有已经设置好的文件路径
# 示例1 - 当前目录中找不到文件时, 按顺序从 src目录 ../parent-dir目录中查找文件
VPATH src:../parent-dir
# 示例2 - .h结尾的文件都从 ./header 目录中查找
VPATH %.h ./header
# 示例3 - 清除示例2中设置的规则
VPATH %.h
# 示例4 - 清除所有VPATH的设置
VPATH
伪目标
伪目标只是一个标签,clean是个伪目标没有依赖文件,只有用make来调用的时候才会执行
当目标下拥有和make命令同名的文件时,执行make命令就会出现错误,解决方法就是使用伪目标。
SRC = $(wildcard *.c)
OBJ = $(patsubst %.c, %.o, $(SRC))
ALL: hello.out
hello.out: $(OBJ)
gcc $< -o $@
$(OBJ): $(SRC)
gcc -c $< -o $@
clean:
rm -rf $(OBJ) hello.out
.PHONY: clean ALL
其他常用功能
代码清理clean
可以编译一条clean语句,清理所有make命令产生的所有文件
SRC = $(wildcard *.c)
OBJ = $(patsubst %.c, %.o, $(SRC))
ALL: hello.out
hello.out: $(OBJ)
gcc $< -o $@
$(OBJ): $(SRC)
gcc -c $< -o $@
clean:
rm -rf $(OBJ) hello.out
Makefile命令前缀
有三种前缀
无前缀:输出执行的命令以及命令执行的结果,出错停止执行
前缀@:只输出命令执行的结果,出错停止执行
前缀-:命令出错的话忽略错误,继续执行
引用其他的Makefile
#include < filename >(filename 可以包括通配符和路径)