1. 程式人生 > 其它 >學習Java之專案1(家庭記賬軟體)

學習Java之專案1(家庭記賬軟體)

makefile

1 make和makefile簡介

(1)make

make是一個應用程式,主要用於解析源程式之間的依賴關係,根據依賴關係自動維護編譯工作,並執行宿主作業系統中的各種命令。

(2)makefie

①概念

makefile是一個描述檔案,可以簡單的認為是一個工程檔案的編譯規則,描述了整個工程的編譯和連結等規則。其中包含了哪些檔案需要編譯,哪些檔案不需要編譯,哪些檔案需要先編譯,哪些檔案需要後編譯,哪些檔案需要重建等等。makefile擁有特定的語法規則,支援函式定義和函式呼叫,能夠直接整合作業系統中的各種命令。編譯整個工程需要涉及到的,在 makefile 中都可以進行描述。換句話說,makefile 可以使得我們的專案工程的編譯變得自動化,不需要每次都手動輸入一堆原始檔和引數。

②makefile 支援多執行緒併發操作,會極大的縮短我們的編譯時間,並且當我們修改了原始檔之後,編譯整個工程的時候,make 命令只會編譯我們修改過的檔案,沒有修改的檔案不用重新編譯,也極大的解決了我們耗費時間的問題。

(3)make和makefile的關係

makefile中的描述用於指導make程式如何完成工作;make根據makefile中的規則執行命令,最後完成編譯輸出。即配置好makefile後,最後通過make命令,即可編譯全部檔案。

make命令格式:

make [options] [target]

常用的引數不多,說下-f和-n其餘的自己可通過make –h瞭解。

  • -f:指定規則檔案(makefile/Makefile可以隨意起名)

  • -n: 輸出make即將執行的命令(可檢查makefile的語法是否正確,要執行的命令是否符合預期)

2 makefile編寫規則

(1)makefile的規則主要由兩部分組成,分別是依賴的關係執行的命令,其結構如下:

targets:prerequisites  
   command

相關說明如下:

  • targets:編譯的目標,可以是 Object File(一般稱它為中間檔案),也可以是可執行檔案,還可以是一個標籤;

  • prerequisites:編譯的目標依賴的條件,要生成 targets 需要的檔案或者是目標。可以是多個,也可以是沒有;

  • command:make 需要執行的命令(任意的 shell 命令)。可以有多條命令,每一條命令佔一行。

    注意:我們的目標和依賴檔案之間要使用冒號分隔開,命令的開始一定要使用Tab鍵

    例如:

test:test.c  
  gcc -o test test.c

上述程式碼實現的功能就是編譯test.c檔案,其中test是目標檔案,也是我們最終生成的可執行檔案,依賴檔案為test.c原始檔,重建目標檔案需要執行的操作是gcc -o test test.c。

(2)規則中的注意事項

[Tab]鍵:'\t'

  • 每一個命令必須以[Tab]字元開始

  • [Tab]字元告訴make此行是一個命令列

續行符:“\”

  • 可以將內容分開寫到下一行,提高可讀性

依賴規則

  • 當目標對應的檔案不存在,執行對應命令

  • 當依賴在時間上比目標更新,執行對應命令(編譯過的不重複編譯,除非make clean

  • 當依賴關係連續發生時,對比依賴鏈上的每一個目標

(3)目標和條件的關係

目標和條件之間的關係是:欲更新目標,必須首先更新它的所有條件;所有條件中只要有一個條件被更新了,目標也必須隨之被更新。所謂“更新”就是執行一遍規則中的命令列表(不需要非得生成目標,執行命令了則視為已更新)

目標被更新的依據:

  • 目標沒有生成。

  • 某個條件需要更新(條件可以是另一個目標,巢狀遞迴)。

  • 某個條件的修改時間比目標晚(修改時間戳)。

(4)-o檔案

對 ".o" 檔案為目標的規則處理有下列三種情況:

  • 目標 ".o" 檔案不存在,使用其描述規則建立它;

  • 目標 ".o" 檔案存在,目標 ".o" 檔案所依賴的 ".c" 原始檔 ".h" 檔案中的任何一個比目標 ".o" 檔案“更新”(在上一次 make 之後被修改)。則根據規則重新編譯生成它;

  • 目標 ".o" 檔案存在,目標 ".o" 檔案比它的任何一個依賴檔案(".c" 原始檔、".h" 檔案)“更新”(它的依賴檔案在上一次 make 之後沒有被修改),則什麼也不做。

編譯案例:

hello.out : main.o  func.o
  gcc -o hello.out  main.o  func.o
main.o : main.c
  gcc -o main.o -c main.c
func.o : func.c
  gcc -o func.o -c func.c

(5) 簡單的概括一下makefile 中的內容,它主要包含有五個部分,分別是:

  1. 顯式規則

顯式規則說明了,如何生成一個或多的的目標檔案。這是由 makefile 的書寫者明顯指出,要生成的檔案,檔案的依賴檔案,生成的命令。

  1. 隱晦規則

由於我們的 make 命名有自動推導的功能,所以隱晦的規則可以讓我們比較粗糙地簡略地書寫 makefile,這是由 make 命令所支援的。

  1. 變數的定義

在 makefile 中我們要定義一系列的變數,變數一般都是字串,這個有點像C語言中的巨集,當 makefile 被執行時,其中的變數都會被擴充套件到相應的引用位置上。

  1. 檔案指示

其包括了三個部分,一個是在一個 makefile 中引用另一個 makefile,就像C語言中的 include 一樣;另一個是指根據某些情況指定 makefile 中的有效部分,就像C語言中的預編譯 #if 一樣;還有就是定義一個多行的命令。有關這一部分的內容,我會在後續的部分中講述。

  1. 註釋

makefile 中只有行註釋,和 UNIX 的 Shell 指令碼一樣,其註釋是用“#”字元,這個就像 C/C++中的“//”一樣。如果你要在你的 makefile 中使用“#”字元,可以用反斜框進行轉義,如:“#”。

3 makefile偽目標

(1)作用

偽目標並不會建立目標檔案,只是想去執行這個目標下面的命令,偽目標的存在可以幫助我們找到命令並執行。

使用偽目標有兩點原因:

  • 避免我們的 makefile 中定義的只執行的命令的目標和工作目錄下的實際檔案出現名字衝突。

  • 提高執行 make 時的效率,特別是對於一個大型的工程來說,提高編譯的效率也是我們所必需的。

(2)如果需要書寫這樣一個規則,規則所定義的命令不是去建立檔案,而是通過 make 命令明確指定它來執行一些特定的命令。例項如下:

clean:
    rm -rf *.o test

執行make clean時:

1)當目錄下不存在名為clean的檔案時:會執行rm -rf *.o test

2)當目錄下存在名為clean的檔案時: 由於這個規則沒有依賴檔案,所以目標被認為是已存在的而不去執行規則所定義的命令。因此命令 rm 將不會被執行。

(3)第(2)點的解決方法:

1)最蠢的方法,人為確保目標名不要跟工程檔名有重名的情況。

2)目標後面加上兩個冒號,代表強制目標。

3)makefile提供了內嵌的關鍵字.PHONY,可提前宣告下clean偽目標。如下所示:

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

這樣 clean 就被宣告成一個偽目標,無論當前目錄下是否存在 clean 這個檔案,當我們執行 make clean 後 rm 都會被執行。而且當一個目標被宣告為偽目標之後,make 在執行此規則時不會去試圖去查詢隱含的關係去建立它。這樣同樣提高了 make 的執行效率,同時也不用擔心目標和檔名重名而使我們的編譯失敗。

4 makefile變數

(1)基本語法

變數的名稱 = 值列表

值列表,既可以是零項,又可以是一項或者是多項,例如:

${TARGET} : func.o main.o

(2)變數的引用

呼叫變數的時候可以用 "$(VALUE_LIST)" 或者是 "${VALUE_LIST}" 來替換,這就是變數的引用,例如:

OBJ=main.o test.o test1.o test2.o
test:$(OBJ)
gcc -o test $(OBJ) 

(3)變數的賦值方式

  • 簡單賦值 ( := ) 程式語言中常規理解的賦值方式,只對當前語句的變數有效。

  • 遞迴賦值 ( = ) 賦值語句可能影響多個變數,所有目標變數相關的其他變數都受影響。

  • 條件賦值 ( ?= ) 如果變數未定義,則使用符號中的值定義變數。如果該變數已經賦值,則該賦值語句無效。

  • 追加賦值 ( += ) 原變數用空格隔開的方式追加一個新值。

(4)自動化變數

自動化變數 說明
$@ 表示規則的目標檔名。如果目標是一個文件檔案(Linux 中,一般成 .a 檔案為文件檔案,也成為靜態的庫檔案), 那麼它代表這個文件的檔名。在多目標模式規則中,它代表的是觸發規則被執行的檔名。
$% 當目標檔案是一個靜態庫檔案時,代表靜態庫的一個成員名。
$< 規則的第一個依賴的檔名。如果是一個目標檔案使用隱含的規則來重建,則它代表由隱含規則加入的第一個依賴檔案。
$? 所有比目標檔案更新的依賴檔案列表,空格分隔。如果目標檔案時靜態庫檔案,代表的是庫檔案(.o 檔案)。
$^ 代表的是所有依賴檔案列表,使用空格分隔。如果目標是靜態庫檔案,它所代表的只能是所有的庫成員(.o 檔案)名。 一個檔案可重複的出現在目標的依賴中,變數“$^”只記錄它的第一次引用的情況。就是說變數“$^”會去掉重複的依賴檔案。
$+ 類似“$^”,但是它保留了依賴檔案中重複出現的檔案。主要用在程式連結時庫的交叉引用場合。
$* 在模式規則和靜態模式規則中,代表“莖”。“莖”是目標模式中“%”所代表的部分(當檔名中存在目錄時, “莖”也包含目錄部分)。

下面是一些詳細的描述:

變數名 功能
$(@D) 表示檔案的目錄部分(不包括斜槓)。如果 "$@" 表示的是 "dir/foo.o" 那麼 "$(@D)" 表示的值就是 "dir"。如果 "$@" 不存在斜槓(檔案在當前目錄下),其值就是 "."。
$(@F) 表示的是檔案除目錄外的部分(實際的檔名)。如果 "$@" 表示的是 "dir/foo.o",那麼 "$@F" 表示的值為 "dir"。
$(D) $(F) 分別代表 "莖" 中的目錄部分和檔名部分
$(%D) $(%F) 當以 "archive(member)" 形式靜態庫為目標時,分別表示庫檔案成員 "member" 名中的目錄部分和檔名部分。踏進對這種新型時的目標有效。
$(<D) $(<F) 表示第一個依賴檔案的目錄部分和檔名部分。
$(^D) $(^F) 分別表示所有依賴檔案的目錄部分和檔案部分。
$(+D) $(+F) 分別表示所有的依賴檔案的目錄部分和檔案部分。
$(?D) $(?F) 分別表示更新的依賴檔案的目錄部分和檔名部分。

5 makefile條件判斷

關鍵字 功能
ifeq 判斷引數是否不相等,相等為 true,不相等為 false。
ifneq 判斷引數是否不相等,不相等為 true,相等為 false。
ifdef 判斷是否有值,有值為 true,沒有值為 false。
ifndef 判斷是否有值,沒有值為 true,有值為 false。

6 makefile命令引數和選項彙總

引數選項 功能
-b,-m 忽略,提供其他版本 make 的相容性
-B,--always-make 強制重建所有的規則目標,不根據規則的依賴描述決定是否重建目標檔案。
-C DIR,--directory=DIR 在讀取 Makefile 之前,進入到目錄 DIR,然後執行 make。當存在多個 "-C" 選項的時候,make 的最終工作目錄是第一個目錄的相對路徑。
-d make 在執行的過程中打印出所有的除錯資訊,包括 make 認為那些檔案需要重建,那些檔案需要比較最後的修改時間、比較的結果,重建目標是用的命令,遺憾規則等等。使用 "-d" 選項我們可以看到 make 構造依賴關係鏈、重建目標過程中的所有的資訊。
--debug[=OPTIONS] make 執行時輸出除錯資訊,可以使用 "OPTIONS" 控制除錯資訊的級別。預設是 "OPTIONS=b" ,"OPTIONS" 的可值為以下這些,首字母有效:all、basic、verbose、implicit、jobs、makefile。
-e,--enveronment -overrides 使用環境變數定義覆蓋 Makefile 中的同名變數定義。
-f=FILE,--file=FILE, --makefile=FILE 指定檔案 "FILE" 為 make 執行的 Makefile 檔案
-p,--help 列印幫助資訊。
-i,--ignore-errors 執行過程中忽略規則命令執行的錯誤。
-I DIR,--include-dir=DIR 指定包含 Makefile 檔案的搜尋目錄,在Makefile中出現另一個 "include" 檔案時,將在 "DIR" 目錄下搜尋。多個 "-i" 指定目錄時,搜尋目錄按照指定的順序進行。
-j [JOBS],--jobs[=JOBS] 可指定同時執行的命令數目,愛沒有 "-j" 的情況下,執行的命令數目將是系統允許的最大可能數目,存在多個 "-j" 目標時,最後一個目標指定的 JOBS 數有效。
-k,--keep-going 執行命令錯誤時不終止 make 的執行,make 盡最大可能執行所有的命令,直至出現知名的錯誤才終止。
-l load,--load-average=[=LOAD],--max-load[=LOAD] 告訴 make 在存在其他任務執行的時候,如果系統負荷超過 "LOAD",不在啟動新的任務。如果沒有指定 "LOAD" 的引數 "-l" 選項將取消之前 "-l" 指定的限制。
-n,--just-print,--dry-run 只打印執行的命令,但是不執行命令。
-o FILE,--old-file=FILE, --assume-old=FILE 指定 "FILE"檔案不需要重建,即使是它的依賴已經過期;同時不重建此依賴檔案的任何目標。注意:此引數不會通過變數 "MAKEFLAGS" 傳遞給子目錄程序。
-p,--print-date-base 命令執行之前,打印出 make 讀取的 Makefile 的所有資料,同時打印出 make 的版本資訊。如果只需要列印這些資料資訊,可以使用 "make -qp" 命令,檢視 make 執行之前預設的規則和變數,可使用命令 "make -p -f /dev/null"
-q,-question 稱為 "詢問模式" ;不執行任何的命令,並且無輸出。make 只返回一個查詢狀態。返回狀態 0 表示沒有目標表示重建,返回狀態 1 表示存在需要重建的目標,返回狀態 2 表示有錯誤發生。
-r,--no-builtin-rules 取消所有的內嵌函式的規則,不過你可以在 Makefile 中使用模式規則來定義規則。同時選項 "-r" 會取消所有後綴規則的隱含字尾列表,同樣我們可以在 Makefile 中使用 ".SUFFIXES",定義我們的字尾名的規則。"-r" 選項不會取消 make 內嵌的隱含變數。
-R,--no-builtin-variabes 取消 make 內嵌的隱含變數,不過我們可以在 Makefile 中明確定義某些變數。注意:"-R" 和 "-r" 選項同時開啟,因為沒有了隱含變數,所以隱含規則將失去意義。
-s,--silent,--quiet 取消命令執行過程中的列印。
-S,--no-keep-going, --stop 取消 "-k" 的選項在遞迴的 make 過程中子 make 通過 "MAKEFLAGS" 變數繼承了上層的命令列選項那個。我們可以在子 make 中使用“-S”選項取消上層傳遞的 "-k" 選項,或者取消系統環境變數 "MAKEFLAGS" 中 "-k"選項。
-t,--touch 和 Linux 的 touch 命令實現功能相同,更新所有的目標檔案的時間戳到當前系統時間。防止 make 對所有過時目標檔案的重建。
-v,version 檢視make的版本資訊。
-w,--print-directory 在 make 進入一個子目錄讀取 Makefile 之前列印工作目錄,這個選項可以幫助我們除錯 Makefile,跟蹤定位錯誤。使用 "-C" 選項時預設開啟這個選項。
--no-print-directory 取消 "-w" 選項。可以是 用在遞迴的 make 呼叫的過程中 ,取消 "-C" 引數的預設開啟 "-w" 的功能。
-W FILE,--what-if=FILE, --new-file=FILE, --assume-file=FILE 設定檔案 "FILE" 的時間戳為當前的時間,但不更改檔案實際的最後修改時間。此選項主要是為了實現對所有依賴於檔案 "FILE" 的目標的強制重建。
--warn-undefined-variables 在發現 Makefile 中存在沒有定義的變數進行引用時給出告警資訊。此功能可以幫助我們在除錯一個存在多級巢狀變數引用的複雜 Makefile。但是建議在書寫的時候儘量避免超過三級以上的變數巢狀引用

7 makefile目標型別彙總

名稱 功能
.PHONY: 這個目標的所有依賴被作為偽目標。偽目標是這樣一個目標:當使用 make 命令列指定此目標時,這個目標所在的規則定義的命令、無論目標檔案是否存在都會被無條件執行。
.SUFFIXES: 這個目標的所有依賴指出了一系列在後綴規則中需要檢查的字尾名
.DEFAULT: Makefile 中,這個特殊目標所在規則定義的命令,被用在重建那些沒有具體規則的目標,就是說一個檔案作為某個規則的依賴,卻不是另外一個規則的目標時,make 程式無法找到重建此檔案的規則,這種情況就執行 ".DEFAULT" 所指定的命令。
.PRECIOUS: 這個特殊目標所在的依賴檔案在 make 的過程中會被特殊處理:當命令執行的過程中斷時,make 不會刪除它們。而且如果目標的依賴檔案是中間過程檔案,同樣這些檔案不會被刪除。
.INTERMEDIATE: 這個特殊目標的依賴檔案在 make 執行時被作為中間檔案對待。沒有任何依賴檔案的這個目標沒有意義。
.SECONDARY: 這個特殊目標的依賴檔案被作為中過程的檔案對待。但是這些檔案不會被刪除。這個目標沒有任何依賴檔案的含義是:將所有的檔案視為中間檔案。
.IGNORE 這個目標的依賴檔案忽略建立這個檔案所執行命令的錯誤,給此目標指定命令是沒有意義的。當此目標沒有依賴檔案時,將忽略所有命令執行的錯誤。
.DELETE_ON_ERROR: 如果在 Makefile 中存在特殊的目標 ".DELETE_ON_ERROR" ,make 在執行過程中,榮國規則的命令執行錯誤,將刪除已經被修改的目標檔案。
.LOW_RESOLUTION_TIME: 這個目標的依賴檔案被 make 認為是低解析度時間戳檔案,給這個目標指定命令是沒有意義的。通常的目標都是高解析度時間戳。
.SILENT: 出現在此目標 ".SILENT" 的依賴檔案列表中的檔案,make 在建立這些檔案時,不打印出此檔案所執行的命令。同樣,給目標 "SILENT" 指定命令列是沒有意義的。
.EXPORT_ALL_VARIABLES: 此目標應該作為一個簡單的沒有依賴的目標,它的功能是將之後的所有變數傳遞給子 make 程序。
.NOTPARALLEL: Makefile 中如果出現這個特殊目標,則所有的命令按照序列的方式執行,即使是存在 make 的命令列引數 "-j" 。但在遞迴呼叫的子make程序中,命令列可以並行執行。此目標不應該有依賴檔案,所有出現的依賴檔案將會被忽略。

參考:

  1. http://c.biancheng.net/makefile/