在項目開放中使用makefile編譯項目,在學習使用過程中出現了一些問題,這裏做一些記錄備份,已提供後面回顧
1、makefile的函數理解
函數定義方法
define func_name
some_operation
endef
實際上makefile本質上是一種多行變量:
如:
define func_name
var := $1
endef
value :=$(call func_name,param1,……)
value的最後結果是
value = var:=param1
爲了驗證一下,編寫簡單的makefile測試一下:
然後make一下,結果如下:
可以看出value的值實際上就是test的展開式,那麼我們在test函數中添加多行操作看看會有什麼變化,修改後如下:
添加var1 := 123,然後執行make,會有報錯:
這是爲什麼呢,其實只要將test展開就知道了:
value = var := 123
var1:= 123
到target執行時就是這樣的:
target:
echo var:=123
var1:= 123
因爲在var1之前有tab空格所以var1被當做了make的指令執行,單實際上make沒有這個執行,所以報錯爲var1 命令沒有找到,由此可以看出定義函數是這一點要尤爲重視,否者函數的引用很容易出現問題。
2. eval函數的理解
make GNU對該函數做了細緻的描述:
簡單翻譯一下:
函數「eval」是一個比較特殊的函數。使用它可以在Makefile中構造一個可變的規則結構關係(依賴關係鏈),其中可以使用其它變量和函數。函數「eval」對它的參數進行展開,展開的結果作爲Makefile的一部分,make可以對展開內容進行語法解析。展開的結果可以包含一個新變量、目標、隱含規則或者是明確規則等。也就是說此函數的功能主要是:根據其參數的關係、結構,對它們進行替換展開。
函數「eval」的返回值時空。
尤爲重要的是 「eval」函數執行時會對它的參數進行兩次展開。:「eval」函數執行時會對它的參數進行兩次展開。第一次展開過程發是由函數本身完成的,第二次是函數展開後的結果被作爲Makefile內容時由make解析時展開的。明確這一過程對於使用「eval」函數非常重要。理解了函數「eval」二次展開的過程後。實際使用時,如果在函數的展開結果中存在引用(格式爲:$(x)),那麼在函數的參數中應該使用「$$」來代替「$」。因爲這一點,所以通常它的參數中會使用函數「value」來取一個變量的文本值
簡單點來說就是$(eval text)會將text展開當做makefile的一部分,被make執行,所以text會被展開二次,第一次是eval展開,第二次則是作爲被make執行進行的展開。還有一點需要特別注意eval的返回值是空,所以不能出現諸如:value := $(eval text)這類使用,value只能爲空
說了這麼多還是用實例測試一下:
定義一個函數,目的是獲取本地目錄,如上,執行make:
可以看出value的值,但是$(local_dir)這個變量的值時空,爲什麼會這樣,實際上我們將函數展開就理解了:
value := local_dir := /home/jimmy/workspace/my_study/test
target:
@echolocal_dir:
看出來沒有,makefile中沒有定義local_dir這個變量。但是這個和eval有什麼關係,不要緊我們來修改一下:
然後make一下:
可以看出value的值時空,因爲eval的返回值是空,而local_dir這個變量這時候被定義了,而且還被賦值了。到這讀者是否看出來了eval的作用。
實際上就是eval展開了eval_test這個函數,並將其作爲makefile的一部分送給make執行了一下,這樣local_dir就被定義了;這個和call函數有明顯的區別,call僅僅是將函數按照多行變量展開了,並不會送給make去執行;
猜測:
不過在上述例子中有個奇怪的點就是$(shell pwd)按照手冊需要添加$$但是沒有添加其結果好像也沒有影響,那麼修改一下如何:
再次make:
還是沒有影響,這是爲什麼,我猜猜是和$(shell pwd)的展開時間有關,在$(call eval_test)時就已經執行,這時候函數展開爲local_dir := $/home/jimmy/workspace/mystudy/test,然後eval將local_dir :=$/home/jimmy/workspace/mystudy/test送給make作爲makefie一部分執行。那爲什麼在輸出是$這個符號不見了,測試一下。
執行make:
可以看出Value2的輸出爲空,那麼是否單個$會被忽略呢?
那麼我們再添加一個試試呢:
輸出錯誤:
這個真是很奇怪原因是什麼呢!我們可以來展開一下。
第一次是call函數展開:
local_dir := $$$/home/jimmy/workspace/mystudy/test
然後eval展開:
local_dir :=$$ /home/jimmy/workspace/mystudy/test
然後執行
local_dir :=$$/home/jimmy/WorkSpace/my_study/test
在echo時:把$$/home/jimmy/WorkSpace/my_study/test作爲一個shell變量,然後會發現shell中不存在該變量,然後報錯??
以上還有很多疑問,後續學習補充
3)makefile的一些注意點
Makefile中所有以$打頭的單詞都會被解釋成Makefile中的變量。如果你需要調用shell中的變量(或者正則表達式中錨定句位$),都需要加兩個$符號($$)。實例如下:
PATH="/home/jimmy"
all:
echo ${PATH}
echo $$PATH
例子中的第一個${PATH}引用的是Makefile中的變量,而不是shell中的PATH環境變量,後者引用的事Shell中的PATH環境變量。
在目標文件冒號後在起一行,並且有tab空格則作爲shell執行
target:
value= $(value2) shell執行
target:value = $(value2) 作爲makefile 的代碼
target:
value = $(value2) 作爲makefile 的代碼
Makefile中的shell,每一行是一個進程,不同行之間變量值不能傳遞。所以,Makefile中的shell不管多長也要寫在一行。