Makefile文件编写

默认的情况下,make命令会在当前目录下按顺序找寻文件名为“GNUmakefile”、“makefile”、“Makefile”的文件,找到了解释这个文件。在这三个文件名中,最好使用“Makefile”这个文件名,因为,这个文件名第一个字符为大写,这样有一种显目的感觉。最好不要用“GNUmakefile”,这个文件是GNU的make识别的。有另外一些make只对全小写的“makefile”文件名敏感,但是基本上来说,大多数的make都支持“makefile”和“Makefile”这两种默认文件名。当然,你可以使用别的文件名来书写Makefile,比如:“Make.Linux”,“Make.Solaris”,“Make.AIX”等,如果要指定特定的Makefile,你可以使用make的“-f”和“--file”参数,如:make -f Make.Linux或make --file Make.AIX。

1.Makefile概述

target… : prerequisites …

command

target也就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label)。

prerequisites就是,要生成那个target所需要的文件或是目标。

command也就是make需要执行的命令。(任意的Shell命令)

1.1.工作方式

执行makefile时找到第一个target并和prerequisites比较,如果没有target或者target比prerequisites老的话就会执行下一行的命令,如果prerequisites也不存在就会在当前文件中为prerequisites寻找依赖。

makefile只执行第一个目标和这个目标所依赖的文件。

1.2.Makefile文件常用函数

wildcard : 扩展通配符函数,功能是展开成一列所有符合由其参数描述的文件名,文件间以空格间隔。例如objects= $(wildcard *.c),会产生一个所有以.c结尾的文件列表(本例结果为add.c sub.c div.c mul.c cal.c),然后存入变量objects里。

patsubst : 匹配替换函数, patsubst函数需要3个参数,第一个是需要匹配的文件样式,第二个是匹配替换成什么文件,第三个是需要匹配的源文件。例如objects : $(patsubst%.c,%.o,$(wildcard*.c)会被处理为objects :=add.o sub.o div.o mul.o cal.o。

notdir : 去除路径函数,只留下文件名,如: .lsrc/mytest.c,通过notdir之后得到mytest.c。

subst : 字符串处理函数,$(subst FROM, TO, TEXT),即将字符串TEXT中的子串FROM变为TO。

filter :

1.3.Makefile变量

变量的命名字可以包含字符、数字,下划线(可以是数字开头),但不应该含有 : 、 # 、 = 或是空字符(空格、回车等)。变量是大小写敏感的,“foo”、“Foo”和“FOO”是三个不同的变量名。

变量在声明时需要给予初值,而在使用时,需要给在变量名前加上$符号,但最好用小括号 () 或是大括号 {} 把变量给包括起来。如果你要使用真实的 $字符,那么你需要用 $$来表示。变量会在使用它的地方精确地展开,就像C/C++中的宏一样。

变量是可以使用后面的变量来定义的。例如:

foo=$(bar)

bar=$(ugh)

ugh=Huh?

all:

echo$(foo)

这样有时候会造成递归调用,如:A=$(B) B=$(A)。想要避免可以使用“:=”符号进行赋值,这样就可以达到前面的变量不能使用后面的变量,只能使用前面已定义好了的变量的效果。

1.3.1多行变量

使用define关键字设置的变量可以有多行,这有利于设置一系列命令。define后面跟的是变量名字,重启一行后是变量的值,变量定义以endef结尾。如果define定义的变量中没有以Tab键开头就不会识别为命令。

1.3.2环境变量

1.3.3目标变量

1.3.4模式变量

1.3.5Makefilei自动化变量

$@  : 目标文件

$^   : 所有依赖文件

$<   : 第一个依赖文件

$?    : 比目标还要新的依赖文件列表

1.3.6变量传递

对于多级的目录下的Makefile,定义的变量是可以传递的,如果想Ⅹ定义的变量传递到下一级使用:

export <变量名>

如果不想让他传递到下一级使用:

unexport <变量名>

如果想把所有变量都传递到下一级直接使用export即可后面不用键变量名。如果什么前缀都不加,会传递到下一级但不会覆盖下一级定义的变量。

1.4.Makefile赋值操作

=    基本的赋值

:=    覆盖之前的值

?=    如果没有被赋值过就赋等号后面的值

+=    给变量追加等号后面的值

1.5Makefile的自助推导

伪目标

伪目标只是一个标签,而不是一个文件,例如最常用的clean标签,下面这个例子:

clean:

rm *.o

清除作用,是一个伪目标,伪目标不是一个文件而是一个标签,make无法生成它的依赖关系和决定它是否要执行。我们只有通过显式地指明这个“目标”才能让其生效,当然,为了避免和文件重名的这种情况,我们可以使用一个特殊的标记“.PHONY”来显式地指明一个目标是“伪目标”,向make说明,不管是否有这个文件,这个目标就是“伪目标”。如:

.PHONY :clean

伪目标一般没有依赖的文件。但是,我们也可以为伪目标指定所依赖的文件。伪目标同样可以作为“默认目标”,只要将其放在第一个。一个示例就是,如果你的Makefile需要一口气生成若干个可执行文件,但你只想简单地敲一个make完事,并且,所有的目标文件都写在一个Makefile中,那么你可以使用“伪目标”这个特性:

all :prog1 prog2 prog3
.PHONY :all

prog1 :prog1.outils.o

cc -o prog1 prog1.o utils.o

prog2 :prog2.o

cc -o prog2 prog2.o

prog3 :prog3.osort.outils.o

cc -o prog3 prog3.o sort.o utils.o

Makefile中的第一个目标会被作为其默认目标。我们声明了一个“all”的伪目标,其依赖于其它三个目标。由于默认目标的特性是,总是被执行的,但由于“all”又是一个伪目标,伪目标只是一个标签不会生成文件,所以不会有“all”文件产生。于是,其它三个目标的规则总是会被决议。也就达到了我们一口气生成多个目标的目的。

文件搜寻

当有多级目录存在时,需要让make自己寻找源文件位置,此时需要变量VPATH

VPATH=src : ../head

make时首先在当前目录查找,找不到的情况下,到所指定的“../head”和“src”目录中去找寻文件了。

另一种方法是使用关键字vpath

vpath <pattern> <directories>

为符合模式<pattern>的文件指定搜索目录<directories>。

vpath <pattern>

清除符合模式<pattern>的文件的搜索目录。

vpath

清除所有已被设置好了的文件搜索目录。

vapth使用方法中的<pattern>需要包含 % 字符。 % 的意思是匹配零或若干字符,例如:

vpath %.h ../headers

该语句表示,要求make在“../headers”目录下搜索所有以 .h 结尾的文件。(如果某文件在当前目录没有找到的话)

我们可以连续地使用vpath语句,以指定不同搜索策略。如果连续的vpath语句中出现了相同的<pattern> ,或是被重复了的<pattern>,那么,make会按照vpath语句的先后顺序来执行搜索。如:

vpath %.c foov

path %   blishv

path %.c bar

其表示 .c 结尾的文件,先在“foo”目录,然后是“blish”,最后是“bar”目录。

vpath %.c foo:bar

vpath %   blish

而上面的语句则表示 .c 结尾的文件,先在“foo”目录,然后是“bar”目录,最后才是“blish”目录。

多目标

有多个目标都需要执行时,参考如下例子:

bigoutput littleoutput :text.g

generate text.g -$(subst output,,$@)> $@

另外也可以使用伪目标的方式来实现多目标的执行,例如:

all1:$(OBJ) subsystem creat2

subsystem:

$(MAKE) -C file2

$(OBJ): %.o: %.c

$(CC) -c $(CFLAGS) $< -o $@ -I ./file2        

creat2:

$(MKDIR) haha2

静态模式

静态模式可以更加容易的定义多目标规则,比如想要一个make命令把所有的.c都生成.o文件,或者想要输出多个可执行文件。

<targets ...> :<target-pattern> : <prereq-patterns...>
    <commands>

targets定义了一系列的目标文件,可以有通配符。是目标的一个集合。

target-pattern是指明了targets的模式,也就是的目标集模式。

prereq-patterns是目标的依赖模式,它对target-pattern形成的模式再进行一次依赖目标的定义。

例如:如果我们的<target-pattern>定义成 %.o ,意思是我们的<target>;集合中都是以 .o 结尾的,而如果我们的<prereq-patterns>定义成 %.c ,意思是对<target-pattern>所形成的目标集进行二次定义,其计算方法是,取<target-pattern>模式中的 % (也就是去掉了 .o 这个结尾),并为其加上 .c 这个结尾,形成的新集合。

所以,我们的“目标模式”或是“依赖模式”中都应该有 % 这个字符,如果你的文件名中有 % 那么你可以使用反斜杠 \ 进行转义,来标明真实的 % 字符。

嵌套执行MAKE

在一些大的工程中,我们会把我们不同模块或是不同功能的源文件放在不同的目录中,我们可以在每个目录中都书写一个该目录的Makefile。总控Makefile的变量可以传递到下级的Makefile中,但是不会覆盖下层的Makefile中所定义的变量,除非指定了 -e 参数。

$(MAKE) -C file2

条件语句

条件表达式的语法为:

<conditional-directive>

<if为真执行的内容>

endif

<conditional-directive>

<if为真执行的内容>

else

<if为假执行的内容>

endif

conditional-directive是关键字,资格关键字有四种

ifeq

ifneq

ifdef

ifndef

2.单个目录编写实例

1.单个文件下目录文件如下

main.c

sub.c

sum.c

mul.c

headfile.h

(放在同一目录下)

2.编写最简单的Makefile文件

main:main.o sub.o add.o mul.o

        gcc -o main main.o sub.o add.o mul.o

main.o:main.c headfile.h

        gcc -c main.c -o main.o

add.o:add.c headfile.h

        gcc -c add.c

sub.o:sub.c headfile.h

        gcc -c sub.c

mul.o:mul.c headfile.h

        gcc -c mul.c

3.对于文件比较多时候,如果需要添加或删除文件可能会出现遗漏,可以使用变量来代替文件名字。(例如定义为OBJ=…,使用时则$(OBJ))。

    在文件开头添加定义

CC = gcc

OBJ = main.o sub.o add.o mul.o

修改使用main.o sub.o add.o mul.o处为$(OBJ)

修改使用gcc处为$(CC)

4.自助推导,只要make看到了*.o文件,它就会自动把与之对应的*.c文件加到依赖文件中,并且gcc -c *.c也会被推导出来,

    根据自动推导修改程序

修改后参考代码:(不唯一)

OBJ = main.o sub.o add.o mul.o

CC = gcc

main: $(OBJ)

        $(CC) -o main $(OBJ)

main.o: headfile.h

add.o: headfile.h

sub.o: headfile.h

mul.o: mul.c headfile.h

PHONY: clean

clean:

        rm $(OBJ)

5. 使用自动变量($^ $< $@)修改Makefile文件

    修改后参考程序

OBJ = main.o sub.o add.o mul.o

CC = gcc

main: $(OBJ)

        $(CC) -o $@ $^

main.o: main.c headfile.h

        $(CC) -c $<

add.o: main.c headfile.h

        $(CC) -c $<

sub.o: sub.c headfile.h

        $(CC) -c $<

mul.o: mul.c headfile.h

        $(CC) -c $<

PHONY: clean

clean:

        rm $(OBJ)

6.使用缺省规则..c.o:

..c.o:

gcc -c $<

这个规则表示,所有的 *.o 目标文件都是依赖于相应的 *.c 源文件的, 例如 main.o 依赖于 main.c

OBJ = main.o sub.o add.o mul.o

CC = gcc

main: $(OBJ)

        $(CC) -o main $@ $^

..c.o:

        $(CC) -c $<

clean:

        rm $(OBJ)

    修改后编译运行

多目录下Makefile文件

3.多级目录编写实例

当文件比较多,有多级目录时情况比较复杂,如果一个子目录与其他子目录无关,不需要调用其他子目录的程序可以考虑使用递归make,如果一个子目录中的代码依赖于另一个子目录中的代码,那么在顶层使用单个 makefile 可能会更好。

示例代码目录树如下:

.

├── file1.c

├── file_t1.c

├── inc

│   └── headfile.h

├── Makefile

├── treeA

│   ├── file2

│   │   ├── file2.c

│   │   ├── file2.h

│   │   └── Makefile

│   ├── file41.c

│   ├── file4.c

│   ├── haha2

│   └── Makefile

└── treeB

    ├── file3.c

    └── Makefile

此示例的Makefile使用递归的方式编写,对于顶级makefile的编写如下,其他目录下Makefile编写类似。

#首先是设置编译器名字

CC=gcc

#使用wildcard函数获取当前目录下所有的.c源文件

SOU = $(wildcard *.c)

#通过替换字符串函数替换上一步中SOU中的.c字符得到.o结尾的目标文件

OBJ_THIS = $(SOU:.c=.o)

#使用shell命令的find命令查找当前文件夹下所有的.o编译的目标文件,用于make clean清除命令

OBJ_ALL = $(shell find ./ -name "*.o")

#当前路径

PATH=$(shell pwd)

#头文件路径,也可以用相对路径

INC=-I/$(PATH)/inc

#要编译的子目录

SUBTREE=treeB treeA

#编译生成的可执行文件

EXE = test

#用伪目标来实现执行多个目标,因为makefile只会编译第一个目标和这个目标的依赖文件,all是一个伪目标

all:$(SUBTREE) test

#声明$(SUBTREE)是一个伪目标

#进入每个子目录分别编译其中的Makefile

.PHONY:$(SUBTREE)

$(SUBTREE):

$(MAKE) -C $@

#编译当前目录的.o到可执行文件

test:$(OBJ_THIS)

$(CC) -o test $(OBJ_THIS) $(INC) $(CFLAGS)

#静态模式:把当前目录所有.c文件生成.o文件

$(OBJ_THIS): %.o: %.c

$(CC) -c $< -o $@ $(INC)

#也是伪目标,清除编译操作生成的文件

clean:

rm ./test

rm $(OBJ_ALL)

使用make编译,要清除编译结果只需执行make clean,Makefile会寻找clean标签执行

详细例子参考../常用linux程序合计合集/其他代码/Makefile

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值