Linux下makefile文件入門

Linux——makefile編寫

        </h1>
        <div class="clear"></div>
        <div class="postBody">

以前對makefile的編寫,限於剛開始接觸,我都比較侷限一些死板的格式,有時候就會顯得有些繁瑣。在進一步瞭解一些系統編譯和鏈接的知識後,對makefile編寫流程有了一些新的認識,所以來此梳理梳理,方便更爲靈活地編寫makefile。

限於makefile認識不足,這裏參考了一篇比較好博文:makefile

 

關於makefile


  makefile帶來直接好處就是——“自動化編譯”。一旦寫好,只需要一個make命令,整個工程完全自動編譯,所以十分方便。而Makefile文件就是告訴make命令怎麼樣地去編譯和鏈接程序。但是想要比較靈活的運用它,還是先要熟悉一些關於系統對程序編譯和鏈接的知識。

 

  一般來說,對C、C++程序、先把源文件編譯成中間代碼文件。Linux下是 .o 文件即 Object File,在Windows下也就是 .obj 文件,這個動作叫做編譯(compile)。然後再把大量的.O文件合成執行文件,這個動作叫作鏈接(link)

  編譯時,編譯器需要的是語法的正確,函數與變量的聲明的正確。對於後者,通常是讓我們告訴編譯器頭文件的所在位置(頭文件中放聲明,而定義放在C/C++文件中),只要所有的語法正確,編譯器就可以編譯出中間目標文件。一般來說,每個源文件都應該對應於一箇中間目標文件(.O文件或是OBJ文件)。

  鏈接時,主要是鏈接函數和全局變量,所以,我們可以使用這些中間目標文件(.O文件或.OBJ文件)來鏈接我們的應用程序。鏈接器並不管函數所在的源文件,只管函數的中間目標文件。在大多數時候,由於源文件太多,編譯生成的中間目標文件太多,而在鏈接時需要明顯地指出中間目標文件名,這對於編譯很不方便,所以,我們要給中間目標文件打個包,在Windows下這種包叫“庫文件”(Library File),也就是 .lib 文件,在Linux下,是Archive File,也就是 .a 文件

  

  總的來說就是,首先源文件-> .o文件,再由.o文件->可執行文件。在編譯時,編譯器只檢測程序語法,和函數、變量是否被聲明。如果函數未被聲明,編譯器會給出一個警告,但可以生成Object File。而在鏈接程序時,鏈接器會在所有的.o文件中找尋函數的實現,如果找不到,那到就會報鏈接錯誤碼(Linker Error 

來個例子感受一下,

hello: hello.o    
hello.o: hello.c
    gcc -c hello.c -o hello.o

這裏make,便會自動編譯了。這當中生成可執行文件hello依賴於hello.o,hello.o 依賴於 hello.c; 最後找到了hello.c便可以gcc生成hello.o這樣往後‘帶’,目標文件的hello便鏈接上.o文件去執行了。這裏值得注意的是寫gcc命令時需要添上 -c選項,用來保證得到的.o文件可重鏈接,不然基本會make報錯(某些情況如直接gcc hello.c -o hello例外)。

 

   直白點說,最後生成的可執行文件就是鏈接.o文件得到;而.o文件靠着“依賴關係”生成。

還有注意一點就是在Makefile中的命令(如gcc ..),必須要以[Tab]鍵開始,不然你很可能就會make出錯哦~。

 

 

 

上面例子直接鏈接一箇中間目標文件,顯得比較簡單,當遇到源文件需要鏈接多箇中間目標文件時會是怎麼個樣子呢?

比如 分別創建一個加法的add.c 和 add.h ,一個減法 sub.c和 sub.h 最後main.c 來調用add 和 sub實現加減法。此時Makefile 會像這樣

複製代碼
main: main.o add.o sub.o
main.o: main.c  
    gcc -c main.c -o main.o
add.o: add.c
    gcc -c add.c -o add.o  #加-c 指定生成爲可重鏈接.o文件
sub.o: sub.c
    gcc -c sub.c -o sub.o

.PHONY:clean
clean:
-rm -rf *.o

使用看看

複製代碼

從上面注意幾個地方

  當最終目標文件依賴多個.o時,將依賴的多個.o  一起寫到main: 後面。然後依次以  目標:依賴文件  gcc...   的格式,羅列所有依賴關係

  由於在上面的過程中生成了多箇中間.o文件(實際工程中肯定是比較多的),所以每次編譯完成,後續基本還需要進行一定的清理工作,這時候就用上一個 "clean" (後面細說一下)來清理。

  .PHONY意思表示clean是一個“僞目標”。也即是無論clean是否最新,一定執行它。rm命令前面加了一個小減號的意思就是,也許某些文件出現問題,但並不理睬。當然,clean的規則不要放在文件的開頭,否則這就會變成make的默認目標,相信誰也不願意這樣。不成文的規矩是——“clean從來都是放在文件的最後”

 

 

關於clean:  

   它只不過是一個動作名字,有點像c語言中的lable一樣,其冒號後什麼也沒有,那麼,make就不會自動去找它的依賴性,也就不會自動執行其後所定義的命令。要執行其後的命令(不僅用於clean,其他lable同樣適用),就要在make命令後明顯得指出這個lable的名字。這樣的方法非常有用,我們可以在一個Makefile中定義不用的編譯或是和編譯無關的命令,比如程序的打包,程序的備份,等等。

 

 

到這,大致可以瞭解了makefile,以及大致怎麼實現makefile.好, 那麼make又是怎麼用makefile進行執行的呢?

 


 

 

 

make怎麼執行


  1、make會在當前目錄下找名字叫“Makefile”或“makefile”的文件。


  2、如果找到,它會找文件中的第一個目標文件(target),在上面的例子中,他會找到“main”這個文件,並把這個文件作爲最終的目標文件。


  3、如果main文件不存在,或是main所依賴的後面的 .o 文件的文件修改時間要比main這個文件新,那麼,它就會執行後面所定義的命令來生成main這個文件。


  4、如果main所依賴的.o文件也不存在,那麼make會在當前文件中找目標爲.o文件的依賴性,如果找到則再根據那一個規則生成.o文件。(這有點像一個堆棧的過程)


  5、當然,你的C文件和H文件是存在的啦,於是make會生成 .o 文件,然後再用 .o 文件生命make的終極任務,也就是執行文件main了。

  

  這就是整個make的依賴性,make會一層又一層地去找文件的依賴關係,直到最終編譯出第一個目標文件。在找尋的過程中,如果出現錯誤,比如最後被依賴的文件找不到,那麼make就會直接退出,並報錯,而對於所定義的命令的錯誤或是編譯不成功make根本不理。make只管文件的依賴性,即如果在我找了依賴關係之後,冒號後面的文件還是不在,那麼對不起,我就不工作啦。


 

 

 

makefile簡化


 從前面的makefile編寫來看,  當中我們每寫一個依賴關係就需要寫一個形如gcc X.c  -o  X.o生成命令,這裏還好,若是較大的工程,這樣難免就太繁瑣了,所以據瞭解,一般在公司專門編寫makefile的人是不會那樣寫的。還有寫着更簡潔方式,就是利用下面這幾個符號:

  $^    代表所有的依賴文件
  $@  代表所有的目標文件 
  $<   代表第一個依賴文件 


注意$<代表的是依賴關係表中的第一項(如果我們想引用的是整個關係表,那麼就應該使用$^),具體到我們這裏就是%.c。而$@代表的是當前語句的目標,即%.o。於是便可以將上面的makefile改寫成

複製代碼
.PHONY:clean

main: main.o add.o sub.o
main.o: main.c
gcc -c $< -o $@
add.o: add.c
gcc
-c $^ -o $@
sub.o: sub.c
gcc
-c $^ -o $@

clean:
rm -rf *.o

複製代碼

 

由於鏈接依賴的是中間目標文件.o ,如果makefile變得複雜,那麼我們就有可能會少寫一個依賴關係,得到.o文件不完整,從而導致編譯失敗。所以,爲了makefile的易維護,在makefile中我們可以使用常量(這裏看到很多人都把它說成變量,個人認爲 它在後面並沒有被改變,因次叫常量更好)。定義一個常量OBJS來表示所有的.o文件,於是便還可將Makefile寫成這樣:

 

複製代碼
.PHONY:clean

OBJS = main.o\ //\轉義字符
add.o
sub.o

main: $(OBJS)
%.o : %.c
gcc
-c $^ -o $@

clean:
-rm -rf $(OBJS)

複製代碼

這樣是不是感覺又簡潔了不少!  這裏的%.o : %.c 想必都可以大家都可以猜出來,這代表的意思就是所有的.o文件依賴相應的.C文件,是一個模式規則。這樣一來,make命令就會自動將所有的.c源文件編譯成同名的.o文件。不用這樣便又省去好幾步。

到這,相信聰明的你,可以更靈活編寫makefile (ヾ(´A`)ノ゚ 打臉,怎能說出如此膨脹的話來)。  不管了,再補充補充關於Makefile的東西  就趕緊溜~~

 


 

 

 

Makefile還有什麼


 

  1. 顯式規則。顯式規則說明了,如何生成一個或多個目標文件。這是由Makefile的書寫者明顯指出,要生成的文件,文件的依賴文件,生成的命令。
  2. 隱晦規則。由於我們的make有自動推導的功能,所以隱晦的規則可以讓我們比較簡略地書寫Makefile,這是由make所支持的。
  3. 變量的定義。在Makefile中我們要定義一系列的變量,變量一般都是字符串,這個有點像你C語言中的宏,當Makefile被執行時,其中的變量都會被擴展到相應的引用位置上。
  4. 文件指示。其包括了三個部分,一個是在一個Makefile中引用另一個Makefile,就像C語言中的include一樣;另一個是指根據某些情況指定Makefile中的有效部分,就像C語言中的預編譯#if一樣;還有就是定義一個多行的命令。有關這一部分的內容,我會在後續的部分中講述。
  5. 註釋。Makefile中只有行註釋,和UNIX的Shell腳本一樣,其註釋是用“#”字符,這個就像C/C++中的“//”一樣。如果你要在你的Makefile中使用“#”字符,可以用反斜槓進行轉義,如:“\#”。

 

 在應用時候,定下的規則一般這樣:

  ①如果這個工程沒有編譯過,那麼我們的所有c文件都要編譯並被鏈接。

  ②如果這個工程的某幾個c文件被修改,那麼我們只編譯被修改的c文件,並鏈接目標程序。

  ③如果這個工程的頭文件被改變了,那麼我們需要編譯引用了這幾個頭文件的c文件,並鏈接目標程序

 

  所以只要我們的makefile寫得夠好,所有的這一切,我們只用一個make命令就可以完成,make命令會自動智能地根據當前的文件修改的情況來確定哪些文件需要重編譯,從而自己編譯所需要的文件和鏈接目標程序。

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