makefile 學習筆記

 

參考資料:陳浩,《跟我一塊兒寫makefile》 :http://blog.csdn.net/haoel/article/details/2886/算法

 

Makefile的格式和規則shell

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

target是目標文件,Object File或者可執行文件,或者標籤編程

prerequisites是生成target依賴的全部文件。編程語言

command是make須要執行的命令。(任意的Shell命令)ide

須要注意的是command前必須是一個[Tab]。函數

這是個文件的依賴關係:生成target依賴於prerequisites 裏面的文件,生成規則由command給出。實際上,Makefile中最核心的內容是,當prerequisites 中有文件比target新,就執行command中的指令。測試

 

make的自動推導

make識別到一個XXX.o時,能夠自動將XXX.c加入到依賴文件中,而且cc –o XXX.c 也能推導出來。所以在書寫makefile的時候能夠省去那些能夠由makefile推導出來的東西。好比以上的makefile簡化以後:ui

objects = main.o kbd.o command.o display.o /
              insert.o search.o files.o utils.ospa

    edit : $(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)

能夠看出,每個XXX.o的依賴文件只列出了頭文件,下面的command也省略了,由於這兩個東西均可以被make自動推導出來。

 

clean規則

每個makefile都應該有一個清理規則,clean老是應該放在makefile的最後部分。通常爲:

        clean:
            rm edit $(objects)

更好的作法是:

.PHONY : clean
        clean :
                -rm edit $(objects)

.PHONY代表clean是一個僞目標,-rm前面的減號意思是當對某些文件操做失敗時,繼續後面的操做。

 

make的工做流程

    讀入全部的Makefile。
    讀入被include的其它Makefile。
    初始化文件中的變量。
    推導隱晦規則,並分析全部規則。
    爲全部的目標文件建立依賴關係鏈。
    根據依賴關係,決定哪些目標要從新生成。
    執行生成命令。

makefile的最開始部分應該是make的最終目標,通常狀況下一個makefile都只有一個最終目標。

          makefile的文件搜尋

          當工程的文件放在不一樣的目錄時,寫makefile時須要指定搜尋的目錄和規則。

          VPATH = ….

          makefile中有一個特殊變量VPATH,經過指定VPATH告訴make在VPATH所指的目錄尋找依賴的文件(固然是在當前目錄沒有找到的前提下),多個目錄用冒號隔開。

          vpath <pattern> <directories>

          <pattern>須要包含「%」字符。「%」的意思是匹配零或若干字符,例如,「%.h」表示全部以「.h」結尾的文件。<pattern>指定了要搜索的文件集,而<directories>則指定了<pattern>的文件集的搜索的目錄,多個目錄用冒號隔開。

          vpath和上面的VPATH並不相同,vpath是makefile中的關鍵字,它能夠指定不一樣的文件在不一樣的搜索目錄中,好比:

          vpath %.h ../headers

          表示在../headers目錄搜索.h頭文件。

           

          僞目標

          前面提到過僞目標,僞目標其實和真正的目標區別不大,最大的區別就是僞目標只是一個便籤,不生成真正的目標文件。咱們可使用一個特殊的標記「.PHONY」來顯示地指明一個目標是「僞目標」,向make說明,無論是否有這個文件,這個目標就是「僞目標」。如:

          .PHONY clean

          僞目標能夠很是有用。以前咱們說過,makefile通常只有一個最終目標,就是其第一個目標,若是想一次性生成多個目標怎麼辦呢?這就能夠藉助於僞目標來實現。好比:

          all : prog1 prog2 prog3
              .PHONY : all

              prog1 : prog1.o utils.o
                      cc -o prog1 prog1.o utils.o

              prog2 : prog2.o
                      cc -o prog2 prog2.o

              prog3 : prog3.o sort.o utils.o
                      cc -o prog3 prog3.o sort.o utils.o

          這裏的最終目標是一個僞目標all,在執行make 時候僞目標老是會被執行,所以依賴的三個文件會被生成。

          再好比,咱們在清理的時候可能須要執行不一樣級別的清理,那麼也能夠用僞目標來區分。

           

          多目標

          上文提到要生成多個目標時候可使用僞目標來實現,然而實際上makefile的target能夠不止一個。有些狀況下,這一組目標有某個或者某些相同的依賴文件並且生成的規則類似,那麼就可使用makefile的多目標來生成。如:

            bigoutput littleoutput : text.g
                      generate text.g -$(subst output,,$@) > $@

          bigoutput : text.g
                      generate text.g -big > bigoutput
              littleoutput : text.g
                      generate text.g -little > littleoutput

          左邊的規則與右邊的等價。簡單解釋一下,因爲bigoutput和littleoutput都依賴文件text.g,且生成規則類似,則寫成下面一行的命令形式。其中-$(subst output,,$@)中的」$」表示要執行一個makefile函數,函數名是subst,後面都是參數。「$@」表示目標的集合。這個函數的做用是截取字符串。

          在定義多目標規則時,利用靜態模式將更加有用。靜態模式的基本語法是:

          <targets ...>: <target-pattern>: <prereq-patterns ...>
                      <commands>
                      ...

          targets定義了一系列的目標文件,能夠有通配符。是目標的一個集合。

          target-parrtern是指明瞭targets的模式,也就是的目標集模式。

          prereq-parrterns是目標的依賴模式,它對target-parrtern造成的模式再進行一次依賴目標的定義。

          換一種更加通俗的說法,target定義的是一個目標的集合,裏面包含了不少的目標,而target-pattern定義的是本次命令執行的目標模式,也就是說從target目標集合裏選出匹配target-pattern模式的目標來做爲本次命令的真正目標。而prereq-patterns定義的是依賴文件的匹配模式。舉個簡單的例子,若是target-pattern爲「%.o」,那麼目標集合就是target中全部以[.o]爲後綴的目標文件。而若是prereq-patterns定義爲「%.c」,因而依賴的文件就是target-pattern匹配的全部的[.o]目標文件對應的[.c]文件。

          一個例子:

              objects = foo.o bar.o

              all: $(objects)

              $(objects): %.o: %.c
                      $(CC) -c $(CFLAGS) $< -o $@

          由以前的分析能夠看出,target-pattern爲[%.o]匹配了objects中全部的[.o]目標文件,即foo.o bar.o,而[%.c]代表命令的依賴文件是foo.c bar.c。命令中的[$<]表示全部的依賴目標集(也就是foo.c bar.c),[$@]表示目標集(也就是foo.o bar.o)。

          自動生成依賴性

           

           

           

          makefile的變量

          makefile的變量在定義時須要賦給一個初值,使用時前面要有一個$符合,通常的使用慣例是「${}」或「$()」,若要使用真正的「$」符號則表示爲「$$」。一個簡單的例子:

          edit : main.o kbd.o command.o display.o /
                     insert.o search.o files.o utils.o
                      cc -o edit main.o kbd.o command.o display.o /
                                 insert.o search.o files.o utils.o

              main.o : main.c defs.h
                      cc -c main.c
              kbd.o : kbd.c defs.h command.h
                      cc -c kbd.c
              command.o : command.c defs.h command.h
                      cc -c command.c
              display.o : display.c defs.h buffer.h
                      cc -c display.c
              insert.o : insert.c defs.h buffer.h
                      cc -c insert.c
              search.o : search.c defs.h buffer.h
                      cc -c search.c
              files.o : files.c defs.h buffer.h command.h
                      cc -c files.c
              utils.o : utils.c defs.h
                      cc -c utils.c
              clean :
                      rm edit main.o kbd.o command.o display.o /
                         insert.o search.o files.o utils.o

           

          這個makefile的edit依賴文件不少,能夠用一個變量來代替它。

          objects = main.o kbd.o command.o display.o /
                        insert.o search.o files.o utils.o

          因而,第一行就能夠變爲:

            edit : $(objects)
                      cc -o edit $(objects)

          這樣爲書寫makefile,以及在大的工程中修改makefile帶了很大的便利,也讓makefile更加簡潔易懂。makefile中的目標、依賴、命令均可以用變量來表示。makefile定義變量的方法有三種,一種是像一般的編程語言同樣使用相似以下的方式。這種方式定義變量的好處在於能夠將變量的定義放在後面,換句話說,前面的命令可使用後面定義的變量。

          objects = main.o kbd.o command.o display.o

          另一種變量定義方式是使用「:=」操做符,這種定義方式不能使用後面定義的變量。例如:

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

          等價於

              y := foo bar
              x := later

          而,

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

          卻等價於

              y := bar
              x := foo

          還有一種用於定義變量的操做符「?=」,

          x ?= foo

          表示若是x沒有定義過,那麼x = foo,不然什麼也不作。

           

          變量值的替換的例子:

              foo := a.o b.o c.o
              bar := $(foo:.o=.c)

             foo := a.o b.o c.o
              bar := $(foo:%.o=%.c)

          上面兩種表示的效果是同樣的——變量替換,讓$(bar)變量的值爲「a.c b.c c.c」,可是有一點點不一樣。左邊$(foo:.o=.c)表示的是,變量foo中全部.o結尾的字符串,替換成.c結尾的字符串。右邊$(foo:%.o=%.c)這種形式是靜態模式的表示形式,變量foo中匹配模式%.o的替換成了%.c,%是必需的。

          咱們可使用「+=」操做符給變量追加值,即增長一個值。

              objects = main.o foo.o bar.o utils.o
              objects += another.o

           

          define關鍵字能夠用來定義多行變量,如:

              define two-lines
              echo foo
              echo $(bar)
              endef

          以define開頭,以endef結尾。命令前面必需有[Tab],至關於
          two-lines = foo $(bar)

           

          有一種很是有用的變量叫目標變量,定義語法和舉例以下:

          <target ...> : <variable-assignment>

          <target ...> : overide <variable-assignment>

              prog : CFLAGS = -g
              prog : prog.o foo.o bar.o
                      $(CC) $(CFLAGS) prog.o foo.o bar.o

              prog.o : prog.c
                      $(CC) $(CFLAGS) prog.c

              foo.o : foo.c
                      $(CC) $(CFLAGS) foo.c

              bar.o : bar.c
                      $(CC) $(CFLAGS) bar.c

          當設置了目標變量時,這個變量會做用到由這個目標所引起的全部的規則中去。如上例,prog : CFLAGS = -g中定義了目標變量CFLAGS,所以在全部由prog引發的命令中,這個CFLAGS都會起做用。target還能夠用模式變量。如:

          <pattern ...> : <variable-assignment>

          <pattern ...> : override <variable-assignment>

              %.o : CFLAGS = -O

           

          條件判斷

            libs_for_gcc = -lgnu
              normal_libs =

              foo: $(objects)
              ifeq ($(CC),gcc)
                      $(CC) -o foo $(objects) $(libs_for_gcc)
              else
                      $(CC) -o foo $(objects) $(normal_libs)
              endif

          條件判斷由ifeq開始,後面括號裏是進行比較的兩個變量或者常量,ifeq ($(CC),gcc)表示若是變量CC是gcc的話,執行下一條命令,不然(else)執行else下的命令,判斷由endif結束。實際上,ifeq能夠由下面幾種方式來使用,而上例使用的是第一種方式。

              ifeq (<arg1>, <arg2>)
              ifeq '<arg1>' '<arg2>'
              ifeq "<arg1>" "<arg2>"
              ifeq "<arg1>" '<arg2>'
              ifeq '<arg1>' "<arg2>"

          另一個條件判斷關鍵字是ifneq,意思很明白,和ifeq的剛好相反,可是用法相同。

          第三個條件關鍵字是「ifdef」,

              ifdef <variable-name>

          若是變量<variable-name>的值非空,那到表達式爲真。不然,表達式爲假。實際上,ifdef就是測試變量是否有值。另外還有一個與之對應的ifndef,意思很是明確就很少說了。

          在條件判斷中最好不要出現自動化變量(如」$@」),由於自動化變量在運行時纔會有值。

           

          makefile的函數

          實際上在以前的例子中已經屢次提到過makefile的函數了。固然這個函數和咱們程序語言的函數有相同點也有很大的不一樣,makefile函數調用後有返回值,並且咱們可使用這個返回值,可是makefile的函數只有那麼多,不能由用戶本身來定義,不過也夠用了。函數的調用語法以下:

              $(<function> <arguments1,arguments2,arguments3>)  或者使用{}括號

          <function>是函數名,後面是參數列表,函數名和參數之間用空格隔開,參數之間用逗號隔開。

          字符串處理函數:

          $(subst <from>,<to>,<text>)

              名稱:字符串替換函數——subst。
              功能:把字串<text>中的<from>字符串替換成<to>。
              返回:函數返回被替換事後的字符串。

           

          $(patsubst <pattern>,<replacement>,<text>)

              名稱:模式字符串替換函數——patsubst。
              功能:查找<text>中的單詞(單詞以「空格」、「Tab」或  「回車」「換行」分隔)是否符合模式<pattern>,若是匹配的話,則以<replacement>替換。

              返回:函數返回被替換事後的字符串。

           

          $(strip <string>)

              名稱:去空格函數——strip。
              功能:去掉<string>字串中開頭和結尾的空字符。
              返回:返回被去掉空格的字符串值。

           

          $(findstring <find>,<in>)

              名稱:查找字符串函數——findstring。
              功能:在字串<in>中查找<find>字串。
              返回:若是找到,那麼返回<find>,不然返回空字符串。

           

          $(filter <pattern...>,<text>)

              名稱:過濾函數——filter。
              功能:以<pattern>模式過濾<text>字符串中的單詞,保留符合模式<pattern>的單詞。能夠有多個模式。
              返回:返回符合模式<pattern>的字串。

           

          $(filter-out <pattern...>,<text>)

              名稱:反過濾函數——filter-out。
              功能:以<pattern>模式過濾<text>字符串中的單詞,去除符合模式<pattern>的單詞。能夠有多個模式。
              返回:返回不符合模式<pattern>的字串。

           

          $(sort <list>)

              名稱:排序函數——sort。
              功能:給字符串<list>中的單詞排序(升序)。
              返回:返回排序後的字符串。

              備註:sort函數會去掉<list>中相同的單詞。

           

          $(word <n>,<text>)

              名稱:取單詞函數——word。
              功能:取字符串<text>中第<n>個單詞。(從一開始)
              返回:返回字符串<text>中第<n>個單詞。若是<n>比<text>中的單詞數要大,那麼返回空字符串。

           

          $(wordlist <s>,<e>,<text>)

              名稱:取單詞串函數——wordlist。
              功能:從字符串<text>中取從<s>開始到<e>的單詞串。<s>和<e>是一個數字。
              返回:返回字符串<text>中從<s>到<e>的單詞字串。若是<s>比<text>中的單詞數要大,那麼返回空字符串。若是<e>大於<text>的單詞數,那麼返回從<s>開始,到<text>結束的單詞串。

           

          $(words <text>)

              名稱:單詞個數統計函數——words。
              功能:統計<text>中字符串中的單詞個數。
              返回:返回<text>中的單詞數。

              備註:若是咱們要取<text>中最後的一個單詞,咱們能夠這樣:$(word $(words <text>),<text>)。

           

          $(firstword <text>)

              名稱:首單詞函數——firstword。
              功能:取字符串<text>中的第一個單詞。
              返回:返回字符串<text>的第一個單詞。
              備註:這個函數能夠用word函數來實現:$(word 1,<text>)。

           

          文件名操做函數:

          $(dir <names...>)

              名稱:取目錄函數——dir。
              功能:從文件名序列<names>中取出目錄部分。目錄部分是指最後一個反斜槓(「/」)以前的部分。若是沒有反斜槓,那麼返回「./」。
              返回:返回文件名序列<names>的目錄部分。

          $(notdir <names...>)

              名稱:取文件函數——notdir。
              功能:從文件名序列<names>中取出非目錄部分。非目錄部分是指最後一個反斜槓(「/」)以後的部分。
              返回:返回文件名序列<names>的非目錄部分。

          $(suffix <names...>)

              名稱:取後綴函數——suffix。
              功能:從文件名序列<names>中取出各個文件名的後綴。
              返回:返回文件名序列<names>的後綴序列,若是文件沒有後綴,則返回空字串。

          $(basename <names...>)

              名稱:取前綴函數——basename。
              功能:從文件名序列<names>中取出各個文件名的前綴部分。
              返回:返回文件名序列<names>的前綴序列,若是文件沒有前綴,則返回空字串

          $(addsuffix <suffix>,<names...>)

              名稱:加後綴函數——addsuffix。
              功能:把後綴<suffix>加到<names>中的每一個單詞後面。
              返回:返回加事後綴的文件名序列。

           

          $(addprefix <prefix>,<names...>)

              名稱:加前綴函數——addprefix。
              功能:把前綴<prefix>加到<names>中的每一個單詞後面。
              返回:返回加過前綴的文件名序列。

           

          $(join <list1>,<list2>)

              名稱:鏈接函數——join。
              功能:把<list2>中的單詞對應地加到<list1>的單詞後面。若是<list1>的單詞個數要比<list2>的多,那麼,<list1>中的多出來的單詞將保持原樣。若是<list2>的單詞個數要比<list1>多,那麼,<list2>多出來的單詞將被複制到<list2>中。
              返回:返回鏈接事後的字符串。

           

          特殊函數:

           

          $(foreach <var>,<list>,<text>)

          foreach函數和shell中的for語句相似,用於循環控制。這個命令的解釋是:將<list>中的字符串逐一取出放入臨時變量<var>中(循環完成,<var>便不復存在),而後再執行<text>中包含的命令,最後將執行以後的結果返回。每次返回的結果由一個空格隔開,循環完成以後的返回值就是由空格隔開的一系列結果。

          例如:

          names := a b c d

          files := $(foreach n,$(names),$(n).o)

          $(name)中的單詞會被挨個取出,並存到變量「n」中,「$(n).o」每次根據「$(n)」計算出一個值,這些值以空格分隔,最後做爲foreach函數的返回,因此,$(files)的值是「a.o b.o c.o d.o」。

           

          $(if <condition>,<then-part>,<else-part>)

          if函數是一個條件控制語句,首先計算<condition>,若是<condition>返回非空字符,則計算<then-part>,不然計算<else-part>,固然和編程語言同樣<else-part>是可選的。最後函數返回計算部分的結果。

           

          $(call <expression>,<parm1>,<parm2>,<parm3>...)

          call函數計算<expression>的結果,<expression>中的變量,如$(1),$(2),$(3),會被parm1,parm2,parm3替換。最後函數返回<expression>的結果。

          例如:

          reverse = $(1) $(2)

          foo = $(call reverse,a,b)

          此時的foo的結果就是「a b」。

           

          $(origin <variable>)

          origin函數返回變量<variable>是在哪兒定義的。通常不在<variable>中使用$,由於此處指的是變量名。

          函數的返回值有幾種狀況:

           

          • 「undefined」

          若是<variable>歷來沒有定義過,origin函數返回這個值「undefined」。

          • 「default」

          若是<variable>是一個默認的定義,好比「CC」這個變量,這種變量咱們將在後面講述。

          • 「environment」

          若是<variable>是一個環境變量,而且當Makefile被執行時,「-e」參數沒有被打開。

          • 「file」

          若是<variable>這個變量被定義在Makefile中。

          • 「command line」

          若是<variable>這個變量是被命令行定義的。

          • 「override」

          若是<variable>是被override指示符從新定義的。

          • 「automatic」

          若是<variable>是一個命令運行中的自動化變量。

           

          $(shell <command> <var>)

          shell函數的做用是在makefile中調用shell命令來處理變量,返回值就是shell命令執行的結果。好比:

          files := $(shell echo *.c)

          files最後的值就是全部.c文件。

           

          make的控制函數:

          $(error <text ...>)

          產生一個致命的錯誤,<text ...>是錯誤信息,並結束make。

           

          $(warning <text ...>)

          產生一個warning信息,並繼續執行make。

           

          GNU makefile中的一些常見目標

               「all」
                  這個僞目標是全部目標的目標,其功能通常是編譯全部的目標。
               「clean」
                  這個僞目標功能是刪除全部被make建立的文件。
               「install」
                  這個僞目標功能是安裝已編譯好的程序,其實就是把目標執行文件拷貝到指定的目標中去。
               「print」
                  這個僞目標的功能是例出改變過的源文件。
               「tar」
                  這個僞目標功能是把源程序打包備份。也就是一個tar文件。
               「dist」
                  這個僞目標功能是建立一個壓縮文件,通常是把tar文件壓成Z文件。或是gz文件。
               「TAGS」
                  這個僞目標功能是更新全部的目標,以備完整地重編譯使用。
               「check」和「test」
                  這兩個僞目標通常用來測試makefile的流程。

           

          makefile的檢查規則

               「-n」
              「--just-print」
              「--dry-run」
              「--recon」
              不執行參數,這些參數只是打印命令,無論目標是否更新,把規則和連帶規則下的命令打印出來,但不執行

           

              「-t」
              「--touch」
              這個參數的意思就是把目標文件的時間更新,但不更改目標文件。也就是說,make僞裝編譯目標,但不是真正的編譯目標,只是把目標變成已編譯過的狀態。

           

              「-q」
              「--question」
              這個參數的行爲是找目標的意思,也就是說,若是目標存在,那麼其什麼也不會輸出,固然也不會執行編譯,若是目標不存在,其會打印出一條出錯信息。

           

              「-W <file>」
              「--what-if=<file>」
              「--assume-new=<file>」
              「--new-file=<file>」
              這個參數須要指定一個文件。通常是是源文件(或依賴文件),Make會根據規則推導來運行依賴於這個文件的命令,通常來講,能夠和「-n」參數一同使用,來查看這個依賴文件所發生的規則命令。

           

          make的參數

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

           

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

           

          「-C <dir>」
          「--directory=<dir>」
          指定讀取makefile的目錄。若是有多個「-C」參數,make的解釋是後面的路徑之前面的做爲相對路徑,並以最後的目錄做爲被指定目錄。

           

          「—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>]」
          指同時運行命令的個數。

           

          「-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中的全部數據,包括全部的規則和變量。

           

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

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

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

          「-S」
          「--no-keep-going」
          「--stop」
          取消「-k」選項的做用

           

          隱含規則

          一、編譯C程序的隱含規則。
          「<n>.o」的目標的依賴目標會自動推導爲「<n>.c」,而且其生成命令是「$(CC) –c $(CPPFLAGS) $(CFLAGS)」

          二、編譯C++程序的隱含規則。
          「<n>.o」的目標的依賴目標會自動推導爲「<n>.cc」或是「<n>.C」,而且其生成命令是「$(CXX) –c $(CPPFLAGS) $(CFLAGS)」。(建議使用「.cc」做爲C++源文件的後綴,而不是「.C」)

          七、彙編和彙編預處理的隱含規則。
          「<n>.o」 的目標的依賴目標會自動推導爲「<n>.s」,默認使用編譯品「as」,而且其生成命令是:「$(AS) $(ASFLAGS)」。「<n>.s」 的目標的依賴目標會自動推導爲「<n>.S」,默認使用C預編譯器「cpp」,而且其生成命令是:「$(AS) $(ASFLAGS)」。

          八、連接Object文件的隱含規則。
          「<n>」目標依賴於「<n>.o」,經過運行C的編譯器來運行連接程序生成(通常是「ld」),其生成命令是:「$(CC) $(LDFLAGS) <n>.o $(LOADLIBES) $(LDLIBS)」。這個規則對於只有一個源文件的工程有效,同時也對多個Object文件(由不一樣的源文件生成)的也有效。

           

          隱含規則使用的變量:

          一、關於命令的變量。

          AR
              函數庫打包程序。默認命令是「ar」。
          AS
              彙編語言編譯程序。默認命令是「as」。
          CC
              C語言編譯程序。默認命令是「cc」。
          CXX
              C++語言編譯程序。默認命令是「g++」。
          CO
              從 RCS文件中擴展文件程序。默認命令是「co」。
          CPP
              C程序的預處理器(輸出是標準輸出設備)。默認命令是「$(CC) –E」。

          RM
              刪除文件命令。默認命令是「rm –f」。

          ARFLAGS
              函數庫打包程序AR命令的參數。默認值是「rv」。
          ASFLAGS
              彙編語言編譯器參數。(當明顯地調用「.s」或「.S」文件時)。
          CFLAGS
              C語言編譯器參數。
          CXXFLAGS
              C++語言編譯器參數。

          CPPFLAGS
              C預處理器參數。( C 和 Fortran 編譯器也會用到)。

          LDFLAGS
              連接器參數。(如:「ld」)

           

          自動化變量

          所謂自動化變量,就是這種變量會把模式中所定義的一系列的文件自動地挨個取出,直至全部的符合模式的文件都取完了。這種自動化變量只應出如今規則的命令中。

           

          $@
              表示規則中的目標文件集。在模式規則中,若是有多個目標,那麼,"$@"就是匹配於目標中模式定義的集合。

          $%
              僅當目標是函數庫文件中,表示規則中的目標成員名。例如,若是一個目標是"foo.a(bar.o)",那麼,"$%"就是"bar.o","$@"就是"foo.a"。若是目標不是函數庫文件(Unix下是[.a],Windows下是[.lib]),那麼,其值爲空。

          $<
              依賴目標中的第一個目標名字。若是依賴目標是以模式(即"%")定義的,那麼"$<"將是符合模式的一系列的文件集。注意,其是一個一個取出來的。

          $?
              全部比目標新的依賴目標的集合。以空格分隔。

          $^
              全部的依賴目標的集合。以空格分隔。若是在依賴目標中有多個重複的,那個這個變量會去除重複的依賴目標,只保留一份。

          $+
              這個變量很像"$^",也是全部依賴目標的集合。只是它不去除重複的依賴目標。

          $*
             這個變量表示目標模式中"%"及其以前的部分。

          $(@D)
              表示"$@"的目錄部分(不以斜槓做爲結尾),若是"$@"值是"dir/foo.o",那麼"$(@D)"就是"dir",而若是"$@"中沒有包含斜槓的話,其值就是"."(當前目錄)。

          $(@F)
              表示"$@"的文件部分,若是"$@"值是"dir/foo.o",那麼"$(@F)"就是"foo.o","$(@F)"至關於函數"$(notdir $@)"。

          "$(*D)"
          "$(*F)"

              和上面所述的同理,也是取文件的目錄部分和文件部分。對於上面的那個例子,"$(*D)"返回"dir",而"$(*F)"返回"foo"

          "$(%D)"
          "$(%F)"

              分別表示了函數包文件成員的目錄部分和文件部分。這對於形同"archive(member)"形式的目標中的"member"中包含了不一樣的目錄頗有用。

          "$(<D)"
          "$(<F)"

              分別表示依賴文件的目錄部分和文件部分。

          "$(^D)"
          "$(^F)"

              分別表示全部依賴文件的目錄部分和文件部分。(無相同的)

          "$(+D)"
          "$(+F)"

              分別表示全部依賴文件的目錄部分和文件部分。(能夠有相同的)

          "$(?D)"
          "$(?F)"

              分別表示被更新的依賴文件的目錄部分和文件部分。

           

          隱含規則搜索算法

          好比咱們有一個目標叫 T。下面是搜索目標T的規則的算法。請注意,在下面,咱們沒有提到後綴規則,緣由是,全部的後綴規則在Makefile被載入內存時,會被轉換成模式規則。若是目標是"archive(member)"的函數庫文件模式,那麼這個算法會被運行兩次,第一次是找目標T,若是沒有找到的話,那麼進入第二次,第二次會把"member"看成T來搜索。

          一、把T的目錄部分分離出來。叫D,而剩餘部分叫N。(如:若是T是"src/foo.o",那麼,D就是"src/",N就是"foo.o")

          二、建立全部匹配於T或是N的模式規則列表。

          三、若是在模式規則列表中有匹配全部文件的模式,如"%",那麼從列表中移除其它的模式。

          四、移除列表中沒有命令的規則。

          五、對於第一個在列表中的模式規則:
              1)推導其"莖"S,S應該是T或是N匹配於模式中"%"非空的部分。
              2)計算依賴文件。把依賴文件中的"%"都替換成"莖"S。若是目標模式中沒有包含斜框字符,而把D加在第一個依賴文件的開頭。
          3)測試是否全部的依賴文件都存在或是理當存在。(若是有一個文件被定義成另一個規則的目標文件,或者是一個顯式規則的依賴文件,那麼這個文件就叫"理當存在")
              4)若是全部的依賴文件存在或是理當存在,或是就沒有依賴文件。那麼這條規則將被採用,退出該算法。

          六、若是通過第5步,沒有模式規則被找到,那麼就作更進一步的搜索。對於存在於列表中的第一個模式規則:
              1)若是規則是終止規則,那就忽略它,繼續下一條模式規則。
          2)計算依賴文件。(同第5步)
          3)測試全部的依賴文件是否存在或是理當存在。
          4)對於不存在的依賴文件,遞歸調用這個算法查找他是否能夠被隱含規則找到。
          5)若是全部的依賴文件存在或是理當存在,或是就根本沒有依賴文件。那麼這條規則被採用,退出該算法。

          七、若是沒有隱含規則可使用,查看".DEFAULT"規則,若是有,採用,把".DEFAULT"的命令給T使用。

          一旦規則被找到,就會執行其至關的命令,而此時,咱們的自動化變量的值纔會生成。

          相關文章
          相關標籤/搜索