Makefile学习

摘自跟我一起写Makefile,作者陈皓

自动化编译
Visual C++ nmake,Linux GNU make
编译:源文件编译成中间代码文件
链接:再把大量的Object File合成执行文件
make命令执行时,需要一个Makefile文件,以告诉make命令需要怎么样的去编译和链接程序
一些规则:
如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接
如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程序
如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的C文件,并链接目标程序


Makefile的规则
target:prerequisites
command
target目标文件
prerequisites生成target所需要的文件或是目标
command,make需要执行的命令


edit:main.o display.o utils.o
cc -o edit main.o display.o utils.o


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


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


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


clean:
rm edit main.o display.o utils.o
如果一行内容太长,可以使用\
command一定要以一个TAB键作为开头
make会比较targets文件和prerequisites文件的修改日期


make会在当前目录下找名字叫Makefile或makefile的文件
如果找到,它会找文件中的第一个目标文件,并把这个文件作为最终的目标文件
如果该目标文件不存在,或者是它所依赖的后面的.o文件的文件修改时间要比它新,那么,它就会执行后面所定义的命令来生成该目标文件,依赖的文件的思路与此类似


优化:
objects = main.o display.o utils.o
edit:$(objects)
cc -o edit $(objects)
rm edit $(objects)


GNU的make可以自动推导文件以及文件依赖关系后面的命令,只要看到.o文件,就会自动把.c文件加在依赖关系中,并且cc -c *.c也会被推导出来
main.o:defs.h
display.o:defs.h
utils.o:defs.h utils.h


很多重复的.h
$(objects):defs.h
utils.o:utils.h
这样,依赖关系有点凌乱,不建议使用


每个Makefile中都应该写一个清空目标文件(.o和执行文件)的规则
更为稳健的做法
.PHONY:clean
clean:
-rm edit $(objects)
.PHONY表示clean是一个伪目标
-,也许某些文件出现问题,但不要管,继续做后面的事


Makefile五部分:显示规则、隐晦规则、变量定义、文件指示和注释
显示规则:如何生成一个或多个目标文件,要生成的文件,文件的依赖文件,生成的命令
隐晦规则:make有自动推导的功能
变量定义:类似C语言中的宏,Makefile被执行时,其中的变量都会被扩展到相应的引用位置上
文件指示:在一个Makefile中引用另一个Makefile(类似C语言中的include),根据某些情况指定Makefile中的有效部分(类似C语言中的#if),定义一个多行的命令
注释:#,\#转义


make -f MakefileName
make --file MakefileName


引用其它的Makefile
include OtherMakefilePath
make会在当前目录下首先寻找
-I或--include-dir,make就会在这个参数所指定的目录下去寻找
如果<prefix>/include(一般是/usr/local/bin,/usr/include)存在的话,make也会去找
如果有文件没有找到的话,make会生成一条警告信息,但不会马上出现知名错误。它会继续载入其它的文件,一旦完成读取,make会再重试这些没有找到或是不能读取的文件,如果还是不行,make才会出现一条致命信息。如果你想让make不理那些无法读取的文件,而继续执行,可以在include前加一个减号-


环境变量MAKEFILES
如果定义了环境变量MAKEFILES,make会把这个变量中的值做一个类似于include的动作
不建议这个环境变量


GNU的make工作时的执行步骤
读入所有的Makefile
读入被include的其它Makefile
初始化文件中的变量
推导隐晦规则,并分析所有规则
为所有的目标文件创建依赖关系链
根据依赖关系,决定哪些目标要重新生成
执行生成命令


在规则中使用通配符
*,?,[]
\转义
clean:
rm *.o
objects = *.o,但是不会展开
objects:=$(wildcard *.o)


文件搜寻
VPATH,如果没有指名这个变量,make只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么make就会在当当前目录找不到的情况下,到所指定的目录中去找寻文件
VPATH=path1:path2
不同路径:分隔,当前目录永远是最高优先搜索的地方
另一种方法是使用make的vpath关键字,更为灵活,需要的时候再去学


伪目标
并不是一个文件,而是一个标签
为了避免和文件重名
.PHONY:name
伪目标同样可以作为默认目标,只要将其放在第一个
如果Makefile需要生成若干个可执行文件,但只想简单得敲一个make,并且所有的目标文件都写在一个Makefile中
all:p1 p2 p3
.PHONY:all
p1: 
cc -o p1 p1.o


多目标
有可能多个目标同时依赖于一个文件,并且其生成的命令大体类似,需要再去学


静态模式
可以更加容易地定义多目标的规则,可以让我们的规则更加的有弹性和灵活。
<targets ...>:<target-pattern>:<prereq-patterns...>
<commands>
targets:定义了一系列的目标文件,可以有通配符,是目标的一个集合
target-pattern:指明了targets的模式,也就是目标集的模式
prereq-patterns:目标的依赖模式,它对target-pattern形成的模式再进行一次依赖目标的定义
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


files=foo.elc bar.o lose.o
$(filter %.o,$(files)):%.o:%.c
$(CC) -c $(CFLAGS) $< -o $@
$(filter %.elc,$(files)):%.elc:%el
emacs -f batch-byte-compile $<


自动生成依赖性(24)
大多数的C/C++编译器都支持-M选项,即自动找寻源文件中包含的头文件,并生成一个依赖关系
cc -M main.c
如果使用的是GNU的C/C++编译器,-MM,-M会把一些标准库的头文件也包含进来
gcc -M main.c
没看懂,不过感觉挺实用的






书写命令
显示命令
make通常会把要执行的命令行在命令执行前输出到屏幕上,如果在命令行前加上@,那么这个命令将不会被make显示出来
@echo 正在编译XXX模块...
输出
正在编译XXX模块...
-n或--just-print:只是显示命令,但不会执行命令,有利于调试Makefile,看执行起来是什么顺序
-s或--silent:全面禁止命令的显示


命令执行
如果执行多条命令,分号分割
exec:
cd /home/star; pwd


命令出错
-忽略命令的出错
clean:
-rm -f *.o
-i或--ignore-errors:所有命令都会忽略错误
.IGNORE,这个规则中的所有命令将会忽略错误
-k或--keep-going:如果某规则中的命令出错了,那么就中止该规则的执行,但继续执行其它规则


嵌套执行make
在一些大的工程中,我们会把不同模块或是不同功能的源文件放在不同的目录中,在每个目录中都书写一个该目录的Makefile,这有利于让我们的Makefile变得更加的简洁
总控Makefile
subsystem:
cd subdir && $(MAKE)
等价于
subsystem:
$(MAKE) -C subdir
传递变量到下级Makefile中
export variable = value

variable = value
export variable
=还可以是:=
unexport
如果传递所有,export不带参数
SHELL和MAKEFLAGS总是会传递到下层Makefile中
-C,-f,-h,-o,-W不往下传递
subsystem:
cd subdir && $(MAKE) MAKEFLAGS=
-w或--print-directory会输出当前的工作目录


定义命令包
define run-yacc
yacc $(firstword $^)
mv y.tab.c $@ #Yacc程序总是生成y.tab.c文件,重命名
endef


foo.c : foo.y
$(run-yacc)
$^:foo.y,$@:foo.c






使用变量
尽量使用:=来定义变量而不是=
前面的变量不能使用后面的变量,只能使用前面已定义好了的变量
FOO ?= bar
如果FOO没有被定义过,那么变量FOO的值就是bar
nullstring := 
space := $(nullstring)
nullstring是Empty变量
space是一个空格
$(var:a=b)把变量var中所有以a串结尾的a替换成b串
还可以使用静态模式
还可以实现变量的拼接
source := $($(a1)_objects:.o=.c)




追加变量值
objects = main.o foo.o bar.o utils.o
objects += another.o


override指示符
如果有变量是通过make的命令行参数设置的,那么Makefile中对这个变量的赋值会被忽略
override <variable> := <value>


多行变量
命令包技术就是利用这个关键字
如果是命令,需要以TAB开头
define name
...
endef


环境变量
系统环境中设置CFLAGS环境变量


目标变量
为某个目标设置局部变量,可以和全局变量同名,因为它的作用范围只在这条规则以及连带规则中
<target...>:<variable-assignment>
<target...>:override <variable-assignment>
prog:CFLAGS = -g


模式变量
给定一种模式,可以把变量定义在符合这种模式的所有目标上
<pattern...>:<variable-assignment>
<pattern...>:override <variable-assignment>
%.o:CFLAGS = -O






使用条件判断
<conditional-directive>
<text-if-true>
endif


<conditional-directive>
<text-if-true>
else
<text-if-false>
endif


ifeq (<arg1>, <arg2>)
ifeq '<arg1>' '<arg2>'
ifeq "<arg1>" "<arg2>"
ifneq
ifdef <variable-name> 值非空,表达式为真
ifndef


make是在读取Makefile时就计算条件表达式的值,并根据条件表达式的值来选择语句,最好不要把自动化变量(如$@)放入条件表达式中,因为自动化变量是在运行时才有的






使用函数
$(<function> <arguments>)
函数名和参数之间以空格分隔,参数之间以逗号分隔


字符串处理函数
$(subst <from>,<to>,<text>)
字符串替换函数,把text中的from替换成to


$(patsubst <pattern>,<replacement>,<text>)
模式字符串替换函数
替换单词,%任意长度的字串
$(patsubst %.c, %.o, x.c bar.c)
结果:x.o bar.o
单词以空格、TAB、回车、换行分隔


$(strip <string>)
去空格函数
去掉string字串中开头和结尾的空字符


$(findstring <find>, <in>)
查找字符串函数
在in中查找find,如果找到,返回find,否则返回空


$(filter <pattern...>, <text>)
过滤函数
以pattern模式过滤text字符串中的单词
source := foo.c bar.c baz.s ugh.h
foo: $(source)
cc $(filter %.c %.s, $(sources)) -o foo


$(filter-out <patter...>, <text>
反过滤函数


$(sort <list>)
排序函数
list中的单词升序排序,会去掉list中相同的单词


$(word <n>, <text>)
取单词函数
取text中的第n个单词,取不到,返回空


$(wordlist <s>, <e>, <text>)
去单词串函数
从text取从s到e的单词串


$(words <text>)
单词个数统计函数


$(firstword <text>)
首单词函数




文件名操作函数
$(dir <names...>)
取目录函数
从文件名序列<names>中取出目录部分


$(notdir <names...>)
取文件函数


$(suffix <names...>)
取后缀函数(扩展文件名)


$(basename <names...>)
取前缀函数(扩展文件名之前)


$(addsuffix <suffix>, <names...>)
加后缀函数


$(addprefix <prefix>, <names...>)


$(join <list1>, <list2>)
连接函数




$(foreach <var>, <list>, <text>)
names := a b c
files := $(foreach n, $(names), $(n).o)
$(files)的值为a.o b.o c.o




$(if <condition>, <then-part>)
$(if <condition>, <then-part>, <else-part>)




$(call <expression>, <parm1>, <parm2>, <parm3>...)
reverse := $(2) $(1)
foo := $(call reverse, a, b)
foo的值为b a




$(origin <variable>)(用到再学)
变量的来源
undefined
default
file
command line
override
automatic




shell函数把执行操作系统命令后的输出作为函数返回
contents := $(shell cat foo)




控制make的函数 
$(error <text...>)
$(warning <text...>)


ifdef ERROR_001
$(error error is $(ERROR_001))
endif


ERR = $(error found an error!)
.PHONY: err
err: ; $(ERR)






make的运行
make的退出码
0-成功
1-出现错误
2-如果使用了-q选项,并且make使得一些目标不需要更新


指定Makefile
-f
--file
make -f star.mk


指定目标
all clean install print tar dist TAGS check test


检查规则
-n
--just-print
--dry-run
--recon


-t
--touch


-q
--question


-W <file>
--what-if=<file>
--assume-new=<file>
--new-file=<file>


make的参数
GNU make 3.80
-b
-m
忽略和其它版本make的兼容性


-B
--always-make
认为所有的目标都需要更新


-C <dir>
--directory=<dir>
指定读取makefile的目录


--debug[=<options>]
输出 make 的调试信息。它有几种不同的级别可供选择,如果没有参数,那就是输出最简单的调试信息。
下面是<options>的取值:
a —— 也就是 all,输出所有的调试信息。
(会非常的多)
b —— 也就是 basic,只输出简单的调试信息。即输出不需要重编译的目标。
v —— 也就是 verbose,在 b 选项的级别之上。输出的信息包括哪个 makefile 被解析,不
需要被重编译的依赖文件(或是依赖目标)等。
i —— 也就是 implicit,输出所以的隐含规则。
j —— 也就是 jobs,输出执行规则中命令的详细信息,如命令的 PID、返回码等。
m —— 也就是 makefile,输出 make 读取 makefile,更新 makefile,执行 makefile 的信
息。
“-d”
相当于“--debug=a”。
“-e”
“--environment-overrides”
指明环境变量的值覆盖 makefile 中定义的变量的值。
“-f=<file>”
“--file=<file>”
“--makefile=<file>”
指定需要执行的 makefile。
“-h”
“--help”
显示帮助信息。
“-i”
“--ignore-errors”
在执行时忽略所有的错误。
“-I <dir>”
“--include-dir=<dir>”
指定一个被包含 makefile 的搜索目标。可以使用多个“-I”参数来指定多个目录。
“-j [<jobsnum>]”
“--jobs[=<jobsnum>]”
指同时运行命令的个数。如果没有这个参数,make 运行命令时能运行多少就运行多少。如
果有一个以上的“-j”参数,那么仅最后一个“-j”才是有效的。
(注意这个参数在 MS-DOS
中是无用的)
“-k”
“--keep-going”
出错也不停止运行。如果生成一个目标失败了,那么依赖于其上的目标就不会被执行了。
“-l <load>”
“--load-average[=<load]”
“—max-load[=<load>]”
指定 make 运行命令的负载。
“-n”
“--just-print”
“--dry-run”
“--recon”
仅输出执行过程中的命令序列,但并不执行。
“-o <file>”
“--old-file=<file>”
“--assume-old=<file>”
不重新生成的指定的<file>,即使这个目标的依赖文件新于它。
“-p”
“--print-data-base”
输出 makefile 中的所有数据,包括所有的规则和变量。这个参数会让一个简单的 makefile
都会输出一堆信息。
如果你只是想输出信息而不想执行 makefile,
你可以使用“make -qp”
命令。如果你想查看执行 makefile 前的预设变量和规则,你可以使用“make –p –f
/dev/null”。这个参数输出的信息会包含着你的 makefile 文件的文件名和行号,所以,用
这个参数来调试你的 makefile 会是很有用的,特别是当你的环境变量很复杂的时候。
“-q”
“--question”
不运行命令,也不输出。仅仅是检查所指定的目标是否需要更新。如果是 0 则说明要更新,
如果是 2 则说明有错误发生。
“-r”
“--no-builtin-rules”
禁止 make 使用任何隐含规则。
“-R”
“--no-builtin-variabes”
禁止 make 使用任何作用于变量上的隐含规则。
“-s”
“--silent”
“--quiet”
在命令运行时不输出命令的输出。
“-S”
“--no-keep-going”
“--stop”
取消“-k”选项的作用。因为有些时候,make 的选项是从环境变量“MAKEFLAGS”中继承下
来的。所以你可以在命令行中使用这个参数来让环境变量中的“-k”选项失效。
“-t”
“--touch”
相当于 UNIX 的 touch 命令,只是把目标的修改日期变成最新的,也就是阻止生成目标的命
令运行。
“-v”
“--version”
输出 make 程序的版本、版权等关于 make 的信息。
“-w”
“--print-directory”
输出运行 makefile 之前和之后的信息。这个参数对于跟踪嵌套式调用 make 时很有用。
“--no-print-directory”
禁止“-w”选项。
“-W <file>”
“--what-if=<file>”
“--new-file=<file>”
“--assume-file=<file>”
假定目标<file>需要更新,
如果和“-n”选项使用,
那么这个参数会输出该目标更新时的运
行动作。
如果没有“-n”那么就像运行 UNIX 的“touch”命令一样,
使得<file>的修改时间
为当前时间。
“--warn-undefined-variables”
只要 make 发现有未定义的变量,那么就输出警告信息。






隐含规则
编译C程序的隐含规则
<n>.o的目标的依赖目标会自动推导为<n>.c,并且其生成命令是$(CC) -c $(CPPFLAGS) $(CFLAGS)


编译C++程序的隐含规则
<n>.o的目标的依赖目标会自动推导为<n>.cc或<n>.C,并且其生成命令是$(CXX) -c $(CPPFLAGS) $(CFLAGS)


Pascal
Fortran/Ratfor
Modula-2
Yacc C
Lex C
Lex Ratfor
Lint


汇编和汇编预处理的隐含规则
<n>.o   <n>.s
$(AS) $(ASFLAGS)
<n>.s   <n>.S
$(AS) $(ASFLAGS)


链接Object文件的隐含规则
<n>目标依赖于<n>.o
$(CC) $(LDFLAGS) <n>.o $(LOADLIBES) $(LDLIBS)
这个规则对于只有一个源文件的工程有效,同时也对多个Objects文件(由不同的源文件生成)的也有效
x : y.o z.o
并且x.c, y.c, z.c都存在时,隐含规则将执行如下命令
cc -c x.c -o x.o
cc -c y.c -o y.o
cc -c z.c -o z.o
cc x.o y.o z.o -o x
rm -f x.o
rm -f y.o
rm -f z.o




# Cleaning is done on three levels.
# make clean     Delete most generated files
#                Leave enough to build external modules
# make mrproper  Delete the current configuration, and all generated files
# make distclean Remove editor backup files, patch leftover files and the like

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值