經(jīng)常在一個(gè)項(xiàng)目中包含多個(gè).c源文件,而且一個(gè).c源文件包含了一堆的頭文件,這種情況下如何編寫(xiě)makefile,使得能成功編譯整個(gè)項(xiàng)目?本博文對(duì)這些問(wèn)題提出自己淺析的理解。涉及到對(duì)gcc命令使用與編譯流程理解及多文件時(shí)makefile的編寫(xiě)。
有些場(chǎng)景下編譯的程序是不能依賴OS和標(biāo)準(zhǔn)的C庫(kù)的,并且需要C和匯編混合編譯,如bootloader程序,就需要制定參數(shù)-nostdlib,這樣的Makefile如下所示:
all:
arm-linux-gcc -O2 -Wall -nostdlib -march=armv4 -Wl,-T,ipl.lds uart.c ipl.c nfc.c nand.c sha1.c arm.s -o ipl.exe
arm-linux-objcopy -Obinary ipl.exe ipl.bin
clean:
rm -rf ipl.exe ipl.bin
很奇怪的是,在《跟我一起學(xué)Makefile-陳皓》的文檔中,第五部分第八章,有講到自動(dòng)生成依賴性的問(wèn)題。從字面意思,我理解為在.c源文件中每添加一個(gè)自己的頭文件都需要在makefile的對(duì)應(yīng)目標(biāo)中添加對(duì)該頭文件的依賴。
添加對(duì)此頭文件的依賴是為了確保有此文件?但是在gcc編譯時(shí),如果尋找不到該頭文件,是會(huì)停止編譯且報(bào)錯(cuò)的。
然后我就直接理解為了:添加頭文件依賴是為了gcc編譯時(shí)讓此頭文件也作為輸入文件
例如:
?。踥bjc] view plain copy《abc.c》:
include “abc.h”
include 《stdio.h》
void main()
{
printf(“helloworld”);
}
編譯成.o文件時(shí),應(yīng)該為:
?。踥bjc] view plain copygcc -c abc.c abc.h //錯(cuò)誤示范
結(jié)果卻是相當(dāng)于執(zhí)行了:
?。踥bjc] view plain copygcc -c abc.c
gcc -c abc.h
是因?yàn)槲业膅cc版本較新?總之,折騰了很久后,我就是發(fā)現(xiàn),頭文件沒(méi)必要寫(xiě)到依賴中,而gcc也不需要直接把此頭文件作為輸入,但是若頭文件不在當(dāng)前目錄下,需要用-I指出頭文件地址。(純粹個(gè)人階段性的理解,還希望大牛能指正)
在編程的時(shí)候,我們可以把一個(gè)完整程序的每個(gè)函數(shù)分離出來(lái),寫(xiě)成.c文件,最后再一起編譯和鏈接。這樣有利于程序功能模塊化,也方便檢查代碼錯(cuò)誤。
.h文件:里面編輯該程序需要引用的頭文件。
#ifndef /#define / #endif : 防止該頭文件被重復(fù)引用。
整體用法:
#ifndef A_H //如果沒(méi)有a.h文件,#define A_H。如果有,結(jié)束定義
#define A_H //定義a.h文件
定義a.h需要的頭文件
#endif //結(jié)束定義部分
例子:
一個(gè).h文件的書(shū)寫(xiě)格式。
#ifndef A_H
#define A_H
定義程序所需的一些頭文件
#include《stdio.h》
#include《string.h》
#include《stdlib.h》
…
#endif
保存退出。
其實(shí)在編寫(xiě)小程序的時(shí)候也可以不用#ifndef /#define / #endif,但是用它更安全,不易出錯(cuò)。
c文件的書(shū)寫(xiě)格式
在寫(xiě)每個(gè)函數(shù)之前要加載頭文件,如果是.h文件,就需要寫(xiě)#include”a.h”。
例如:引用之前的a.h文件。
add.c
#include”a.h”
int add(int a,intb)
{
return a+b;
}
保存退出。
程序編輯完成之后,需要編譯鏈接。
我們可以用gcc編譯每個(gè).c文件。如果有三個(gè).c文件a.c、b.c、c.c,編譯方法如下:
gcc a.c –o a.o //將三個(gè).c文件編譯成.o文件
gcc b.c -o b.o
gcc c.c -o c.o
gcc a.o b.o c.o –o all //將三個(gè).o文件編譯成一個(gè)可執(zhí)行文件
。/all //執(zhí)行程序
例如:
test.h
add.c
main.c
編譯:
執(zhí)行:
如果我們有很多個(gè).c文件,這個(gè)方法就不太好了,這時(shí),我們提出Makefile文件。
Makefile:自動(dòng)編譯。先將每個(gè).c文件的編譯過(guò)程提前寫(xiě)在Makefile文件中,在運(yùn)行程序時(shí),系統(tǒng)直接用make命令使文件自動(dòng)編譯,提高效率。
Makefile文件的書(shū)寫(xiě)格式:
vim Makefile
文件類型:由哪個(gè)文件得到
得到過(guò)程
例如:
main:main.o //可執(zhí)行文件main是由目標(biāo)文件main.o得到。
gcc main.o –o main //得到過(guò)程是將main.o編譯成main文件。
main.o:main.c
gcc -c main.c -o main.o
在Makefile文件中一定要將每一個(gè).c文件按執(zhí)行順序先編譯成.o文件,再按順序?qū)?o文件編譯成可執(zhí)行文件。
每次編譯過(guò)后會(huì)產(chǎn)生很多的.o文件,對(duì)于程序運(yùn)行沒(méi)什么太大意義,反而會(huì)占內(nèi)存,所以我們也可以在Makefile文件中添加清除命令(clean),如:
.PHONY:clean
clean: 刪除所有文件類型為.o的文件
rm –rf *.o
一些文件也可用下面符號(hào)表示:
$@: 代表規(guī)則里面的目標(biāo)文件。
$《:代表規(guī)則當(dāng)中的第一個(gè)依賴文件。
%.c:%.o 所有.c文件全部編譯成.o文件
.PHONY:clean
如果有.PHONY:clean,外面也有clean文件,執(zhí)行make clean時(shí),里面的.o文件會(huì)被刪除而外面的clean文件還在。確保了外面clean文件的安全性。如果沒(méi)有.PHONY:clean 語(yǔ)句,外面也沒(méi)有clean文件時(shí),在執(zhí)行make clean也會(huì)刪除.o文件,如果外面有clean,則會(huì)報(bào)錯(cuò)。
PHONY:目標(biāo)并非實(shí)際的文件名,只是顯示在請(qǐng)求時(shí)執(zhí)行命令的名字。有兩種理由需要使用PHONY目標(biāo):避免和文件名沖突,改善性能。
例如:
vim Makefile
編譯和執(zhí)行(make:編譯, 。/all:執(zhí)行):
評(píng)論