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 :=)

a = 11 22
b = $(a) 33
# 或者
a := 11 22
b := $(a) 33

其中 = 和 := 的区别在于,:= 只能使用前面定义好的变量,= 可以使用后面定义的变量

= 案例

# Makefile内容
a = $(b) 111
b = 222 333

all:
    @echo $(a)

# bash中执行 make, 可以看出虽然 b 是在 a 之后定义的, 但可以提前使用
$ make
222 333 111
View Code

:= 案例

# Makefile内容
b := $(a) programC.o
a := programA.o programB.o

all:
    @echo $(b)

# bash中执行 make, 可以看出 b 中的 $(a) 为空
$ make
programC.o
View Code

变量替换

# 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
View Code

变量覆盖 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
View Code

目标变量

  使变量的作用域仅限于这个目标(target),而不是对整个 Makefile 都有效

语法:

  • <target :>  <variable-assignment>
  • <target :>  override <variable-assignment> (override作用参见 变量覆盖的介绍)
# Makefile 内容
var_1 := programA.c programB.c programC.c

target1: var_2 := programD.c
target1:
    @echo "var_1:" $(var_1)
    @echo "var_2:" $(var_2)

target2:
    @echo "var_1:" $(var_1)
    @echo "var_2:" $(var_2)

# bash中执行make
$ make target1
"var_1:" programA.c programB.c programC.c
"var_2:" programD.c

$ make target2     <-- target2中显示不了 $(TARGET1-SRCS)
"var_1:" programA.c programB.c programC.c
"var_2:"

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)”,不像真正的目标那样会生成一个目标文件。

典型的伪目标是clean:用来清理编译过程中 中间文件,一般格式如下:

.PHONY: clean   <-- 这句没有也行, 但是最好加上
clean:
    -rm -f *.o

引用其他的 Makefile

语法: include Makefile路径 

# 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
$ 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 中很多时候通过自动变量来简化书写,各个自动变量的含义如下:

自动变量

含义

$@ 目标集合
$% 当目标是函数库文件时,表示其中的目标文件名
$< 第一个依赖目标. 如果依赖目标是多个,逐个表示依赖目标
$? 比目标新的依赖目标的集合
$^ 所有依赖目标的集合,会去除重复的依赖目标
$+ 所有依赖目标的集合,不会去除重复的依赖目标
$* 这个是GNU make特有的,其它的make不一定支持

Makefile高级语法

 

参考文献

Makefile简易教程

Makefile使用总结

Windows安装GNU编译器使用makefile

$ make target

Makefile语法

tar zxvf test.tar.gz -C test

MinGW-w64安装教程——著名C/C++编译器GCC的Windows版本

posted @ 2019-03-26 19:19  凌逆战  阅读(703)  评论(0编辑  收藏  举报