Makefile 學習 1 - 基於若干 Blog 的彙總

基於若干 Blog 彙總的 makefile 教程shell

陳皓 http://www.javashuo.com/article/p-sfuuzyix-mm.html編程

Makefile 基礎知識

1. 什麼是 Makefile?

規定軟件工程的編譯規則。一個工程中的源文件,其按類型功能模塊分別放在若干個目錄中,makefile 定義了一系列的規則來指定,哪些文件須要先編譯,哪些文件須要後編譯,哪些文件須要從新編譯,甚至於進行更復雜的功能操做,由於 makefile 就像一個Shell腳本同樣,其中也能夠執行操做系統的命令。makefile 帶來的好處就是——「自動化編譯」,一旦寫好,只須要一個 make 命令,整個工程徹底自動編譯,極大的提升了軟件開發的效率。windows

2. 程序的編譯和連接

程序編譯過程: 源文件 --- 編譯 compile ---> 中間代碼文件 .obj (windows) .o (unix) --- 連接 link ---> 可執行文件 .exe函數

編譯:語法的正確;函數與變量的聲明的正確。對於後者,一般是你須要告訴編譯器頭文件的所在位置(頭文件中應該只是聲明,而定義應該放在C/C++文件中),只要全部的語法正確,編譯器就能夠編譯出中間目標文件。通常來講,每一個源文件都應該對應於一箇中間目標文件(O文件或是OBJ文件)。ui

連接:主要是連接函數和全局變量,因此,咱們可使用這些中間目標文件(O文件或是OBJ文件)來連接咱們的應用程序。連接器並無論函數所在的源文件,只管函數的中間目標文件(Object File),在大多數時候,因爲源文件太多,編譯生成的中間目標文件太多,而在連接時須要明顯地指出中間目標文件名,這對於編譯很不方便,因此,咱們要給中間目標文件打個包,在Windows下這種包叫「庫文件」(Library File),也就是 .lib 文件,在UNIX下,是Archive File,也就是 .a 文件。this

總結:源文件首先會生成中間目標文件,再由中間目標文件生成執行文件。在編譯時,編譯器只檢測程序語法,和函數、變量是否被聲明。若是函數未被聲明,編譯器會給出一個警告,但能夠生成Object File。而在連接程序時,連接器會在全部的Object File中找尋函數的實現,若是找不到,那到就會報連接錯誤碼(Linker Error)操作系統

3. Makefile 規則

Makefile 文件模板 -.net

target (目標文件,object file,exe file or Label) :prerequisites (要生成 target 文件所須要的文件或者目標)…..
command (make 要執行的 shell 命令)unix

………

這是一個文件的依賴關係,也就是說,target 這一個或多個的目標文件依賴於 prerequisites 中的文件,其生成規則定義在 command 中。說白一點就是說,prerequisites 中若是有一個以上的文件比target文件要新的話,command 所定義的命令就會被執行。這就是 Makefile 的規則。也就是Makefile 中最核心的內容。code

4. Makefile example

edit: main.o kbd.o command.o display.o insert.o search.o files.o utils.o   
    cc -o edit: main.o kbd.o command.o display.o insert.o search.o files.o utils.o

explain:

main.o (target file, example is object file): main.c defs.h (prerequisites: to compile this object file, main.c and defs.h is needed)  
(command line should start with a Tab)cc -c main.c (command: shell 命令用於使用 prerequisites 用於生成 target file)
main.o : main.c defs.h  
    cc -c main.c  
kbd.o : kbd.c defs.h command.h  
    cc -c kbd.c  
……  
utils.o : utils.c defs.h  
    cc -c utils.c

clean : rm edit main.o kbd.o command.o display.o insert.o search.o files.o utils.o

咱們能夠把這個內容保存在文件爲 「Makefile」 或 「makefile」 的文件中,而後在該目錄下直接輸入命令 「make」 就能夠生成執行文件 edit。若是要刪除執行文件和全部的中間目標文件,那麼,只要簡單地執行一下 「make clean」 就能夠了。

在這個 makefile 中,目標文件(target)包含:執行文件edit和中間目標文件(.o),依賴文件(prerequisites)就是冒號後面的那些 .c 文件和 .h文件。每個 .o 文件都有一組依賴文件,而這些 .o 文件又是執行文件 edit 的依賴文件。依賴關係的實質上就是說明了目標文件是由哪些文件生成的,換言之,目標文件是哪些文件更新的。

在定義好依賴關係後,後續的那一行定義瞭如何生成目標文件的操做系統命令,必定要以一個 Tab 鍵做爲開頭。記住,make 並無論命令是怎麼工做的,他只管執行所定義的命令。make 會比較 targets 文件和 prerequisites 文件的修改日期,若是 prerequisites 文件的日期要比 targets 文件的日期要新,或者 target 不存在的話,那麼,make 就會執行後續定義的命令。

這裏要說明一點的是,clean 不是一個文件,它只不過是一個動做名字,有點像 C 語言中的 lable 同樣,其冒號後什麼也沒有,那麼,make 就不會自動去找文件的依賴性,也就不會自動執行其後所定義的命令。要執行其後的命令,就要在 make 命令後明顯得指出這個 lable 的名字。這樣的方法很是有用,咱們能夠在一個 makefile 中定義不用的編譯或是和編譯無關的命令,好比程序的打包,程序的備份,等等。

5. make 如何工做

在默認的方式下,也就是咱們只輸入make命令。那麼,

  • make會在當前目錄下找名字叫「Makefile」或「makefile」的文件。
  • 若是找到,它會找文件中的第一個目標文件(target),在上面的例子中,他會找到「edit」這個文件,並把這個文件做爲最終的目標文件。
  • 若是edit文件不存在,或是edit所依賴的後面的 .o 文件的文件修改時間要比edit這個文件新,那麼,他就會執行後面所定義的命令來生成edit這個文件。
  • 若是edit所依賴的.o文件也存在,那麼make會在當前文件中找目標爲.o文件的依賴性,若是找到則再根據那一個規則生成.o文件。(這有點像一個堆棧的過程)
  • 固然,你的C文件和H文件是存在的啦,因而make會生成 .o 文件,而後再用 .o 文件聲明make的終極任務,也就是執行文件edit了。

這就是整個make的依賴性,make會一層又一層地去找文件的依賴關係,直到最終編譯出第一個目標文件。在找尋的過程當中,若是出現錯誤,好比最後被依賴的文件找不到,那麼make就會直接退出,並報錯,而對於所定義的命令的錯誤,或是編譯不成功,make根本不理。make只管文件的依賴性,即,若是在我找了依賴關係以後,冒號後面的文件仍是不在,那麼對不起,我就不工做啦。 經過上述分析,咱們知道,像clean這種,沒有被第一個目標文件直接或間接關聯,那麼它後面所定義的命令將不會被自動執行,不過,咱們能夠顯示要make執行。即命令——「make clean」,以此來清除全部的目標文件,以便重編譯。

因而在咱們編程中,若是這個工程已被編譯過了,當咱們修改了其中一個源文件,好比file.c,那麼根據咱們的依賴性,咱們的目標file.o會被重編譯(也就是在這個依性關係後面所定義的命令),因而file.o的文件也是最新的啦,因而file.o的文件修改時間要比edit要新,因此edit也會被從新連接了(詳見edit目標文件後定義的命令)。

6. makefile 中使用變量

在上面的例子中,先讓咱們看看edit的規則:

edit : main.o kbd.o command.o display.o  insert.o search.o files.o utils.o
       cc -o edit main.o kbd.o command.o display.o  insert.o search.o files.o utils.o

咱們能夠看到[.o]文件的字符串被重複了兩次,若是咱們的工程須要加入一個新的[.o]文件,那麼咱們須要在兩個地方加(應該是三個地方,還有一個地方在clean中)。固然,咱們的makefile並不複雜,因此在兩個地方加也不累,但若是makefile變得複雜,那麼咱們就有可能會忘掉一個須要加入的地方,而致使編譯失敗。因此,爲了makefile的易維護,在makefile中咱們可使用變量。makefile的變量也就是一個字符串,理解成C語言中的宏可能會更好。

好比,咱們聲明一個變量,叫objects, OBJECTS, objs, OBJS, obj, 或是 OBJ,反正無論什麼啦,只要可以表示obj文件就好了。咱們在makefile一開始就這樣定義:

objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.o

因而,咱們就能夠很方便地在咱們的makefile中以「$(objects)」的方式來使用這個變量了,因而咱們的改良版makefile就變成下面這個樣子:

objects = main.o kbd.o command.o display.o insert.osearch.o files.o utils.o 
edit : $(objects) 
    cc -o edit $(objects)
......
clean :
    rm edit $(objects)

7. GNU make 自動推導 command 命令

objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.o
   edit : $(objects)
           cc -o edit $(objects)

   main.o : defs.h
   kbd.o : defs.h command.h
......
   utils.o : defs.h

   .PHONY : clean
   clean :
           rm edit $(objects)

簡而言之:GNU 的推導規則即便,在已知 target 文件和 prerequisites 文件的狀況下,自動推導出 command 語句
例如: 在已知目標文件 main.o 和依賴文件 defs.h 的狀況下, cc -c defs.h 這個command 預計被推導出來

相關文章
相關標籤/搜索