1

#导入Word文档图片# Makefile使用规则

 1 year ago
source link: https://blog.51cto.com/u_11822586/5353471
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

1.1 makefile简介​

在 Linux(unix )环境下使用GNU 的make工具能够比较容易的构建一个属于你自己的工程,整个工程的编译只需要一个命令就可以完成编译、连接以至于最后的执行。不过这需要我们投入一些时间去完成一个或者多个称之为Makefile 文件的编写。

所要完成的Makefile 文件描述了整个工程的编译、连接等规则。其中包括:工程中的哪些源文件需要编译以及如何编译、需要创建那些库文件以及如何创建这些库文件、如何最后产生我们想要的可执行文件。尽管看起来可能是很复杂的事情,但是为工程编写Makefile 的好处是能够使用一行命令来完成“自动化编译”,一旦提供一个(通常对于一个工程来说会是多个)正确的 Makefile。编译整个工程你所要做的唯一的一件事就是在shell 提示符下输入make命令。整个工程完全自动编译,极大提高了效率。

make是一个命令工具,它解释Makefile 中的指令(应该说是规则)。在Makefile文件中描述了整个工程所有文件的编译顺序、编译规则。Makefile 有自己的书写格式、关键字、函数。像C 语言有自己的格式、关键字和函数一样。而且在Makefile 中可以使用系统shell所提供的任何命令来完成想要的工作。Makefile(在其它的系统上可能是另外的文件名)在绝大多数的IDE 开发环境中都在使用,已经成为一种工程的编译方法。

1.2 编写makefile格式说明​

  1. Makefile文件里使用shell命令时,命令的前面必须是TAB键。
  2. 输入make默认执行Makefile文件里的第一个命令。
  3. 在Makefile文件里#号代表注释
  4. Make命令支持寻找解析:makefile”和“Makefile”这两种默认文件名。
  5. 如果要指定特定的 Makefile,你可以使用 make 的“-f”和“--file”参数,如:make -f Make.Linux 或 make --file Make.AIX。
  6. make –v 输出make版本和版权问题
  7. makefile里使用echo命令进行信息输出,类似于C语言的printf。示例:echo “12345” 或者 echo $(ABC)
  8. 在shell命令前加上@符号,可以隐藏命令的执行过程! 比如:@echo “12345” ,只会输出12345。
  • Make命令的参数选项:

Make命令本身可带有四种参数:标志、宏定义、描述文档名和目标文档名。其标准形式为:

Make [flags] [macro definitions] [targets]

Unix系统下标志位flags选项及其含义为:

-f file 指定file文档为描述文档,假如file参数为"-"符,那么描述文档指向标准输入。假如没有"-f"参数,则系统将默认当前目录下名为makefile或名为Makefile的文档为描述文档。在Linux中, GNU make 工具在当前工作目录中按照GNUmakefile、makefile、Makefile的顺序搜索 makefile文档。

-i 忽略命令执行返回的出错信息。

-s 沉默模式,在执行之前不输出相应的命令行信息。

-r 禁止使用build-in规则。

-n 非执行模式,输出任何执行命令,但并不执行。

-t 更新目标文档。

-qmake操作将根据目标文档是否已更新返回"0"或非"0"的状态信息。

-p 输出任何宏定义和目标文档描述。

-dDebug模式,输出有关文档和检测时间的周详信息。

Linux下make标志位的常用选项和Unix系统中稍有不同,下面只列出了不同部分:

-c dir 在读取 makefile 之前改变到指定的目录dir。

-I dir 当包含其他 makefile文档时,利用该选项指定搜索目录。

-h help文挡,显示任何的make选项。

-w 在处理 makefile 之前和之后,都显示工作目录。

1.3 makefile变量的定义与使用​

注意:变量只能在目标:符号范围之外进行定义赋值。

#导入Word文档图片# Makefile使用规则_Makefile

变量的定义格式: ABC=

变量的引用方式: $(ABC)

变量的赋值方式:

  1. 直接赋值:ABC=1234
  2. 赋值多个值:ABC= 123 567 890
  3. 在之前的变量基础上增加值:ABC+=789
#导入Word文档图片# Makefile使用规则_Makefile_02

图1-1 变量的定义与使用

变量在目标的前面或者后面定义都可以正常使用,make会先解析整个文件,然后再执行特定的目标。

1 all:

2 echo "a="$(a)

3 a=1234567890

4 a+=8888

1.4 echo命令​

通常,make 会把其要执行的命令行在命令执行前输出到屏幕上。当我们用“@”字符在命令行前,那么,这个命令将不被 make 显示出来,最具代表性的例子是,我们用这个功能来像屏幕显示一些信息。如:

@echo 正在编译 XXX 模块......

当 make 执行时,会输出“正在编译 XXX 模块......”字串,但不会输出命令,如果没有“@”,那么,make 将输出:

echo 正在编译 XXX 模块......

如果 make 执行时,带入 make 参数“-n”或“--just-print”,那么其只是显示命令,但不会执行命令,这个功能很有利于我们调试我们的 Makefile,看看我们书写的命令是执行起来是什么样子的或是什么顺序的。

而 make 参数“-s”或“--slient”则是全面禁止命令的显示。

-i 表示忽略make中出现的错误!

  • 同时输出多个值示例:

@echo "1234""5678""8900"

@echo "1234" "5678" "8900"

@echo "1234" $(aa) $(bb)........

@echo "1234 =$(ppp)"

  • 输出””号

@echo "\"AAAAAA"\"

输出结果:

"AAAAAA"

1.5 Makefile的条件判断​

1.5.1 ifeq与ifneq​

使用条件判断,可以让 make 根据运行时的不同情况选择不同的执行分支。条件表达式可以是比较变量的值,或是比较变量和常量的值。

all:#注意ifeq后边必须要有一个空格

ifeq (12,13)#相等为真,执行下面代码。不相等就为假,执行else下边代码

@echo "相等!"

@echo "不相等!"

endif#条件判断的结束语句

我们可以从上面的示例中看到三个关键字:ifeq、else 和 endif。ifeq 的意思表示条件语句的开始,并指定一个条件表达式,表达式包含两个参数,以逗号分隔,表达式以圆括号括起。else 表示条件表达式为假的情况。endif 表示一个条件语句的结束,任何一个条件表达式都应该以 endif 结束。注意ifeq后边必须要有一个空格。其他linux命令需要以TAB键开头。

与ifeq相反

ifneq (12,13)

@echo "不相等!"

@echo "相等!"

endif

1.5.2 ifdef与ifndef​

关键字“ifdef”。语法是:ifdef <variable-name>
如果变量<variable-name>的值非空,那到表达式为真。否则,表达式为假。

ABC=123

ifdef ABC #检测ABC是否定义

@echo "已经定义!"

@echo "没有定义!"

Endif

与ifdef刚好相反

ABC=123

ifndef ABC #检测ABC是否定义

@echo "没有定义!"

@echo "已经定义!"

endif

1.6 设置Makefile文件搜索路径​

注意:vpath和VPATH 设置的路径只针对Makefile的依赖有效。对linux的命令无效。比如:GCC

gcc 编译依赖的.h文件需要使用-I(大写i) 进行指定!

1.6.1 VPATH​

VPATH 特殊变量,可以设置Makefile中所有文件的搜索路径,包括依赖文件和目标文件。

变量“VPATH”的定义中,使用空格或者冒号(:)将多个目录分开。make 搜索的目录顺序,按照变量“VPATH”定义中顺序进行(当前目录永远是第一搜索目录)。

VPATH = src:../headers

它指定了两个搜索目录,“src”和“../headers”。

  • VPATH示例:

VPATH=src #设置搜索的路径

all:123.o #依赖条件

@gcc 123.o -o app

注意:gcc编译时需要自己包含.h头文件。 否则会报错。 比如:-I ./include -L 表示指定库路径。

1.6.2 vpath​

vpath:关键字

它所实现的功能和上一小节提到的“VPATH”变量很类似,但是

它更为灵活。它可以为不同类型的文件(由文件名区分)指定不同的搜索目录。

vpath %.c ./FILE_1 #设置.c文件的搜索路径

vpath %.h ./FILE_1 #设置.h文件的搜索路径

vpath与VPATH的区别在于VPATH指定全局的搜索路径,而vpath可以针对特定的文件搜索路径。vpath命令主要有三种形式:

vpath pattern path : 符合pattern的文件在path目录搜索。

vpath pattern : 清除pattern指定的文件搜索路径

vpath : 清除所有文件搜索路径。

  • vpath示例:

vpath %.c src

all:123.o

@gcc 123.o -o app

注意:如果使用makefile的自动推导功能就不能给GCC指定头文件路径,编译就会报错,可将.h和.c放入同一个文件下即可。

1.7 makefile之间嵌套调用与参数传递​

1.7.1 Makefile的嵌套调用​

第一层的makefile:

ADDR=./addr_1 #存放makefile文件的路径

make -C $(ADDR)

@echo "调用成功!"

第二层的Makefile:

@echo "下层Makefile调用成功!!"

1.7.2 Makefile之间传参传递-1​

使用export关键字声明!

第一层Makefile:

ADDR=./addr_1

export ABC+=123 456 789

make -C $(ADDR)

@echo "调用成功!"

第二层makefile:

@echo "下层Makefile调用成功!!"

@echo $(ABC)

1.7.3 Makefile之间传参传递2​

直接传递参数。

第一层Makefile:

ADDR=./addr_1

make -C $(ADDR) ABC="123456789"

@echo "调用成功!"

第二层makefile:

@echo "下层Makefile调用成功!!"

@echo $(ABC)

1.7.4 指定调用下层Makefile命令1​

第一层Makefile:

ADDR=./addr_1

make -C $(ADDR) ABC="123456789" clean

@echo "调用成功!"

第二层makefile:

@echo "下层Makefile调用成功!!"

@echo $(ABC)

clean:

@echo "下层clean调用成功!!"

1.7.5 指定调用下层Makefile命令2​

第一层Makefile:

ADDR=./addr

make -C $(ADDR) obj1obj2

第二层makefile:

@echo "all命令调用成功!"

obj1:

@echo "obj1命令调用成功!"

obj2:

@echo "obj2命令调用成功!"

执行结果:

obj1命令调用成功!

obj2命令调用成功!

1.8 Makefile获取shell命令的输出​

比如命令:ls pwd

两种形式调用:1. `pwd` 2. $(shell pwd)

  • 调用示例1 :

A+= $(shell ls)

B+= `ls`

@echo $(A)

@echo $(B)

  • 调用示例2:

ADDR=`pwd`/addr_1

@echo $(ADDR)

make -C $(ADDR) PWD=`pwd`

1.9 自动化编译​

目的:将三个.c文件,和两个.h文件编译一个可执行文件。

#include "main_1.c"

#include "main_2.h"

#include "main_2.c"

#include "main_3.h"

#include "main_3.c"

1.9.1 Makefile文件编写示例1​

NUM就是将要生成的目标文件。

NUM:main_1.o main_2.o main_3.o#依赖项Makefile就是一层层的查找依赖

gcc -o NUM main_1.o main_2.o main_3.o

main_1.o:main_1.c main_2.h main_3.h

gcc -c main_1.c

main_2.o:main_2.c main_2.h

gcc -c main_2.c

main_3.o:main_3.c main_3.h

gcc -c main_3.c

clear:

rm *.o -rf

1.9.2 Makefile文件编写示例2​

使用Makefile自动推导的功能

OBJ=main_1.o main_2.o main_3.o #变量赋值,将依赖文件赋值给OBJ变量

NUM: $(OBJ)

gcc -o NUM $(OBJ)

main_1.o:main_1.c main_2.h main_3.h #生成main_1.o需要依赖下面的文件

main_2.o:main_2.c main_2.h #使用Makefile自动推导功能,省去gcc -c main_3.c

main_3.o:main_3.c main_3.h

clear:

rm $(OBJ) -rf

1.9.3 Makefile文件编写示例3​

Makefile文件自动推导功能

OBJ=main_1.o main_2.o main_3.o #变量赋值,将依赖文件赋值给OBJ变量

NUM: $(OBJ)

gcc -o NUM $(OBJ)

$(OBJ):main_2.h main_3.h #将依赖文件合在一起,自动推导的命令

clear:

rm $(OBJ) -rf

1.10 Makefile的特殊符号​

  • include包含其他Makefile。 语法: include <路径>

1.10.1 赋值符号​

最基本的赋值语句。

覆盖变量之前的值。比如:ABC=123 ABC:=897 那么ABC最终的值等于897

如果变量定义过,则使用之前的值,本次赋值没有效。

追加变量,每个变量之间自动使用空格隔开。

通配符,表示匹配所有文件。

忽略命令的错误。比如: -rm 123.c 。如果123.c文件不存在,rm删除就会报错,加上-可以忽略错误。

加在命令前面,隐藏命令的输出

依赖规则定义符。 格式:规则:依赖文件

1.10.2 自动化编译​

表示目标文件。

$<

表示依赖文件集合中的第一个依赖文件。

表示更新的依赖文件。

表示所有的依赖文件,去除重复的依赖文件。

和$^符号一样 ,没有去重的功能。

  • 加入自动化编译变量的makefile写法

目的:编译1个.h和2个.c

CC=gcc

OBJ=main.o print.o

app:$(OBJ)

$(CC) -o $@ $(OBJ)

%.o:%.c

$(CC) -c $< -o $@ 同等于 $(CC) -c $< 不指定生成目标名称,默认使用默认的.c文件命名。

clean:

@rm $(OBJ) -fv

1.11 常用的makefile函数​

注意:函数只能在目标之外调用。

1.11.1 字符串替换函数​

函数原型格式:$(subst <A>,<C>,<D>)

函数功能:将D中的A全部替换为C

函数返回值:替换后的完整值

ABC=1111188888

aa=$(subst 1,A,$(ABC)) 注意:subst后面必须有一个空格

@echo "变量="$(aa)

输出结果:

[root@xiaolong 2th]# make

变量=AAAAA88888

1.11.2 去掉字符串的前后空字符串​

函数原型格式:$(strip <str>)

函数功能:将str字符串的前后空字符去掉。

函数返回值:替换后的完整值

ABC=" 12345"

DATA=$(strip $(ABC))

@echo "替换前"=$(ABC)

@echo "替换后"=$(DATA)

输出结果:

[root@xiaolong 2th]# make

替换前= 12345

替换后= 12345

1.11.3 字符串查找​

函数原型格式:$(findstring <A>,<C>)

函数功能:在C数据包中查找是否有A数据存在

函数返回值:查找成功返回查找到的数据,否则返回值空字符

ABC=" 12345"

@echo $(findstring 5,$(ABC))

输出结果:

[root@xiaolong 2th]# make

ABC=12345哈哈

@echo $(findstring 哈,$(ABC))

输出结果:

[root@xiaolong 2th]# make

ABC=12345

ifeq ($(findstring 5,$(ABC)),5)

@echo "查找成功"

@echo "查找失败"

endif

输出结果:

[root@xiaolong 2th]# make

1.11.4 执行shell命令

格式:$(shell <执行的shell命令>)

#导入Word文档图片# Makefile使用规则_Makefile_03

1.11.5 产生错误信息

格式:$(error <想要打印的错误提示>)

功能:执行该函数会立即产生一个错误,终止Makefile的执行。

ABC="123456789"

@echo $(error $(ABC))

@echo "hello world"

输出结果:

[root@xiaolong 2th]# make

Makefile:3: *** "123456789"。 停止。

1.11.6 产生警告信息

格式:$(warning <输出的警告提示信息>)

功能:执行该函数会产生警告信息,但是不会终止makefile的执行。

ABC="123456789"

@echo $(warning $(ABC))

@echo "hello world"

输出结果:

[root@xiaolong 2th]# make

Makefile:3: "123456789"

hello world

1.11.7 判断变量是否是环境变量

语法:$(origin <变量名称>)

如果是环境变量函数返回: environment ,不是环境变量返回undefined。

#导入Word文档图片# Makefile使用规则_Makefile_04

1.12 特殊变量

1.12.1 CC变量

CC变量定义了makefile默认编译程序使用的编译器。 如果makefile中不定义CC变量,CC默认表示gcc

OBJ=main.o print.o

CC=arm-linux-gcc

app:$(OBJ)

$(CC) $(OBJ) -o app

输出结果:

[root@xiaolong 2th]# make

arm-linux-gcc -c -o main.o main.c

arm-linux-gcc -c -o print.o print.c

arm-linux-gcc main.o print.o -o app

1.12.2 模式指定变量CFLAGS

CFLAGS变量可以指定目标在编译时加载的参数。

%.o:CFLAGS=-c

在生成.o的时候,都会加上-c参数。

在生成.o时就是这样:gcc -c xxx.c

  • 当使用“%”作为目标时,指定的变量会对所有类型的目标文件有效。

例如:%:CFLAGS=-c

  • 编译多个目录下的文件时makefile编写方式

源码目录结构:

├── include

│ └── print.h

├── Makefile

└── src

├── main.c

└── print.c

Makefile编写方式1:

VPATH=include:src

OBJ=main.o print.o

CC=gcc

INCLUDE=-I include

app:$(OBJ)

$(CC) $(OBJ) -o app

%.o:CFLAGS = $(INCLUDE)

clean:

@rm $(OBJ) -f

编译结果:

[root@xiaolong 2th]# make

gcc -I include -c -o main.o src/main.c

gcc -I include -c -o print.o src/print.c

gcc main.o print.o -o app

Makefile编写方式2:

VPATH=include:src

OBJ=main.o print.o

CC=gcc

INCLUDE=-I include

CFLAGS = $(INCLUDE) #全局指定,后面的所有编译都会加上这个参数

app:$(OBJ)

@$(CC) $(OBJ) -o app

clean:

@rm $(OBJ) -f

1.12.3 环境变量

  • SHELL :环境变量,表示当前所用的shell
  • CURDIR :环境变量,表示当前目录
  • MAKEFLAGS :环境变量,存储make的参数信息

比如:make pwd=123 abc=888 那么MAKEFLAGS 就等于pwd=123 abc=888

1.13 Makefile中的伪目标

所谓伪目标就是这样一个目标,它不代表一个真正的文件名,在执行make时可以指定这个目标来执行其所在规则定义的命令,有时我们将一个伪目标成为标签。

伪目标声明示例: .PHONY:clean

为什么要声明伪目标?

为了避免在makefile中定义的执行命令的目标和工作目录下的实际文件出现名字冲突,另一种是提交执行makefile时的效率。

比如:我们需要书写这样的一个规则:规则所定义的命令不是去创建目标文件,而是通过make命令行明确指定它来执行一些特点的命令,就像clean。当文件夹中没有clean这个文件的时候,我们输入“make clean”能按照初衷执行,但是一旦文件夹中出现clean文件,我们再次输入“make clean”,由于这个规则没有任何依赖文件,所以目标被认为是最新的而不去执行规则所定义的命令。所以目标代码不会被执行。为了解决问题,我们将目标clean定义成伪目标。

1.13.1(clean没有声明伪目标的情况)

#导入Word文档图片# Makefile使用规则_Makefile_05
#导入Word文档图片# Makefile使用规则_Makefile_06

1.13.2 声明伪目标的方法

#导入Word文档图片# Makefile使用规则_Makefile_07

1.13.3 声明多个伪目标的方法

#导入Word文档图片# Makefile使用规则_Makefile_08

1.13.4 Makefile生成多个目标的方法

#导入Word文档图片# Makefile使用规则_Makefile_09
#导入Word文档图片# Makefile使用规则_Makefile_10

一般这种情况,我们会使用特别的伪目标——all进行表示。

#导入Word文档图片# Makefile使用规则_Makefile_11

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK