2-Makefile初级语法

Makefile规则

1、规则语法

规则主要有2部分: 依赖关系和生成目标的方法。

语法有以下2种:

target ... : prerequisites ...
    command
    ...

或者:

target ... : prerequisites ; command
    command
    ...

【温馨提示】command太长,可以用 "\" 作为换行符。

2、规则中的通配符

*     :: 表示任意一个或多个字符

?    :: 表示任意一个字符

[...] :: ex. [abcd] 表示a,b,c,d中任意一个字符,[^abcd]表示除a,b,c,d以外的字符,[0-9]表示 0~9中任意一个数字

~    :: 表示用户的home目录

3、路径搜索

当一个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

Makefile中的变量

1、变量定义(= 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

2、变量替换

# 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

3、变量追加值 +=

# 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

4、变量覆盖 override

作用是使Makefile中定义的变量能够覆盖make命令参数中指定的变量。

语法:

override <variable> = <value>
override <variable> := <value>
override <variable> += <value>

【范例代码】使用override

# 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

Makefile命令前缀

Makefile中书写shell命令时可以加2种前缀@和-,或者不用前缀。

3种格式的shell命令区别如下:

1、不用前缀 :: 输出执行的命令以及命令执行的结果,出错的话停止执行

2、前缀 @   :: 只输出命令执行的结果,出错的话停止执行

3、前缀 -     :: 命令执行有错的话,忽略错误, 继续执行

【范例代码】

# 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命令中指定特定的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

make参数介绍

make的参数有很多,可以通过make -h去查看,下面只介绍几个我认为比较有用的。

参数

含义

--debug[=<options>] 输出make的调试信息, options 可以是 a, b, v
-j --jobs 同时运行的命令的个数, 也就是多线程执行 Makefile
-r --no-builtin-rules 禁止使用任何隐含规则
-R --no-builtin-variabes 禁止使用任何作用于变量上的隐含规则
-B --always-make 假设所有目标都有更新, 即强制重编译

 

 

 

 

 

 

Makefile隐含规则

这里只列一个和编译C相关的。编译C时,.o的目标会自动推导为.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

隐含规则中的命令变量和命令参数变量

1、命令变量,书写Makefile可以直接写shell时用这些变量

下面只列出一些C相关的:

变量名

含义

RM rm -f
AR ar
CC cc
CXX g++

 

 

 

 

 

 

【范例代码】

# Makefile 内容
all:
    @echo $(RM)
    @echo $(AR)
    @echo $(CC)
    @echo $(CXX)

# bash 中执行make, 显示各个变量的值
$ make
rm -f
ar
cc
g++ 

2、命令参数变量

变量名

含义

ARFLAGS AR命令的参数
CFLAGS C语言编译器的参数
CXXFLAGS C++语言编译器的参数

 

 

 

 

 

【范例代码】以CFLAGS为例

# test.c 内容
#include <stdio.h>

int main(int argc, char *argv[])
{
    printf ("Hello Makefile\n");
    return 0;
}

# Makefile 内容
test: test.o
    $(CC) -o test test.o

# bash 中用 make 来测试
$ ll
total 24K
-rw-r--r-- 1 wangyubin wangyubin  69 Sep 23 17:31 Makefile
-rw-r--r-- 1 wangyubin wangyubin 14K Sep 23 19:51 makefile.org   <-- 请忽略这个文件
-rw-r--r-- 1 wangyubin wangyubin 392 Sep 23 17:31 test.c

$ make
cc    -c -o test.o test.c
cc -o test test.o               <-- 这个是自动推导的

$ rm -f test test.o

$ make CFLAGS=-Wall             <-- 命令中加的编译器参数自动追加入下面的编译中了
cc -Wall   -c -o test.o test.c
cc -o test test.o

3、自动变量

Makefile中很多时候通过自动变量来简化书写,各个自动变量的含义如下:

自动变量

含义

$@ 目标集合
$% 当目标是函数库文件时, 表示其中的目标文件名
$< 第一个依赖目标. 如果依赖目标是多个, 逐个表示依赖目标
$? 比目标新的依赖目标的集合
$^ 所有依赖目标的集合, 会去除重复的依赖目标
$+ 所有依赖目标的集合, 不会去除重复的依赖目标
$* 这个是GNU make特有的, 其它的make不一定支持
posted @ 2018-06-12 13:19  老姚大大  阅读(407)  评论(0编辑  收藏  举报