https://github.com/processone/ejabberd/blob/3d3a4f75435ad353cf9a192b8ef23335be0a5b01/Makefile.in
ejabberd 16.04 (639c2fb)https://github.com/processone/ejabberd/blob/639c2fb6401391663206c0e4c946d1a699689ac7/Makefile.in
区别最大的就是下面这一段 下面这一段比之前的用法要复杂,正好可以借这个机会把makefile更好的理解一下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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
JOIN_PATHS=$(if $(wordlist 2,1000,$(1)),$(firstword $(1))/$(call JOIN_PATHS,$(wordlist 2,1000,$(1))),$(1))
VERSIONED_DEP=$(if $(DEP_$(1)_VERSION),$(DEP_$(1)_VERSION),$(1))
ELIXIR_TO_DEST=$(LIBDIR) $(call VERSIONED_DEP,$(word 2,$(1))) $(wordlist 5,1000,$(1))
DEPS_TO_DEST=$(LIBDIR) $(call VERSIONED_DEP,$(word 2,$(1))) $(wordlist 3,1000,$(1))
MAIN_TO_DEST=$(LIBDIR) $(call VERSIONED_DEP,ejabberd) $(1)
TO_DEST_SINGLE=$(if $(subst XdepsX,,X$(word 1,$(1))X),$(call MAIN_TO_DEST,$(1)),$(if $(subst XlibX,,X$(word 3,$(1))X),$(call DEPS_TO_DEST,$(1)),$(call ELIXIR_TO_DEST,$(1))))
TO_DEST=$(foreach path,$(1),$(call JOIN_PATHS,$(call TO_DEST_SINGLE,$(subst /, ,$(path)))))
FILTER_DIRS=$(foreach path,$(1),$(if $(wildcard $(path)/*),,$(path)))
FILES_WILDCARD=$(call FILTER_DIRS,$(foreach w,$(1),$(wildcard $(w))))
ifeq ($(MAKECMDGOALS),copy-files-sub)
DEPS:=$(sort $(shell $(REBAR) list-deps|$(SED) -e '/^=/d;s/ .*//'))
DEPS_FILES=$(call FILES_WILDCARD,$(foreach DEP,$(DEPS),deps/$(DEP)/ebin/*.beam deps/$(DEP)/ebin/*.app deps/$(DEP)/priv/* deps/$(DEP)/priv/lib/* deps/$(DEP)/priv/bin/* deps/$(DEP)/include/*.hrl deps/$(DEP)/lib/*/ebin/*.beam deps/$(DEP)/lib/*/ebin/*.app))
DEPS_FILES_FILTERED=$(filter-out %/epam deps/elixir/ebin/elixir.app,$(DEPS_FILES))
DEPS_DIRS=$(sort deps/ $(foreach DEP,$(DEPS),deps/$(DEP)/) $(dir $(DEPS_FILES)))
MAIN_FILES=$(filter-out %/configure.beam,$(call FILES_WILDCARD,ebin/*.beam ebin/*.app priv/msgs/*.msg priv/lib/* include/*.hrl))
MAIN_DIRS=$(sort $(dir $(MAIN_FILES)) priv/bin priv/sql)
define DEP_VERSION_template
DEP_$(1)_VERSION:=$(shell $(SED) -e '/vsn/!d;s/.*, *"/$(1)-/;s/".*//' $(2) 2>/dev/null)
endef
$(foreach DEP,$(DEPS),$(eval $(call DEP_VERSION_template,$(DEP),deps/$(DEP)/ebin/$(DEP).app)))
$(eval $(call DEP_VERSION_template,ejabberd,ebin/ejabberd.app))
define COPY_template
$(call TO_DEST,$(1)): $(1) $(call TO_DEST,$(dir $(1))) ; $$(INSTALL) -m 644 $(1) $(call TO_DEST,$(1))
endef
$(foreach file,$(DEPS_FILES_FILTERED) $(MAIN_FILES),$(eval $(call COPY_template,$(file))))
$(sort $(call TO_DEST,$(MAIN_DIRS) $(DEPS_DIRS))):
$(INSTALL) -d $@
$(call TO_DEST,deps/p1_pam/priv/bin/epam): $(LIBDIR)/%: deps/p1_pam/priv/bin/epam $(call TO_DEST,deps/p1_pam/priv/bin/)
$(INSTALL) -m 750 $(O_USER) $< $@
$(call TO_DEST,priv/sql/lite.sql): sql/lite.sql $(call TO_DEST,priv/sql)
$(INSTALL) -m 644 $< $@
$(call TO_DEST,priv/bin/captcha.sh): tools/captcha.sh $(call TO_DEST,priv/bin)
$(INSTALL) -m 750 $(O_USER) $< $@
copy-files-sub2: $(call TO_DEST,$(DEPS_FILES) $(MAIN_FILES) priv/bin/captcha.sh priv/sql/lite.sql)
endif
copy-files:
$(MAKE) copy-files-sub
copy-files-sub: copy-files-sub2
install: all copy-files
最近的变更 其实之前改makefile都是照猫画虎,改动较小,就模仿一下能work就行了。
这次16.04的改动比较大,而且看不太懂,故此仔细理解一下。 makefile reference https://www.gnu.org/software/make/manual/
首先过了一遍手册详细理解了一下makefile的语法 关于makefile的call https://www.gnu.org/software/make/manual/html_node/Call-Function.html
$(call variable,param,param,…)
注意call函数后面第一个参数是个变量,后面是给变量再传递的函数。
这段新代码的入口我们看看入口:
copy-files-sub: copy-files-sub2copy-files-sub2: $(call TO_DEST,$(DEPS_FILES) $(MAIN_FILES) priv/bin/captcha.sh priv/sql/lite.sql)
copy-files-sub2是copy-files-sub的prerequisite
copy-files-sub2是个target,:后面的是prerequisites,我们在讨论make install的执行过程前,先理解一下后面这个call的语句。
这里是call了 TO_DEST这个变量,并将$(DEPS_FILES) $(MAIN_FILES) priv/bin/captcha.sh priv/sql/lite.sql 传给这个变量 理解 $(call TO_DEST,$(DEPS_FILES) $(MAIN_FILES) priv/bin/captcha.sh priv/sql/lite.sql) 再来看看TO_DEST的定义
TO_DEST=$(foreach path,$(1),$(call JOIN_PATHS,$(call TO_DEST_SINGLE,$(subst /, ,$(path)))))
这里$(1)就是$(DEPS_FILES) $(MAIN_FILES) priv/bin/captcha.sh priv/sql/lite.sql了 make的函数 foreach是make的一个函数
make的function可以在这里找到
https://www.gnu.org/software/make/manual/html_node/Functions.html#Functions foreach function的文档在这个链接
https://www.gnu.org/software/make/manual/html_node/Foreach-Function.html#Foreach-Function 赋值表达式 DEPS:=$(sort $(shell $(REBAR) list-deps|$(SED) -e ‘/^=/d;s/ .*//‘))
TO_DEST=$(foreach path,$(1),$(call JOIN_PATHS,$(call TO_DEST_SINGLE,$(subst /, ,$(path))))) 注意这里DEPS使用:=赋值,而TO_DEST使用=赋值,具体有什么区别呢?
详细见这里
https://www.gnu.org/software/make/manual/html_node/Setting.html#Setting
Variables defined with ‘=’ are recursively expanded variables. Variables defined with ‘:=’ or ‘::=’ are simply expanded variables; these definitions can contain variable references which will be expanded before the definition is made. See The Two Flavors of Variables.
recursively expanded variables: 简单的说,就是在定义时就确定好值了,和一般编程语言的用法一致simply expanded variables: 只有在变量被使用时,才对这个变量里引用的其他变量取当前值。 如果实在无法理解,可以本地创建makefile,执行一下试试
1
2
3
4
5
6
7
8
9
cat makefile
X = foo
Y = $(X) bar
X = notfoo
all:
@echo $(Y)
darcy/tmpmake
notfoo bar
1
2
3
4
5
6
7
8
9
10
cat makefile
X := foo
Y := $(X) bar
X := notfoo
all:
@echo $(Y)
darcy/tmpmake
foo bar
不过这里DEPS用:=还真是没看出什么特别之处,可能是防止在make里会添加其他运算,而这里需要最终值吧
逻辑梳理知道了大概的几个语法,我们来详细看看这段代码的作用
TO_DEST=$(foreach path,$(1),$(call JOIN_PATHS,$(call TO_DEST_SINGLE,$(subst /, ,$(path)))))$(foreach <var>,<list>,<text>) 这里path是变量名,传递给后面的subst $(1) 是之前传入的paramlist: $(DEPS_FILES) $(MAIN_FILES) priv/bin/captcha.sh priv/sql/lite.sql priv/lib/zoom_cpu_stat.so text参数是另三个嵌套的表达式
$(subst <from>,<to>,<text>), 把字符串<text> 中的 <from> 替换为 <to> $(subst /, ,$(path)) , priv/bin/captcha.sh 会变成 priv bin captcha
call TO_DEST_SINGLE,$(subst /, ,$(path)) ,这里call了另一个TARGET TO_DEST_SINGLE,见下面介绍
JOIN_PATHS=$(if $(wordlist 2,1000,$(1)),$(firstword $(1))/$(call JOIN_PATHS,$(wordlist 2,1000,$(1))),$(1))
如果$(wordlist 2,1000,$(1))为true,则执行$(firstword $(1))/$(call JOIN_PATHS,$(wordlist 2,1000,$(1))),否则返回$(1) #$(if <condition>,<then-part>,<else-part>)
@echo $(wordlist 1,3,aa bb cc dd) # aa bb cc if $(wordlist 2,1000,$(1)), 如果路径的深度大于1,比如priv bin,为true
后面是个递归调用,如果路径长度小于2,则直接返回,否则取路径第一个参数后面加个/
总之最终的效果就是把之前/替换成的空格,又换成了/. 为什么要这么写呢?感觉特别蠢,但是貌似暂时想不到特别好的办法,因为TO_DEST_SINGLE需要处理路径,比如替换路径名,但是word只支持空格分隔的字符串。 TO_DEST_SINGLE
TO_DEST_SINGLE=$(if $(subst XdepsX,,X$(word 1,$(1))X),$(call MAIN_TO_DEST,$(1)),$(if $(subst XlibX,,X$(word 3,$(1))X),$(call DEPS_TO_DEST,$(1)),$(call ELIXIR_TO_DEST,$(1))))
$(word 1,$(1)) #@echo $(word 1,aa bb cc dd) 会显示aa,返回第一个单词 $(subst XdepsX,,X$(word 1,$(1))X), 如果路径第一个单词是deps, call MAIN_TO_DEST, 否则执行$(if $(subst XlibX,,X$(word 3,$(1))X),$(call DEPS_TO_DEST,$(1)),$(call ELIXIR_TO_DEST,$(1)))现在看看true的逻辑:MAIN_TO_DEST=$(LIBDIR) $(call VERSIONED_DEP,ejabberd) $(1)
VERSIONEDDEP=$(if $(DEP$(1)VERSION),$(DEP$(1)_VERSION),$(1)) … 这是什么鬼东西? DEP_$(1)_VERSION:=$(shell $(SED) -e ‘/vsn/!d;s/., “/$(1)-/;s/“.*//‘ $(2) 2>/dev/null) …这里$(2)是啥 VERSIONED_DEP, make template的定义 下面解释一下VERSIONED_DEP, 代码里定义了一个templatedefine DEP_VERSIONtemplate
DEP$(1)_VERSION:=$(shell $(SED) -e ‘/vsn/!d;s/., “/$(1)-/;s/“./