目錄
一、Makefile是啥?
1.1、了解幾種文件(.o 文件和.c文件 )
1.2、關于Makefile的寫法
1.3、簡單使用Makefile基本指令
1.4、引入偽目標
1.5、Makefile的優點
1.6、Makefile的使用
二、Makefile創建和使用變量
2.1、創建變量的目的
2.2、自動變量的使用
2.3、Makefile即時變量、延時變量
2.3、Makefile通配符
2.4、Makefile的常用函數
一、Makefile是啥?
在 Linux 中使用?make?命令來編譯程序,特別是大程序;而 make 命令所執行的動作依賴于?Makefile?文件。最簡單的?Makefile?文件如下:
1.1、了解幾種文件(.o 文件和.c文件 )
.o文件是由.c文件編譯得到的,一個可執行程序里面不可能只含有一個.c文件,里面肯定包含著很多其他子文件,所以我們編譯得到的程序就是將很多個.c文件編譯得到.o文件,然后再將很多個.o文件聯合起來變成一個可執行程序!所以Makefile就是一個很好的工具!
那怎么將一個.c文件編譯成一個.o文件呢?
gcc -c f1.c -o f1.o
f1.c:想要編譯的.c文件
f1.o:想要編譯成的.o文件
那怎么將一堆.o文件聯合編譯成一個可執行程序呢?
gcc f1.o f2.o main.o -o test
將f1.o,f2.o,main.o編譯成一個可執行文件test
1.2、關于Makefile的寫法
一個文件里面想要使用make指令來編譯程序,那么里面就一定要含有一個文件(命名為Makefile或者makefile),M大小寫都可以,我個人喜歡大寫,看起來更好看,后面提到的關于這個名詞,都會用Makefile來講。
Makefile里面的內容格式:
注意:這里的TAB鍵不可以用四個空格來代替!!!
例:
f2.o:f2.cgcc -c f2.c -o f2.o
1.3、簡單使用Makefile基本指令
我們來實現一個簡單的程序,關于程序內容:
f1.c:實現簡單printf1打印函數
f2.c:實現簡單printf2打印函數
head.h:聲明printf1和printf2函數
main.c:添加head頭文件,使用printf1和printf2函數
做完以上的準備工作后,我們就來編寫一個簡單的Makefile:
根據格式:
我們的Makefile:
test:f1.o f2.o main.ogcc f1.o f2.o main.o -o test
f1.o:f1.cgcc -c f1.c -o f1.o
f2.o:f2.cgcc -c f2.c -o f2.o
main.o:main.cgcc -c main.c -o main.o
然后使用make就可以編譯得到我們的可執行程序test了
然后我們就可以運行:./test
假如我們不使用make指令,我們可以更有目的性的去編譯:
例:只想要編譯出f1.o文件
1.4、引入偽目標
繼續在上面的測驗來實現:
我們完善一下我們的Makefile,修改為:
test:f1.o f2.o main.ogcc f1.o f2.o main.o -o test
f1.o:f1.cgcc -c f1.c -o f1.o
f2.o:f2.cgcc -c f2.c -o f2.o
main.o:main.cgcc -c main.c -o main.oclean:rm *.o test
有前面可以知道:
make f1.o就是:
那么我們make clean不就是輸入了rm *.o test指令嗎?
事實確實是這樣的!
我們看規律來看,都是make一個目標文件,假如我們的程序里面真的有一個clean文件呢?那會怎么樣呢?
我們不就用不了這個簡單的一次性刪除所有.o文件和可執行程序(或者其他便捷指令)了嗎?所以我們要引入偽目標:
這樣就不會互相干擾了!
1.5、Makefile的優點
按照這樣的編譯,我們就可以寫一個Makefile然后使用make指令即可編譯,不用我們一條條去編譯,假如我們編寫了一個程序,里面有著非常復雜的代碼和繁多的文件,就算用Makefile這么牛的工具也要編譯個一個半個小時,編譯成功之后,突然想起來一個Bug,但是只需要修改一個底層文件即可,那么修改后又得重新編譯,還是要這么多時間嗎?
這就要說到Makefile的優點了,Makefile會根據文件的時間戳來發現更新過的文件,減少編譯工具量!
例:
第一次編譯時:(會把全部文件指令都執行一遍)
假如我們的f1.c文件需要修改一下Bug,我們后來修改了一下f1.c文件(其實沒有修改,這里使用了touch指令更新了一下f1.c的時間戳代替f1.c被修改):
這里只執行有關于f1.c更新的Makefile指令,可見Makefile的強大!
1.6、Makefile的使用
二、Makefile創建和使用變量
2.1、創建變量的目的
創建變量的目的就是用來代替一個文本字符串。
比如:
1、用于代替系列文件的名字
2、代替傳遞個編譯器的參數
3、需要運行的程序。。。。。。。等等等等
這里就簡單來使用一下變量。(還是基于上面的Makefile來修改)
定義一個變量,以及使用變量:|
變量定義:OBJS=man.o out.o
變量使用:$(OBJS)
test:f1.o f2.o main.ogcc f1.o f2.o main.o -o test
f1.o:f1.cgcc -c f1.c -o f1.o
f2.o:f2.cgcc -c f2.c -o f2.o
main.o:main.cgcc -c main.c -o main.o.PHONY:clean
clean:rm *.o test
簡單使用變量優化Makefile:(這樣修改工具鏈的時候就很方便了!)
OBJS=f1.o f2.o main.o
CC=gcctest:$(OBJS)$(CC) f1.o f2.o main.o -o test
f1.o:f1.c$(CC)-c f1.c -o f1.o
f2.o:f2.c$(CC)-c f2.c -o f2.o
main.o:main.c$(CC)-c main.c -o main.o.PHONY:clean
clean:rm *.o test
2.2、自動變量的使用
$@:表示規則中的目標文件名。
$<:表示規則中的第一個依賴文件名。
$^:表示規則中的所有依賴文件名,以空格分隔。
$?:表示比目標文件更新的所有依賴文件名,以空格分隔。
$(@D):表示目標文件所在的目錄名。
$(@F):表示目標文件的文件名(不包含路徑)。
$+:表示規則中的所有依賴文件名,以空格分隔,并以出現的先后順序為序。
例:(依然是在上面的Makefile修改優化)
OBJS=f1.o f2.o main.o
CC=gcctest:$(OBJS)$(CC) $^ -o $@
f1.o:f1.cgcc -c $< -o $@
f2.o:f2.cgcc -c $^ -o $@
main.o:main.cgcc -c $^ -o $@.PHONY:clean
clean:rm *.o test
2.3、Makefile即時變量、延時變量
語法如下:
A = xxx // 延時變量
B ?= xxx // 延時變量,只有第一次定義時賦值才成功;如果曾定義過,此賦值無效
C := xxx // 立即變量
D += yyy // 如果 D 在前面是延時變量,那么現在它還是延時變量;
一個例子理解即時變量、延時變量:
A = $@
test:@echo $A
上述 Makefile 中,變量 A 的值在執行時才確定,它等于 test,是延時變量。如
果使用“A :=?@”,這是立即變量,這時@”,這是立即變量,這時@為空,所以 A 的值就是空。
OBJS=f1.o f2.o
OBJS+=main.o
此時OBJS=f1.o f2.o main.o
2.3、Makefile通配符
在 Makefile 中,通配符用于匹配文件名或文件路徑中的多個字符,以便在規則中批量處理文件。通配符常見的有以下幾種:
*:匹配零個或多個字符。
?:匹配一個任意字符。
[…]:匹配方括號內的任意一個字符。
[!..]:匹配除了方括號內的字符之外的任意一個字符。
%:%,o&%,c表示任意的.o和.c文件
在?Makefile?中,通配符通常與模式規則結合使用,以便自動化地處理一類文件。
例:
常用通配符使用:
program: *.cgcc $^ -o $@%.o:%.cgcc -c $^ -o $@
在這個?Makefile?中,==*.c ==通配符匹配當前目錄下所有以 .c 結尾的文件。$ ^表示所有匹配到的文件,$@ 表示目標文件。因此,program: *.c 規則會將所有的 .c 文件編譯鏈接成一個名為 program 的可執行文件。
2.4、Makefile的常用函數
函數調用的格式如下:
$(function arguments)
1、$(foreach var, list, text)
對 list 中的每一個元素,取出來賦給 var,然后把 var 改為 text 所描述的形式。
例:
objs := a.o b.o
dep_files := $(foreach f,?(𝑜𝑏𝑗𝑠),.(objs),.(f).d) // 最終 dep_files := .a.o.d .b.o.d
objs := a.o b.o
dep_files := $(foreach f, $(objs), $(f).s) // 最終 dep_files := a.o.s b.o.s
2、$(wildcard pattern)
pattern 所列出的文件是否存在,把存在的文件都列出來。
src_files := $( wildcard *.c) // 最終 src_files 中列出了當前目錄下的所有.c 文件
3、$(subst from,to,text)
在文本text’中使用
to’替換每一處`from’。
比如:$(subst ee, EE, feet on the street)
結果為‘fEEt on the strEEt’。
4、$(patsubst pattern,replacement,text)
尋找text’中符合格式
pattern’的字,用replacement’替換它們。?
pattern’和`replacement’中可以使用通配符。
比如:$(patsubst %.c,%.o,x.c.c bar.c)
結果為:`x.c.o bar.o’。
5、$(strip string)
去掉前導和結尾空格,并將中間的多個空格壓縮為單個空格。
比如:$(strip a b c )
結果為`a b c’。
6、$(findstring find,in)
在字符串in’中搜尋
find’,如果找到,則返回值是`find’,否則返回值為空。
將分別產生值a’和
’(空字符串)。
7、$(filter pattern…,text)
返回在text’中由空格隔開且匹配格式
pattern…’的字,去除不符合格式`pattern…’的字。
比如:$(filter %.c %.s,foo.c bar.c baz.s ugh.h)
結果為`foo.c bar.c baz.s’。
8、$(filter-out pattern…,text)
返回在text’中由空格隔開且不匹配格式
pattern…’的字,去除符合格式`pattern…’的字。它是函數 filter 的反函數。
比如:$(filter %.c %.s,foo.c bar.c baz.s ugh.h)
結果為`ugh.h’。
9、$(sort list)
將‘list’中的字按字母順序排序,并去掉重復的字。輸出由單個空格隔開的字的列表。
比如:$(sort foo bar lose)
返回值是‘bar foo lose’。