超詳細的Makefile語法#實例

關於Makefile腳本的編寫,在Linux開發項目中使用的比較多,一個好的Makefile能讓你編譯源碼事半功倍,高效率讓整個項目編譯起來。
這裏主要針對平時工作中常用的語法做了下總結,在講解Makefile語法的編寫前,先囉嗦下程序編譯的幾個步驟,可能在window的IDE環境,可能編譯的幾個步驟很少注意到,但在linux系統環境下編譯,你可以很明顯的觀察到程序編譯包括了預編譯,彙編,編譯,鏈接幾個步驟,我們常說的程序編譯其實僅僅只是幾個步驟中的一個,好,言歸正傳,GNU的make有許多的內容,閒言少敘,還是讓我們開始走進Makefile的世界裏。

Makefile的規則

target:prerequisites
	command
  1. target:表示一個目標文件,可以是Object File,也可以是執行文件,還可以是一個標籤(Label),後面對對於這種用法進行說明
  2. prerequisites:生成target目標所需要的文件或是目標或者可以理解是生成target的依賴文件
  3. command: make需要執行的命令可以是任意的Shell命令,但是需要注意command一定要以TAB鍵開頭,否則make不認識

幾乎所有的Makefile編寫都少不了在這個規則下添磚加瓦,接下來就開啓認識Makefile的大門

在Makefile中使用變量

變量聲明和使用

Makefile在聲明變量前需要給予初值,在使用變量時在變量名前加上“$”符號,但最好用小括號“()”或是大括號“{}”把變量給包括起來
舉個栗子:

objects = obj_a.o obj_b.o obj_c.o

program : $(objects)
	cc -o program $(objects)

自動化變量$@ $< $<

經常會使用到 $@, $<, $^三個變量,這是Makefile非常重要的三個變量
$@ 表示目標文件, $< 表示第一個依賴文件, $^表示所有的依賴文件

變量賦值常用符號

= 是最基本的賦值(與位置無關,整個makefile展開後再賦值)
:= 是覆蓋之前的值(與位置有關,變量在makefile裏隨時賦值)
?= 是如果沒有被賦值過就賦予等號後面的值
+= 是添加等號後面的值

?=和+=都比較容易理解,就不舉栗子了,這裏對=和:=舉個栗子:

"="表示make會將整個makefile展開後,再決定變量的值
 x = foo
 y = $(x) bar
 x = xyz
 在上例中,y的值將會是 xyz bar,而不是 foo bar 

":="表示變量的值決定於它在makefile中的位置,而不是整個makefile展開後的最終值
x := foo
y := $(x) bar
x := xyz
在上例中,y的值將會是 foo bar,而不是 xyz bar了

Makefile環境變量

make 運行時的系統環境變量可以在make開始運行時被載入到Makefile文件中,但是如果Makefile中已定義了這個變量,或是這個變量由make命令行帶入,那麼系統的環境變量的值將被覆蓋,如果我們在環境變量中設置了“CFLAGS”環境變量,那麼我們就可以在所有的Makefile中使用這個變量了,上層Makefile中定義的變量會以系統環境變量的方式傳遞到下層的Makefile中。當然,默認情況下,只有通過命令行設置的變量會被傳遞。而定義在文件中的變量,如果要向下層 Makefile傳遞,則需要使用exprot關鍵字來聲明

在Makefile使用函數

函數使用語法

$(<function> <arguments> )
或者
${<function> <arguments>}
很像變量的使用,也是用$來標識的
<function>就是函數名
<arguments>是函數的參數,參數間以逗號“,”分隔,而函數名和參數之間以“空格”分隔

字符串常用處理函數

函數 意義 返回
$(subst from, to, text ) 把字符串text中的from字符串替換成to 函數返回被替換過後的字符串

舉個栗子:

$(subst .c, .o, hello.c)
把hello.c的.c替換爲.o輸出爲hello.o
函數 意義 返回
$(patsubst pattern, replacement, text ) 查找text中的單詞(單詞以“空格”、“Tab”或“回車”“換行”分隔)是否符合模式pattern,如果匹配的話,則以replacement替換 函數返回被替換過後的字符串

舉個栗子:

$(patsubst %.c, %.o, a.c.c b.c c.c)
把字串"a.c.c b.c c.c"符合模式[%.c]的單詞替換成[%.o],返回結果是"a.c.o b.o c.o"
函數 意義 返回
$(strip string) 去掉string字串中開頭和結尾的空字符 返回被去掉空格的字符串值

舉個栗子:

$(strip "a b c ")
把字符串"a b c "去到開頭和結尾的空格,結果是"a b c"

文件名常用操作函數

函數 意義 返回
$(dir <names…> ) 從文件名序列names中取出目錄部分 返回文件名序列names的目錄部分

舉個栗子:

$(dir src/foo.c hacks)
返回值是"src/ ./"
函數 意義
$(notdir <names…> ) 從文件名序列names中取出非目錄部分,非目錄部分是指最後一個反斜槓("/")之後的部分 返回文件名序列names的非目錄部分

舉個栗子:

$(notdir src/foo.c hacks)
返回值是"foo.c hacks"

常用遍歷操作函數

函數 意義 返回
$(foreach var, list, text ) 把參數list中的單詞逐一取出放到參數var所指定的變量中,然後再執行text所包含的表達式 返回的每個字符串所組成的整個字符串(以空格分隔)

舉個栗子:

names := a b c d
files := $(foreach n, $(names), $(n).o)
$(name)中的單詞會被挨個取出,並存到變量"n"中,"$(n).o"每次根據"$(n)"計算出一個值,
這些值以空格分隔,最後作爲foreach函數的返回,
所以$(files)的值是"a.o b.o c.o d.o"
函數 意義 返回
$(wildcard pattern) 展開爲已經存在的、使用空格分開的、匹配此模式的所有文件列表 返回匹配的以空格分開的文件列表

舉個栗子:

foo.c bar.c foo bar 
$(wildcard ./*.c)
展開後返回的字符串爲"foo.c bar.c"

這個函數常常和patsubst配合使用
$(patsubst %.c, %.o, $(wildcard *.c))
首先使用“wildcard”函數獲取工作目錄下的.c文件列表,
之後將列表中所有文件名的後綴.c替換爲.o,
這樣我們就可以得到在當前目錄可生成的.o文件列表

Makefile執行shell函數

shell 函數也不像其它的函數,它的參數應該就是操作系統Shell的命令
舉個栗子:

./a.c ./b.c
files := $(shell echo *.c)
這樣就把當前路徑的"a.c b.c"賦值給files了

Makefile的僞目標

“僞目標”並不是一個文件,只是一個標籤,由於“僞目標”不是文件,所以make無法生成它的依賴關係和決定它是否要執行。我們只有通過顯示地指明這個“目標”才能讓其生效。
爲了避免和文件重名的這種情況,我們可以使用一個特殊的標記“.PHONY”來顯示地指明一個目標是“僞目標”

all 這個僞目標是所有目標的目標,其功能一般是編譯所有的目標
clean 這個僞目標功能是刪除所有被make創建的文件
install 這個僞目標功能是安裝已編譯好的程序,就是把目標執行文件拷貝到指定的目標中

舉個栗子:

all:prog1 prog2 prog3 prog4

clean:
	rm -f ./*.o
install:
	install -m 755 bin BIN_DIR
	 
.PHONY: all clean install

頭文件路徑及系統邏輯目錄

-Idir_include
大寫的I緊跟着頭文件路徑
–sysroot
如果在編譯時指定了-sysroot就是爲編譯時指定了邏輯目錄。編譯過程中需要引用的庫,頭文件,如果要到/usr/include目錄下去找的情況下,則會在前面加上邏輯目錄

使用動態庫編譯

動態庫鏈接原理和使用

  1. Linux是通過 /etc/ld.so.cache 文件搜尋要鏈接的動態庫的。而 /etc/ld.so.cache 是 ldconfig 程序讀取 /etc/ld.so.conf 文件生成的,注意 /etc/ld.so.conf 中並不必包含 /lib 和 /usr/lib,ldconfig程序會自動搜索這兩個目錄,所以把 libtest.so 所在的路徑添加到 /etc/ld.so.conf 中,再以root權限運行 ldconfig 程序更新ld.so.cache
  2. export LD_LIBRARY_PATH=庫文件patch

舉個栗子:

編譯出動態庫

gcc test.c  -fPIC -shared -o libtest.so
-fPIC:Position Independent Code編譯出位置無關代碼
-shared:編譯動態庫選項

使用動態庫編譯程序

gcc -o hello hello.c -L. -ltest
-L 表示動態庫所在路徑注意-L和path間不能有空格
-ltest表示動態庫libtest.so

總結

本篇文章到此已經告一段落了,工作中使用Makefile常見的語法就由這些最基礎的構成,比較複雜的那就是根據實際項目構建通用性,擴展性比較強的Makefile腳本了,這裏等後續再逐漸深入講解,下面附上一個最近寫的一個組件對應的Makefile,詳細源碼部分可以參考源碼

BIN = app

SRC = $(wildcard ./*.c)
OBJ = $(subst .c,.o,$(SRC))

all:$(BIN)

$(BIN):$(OBJ)
	gcc -o $@ $^

$(OBJ):%.o:%.c
	gcc -c $< -o $@

clean:
	rm -rf ./*.o
	rm -rf ${BIN} ${OBJ}

.PHONY: clean all
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章