本文章介紹了makefile跟kconfig文件,包括編譯過(guò)程與makefile編碼規(guī)則咖祭。
編譯過(guò)程:
我們?cè)谶M(jìn)行l(wèi)inux內(nèi)核配置的時(shí)候經(jīng)常會(huì)執(zhí)行make menuconfig這個(gè)命令锻弓,然后屏幕上會(huì)出現(xiàn)以下界面:
這個(gè)界面是怎么生成的呢砾赔?
跟我們經(jīng)常說(shuō)的內(nèi)核配置與與編譯又有什么關(guān)系呢?
下面我們借此來(lái)講解一下linux內(nèi)核的配置機(jī)制及其編譯過(guò)程青灼。
一暴心、配置系統(tǒng)的基本結(jié)構(gòu)
Linux內(nèi)核的配置系統(tǒng)由三個(gè)部分組成,分別是:
1杂拨、Makefile:分布在 Linux 內(nèi)核源代碼根目錄及各層目錄中酷勺,定義 Linux 內(nèi)核的編譯規(guī)則;
2扳躬、配置文件(config.in(2.4內(nèi)核脆诉,2.6內(nèi)核)):給用戶提供配置選擇的功能甚亭;
3、配置工具:包括配置命令解釋器(對(duì)配置腳本中使用的配置命令進(jìn)行解釋)和配置用戶界面(提供基于字符界面击胜、基于 Ncurses 圖形界面以及基于 Xwindows 圖形界面的用戶配置界面亏狰,各自對(duì)應(yīng)于 Make config、Make menuconfig 和 make xconfig)偶摔。
這些配置工具都是使用腳本語(yǔ)言暇唾,如 Tcl/TK、Perl 編寫的(也包含一些用 C 編寫的代碼)辰斋。本文并不是對(duì)配置系統(tǒng)本身進(jìn)行分析策州,而是介紹如何使用配置系統(tǒng)。所以宫仗,除非是配置系統(tǒng)的維護(hù)者够挂,一般的內(nèi)核開發(fā)者無(wú)須了解它們的原理,只需要知道如何編寫 Makefile 和配置文件就可以藕夫。
二孽糖、makefile menuconfig過(guò)程講解
當(dāng)我們?cè)趫?zhí)行make menuconfig這個(gè)命令時(shí),系統(tǒng)到底幫我們做了哪些工作呢毅贮?
這里面一共涉及到了一下幾個(gè)文件我們來(lái)一一講解
Linux內(nèi)核根目錄下的scripts文件夾
arch/$ARCH/Kconfig文件办悟、各層目錄下的Kconfig文件
Linux內(nèi)核根目錄下的makefile文件、各層目錄下的makefile文件
Linux內(nèi)核根目錄下的的.config文件滩褥、arm/$ARCH/下的config文件
Linux內(nèi)核根目錄下的 include/generated/autoconf.h文件
1)scripts文件夾存放的是跟make menuconfig配置界面的圖形繪制相關(guān)的文件病蛉,我們作為使用者無(wú)需關(guān)心這個(gè)文件夾的內(nèi)容
2)當(dāng)我們執(zhí)行make menuconfig命令出現(xiàn)上述藍(lán)色配置界面以前,系統(tǒng)幫我們做了以下工作:
首先系統(tǒng)會(huì)讀取arch/$ARCH/目錄下的Kconfig文件生成整個(gè)配置界面選項(xiàng)(Kconfig是整個(gè)linux配置機(jī)制的核心)瑰煎,那么ARCH環(huán)境變量的值等于多少呢铺然?
它是由linux內(nèi)核根目錄下的makefile文件決定的,在makefile下有此環(huán)境變量的定義:
或者通過(guò) make ARCH=arm menuconfig命令來(lái)生成配置界面丢间,默認(rèn)生成的界面是所有參數(shù)都是沒(méi)有值的探熔。
比如教務(wù)處進(jìn)行考試驹针,考試科數(shù)可能有外語(yǔ)烘挫、語(yǔ)文、數(shù)學(xué)等科柬甥,這里相當(dāng)于我們選擇了arm科可進(jìn)行考試饮六,系統(tǒng)就會(huì)讀取arm/arm/kconfig文件生成配置選項(xiàng)(選擇了arm科的卷子),系統(tǒng)還提供了x86科苛蒲、milps科等10幾門功課的考試題
3)假設(shè)教務(wù)處比較“仁慈”卤橄,為了怕某些同學(xué)做不錯(cuò)試題,還給我們準(zhǔn)備了一份參考答案(默認(rèn)配置選項(xiàng))臂外,存放在arch/$ARCH/configs下窟扑,對(duì)于arm科來(lái)說(shuō)就是arch/arm/configs文件夾:
此文件夾中有許多選項(xiàng)喇颁,系統(tǒng)會(huì)讀取哪個(gè)呢??jī)?nèi)核默認(rèn)會(huì)讀取linux內(nèi)核根目錄下.config文件作為內(nèi)核的默認(rèn)選項(xiàng)(試題的參考答案)嚎货,我們一般會(huì)根據(jù)開發(fā)板的類型從中選取一個(gè)與我們開發(fā)板最接近的系列到Linux內(nèi)核根目錄下(選擇一個(gè)最接近的參考答案)
#cp arch/arm/configs/s3c2410_defconfig .config
4).config
假設(shè)教務(wù)處留了一個(gè)心眼橘霎,他提供的參考答案并不完全正確(.config文件與我們的板子并不是完全匹配),這時(shí)我們可以選擇直接修改.config文件然后執(zhí)行make menuconfig命令讀取新的選項(xiàng)
但是一般我們不采取這個(gè)方案殖属,我們選擇在配置界面中通過(guò)空格姐叁、esc、回車選擇某些選項(xiàng)選中或者不選中洗显,最后保存退出的時(shí)候外潜,Linux內(nèi)核會(huì)把新的選項(xiàng)(正確的參考答案)更新到.config中,此時(shí)我們可以把.config重命名為其它文件保存起來(lái)(當(dāng)你執(zhí)行make distclean時(shí)系統(tǒng)會(huì)把.config文件刪除)挠唆,以后我們?cè)倥渲脙?nèi)核時(shí)就不需要再去arch/arm/configs下考取相應(yīng)的文件了处窥,省去了重新配置的麻煩,直接將保存的.config文件復(fù)制為.config即可.
5)經(jīng)過(guò)以上兩步损搬,我們可以正確的讀取碧库、配置我們需要的界面了
那么他們?nèi)绾胃鷐akefile文件建立編譯關(guān)系呢?
當(dāng)你保存make menuconfig選項(xiàng)時(shí)巧勤,系統(tǒng)會(huì)除了會(huì)自動(dòng)更新.config外嵌灰,還會(huì)將所有的選項(xiàng)以宏的形式保存在
Linux內(nèi)核根目錄下的 include/generated/autoconf.h文件下
內(nèi)核中的源代碼就都會(huì)包含以上.h文件,跟宏的定義情況進(jìn)行條件編譯颅悉。
當(dāng)我們需要對(duì)一個(gè)文件整體選擇如是否編譯時(shí)沽瞭,還需要修改對(duì)應(yīng)的makefile文件,例如:
我們選擇是否要編譯s3c2410_ts.c這個(gè)文件時(shí)剩瓶,makefile會(huì)根據(jù)CONFIG_TOUCHSCREEN_S3C2410來(lái)決定是編譯此文件驹溃,此宏是在Kconfig文件中定義,當(dāng)我們配置完成后延曙,會(huì)出現(xiàn)在.config及autconf中豌鹤,至此,我們就完成了整個(gè)linux內(nèi)核的編譯過(guò)程枝缔。
最后我們會(huì)發(fā)現(xiàn)布疙,整個(gè)linux內(nèi)核配置過(guò)程中,留給用戶的接口其實(shí)只有各層Kconfig愿卸、makefile文件以及對(duì)應(yīng)的源文件灵临。
比如我們?nèi)绻胍o內(nèi)核增加一個(gè)功能,并且通過(guò)make menuconfig控制其聲稱過(guò)程
首先需要做的工作是:修改對(duì)應(yīng)目錄下的Kconfig文件趴荸,按照Kconfig語(yǔ)法增加對(duì)應(yīng)的選項(xiàng)儒溉;
其次執(zhí)行make menuconfig選擇編譯進(jìn)內(nèi)核或者不編譯進(jìn)內(nèi)核,或者編譯為模塊发钝,.config文件和autoconf.h文件會(huì)自動(dòng)生成顿涣;
最后修改對(duì)應(yīng)目錄下的makefile文件完成編譯選項(xiàng)的添加波闹;
最后的最后執(zhí)行make zImage命令進(jìn)行編譯。
三涛碑、具體實(shí)例
下面我們以前面做過(guò)的模塊實(shí)驗(yàn)為例舔痪,講解如何通過(guò)make menuconfig機(jī)制將前面單獨(dú)編譯的模塊編譯進(jìn)內(nèi)核或編譯為模塊
假設(shè)我已經(jīng)有了這么一個(gè)驅(qū)動(dòng):
modules.c
1.#include <linux/module.h> /*module_init()*/
2.#include <linux/kernel.h> /* printk() */
3.#include <linux/init.h> /* __init __exit */
4.
5.#define DEBUG //open debug message
6.
7.#ifdef DEBUG
8.#define PRINTK(fmt, arg...) printk(KERN_WARNING fmt, ##arg)
9.#else
10.#define PRINTK(fmt, arg...) printk(KERN_DEBUG fmt, ##arg)
11.#endif
12.
13./* Module Init & Exit function */
14.static int __init myModule_init(void)
15.{
16. /* Module init code */
17. PRINTK("myModule_init\n");
18. return 0;
19.}
20.
21.static void __exit myModule_exit(void)
22.{
23. /* Module exit code */
24. PRINTK("myModule_exit\n");
25. return;
26.}
27.
28.module_init(myModule_init);
29.module_exit(myModule_exit);
30.
31.MODULE_AUTHOR("MC"); /*模塊作者,可選*/
32.MODULE_LICENSE("GPL"); /*模塊許可證明锌唾,描述內(nèi)核模塊的許可權(quán)限锄码,必須*/
33.MODULE_DESCRIPTION("A simple Hello World Module"); /*模塊說(shuō)明,可選*/
Step1:將modules.c拷到drivers/char/目錄下(這個(gè)文件夾一般存放常見的字符驅(qū)動(dòng))
Step2: vi driver/char/Kconfig,在
config DEVKMEM后添加以下信息
config MODULES
tristate "modules device support"
default y
help
Say Y here,the modules will be build in kernel.
Say M here,the modules willbe build to modules.
Say N here,there will be nothing to be do.
Step3:make menuconfig
Device driver-character devices
[*]modules device suppor
Step4:vi driver/char/Makefile晌涕,在js-rtc后添加
obj-$(CONFIG_MODULES)+= modules.o
CONFIG_MODULES 必須跟上面的Kconfig中保持一致滋捶,系統(tǒng)會(huì)自動(dòng)添加CONFIG_前綴
modules.o必須跟你加入的.c文件名一致
最后執(zhí)行:make zImage modules就會(huì)被編譯進(jìn)內(nèi)核中
第三步:
Step3:make menuconfig
Device driver-character devices
[M]modules device suppor
把星號(hào)在配置界面通過(guò)空格改為M,最后執(zhí)行make modules余黎,在driver/char/目錄下會(huì)生成一個(gè)modules.ko文件
跟我們前面講的單獨(dú)編譯模塊效果一樣重窟,也會(huì)生成一個(gè)模塊,將它考入開發(fā)板執(zhí)行insmod moudles.ko惧财,即可將生成的模塊插入內(nèi)核使用巡扇。
###makefile 介紹
make命令執(zhí)行時(shí),需要一個(gè) makefile 文件垮衷,以告訴make命令需要怎么樣的去編譯和鏈接程序厅翔。
首先,我們用一個(gè)示例來(lái)說(shuō)明makefile的書寫規(guī)則搀突。以便給大家一個(gè)感性認(rèn)識(shí)刀闷。這個(gè)示例來(lái)源于gnu的make使用手冊(cè),在這個(gè)示例中仰迁,我們的工程有8個(gè)c文件甸昏,和3個(gè)頭文件,我們要寫一個(gè)makefile來(lái)告訴make命令如何編譯和鏈接這幾個(gè)文件徐许。我們的規(guī)則是:
1)如果這個(gè)工程沒(méi)有編譯過(guò)施蜜,那么我們的所有c文件都要編譯并被鏈接。
2)如果這個(gè)工程的某幾個(gè)c文件被修改雌隅,那么我們只編譯被修改的c文件翻默,并鏈接目標(biāo)程序。
3)如果這個(gè)工程的頭文件被改變了澄步,那么我們需要編譯引用了這幾個(gè)頭文件的c文件冰蘑,并鏈接目標(biāo)程序和泌。
只要我們的makefile寫得夠好村缸,所有的這一切,我們只用一個(gè)make命令就可以完成武氓,make命令會(huì)自動(dòng)智能地根據(jù)當(dāng)前的文件修改的情況來(lái)確定哪些文件需要重編譯梯皿,從而自己編譯所需要的文件和鏈接目標(biāo)程序仇箱。
####makefile編碼規(guī)則
######一、makefile的規(guī)則
在講述這個(gè)makefile之前东羹,還是讓我們先來(lái)粗略地看一看makefile的規(guī)則剂桥。
target ... : prerequisites ...
command
...
...
target可以是一個(gè)object file(目標(biāo)文件),也可以是一個(gè)執(zhí)行文件属提,還可以是一個(gè)標(biāo)簽(label)权逗。對(duì)于標(biāo)簽這種特性,在后續(xù)的“偽目標(biāo)”章節(jié)中會(huì)有敘述冤议。
prerequisites就是斟薇,要生成那個(gè)target所需要的文件或是目標(biāo)。
command也就是make需要執(zhí)行的命令恕酸。(任意的shell命令)
這是一個(gè)文件的依賴關(guān)系堪滨,也就是說(shuō),target這一個(gè)或多個(gè)的目標(biāo)文件依賴于prerequisites中的文件蕊温,其生成規(guī)則定義在 command中袱箱。說(shuō)白一點(diǎn)就是說(shuō),prerequisites中如果有一個(gè)以上的文件比target文件要新的話义矛,command所定義的命令就會(huì)被執(zhí)行发笔。這就是makefile的規(guī)則。也就是makefile中最核心的內(nèi)容凉翻。
說(shuō)到底筐咧,makefile的東西就是這樣一點(diǎn),好像我的這篇文檔也該結(jié)束了噪矛。呵呵量蕊。還不盡然,這是makefile的主線和核心艇挨,但要寫好一個(gè)makefile還不夠残炮,我會(huì)以后面一點(diǎn)一點(diǎn)地結(jié)合我的工作經(jīng)驗(yàn)給你慢慢道來(lái)。內(nèi)容還多著呢缩滨。:)
######二势就、一個(gè)例子
正如前面所說(shuō)的,如果一個(gè)工程有3個(gè)頭文件脉漏,和8個(gè)c文件苞冯,我們?yōu)榱送瓿汕懊嫠龅哪侨齻€(gè)規(guī)則,我們的makefile應(yīng)該是下面的這個(gè)樣子的侧巨。
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
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 main.o kbd.o command.o display.o
insert.o search.o files.o utils.o
反斜杠(\)是換行符的意思舅锄。這樣比較便于makefile的易讀。我們可以把這個(gè)內(nèi)容保存在名字為“makefile”或“Makefile” 的文件中司忱,然后在該目錄下直接輸入命令“make”就可以生成執(zhí)行文件edit皇忿。如果要?jiǎng)h除執(zhí)行文件和所有的中間目標(biāo)文件畴蹭,那么,只要簡(jiǎn)單地執(zhí)行一下 “make clean”就可以了鳍烁。
在這個(gè)makefile中叨襟,目標(biāo)文件(target)包含:執(zhí)行文件edit和中間目標(biāo)文件(*.o),依賴文件(prerequisites)就是冒號(hào)后面的那些 .c 文件和 .h文件幔荒。每一個(gè) .o 文件都有一組依賴文件糊闽,而這些 .o 文件又是執(zhí)行文件 edit 的依賴文件。依賴關(guān)系的實(shí)質(zhì)上就是說(shuō)明了目標(biāo)文件是由哪些文件生成的爹梁,換言之墓怀,目標(biāo)文件是哪些文件更新的。
在定義好依賴關(guān)系后卫键,后續(xù)的那一行定義了如何生成目標(biāo)文件的操作系統(tǒng)命令傀履,一定要以一個(gè)tab鍵作為開頭。記住莉炉,make并不管命令是怎么工作的钓账,他只管執(zhí)行所定義的命令。make會(huì)比較targets文件和prerequisites文件的修改日期絮宁,如果prerequisites文件的日期要比targets文件的日期要新梆暮,或者target不存在的話,那么绍昂,make就會(huì)執(zhí)行后續(xù)定義的命令啦粹。
這里要說(shuō)明一點(diǎn)的是,clean不是一個(gè)文件窘游,它只不過(guò)是一個(gè)動(dòng)作名字唠椭,有點(diǎn)像c語(yǔ)言中的lable一樣,其冒號(hào)后什么也沒(méi)有忍饰,那么贪嫂,make就不會(huì)自動(dòng)去找它的依賴性,也就不會(huì)自動(dòng)執(zhí)行其后所定義的命令艾蓝。要執(zhí)行其后的命令(不僅用于clean力崇,其他lable同樣適用),就要在make命令后明顯得指出這個(gè)lable的名字赢织。這樣的方法非常有用亮靴,我們可以在一個(gè)makefile中定義不用的編譯或是和編譯無(wú)關(guān)的命令,比如程序的打包于置,程序的備份茧吊,等等。
######三、make是如何工作的
在默認(rèn)的方式下饱狂,也就是我們只輸入make命令。那么宪彩,
1.make會(huì)在當(dāng)前目錄下找名字叫“Makefile”或“makefile”的文件休讳。
2.如果找到,它會(huì)找文件中的第一個(gè)目標(biāo)文件(target)尿孔,在上面的例子中俊柔,他會(huì)找到“edit”這個(gè)文件,并把這個(gè)文件作為最終的目標(biāo)文件活合。
3.如果edit文件不存在雏婶,或是edit所依賴的后面的 .o 文件的文件修改時(shí)間要比edit這個(gè)文件新,那么白指,他就會(huì)執(zhí)行后面所定義的命令來(lái)生成edit這個(gè)文件留晚。
4.如果edit所依賴的.o文件也不存在,那么make會(huì)在當(dāng)前文件中找目標(biāo)為.o文件的依賴性告嘲,如果找到則再根據(jù)那一個(gè)規(guī)則生成.o文件错维。(這有點(diǎn)像一個(gè)堆棧的過(guò)程)
5.當(dāng)然,你的C文件和H文件是存在的啦橄唬,于是make會(huì)生成 .o 文件赋焕,然后再用 .o 文件生成make的終極任務(wù),也就是執(zhí)行文件edit了仰楚。
這就是整個(gè)make的依賴性隆判,make會(huì)一層又一層地去找文件的依賴關(guān)系,直到最終編譯出第一個(gè)目標(biāo)文件僧界。在找尋的過(guò)程中侨嘀,如果出現(xiàn)錯(cuò)誤,比如最后被依賴的文件找不到捂襟,那么make就會(huì)直接退出飒炎,并報(bào)錯(cuò),而對(duì)于所定義的命令的錯(cuò)誤笆豁,或是編譯不成功郎汪,make根本不理。make只管文件的依賴性闯狱,即煞赢,如果在我找了依賴關(guān)系之后,冒號(hào)后面的文件還是不在哄孤,那么對(duì)不起照筑,我就不工作啦。
通過(guò)上述分析,我們知道凝危,像clean這種波俄,沒(méi)有被第一個(gè)目標(biāo)文件直接或間接關(guān)聯(lián),那么它后面所定義的命令將不會(huì)被自動(dòng)執(zhí)行蛾默,不過(guò)懦铺,我們可以顯示要make執(zhí)行。即命令——“make clean”支鸡,以此來(lái)清除所有的目標(biāo)文件冬念,以便重編譯。
于是在我們編程中牧挣,如果這個(gè)工程已被編譯過(guò)了急前,當(dāng)我們修改了其中一個(gè)源文件,比如file.c瀑构,那么根據(jù)我們的依賴性裆针,我們的目標(biāo)file.o會(huì)被重編譯(也就是在這個(gè)依性關(guān)系后面所定義的命令),于是file.o的文件也是最新的啦寺晌,于是file.o的文件修改時(shí)間要比edit要新据块,所以 edit也會(huì)被重新鏈接了(詳見edit目標(biāo)文件后定義的命令)。
而如果我們改變了“command.h”折剃,那么另假,kdb.o、command.o和files.o都會(huì)被重編譯怕犁,并且边篮,edit會(huì)被重鏈接。
######四奏甫、makefile中的使用變量
在上面的例子中戈轿,先讓我們看看edit的規(guī)則:
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
我們可以看到[.o]文件的字符串被重復(fù)了兩次,如果我們的工程需要加入一個(gè)新的[.o]文件阵子,那么我們需要在兩個(gè)地方加(應(yīng)該是三個(gè)地方思杯,還有一個(gè)地方在clean中)。當(dāng)然挠进,我們的makefile并不復(fù)雜色乾,所以在兩個(gè)地方加也不累,但如果makefile變得復(fù)雜领突,那么我們就有可能會(huì)忘掉一個(gè)需要加入的地方暖璧,而導(dǎo)致編譯失敗。所以君旦,為了makefile的易維護(hù)澎办,在makefile中我們可以使用變量嘲碱。makefile的變量也就是一個(gè)字符串,理解成C語(yǔ)言中的宏可能會(huì)更好局蚀。
比如麦锯,我們聲明一個(gè)變量,叫objects, OBJECTS, objs, OBJS, obj, 或是 OBJ琅绅,反正不管什么啦扶欣,只要能夠表示obj文件就行了。我們?cè)趍akefile一開始就這樣定義:
objects = main.o kbd.o command.o display.o
insert.o search.o files.o utils.o
于是奉件,我們就可以很方便地在我們的makefile中以“$(objects)”的方式來(lái)使用這個(gè)變量了宵蛀,于是我們的改良版makefile就變成下面這個(gè)樣子:
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 : 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)
于是如果有新的 .o 文件加入昆著,我們只需簡(jiǎn)單地修改一下 objects 變量就可以了县貌。
關(guān)于變量更多的話題,我會(huì)在后續(xù)給你一一道來(lái)凑懂。
######五煤痕、讓make自動(dòng)推導(dǎo)
GNU的make很強(qiáng)大,它可以自動(dòng)推導(dǎo)文件以及文件依賴關(guān)系后面的命令接谨,于是我們就沒(méi)必要去在每一個(gè)[.o]文件后都寫上類似的命令摆碉,因?yàn)椋覀兊膍ake會(huì)自動(dòng)識(shí)別脓豪,并自己推導(dǎo)命令巷帝。
只要make看到一個(gè)[.o]文件,它就會(huì)自動(dòng)的把[.c]文件加在依賴關(guān)系中扫夜,如果make找到一個(gè)whatever.o楞泼,那么 whatever.c,就會(huì)是whatever.o的依賴文件笤闯。并且 cc -c whatever.c 也會(huì)被推導(dǎo)出來(lái)堕阔,于是,我們的makefile 再也不用寫得這么復(fù)雜颗味。我們的新makefile又出爐了超陆。
objects = main.o kbd.o command.o display.o
insert.o search.o files.o utils.o
cc = gcc
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)
這種方法,也就是make的“隱晦規(guī)則”浦马。上面文件內(nèi)容中时呀,“.PHONY”表示,clean是個(gè)偽目標(biāo)文件晶默。
關(guān)于更為詳細(xì)的“隱晦規(guī)則”和“偽目標(biāo)文件”退唠,我會(huì)在后續(xù)給你一一道來(lái)。
######六荤胁、另類風(fēng)格的makefile
既然我們的make可以自動(dòng)推導(dǎo)命令瞧预,那么我看到那堆[.o]和[.h]的依賴就有點(diǎn)不爽屎债,那么多的重復(fù)的[.h],能不能把其收攏起來(lái)垢油,好吧盆驹,沒(méi)有問(wèn)題,這個(gè)對(duì)于make來(lái)說(shuō)很容易滩愁,誰(shuí)叫它提供了自動(dòng)推導(dǎo)命令和文件的功能呢躯喇?來(lái)看看最新風(fēng)格的makefile吧。
objects = main.o kbd.o command.o display.o
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h
.PHONY : clean
clean :
rm edit $(objects)
這種風(fēng)格硝枉,讓我們的makefile變得很簡(jiǎn)單廉丽,但我們的文件依賴關(guān)系就顯得有點(diǎn)凌亂了。魚和熊掌不可兼得妻味。還看你的喜好了正压。我是不喜歡這種風(fēng)格的,一是文件的依賴關(guān)系看不清楚责球,二是如果文件一多焦履,要加入幾個(gè)新的.o文件,那就理不清楚了雏逾。
######七嘉裤、清空目標(biāo)文件的規(guī)則
每個(gè)Makefile中都應(yīng)該寫一個(gè)清空目標(biāo)文件(.o和執(zhí)行文件)的規(guī)則,這不僅便于重編譯栖博,也很利于保持文件的清潔屑宠。這是一個(gè)“修養(yǎng)”(呵呵,還記得我的《編程修養(yǎng)》嗎)仇让。一般的風(fēng)格都是:
clean:
rm edit $(objects)
更為穩(wěn)健的做法是:
.PHONY : clean
clean :
-rm edit $(objects)
前面說(shuō)過(guò)典奉,.PHONY意思表示clean是一個(gè)“偽目標(biāo)”,妹孙。而在rm命令前面加了一個(gè)小減號(hào)的意思就是秋柄,也許某些文件出現(xiàn)問(wèn)題,但不要管蠢正,繼續(xù)做后面的事骇笔。當(dāng)然,clean的規(guī)則不要放在文件的開頭嚣崭,不然笨触,這就會(huì)變成make的默認(rèn)目標(biāo),相信誰(shuí)也不愿意這樣雹舀。不成文的規(guī)矩是——“clean從來(lái)都是放在文件的最后”芦劣。
上面就是一個(gè)makefile的概貌,也是makefile的基礎(chǔ)说榆,下面還有很多makefile的相關(guān)細(xì)節(jié)虚吟,準(zhǔn)備好了嗎寸认?準(zhǔn)備好了就來(lái)。
######八串慰、makefile里有什么
Makefile里主要包含了五個(gè)東西:顯式規(guī)則偏塞、隱晦規(guī)則、變量定義邦鲫、文件指示和注釋灸叼。
1.顯式規(guī)則摔桦。顯式規(guī)則說(shuō)明了躬翁,如何生成一個(gè)或多個(gè)目標(biāo)文件枚赡。這是由Makefile的書寫者明顯指出狰腌,要生成的文件,文件的依賴文件四濒,生成的命令讥脐。
2.隱晦規(guī)則嫩实。由于我們的make有自動(dòng)推導(dǎo)的功能醉者,所以隱晦的規(guī)則可以讓我們比較簡(jiǎn)略地書寫Makefile但狭,這是由make所支持的披诗。
3.變量的定義撬即。在Makefile中我們要定義一系列的變量,變量一般都是字符串呈队,這個(gè)有點(diǎn)像你C語(yǔ)言中的宏剥槐,當(dāng)Makefile被執(zhí)行時(shí),其中的變量都會(huì)被擴(kuò)展到相應(yīng)的引用位置上宪摧。
4.文件指示粒竖。其包括了三個(gè)部分,一個(gè)是在一個(gè)Makefile中引用另一個(gè)Makefile几于,就像C語(yǔ)言中的include一樣蕊苗;另一個(gè)是指根據(jù)某些情況指定Makefile中的有效部分,就像C語(yǔ)言中的預(yù)編譯#if一樣沿彭;還有就是定義一個(gè)多行的命令朽砰。有關(guān)這一部分的內(nèi)容,我會(huì)在后續(xù)的部分中講述喉刘。
5.注釋瞧柔。Makefile中只有行注釋,和UNIX的Shell腳本一樣睦裳,其注釋是用“#”字符造锅,這個(gè)就像C/C++中的“//”一樣。如果你要在你的Makefile中使用“#”字符廉邑,可以用反斜框進(jìn)行轉(zhuǎn)義哥蔚,如:“\#”倒谷。
最后,還值得一提的是糙箍,在Makefile中的命令恨锚,必須要以[Tab]鍵開始。
######九倍靡、makefile文件名規(guī)則
默認(rèn)的情況下,make命令會(huì)在當(dāng)前目錄下按順序找尋文件名為“GNUmakefile”塌西、“makefile”他挎、“Makefile”的文件办桨,找到了解釋這個(gè)文件殊霞。在這三個(gè)文件名中祝钢,最好使用“Makefile”這個(gè)文件名,因?yàn)椋@個(gè)文件名第一個(gè)字符為大寫锚贱,這樣有一種顯目的感覺(jué)拧廊。最好不要用 “GNUmakefile”倦春,這個(gè)文件是GNU的make識(shí)別的尿庐。有另外一些make只對(duì)全小寫的“makefile”文件名敏感,但是基本上來(lái)說(shuō)席舍,大多數(shù)的make都支持“makefile”和“Makefile”這兩種默認(rèn)文件名。
當(dāng)然滑黔,你可以使用別的文件名來(lái)書寫Makefile略荡,比如:“Make.Linux”粥谬,“Make.Solaris”喉脖,“Make.AIX”等,如果要指定特定的Makefile,你可以使用make的“-f”和“--file”參數(shù)叫胖,如:make -f Make.Linux或make --file Make.AIX。
######十、引用其他的makefile
在Makefile使用include關(guān)鍵字可以把別的Makefile包含進(jìn)來(lái),這很像C語(yǔ)言的#include蠕趁,被包含的文件會(huì)原模原樣的放在當(dāng)前文件的包含位置诱咏。include的語(yǔ)法是:
include <filename>;
filename可以是當(dāng)前操作系統(tǒng)Shell的文件模式(可以包含路徑和通配符)
在include前面可以有一些空字符棚点,但是絕不能是[Tab]鍵開始早处。include和<filename>;可以用一個(gè)或多個(gè)空格隔開。舉個(gè)例子瘫析,你有這樣幾個(gè)Makefile:a.mk砌梆、b.mk、c.mk贬循,還有一個(gè)文件叫foo.make咸包,以及一個(gè)變量$(bar),其包含了 e.mk和f.mk甘有,那么诉儒,下面的語(yǔ)句:
include foo.make *.mk $(bar)
等價(jià)于:
include foo.make a.mk b.mk c.mk e.mk f.mk
make命令開始時(shí),會(huì)找尋include所指出的其它Makefile亏掀,并把其內(nèi)容安置在當(dāng)前的位置。就好像C/C++的#include指令一樣泛释。如果文件都沒(méi)有指定絕對(duì)路徑或是相對(duì)路徑的話滤愕,make會(huì)在當(dāng)前目錄下首先尋找,如果當(dāng)前目錄下沒(méi)有找到怜校,那么间影,make還會(huì)在下面的幾個(gè)目錄下找:
1.如果make執(zhí)行時(shí),有“-I”或“--include-dir”參數(shù)茄茁,那么make就會(huì)在這個(gè)參數(shù)所指定的目錄下去尋找魂贬。
2.如果目錄<prefix>;/include(一般是:/usr/local/bin或/usr/include)存在的話巩割,make也會(huì)去找。
如果有文件沒(méi)有找到的話付燥,make會(huì)生成一條警告信息宣谈,但不會(huì)馬上出現(xiàn)致命錯(cuò)誤。它會(huì)繼續(xù)載入其它的文件键科,一旦完成makefile的讀取闻丑, make會(huì)再重試這些沒(méi)有找到,或是不能讀取的文件勋颖,如果還是不行嗦嗡,make才會(huì)出現(xiàn)一條致命信息。如果你想讓make不理那些無(wú)法讀取的文件饭玲,而繼續(xù)執(zhí)行侥祭,你可以在include前加一個(gè)減號(hào)“-”。如:
-include <filename>;
其表示茄厘,無(wú)論include過(guò)程中出現(xiàn)什么錯(cuò)誤卑硫,都不要報(bào)錯(cuò)繼續(xù)執(zhí)行。和其它版本make兼容的相關(guān)命令是sinclude蚕断,其作用和這一個(gè)是一樣的欢伏。
######十一、環(huán)境變量 MAKEFILES
如果你的當(dāng)前環(huán)境中定義了環(huán)境變量MAKEFILES亿乳,那么硝拧,make會(huì)把這個(gè)變量中的值做一個(gè)類似于include的動(dòng)作。這個(gè)變量中的值是其它的Makefile葛假,用空格分隔障陶。只是,它和include不同的是聊训,從這個(gè)環(huán)境變量中引入的Makefile的“目標(biāo)”不會(huì)起作用抱究,如果環(huán)境變量中定義的文件發(fā)現(xiàn)錯(cuò)誤,make也會(huì)不理带斑。
但是在這里我還是建議不要使用這個(gè)環(huán)境變量鼓寺,因?yàn)橹灰@個(gè)變量一被定義,那么當(dāng)你使用make時(shí)勋磕,所有的Makefile都會(huì)受到它的影響妈候,這絕不是你想看到的。在這里提這個(gè)事挂滓,只是為了告訴大家苦银,也許有時(shí)候你的Makefile出現(xiàn)了怪事,那么你可以看看當(dāng)前環(huán)境中有沒(méi)有定義這個(gè)變量。
######十二幔虏、make的工作方式
GNU的make工作時(shí)的執(zhí)行步驟如下:(想來(lái)其它的make也是類似)
1.讀入所有的Makefile纺念。
2.讀入被include的其它Makefile。
3.初始化文件中的變量想括。
4.推導(dǎo)隱晦規(guī)則陷谱,并分析所有規(guī)則。
5.為所有的目標(biāo)文件創(chuàng)建依賴關(guān)系鏈主胧。
6.根據(jù)依賴關(guān)系叭首,決定哪些目標(biāo)要重新生成踪栋。
7.執(zhí)行生成命令。
1-5步為第一個(gè)階段,6-7為第二個(gè)階段。第一個(gè)階段中,如果定義的變量被使用了,那么政供,make會(huì)把其展開在使用的位置踩晶。但make并不會(huì)完全馬上展開,make使用的是拖延戰(zhàn)術(shù),如果變量出現(xiàn)在依賴關(guān)系的規(guī)則中,那么僅當(dāng)這條依賴被決定要使用了哭靖,變量才會(huì)在其內(nèi)部展開。
當(dāng)然,這個(gè)工作方式你不一定要清楚,但是知道這個(gè)方式你也會(huì)對(duì)make更為熟悉橘忱。有了這個(gè)基礎(chǔ),后續(xù)部分也就容易看懂了