linux環境下的GUN make學習筆記(一),gun學習筆記
第一章:概述
1.1:make概述
在linux環境下使用make工具能夠比較容易的構建一個屬於自己的工程,整個工程的編譯只需要一個命令就可以完成編譯、串連以至於最後的執行。不過我們需要投入一些時間去學習如何完成makefile檔案的編寫,這個檔案也是make正常工作的基礎。
所要完成的makefile檔案描述了整個工程的編譯、串連等規則。其中包括:工程中的哪些源檔案需要編譯以及如何編譯、需要建立那些庫檔案以及如何建立這些庫檔案、如何最後產生我們想要的可執行檔。
make是一個命令工具,他解釋了makefile中的指令。在makefile檔案中描述了整個工程所有檔案的編譯順序、編譯規則。makefile有自己的書寫格式,關鍵字、函數。想c語言有自己的格式、關鍵字和函數一樣。而且在makefile中可以使用系統shell所提供的任何命令來完成想要的工作。
1.2:準備知識
在討論make之前首先需要明確一些基本概念:
編譯:把進階語言書寫的代碼轉換為機器可識別的機器指令。編譯後產生的機器指令雖然可被機器識別,但是還不能被執行。編譯時間,編譯器檢察進階語言的文法、函數與變數的聲明是否正確。只有所有文法正確、相關變數定義正確,編譯器就可以編譯出中間目標檔案。通常一個進階語言的源檔案都可對應一個目標檔案。目標檔案在linux中預設尾碼為“.o”。
連結:將多個目標檔案,或者目標檔案和庫檔案連結稱為可被作業系統執行的可執行程式。連結器不檢查函數所在的源檔案,只檢查所有目標檔案的定義的符號。將目標檔案使用的函數和其他目標或者庫檔案中的相關符號進行合并,並對所有檔案中的符號進行重新安排,並串連系統相關檔案最終產生可執行程式。
靜態庫:又稱文檔檔案,十多個目標檔案的集合。
共用庫:也是多個目標檔案的集合,但是這些目標檔案按照一種特殊的方式產生。模組中各個成員的地址都是相對位址。使用此共用庫的程式在運行時,共用庫被動態載入到記憶體並和主程式在記憶體中進行連結。多個可執行程式可共用庫檔案中的程式碼片段。
第二章:GUN make介紹
makemakefile告訴make以何種方式編譯和連結程式,當某一檔案更新時,make通過比較對應檔案的最後修改時間,來決定那些檔案需要更新,對需要的檔案執行相應命令。
2.1:makefile簡介:
當使用make工具進行編譯時間,工程中一些集中檔案在執行make時將會被編譯:
1、所有源檔案中沒有被編譯過,則對各個c源檔案進行編譯並進行連結,產生最後的可執行程式;
2、每一個在上次執行make之後修改過的c原始碼檔案在本次執行make時將會被重新編譯;
3、標頭檔在上一次執行make之後被修改。則所有包含此標頭檔的c源檔案在本次執行make時將會被重新編譯。
2.2:makefile規則介紹:
一個簡單的makefile描述規則群組成如下:
TARGET... : PREREQUITES
COMMAND
target:規則的目標,通常是最後需要產生的檔案名稱或者為了實現這個目標而必須的中間過程檔案名稱。可以是目標檔案、也可以是最後的可執行程式的檔案名稱。另外目標也可以使一個make執行的動作的名稱,如clean,我們稱這樣的目標是偽目標。
prerequisites:規則的依賴。建置規則所需要的檔案名稱列表。通常一個檔案依賴於一個或者多個檔案。
command:規則的命令列。是規則所要執行的動作(任意的shell命令或者是可在shell下執行的程式)。它限定了make執行這條規則時所需要的動作。
一個規則可以有多個命令列,每一條命令佔一行。注意:每一個命令列必須一[tab]字元開始,[tab]字元告訴make此行是一個命令列。make按照命令完成相應的動作。這也是書寫makefile中容易產生,而且比較隱形錯誤。
命令就是任意一個目標的依賴檔案發生變化後重建目標的動作描述。一個目標可以沒有依賴而只有動作。
在makefile中的規則就是描述在什麼情況下、如何重建規則的目標檔案,通常規則中包括了目標的依賴關係和重建目標的命令。make執行重建目標的命令,來建立或者重建目標。規則包含了檔案之間的依賴關係和更新此規則目標所需要的命令。
make程式根據規則的依賴關係,決定是夠執行規則所定義的命令的過程我們稱之為執行規則。
2.3:簡單的樣本
main.c:
#include"hello.h"int main(){ print(); return 0;}
hello.c:
#include"hello.h"void print(){ printf("hello world!\n"); }
hello.h:
#ifndef HELLO_H#define HELLO_H#include<stdio.h>void print();#endif
makefile檔案如下:
obj = main.o \
hello.omain: $(obj)gcc $(obj) -o mainmain.o: main.c hello.hgcc -c main.c -o main.ohello.o: hello.c hello.hgcc -c hello.c -o hello.oclean:rm -rf *.o main
首先書寫時,可以將一個較長行使用反斜線分解為多行,使得更容易理解。但是需要注意:反斜線之後不能有空格。在完成這個makefile以後;需要建立可執行程式main,所要做的就是在包含此makefile的目錄下鍵入命令make。刪除此目錄下的使用make產生的檔案,也只需要鍵入命令make clean就可以了。
命令列必須以tab鍵開始,以和makefile其他行區別。就是說所有的命令列必須以tab字元開始,但並不是所有的以tab字元開始的行都作為命令列來處理。(記住:make程式本身並不關心命令是如何工作的,對目標檔案的更新需要你在規則描述中提供正確的命令。make程式所做的就是當目標程式需要更新時執行規則所定義的命令)。
目標clean不是一個檔案,它僅僅代表執行一個動作的標識。正常情況下,不需要執行這個規則所定義的動作,因此目標clean沒有出現在其他任何規則的依賴列表中。因此在執行make時,它所指定的動作不會被執行。除非在執行make時明確地指定它。而且目標clean沒有任何依賴檔案,他只有一個目的,就是通過這個目標名來執行它所定義的命令。makefile中把那些沒有任何依賴只有執行動作的目標稱為偽目標。
2.4:make如何工作
預設情況下,make執行的是makefile中的第一個規則,此規則的第一個目標稱為最終目的或者終極目標。例如上例中的main檔案。
當在shell中鍵入make命令後,make讀取目前的目錄下的makefile檔案,並將makefile檔案中的第一個目標作為其執行的終極目標,開始處理第一個規則。在處理此規則所定義的命令之前,首先處理目標main所有的依賴檔案的更新規則。這些目標檔案為目標的規則的處理有下列三種情況:
1、目標檔案不存在,使用其描述規則建立它;
2、目標檔案存在,目標檔案所以來的.c源檔案或者標頭檔中的任何一個比目標檔案更新。則根據規則重新編譯產生它;
3、目標檔案存在,目標檔案比它的任何一個依賴檔案更新,什麼也不做。
在makefile中的一個規則的目標不是終極目標所依賴的,那麼這個規則不會被執行,除非明確指定執行這個規則。
完成了對目標檔案的建立或者更新之後,make程式將處理終極目標main所在的規則,分為以下三種情況:
1、目標檔案不存在,則執行規則以建立目標main;
2、目標檔案main存在,其依賴檔案中有一個或者多個檔案比它更新,則根據規則重新連結產生mian;
3、目標檔案存在。它比它的任何一個依賴檔案都新,則什麼都不做。
總結對一個makefile檔案,make首先解析終極目標所在的規則,根據其依賴檔案依次尋找建立這些以來檔案的規則。首先為第一個依賴檔案尋找建立規則,如果第一個依賴檔案依賴於其他檔案,則同樣為這個依賴檔案尋找建立規則,知道為所有依賴檔案找到合適的建立規則。之後make從最後一個規則回退開始執行,最終完成終極目標的第一個依賴檔案的建立和更新。之後對第一個、第二個、第三個、。。。終極目標的依賴檔案執行同樣的過程。最後一步是建立此規則的目標。
更新終極目標的過程中,如果任何一個規則出現錯誤make就立即報錯並退出。整個過程make只是負責執行規則,而對具體規則所描述的依賴關係的正確性、規則所定義的命令的正確性不做任何判斷。就是說,一個規則的依賴關係是否正確、描述重建目標的規則命令列是夠正確,make不做任何錯誤檢查。因此編寫一個正確的makefile檔案愛你就顯得尤為重要。
2.5:制定變數
上面的例子中的obj就是一個指定變數,下面每次使用時都可以直接使用;這樣做不但減少書寫的工作量,而且還可以減少修改而產生錯誤的可能。
2.6:自動推導規則
在使用make編譯.c源檔案時,編譯.c源檔案時,編譯.c源檔案規則的命令可以不用明確給出。這是因為make本身存在一個預設的規則,能夠自動完成對.c檔案的編譯並產生對應的目標檔案。他執行命令cc -c來編譯.c檔案。在makefile中我們只需要給出需要重建的目標檔案名,meke會自動為這個目標檔案尋找合適的依賴檔案,並且使用正確的命令,來重建這個目標檔案,對於上面的例子,此預設規則就是使用命令“cc -c main.c -o main.o”來建立檔案'main.o'對一個目標檔案。此預設規則稱為make的隱含規則。
在書寫時我們就可以省略掉描述重名的目標檔案與依賴檔案的規則,只需要給出那些特定的規則描述。因此上面的例子就可以更加簡單的寫成:
obj = main.o hello.omain: $(obj) cc -o main $(obj)main.o: hello.hhello.o: hello.hclean: rm -rf *.o main
2.7:另類風格的makefile
我們也可以根據依賴而不是目標對規則進行分組。上例的makefile就可以這樣來實現:
obj = main.o hello.omain : $(obj) cc -o main $(obj)#(obj) : hello.hclean: rm -rf *.o main
例子中hello.h作為所有.o檔案的依賴檔案。但這種書寫方式不建議,後期維護會比較麻煩。
書寫規則建議的方式是:單目標,多依賴。就是說盡量做到一個規則中只存在一個目標檔案,可以有多個依賴檔案。盡量避免多目標,單依賴的方式。
2.8:清除工作目錄過程檔案
規則除了完成原始碼編譯之外,也可以完成其他任務,例如前面提到的清除編譯過程中產生的臨時檔案的規則。
clean: rm -rf *.o main
在實際應用時,我們把這個規則寫成如下稍微複雜一些的樣子,以防止始料未及的情況。
.PHONY:cleanclean: -rm -rf *.o main
這兩個實現有兩點不同:
1、通過“.PHONY”特殊目標將clean目標聲明為偽目標。避免當磁碟上存在一個名為clean檔案時,目標clean所在規則的命令無法執行。
2、在命令列之前使用-,意思是忽略命令“rm”的執行錯誤。
這樣一個目標在makefile中,不能將其作為終極目標。因為我們的初衷並不是當你在命令列上輸入make以後執行刪除動作。而是要建立或者更新程式。