Makefile
Posted lxp-never
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Makefile相关的知识,希望对你有一定的参考价值。
Makefile简介
很多大型项目的编译都是通过Makefile来组织各种库和代码之间的依赖关系。Makefile不仅可以用来编译项目还可以用来组织我们一些日常操作。
Makefile是和make命令一起配合使用的,Makefile就像shell脚本一样,同时执行操作系统的命令。
Makefile的好处就是能够使用一行命令来完成“自动编译”,编译整个工程我们要做的唯一事情就是在shell提示符下输入make命令,整个工程就会自动编译。
make是命令工具,它解释makefile指令规则。
Makefile基本格式如下:
target ... : prerequisites ...
command
...
...
- target: 目标文件
- prerequisties: 生成target所需要的文件或目标
- command: make需要执行的命令(任意的shell命令),makefile中的命令必须以缩进[tab]开头
Makefile的初级语法
规则主要有两个部分:依赖关系 和 生成目标的方法
语法有以下2种
target ... : prerequisites ...
command
...
或
target ... : prerequisites ; command command ...
*注* command太长, 可以用 "\\" 作为换行符
规则中的通配符
- * :表示任意一个或多个字符
- ? :表示任意一个字符
- [...] :[abcd] 表示a,b,c,d中任意一个字符, [^abcd]表示除a,b,c,d以外的字符, [0-9]表示 0~9中任意一个数字
- ~ :表示用户的根目录
路径搜索
将源文件的路径明确在Makefile中,便于编译查找,Makefile中有个特殊的变量 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
Makefileh中的变量
变量定义 (= or :=)
OBJS = programA.o programB.o OBJS-ADD = $(OBJS) programC.o # 或者 OBJS := programA.o programB.o OBJS-ADD := $(OBJS) programC.o
其中 = 和 := 的区别在于, := 只能使用前面定义好的变量, = 可以使用后面定义的变量
测试 =
# Makefile内容 OBJS2 = $(OBJS1) programC.o OBJS1 = programA.o programB.o all: @echo $(OBJS2) # bash中执行 make, 可以看出虽然 OBJS1 是在 OBJS2 之后定义的, 但在 OBJS2中可以提前使用 $ make programA.o programB.o programC.o
测试 :=
# Makefile内容 OBJS2 := $(OBJS1) programC.o OBJS1 := programA.o programB.o all: @echo $(OBJS2) # bash中执行 make, 可以看出 OBJS2 中的 $(OBJS1) 为空 $ make programC.o
变量替换
# Makefile内容 SRCS := programA.c programB.c programC.c OBJS := $(SRCS:%.c=%.o) all: @echo "SRCS: " $(SRCS) @echo "OBJS: " $(OBJS) # bash中运行make $ make SRCS: programA.c programB.c programC.c OBJS: programA.o programB.o programC.o
变量追加值 +=
# Makefile内容 SRCS := programA.c programB.c programC.c SRCS += programD.c all: @echo "SRCS: " $(SRCS) # bash中运行make $ make SRCS: programA.c programB.c programC.c programD.c
变量覆盖 override
作用是使 Makefile中定义的变量能够覆盖 make 命令参数中指定的变量
语法:
- override <variable> = <value>
- override <variable> := <value>
- 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>
- <target :> override <variable-assignment> (override作用参见 变量覆盖的介绍)
# 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 中书写shell命令时可以加2种前缀 @ 和 -, 或者不用前缀.
3种格式的shell命令区别如下:
- 不用前缀 输出 执行的命令 以及 命令执行的结果, 出错的话停止执行
- 前缀 @ 只输出命令执行的结果, 出错的话停止执行
- 前缀 - 命令执行有错的话, 忽略错误, 继续执行
# Makefile 内容 (不用前缀) all: echo "没有前缀" cat this_file_not_exist echo "错误之后的命令" <-- 这条命令不会被执行 # bash中执行 make $ make echo "没有前缀" <-- 命令本身显示出来 没有前缀 <-- 命令执行结果显示出来 cat this_file_not_exist cat: this_file_not_exist: No such file or directory make: *** [all] Error 1 ########################################################### # Makefile 内容 (前缀 @) all: @echo "没有前缀" @cat this_file_not_exist @echo "错误之后的命令" <-- 这条命令不会被执行 # bash中执行 make $ make 没有前缀 <-- 只有命令执行的结果, 不显示命令本身 cat: this_file_not_exist: No such file or directory make: *** [all] Error 1 ########################################################### # Makefile 内容 (前缀 -) all: -echo "没有前缀" -cat this_file_not_exist -echo "错误之后的命令" <-- 这条命令会被执行 # bash中执行 make $ make echo "没有前缀" <-- 命令本身显示出来 没有前缀 <-- 命令执行结果显示出来 cat this_file_not_exist cat: this_file_not_exist: No such file or directory make: [all] Error 1 (ignored) echo "错误之后的命令" <-- 出错之后的命令也会显示 错误之后的命令 <-- 出错之后的命令也会执行
伪目标
伪目标并不是一个"目标(target)", 不像真正的目标那样会生成一个目标文件.
典型的伪目标是 Makefile 中用来清理编译过程中中间文件的 clean 伪目标, 一般格式如下:
.PHONY: clean <-- 这句没有也行, 但是最好加上 clean: -rm -f *.o
引用其他的 Makefile
语法: include <filename> (filename 可以包含通配符和路径)
# Makefile 内容 all: @echo "主 Makefile begin" @make other-all @echo "主 Makefile end" include ./other/Makefile # ./other/Makefile 内容 other-all: @echo "other makefile begin" @echo "other makefile end" # bash中执行 make $ ll total 20K -rw-r--r-- 1 wangyubin wangyubin 125 Sep 23 16:13 Makefile -rw-r--r-- 1 wangyubin wangyubin 11K Sep 23 16:15 makefile.org <-- 这个文件不用管 drwxr-xr-x 2 wangyubin wangyubin 4.0K Sep 23 16:11 other $ ll other/ total 4.0K -rw-r--r-- 1 wangyubin wangyubin 71 Sep 23 16:11 Makefile $ make 主 Makefile begin make[1]: Entering directory `/path/to/test/makefile‘ other makefile begin other makefile end make[1]: Leaving directory `/path/to/test/makefile‘ 主 Makefile end
查看C文件的依赖关系
写 Makefile 的时候, 需要确定每个目标的依赖关系.
GNU提供一个机制可以查看C代码文件依赖那些文件, 这样我们在写 Makefile 目标的时候就不用打开C源码来看其依赖那些文件了.
比如, 下面命令显示在内核源码中 virt/kvm/kvm_main.c 中的依赖关系
$ cd virt/kvm/ $ gcc -MM kvm_main.c kvm_main.o: kvm_main.c iodev.h coalesced_mmio.h async_pf.h <-- 这句就可以加到 Makefile 中作为编译 kvm_main.o 的依赖关系
make 退出码
Makefile的退出码有以下3种:
- 0 表示成功执行
- 1 表示make命令出现了错误
- 2 使用了 "-q" 选项, 并且make使得一些目标不需要更新
指定 Makefile, 指定特定目标
默认执行 make 命令时, GNU make在当前目录下依次搜索下面3个文件 "GNUmakefile", "makefile", "Makefile",
找到对应文件之后, 就开始执行此文件中的第一个目标(target). 如果找不到这3个文件就报错.
非默认情况下, 可以在 make 命令中 -f 指定特定的 Makefile 和特定的 目标.
# Makefile文件名改为 MyMake, 内容 target1: @echo "target [1] begin" @echo "target [1] end" target2: @echo "target [2] begin" @echo "target [2] end" # bash 中执行 make $ ls Makefile $ mv Makefile MyMake $ ls MyMake $ make <-- 找不到默认的 Makefile make: *** No targets specified and no makefile found. Stop. $ make -f MyMake <-- 指定特定的Makefile target [1] begin target [1] end $ make -f MyMake target2 <-- 指定特定的目标(target) target [2] begin target [2] end
Makefile 隐含规则
这里只列一个和编译C相关的.
编译C时,<n>.o 的目标会自动推导为 <n>.c
# Makefile 中 main : main.o gcc -o main main.o #会自动变为: main : main.o gcc -o main main.o main.o: main.c <-- main.o 这个目标是隐含生成的 gcc -c main.c
自动变量
Makefile 中很多时候通过自动变量来简化书写, 各个自动变量的含义如下:
自动变量 |
含义 |
[email protected] | 目标集合 |
$% | 当目标是函数库文件时, 表示其中的目标文件名 |
$< | 第一个依赖目标. 如果依赖目标是多个, 逐个表示依赖目标 |
$? | 比目标新的依赖目标的集合 |
$^ | 所有依赖目标的集合, 会去除重复的依赖目标 |
$+ | 所有依赖目标的集合, 不会去除重复的依赖目标 |
$* | 这个是GNU make特有的, 其它的make不一定支持 |
参考文献:
以上是关于Makefile的主要内容,如果未能解决你的问题,请参考以下文章
使用带有 NMake 样式 Makefile 的 clang-cl 无法回显