- 根據 makefile,控制如何從 source files 產生 non-source files。
- 可以描述 dependency,除了會影響執行順序之外,也會自動檢查哪些檔案因為 source 或 dependency 有變動而需要重新產生 (不用全部重新產生)。
- 任何想自動化的事情都可以 (anything else you want to do often enough to make it worth while writing down how to do it),不限定程序語言或特定用途,只要能用 shell commands 描述要做什麼。
參考資料:
- Make - GNU Project - Free Software Foundation
- Make (software) - Wikipedia #ril
- make(1) - Linux man page #ril
- make 會自動決定一個 large program 裡有哪些需要 recompile。這裡用 C 說明,但任何支援 shell 用法的 compiler 都可以用 make。
- 事實上,make 不只可以用在程式 -- any task where some files must be updated automatically from others whenever the others change,概念上就是 "自動"
Makefile
:
# Environment variable `WHO` for customization
TO = $(or $(WHO), 'World')
hello: time
echo Hello, $(TO)'!' # Inline comments passed to shell
time:
@echo `date`
執行起來像是:
$ make
Tue Dec 19 08:05:14 CST 2017
echo Hello, 'World''!' # Inline comments passed to shell
Hello, World!
$
$ WHO=Jeremy make
Tue Dec 19 08:05:26 CST 2017
echo Hello, Jeremy'!' # Inline comments passed to shell
Hello, Jeremy!
從中可以觀察出:
make
會自動找當前目錄下的Makefile
。make [target]...
預設會執行第 1 個 target。- 註解用
#
表示,但接在指令後的註解會傳給 shell,由 shell 決定如何處理。 $(or $(WHO), 'World')
的用法是 function -$(function arguments)
。- 變數跟環境變數都用
$(name)
的方式引用。 hello: time
描述了相依性time
,所以hello
前會先執行time
。- 指令要以 tab 內縮開頭,以
@
開頭的指令不會被印出來 (echoing)。
- 2.2 A Simple Makefile - GNU make #ril
- Appendix C Complex Makefile Example - GNU make #ril
- Makefile - Wikipedia #ril
- It’s time for makefiles to make a comeback – Coding Coda (2017-09-05) #ril
- 該把 makefile 請回來了 – CQD – Medium (2017-09-16) #ril
- Make Rules and Targets - Make - GNU Project - Free Software Foundation Rule =
target: dependencies ...
+commands...
,也就是如何 build 出 target,為什麼有 "target file" 的說法?? #ril - Recipe Echoing - GNU make 預設會將 recipe 印出來 (echoing),可以用開頭的
@
抑制這個行為,例如@echo bla bla
。另外-n, --just-print, --dry-run, --recon
會印出所有的 recipe (包括以@
開頭者),但不會真的執行 (就是 dry-run)。 - 4.2 Rule Syntax - GNU make
targets : prerequisites
其中targets
跟prerequisites
都是 separated by whitespace #ril - makefile - How does "make" app know default target to build if no target is specified? - Stack Overflow anon: 執行第一個不是以
.
開頭的 target #ril
- 實務上不需要將所有的 target 列在
.PHONY: ...
後面,只要在可能發生衝突的 target 前加上.PHONY: <target>
即可。
參考資料:
- GNU make: Phony Targets
- A phony target is one that is not really the name of a file; rather it is just a name for a recipe to be executed when you make an explicit request. 識別某個 target 就是個要做事情的 target,跟檔名無關 (phony 是口語上的 "假的");除了避免跟檔名衝突之外,另一點是提昇效能。
- 以
clean: rm *.o temp
為例,由於clean
(target) 不會建立clean
(file),所以這個 target 每次都會執行 ... 這沒什麼問題,但如果一個名叫clean
的檔案/目錄出現時,由於它沒有 prerequisites,所以clean
總被視為 up-to-date,結果就是clean
target 不會被執行。為了避免這種問題,可以將它列為.PHONY
(special target) 的 prerequisites,這樣clean
target 不管clean
file/dir 是否存在,都會執行。 - 這裡把
.PHONY: clean
寫在clean:
前面好像不錯?
- Appendix C Complex Makefile Example - GNU make 用到許多
.PHONY
,包括install
、clean
、dist
等,都是以動詞命名。 - Search · filename:makefile .phony
.PHONY
被使用很多次,甚至寫在 target 之後。
- make(1) - Linux man page #ril
make [ -f makefile ] [ options ] ... [ targets ] ...
- 因為早期 backward compatibility 的考量,每一個 recipe 都要以 tab 開頭沒錯 - TAB indented command lines。
- Make 3.82 後加入了
.RECIPEPREFIX
這個 special variable,不過目前 macOS 10.13.1 (High Sierra) 只內建 Make 3.81。 - 用 Vim 編輯
Makefile
時,按 Enter 換行預設會用 tab 內縮,雖然有設定set expandtab
,大概是 syntax highlighting 在作用? 不過
參考資料:
- Rules - Makefile - Wikipedia 提到 "Note the use of meaningful indentation in specifying commands; also note that the indentation must consist of a single character.",至少要有一個 tab 這要求還真特別! (事實上是以 tab 開頭)
- Appendix B Errors Generated by Make - GNU make "missing separator. Stop." 這樣的錯誤,通常是因為 recipe 用 space 內縮而非 tab,但 make 預會要求 "every line in the recipe must begin with a tab character",除非用
.RECIPEPREFIX
自訂。 - Rules - Make (software) - Wikipedia TAB indented command lines 的設計惹來許多批評 (跟一連串的 space 沒有 visual difference),作者 Stuart Feldman 說明那是基於 backward compatibility 的考量,雖然早期並沒有多少使用者,很抱歉那就是 history。在 v3.82 後加入了
.RECIPEPREFIX
這個 special variable 可以自訂 recipe prefix。 - See the tabs in your file | Vim Tips Wiki | FANDOM powered by Wikia 可以搭配
:set list
與:set listchars=tab:\|\
營造出效果。 - .RECIPEPREFIX - GNU make
.RECIPEPREFIX
#ril
- What Makefiles Contain - GNU make 以
#
開始的整行 (前面可能有些空白) 會被視為註解,也可以用在行內,不過解讀方式不同,例如 recipe 裡的註解會整個傳給 shell,就看 shell 如何處理 #ril
- 6 How to Use Variables - GNU make #ril
- Variable 用一個 name 來代表一個 value -- a string of text,可以在 makefile 的其他地方用 name 引用 variable,代換成背後的 value。
- Variable 跟 function 一樣,在讀入 makefile 時就會展開,除非在 recipe 裡、variable definition (
=
) 的右側、define
的 body?? - Variable name 可以由
:#=
或 whitespace 以外的字元組成 (區分大小寫),但建議只用英數字及底線就好。另外以.
及大寫字母開頭的變數可能會跟 make 自己 special varialbes 衝突,應該避免使用。 - 建議內部用的變數只用小寫字母,將大寫字母保留給 (外部的) parameters 使用;這可能跟環境變數習慣用大寫有關? 但那常數怎麼辦? 或許是指 function 裡的變數吧??
- 6.2 The Two Flavors of Variables - GNU make 說明
=
與?=
的差別,後者是 conditional variable assignment,未定義時才會 assign #ril - 10.5.3 Automatic Variables - GNU make #ril
- 6.8 Defining Multi-Line Variables - GNU make #ril
- 另一種變數給值的方式是透過
define
directive,由於允許 value 裡出現 newline (但endef
前的 newline 不會被視為 value 的一部份),可以用來定義 canned recipes。 define
後面除了變數名稱,只能有 assignment operator (預設是=
),其餘都要寫在下一行,並以單獨一行的endef
結束,例如define variable ... endef
。
- 另一種變數給值的方式是透過
當 Makefile
會動態 export 一些 environment variables 給 subprocess 時,想遶過 Makefile
直接在 shell 裡操作會不太方便。這個問題可以在 Makefile
裡安排個 env
target,印出一堆 export VARIABLE=VALUE
,這樣透過 eval $(make env)
就可以把 shell 營造成 Makefile
呼叫 subprocess 時的樣子。例如:
$ cat Makefile
...
env:
@echo export DEBUG=$(DEBUG)
@echo export COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME)
...
$ eval $(make env)
$ docker-compose ...
這種做法類似於 docker-machine env
,例如:
$ eval $(docker-machien evn managed-host)
$ docker ... # 接下來 docker 指令會作用在 managed-host 上
參考資料:
-
5.7.2 Communicating Variables to a Sub-make - GNU make
-
Variable values of the top-level
make
can be passed to the sub-make
through the environment by EXPLICIT REQUEST. These variables are defined in the sub-make
as defaults, but they do not override variables defined in the makefile used by the sub-make
unless you use the ‘-e
’ switch (see Summary of Options).所謂 explicit request 指的是使用
export
directive;雖然標題是 "to a sub-make",但廣義地來說是 "to a sub-process",也就是每一行 recipe。 -
To PASS DOWN, or EXPORT, a variable,
make
ADDS THE VARIABLE AND ITS VALUE TO THE ENVIRONMENT for running each line of the recipe. The sub-make
, in turn, uses the environment to initialize its table of variable values. See Variables from the Environment.注意這裡 variable 與 environment 的差別,往 subprocess 傳的方法就是透過 environment variables。
-
Except by explicit request,
make
exports a variable only if it is either DEFINED IN THE ENVIRONMENT INITIALLY or set on the command line, and if its name consists only of letters, numbers, and underscores. Some shells cannot cope with environment variable names consisting of characters other than letters, numbers, and underscores.來自 invoking environemnt 的 environment variables 本來就會往下傳。
-
The value of the
make
variableSHELL
is not exported. Instead, the value of theSHELL
variable from the INVOKING ENVIRONMENT is passed to the sub-make
. You can forcemake
to export its value forSHELL
by using theexport
directive, described below. See Choosing the Shell.注意 "
make
variable" 的說法,透過export make
才會進到 environment。 -
The special variable
MAKEFLAGS
is always exported (unless you UNEXPORT it).MAKEFILES
is exported if you set it to anything.可以由 top-level
make
一開始就決定了。make
automatically passes down variable values that were defined on the command line, by putting them in theMAKEFLAGS
variable. See Options/Recursion. -
Variables are not normally passed down if they were created by default by
make
(see Variables Used by Implicit Rules). The sub-make
will define these for itself. -
If you want to export SPECIFIC variables to a sub-
make
, use theexport
directive, like this:export variable …
會有 specific 的說法,是因為
export
不指定 variable 時會將所有 variable 都 export。 -
If you want to prevent a variable from being exported, use the
unexport
directive, like this:unexport variable …
In both of these forms, the arguments to
export
andunexport
are expanded, and so could be variables or functions which expand to a (list of) variable names to be (un)exported. -
As a convenience, you can define a variable and export it at the same time by doing:
export variable = value
has the same result as:
variable = value export variable
You may notice that the
export
andunexport
directives work inmake
in the same way they work in the shell,sh
.不過在 shell 裡
=
兩側不可以有空白。 -
If you want all variables to be exported by default, you can use
export
by itself:export
This tells
make
that variables which are NOT EXPLICITLY MENTIONED in anexport
orunexport
directive should be exported. Any variable given in anunexport
directive will still not be exported. If you useexport
by itself to export variables by default, variables whose names contain characters other than alphanumerics and underscores will not be exported unless specifically mentioned in anexport
directive. -
The behavior elicited by an
export
directive by itself was the default in older versions of GNUmake
. If your makefiles depend on this behavior and you want to be compatible with old versions ofmake
, you can write a rule for the special target.EXPORT_ALL_VARIABLES
instead of using theexport
directive. This will be ignored by old makes, while theexport
directive will cause a syntax error.要相容於舊版的 make 才會用
.EXPORT_ALL_VARIABLES
? 如果只想針對特定 target 才 export 要怎麼做??Likewise, you can use
unexport
by itself to tell make not to export variables by default. Since this is THE DEFAULT BEHAVIOR, you would only need to do this ifexport
had been used by itself earlier (in an included makefile, perhaps). You cannot useexport
andunexport
by themselves to have variables exported for some recipes and not for others. The lastexport
orunexport
directive that appears by itself determines the behavior for the ENTIRE RUN ofmake
.看來要 export 哪些 varible 並不是在
export
該行決定的,export
/unexport
只是調整模式,在呼叫 subprocess 的當下,才會決定哪些 variable 要以 environment variables 的型式傳進去。 -
As a special feature, the variable
MAKELEVEL
is changed when it is passed down from level to level. This variable’s value is a string which is the depth of the level as a decimal number. The value is ‘0
’ for the top-levelmake
; ‘1
’ for a sub-make
, ‘2
’ for a sub-sub-make
, and so on. The incrementation happens whenmake
sets up the environment for a recipe.The main use of
MAKELEVEL
is to test it in a conditional directive (see Conditional Parts of Makefiles); this way you can write a makefile that behaves one way if RUN RECURSIVELY and another way if run directly by you.實務上有什麼應用 ??
-
You can use the variable
MAKEFILES
to cause all sub-make
commands to USE ADDITIONAL MAKEFILES. The value ofMAKEFILES
is a whitespace-separated list of file names. This variable, if defined in the outer-level makefile, is passed down through the environment; then it serves as a list of extra makefiles for the sub-make
to read before the usual or specified ones. See The VariableMAKEFILES
.在
Makefile
外要再 include 哪些檔案 ??
-
- 6.11 Target-specific Variable Values - GNU make
- Variable 通常都是 global 的 -- 一旦過了 evaluation 就不會變 (除 automatic variables),但 target-specific variable value 可以依 target 改變 variable 的值 -- 只在 target 內有效用。
- 用法
target ...: variable-assignment
(可以作用在多個 target?),其中 variable assignment 可以是=
/:=
/::=
/+=
/?=
各種型式,前面也可以加export
/override
/private
等 keyword。由於 target-specific variable 的 priority 跟其他 variable 一樣低於 command line 給的值,除非有加override
keyword。 - Target-specific variable 會作用在 prerequisites (及其 prerequisites),例如
prog: CFLAGS = -g
與prog : prog.o foo.o bar.o
,CFLAGS = -g
也會作用在prog.o
/foo.o
/bar.o
等 target,除非那些 target 又用自己的 target-specific variable (assignment) 覆寫。
- 若單純要 export 某個變數呢?
target: export MYVAR
行不通,但target: export MYVAR ?=
用 variable-assignment 的語法就可以。
- 5.1 Recipe Syntax - GNU make 最後提到 conditional expression (
ifdef
、ifeq
等),在 rule context 裡若是以 tab 做為開頭的話,會被視為 recipe 交給 shell;暗示著,rule 裡是可以用ifdef
的。 - 7 Conditional Parts of Makefiles - GNU make #ril
- Conditional directive 可以根據 variable 的值 (跟常數或另一個 variable 比較),決定要採納/忽略 makefile 的某個部份。
- 看到 "a conditional" 才知道 conditional 也可以當名詞用,意思是 "條件句",相對於 conditional,外面的稱做 unconditional。
- ... conditionals work at the TEXTUAL LEVEL: the lines of the conditional are treated as part of the makefile, or ignored, according to the condition. This is why the larger syntactic units of the makefile, such as rules, may cross the beginning or the end of the conditional. 呼應了一開始 Conditionals control what make actually “SEES” in the makefile, so they cannot be used to control recipes at the time of execution. 的說法,也難怪在 Index of Concept 裡出現 "ifdef, expansion" 之類的說法。
- 不能動態改變 recipe,但可以控制 rule context 下有哪些 recipes。下面的例子會判斷
CC
的值是否為gcc
,以決定 C compiler 的參數。
libs_for_gcc = -lgnu
normal_libs =
foo: $(objects)
ifeq ($(CC),gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else
$(CC) -o foo $(objects) $(normal_libs)
endif
- 一個 conditional 以 `ifeq` 做為開始、中間有 `else` (選用)、最後以 `endif` 結束。其中 `ifeq` directive 會比較放在括號裡的兩個參數 `(arg1,arg2)`,若兩者相等,就會採用 `ifeq` 跟 `else` 中間的部份,否則就採用 `else` 與 `endif` 中間的部份。
- 完整的語法如下:其中 `else` 跟 `else conditional-directive` 可以有多個,但並非必要。共有 4 種 conditional directive -- `ifeq`/`ifneq`、`ifdef`/`ifndef`
conditional-directive-one
text-if-one-is-true
else conditional-directive-two
text-if-two-is-true
else
text-if-one-and-two-are-false
endif
-
3.6 Overriding Part of Another Makefile - GNU make 用
%:
來表示 any target,內部的 recipe 再用$(MAKE) -f Makefile $@
轉向另一個 makefile;其中$@
是一種 automatic variable,代表 The file name of the target。 -
5.7 Recursive Use of make - GNU make #ril
-
所謂 "recursive use of
make
" 是指在 makefile 調用make
指令,可以應用在 subsystem 有各自的 makefile 時。出現 sub-make 與 top-level make 的說法。subsystem: cd subdir && $(MAKE)
或
subsystem: $(MAKE) -C subdir
在 Make 3.81 上發現,
-C subdir
確實會讀取subdir/Makefile
,但 CWD 似乎沒有變動?? -
5.7.1 How the MAKE Variable Works 開頭強調 recursive make 不應該明確調用
make
而要透過$(MAKE)
。$(MAKE)
記錄著 top-level make 的位置 (例如/bin/make
),這樣做可以確保 recursive make invocation 都用同一個make
binary。
-
-
How to manually call another target from a make target? - Stack Overflow mklement0: 呼叫另一個 target 用
$(MAKE) -f $(THIS_FILE) other-target
,其中$(MAKE)
確保同一個make
binary 被呼叫 #ril -
Makefile: use multiple makefiles - Stack Overflow 出現
-f somename.mk
的手法 #ril -
MK - List of filename extensions (M–R) - Wikipedia 跟 Makefile 相關的副檔名有
.mk
、.mke
、.mkg
、.mak
。
- 踩了幾次變數打錯名稱 (例如
$(do_something_importan)
,但 runtime 也不會提醒 ($(call do_something_importan)
也一樣)。建議編寫Makefile
時,搭配--warn-undefined-variables
檢查,就會提醒Makefile:NN: warning: undefined variable 'do_something_importan'
,透過MAKEFLAGS=--warn-undefined-variables
直接寫在Makefile
裡當然更方便,避免未來犯蠢。
參考資料:
- Functions for Transforming Text - GNU make 只講 function 怎麼用,沒有提到如何自訂 #ril
- GNU make: Canned Recipes 用 canned sequence (of commands) 來定義一個 variable,搭配
$(variable)
將它展開;裡面可以用$^
與$@
分別取得 target 與 dependencies #ril - 8.7 The call Function - GNU make: Call Function TOC 裡出現 "expand a user-defined function" 的說法 #ril
- 雖說是 parameterized function,但實際上是把內含 complex expression 的 varialbe 展開 (use call to expand it);難怪用
define varialbe ... endef
宣告,也難怪 Call Function 會被歸在 Functions for Transforming Text 底下 $(call variable, param, param, ...)
會將param
依序指定給 temporary variable$(1)
、$(2)
等,而$(0)
則對應到variable
;雖然不一定要傳 param,但用$(call)
不傳 param 沒有意義 -- 用 canned recipe 直接展開變數就好。reverse = $(2) $(1)
搭配foo = $(call reverse, a, b)
可以調換位置,所以foo
的內容會是b, a
- 最後提到 It’s generally safest to remove all extraneous whitespace when providing parameters to call 發現用
,
拆分 params 且會保留空白,所以就別管$(call)
好不好看了,把空白拿掉
- 雖說是 parameterized function,但實際上是把內含 complex expression 的 varialbe 展開 (use call to expand it);難怪用
- Define your own function in a Makefile (Example) 用
define ... endef
宣告 custom function,搭配$call(function, )
#ril - 實驗發現 (v3.81) 把 variable 宣告在 targets 下面也是可以的,這樣
Makefile
的可讀性會較高,尤其make
預設會執行第一個 target。 - 8.1 Function Call Syntax - GNU make 不用 `$(call ...)? #ril
- makefile - Can make warn me, when I use unset variables? - Stack Overflow sirgeorge: Make 3.81 增加了
--warn-undefined-variables
,另外也可以宣告在 Makefile 裡的MAKEFLAGS=...
,避免未來犯蠢。
- 將
command
改成VAR=$(VAR) command
可以 - Variables from the Environment - GNU make 環境變數一開始都會成為 make 的變數 (同名),但如果 makefile 裡有明確指定的話就會被覆寫 #ril
- shell - How to set environment variable in Makefile - Stack Overflow 用 export? #ril
- 5.7.2 Communicating Variables to a Sub-make - GNU make 提到
export variable = value
的用法,雖然在講 sub-make,但適用所有 external command 的呼叫 #ril
- 檔名叫
Makefile.settings
好像不錯,暗示被引入的檔案其語法同Makefile
;範例叫Makefile.settings.sample
,可以準備多組 settings,再用 symbolic link 指向,例如ln -s Makefile.settings.dev Makefile.settings
。 -include Makefile.settings
用法,同時適用開發及 CI;開發時的設定來自Makefile.settings
,跑 CI 時的設定來自於環境變數,就 GitLab CI 而言,環境變數可以來自.gitlab-ci.yaml
,也可以是 CI/CD Settings > Secret variables。
參考資料:
- Including Other Makefiles - GNU make
include [FILENAME]...
可以將其他 makefile 插入現在的位置,可以用 shell file name pattern,裡面的 variable/function reference 也會展開,後面可以加 comment。- 通常用於多個 makefile 要共用 variable definitions 時。
- 若 current directory 找不到檔案,會先從
-I
/--include-dir
指定的目錄找,然後再找prefix/include
(通常是/usr/local/include
)、/usr/gnu/include
、/usr/local/include
、/usr/include
- 若要引入的檔案可有可無,可以改用
-include
。
-
How do I check if file exists in Makefile so I can delete it? - Stack Overflow #ril
-
kenorb: Then you can use a
test
command to test if the file does exist, e.g.:test -f myApp && echo File does exist
-f file
True if file exists and is a regular file.-s file
True if file exists and has a size greater than zero.
or does not:
test -f myApp || echo File does not exist
test ! -f myApp && echo File does not exist
The
test
is equivalent to[
command.[ -f myApp ] && rm myApp # remove myApp if it exists
and it would work as in your original example. See:
help [
orhelp test
for further syntax. -
holms: It's strange to see so many people using shell scripting for this. I was looking for a way to use NATIVE MAKEFILE SYNTAX, because I'm writing this outside of any target. You can use the
wildcard
function to check if file exists:ifeq ($(UNAME),Darwin) SHELL := /opt/local/bin/bash OS_X := true else ifneq (,$(wildcard /etc/redhat-release)) OS_RHEL := true else OS_DEB := true SHELL := /bin/bash endif
Update:
I found a way which is really working for me:
ifneq ("$(wildcard $(PATH_TO_FILE))","") FILE_EXISTS = 1 else FILE_EXISTS = 0 endif
不用 shell scripting 的話,才不會踩到跨平台的問題;不過
test
/[
可以做更細部的判斷。
-
-
Makefile - Check if a file exists using wildcard function - Humbug #ril
-
Self-Documented Makefile (2016-02-29) #ril
-
在 target 後面用
##
寫說明,還能避開 internal target。增加一個help
target 並將它設為 default,就可以用make [help]
看說明.PHONY: help .DEFAULT_GOAL := help help: @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
-
-
Running the build - Getting Started — Sphinx 3.0.0+/f7bf66012 documentation
-
Execute make without an argument to SEE WHICH TARGETS ARE AVAILABLE.
-
sphinx-quickstart
產生的Makefile
像這樣:# You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
將
help
安排成第一個 target 的想法很不錯,跟make help
的效果一樣。
-
-
Ned Batchelder: Makefile help target (2018-04-04) #ril
-
Makefile help target - Andrey Zhukov's Tech Blog (2017-06-13) 還加了 usage #ril
-
Add a help target to a Makefile that will allow all targets to be self documenting #ril
-
Add a help target to a Makefile that will allow all targets to be self documenting #ril
$ cat Makefile
hello:
WHO=World
echo 'Hello, $(WHO)!'
$ make
WHO=World
echo 'Hello, !'
Hello, !
顯然 WHO=World
沒有作用。改寫成 $(eval WHO = World)
就可以,這時候已經是 Make 的變數,而非 shell 的變數。
$ cat Makefile
hello:
$(eval WHO=World)
echo 'Hello, $(WHO)!'
$ make
echo 'Hello, World!'
Hello, World!
參考資料:
- makefile - Can't assign variable inside recipe - Stack Overflow 用 $(eval ...)
function,例如
$(eval who = World)`。 - Variables in GNU Make recipes, is that possible? - Super User grawity: 每一個 recipe line 起一個 shell process 執行,所以 shell 結束就消失了;另外提到
.ONESHELL:
可以要求make
用一個 shell 來執行所有 recipe。 - 5.3 Recipe Execution - GNU make 每一行 recipe 都會用 sub-shell 來執行,除非有
.ONESHELL
這個特殊的 target 存在;特別舉例設定 shell variable 或cd
切換目錄,下一個 recipe 都不受影響。 - 8.9 The eval Function - GNU make
eval
的結果固定是空字串 #ril
-
8.13 The shell Function - GNU make
The commands run by calls to the
shell
function are run WHEN THE FUNCTION CALLS ARE EXPANDED (see How make Reads a Makefile).之前踩到
$(shell)
的執行時機跟預期不符的問題:define exit exit $(shell cat $(exit_file)) endef test: _test-run $(call exit)
假設
_test-run
會更新exit_file
的內容,原以為$(shell cat $(exit_file))
會發生在_test-run
之後,但似乎在那之前就發生了,所以無法反應最新的內容。可能跟 Function Call Syntax - GNU make 的一段話有關:
A function call resembles a variable reference. It can appear anywhere a variable reference can appear, and it is expanded using the SAME RULES AS VARIABLE REFERENCES.
以及 Using Variables in Recipes - GNU make:
The other way in which
make
processes recipes is by expanding any variable references in them (see Basics of Variable References). This occurs AFTERmake
HAS FINISHED READING ALL THE MAKEFILES AND THE TARGET IS DETERMINED TO BE OUT OF DATE; so, the recipes for targets which are not rebuilt are never expanded.似乎是說 target 在執行前,裡面的 variable reference (包含 function call) 就已經展開。
-
3.7 How make Reads a Makefile - GNU make
-
GNU
make
does its work in TWO DISTINCT PHASES. During the first phase it reads all the makefiles, included makefiles, etc. and INTERNALIZES ?? all the variables and their values, implicit and explicit rules, and constructs a DEPENDENCY GRAPH of all the targets and their prerequisites. During the second phase, make uses these internal structures to determine what targets will need to be REBUILT and to invoke the rules necessary to do so. -
It’s important to understand this two-phase approach because it has a direct impact on HOW VARIABLE AND FUNCTION EXPANSION HAPPENS; this is often a source of some CONFUSION when writing makefiles. Here we will present a summary of the phases in which expansion happens for different constructs within the makefile.
We say that expansion is IMMEDIATE if it happens during the FIRST PHASE: in this case
make
will EXPAND any variables or functions in that section of a construct as the makefile is PARSED. We say that expansion is DEFERRED if expansion is not performed immediately. Expansion of a deferred construct is not performed until either the construct appears later in an IMMEDIATE CONTEXT ??, or until the second phase.You may not be familiar with some of these constructs yet. You can reference this section as you become familiar with them, in later chapters.
Variable Assignment
-
Variable definitions are parsed as follows:
immediate = deferred immediate ?= deferred immediate := immediate immediate ::= immediate immediate += deferred or immediate immediate != immediate define immediate deferred endef define immediate = deferred endef define immediate ?= deferred endef define immediate := immediate endef define immediate ::= immediate endef define immediate += deferred or immediate endef define immediate != immediate endef
-
For the append operator,
+=
, the right-hand side is considered immediate if the variable was previously set as a simple variable (:=
or::=
), and deferred otherwise. -
For the SHELL ASSIGNMENT operator,
!=
, the right-hand side is evaluated immediately and HANDED TO THE SHELL. The result is stored in the variable named on the left, and that variable becomes a SIMPLE VARIABLE (and will thus be RE-EVALUATED ON EACH REFERENCE ??).為什麼 simple variable 反而每次存取時都要 re-evaluate ??
Conditional Directives
- Conditional directives are parsed immediately. This means, for example, that automatic variables cannot be used in conditional directives, as automatic variables are not set until the recipe for that rule is invoked. If you need to use automatic variables in a conditional directive you must move the condition into the recipe and use SHELL CONDITIONAL SYNTAX instead. ??
Rule Definition
-
A rule is always expanded the same way, regardless of the form:
immediate : immediate ; deferred deferred
That is, the target and prerequisite sections are expanded immediately, and the recipe used to construct the target is always deferred. This general rule is true for explicit rules, pattern rules, suffix rules, static pattern rules, and simple prerequisite definitions.
-
社群:
文件:
手冊: