makefile how to

来源:互联网 发布:win10磁盘优化闪退 编辑:程序博客网 时间:2024/05/04 17:48

http://www.worldhello.net/doc/makefile_howto/makefile_howto.mm.htm

World Hello

  • 首页

  • 博客

  • 文章

  • 关于

  • GitHub

  • 微博

Makefile Howto

Makefile Howto

入门

Why called Makefile

make 命令依次查找如下文件 `GNUmakefile', `makefile' and `Makefile'

GNUmakefile 可能不被非 gnu 的 make 识别

之所以用 Makefile,因为显示文件列表排在最前

Makefile 规则介绍

 target ... : prerequisites ...

command

...

命令前面是一个 Tab 制表符,而不是空格!

目标和依赖都可以是多个

依赖也可以为空。例如 clean 不需要依赖任何文件

依赖可以决定 target 是否 outofdate,命令告诉如何生成 target

变量

变量定义,如:

objects = main.o kbd.o command.o display.o \

insert.o search.o files.o utils.o

变量引用,如:$(objects)

如果要显示 $ 字符,则可以 $$

示例

 objects = main.o kbd.o command.o display.o \

insert.o search.o files.o utils.o



edit : $(objects)

cc -o edit $(objects)



$(objects) : defs.h

kbd.o command.o files.o : command.h

display.o insert.o search.o files.o : buffer.h



.PHONY : clean

clean :

-rm edit $(objects)

进阶

Makefile 的五大要素

显示规则

隐含规则

变量定义

指令

注视

5-1. 显示规则

指定目标以及该目标的依赖,以及生成目标文件的命令

格式

命令另起一行,首字符是 tab

 target ... : prerequisites ...

command

...

命令可以和 依赖处于同一行,分号隔开

targets : prerequisites ; command

command

...

依赖

normal prerequisites

作用1:指定编译顺序,先执行依赖本身的编译,之后再执行目标的编译

作用2:确定依赖关系,根据依赖文件于目标文件的时间戳对比,确认是否 outofdate

order-only prerequisites

 

格式:targets : normal-prerequisites | order-only-prerequisites

即用竖线分隔开普通依赖和顺序依赖

顺序依赖只起到前述的作用1,而不会影响 target 的 update 状态

也不会影响自动变量 $^ 等

例如 DocBook Makefile

autolayout.xml: layout.xml | docbook.test

测试一下

test : 1.xxx 2.xxx | 3.xxx

@echo "test depends: $^"

%.xxx :

@echo "now make target: $@"



执行 make -n test 将显示



echo "now make target: 1.xxx"

echo "now make target: 2.xxx"

echo "now make target: 3.xxx"

echo "test depends: 1.xxx 2.xxx"

5-2. 隐含规则

built-in 隐含规则

隐含的 C 规则

*.c 文件生成 *.o 文件,使用命令 $(CC) -c $(CPPFLAGS) $(CFLAGS)

隐含的 C++ 规则

*.cc/*.C 文件生成 *.o 文件,使用命令 $(CXX) -c $(CPPFLAGS) $(CXXFLAGS)

隐含的 Pascal 规则

*.p 文件生成 *.o 文件,使用命令 $(PC) -c $(PFLAGS)

链接目标文件规则

将目标文件 *.o 链接为可执行文件,命令: $(CC) $(LDFLAGS) *.o $(LOADLIBES) $(LDLIBS)

自定义模式规则(Pattern Rules )

例如

 %.o : %.c

$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@

 % :: RCS/%,v

$(CO) $(COFLAGS) $<

%.tab.c %.tab.h: %.y

bison -d $<

自定义的模式规则,可以替代自带的隐含规则

5-3. 变量定义

变量

大小写敏感

变量定义的

几种风格

风格1: 递归扩展变量

(recursively expanded variable)

变量定义格式是,变量和值之间用等号,即 =

例如:



foo = $(bar)

bar = $(ugh)

ugh = Huh?

all:;echo $(foo)

将显示 Huh?

再例如:

CFLAGS = $(include_dirs) -O

include_dirs = -Ifoo -Ibar

缺点是不能这么定义:CFLAGS = $(CFLAGS) -O ,将会死循环

风格2: 简单扩展变量

(simply expanded variables)

变量定义格式是,变量和值之间用冒号等号,即 :=

例如

x := foo

y := $(x) bar

x := later



等价于:



y := foo bar

x := later

另外 ?= 含义为:没有定义则赋值

 FOO ?= bar



等价于



ifeq ($(origin FOO), undefined)

FOO = bar

endif

+= 是为变量后面追加字符

变量替换

 

$(var:a=b),是将 var 变量中每一个单词后面的 a 替换为 b

$(var:suffix=replacement)



等价于



$(patsubst %suffix,%replacement,$(var))

$(foo:%.o=%.c) ,由于出现了 %, 其功能和 patsubst 等价

$(var:pattern=replacement)



等价于



$(patsubst pattern,replacement,$(var))

变量计算

$($(var))

x = $(y)

y = z

z = Hello

a := $($(x))

x = y

y = z

z = u

a := $($($(x)))

通配符变量

如果在变量定义中使用通配符,objects = *.o ,并不能展开通配符,*.o 被当做3个字符的字符串

如下格式定义: objects := $(wildcard *.o)

使用函数,将 .c 文件转换为 .o 文件: $(patsubst %.c,%.o,$(wildcard *.c))

自动变量

$@

目标文件。当目标文件有多个,$@是触发规则的那个目标文件

当目标文件是 archive member,$@是 archive file,$% 是member name

$%

当目标文件是 archive member,$@是 archive file,$% 是member name

例如 目标若是 foo.a(bar.o),则 $%是 bar.o,$@是 foo.a

$<

第一个依赖文件

$?

比目标文件新的所有依赖文件,文件之间用空格分开

当依赖文件是 archive members,$? 是 member name

$^

所有依赖文件(包括比目标旧的依赖文件),文件之间用空格分开

当依赖文件是 archive members,$? 是 member name

当一个文件在依赖列表中被罗列多次, $^ 只包含一次

$+

很 $^ 类似。

当一个文件在依赖列表中被罗列多次, $+ 不同于 $^,包含多个

$*

Patterns Match 中和目标文件匹配的部分

如: 目标为 `dir/a.foo.b' 并且目标表达式为 `a.%.b,则 $* 返回匹配的部分: `dir/foo'

$(@D), $(@F), $(*D), $(*F), 

$(%D), $(%F), $(<D), $(<F), 

$(^D), $(^F), $(+D), $(+F), 

$(?D), $(?F)

分别标识上述变量中的目录部分(D),或者文件部分 (F)

目录部分最后的 /,被删除

如 `$(@F)' 等价于 `$(notdir $@)'.

5-4. 指令

include

包含其它文件

-include 含义为,如果被包含文件不存在,不报错

条件判断

ifeq(var1, var2) ... else ... endif

如果 var1, va2 相等

ifeq ($(CC),gcc)

$(CC) -o foo $(objects) $(libs_for_gcc)

else

$(CC) -o foo $(objects) $(normal_libs)

endif

ifneq "var1" "var2" ... else ... endif

如果 var1, var2 不相等

ifdef var ... else ... endif

如果 var 不为空

例如

ifdef XML_CATALOG_FILES

ENSURE_XSL = 

else

ENSURE_XSL = if ! test -e "$(TOOLS_DIR)/xsl"; \

then $(TOOLS_DIR)/bin/find-xsl.py; fi

endif

ifndef variable-name

定义包含多行文本的变量

例如下面的指令,定义了包含两条 echo 命令的变量 two-lines

define two-lines

echo foo

echo $(bar)

endef

例如:

define run-yacc

yacc $(firstword $^)

mv y.tab.c $@

endef



foo.c : foo.y

$(run-yacc)



5-5. 注释

# 注释一行,\# 代表真正的 井号

# 注释行最后的 \ 字符,将会使下一行也成为注释

规则中的命令

TAB 字符

除了第一行命令可以于 target-and-prerequisites 同一行,用分号分隔外,都要在行首用 Tab 缩进。

注释和空行被忽略。但要注意所谓空行,也要有一个 TAB 起始!

条件指令不需要 有 Tab 起始?

@ 字符

执行命令,但不显示命令本身。@ 字符脱掉之后,传递给 Shell 执行

make -s/--silent 可以起到同样效果

\ 续行符

位于行尾的 \ ,作为续行符

cd 目录的作用范围

cd命令,改变目录,不会影响后续命令的路径

除非和 cd 命令处于同一行,用分号分开

- 忽略错误

在 TAB 之后的减号 -, 将忽略该命令的错误

如:

 clean:

-rm -f *.o

特殊目标:all

执行 make 如果不指定目标,将执行第一个目标

多目标 Makefile,则可以将第一个目标定为 all,将其它目标作为其依赖,这样就可以执行所有目标编译,并指定编译顺序。

Phony Targets

clean:

rm *.o temp

clean 这样的 target 本身没有任何依赖,

如果目录中存在名为 clean 的文件,则

不再执行,因为认为 clean 的状态是更新的。

.PHONY : clean

将 clean 加入 .PHONY ,则 clean 的执行不会收到存在同名文件的影响

.PHONY : all clean

像 all 这样拥有依赖目标的,也可以加入到 phony 中

.PHONY 的替代方案 "FORCE"

如:

clean: FORCE

rm $(objects)

FORCE:

有的 make 不支持 .PHONY,则可以定义一个不存在的目标,没有任何依赖,也没有任何命令,如 FORCE:

FORCE 因为不存在,且没有任何依赖,其本身如果被当做依赖,则相应的目标必然执行。起到了 .PHONY 的作用

函数

格式

$(function arguments) 或者 ${function arguments}

function 和 arguments 之间空格分开

各个 argument 之间用冒号分开

字符串函数

替换

$(subst from,to,text)

子串替换。$(subst from,to,text) ,将 text 中出现的 from 用 to 替换

$(subst ee,EE,feet on the street)

$(patsubst pattern,replacement,text)

$(patsubst %.c,%.o,x.c.c bar.c)

去掉首尾空格

$(strip string)

.PHONY: all

ifneq $(strip $(needs_made)) ""

all: $(needs_made)

else

all:;@echo 'Nothing to make!'

endif

查找、过滤

$(findstring find,in)

找到,则返回 find, 否则返回空串

如:



$(findstring a,a b c)

$(findstring a,b c)

$(filter pattern...,text)

在 text 中查找匹配 pattern(可为多个)的单词

如:

sources := foo.c bar.c baz.s ugh.h

foo: $(sources)

cc $(filter %.c %.s,$(sources)) -o foo

$(filter-out pattern...,text)

和 filter 函数相反,在 text 中查找除了 pattern(可为多个)之外的单词

如:

objects=main1.o foo.o main2.o bar.o

mains=main1.o main2.o



$(filter-out $(mains),$(objects))

排序、次序

$(sort list)

如: $(sort foo bar lose)

$(words text)

返回 text 中单词数量

$(word n,text)

返回 text 中第几个单词,从1开始

$(word 2, foo bar baz)

$(wordlist s,e,text)

返回 text 中第s个到第e个单词

$(wordlist 2, 3, foo bar baz)

$(firstword names...)

返回第一个单词

例如:测试 DocBook XSLT 引擎

# XSLT=java \

-Djavax.xml.parsers.DocumentBuilderFactory=org.apache.xerces.jaxp.DocumentBuilderFactoryImpl \

...

XSLT=/usr/bin/xsltproc --nonet --timing



ifeq ($(notdir $(firstword $(XSLT))),xsltproc)

...

else

...

endif

联合

$(join list1,list2)

文件名函数

$(dir names...)

返回的目录名包括最后的斜杠

如: $(dir src/foo.c hacks)

$(notdir names...) 

返回文件名

如: $(notdir src/foo.c hacks)

例如

 

$(suffix names...)

返回文件扩展名

如:$(suffix src/foo.c src-1.0/bar.c hacks) 返回 .c .c

$(basename names...)

注意:此 basename 和 shell 的 basename 不同!返回去掉扩展名之后的文件名包含目录名。

 $(basename src/foo.c src-1.0/bar hacks) 返回 src/foo src-1.0/bar hacks

$(addsuffix suffix,names...)

为文件增加扩展名

$(addsuffix .c,foo bar)

$(addprefix prefix,names...)

增加前缀

$(addprefix src/,foo bar)

$(join list1,list2)

$(wildcard pattern)

展开通配符

例如

ALL_SOURCE := $(wildcard $(XML_SRCDIR)/*.xml)

ALL_SOURCE := $(filter-out $(VERSION_SOURCE),$(ALL_SOURCE))



# 如果不用 wildcard, $(ALL_SOURCE) 依然是 *.xml ,仍然包括 version.xml,造成循环依赖

$(VERSION_SOURCE) : $(ALL_SOURCE)

... ...

foreach

find_files = $(wildcard $(dir)/*)

dirs := a b c d

files := $(foreach dir,$(dirs),$(find_files))



等价于



files := $(wildcard a/* b/* c/* d/*)

call

如:

reverse = $(2) $(1)

foo = $(call reverse,a,b)

pathsearch = $(firstword $(wildcard $(addsuffix /$(1),$(subst :, ,$(PATH)))))

LS := $(call pathsearch,ls)

map = $(foreach a,$(2),$(call $(1),$(a)))

o = $(call map,origin,o map MAKE)

origin

$(origin variable)

查看变量 variable 的来源,variable 不要带 $。

返回值:undefined,default,environment,environment override,

command line,override,automatic

如: DocBook Makefile 测试环境变量 XML_CATALOG_FILES

docbook.test:

ifeq "$(XML_CATALOG_FILES)" ""

$(error XML_CATALOG_FILES is blank!)

endif

ifeq "$(origin XML_CATALOG_FILES)" "undefined"

$(error XML_CATALOG_FILES is $(origin XML_CATALOG_FILES) !)

endif

SHELL 函数

contents := $(shell cat foo)

files := $(shell echo *.c)

出错处理函数

$(error text...)

显示异常,并退出

如: DocBook Makefile 测试环境变量 XML_CATALOG_FILES

docbook.test:

ifeq "$(XML_CATALOG_FILES)" ""

$(error XML_CATALOG_FILES is blank!)

endif

ifeq "$(origin XML_CATALOG_FILES)" "undefined"

$(error XML_CATALOG_FILES is $(origin XML_CATALOG_FILES) !)

endif

$(warning text...)

显示警告,不退出

诊断、调试

make -n

不执行命令,只是显示每条命令的执行

@echo ...

打印消息

$(error text...)

显示异常,并退出

$(warning text...)

显示警告,不退出

Makefile Samples

WHODO DocBook Makefile(s)

关于本文

版本

v0.1 at 2005/08

作者

J

Jiang Xin


0 0