Makefile Howto
- Makefile Howto
- 入门
- Why called Makefile
- make 命令依次查找如下文件 `GNUmakefile', `makefile' and `Makefile'
- GNUmakefile 可能不被非 gnu 的 make 识别
- 之所以用 Makefile,因为显示文件列表排在最前
- Makefile 规则介绍
- target ... : prerequisites ...
command
...
- 依赖也可以为空。例如 clean 不需要依赖任何文件
- 依赖可以决定 target 是否 outofdate,命令告诉如何生成 target
- 变量
- 变量定义,如:
- objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
- 示例
- 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)
- 进阶
- 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)
- 再例如:
- 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))
- 变量计算
- 如
- 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
- ifeq ($(CC),gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else
$(CC) -o foo $(objects) $(normal_libs)
endif
- ifneq "var1" "var2" ... else ... endif
- ifdef var ... else ... endif
- 例如
- ifdef XML_CATALOG_FILES
ENSURE_XSL =
else
ENSURE_XSL = if ! test -e "$(TOOLS_DIR)/xsl"; \
then $(TOOLS_DIR)/bin/find-xsl.py; fi
endif
- 定义包含多行文本的变量
- 例如下面的指令,定义了包含两条 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 起始!
- @ 字符
- 执行命令,但不显示命令本身。@ 字符脱掉之后,传递给 Shell 执行
- make -s/--silent 可以起到同样效果
- 特殊目标:all
- 多目标 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 之间空格分开
- 字符串函数
- 替换
- $(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)
- 如:
-
$(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))
- 排序、次序
- $(wordlist s,e,text)
- $(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
- 文件名函数
- $(dir names...)
- 返回的目录名包括最后的斜杠
- 如: $(dir 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...)
- $(addprefix prefix,names...)
- $(addprefix src/,foo bar)
- $(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
- 查看变量 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
- Makefile Samples
- WHODO DocBook Makefile(s)
Makefile Howto//mm2html.xsl FreemindVersion:0.8.0