0
点赞
收藏
分享

微信扫一扫

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

seuleyang 2022-06-02 阅读 87


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 "相等!"

else

@echo "不相等!"

endif#条件判断的结束语句

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

与ifeq相反

all:

ifneq (12,13)

@echo "不相等!"

else

@echo "相等!"

endif


1.5.2 ifdef与ifndef

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

ABC=123

all:

ifdef ABC #检测ABC是否定义

@echo "已经定义!"

else

@echo "没有定义!"

Endif

与ifdef刚好相反

ABC=123

all:

ifndef ABC #检测ABC是否定义

@echo "没有定义!"

else

@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文件的路径

all:

make -C $(ADDR)

@echo "调用成功!"

第二层的Makefile:

all:

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

1.7.2 Makefile之间传参传递-1

使用export关键字声明!

第一层Makefile:

ADDR=./addr_1

export ABC+=123 456 789

all:

make -C $(ADDR)

@echo "调用成功!"

第二层makefile:

all:

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

@echo $(ABC)

1.7.3 Makefile之间传参传递2

直接传递参数。

第一层Makefile:

ADDR=./addr_1

all:

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

@echo "调用成功!"

第二层makefile:

all:

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

@echo $(ABC)

1.7.4 指定调用下层Makefile命令1

第一层Makefile:

ADDR=./addr_1

all:

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

@echo "调用成功!"

第二层makefile:

all:

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

@echo $(ABC)

clean:

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

1.7.5 指定调用下层Makefile命令2

第一层Makefile:

ADDR=./addr

all:

make -C $(ADDR) obj1obj2

第二层makefile:

all:

@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`

all:

@echo $(A)

@echo $(B)


  • 调用示例2:

ADDR=`pwd`/addr_1

all:

@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后面必须有一个空格

all:

@echo "变量="$(aa)

输出结果:

[root@xiaolong 2th]# make

变量=AAAAA88888

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

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

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

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

示例:

ABC=" 12345"

DATA=$(strip $(ABC))

all:

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

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

输出结果:

[root@xiaolong 2th]# make

替换前= 12345

替换后= 12345

1.11.3 字符串查找

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

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

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

  • 示例1:

ABC=" 12345"

all:

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

输出结果:

[root@xiaolong 2th]# make

5

  • 示例2:

ABC=12345哈哈

all:

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

输出结果:

[root@xiaolong 2th]# make

  • 示例3:

ABC=12345

all:

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

@echo "查找成功"

else

@echo "查找失败"

endif

输出结果:

[root@xiaolong 2th]# make

查找成功


1.11.4 执行shell命令

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

示例:

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

1.11.5 产生错误信息

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

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

示例:

ABC="123456789"

all:

@echo $(error $(ABC))

@echo "hello world"

输出结果:

[root@xiaolong 2th]# make

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

1.11.6 产生警告信息

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

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

示例:

ABC="123456789"

all:

@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变量可以指定目标在编译时加载的参数。

  • 示例1:

%.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


举报

相关推荐

0 条评论