目錄
- 一、自動化構建
- 二、make/Makefile
- 2.1 見識一個簡單的make/Makefile
- 2.2 Makefile的基本語法
- 2.3 Makefile的語法細節
個人主頁<—請點擊
Linux專欄<—請點擊
一、自動化構建
自動化構建是指通過構建工具(如make
)解析構建腳本(如Makefile
),自動執行一系列步驟(編譯、鏈接等)以將源代碼生成可執行程序或庫的過程。
想象一下你要編譯一個大型C++
項目,比如一個游戲引擎,它有成千上萬個源文件.cpp
和頭文件.h
。
如果沒有自動化構建,你會怎么做?你可能會在終端里輸入一條巨長的命令:g++ main.cpp renderer.cpp physics.cpp audio.cpp network.cpp ...(列出所有文件) -o MyGameEngine -lSDL2 -lOpenGL ...
,這種方法效率低下、容易出錯、無法復用,并且依賴管理困難。自動化構建就是為了解決這些問題而生的。
所以會不會寫Makefile
,從側面說明了一個人是否具備完成大型工程的能力。在Windows
下vs
幫我們做了自動化構建,而在Linux
下我們需要使用make/Makefile
完成對自動化項目的構建。
上述我們所說的make
和Makefile
,其中前者是一條命令,而后者是一個文件。后者也可以寫成makefile
。
二、make/Makefile
2.1 見識一個簡單的make/Makefile
現在我們在我們的當前目錄下準備了一份文件code.c
,里面寫了一些代碼,現在我要使用make
對它進行編譯。我們開始創建Makefile
文件,并在其中寫入以下指令:
我們執行make
命令對code.c
進行編譯。
如上圖,make
命令自動執行了我在Makefile
文件中寫入的指令生成了可執行文件!
那么Makefile
中寫的那兩行究竟是什么意思呢?接著往下看。
如上圖,形成code.exe
目標文件需要依賴code.c
文件,而解決依賴關系需要依賴下面的依賴方法gcc -o code.exe code.c
,所以依賴關系和依賴方法共同構成了形成可執行程序的語義。
2.2 Makefile的基本語法
其中上面的 依賴方法一行必須以Tab鍵
開頭,且對應的依賴方法可以是多條,接下來我們依據上期博客講解的編譯的詳細過程進行編寫Makefile
文件。注意:Makefile
里面想要注釋是#
。
執行make
命令:
如上圖,make
命令會解析Makefile
文件,解析過程中會形成推導棧,這個棧中是依賴方法的集合,直到找到存在的依賴文件時就會執行相應的依賴方法,然后依次出棧,就形成了上圖中依次運行的指令。如果make
推導失敗了它就會報錯。
Makefile
其實就是一種腳本語言,make
要從最終目標code.exe
開始,遞歸地向下檢查依賴,再向上決定是否構建。這個過程是深度優先的。
當然我們實際的寫法可不會這樣寫,太麻煩了。
接下來我們學習一個新的語法,我們在vs
下不僅要能夠生成可執行程序,我們還需要能夠清理呀,所以接下來清理可執行程序。
其中.PHONY
的作用是聲明一個符號clean
是偽目標。 clean
的依賴方法是清理可執行程序,這樣就可以清理了。
從上面clean
的執行操作,我們可以知道 make
命令后面可以直接跟“目標名” 。
如上圖,make
命令后面跟誰就會解析誰的依賴關系和依賴方法,并且make
命令默認只會推導一條完整的鏈路。
現在我把.PHONY:clean
部分放在前面,再執行make
命令。
所以單獨執行make
指令,默認只推導第一個依賴關系對應的推導鏈。
.PHONY
:用來修飾一個目標文件是偽目標,這個我們剛講過。偽目標的本質是總是被執行的。像我們的形成code.exe
的命令通常情況下只能被執行一次。
而當我們對code.exe
進行修飾后就可以一直執行了。
不建議.exe
被.PHONY
修飾,這樣能夠加快編譯的效率,而且當你的源文件再次被修改后,就又可以進行編譯操作了。
那么無法二次編譯老項目是如何做到的呢? 答案是通過比較創建時間的新舊。
在Linux
下有一個stat
命令,可以查看文件的相關時間信息。
當源文件code.c
文件內容被更改時間比code.exe
的文件內容被更改時間新,make
就允許進行重新編譯。而被.PHONY
修飾能夠總是執行,是讓make
工具忽略了時間對比。
擴充:上圖中,文件內容被更改時Modify
時間會更改,文件屬性被更改時Change
時間會更改,文件被訪問特定次數后,Access
時間會更改。
2.3 Makefile的語法細節
我們在了解基本語法的時候,我們發現編譯、清理的過程,make
會將執行命令回顯出來,為了不讓它回顯出來,可以在命令前加上@
。
現在假設我要對生成的文件名進行修改,我還要一個名稱一個名稱更改,太麻煩了吧,所以我們使用變量對Makefile
做出大的更改!
如上圖所示,我對Makefile
文件進行了更改,接下來我們逐行分析。首先定義了兩個變量名BIN
和SRC
分別代表目標文件和源文件,注意=
兩側不要加空格。下面的$()
的作用相當于提取變量名對應的文件。第五行中的$@
和$^
是自動變量,前者代表目標文件,后者代表依賴文件。
很好,現在我們能夠編譯一個源文件了,那如果源文件有100
個呢?
如上圖,創建了100
個源文件,現在我們要將他們編譯性成一個可執行程序。我們要按照下圖的方式進行編譯:
首先,如何獲取這100
個源文件就是一個問題,一個個寫嗎? 當然不行,這里我們可以使用shell
命令ls
進行獲取,或者使用Makefile
中自帶的wildcard
函數獲取。
這就是兩種獲取源文件的方法,第一個是執行ls
命令將列出的源文件提取寫入到SRC
中,第二個是使用指定函數獲取源文件。
接下來完成Makefile
的編寫。
為了獲取.o
文件,我們還要定義一個變量OBJ
,其中SRC:.c=.o
是將SRC
的所有同名.c
替換成為.o
形成目標文件列表。接下來又對gcc、rm -rf、echo
命令進行了包裝。然后在編譯的過程將每個文件的.c
變.o
的過程和.o
變可執行程序的過程使用echo
打印了出來。
另外%.c
展開當前目錄下所有的.c
,%.o
: 同時展開當前目錄下所有的.o
。gcc -c code.c -o code.o
這個命令的-o code.o
部分也可以去掉,所以就變成了上圖中這樣,%<
是將展開的依賴.c
文件,?個?個的交給gcc
。
如上圖執行make
命令,它就自動將100
個源文件先編譯成.o
,然后再進行鏈接編譯成了可執行程序code.exe
。我們試著運行一下。
如上圖再make clean
它就自動將形成的.o
及可執行程序清除了!
總結:
以上就是本期博客分享的全部內容啦!如果覺得文章還不錯的話可以三連支持一下,你的支持就是我前進最大的動力!
技術的探索永無止境! 道阻且長,行則將至!后續我會給大家帶來更多優質博客內容,歡迎關注我的CSDN賬號,我們一同成長!
(~ ̄▽ ̄)~