|
发表于 2008-1-25 23:45:14
|
显示全部楼层
晕,可你写的是 $<@
我的 make 笔记,借你用来快速参考吧
不过如果有时间的话,我还是建议你系统的学一下 make
参数
make // If you have named your Makefile either Makefile or makefile, make will recognize it.
make -f mymakefile
make –p –f /dev/null //查看执行 makefile 前的预设变量和规则
make -n
-B/--always-make //强制重新编译
-C <DIRECTORY> // Change to DIRECTORY before doing anything.
--debug[=FLAGS]
FLAGS
a // all,输出所有调试信息
b // basic,只输出简单的调试信息;即输出不需要重编译的目标
i // implicit,输出所有的隐含规则
j // jobs,输出执行规则中命令的详细信息;如命令的 PID、返回码等
m // makefile,输出 make 读取 makefile,更新 makefile,执行 makefile 的信息
v // verbose,在 b 选项的级别之上
// 输出的信息包括哪个 makefile 被解析,不需要被重编译的依赖文件(或是依赖目标)等
-e/--environment-overrides // Environment variables override makefiles.
//一般情况下,makefile 内部定义的变量覆盖外部传入的环境变量
-i/--ignore-errors // Ignore errors from commands.
-k/--keep-going // Keep going when some targets can't be made.
-l [N] // Don't start multiple jobs unless load is below N.
-l 2.5
-n/--just-print //只是显示命令,但不会执行命令;用于调试 makefile 文件
-q/--question // Run no commands; exit status says if up to date.
// Exit code 0 means need to update,2 means some error occured.
-r/--no-builtin-rules //禁止 make 使用任何隐含规则
-R/--no-builtin-variabes //禁止 make 使用任何作用于变量上的隐含规则
-s/--silent/--quiet // Don't echo commands.
-t/--touch //更新目标文件的时间,把目标变成已编译过的状态
-w/--print-directory // Print the current directory.
--no-print-directory
-W <file> // Make 会根据规则推导来运行依赖于这个文件的命令
//可以和 -n 参数一同使用,来查看这个依赖文件所发生的规则命令
退出码
0 //成功执行
1 //出现错误
2 //如果你使用了 make 的 "-q" 选项,并且 make 使得一些目标不需要更新,那么返回 2
可能目标
all //所有目标的目标,其功能一般是编译所有的目标
check / test //测试 makefile 的流程
clean //删除所有被 make 创建的文件
dist //创建一个压缩文件,一般是把tar文件压成 Z 文件
distclean //删除中间文件
install //安装已编译好的程序,其实就是把目标执行文件拷贝到指定的目标中去
make DESTDIR=/home/f95-build install
print //列出改变过的源文件
TAGS //更新所有的目标,以备完整地重编译使用
tar //把源程序打包备份,也就是一个 tar 文件
示例
Listing dependencies
object = data.o main.o io.o
project: $(object) //第一个文件 project 是最终的生成文件
cc $(object) -o project //命令一定要以一个 Tab 键作为开头
data.o: data.c data.h //依赖关系,make 命令将比较文件的新旧关系以决定是否执行下面的生成动作
cc -c data.c //生成动作
main.o: data.h io.h main.c
cc -c main.c
io.o: io.h io.c
cc -c io.c
clean: //如果后面没有文件,则将 clean 解释为动作,直接执行下面的命令
rm project $(object) *.o //注意:*.o 是 shell 通配符,并不是 make 的通配符
依赖关系的自动推导
object = data.o main.o io.o
project: $(object) //第一个段是默认目标
cc $(object) -o project
data.o: data.h // make 命令将自动推导出 data.c 文件,cc 命令也将被自动推导
main.o: data.h io.h main.c
io.o: io.h io.c
.PHONY: clean // .PHONY 表示,clean 是个伪目标文件
clean: //如果后面没有文件,则将 clean 解释为动作,直接执行下面的命令
-rm project $(object)
变量应用技巧
a_objects := a.o b.o c.o
1_objects := 1.o 2.o 3.o
sources := $($(a1)_objects:.o=.c) //注意:变量 $al 是可以延迟定义的
子 make 也使用多线程编译
+make b=a c=g
重载 //使用默认变量进行重载
foo:
frobnicate > foo
%: force
@$(MAKE) -f Makefile $@
force:;
变量展开时机 // IMMEDIATE 表示在第一阶段展开,DEFERRED 在第二阶段展开
IMMEDIATE = DEFERRED
IMMEDIATE ?= DEFERRED
IMMEDIATE := IMMEDIATE
IMMEDIATE += DEFERRED or IMMEDIATE //如果此前这个变量是一个简单变量(使用 := 定义的)则认为它是立即展开的
//其它情况时都被认为是“延后”展开的变量
define IMMEDIATE
DEFERRED
Endef
IMMEDIATE: IMMEDIATE; DEFERRED
DEFERRED
变量 //为了保证正确,变量的定义在任何情况下应该顶格写
预设变量 //可用于隐含规则
CFLAGS
CPPFLAGS
CXXFLAGS
自动变量 //属于规则型变量,变量值依赖于规则的目标和依赖目标的定义
$@ // Full name of the current target.
bigoutput littleoutput: text.g
generate text.g -$(subst output,,$@) > $@ // $@ 表示 bigoutput littleoutput,"$@" 依次取出目标,并执于命令
bigoutput: text.g
generate text.g -big > bigoutput
littleoutput: text.g
generate text.g -little > littleoutput
$% //仅当目标是函数库文件的情况下,表示规则中的目标成员名
$(<) //依赖目标中的第一个目标名字
$? //所有比目标新的依赖目标的集合,以空格分隔
print: foo.c bar.c
lpr -p $? //只打印出那些变化了的文件
touch print
$^ //所有的依赖目标的集合,以空格分隔;如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份
$+ //很像 $^ ,也是所有依赖目标的集合,只是它不去除重复的依赖目标
$* //表示目标模式中 % 及其之前的部分,如果目标是 dir/a.foo.b,并且目标的模式是 a.%.b,那么 $* 的值就是 dir/a.foo
$@ $(<) $% $* //扩展值只会有一个文件
$? $^ $+ //扩展值是一个文件列表
$(@D) //表示 $@ 的目录部分并不以斜杠作为结尾,如果 $@ 中没有包含斜杠的话,其值就是 . 当前目录
$(@F) //表示 $@ 的文件部分,如果 $@ 值是 dir/foo.o,那么 $(@F) 就是 foo.o
特殊变量 //属于全局变量,整个文件中都可以访问的到
.LIBPATTERNS //在规则中出现 -lNAME 格式的的依赖时,首先使用这里的 NAME 代替变量 .LIBPATTERNS 的第一个字的模式字符
// % 而得到第一个库文件名根据这个文件名在搜索目录下查找,如果能够找到、就是用这个文件,
//否则使用 NAME 代替第二个字的模式字符,进行同样的查找
//查找目标时的优先级为:当前目录、VPATH vpath 指定的目录、系统默认目录 /lib /usr/lib /usr/local/lib
.LIBPATTERNS := lib%.a lib%.so
.VARIABLES //此引用点之前,makefile 文件中所定义的所有全局变量列表。包括:空变量(未赋值的变量)和 make 的内嵌变量
变量
GPATH //如果通过目录搜寻得到一个过时的(即需要被更新的)完整的目标文件路径名
//而目标存在的目录又出现在 GPATH 变量的定义列表中,则该目标的完整路径将不废弃,目标将在该路径下被重建
MAKE //变量 MAKE 的值是 make 程序的文件名以及 make 的启动选项
//它所实现的功能和在规则中命令行首使用字符 + 的效果相同
MAKECMDGOALS //存放你所指定的终极目标的列表;如果在命令行上,你没有指定目标,那么这个变量是空值
sources = foo.c bar.c
ifneq ($(MAKECMDGOALS),clean)
include $(sources:.c=.d)
endif
MAKEFILE_LIST //可以获得刚才处理了哪一个 makefile 的信息
name1 := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
include inc.mk
name2 := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
all:
@ echo name1 = $(name1)
@ echo name2 = $(name2)
输出为:
name1 = Makefile
name2 = inc.mk
MAKEFILES //如果你的当前环境中定义了环境变量 MAKEFILES,那么,make 会把这个变量中的值做一个类似于 include 的动作
MAKEFLAGS //执行 make 时的命令行选项参数被通过一个变量 MAKEFLAGS 传递给子目录下的 make 程序
//在主控执行 make 时使用 -k 和 -s 选项,那么 MAKEFLAGS 的值就为 "ks"
subsystem: //如果不希望 MAKEFLAGS 的值传递给子 make,就需要在执行子 make 时对它重新进行赋值
cd subdir && $(MAKE) MAKEFLAGS=
MAKELEVEL //记录了当前 Makefile 的调用层数
//最上一级时 MAKELEVEL 的值为 0,下一级时为 1,再下一级为 2
SHELL // make 对所有规则命令的解析使用环境变量 SHELL 所指定的那个程序
VPATH //依赖文件和目标文件搜索路径;当前目录拥有最高的搜索优先级
// VPATH 会废弃目标完整路径名
VPATH = src:../headers
技巧
执行命令
exec:
cd /home/hchen
pwd //每次命令的执行都从当前目录开始
exec:;cd /home/hchen; pwd
变量值替换
$(var: <suffix>=<replacement>) // $(patsubst %<suffix>,%<replacement>,$(var))
foo := a.o b.o c.o
bar := $(foo:.o=.c)
$(var: <pattern>=<replacement>) // $(patsubst <pattern>,<replacement>,$(var))
指针
x = y
y = z
a := $($(x))
first_second = Hello
a = first
b = second
all = $($a_$b)
子模块的处理 //在使用 make 的递归调用时,在 Makefile 中规则的命令行中应该使用变量 MAKE 来代替直接使用 make
SUBDIRS = foo bar baz
.PHONY: subdirs $(SUBDIRS)
subdirs: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $@
foo: baz //用于限制子模块的处理顺序
依赖对象 //依赖对象可以是普通文件、库文件、伪目标、空
//其中,伪目标总被认为是最新的,因为不存在与伪目标相对应的文件
//空则被认为是最旧的
//依赖对象的查找:VPATH 变量或者 vpath 命令指定
foo : foo.c -lcurses //如果 libcurses.a 需要在执行 make 的时生成,那么就不能这样写
cc $^ -o $@
依赖规则 //注意:模式匹配只匹配文件名,并不匹配路径
order-only 依赖 // ORDER-ONLY-PREREQUISITES 仅仅用于产生目标,不作为更新 TARGET 的标准
TARGETS: NORMAL-PREREQUISITES | ORDER-ONLY-PREREQUISITES
Macro feature
.SUFFIXES: .C .f .c .o // Gives a list of file extensions that will be used in the final compilation.
.f.o: // Show how to turn a file with a .f extension into a file with a .o extension.
g77 -c $< // Make absolutely certain you prefix each command line with a TAB character, not spaces.
.C.o:
g++ -c $<
.c.o:
gcc -c $<
多目标 //每一个目标将被依次生成
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
静态模式 //限定了针对哪些 target 使用你给定的命令
objects = foo.o bar.o
$(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
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 $<
双冒号规则 //一个目标可以出现在多个规则中,但是这些规则必须是同一种规则,要么都是普通规则,要么都是双冒号规则
//当同一个文件作为多个双冒号规则的目标时,这些不同的规则会被独立的处理
//而不是像普通规则那样合并所有的依赖到一个目标文件
//对于一个没有依赖而只有命令行的双冒号规则,当引用此目标时,规则的命令将会被无条件执行
//对于一个没有依赖而只有命令行的单引号规则,当引用此目标时,如果目标对象文件存在,则不执行
//注意:双冒号规则不能用于生成 inlcude 的 makefile,因为那样会引起递归
Newprog :: foo.c
$(CC) $(CFLAGS) $< -o $@
Newprog :: bar.c
$(CC) $(CFLAGS) $< -o $@
空命令
%.obj: .%.$(PROJECT_ID).obj;
Target-specific-Variable
<target ...> : <variable-assignment>
prog : CFLAGS = -g
prog : prog.o foo.o bar.o
$(CC) $(CFLAGS) prog.o foo.o bar.o
prog.o : prog.c
$(CC) $(CFLAGS) prog.c
foo.o : foo.c
$(CC) $(CFLAGS) foo.c
bar.o : bar.c
$(CC) $(CFLAGS) bar.c
%.o : CFLAGS = -O
<target ...> : overide <variable-assignment>
通配符 //出现在 targets、prerequisites 中的通配符由 Make 展开,而出现在 commands 中的通配符由 shell 展开
//若想使用通配符所代表字符的原意,在它之前加上转义字符 \;比如 \? 表示问号,而非任意单个字符
//在规则中,通配符会被自动展开;但在变量的定义和使用函数时,通配符不会被自动的展开
//注意:只能表示单一的文件名
~ //当前用户的 home 目录
~zp //用户 zp 的 home 目录
*.o //所有的目标文件
print: *.c
lpr -p $?
touch print
? //单个字符
[abc] // a 或 b 或 c
[a-zA-Z] //所有大小写字母
[!0-9] //所有的非数字
赋值
= //这种定义允许使用未经地定义的变量的值,未经定义的变量的值是该变量的最终值
foo = $(bar)
bar = $(ugh)
ugh = Huh?
ugh = mm?
all:
@echo $(foo)
:= //这种定义允许使用未经定义的变量的值,但是未经定义的变量的值是空
y := $(x) bar
x := foo
empty:=
space:= $(empty) $(empty) //定义一个空格
?=
FOO ?= bar //如果 FOO 没有被定义过,那么变量 FOO 的值就是 "bar",如果 FOO 先前被定义过,那么这条语将什么也不做
//注意:空值也算被定义过
+= //给变量追加值
objects = main.o foo.o bar.o utils.o
objects += another.o
variable := value
variable += more
variable = value
variable += more
内部命令
define //定义多行变量
define two-lines // two-lines 是变量名
echo foo //命令必须以 [Tab] 键开头
echo $(bar)
endef
//该技术可以用于定义 "命令包"
define run-yacc
yacc $(firstword $^)
mv y.tab.c $@
endef
foo.c: foo.y
$(run-yacc)
export/unexport //传递或者不传递变量到下级 Makefile
// make 命令行带入的变量,或是系统环境变量默认 export
//注意:SHELL、MAKEFLAGS 这两个变量不管你是否 export,总是要传递到下层 Makefile,因为它们是系统变量
export variable = value
export variable += value
export //传递所有变量,在新版本的 GNU make 中取消了这一默认的行为,因此在编写和老版本 GNU
// make 兼容的 Makefile 时,需要使用特殊目标 .EXPORT_ALL_VARIABLES 来代替 export
include
include foo.make *.mk $(bar) //在 include 前面可以有一些空字符,但是绝不能是 [Tab] 键开始
-include foo.make *.mk $(bar) // - 表示出错后不报错,无论命令成功与否都认为命令成功
# 将当前目录下所有 flags 文件嵌入
files_flags_exist := $(wildcard .*.flags)
ifneq ($(files_flags_exist),)
include $(files_flags_exist)
endif
override //重新设置由命令行参数设置的变量以及系统环境变量
override <variable> = <value>
override <variable> := <value>
override <variable> += <more text>
vpath //只用于搜索依赖关系中的文件
vpath <pattern> <directories> //为符合模式 <pattern> 的文件指定搜索目录 <directories>
vpath %.h ../headers // "%" 是通配符
vpath <pattern> //清除符合模式 <pattern> 的文件的搜索目录
vpath //清除所有已被设置好了的文件搜索目录
特殊目标
.DEFAULT //一个文件作为某个规则的依赖,但却不是另外一个规则的目标时
// Make 程序无法找到重建此文件的规则,就执行 ".DEFAULT" 所指定的命令
.DELETE_ON_ERROR //如果在 Makefile 中存在特殊目标 ".DELETE_ON_ERROR"
//则规则的命令执行错误,将删除已经被修改的目标文件
.EXPORT_ALL_VARIABLES //此目标应该作为一个简单的没有依赖的目标,它的功能含义是将之后所有的变量传递给子 make 进程
.IGNORE //如果给目标 .IGNORE 指定依赖文件,则忽略创建这个文件所执行命令的错误;给此目标指定命令是没有意义的
//当此目标没有依赖文件时,将忽略所有命令执行的错误
.INTERMEDIATE //目标 .INTERMEDIATE 的依赖文件在 make 时被作为中间过程文件对待
.LOW_RESOLUTION_TIME //目标 .LOW_RESOLUTION_TIME 的依赖文件被 make 认为是低分辨率时间戳文件
.LOW_RESOLUTION_TIME: dst
dst: src
cp -p src dst
.NOTPARALLEL // Makefile 中,如果出现目标 .NOPARALLEL,则所有命令按照串行方式执行,即使存在 make 的命令行参数 -j
//但在递归调用的子 make 进程中,命令可以并行执行
//此目标不应该有依赖文件,所有出现的依赖文件将被忽略
.PHONY // .PHONY的作用:
// 1. 告知 Make,该伪目标并不是真正的文件,不遵循由源代码生成目标文件的规则
// 2. 告诉 Make,该伪目标始终需要被更新
.PHONY: clean
clean:
rm *.o temp
all: prog1 prog2 //伪目标同样可以作为 "默认目标",只要将其放在第一个;也可以为伪目标指定所依赖的文件
.PHONY: all
prog1: prog1.o utils.o
cc -o prog1 prog1.o utils.o
prog2: prog2.o
cc -o prog2 prog2.o
.PHONY: cleanall cleanobj cleandiff
cleanall: cleanobj cleandiff //伪目标同样也可成为依赖
rm program
cleanobj:
rm *.o
cleandiff:
rm *.diff
.PRECIOUS //通过伪目标保存中间临时文件
.PRECIOUS %.o
.SECONDARY //目标 .SECONDARY 的依赖文件被作为中间过程文件对待
//没有任何依赖文件的目标 .SECONDARY 的含义是:将所有的文件作为中间过程文件
相等测试 //注意:ifdef、else、endif 等等不能以 [Tab] 开头
ifdef //实际上是用于判断变量是不是空值
ifdef do_sort
func := sort
else
func := strip
endif
bar := a d b g q c
foo := $($(func) $(bar))
bar =
foo = $(bar) // foo 定义了
foo = // foo 没有进行定义
ifeq
ifeq ($(CC),gcc)
$(CC) -o foo $(objects) $(normal_libs)
endif
ifeq ($(CC),gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else
$(CC) -o foo $(objects) $(normal_libs)
endif
ifneq
ifndef
函数 //调用规则:$(<function> <arg1>,<arg2>,<arg3>),参数之间以 "," 分割;注意:空格不是乱加的
shell 函数 //把执行操作系统命令后的输出作为函数返回
//这个函数会新生成一个 Shell 程序来执行命令,所以你要注意其运行性能
contents := $(shell cat foo)
files := $(shell echo *.c)
字符串处理函数
filter <pattern...>,<text> //以 <pattern> 模式过滤 <text> 字符串中的单词,保留符合模式 <pattern> 的单词
//可以有多个模式
sources := foo.c bar.c baz.s ugh.h
foo: $(sources)
cc $(filter %.c %.s,$(sources)) -o foo
filter-out <pattern...>,<text> //以 <pattern> 模式过滤 <text> 字符串中的单词,去除符合模式 <pattern> 的单词
objects=main1.o foo.o main2.o bar.o
mains=main1.o main2.o
$(filter-out $(mains),$(objects))
findstring <find>,<in> //在字串 <in> 中查找 <find> 字串;如果找到,那么返回 <find>,否则返回空字符串
$(findstring a,b c)
firstword <text> //取字符串 <text> 中的第一个单词
$(firstword foo bar)
patsubst <pattern>,<replacement>,<text> //模式字符串替换函数;返回被替换过后的字符串
$(patsubst %.c,%.o,x.c.c bar.c)
sort <list> //给字符串 <list> 中的单词排序(升序);注意:会去掉 <list> 中相同的单词
$(sort foo bar lose)
strip <string> //去掉 <string> 字串中开头和结尾的空字符
$(strip a b c)
subst <from>,<to>,<text> //字符串替换函数,返回被替换过后的字符串
$(subst ee,EE,feet on the street)
override CFLAGS += $(patsubst %,-I%,$(subst :, ,$(VPATH)))
wildcard // make 支持三个通配符:"*"、"?"、"[...]"
//注意:只在工作目录下展开
~ //当前用户的 $HOME 目录
objects = *.o //不展开通配符 *
objects := $(wildcard *.[chS]) //展开通配符 *
word <n>,<text> //取字符串 <text> 中第 <n> 个单词
$(word 2, foo bar baz)
wordlist <n>,<m>,<text> //从字符串 <text> 中取从 <n> 开始到 <m> 的单词串
$(wordlist 2, 3, foo bar baz)
words <text> //统计 <text> 中字符串中的单词个数
$(words foo bar baz)
$(word $(words <text>),<text>)
文件名操作函数
addprefix <prefix>,<names...> //把前缀 <prefix> 加到 <names> 中的每个单词前面
$(addprefix src/,foo bar)
addsuffix <suffix>,<names...> //把后缀 <suffix> 加到 <names> 中的每个单词后面
$(addsuffix .c,foo bar)
basename <names...> //从文件名序列 <names> 中取出各个文件名的前缀部分;如果文件没有前缀,则返回空字符串
$(basename src/foo.c src-1.0/bar.c hacks)
dir <names...> //从文件名序列 <names> 中取出目录部分
//目录部分是指最后一个反斜杠 "/" 之前的部分;如果没有反斜杠,那么返回 "./"
$(dir src/foo.c hacks)
join <list1>,<list2> //把 <list2> 中的单词对应地加到 <list1> 的单词后面
$(join aaa bbb , 111 222 333) //返回值: "aaa111 bbb222 333"
notdir <names...> //从文件名序列 <names> 中取出非目录部分;非目录部分是指最后一个反斜杠 "/" 之后的部分
$(notdir src/foo.c hacks)
suffix <names...> //从文件名序列 <names> 中取出各个文件名的后缀;如果文件没有后缀,则返回空字符串
$(suffix src/foo.c src-1.0/bar.c hacks)
其它函数
call <expression>,<parm1>,<parm2>,<parm3>...
reverse = $(1) $(2) // $(1) 是位置参数
foo = $(call reverse,a,b)
error <text ...> //输出错误信息并退出
ifdef ERROR_001
$(error error is $(ERROR_001)) //注意:不能顶格书写,会被当作命令处理
endif
ERR = $(error found an error!)
.PHONY: err
err: ; $(ERR) //注意:makefile 中的变量其实是宏;即使用缓式评估策略
foreach <var>,<list>,<text> // foreach 中的 <var> 参数是一个临时的局部变量,其作用域只在 foreach 函数当中
// foreach 函数执行完后,参数 <var> 的变量将不在作用
names := a b c d
files := $(foreach n,$(names),$(n).o) //返回值: "a.o b.o c.o d.o"
if <condition>,<then-part> // <condition> 参数返回的为非空字符串,那么这个表达式就相当于返回真
if <condition>,<then-part>,<else-part>
origin <variable-name>
ifeq "$(origin bletch)" "environment"
返回值
automatic //自动化变量
command line //变量是被命令行定义的
default //默认变量
environment //环境变量,并且当 Makefile 被执行时,"-e" 参数没有被打开
file //变量被定义在 Makefile 中
override //变量被 override 指示符重新定义
undefined //没有定义过
warning <text ...> //输出警告信息并退出 |
|