初識MakefIle 在學習Linux過程中,我越發的覺得Linux系統給了使用者更大的自由,同時也就增加了學習的成本。在gcc下去調試代碼,沒有了熟悉的VS,沒有的人性話的錯誤提示(當然Makefile是有錯誤提示和警告的),也沒有一鍵編譯。全得自己來,但是在這個過程中,你將會熟悉系統的整個編譯過 ...
在學習Linux過程中,我越發的覺得Linux系統給了使用者更大的自由,同時也就增加了學習的成本。在gcc下去調試代碼,沒有了熟悉的VS,沒有的人性話的錯誤提示(當然Makefile是有錯誤提示和警告的),也沒有一鍵編譯。全得自己來,但是在這個過程中,你將會熟悉系統的整個編譯過程,以及自己去寫編譯文件的那種快感。今天我將不去重點介紹編譯的過程,重點在GNU的Makefile怎樣寫,算是自己的一個總結。
在此我先說一下,我的Makefile是在https://blog.csdn.net/haoel/article/details/2886陳皓《跟我一起寫Makefile》學到的,裡面講的很是全面有興趣的話可以認真的去讀一遍(內容比較多)。
從認為,Makefile是為生成,編譯好的、可執行的文件(make只是一個根據指定的Shell命令進行構建的工具),可以用來調試,也可以直接運行。有人可能要說用gcc自己寫命令編譯不是更好嗎?但是如果一個項目有成百上千個.c .h文件怎麼整。
從最簡單的說起,一般情況下我們執行Makefile文件直接make就可以了(有些人會進行一些騷操作這就不說了),會生成許多 .o文件最終會有一個或多個可執行文件。
咱們還是直接從例子中去體會吧!
SRC = $(wildcard *.c)
OBJ = $(patsubst %.c,%.o,$(SRC))
target = SendMsg
mode = -g
CC := gcc
$(target):$(OBJ)
$(CC) $(CFLAGS) $^ -o $(target)
.PHONY:
clean:
rm -rf *.o $(target)
上面的代碼塊就是一個簡單的Makefile,我們一句一句的分析他的語法,等到末尾基本上簡單的Makefile也就會寫了,再去系統的學習Makefile也比較容易(請保持耐心):
整體的看最多的就是賦值符號,細心點會發現有兩種複製方式 “=”和“:=”,這兩個都是賦值符號,符號左邊是變數,右面是所要賦值的內容;
"="是最基本的賦值,如同我們c/c++語言中的賦值一樣;
":="表示覆蓋賦值,也就是同一個變數在如果用":="賦值,則會覆蓋掉之前的內容。
開始看第一句:
SRC = $(wildcard *.c)
之前說過“=”左邊是變數,則"SRC"就是變數,想來我不用解釋變數是什麼意思吧。“=”右邊是所要賦值的內容。
$(wildcard *.c)
在這內容裡面"$"是函數的調用符,也是變數的調用符。就是說你調用一個變數或一個函數時前面要加上"$"來作為標誌,表示這是個函數,或者這是個變數,並且裡面的內容需要用括弧括起來,括弧可以用"{}"也可以用"()"但是最好統一一下不要混用,顯得雜亂。(如果對指針比較熟悉的話很快就應該想到“$”跟指針中的“*”用法很類似,為了提取出對方的內容所要加的標誌符,如果對指針不熟悉可以忽略這句話)。
因此上面那句話也可以這樣寫 ${wildcard *.c};
我們分析一下 $(wildcard *.c) 括弧裡面的內容“*”是通配符的一種,這裡表示所有的以".c"結尾的文件,如果是"*.o"那就是所有的以“.o”結尾的文。“wildcard”是Makefile中的函數,Makefile中是有自己內置函數就像我們c/c++中的庫函數一樣。在這裡“wildcard”函數的作用是來獲取工作目錄下的所有的 “*.c” 文件列表。Makefile中不止這一個函數,還有很多可以自己去看去實踐。
現在整體看“SRC = $(wildcard *.c)”這句話的意思就是,取出當前目錄下所有的".c"文件並且將此賦給 SRC,那麼 SRC 將會代表此目錄下的所有 “.c”文件。
第二句
OBJ = $(patsubst %.c,%.o,$(SRC))
同樣定義“OBJ”變數,調用“patsubst”函數,函數參數為“%.c”,“%.o”,“$(SRC)”;(函數中有多個參數時,以逗號隔開);
“%”也是一中通配符:非空長度任意的非空字元串。(“%”與“*”是有區別的)
“patsubst”函數的作用是將變數“SRC”中所用“.c”文件用“.o”替代。因此OBJ就是很多將尾碼為".c"變成“.o”的文件集合;
第三、四、五句(同類型)
target = SendMsg
mode = -g
CC := gcc
定義target變數名字角SendMsg;
定義“mode”變數且賦值為“-g”;
定義“CC”變數且賦值為"gcc"
$(target):$(OBJ)
$(CC) $(CFLAGS) $^ -o $(target)
這段代碼是核心,它符合Makefile 的編譯規則如下:
target ... : prerequisites ...
command
...
...
target是目標文件(可以不止一個),prerequisites是目標的依賴文件(可以不止一個),(command)生成目標文件所需要執行的命令。
“目標文件”:即最終生成的可執行的文件;
“目標依賴文件”:即生成目標文件所需要的文件;
“命令”:即處依賴文件到目標文件的過程。註意命令需要以開頭必須是table,這是命令的標識。
現在回過頭去看我們的程式,五六句:
$(target),表示所要生成的目標文件;
$(OBJ),表示生成目標文件所需的依賴文件;
$(CC) $(mode) $^ -o $(target),處理生成目標的命令;
{命令中"$^"是自動化變數終端的一種,表示所有依賴文件的集合,如果有重覆的依賴文件,則去掉重覆的依賴文件(自動化變數:會將模式變數一次取出的一種機制變數,自動化變數有好幾個,可以去詳細解讀)};
重點:在Makefile中是以時間戳為參考標準去更新文件的,假若依賴文件中任意一個文件比目標文件的時間戳新,則再次編譯目標文件,直到目標文件的時間戳為最新為止。
則五六句代碼的理解就是:
以OBJ為依賴文件生成target文件,命令規則是“gcc -g $^ -o target”(假設懂 gcc 的基本語法)。
八、九、十句
.PHONY:
clean:
rm -rf *.o $(target)
這個小模塊的目的是為了,清除所有的已經編譯後的目標文件;在調試時改變更改程式後需要重新編譯,那麼將會生成新的編譯文件;為了比避免出錯,一般先會清除先前的編譯文件,在重新生成編譯文件。
在這個模塊中“clean”時一條偽命令,為什麼這樣說,是因為它不會再編譯的過程中執行,只有使用語句 “make clean”時才會執行此命令。其中“.PHONY”是偽命令的標識,可以選擇不寫。
最後“rm -rf *.o $(target)”,想來應該也都清楚了,清除所有以“.o”和“target”文件。
至此整個Makefile結束。此段程式雖然簡短但是五臟俱全,其中涉及很多Makefile的知識點,可以由此去逐點擊破,對Makefile做個詳細的瞭解。