Makefile其實不難學,對於一些基本概念百度上應該很多,這裏分享一個循序漸進的學習方式,保證讓你快速掌握Makefile的編寫。
本文正選於公眾號: 良許Linux ,裏面有一個 Makefile 系列,歡迎關註交流!
另外,想進大廠的同學,僅僅學會Makefile是不夠的,一定要好好學演算法,這是面試必備的。這裏準備了一份 BAT 大佬總結的 LeetCode 刷題寶典,很多人靠它們進了大廠。
1. 前言
透過之前章節的學習,我們對Makefile有個基礎的認識,現在開始自己動手寫Makefile。
目前網絡上有不少可以自動生成Makefile的工具,但很多專案其實沒必要那麽復雜,完全可以自己動手寫出來。
而且對於初學者來說,自己動手寫一遍Makefile可以頂看十遍高手寫的Makefile,也可以加深對Makefile的理解,將來公司的Makefile有需要修改的時候自己就可以動手搞定,不需要依靠他人,何樂而不為?
2. 原始碼介紹
在本教程中用於範例的程式碼很簡單,僅僅是在main函數中呼叫了fun1及fun2函數,而fun1及fun2獨立寫在fun1.c及fun2.c裏。程式碼如下:
//main.c
int main()
{
printf("hello world\n");
fun1();
fun2();
}
//fun1.c
void fun1()
{
printf("this is fun1\n");
}
//fun2.c
void fun2()
{
printf("this is fun2\n");
}
3. 第一版Makefile
對於我們的範例程式碼,不透過Makefile編譯其實也很簡單:
gcc main.c fun1.c fun2.c -o app
我們知道,Makefile其實就是按規則一條條的執行。所以,我們完全可以把上面那條命令寫成Makefile的一個規則。我們的目標是app,按此寫法依賴是main.c fun1.c fun2.c,則最終的Makefile如下:
app: main.c fun1.c fun2.c
gcc main.c fun1.c fun2.c -o app
但這個版本的Makefile有兩個很重要的不足:
- 對於簡單程式碼還好,而對於大型專案,具有成千上萬程式碼來說,僅用一行規則是完全不夠的,即使夠的話也需要寫很長的一條規則;
- 任何檔只要稍微做了修改就需要整個專案完整的重要編譯。
基於此,我們在第一版的基礎上最佳化出第二版。
4. 第二版Makefile
在第二版Makefile中,為了避免改動任何程式碼就需要重新編譯整個專案的問題,我們將主規則的各個依賴替換成各自的中間檔,即main.c --> main.o,fun1.c --> fun1.o,fun2.c --> fun2.o,再對每個中間檔的生成各自寫條規則比如對於main.o,規則為:
main.o: main.c
• gcc -c main.c -o main.o
這樣做的好處是,當有一個檔發生改動時,只需重新編譯此檔即可,而無需重新編譯整個專案。完整Makefile如下:
app: main.o fun1.o fun2.o
gcc main.o fun1.o fun2.o -o app
main.o: main.c
gcc -c main.c -o main.o
fun1.o: fun1.c
gcc -c fun1.c -o fun1.o
fun2.o: fun2.c
gcc -c fun2.c -o fun2.o
第二版Makefile同樣具有一些缺陷:
- 裏面存在一些重復的內容,可以考慮用變量代替;
- 後面三條規則非常類似,可以考慮用一條模式規則代替。
基於此,我們在第二版的基礎上最佳化出第三版。
5. 第三版Makefile
在第三版Makefile中,我們使用變量及模式規則使Makefile更加簡潔。使用的三個變量如下:
obj = main.o fun1.o fun2.o
target = app
CC = gcc
使用的模式規則為:
%.o: %.c
• $(CC) -c $< -o $@
這條模式規則表示:所有的.o檔都由對應的.c檔生成。在規則裏,我們又看到了兩個自動變量:$<和$@。其實自動變量有很多,常用的有三個:
$<:第一個依賴檔;
$@:目標;
$^:所有不重復的依賴檔,以空格分開
obj = main.o fun1.o fun2.o
target = app
CC = gcc
$(target): $(obj)
$(CC) $(obj) -o $(target)
%.o: %.c
$(CC) -c $< -o $@
第三版Makefile依然存在一些缺陷:
- obj對應的檔需要一個個輸入,工作量大;
2. 檔數目比較少時還好,檔數目一旦很多的話,obj將很長;
3. 而且每增加/刪除一個檔,都需要修改Makefile。
基於此,我們在第二版的基礎上最佳化出第四版。
6. 第四版Makefile
在第四版Makefile中,我們隆重推出了兩個函數: wildcard 和 patsubst 。
wildcard:
擴充套件通配符,搜尋指定檔。在此我們使用src = $(wildcard ./*.c),代表在當前目錄下搜尋所有的.c檔,並賦值給src。函數執行結束後,src的值為:main.c fun1.c fun2.c。
patsubst:
替換通配符,按指定規則做替換。在此我們使用
obj = $(patsubst %.c, %.o, $(src))
代表將src裏的每個檔都由.c替換成.o。函數執行結束後,obj的值為main.o fun1.o fun2.o,其實跟第三版Makefile的obj值一模一樣,只不過在這裏它更智能一些,也更靈活。
除了使用patsubst函數外,我們也可以使用模式規則達到同樣的效果,比如:
obj = $(src:%.c=%.o)
也是代表將src裏的每個檔都由.c替換成.o。
幾乎每個Makefile裏都會有一個偽目標clean,這樣我們透過執行make clean命令就是將中間檔如.o檔及目標文件全部刪除,留下幹凈的空間。一般是如下寫法:
.PHONY: clean
clean:
• rm -rf $(obj) $(target)
.PHONY代表聲明clean是一個偽目標,這樣每次執行make clean時,下面的規則都會被執行。
src = $(wildcard ./*.c)
obj = $(patsubst %.c, %.o, $(src))
#obj = $(src:%.c=%.o)
target = app
CC = gcc
$(target): $(obj)
$(CC) $(obj) -o $(target)
%.o: %.c
$(CC) -c $< -o $@
.PHONY: clean
clean:
rm -rf $(obj) $(target)
7. 總結
Makefile其實也並不難,但關鍵的是一定要自己動手寫,這樣才會更加加深理解,否則也容易造成眼高手低。如果實在不知道從何下手,可以嘗試按上面的教程,一步步寫下來,也只需要寫四個版本而已,寫完了相信就有了初步的理解。
學習編程,千萬不要急於求成,一定要多讀一些經典書籍,多看源碼,多下苦功夫去死磕程式碼,這樣技術才能長進。給大家分享一些程式設計師必讀經典書籍,一定要多讀幾遍:
對應書單:
附:近期高贊回答
Linux的功能有多強大?
學習Linux有沒有比【鳥哥的Linux私房菜】更好的書?
有沒有學習Linux比較好的入門書籍?
碼字不易,硬核碼字更難,希望大家不要吝嗇自己的鼓勵。我是:
@程式設計師良許
歡迎關註我!
我的個人區域網絡站,滿滿的都是Linux幹貨:良許Linux教程網
如果本文對你有幫助,歡迎點贊、收藏、轉發給朋友,讓我有持續創作的動力!