1.關於程序的編譯和鏈接
一般來說,無論是C、C++首先要把源文件編譯成中間目標文件即 Object File(windows為.obj文件,unix為.o文件),這個動作叫做編譯(compile)。然后再把大量的Object File合成執行文件,這個動作叫作鏈接(link)。
1.1編譯
編譯時編譯器只檢查語法是否正確,函數與變量的聲明是否正確。如果函數未被聲明,編譯器會給出一個警告,但可以生成Object File。一般來說,每個源文件都應該對應於一個中間目標文件(O文件或是OBJ文件)。
cc -c foobar.c ==>將源文件編譯但不鏈接,成目標文件foobar.o
cc foobar.c -o foobar ==>將源文件編譯並鏈接,生成可執行文件foobar
1.2鏈接
鏈接時主要是鏈接函數和全局變量。鏈接器會在所有的Object File中找尋函數的實現,如果找不到,那到就會報鏈接錯誤碼(Linker Error)。
cc -o foobar foobar.c ==>鏈接生成可執行文件foobar
1.3自定義函數庫(打包中間目標文件)ar命令
如果需要鏈接中間目標文件太多,鏈接時需要明顯地指出所有中間目標文件名,十分不便。可以給中間目標文件打個包,在Windows下這種包叫“庫文件”(Library File),也就是 .lib 文件,在UNIX下,是Archive File,也就是 .a 文件靜態庫。
PS:我們可以使用中間目標文件(O文件或是OBJ文件)或靜態庫文件來鏈接我們的應用程序。
ar crv libtest.a *.o ==>將該目錄下的所有目標文件打包生成了libtest.a文件靜態庫
2.make命令
make是一個命令工具,是一個解釋Makefile中指令的命令工具。在命令行輸入make命令后,會查找當前目錄下的Makefile文件來執行,根據Makefile文件編譯源代碼生成中間目標文件、鏈接后生成可執行文件。
一般來說,大多數的IDE都有這個命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。使用示例(其中all、install和clean均為Makefile文件中定義的偽目標):
make ==>默認找到Makefile中第一個目標,進行編譯鏈接
make all make clean
3.Makefile文件
make命令執行時,需要一個 Makefile 文件,告訴make命令需要怎么樣的去編譯和鏈接程序。Makefile帶來的好處就是“自動化編譯”,一旦寫好,只需要一個make命令,整個工程完全自動編譯,極大的提高了軟件開發的效率。
Makefile中除了編譯鏈接目標外,還可以使用偽目標。通常情況下,為了規范和統一,會參考Linux源碼的Makefile規則來書寫我們的Makefile中的偽目標,這些偽目標都是GNU開源軟件定義和采用的。
“all”—— 這個偽目標是所有目標的目標,其功能一般是編譯所有的目標。 “clean” —— 這個偽目標功能是刪除所有被make創建的文件。 “install” —— 這個偽目標功能是安裝已編譯好的程序,其實就是把目標執行文件拷貝到指定的目標中去。 “print” —— 這個偽目標的功能是例出改變過的源文件。 “tar” —— 這個偽目標功能是把源程序打包備份。也就是一個tar文件。 “dist” —— 這個偽目標功能是創建一個壓縮文件,一般是把tar文件壓成Z文件,或是gz文件。 “TAGS” —— 這個偽目標功能是更新所有的目標,以備完整地重編譯使用。 “check”和“test” —— 這兩個偽目標一般用來測試makefile的流程。
3.1 Makefile規則
target... : prerequisites ...
command
...
target:是一個目標,可以是目標文件Object File,也可以是執行文件,還可以是一個標簽(偽目標)。
prerequisites:是要生成那個target所需要的文件或目標。
command:也就是make需要執行的命令(任意的Shell命令,一定要以Tab鍵開頭)。
3.2 Makefile規則示例
僅做編譯:main.o是我們的第一個目標,main.c和defs.h是目標所依賴的源文件,而執行命令為“cc -c main.c”
編譯鏈接:main是我們的第二個目標,main.c和defs.h是目標所依賴的源文件,而執行命令為“cc main.c -o main”
main.o : main.c defs.h cc -c -o main.o main.c main: main.c defs.h cc main.c -o main
3.3 Makefile規則示例(自動推導)
GNU的make很強大,它可以自動推導依賴c文件以及編譯命令。以main.c為例
僅做編譯:make看到一個[main.o]目標,它會自動的把[main.c]文件加在依賴關系中,並自動推導出cc -c -o main.o main.c
編譯鏈接:make看到一個[main]目標,它會自動把[main.c]文件加在依賴關系中,並自動推導出cc main.c -o main
Makefile中如下定義
main.o: defs.h
main: defs.h
等同於
main.o : main.c defs.h
cc -c -o main.o main.c main: main.c defs.h cc main.c -o main
3.4 清空目標文件規則示例
每個Makefile中都應該寫一個清空目標文件(執行文件和.o目標文件)的規則,這不僅便於重編譯,也很利於保持文件的清潔。規矩:使用clean偽目標,且永遠放在Makefile文件的最后。
目標:清理可執行文件main和目標文件mian.o
一般常用寫法:
clean : rm main main.o
更健壯寫法:
.PHONY : clean clean : -rm main main.o
.PHONY表示clean是一個“偽目標”。在rm命令前面加了一個小減號,標識忽略文件出現的問題,繼續執行。
3.5 Makefile變量定義示例
目標:生成可執行文件main,引用多個目標文件
objects = main.o kbd.o \
insert.o search.o files.o utils.o main: $(objects) cc -o main $(objects)
定義變量objects后,引用變量使用$(objects) 。.反斜杠(\)是換行符的意思,這樣比較便於Makefile的易讀。
3.6 Makefile經典示例
目標:一個工程包含有3個頭文件和8個C文件,需要編譯鏈接成可執行文件edit。
objects = main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o
.PHONY : all all: $(objects) cc -o edit $(objects) main.o : defs.h kbd.o : defs.h command.h command.o : defs.h command.h display.o : defs.h buffer.h insert.o : defs.h buffer.h search.o : defs.h buffer.h files.o : defs.h buffer.h command.h utils.o : defs.h .PHONY : clean clean : -rm edit $(objects)
3.7 測試使用源文件main.c
#include <stdio.h> int main(void) { printf("Hello world/n"); return 0; }
參考文檔:
make all、make clean、make install 等命令的來源