erlang.mk和makefile語法剖析


1. makefile 基本規則:

1. 所有的源文件沒有被編譯過,則對各個源文件進行編譯並進行鏈接,生成最后的可執行程序;
2. 每一個在上次執行make之后修改過的源代碼文件在本次執行make時將會 被重新編譯;
3. 頭文件在上一次執行make之后被修改。則所有包含此頭文件的源文件在本次執行 make 時將會被重新編譯。

2. 基本格式:

TARGET... : PREREQUISITES... 
  COMMAND
  ...

2.1 target(目標)通常是最后需要生成的文件名或者為了實現這個目的而必需的中間過程文件名。

      也可以是一個make執行的動作的名稱,如目標“clean”:偽目標(phony target)。

2.2 PREREQUISITES(依賴條件) 生成規則目標所需要的文件名列表。通常一個目標依賴於一個或者多個文件。

2.3 COMMAND(命令行) 任意的shell命令或者是可在shell下執行的程序。

  它限定了make執行這條規則時所需要的動作。可以有多個命令行,每一條命令占一行。

注意:每一個命令行必須以[Tab]字符開始,[Tab]字符告訴 make 此行是一個命令行,make程序本身並不關心命令是如何工作的,對目標文件的更新需要你在規則描述中提供正確的命令。

“make”程序所做的 就是當目標程序需要更新時執行規則所定義的命令。

建議:單目標,多依賴。就是說盡量要做到一個規則中只存在一 個目標文件,可有多個依賴文件。盡量避免使用多目標,單依賴的方式 。

最神奇的就是這三個元素都可以使用變量,變量又可以使用通配符展開。

 

下面通過erlang.mk這個項目自己的build過程來了解一下最基本makefile的工作步驟:

復制代碼
##makefile
BUILD_CONFIG_FILE ?= $(CURDIR)/build.config
BUILD_CONFIG = $(shell sed "s/\#.*//" $(BUILD_CONFIG_FILE))

ERLANG_MK = erlang.mk
ERLANG_MK_VERSION = $(shell git describe --tags --dirty)

.PHONY: all check

all:
awk 'FNR==1 && NR!=1{print ""}1' $(patsubst %,%.mk,$(BUILD_CONFIG)) \
| sed 's/^ERLANG_MK_VERSION = .*/ERLANG_MK_VERSION = $(ERLANG_MK_VERSION)/' > $(ERLANG_MK)

ifeq ($(p),)
check:
$(MAKE) -C test
else
check:
$(MAKE) -C test pkg-$(p)
endif
復制代碼

以上是根據build.config生成給我們項目自己使用的erlang.mk文件 (注意這個並不在build自定義項目,而只是生成erlang.mk,然后用它來build我們自定義的項目),它包括了變量賦值.引用,特殊變量 偽目標,終極目標, 條件語句:

3.1 變量

1. Makefile 中變量和函數的展開(除規則命令行中的變量和函數以外),是在 make 讀取 makefile 文件時進行的,這里的變量包括了使用“=”定義和使用指示符 “define”定義的。
2. 變量可以用來代表一個文件名列表、編譯選項列表、程序運行的選項參數列表、 搜索源文件的目錄列表、編譯輸出的目錄列表和所有我們能夠想到的事物。
3. 變量名是不包括“:”、“#”、“=”、前置空白和尾空白的任何字符串。 
4. 變量名是大小寫敏感的。
5. 另外有一些變量名只包含了一個或者很少的幾個特殊的字符(符號)。稱它們為 自動化變量。像“$<”、“$@”、“$?”、“$*” 2. 變量的賦值

3.1.1 遞歸展開式變量

語法:VAR  = Value

BUILD_CONFIG = $(shell sed "s/\#.*//" $(BUILD_CONFIG_FILE))

使用此風格的變量定義,可能會由於出現變量的遞歸定義而導致make陷入到無限的變量展開過程中,最終使 make 執行失敗。如下:

CFLAGS = $(CFLAGS) –O

這種風格的變量定義中如果使用了函數,那么包含在變量值中的 函數總會在變量被引用的地方執行(變量被展開時)。

3.1.2 直接展開式變量  

語法  VAR := Value
變量值中對其他量或者函數的引用在定義變量時被展開(對變量進行替換)

x := foo 
y := $(x) bar 
x := later
# 就等價於: 
y := foo bar
 x := later

 3.1.3 ?= 操作符

 BUILD_CONFIG_FILE ?= $(CURDIR)/build.config 

只有此變量在之前沒有賦值的情況下才會對這個變量進行賦值。

正因為變量只是簡單的宏展開,且所有的makefile都是先從頭到尾先計算好更新規則后,一次性從終極目標(第一個目標)開始執行的。所以在erlang.mk中才必須core的部分寫在編譯前面(因為core中定義了大量與編譯erl相關的參數)。

3.2 變量的引用

$(BUILD_CONFIG)    
#或
${BUILD_CONFIG}

3.3 特殊的變量
$(CURDIR) 此變量代表 make 的工 作目錄。當使用“-C”選項進入一個子目錄后,此變量將被重新賦值。總之,如果在 Makefile 中沒有對此變量進行顯式的賦值操作,那么它代表 make 的工作目錄。
$(MAKE) 用於遞歸展開make時MAKE可以帶不一樣的參數,比如MAKE := /bin/make 在另一個目錄就是MAKE := /bin/make -t
3.4 偽目標 
語法 :target不是一個真正的文件名
使用.phony:all check 的原因:避免 目錄中有一個文件叫all或check,強制說明這個是偽目標。
3.5 命令行
在 Makefile 中書寫在同一行中的多個命令屬於一個完整的 shell 命令行,書寫在獨立行的一條命令是一個獨立的 shell 命令行。
如果想在同一個shell中運行多條命令,應該用 \ 連接各個命令。讓它成為一行命令。
3.6. 條件語句:

復制代碼
ifeq($(p),)
check:
$(MAKE) -C test
else
check:
$(MAKE) -C test pkg-$(p)
endif 
復制代碼

3.7 shell 命令:

#把BUILD_CONFIG_FILE里面的所有注釋去掉。
sed 's/pattern/replace_string' FileName sed "s/\#.*//" $(BUILD_CONFIG_FILE)
復制代碼
#表示在除了第一個文件croe/core.mk外,其它的文件第一行都要先加一個空行。1表示返回值為1
awk 'BEGIN{print "start"} pattern{ commands} END{print "end"} file awk 'FNR==1 && NR!=1{print ""}1' $(patsubst %,%.mk,$(BUILD_CONFIG)) #展開后: awk 'FNR==1 && NR!=1{print ""}1' core/core.mk index/*.mk core/index.mk core/deps.mk plugins/protobuffs.mk core/erlc.mk core/docs.mk core/test.mk plugins/asciidoc.mk plugins/bootstrap.mk plugins/c_src.mk plugins/ci.mk plugins/ct.mk plugins/dialyzer.mk plugins/edoc.mk plugins/elvis.mk plugins/erlydtl.mk plugins/escript.mk plugins/eunit.mk plugins/relx.mk plugins/shell.mk plugins/triq.mk plugins/xref.mk plugins/cover.mk
復制代碼

awk特殊變量:
 NR:表示記錄數量,在執行過程中對應於當前行號
 FNR:表示單個文件的當前行號,在多個文件中不會累加,NR會累加。
3.8 make 內置函數

patsubst %, %.mk,$(BUILD_CONFIG))

把BUILD_CONFIG里面的第行都加上.mk結尾。patsubst是makefile的一個內部函數。

3.9 終極目標

默認的情況下,make執行的是Makefile中的第一個規則,此規則的第一個目標稱 之為“最終目的”或者“終極目標”(就是一個Makefile最終需要更新或者創建的目標)。

 

摘抄博客:http://www.cnblogs.com/zhongwencool/p/erlang_mk.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM