定义
Makefile 是一个工程文件的编译规则,描述了整个工程的编译和链接等规则
使用场景
-
数学计算库 math 中的函数,我们得手动添加参数 -Im;
-
小型数据库 SQLite 中的函数,我们得手动添加参数 -lsqlite3;
-
线程,我们需要去手动添加参数 -lpthread。
-
多个文件链接
Makefile 支持多线程并发操作,会极大的缩短我们的编译时间,并且当我们修改了源文件之后,编译整个工程的时候,make 命令只会编译我们修改过的文件,没有修改的文件不用重新编译,不增加或者是删除工程中的文件,Makefile 基本上不用去修改。
规则
依赖的关系和执行的命令,格式如下:目标和依赖文件之间要使用冒号分隔开,命令的开始一定要使用 Tab键
targets : prerequisites
command
#targets:规则的目标,可以是 Object File(一般称它为中间文件),也可以是可执行文件或一个标签;
#prerequisites:是我们的依赖文件,要生成 targets 需要的文件或者是目标。可有可无
#command:make 需要执行的命令(任意的 shell 命令)。可以有多条命令,每一条命令占一行。
Makefile只有行注释:#
make 执行的是 Makefile 中的第一规则(Makefile 中出现的第一个依赖关系),此规则的第一目标称之为“最终目标”.如:
main:main.o test1.o test2.o # 最终生成main可执行文件, 其中main.o、test1.o、test2.o文件是依赖文件
gcc main.o test1.o test2.o -o main
main.o:main.c test.h #得到main.o
gcc -c main.c -o main.o
test1.o:test1.c test.h #得到test1.o
gcc -c test1.c -o test1.o
test2.o:test2.c test.h #得到test2.o
gcc -c test2.c -o test2.o
工作流程:
清除工作
一般会在Makefile文尾处会添加一个clean规则,如下面用于内核编程的Makefile:
bj-m := myko.o
KERNELDR := ~/linux-4.4.72/
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDR) M=$(PWD) modules
moduels_install:
$(MAKE) -C $(KERNELDR) M=$(PWD) modules_install
clean:
rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions
clean 是独立的,它只是一个伪目标所以我们在执行 make 的时候也不会执行下面的命令。在shell 中执行 “make clean” 命令即可执行下面定义的命令
变量
格式:key=value
,调用:$(key) 或 ${key}
赋值方式:
-
简单赋值 ( := ) 编程语言中常规理解的赋值方式,只对当前语句的变量有效。(局部)
-
递归赋值 ( = ) 赋值语句可能影响多个变量,所有目标变量相关的其他变量都受影响。(全局)
-
条件赋值 ( ?= ) 如果变量未定义,则使用符号中的值定义变量。如果该变量已经赋值,则该赋值语句无效。(判空)
-
追加赋值 ( += ) 原变量用空格隔开的方式追加一个新值。(追加)
系统变量
变量 | 用途 |
---|---|
$@ | 表示规则的目标文件名 |
$% | 当目标文件是一个静态库文件时,代表静态库的一个成员名 |
$< | 规则的第一个依赖的文件名 |
$? | 所有目标文件更新的依赖文件列表,空格分隔 |
$^ | 代表的是所有依赖文件列表,使用空格分隔 |
$+ | 类似“$^”,但是它保留了依赖文件中重复出现的文件。主要用在程序链接时库的交叉引用场合。 |
$* | 在模式规则和静态模式规则中,代表“茎”。“茎”是目标模式中“%”所代表的部分(当文件名中存在目录时, “茎”也包含目录部分)。 |
$(@D) | 表示文件的目录部分(不包括斜杠)。如果 “$@” 表示的是 “dir/foo.o” 那么 “$(@D)” 表示的值就是 “dir”。如果 “$@” 不存在斜杠(文件在当前目录下),其值就是 “.” |
$(@F) | 表示的是文件除目录外的部分(实际的文件名)。如果 “$@” 表示的是 “dir/foo.o”,那么 “$@F” 表示的值为 “foo.o” |
test:test.o test1.o test2.o
gcc -o $@ $^ #等同于gcc -o test test.o test1.o test2.o
test.o:test.c test.h
gcc -o $@ $<
test1.o:test1.c test1.h
gcc -o $@ $<
test2.o:test2.c test2.h
gcc -o $@ $< #等同于gcc -o test2.o test2.c
编译环境
当采用模块话开发时,比如h文件放在include文件目录下,c文件放在src目录下,而Makefile放在上一级目录
那么此时make肯定是找不到文件的,那么就需要对它指定路径 :
VPATH=src include
#或者采用vpath (小写)
#vpath %.c src
#vpath %.h include
main:main.o list1.o list2.o
gcc -o $@ $<
main.o:main.c
gcc -o $@ $^
list1.o:list1.c list1.h
gcc -o $@ $<
list2.o:list2.c list2.h
gcc -o $@ $<
VPTH表示指定make的路径搜索为当前目录下的src目录下和当前目录的include目录下
隐含规则
即make会智能处理的规则,不需要手动定义也可完成的默认规则,如下代码
test:test.o
gcc -o test test.o
test.o:test.c
#gcc -o test.o test.c #这里注释了后没有手动生成test.o文件,但是makefile会默认执行 gcc -o test.o test.c
经过上面又可简化为如下代码
test:test.o
test.o:test.c
从而代替了 gcc -o test.o test.c
----> gxx -o test test.o
注意:隐含条件只能省略中间目标文件重建的命令和规则,但是最终目标的命令和规则不能省略
伪目标
定义:.PHONY:clean \n clean: 或clean:
区别是当make工作目录下存在一个clean文件时就会发生冲突从而导致停止执行
也可以认为是解决文件名冲突的一个解决方案
关键字
- @符号 : 不在控制台显示该命令,或make -s 所有命令不显示打印,如下
@echo hello -------输出-------> hello
echo hello -------输出-------> echo hello
- include : 暂停读取当前的 Makefile,而是去读 “include” 包含的文件,读取结束后再继读取当前的 Makefile 文件
- $(MAKE) : make嵌套执行,如下
subsystem:
cd subdir && $(MAKE) #到subdir目录下 执行make命令
#$(MAKE) -C subdir #另一种写法
- export :用于make嵌套执行时的变量传递
make参数
参数选项 | 功能 |
---|---|
-b,-m | 忽略,提供其他版本 make 的兼容性 |
-B,–always-make | 强制重建所有的规则目标,不根据规则的依赖描述决定是否重建目标文件。 |
-C DIR,–directory=DIR | 在读取 Makefile 之前,进入到目录 DIR,然后执行 make。当存在多个 “-C” 选项的时候,make 的最终工作目录是第一个目录的相对路径。 |
-d | make 在执行的过程中打印出所有的调试信息,包括 make 认为那些文件需要重建,那些文件需要比较最后的修改时间、比较的结果,重建目标是用的命令,遗憾规则等等。使用 “-d” 选项我们可以看到 make 构造依赖关系链、重建目标过程中的所有的信息。 |
–debug[=OPTIONS] | make 执行时输出调试信息,可以使用 “OPTIONS” 控制调试信息的级别。默认是 “OPTIONS=b” ,“OPTIONS” 的可值为以下这些,首字母有效:all、basic、verbose、implicit、jobs、makefile。 |
-e,–enveronment -overrides | 使用环境变量定义覆盖 Makefile 中的同名变量定义。 |
-f=FILE,–file=FILE, --makefile=FILE | 指定文件 “FILE” 为 make 执行的 Makefile 文件 |
-p,–help | 打印帮助信息。 |
-i,–ignore-errors | 执行过程中忽略规则命令执行的错误。 |
-I DIR,–include-dir=DIR | 指定包含 Makefile 文件的搜索目录,在Makefile中出现另一个 “include” 文件时,将在 “DIR” 目录下搜索。多个 “-i” 指定目录时,搜索目录按照指定的顺序进行。 |
-j [JOBS],–jobs[=JOBS] | 可指定同时执行的命令数目,没有 “-j” 的情况下,执行的命令数目将是系统允许的最大可能数目,存在多个 “-j” 目标时,最后一个目标指定的 JOBS 数有效。 |
-k,–keep-going | 执行命令错误时不终止 make 的执行,make 尽最大可能执行所有的命令,直至出现知名的错误才终止。 |
-l load,–load-average=[=LOAD],–max-load[=LOAD] | 告诉 make 在存在其他任务执行的时候,如果系统负荷超过 “LOAD”,不在启动新的任务。如果没有指定 “LOAD” 的参数 “-l” 选项将取消之前 “-l” 指定的限制。 |
-n,–just-print,–dry-run | 只打印执行的命令,但是不执行命令。 |
-o FILE,–old-file=FILE, --assume-old=FILE | 指定 "FILE"文件不需要重建,即使是它的依赖已经过期;同时不重建此依赖文件的任何目标。注意:此参数不会通过变量 “MAKEFLAGS” 传递给子目录进程。 |
-p,–print-date-base | 命令执行之前,打印出 make 读取的 Makefile 的所有数据,同时打印出 make 的版本信息。如果只需要打印这些数据信息,可以使用 “make -qp” 命令,查看 make 执行之前预设的规则和变量,可使用命令 “make -p -f /dev/null” |
-q,-question | 称为 “询问模式” ;不运行任何的命令,并且无输出。make 只返回一个查询状态。返回状态 0 表示没有目标表示重建,返回状态 1 表示存在需要重建的目标,返回状态 2 表示有错误发生。 |
-r,–no-builtin-rules | 取消所有的内嵌函数的规则,不过你可以在 Makefile 中使用模式规则来定义规则。同时选项 “-r” 会取消所有后缀规则的隐含后缀列表,同样我们可以在 Makefile 中使用 “.SUFFIXES”,定义我们的后缀名的规则。“-r” 选项不会取消 make 内嵌的隐含变量。 |
-R,–no-builtin-variabes | 取消 make 内嵌的隐含变量,不过我们可以在 Makefile 中明确定义某些变量。注意:“-R” 和 “-r” 选项同时打开,因为没有了隐含变量,所以隐含规则将失去意义。 |
-s,–silent,–quiet | 取消命令执行过程中的打印。 |
-S,–no-keep-going, --stop | 取消 “-k” 的选项在递归的 make 过程中子 make 通过 “MAKEFLAGS” 变量继承了上层的命令行选项那个。我们可以在子 make 中使用“-S”选项取消上层传递的 “-k” 选项,或者取消系统环境变量 “MAKEFLAGS” 中 "-k"选项。 |
-t,–touch | 和 Linux 的 touch 命令实现功能相同,更新所有的目标文件的时间戳到当前系统时间。防止 make 对所有过时目标文件的重建。 |
-v,version | 查看make的版本信息。 |
-w,–print-directory | 在 make 进入一个子目录读取 Makefile 之前打印工作目录,这个选项可以帮助我们调试 Makefile,跟踪定位错误。使用 “-C” 选项时默认打开这个选项。 |
–no-print-directory | 取消 “-w” 选项。可以是 用在递归的 make 调用的过程中 ,取消 “-C” 参数的默认打开 “-w” 的功能。 |
-W FILE,–what-if=FILE, --new-file=FILE, --assume-file=FILE | 设定文件 “FILE” 的时间戳为当前的时间,但不更改文件实际的最后修改时间。此选项主要是为了实现对所有依赖于文件 “FILE” 的目标的强制重建。 |
–warn-undefined-variables | 在发现 Makefile 中存在没有定义的变量进行引用时给出告警信息。此功能可以帮助我们在调试一个存在多级嵌套变量引用的复杂 Makefile。但是建议在书写的时候尽量避免超过三级以上的变量嵌套引用。 |