Makefile编写

TODO:完成所有整理

介绍

基本规则

1
2
target  : prerequisites 
command#shell command,并且所有命令都需要tab来空出格子:也可以用空格四格

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* c文件及其头文件
* main.c: defs.h
* command.c: defs.h command.h
* diplay.c: defs.h display.h buffer.h
*/

#链接所有文件生成edit可执行文件
edit: main.o command.o diplay.o
cc -o edit main.o command.o \
diplay.o

main.o: main.c defs.h
cc -c main.o

command.o: defs.h command.h
cc -c command.c

diplay.o: defs.h diplay.h buffer.h
cc -c diplay.c

#clean:为了重新编译可链接文件,放在文件最后,如果放在头会变成make执行的命令
clean:
rm -f edit main.o command.o display.o

编译过程为依赖自顶向下:

  • 如果中途失败,很可能会残留已经成功的部分.o文件:比如diplay.o编译失败,但是main.o command.o会被生成
  • 如果修改了.h文件,那么引入头文件的.c文件也会被重新编译为.o文件
  • 假如新增一个.c文件,那么修改地方一般为三个:目标文件,.o生成,clean命令

使用变量

使用变量来减少编写,规则类似shell里的 命令输出定向为参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* c文件及其头文件
* main.c: defs.h
* command.c: defs.h command.h
* diplay.c: defs.h display.h buffer.h
*/

#链接所有文件生成edit可执行文件
objs = main.o command.o diplay.o
edit: $(objs)
cc -o edit $(objs)

main.o: main.c defs.h
cc -c main.o

command.o:command.o defs.h command.h
cc -c command.c

diplay.o:diplay.o defs.h diplay.h buffer.h
cc -c diplay.c

#clean:为了重新编译可链接文件
clean:
rm -f edit $(objs)

自动推导

GNU的make会自动根据.o文件推导.c文件,生成相应的cc命令,同时头文件也可以共有推导

问题:为什么需要写.c文件的头文件依赖?include已经包含了,那么执行cc也不需要才对,实际上我的测试也是没有问题。。。或许是路径的问题,makefile可以保证不在同一个目录下的引入?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* c文件及其头文件
* main.c: defs.h
* command.c: defs.h command.h
* diplay.c: defs.h display.h buffer.h
*/

#链接所有文件生成edit可执行文件
objs = main.o command.o diplay.o
edit: $(objs)
cc -o edit $(objs)

#或许你不需要这部分书写
$(objs): defs.h
command.o:command.h
diplay.o:diplay.h buffer.h

#clean:为了重新编译可链接文件
clean:
rm -f edit $(objs)

总述

内容

主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释

  • 显式规则:目标文件,依赖,命令
  • 隐晦规则:自动推导部分
  • 变量定义:变量简化字符串书写
  • 文件指示:makefile之间的引入
  • 注释:#作为注释

文件名

一般make识为文件为当前目录下的“makefile“或者”Makefile“,而如果自定义了”makefile.Linux“, 则使用”-f”参数来指定文件

引用

项目中很可能的一种情况:由于makefile只能考虑一级子目录,所以常在一级子目录content下也创建一个makefile;同时如果考虑makefile内容太多需要分开,则会在当前目录下新增一些诸如:makeconfig.mk之类,则可以通过总的makefile引入来覆盖整个项目。

1
2
3
4
5
6
#1.*作为通配符使用
include *.mk content/makefile
#2如果写的目录没有找到,可以在命令中引入 -I参数来指定目录
#3.还没有,会在usr/local/bin和usr/include中找
-include *.mk#4.代表如果没找到也不要报错,忽略它

环境变量

先不要使用,但是如果有问题,可以看看环境变量是否有MAEKFILES,在执行的时候会因此执行其他的makefile脚本

1
echo MAKEFILES

工作方式

  1. 读入所有的makefile
  2. 读入被include的makefile
  3. 初始化变量:展开宏
  4. 推导隐晦规则
  5. 创建依赖链
  6. 根据依赖关系决定生成的文件
  7. 自顶向下执行命令

书写规则

根据自顶向下的生成规则,你应该把第一个目标文件作为最终目标;同时目标文件一定是比依赖文件日期新,以此来判断是否重新编译

1
2
3
4
5
targets: prerequisites
command
#其中,targets可以是使用通配符来产生的多个目标文件
#紧凑型书写也可以:
targets: prerequisites; command

对于通配符,make支持三种:“*”, “?”,“…”;还有一个Unix下的”~”

1
2
3
4
5
6
7
8
9
clean:
rm -f *.o

print: *.c
lpr -p $?
touch print

objs = *.o#变量作为宏展开得到的还是*.o
objs = $(wildcard *.o)#使用关键字来保证查找

搜寻文件可以有多个途径:

  • 指定的文件路径名(无论是相对还是绝对)
  • 当前文件夹下
  • 默认的一些文件夹
  • *类比于环境变量的VPATH:从左到右根据“:”分割开的文件夹里去找
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#常见
VPATH = src : ../headers#src优先于headers

#还有三种使用vpath关键字的:可以在不同文件目录查找
#1
vpath <pattern> <directories>#为符合模式<pattern>的文件指定搜索目录<directories>
vpath %.h ../headers#如果没指定,当前文件夹也没有,那么:在headers目录下查找所有以.h结尾的文件
#2
vpath <pattern>#清除符合模式<pattern>的文件的搜索目录
#3
vpath#清除所有已被设置好了的文件搜索目录


#如果多个vpath的重复pattern,那么就会形成顺序查找,直到找到
#1
vpath %.c foo
vpath % blish
vpath %.c bar
#2
vpath %.c foo:bar
vpath % blish

伪目标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ps:make clean查找所有的cleanall 和clean
#增加一行,因此make clean来代表生成clean这个文件,我还不知道使用有啥区别。。。
.PHONY: clean
clean:
rm -f *.o

#同样,将all放在开头,那么就可以依此生成多个文件的依赖链,一次make生成多个目标文件
all: main command diplay
.PHONY:all

#同样,伪目标也可以成为依赖,但是还是感觉没了.PHONY没啥差别,比如命令:make cleanall或者上面例子的make all都可以在没有.PHONY这一行情况下正常执行(目前为止)
cleanall: cleanobj cleanprog
.PHONY:cleanall

cleanobj:
rm -f *.o
cleanprog:
rm -f edit

多文件:$@代表所有目标集,$<代表所有依赖集

1
2
3
4
5
6
7
8
9
#相同依赖的多个目标生成简写
bigoutput littleoutput : text.g
generate text.g -$(subst output,,$@) > $@

#上述规则等价于:
bigoutput : text.g
generate text.g -big > bigoutput
littleoutput : text.g
generate text.g -little > littleoutput

静态模式:更加方便定义多目标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#格式
<targets ...>: <target-pattern>: <prereq-patterns ...>
<commands>
....
#给出两个例子

#1
objects = foo.o bar.o
all: $(objects) #作为最终目标
#静态模式:看看这两个“:”,他们决定了$@,$<,并且保证扩展的部分一一对应
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
#上述规则等价于:
foo.o : foo.c
$(CC) -c $(CFLAGS) foo.c -o foo.o
bar.o : bar.c
$(CC) -c $(CFLAGS) bar.c -o bar.o

#2
files = foo.elc bar.o lose.o
#静态模式:看看这两个“:”
#第一个filter,得到新的$(files)
$(filter %.o,$(files)): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
#第二个filter,得到新的$(files)
$(filter %.elc,$(files)): %.elc: %.el
emacs -f batch-byte-compile $<
#上述规则等价于:
bar.o : bar.c
$(CC) -c $(CFLAGS) bar.c -o bar.o
lose.o : lose.c
$(CC) -c $(CFLAGS) lose.c -o lose.o
foo.elc : foo.el
emacs -f batch-byte-compile foo.el

自动生成依赖性

假如,不管自动推导.c以来和cc命令,也不管.c的.h依赖问题(见前面我的include问题)。

那么我们还有以下方式来化简书写

  • 自动生成.h依赖

    1
    2
    3
    	cc -MM main.c#GNU如此,非GNU编译器参数为-M
    #等价于
    main.o: main.c defs.h

  • 为每个.c文件生成依赖文件.d

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #写出.c和.d依赖,让makefile自动生成.d文件
    %.d: %.c
    @set -e; rm -f $@; \
    $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
    sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
    rm -f $@.$$$$

    #这个规则的意思是,所有的[.d]文件依赖于[.c]文件,“rm -f $@”的意思是删除所有的目标,也就是[.d]文件,第二行的意思是,为每个依赖文件“$<”,也就是[.c]文件生成依赖文件,“$@”表示模式“%.d”文件,如果有一个 C 文件是 name.c,那么“%”就是“name”,“$$$$”意为一个随机编号,第二行生成的文件有可能是“name.d.12345”,第三行使用 sed 命令做了一个替换,第四行就是删除临时文件。

    #上述规则等价于:
    main.o : main.c defs.h
    转成:
    main.o main.d : main.c defs.h

    #因此得到了.d文件,存放了依赖关系,我们因此可以在接下来的编译中引入.d文件
    sources = foo.c bar.c
    include $(sources:.c=.d)
    #上述语句中的“$(sources:.c=.d)”中的“.c=.d”的意思是做一个替换,把变量$(sources)所有[.c]的字串都替换成[.d],关于这个“替换”的内容,在后面我会有更为详细的讲述。当然,你得注意次序,因为 include 是按次来载入文件,最先载入的[.d]文件中的目标会成为默认目标

Make运行

make 3个推出码

  • 0:成功
  • 1:错误
  • 2:”-q”参数,表示部分更新

指定Make

对于makeconfig.mk这样的分层makefile,使用参数“-f”来找到

1
make –f hchen.mk

。。。很多参数和惯例,用到时再说

参考文献

[1]《跟我一起学makefile》-陈皓

Donate
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2020-2024 环烷烃
  • Visitors: | Views:

我很可爱,请我喝一瓶怡宝吧~

支付宝
微信