Linux Makefile與Kconfig文件詳解

本文章介紹了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)以下界面:


alt
alt

這個(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)境變量的定義:


alt
alt

或者通過(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文件夾:


alt
alt

此文件夾中有許多選項(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ù)部分也就容易看懂了
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末芦岂,一起剝皮案震驚了整個(gè)濱河市川无,隨后出現(xiàn)的幾起案子仅叫,更是在濱河造成了極大的恐慌,老刑警劉巖诫咱,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件笙隙,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡遂跟,警方通過(guò)查閱死者的電腦和手機(jī)逃沿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)幻锁,“玉大人凯亮,你說(shuō)我怎么就攤上這事『宥” “怎么了假消?”我有些...
    開封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)岭接。 經(jīng)常有香客問(wèn)我富拗,道長(zhǎng),這世上最難降的妖魔是什么鸣戴? 我笑而不...
    開封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任啃沪,我火速辦了婚禮,結(jié)果婚禮上窄锅,老公的妹妹穿的比我還像新娘创千。我一直安慰自己,他們只是感情好入偷,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開白布追驴。 她就那樣靜靜地躺著,像睡著了一般疏之。 火紅的嫁衣襯著肌膚如雪殿雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天锋爪,我揣著相機(jī)與錄音丙曙,去河邊找鬼爸业。 笑死,一個(gè)胖子當(dāng)著我的面吹牛河泳,可吹牛的內(nèi)容都是我干的沃呢。 我是一名探鬼主播年栓,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼拆挥,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了某抓?” 一聲冷哼從身側(cè)響起纸兔,我...
    開封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎否副,沒(méi)想到半個(gè)月后汉矿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡备禀,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年洲拇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片曲尸。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡赋续,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出另患,到底是詐尸還是另有隱情纽乱,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布昆箕,位于F島的核電站鸦列,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏鹏倘。R本人自食惡果不足惜薯嗤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望纤泵。 院中可真熱鬧骆姐,春花似錦、人聲如沸夕吻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)涉馅。三九已至归园,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間稚矿,已是汗流浹背庸诱。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工捻浦, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人桥爽。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓朱灿,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親钠四。 傳聞我的和親對(duì)象是個(gè)殘疾皇子盗扒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 來(lái)自陳浩的一片老文,但絕對(duì)營(yíng)養(yǎng)缀去。 示例工程:3 個(gè)頭文件*.h侣灶,和 8 個(gè) C 文件*.c。 初 編譯過(guò)程缕碎,源文件...
    周筱魯閱讀 4,688評(píng)論 0 17
  • linux資料總章2.1 1.0寫的不好抱歉 但是2.0已經(jīng)改了很多 但是錯(cuò)誤還是無(wú)法避免 以后資料會(huì)慢慢更新 大...
    數(shù)據(jù)革命閱讀 12,146評(píng)論 2 34
  • 我們說(shuō)的Linux其實(shí)指的就是 內(nèi)核(kernel)而已褥影。這個(gè)內(nèi)核控制你主機(jī)的所有硬件并提供系統(tǒng)所有的功能,所以它...
    Zhang21閱讀 7,391評(píng)論 0 18
  • 最近稍微空閑了一點(diǎn)咏雌,然后開始整理一下之前項(xiàng)目用到的東西凡怎,方便以后項(xiàng)目再次使用。很多項(xiàng)目需要用到發(fā)布圖片的功能吧赊抖,像...
    在尋找雪見的景天閱讀 2,433評(píng)論 1 7
  • 第一章 或喜或愛(ài) 這是個(gè)快入冬的季節(jié)统倒,風(fēng)總是冷的,吹的簡(jiǎn)單直發(fā)...
    yjr么么噠閱讀 190評(píng)論 7 5