Makefile入門

1.Makefile概述:

  什麼是makefile?或許不少Winodws的程序員都不知道這個東西,由於那些Windows的IDE都爲你作了這個工做,但我以爲要做一個好的和professional的程序員,makefile仍是要懂。這就好像如今有這麼多的HTML的編輯器,但若是你想成爲一個專業人士,你仍是要了解HTML的標識的含義。特別在Unix下的軟件編譯,你就不能不本身寫makefile了,會不會寫makefile,從一個側面說明了一我的是否具有完成大型工程的能力。c++

  由於,makefile關係到了整個工程的編譯規則。一個工程中的源文件不計數,其按類型、功能、模塊分別放在若干個目錄中,makefile定義了一系列的規則來指定,哪些文件須要先編譯,哪些文件須要後編譯,哪些文件須要從新編譯,甚至於進行更復雜的功能操做,由於makefile就像一個Shell腳本同樣,其中也能夠執行操做系統的命令。程序員

  makefile帶來的好處就是——「自動化編譯」,一旦寫好,只須要一個make命令,整個工程徹底自動編譯,極大的提升了軟件開發的效率。make是一個命令工具,是一個解釋makefile中指令的命令工具,通常來講,大多數的IDE都有這個命令,好比:Delphi的make,Visual C++的nmake,Linux下GNU的make。可見,makefile都成爲了一種在工程方面的編譯方法。編輯器

  在這篇文檔中,將以C/C++的源碼做爲咱們基礎,因此必然涉及一些關於C/C++的編譯的知識,相關於這方面的內容,還請各位查看相關的編譯器的文檔。這裏所默認的編譯器是UNIX下的GCC和CC。ide

2.Makefile規則(顯示規則, 隱晦規則, 變量定義, 文件指示, 註釋)

   2.1Makefile基本編寫格式:

target ... : prerequisites ...
    command
    ...
    ...

  target:    目標文件,能夠是Object File,也能夠是執行文件。還能夠是一個標籤(Label)工具

  prerequisites: 要生成那個target所依賴的文件或是目標。優化

  command:   也就是make須要執行的命令。(任意的Shell命令)ui

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

  2.2編寫規則說明:

  1. 顯示規則 :: 說明如何生成一個或多個目標文件(包括 生成的文件, 文件的依賴文件, 生成的命令)
  2. 隱晦規則 :: make的自動推導功能所執行的規則($@ $* $^ ...)
  3. 變量定義 :: Makefile中定義的變量
  4. 文件指示 ::其包括了三個部分,一個是在一個Makefile中引用另外一個Makefile,就像C語言中的include同樣;另外一個是指根據某些狀況指定Makefile中的有效部分,就像C語言  中的預編譯#if同樣;還有就是定義一個多行的命令。有關這一部分的內容,我會在後續的部分中講述。
  5. 註釋     :: Makefile只有行註釋 "#", 若是要使用或者輸出"#"字符, 須要進行轉義, "\#"

  簡單Makefile展現以下:操作系統

include ../Make.defines

PROGS =    test01 \
           test02

all:    ${PROGS}
test01:    test01.o
        ${CC} ${CFLAGS} -o $@ test01.o ${LIBS}
test02:    test02.o
        ${CC} ${CFLAGS} -o $@ test02.o ${LIBS}
        
clean:
        rm -f ${PROGS} ${CLEANFILES}

   事例中幾點注意以下:命令行

  1.首行導入了其餘的Makefile相關文件,事例假定此文件爲二級目錄,導入文件爲一級目錄下的Make.define文件

  2.第二行顯示定義了變量PROGS, test01後面的 \爲換行符,在參數較多時方便代碼查看。

  3.all表示一個標籤,在此例中表示全部目標,即${PROGS}所指定的全部執行文件。clean同理。

  4.命令行必定要以TAB開頭(Makefile規定)

  5.命令行定義中使用了隱晦規則,$@表示目標文件,在此例中即指代test01或test02.

  6.命令行中的變量均爲Make.define文件中定義,表示編譯所用的參數
  

3.Makefile工做方式

  爲方便說明,簡化上述Makefile以下:

include ../Make.defines

test01:    test01.o
        ${CC} ${CFLAGS} -o $@ test01.o ${LIBS}
        
clean:
        rm -f ${PROGS} ${CLEANFILES}

  在默認的方式下,在make命令後:

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

 4.Makefile中一些雜項說明

  4.1 文件包含

  Makefile支持文件包含功能,相似於c/c++中的#include功能,最開始的例子已經使用,實例模型能夠參考。其具體使用方法介紹以下:

  include <filename>        #  filename能夠是當前操做系統Shell的文件模式(能夠保含路徑和通配符)

   在include前面能夠有一些空字符,可是毫不能是[Tab]鍵開始。include和<filename>;能夠用一個或多個空格隔開。舉個例子,你有這樣幾個Makefile:a.mk、b.mk、c.mk,還有一個文件叫foo.make,以及一個變量$(bar),其包含了e.mk和f.mk,那麼,下面的語句:

    include foo.make *.mk $(bar)

    等價於:

    include foo.make a.mk b.mk c.mk e.mk f.mk

  make命令開始時,會把找尋include所指出的其它Makefile,並把其內容安置在當前的位置。就好像C/C++的#include指令同樣。若是文件都沒有指定絕對路徑或是相對路徑的話,make會在當前目錄下首先尋找,若是當前目錄下沒有找到,那麼,make還會在下面的幾個目錄下找:

    一、若是make執行時,有「-I」或「--include-dir」參數,那麼make就會在這個參數所指定的目錄下去尋找。
    二、若是目錄<prefix>;/include(通常是:/usr/local/bin或/usr/include)存在的話,make也會去找。

若是有文件沒有找到的話,make會生成一條警告信息,但不會立刻出現致命錯誤。它會繼續載入其它的文件,一旦完成makefile的讀取,make會再重試這些沒有找到,或是不能讀取的文件,若是仍是不行,make纔會出現一條致命信息。若是你想讓make不理那些沒法讀取的文件,而繼續執行,你能夠在include前加一個減號「-」。如:

    -include <filename>;
    其表示,不管include過程當中出現什麼錯誤,都不要報錯繼續執行。和其它版本make兼容的相關命令是sinclude,其做用和這一個是同樣的。

  4.2 幾種賦值符號差別

  Makefile中賦值符號中四種(=  :=  ?= +=),區別以下:

  1. = 是最基本的賦值,他會將整個Makefile展開後,將最後一個值付給變量。

  x = foo
    y = $(x) bar
    x = xyz

    在上例中,y的值將會是 xyz bar ,而不是 foo bar 。

  2. := 是覆蓋以前的值,無需所有展開,在賦值處直接覆蓋。

   x := foo
   y := $(x) bar
   x := xyz

   在上例中,y的值將會是 foo bar ,而不是 xyz bar 了。

  3. ?= 是若是沒有被賦值過就賦予等號後面的值(容易理解)
  4. += 是添加等號後面的值(字面含義,直接追加)

  4.3 編譯參數整理

  4.3.1: GCC參數整理:  

  在Makefile編譯命令行中(即commend),第一個例子的${CCFLAG}. 變量中定義了一些編譯器須要處理的工做(如:自動導出頭文件包含 -M; 添加GDB調試:-g; 優化等級:-o ~ -o2), gcc選項以下

  用法:gcc [選項] 文件...
  選項:
    -pass-exit-codes         在某一階段退出時返回最高的錯誤碼
    --help                   顯示此幫助說明
    --target-help            顯示目標機器特定的命令行選項
    (使用‘-v --help’顯示子進程的命令行參數)
    -dumpspecs               顯示全部內建 spec 字符串
    -dumpversion             顯示編譯器的版本號
    -dumpmachine             顯示編譯器的目標處理器
    -print-search-dirs       顯示編譯器的搜索路徑
    -print-libgcc-file-name  顯示編譯器伴隨庫的名稱
    -print-file-name=<庫>    顯示 <庫> 的完整路徑
    -print-prog-name=<程序>  顯示編譯器組件 <程序> 的完整路徑
    -print-multi-directory   顯示不一樣版本 libgcc 的根目錄
    -print-multi-lib         顯示命令行選項和多個版本庫搜索路徑間的映射
    -print-multi-os-directory 顯示操做系統庫的相對路徑
    -Wa,<選項>               將逗號分隔的 <選項> 傳遞給彙編器
   -Wp,<選項>               將逗號分隔的 <選項> 傳遞給預處理器
    -Wl,<選項>               將逗號分隔的 <選項> 傳遞給連接器

    -Wall         打開全部編譯警告
    -Xassembler <參數>       將 <參數> 傳遞給彙編器
    -Xpreprocessor <參數>    將 <參數> 傳遞給預處理器
    -Xlinker <參數>          將 <參數> 傳遞給連接器
    -combine                 將多個源文件一次性傳遞給彙編器
    -save-temps              不刪除中間文件
    -pipe                    使用管道代替臨時文件
    -time                    爲每一個子進程計時
    -specs=<文件>            用 <文件> 的內容覆蓋內建的 specs 文件
    -std=<標準>              指定輸入源文件遵循的標準
    --sysroot=<目錄>         將 <目錄> 做爲頭文件和庫文件的根目錄
    -B <目錄>                將 <目錄> 添加到編譯器的搜索路徑中
    -b <機器>                爲 gcc 指定目標機器(若是有安裝)
    -V <版本>                運行指定版本的 gcc(若是有安裝)
    -v                       顯示編譯器調用的程序
    -###                     與 -v 相似,但選項被引號括住,而且不執行命令
    -E                       僅做預處理,不進行編譯、彙編和連接
    -S                       編譯到彙編語言,不進行彙編和連接
    -c                       編譯、彙編到目標代碼,不進行連接
    -o <文件>                輸出到 <文件>
    -x <語言>                指定其後輸入文件的語言容許的語言包括:c c++ assembler none
                           ‘none’意味着恢復默認行爲,即根據文件的擴展名猜想
                           源文件的語言

  以 -g、-f、-m、-O、-W 或 --param 開頭的選項將由 gcc 自動傳遞給其調用的不一樣子進程。若要向這些進程傳遞其餘選項,必須使用 -W<字母> 選項。

  4.3.2: make參數整理(摘自跟我一塊兒寫Makefile)

下面列舉了全部GNU make 3.80版的參數定義。其它版本和產商的make大同小異,不過其它產商的make的具體參數仍是請參考各自的產品文檔。

「-b」
「-m」
這兩個參數的做用是忽略和其它版本make的兼容性。

「-B」
「--always-make」
認爲全部的目標都須要更新(重編譯)。

「-C <dir>;」
「--directory=<dir>;」
指定讀取makefile的目錄。若是有多個「-C」參數,make的解釋是後面的路徑之前面的做爲相對路徑,並以最後的目錄做爲被指定目錄。如:「make –C ~hchen/test –C prog」等價於「make –C ~hchen/test/prog」。

「—debug[=<options>;]」
輸出make的調試信息。它有幾種不一樣的級別可供選擇,若是沒有參數,那就是輸出最簡單的調試信息。下面是<options>;的取值:
    a —— 也就是all,輸出全部的調試信息。(會很是的多)
    b —— 也就是basic,只輸出簡單的調試信息。即輸出不須要重編譯的目標。
    v —— 也就是verbose,在b選項的級別之上。輸出的信息包括哪一個makefile被解析,不須要被重編譯的依賴文件(或是依賴目標)等。
    i —— 也就是implicit,輸出因此的隱含規則。
    j —— 也就是jobs,輸出執行規則中命令的詳細信息,如命令的PID、返回碼等。
    m —— 也就是makefile,輸出make讀取makefile,更新makefile,執行makefile的信息。

「-d」
至關於「--debug=a」。

「-e」
「--environment-overrides」
指明環境變量的值覆蓋makefile中定義的變量的值。

「-f=<file>;」
「--file=<file>;」
「--makefile=<file>;」
指定須要執行的makefile。

「-h」
「--help」
顯示幫助信息。

「-i」
「--ignore-errors」
在執行時忽略全部的錯誤。

「-I <dir>;」
「--include-dir=<dir>;」
指定一個被包含makefile的搜索目標。可使用多個「-I」參數來指定多個目錄。

「-j [<jobsnum>;]」
「--jobs[=<jobsnum>;]」
指同時運行命令的個數。若是沒有這個參數,make運行命令時能運行多少就運行多少。若是有一個以上的「-j」參數,那麼僅最後一個「-j」纔是有效的。(注意這個參數在MS-DOS中是無用的)

「-k」
「--keep-going」
出錯也不中止運行。若是生成一個目標失敗了,那麼依賴於其上的目標就不會被執行了。

「-l <load>;」
「--load-average[=<load]」
「—max-load[=<load>;]」
指定make運行命令的負載。

「-n」
「--just-print」
「--dry-run」
「--recon」
僅輸出執行過程當中的命令序列,但並不執行。

「-o <file>;」
「--old-file=<file>;」
「--assume-old=<file>;」
不從新生成的指定的<file>;,即便這個目標的依賴文件新於它。

「-p」
「--print-data-base」
輸出makefile中的全部數據,包括全部的規則和變量。這個參數會讓一個簡單的makefile都會輸出一堆信息。若是你只是想輸出信息而不想執行makefile,你可使用「make -qp」命令。若是你想查看執行makefile前的預設變量和規則,你可使用「make –p –f /dev/null」。這個參數輸出的信息會包含着你的makefile文件的文件名和行號,因此,用這個參數來調試你的makefile會是頗有用的,特別是當你的環境變量很複雜的時候。

「-q」
「--question」
不運行命令,也不輸出。僅僅是檢查所指定的目標是否須要更新。若是是0則說明要更新,若是是2則說明有錯誤發生。

「-r」
「--no-builtin-rules」
禁止make使用任何隱含規則。

「-R」
「--no-builtin-variabes」
禁止make使用任何做用於變量上的隱含規則。

「-s」
「--silent」
「--quiet」
在命令運行時不輸出命令的輸出。

「-S」
「--no-keep-going」
「--stop」
取消「-k」選項的做用。由於有些時候,make的選項是從環境變量「MAKEFLAGS」中繼承下來的。因此你能夠在命令行中使用這個參數來讓環境變量中的「-k」選項失效。

「-t」
「--touch」
至關於UNIX的touch命令,只是把目標的修改日期變成最新的,也就是阻止生成目標的命令運行。

「-v」
「--version」
輸出make程序的版本、版權等關於make的信息。

「-w」
「--print-directory」
輸出運行makefile以前和以後的信息。這個參數對於跟蹤嵌套式調用make時頗有用。

「--no-print-directory」
禁止「-w」選項。

「-W <file>;」
「--what-if=<file>;」
「--new-file=<file>;」
「--assume-file=<file>;」
假定目標<file>;須要更新,若是和「-n」選項使用,那麼這個參數會輸出該目標更新時的運行動做。若是沒有「-n」那麼就像運行UNIX的「touch」命令同樣,使得<file>;的修改時間爲當前時間。

「--warn-undefined-variables」
只要make發現有未定義的變量,那麼就輸出警告信息。

  4.4 Makefile規則檢查

  有時候,咱們不想讓咱們的makefile中的規則執行起來,咱們只想檢查一下咱們的命令,或是執行的序列。因而咱們可使用make命令的下述參數:     「-n」     「--just-print」     「--dry-run」     「--recon」     不執行參數,這些參數只是打印命令,無論目標是否更新,把規則和連帶規則下的命令打印出來,但不執行,這些參數對於咱們調試makefile頗有用處。     「-t」     「--touch」     這個參數的意思就是把目標文件的時間更新,但不更改目標文件。也就是說,make僞裝編譯目標,但不是真正的編譯目標,只是把目標變成已編譯過的狀態。     「-q」     「--question」     這個參數的行爲是找目標的意思,也就是說,若是目標存在,那麼其什麼也不會輸出,固然也不會執行編譯,若是目標不存在,其會打印出一條出錯信息。     「-W <file>;」     「--what-if=<file>;」     「--assume-new=<file>;」     「--new-file=<file>;」     這個參數須要指定一個文件。通常是是源文件(或依賴文件),Make會根據規則推導來運行依賴於這個文件的命令,通常來講,能夠和「-n」參數一同使用,來查看這個依賴文件所發生的規則命令。

相關文章
相關標籤/搜索