很久以前寫的一篇技術(shù)備忘了,隱約記得當(dāng)時(shí)是看了APUE 2nd Edition這本書后,下載的源碼在CentOS7以及MacOX10.X上都編譯不過。在解決編譯問題的同時(shí),被APUE源碼中的MakeFile中的天書給震驚后,學(xué)習(xí)了一些相關(guān)入門知識(shí)。并以看懂并向后來的好奇寶寶們解釋APUE的MakeFile含義作為小目標(biāo),記錄了這篇文章。
1. MakeFile的基礎(chǔ)格式
target...:prerequisites
command
...
target 就是我們需要生成的目標(biāo)文件??梢允强蓤?zhí)行文件,也可以是object file中間文件,還可以是其他的標(biāo)簽
prerequisites 就是target所依賴的文件
command 就是生成target需要執(zhí)行的命令,特別注意的是所有的shell command都可以在這里執(zhí)行
下面就是一個(gè)最樸實(shí)的MakeFile文件,沒有使用任何技巧
all:foo
foo:hello.o world.o
gcc -o foo hello.o world.o -I.
hello.o:hello.c hello.h world.h
gcc -c hello.c
world.o:world.c world.h
gcc -c world.c
clean:
rm hello.o world.o
2. 隱晦(高級(jí))規(guī)則
我們可以給需要編譯的這些文件名稱用一個(gè)變量來代替,有點(diǎn)像常量的使用方法一樣
其實(shí)我們看到的那些開源代碼的Makefile用了很多隱晦規(guī)則
比如test.o一般來說對(duì)應(yīng)的源文件就是test.c,并且調(diào)用編譯器生成[.o]文件也是固定的。那么我們就不用像上面那樣這樣寫了,而是像下面一樣省略
objects=hello.o world.o
all:foo
foo:$(objects)
gcc -o foo $(objects)
hello.o:hello.h world.h
world.o:world.h
.PHONY:clean
clean:
rm $(objects)
注釋和shell腳本一樣用#號(hào),記得命令必須以tab開頭
命令前面加一個(gè)-減號(hào)是告訴make忽略這條命令可能引起的錯(cuò)誤
代表了$HOME目錄,fengzhao代表了某位
自動(dòng)化變量
$? 所有比target更新的prerequisites,以空格間隔,如下所示
print:*.c
lpr -p $?
特別注意的是,make為變量賦值一個(gè)含有通配符的變量需要用上wildcard函數(shù),否則通配符就不會(huì)被解釋
objects=.o #objects真的就只是.o而已
objects:=$(wildcard *.o)#objects會(huì)用通配符去展開這個(gè)變量得到當(dāng)前目錄下面的中間文件
:= 和 = 的區(qū)別是
A=$(B)
B=hello.c world.c#上一行還沒有定義B,但是用=已經(jīng)可以引用到后面定義的B,然后給A賦值
B:=hello.c world.c
A:=$(B)#用:=進(jìn)行必須引用已經(jīng)定義了的變量
==?=== 的意思是 hello?=world 如果hello沒有定義的話,它就等于world。如果定義了的,那么什么都不做
3. 多個(gè)target的語法
target可以是多個(gè)
靜態(tài)模式的形式如下
target:target-pattern:prereq-pattern
command
...
舉個(gè)例子來說明
objects=hello.o world.o
$(objects):%.o:%.c
$(CC) -c $(CFLAGS) $< -o $@
第二行的意思就是目標(biāo)是objects里面那些[.o]文件,把xxx.o中的xxx取出來,找到的xxx.c就是其依賴的文件
==$<== 表示的 prereq-pattern 指代的文件,在這里就是 hello.c world.c
==$@== 表示的 target 指代的文件,在這里就是 hello.o world.o
再看一個(gè)加入了filter函數(shù)的例子
objects=hello.o foo.elc world.o
$(filter %.o, $(objects)):%.o:%.c
$(CC) -c $(CFLAGS) $< -o $@
$(filter %.elc, $(objects)):%.elc:%el
emacs -f batch-byte-compile $<
這里的filter就是一個(gè)make build-in函數(shù)。函數(shù)名和參數(shù)用空格分隔,參數(shù)之間用,分隔
source=hello.c world.c
include ($source:.c=.d)
這個(gè)語法的意思是用.d來替換source中的.c(.c必須是結(jié)尾處的),然后形成新的新的集合
source=hello.c world.c
include ($source:%.c=%.d)
該語法的效果同上,只不過用的是靜態(tài)規(guī)則
在命了前面加上 @ 可以讓命令不顯示出來
如果要執(zhí)行多條(行)命令,且后面的命令依賴前面的命令,需要在命令之間加上 ; 號(hào),并且寫在一行
==$^== 表示 prereq 指代的文件集合并且去重
==$*== 表示模式 % 符號(hào)前面的部分
x=var1
var2=Hello
y=$(subst,1,2,$(x))
這里的函數(shù)subst是把$(x)中的1全部替換成2,所以y最后等于Hello
4. 其它更老的規(guī)則
模式變量
prog:CFLAGS=-g
目標(biāo)變量
%.o:CFLAGS=-O
這兩種變量都是,當(dāng)且僅當(dāng)特定的目標(biāo)需要被執(zhí)行打時(shí)候,用設(shè)置的變量取代環(huán)境變量的值
后綴規(guī)則,其實(shí)就是老式的隱含規(guī)則
.c.o:
$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<
庫文件的目標(biāo)是
helloworld.a(hello.o world.o):hello.o world.o
ar cr helloworld.a hello.o world.o
看APUE的make發(fā)現(xiàn)它的makefile寫的那叫一個(gè)簡單,能用隱含規(guī)則的全部用上了
比如 all: helloworld 就這么一句,合理推測(cè)的話,make會(huì)去自動(dòng)尋找helloworld.c并去調(diào)用對(duì)應(yīng)compile規(guī)則
比如 objs: hello.o world.o 也是一句,道理同上
還需要注意的是
編譯 .c 文件到可執(zhí)行文件的隱含規(guī)則的名字叫
COMPILE.c=$(CC) $(CFLAGS) $(CPPFLAGS) -c
鏈接 .c 文件到obj文件的隱含規(guī)則的名字叫
LINK.c=$(CC) $(CFLAGS) $(CPPFLAGS) $(LDDIR) $(LDFLAGS)
沒想到看了上面這么多的內(nèi)容,APUE的makefile還是遇到了小挑戰(zhàn),不過思考后還是解決了
GCC
gcc -c hello.c //將只生成 hello.o
鏈接
- ar 命令的作用是把obj文件,打包成一個(gè) .a 庫文件,ar rv libtest.a hello.o fibo.o 便于其他的程序鏈接 libtest.a。其他程序通過 -ltest 就可以鏈接到這個(gè)庫
- nm 命令的作用是查看 ar 命令打包好的 .a 庫文件里面由哪些符號(hào)構(gòu)成