Makefile 自己的一點理解

原博客

https://blog.csdn.net/weixin_38391755/article/details/80380786

https://www.cnblogs.com/nosadness/p/5136652.html

Makefile是什麼?

個人理解是描述文件依賴關係的文件,make是一個解釋Makefile文件指令的命令工具

Makefile是爲了實現工程的自動編譯而存在的

關於程序的編譯

程序編譯的過程大概是

預編譯    編譯   彙編   鏈接

預編譯階段(.cpp-->.i文件)

  • 1刪除#define並做文本替換
  • 2處理預編譯命令 如 #ifndef,#if 0,#endif等
  • 3遞歸展開頭文件
  • 4刪除註釋
  • 5添加行號和文本標識符
  • 6保留#pargma編譯器處理

//pargma它的作用是設定編譯器的狀態或者是指示編譯器完成一些特定的動作

//猜想編譯階段才處理

編譯階段(.i-->.s文件)

  • 1語法檢測,函數和變量的聲明和定義
  • 2代碼的優化
  • 3生成彙編指令

彙編階段(.s-->.o文件)

將彙編代碼翻譯成二進制機器指令

鏈接階段(.o-->.exe)

鏈接階段主要是針對.o文件,無關源文件,但是.o的中間文件太多,所以我們給中間文件打個包,在windows下包叫作“庫文件”  (.lib),在linux下叫作Archive File文件,也就是.a文件


Makefile的規則

target...:prerequisites.....

command

..............


target是目標文件,可以是objectfile,也可以是執行文件,也可以是一個標籤Lablel,(一種爲目標)

prerequisite是要生成target所需的文件或目標

command就是make需要執行的命令 (任意的shell命令)

target依賴於prerequisite中的文件,prerequisite中如果有一個以上的文件更新時間晚於target,那麼command中的命令就會被執行


三個奇怪的變量

$@      目標文件

$^       所有的依賴文件

$<      第一個依賴文件
$?      表示比目標還要新的依賴文件列表


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
//cc -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 bufer.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 或者 makefile 的文件 

然後在該目錄下直接輸入命令 make ,就可以生成edit文件了,執行make clear 刪除執行文件edit和所有的中間目標文件

說明

clean不是一個文件,它只不過是一個動作的名字,冒號後面什麼都沒有,說明clean不依賴任何文件,當然也不能執行後面的command,要執行command,需要使用make clear

make命令後需要顯示指出這個lable的名字


make是如何工作的

1make命令會在當前目錄下查找 名爲 Makefile  或者 makefile

2如果找到了,它會找到文件中的第一個目標文件,並把這個文件作爲最終的目標文件,也就是例子中的edit

3 如果edit文件不存在,或者edit所依賴的後面的.o文件的修改時間要比edit這個文件晚,那麼就執行後面的command命令

4 如果edit所依賴的.o文件也存在依賴,那麼make會在當前文件中查找目標文件.o的依賴,如果找到則再生成.o文件

5 Makefile 文件裏的.c 和.h文件必須存在



make會一層一層找出文件的依賴關係,直到找到編譯出第一個目標文件,過程中如果make最終依賴的文件不存在則make直接退出

並且保存,但是command的命令和編譯是否成功make是不關心的,make只管文件的依賴性,如果我找到依賴後冒號前面的文件在執行command後還是不存在,則make報錯


Makefile中使用變量

類似於c語言裏的宏

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

insert.o search.o files.o utils.o

 

使用  $(objects)的方式來使用這個變量

 objects = main.o kbd.o command.o display.o \
             insert.osearch.o files.o utils.o 
   edit : $(objects)
           cc -o edit $(objects)
   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 $(objects)

Makefile中的自動推導

GNU的make可以自動推導文件以及文件依賴關係後面的命令,於是我們就沒有必要去在每一個.o文件後面都寫上類似的命令

kbd.o:defs.h command.h

只要make看到一個[.o]文件,它就會自動把[.c]文件加在依賴關係中,如果make找到一個a.o文件,那麼a.c就會被加入a.o的文件依賴列表,並且推導出cc -c a.c 就會被推導出來,所以我們上面的Makefile文件就可以被精簡被

  objects = main.o kbd.o command.o display.o \
             insert.o search.o files.o utils.o
 
   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)

上文中“.PHONY”表示clean是一個僞目標文件


清空目標文件的規則

每一個Makefile文件中都會定義一個清空目標文件的規則(.o和執行文件 )這樣有便於重新編譯

clean:

rm edit $(objects)

使用make clean執行

更穩健的做法

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

在rm命令前面加一個小減號的意思就是,也許某些文件會出現錯誤,但是忽略掉,繼續做後面的事情


Makefile 總述

Makefile裏主要包含

5大方面的內容

  • 1顯示規則
  • target: prerequisites
  • command
  • 顯示闡釋文件之間的依賴關係
  • 如何生成一個或者多個目標文件,要生成的文件,文件的依賴,生成命令
  • 2隱晦規則
  •   make的自動推導功能,如果.o文件作爲一個目標文件,則對應的.c文件會被自動加入目標文件的依賴列表
  • cc -c xxx.c  這個生成xxx.o 的命令make也會自動幫你推導出來
  • 3變量的定義,一般由普通字符主成,用$(xxxxxxxxx)引用,類似於c語言的宏,在Makefile文件執行的時候,變量會被替換到引用的位置上去
  • 4文件指示

                    ~~~~在Makefile文件中包含另一個Makefile文件(類似c語言的#include)

                     使用include關鍵字可以把別的Makefile包含進來

                   include<filename>      fi;lename可以是當前操作系統shell的文件格式(也可以包含路徑和通配符)

                    在include前面可以有一些空格,但是絕對不能是“tab”鍵

   如果文件都沒指定絕對路徑或者相對路徑,則默認查找順序爲   當前文件夾, 如果make執行時有指定 -I  或者 --include-dir 那麼make就會在參數指定目錄下查找,如果目錄/include(一般是:/usr/locla/bin或/usr/include)存在的話make也會去找

如果有文件沒有找到的的話,make會產生一條警告信息,但是不會馬上報錯,它會繼續載入其他文件,一旦讀取完整個Makefile文件,make會再次尋找,如果還是沒有就報錯

如果你想讓make忽略那些沒有讀取到的文件前面就加小減號    -include<filename>     等價sinclude

               ~~~~   指根據某些特殊情況,特定部分的Makefile文件生效(類似於c語言中的#if一樣)

                 ~~~~~~~~  定義一個多行命令

  • 5註釋 Makefile中只有行註釋,和shell一樣註釋用#字符,類似於c語言中的//

在Makefile中的命令必須用“tab”鍵開始


Makefile文件的命名

make命令會在當前文件夾下依次尋找

GNUmakefile,makefile,Makefile

推薦使用 Makefile,也可以添加後綴,Make.Linux,然後使用make -f Make.Linux    (-file)


 環境變量 MAKEFILES

如果你的當前環境中定義了環境變量MAKEFILES,那麼,make會把這個變量中的值做一個類似於include的動作。這個變量中的值是其它的Makefile,用空格分隔。只是,它和include不同的是,從這個環境變中引入的Makefile的“目標”不會起作用,如果環境變量中定義的文件發現錯誤,make也會不理。

但是在這裏我還是建議不要使用這個環境變量,因爲只要這個變量一被定義,那麼當你使用make時,所有的Makefile都會受到它的影響,這絕不是你想看到的。在這裏提這個事,只是爲了告訴大家,也許有時候你的Makefile出現了怪事,那麼你可以看看當前環境中有沒有定義這個變量。


make的工作方式

GNU的make的工作方式如下

  • 1 讀入所有的Makefile
  • 2 讀入被include的其他Makefile
  • 3初始化文件中的變量,僅做替換,不做展開,懶執行,只有當依賴關係確定後變量確定被使用後纔會被展開替換
  • 4推導隱晦規則,並且分析所有規則
  • 5爲所有的目標文件創建關係鏈
  • 6根據依賴關係,決定哪些目標文件需要重新生成
  • 7執行生成命令

1-5步爲第一個階段,6-7爲第二個階段。第一個階段中,如果定義的變量被使用了,那麼,make會把其展開在使用的位置。但make並不會完全馬上展開,make使用的是拖延戰術,如果變量出現在依賴關係的規則中,那麼僅當這條依賴被決定要使用了,變量纔會在其內部展開。


Makefile的書寫規則

規則1 依賴關係

規則2 生成目標的命令

規則的順序非常重要,Makefile中只有一個最終目標文件,所以讓make知道你的最終目標是什麼非常關鍵

定義在Makefile中的目標文件有很多個,但是只有在第一條規則中的target纔會被定義成最終目標,如果第一條規則中的target有多個,則第一條規則中的多個target中的第一個target纔會被當做目標文件

 


在規則中使用通配符

make支持三個通配符

“~”

~/test 當前用戶的$HOME目錄下的test目錄

~zxy/test 表示用戶zxy宿主目錄下的test目錄

windows和dos下,用戶沒有宿主目錄,所以波浪號所代指的具體目錄有環境變量HOME而定

“*”

*.c 所有後綴爲c的文件

如:“*”,那麼可以用轉義字符“\”,如“\*”來表示真實的“*”字符,而不是任意長度的字符串。

“?”

zxy?.c代表以zxy開頭和.c結尾,中間有一個字符的文件

? 匹配任意一個字符

 

通配符和正則表達式    科普鏈接

https://blog.csdn.net/zgqxiexie/article/details/51184602


Makefile中的自動化變量

變量定義:

$^

所有的依賴目標的集合。以空格分隔。如果在依賴目標中有多個重複的,那個這個變量

會去除重複的依賴目標,只保留一份。

 

$@

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

目標中模式定義的集合

 

$?

所有比目標新的依賴目標的集合。以空格分隔。

 

$<

依賴目標中的第一個目標名字。如果依賴目標是以模式(即"%")定義的,那麼"$<"將

是符合模式的一系列的文件集。注意,其是一個一個取出來的。

 

$(@D)

表示"$@"的目錄部分(不以斜槓作爲結尾) ,如果"$@"值是"dir/foo.o",那麼"$(@D)"就

是"dir",而如果"$@"中沒有包含斜槓的話,其值就是"."(當前目錄) 。

 

$(@F)

表示"$@"的文件部分,如果"$@"值是"dir/foo.o",那麼"$(@F)"就是"foo.o","$(@F)"相

當於函數"$(notdir $@)"

 

舉例詳解:

有main.c  test.c  test1.c  test2.c 四個源文件

 

例子1:

%.o : %.c

gcc  -c  $<  -o  $@

 

把所以的c文件編譯生成對應的o文件,$<代表每次取的c文件,$@代表每次c文件對應的目標文件

 

 

例子2:

main : main.o  test.o  test1.o  test2.o

gcc  -o  $@  $^

把所有的o文件編譯生成可執行的main文件,$^代表所以的依賴文件集合(main.o  test.o  test1.o  test2.o),@代表目標文件(main)

 

例子3:

lib : test.o  test1.o  test2.o

ar r lib $?

 

把有更新的依賴文件重新打包到庫lib中, 如果只有test1.o更新,則$?代表test1.o, 如果test.o  test1.o都有更新,則$?代表test.o  test1.o的集合。

 

總結:

 

$^      所有依賴目標的集合

$?      所有有更新的依賴目標集合

$<      依賴目標中的第一個目標,如果依賴以(%)模式定義,則一個一個取出來的

$@     目標文件

$(@D)   $@的目錄部分

$(@F)   $@的文件部分

 

記憶方法:

 

dst:source1.o  source2.o  source3.o  source4.o  

xx ......

 

$^    其中^表示水平的範圍限定,包含所有的依賴文件集合(source1.o  source2.o  source3.o  source4.o )

$?    其中?表示哪些依賴文件有更新是未知的,有更新的依賴文件集合(?)

$<    其中<表示從集合中取值,第一個依賴的文件 (source1.o)

$@   目標文件  (dst)

$(@D)   $@的目錄部分

$(@F)   $@的文件部分
————————————————
版權聲明:本文爲CSDN博主「丁香樹下丁香花開」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/csdn66_2016/article/details/74199681



文件搜索

引入文件搜索的原因

在一些大工程裏,有大量源文件,我們通常的做法是把源文件分類放到文件夾裏,當make需要去尋找文件的依賴關係的時候,我們可以在文件後面加上文件的搜索路徑,以提高搜索速度

方法1

引入Makefile文件中的特殊變量VPATH

VPATH=src:../headers

這個語句定義了兩個目錄 src和../headers, 

當前目錄---->src和../headers ,make會按照這個順序搜索

方法2(推薦這個方法)

引入make的關鍵字"vpath"(全部小寫),它不是一個變量,是一個make的關鍵字

更靈活,指定不同的文件在不同的目錄中搜索

它的三種方法

vpath 要搜索的文件集合      目錄:目錄

  • vpath <pattern> <directories>      爲符合模式<pattern>的文件指定搜索目錄<directories>
  • vpath <pattern>                      清除符合模式<pattern>的文件的搜索目錄
  • vpath                                       清除所有已被設置好了的文件的搜索目錄

vapth使用方法中的<pattern>需要包含  %   字符,%字符表示匹配零或者若干字符

%.h表示所有以.h結尾的文件,<pattern>指定了要搜索的文件集合,<directories>指定了文件集的搜索的目錄

vpath %.h   ../headers

該語句表示 在../headers下搜索所有以.h結尾的文件(當前文件夾依然是最高搜索級別)

我們可以連續的使用vpath語句指定文件集的搜索範圍,如果出現了相同的<pattern>或者被重複的<pattern>,那麼make會按照vpath語句的先後順序來執行搜索

vpath %.c foo

vpath % blish

vpath %.c bar

表示.c結尾的文件先在foo目錄,然後在blish,最後在bar目錄

vpath  %.c   foo:bar

vpath   %   blish  

表示.c結尾的文件,先在foo目錄,然後在bar目錄搜索,最後纔是blish目錄


僞目標

引入僞目標的原因

目標文件和僞目標重名

clean:

           rm   *.o   temp

使用make clear 觸發命令執行

 僞目標並不是一個文件,它更像是label,所以make無法生成它的依賴關係和決定他是否執行。

僞目標的取名不能和文件名重複,不然就失去了 僞目標 的意義了 。我們爲了避免這種情況的發生引入了一個特殊標記

“.PHONY”,使用.PHONY來表明目標是一個僞目標,不管是否有這個文件,這個目標就是僞目標

.PHONY : clean

clean: 

          rm *.o temp

僞目標一般沒有依賴的文件,但是我們也可以爲僞目標指定所依賴的文件。僞目標同樣可以作爲“默認目標”

 

 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新,所以這三個目標總是會被  被迫執行

,這個我們就實現了一次生成多個目標的需求

 

隨便提一句,從上面的例子我們可以看出,目標也可以成爲依賴。所以,僞目標同樣也可成爲依賴。看下面的例子:

   .PHONY: cleanall cleanobj cleandiff

 

   cleanall : cleanobj cleandiff

           rm program

 

   cleanobj :

           rm *.o

 

   cleandiff :

           rm *.diff

“makeclean”將清除所有要被清除的文件。“cleanobj”和“cleandiff”這兩個僞目標有點像“子程序”的意思。我們可以輸入“makecleanall”和“make cleanobj”和“makecleandiff”命令來達到清除不同種類文件的目的



多目標

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

其中-$要執行一個Makefile的函數,函數名爲subst,後面是參數,這個函數是截取字符串的意思

$@表示目標的集合,就像一個數組,$@依次取出目標,並執行命令。



靜態模式

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

          <commands>

 

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

target-pattern指定的是目標集的模式

prereq-patterns  它是對target-pattern這類target所需要的依賴文件的文件集的一個定義

許多targets---》target-pattern符合某類特殊pattern的target集合----》這類特定的targets集合所依賴的文件的集合

 

 

 

 

 

 

 

發佈了33 篇原創文章 · 獲贊 5 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章