基于make命令與makefile文件詳解
一、多個(gè)源文件帶來的問題
在編寫c/c++測(cè)試程序時(shí),我們習(xí)慣每次修改一處代碼,然后就馬上編譯運(yùn)行來查看運(yùn)行的結(jié)果。這種編譯方式對(duì)于小程序來說是沒有多大問題的,可對(duì)于大型程序來說,由于包含了大量的源文件,如果每次改動(dòng)一個(gè)地方都需要編譯所有的源文件,這個(gè)簡(jiǎn)單的直接編譯所有源文件方式對(duì)程序員來說簡(jiǎn)直是噩耗。
我們看一個(gè)例子:
// main.c #include "a.h" // 2.c #include "a.h" #include "b.h" // 3.c #include "b.h" #include "c.h"
如果程序員只修改了頭文件c.h,則源文件main.c和2.c都無需編譯,因?yàn)樗鼈儾灰蕾囘@個(gè)頭文件。而對(duì)3.c來說,由于它包含了c.h,所以在頭文件c.h改動(dòng)后,就必須得新編譯。
而如果改動(dòng)了b.h可是忘記編譯了2.c,那么最終的程序就可能無法正常工作。
make 工具就是為了解決上述問題而出現(xiàn)的,它會(huì)在必要時(shí)重新編譯所有受改動(dòng)影響的源文件。
二、make 命令
make命令本身支持許多選項(xiàng),最常用的是-f選項(xiàng)。如果我們直接運(yùn)行
make
那么make命令會(huì)首先在當(dāng)前目錄查找名為makefile的文件,如果找不到,就會(huì)查找名為Makefile的文件。
為了指示make命令將哪個(gè)文件作為makefile文件,可以使用 -f 選項(xiàng):
make -f Makefile1
三、makefile 文件
上面提到makefile文件,那么什么是makefile文件呢?
make命令功能雖然十分強(qiáng)大,但是光憑其自身無法了解如何構(gòu)建應(yīng)用程序的。這時(shí),makefile就出來了,它告訴make應(yīng)用程序如何構(gòu)建的。make命令和makefile文件的結(jié)合提供了一個(gè)在管理項(xiàng)目的十分強(qiáng)大的工具,它們不僅用于控制源文件的編譯,而且還提供了將應(yīng)用程序安裝到目標(biāo)目錄等其他功能。
3.1 依賴關(guān)系
依賴關(guān)系定義了應(yīng)用程序里面每個(gè)文件與其他源文件之間的關(guān)系。例如在上面的例子中,我們可以定義最終應(yīng)用程序依賴于目標(biāo)文件main.o,2.o和3.o。同樣,main.o依賴于main.c和a.h,2.o依賴于2.c,a.h和b.h,3.o依賴于3.c,b.h和c.h。
在makefile文件中,依賴關(guān)系的寫法是:先寫目標(biāo)的名稱,然后緊跟一個(gè)冒號(hào),接著是空格或者制表符tab,最后是用空格或者制表符tab隔開的文件列表。上面的例子的依賴關(guān)系如下:
myapp: main.o 2.o 3.o main.o: main.c a.h 2.o: 2.c a.h b.h 3.o: 3.c b.h c.h
這組依賴關(guān)系形成一個(gè)層次結(jié)構(gòu),展示了源文件之間的關(guān)系。例如,如果源文件b.h發(fā)生改變,就需要重新編譯2.o和3.o,接下來還需要重新編譯myapp。
3.2 規(guī)則
makefiel文件中的規(guī)則定義了目標(biāo)的創(chuàng)建方式。在上面的例子中,我們使用gcc -c 2.c創(chuàng)建2.o。這個(gè)gcc命令即是目標(biāo)2.o的創(chuàng)建方式,也即是規(guī)則。
在makefile文件中,規(guī)則都必須以tab開頭。
在源文件所在的目錄下創(chuàng)建Makefile1文件,其內(nèi)容如下。
myapp: main.o 2.o 3.o gcc -o myapp main.o 2.o 3.o main.o: main.c a.h gcc -c main.c 2.o: 2.c a.h b.h gcc -c 2.c 3.o: 3.c b.h c.h gcc -c 3.c
三個(gè)頭文件a.h,b.h,c.h內(nèi)容都為空,源文件的內(nèi)容如下:
/* main.c */ #include <stdlib.h> #include "a.h" extern void function_two(); extern void function_three(); int main() { function_two(); function_three(); exit(EXIT_SUCCESS); }
/* 2.c */ #include <stdio.h> #include "a.h" #include "b.h" void function_two() { printf("function two\n"); }
/* 3.c */ #include <stdio.h> #include "b.h" #include "c.h" void function_three() { printf("function three\n"); }
執(zhí)行make命令,:
$ make -f Makefile1 gcc -c main.c gcc -c 2.c gcc -c 3.c gcc -o myapp main.o 2.o 3.o
運(yùn)行應(yīng)用程序:
$ ./myapp function two function three
從輸出可以說明應(yīng)用程序已被正確構(gòu)建。
如果改變b.h頭文件,makefile能夠正確處理這一變化,只有2.c和3.c發(fā)生重新編譯:
$ touch b.h $ make -f Makefile1 gcc -c 2.c gcc -c 3.c gcc -o myapp main.o 2.o 3.o
3.3 注釋
makefile文件使用#來表示注釋,一直延續(xù)到這一行的結(jié)束。
3.4 宏
不同的平臺(tái)下可能使用不同的編譯器,不同的環(huán)境(例如開發(fā)與線上環(huán)境)也可能使用不同的編譯器選項(xiàng),為了便于修改makefile這些可變的參數(shù),我們可以使用宏來實(shí)現(xiàn)makefile。
makefile引用宏定義的方法為$(MACRONAME)。我們來看如何使用宏來改寫上面的makefile文件。
all: myapp # 編譯器 CC = gcc # include的搜索路徑 INCLUDE = . # 編譯器參數(shù) CFLAGS = -g -Wall -ansi myapp: main.o 2.o 3.o $(CC) -o myapp main.o 2.o 3.o main.o: main.c a.h $(CC) -I$(INCLUDE) $(CFLAGS) -c main.c 2.o: 2.c a.h b.h $(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c 3.o: 3.c b.h c.h $(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c
我們習(xí)慣在makefile文件中將第一個(gè)目標(biāo)定義為all,然后再列出其他從屬的目標(biāo),上面的makefile也遵循這個(gè)約定。
運(yùn)行make命令:
$ make -f Makefile2 gcc -I. -g -Wall -ansi -c main.c gcc -I. -g -Wall -ansi -c 2.c gcc -I. -g -Wall -ansi -c 3.c gcc -o myapp main.o 2.o 3.o
同樣也正確構(gòu)建了應(yīng)用程序myapp。
3.5 多個(gè)目標(biāo)
makefile文件除了定義編譯的目標(biāo)外,還可以定義其他的目標(biāo)。例如,增加一個(gè)clean選項(xiàng)來刪除不需要的目標(biāo)文件,增加一個(gè)install選項(xiàng)來將編譯成功的應(yīng)用程序安裝到另一個(gè)目錄下,等等。
all: myapp CC = gcc INSTDIR = /usr/local/bin INCLUDE = . CFLAGS = -g -Wall -ansi myapp: main.o 2.o 3.o $(CC) -o myapp main.o 2.o 3.o main.o: main.c a.h $(CC) -I$(INCLUDE) $(CFLAGS) -c main.c 2.o: 2.c a.h b.h $(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c 3.o: 3.c b.h c.h $(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c clean: -rm main.o 2.o 3.o install: myapp @if [ -d $(INSTDIR) ]; \ then \ cp myapp $(INSTDIR);\ chmod a+x $(INSTDIR)/myapp;\ chmod og-w $(INSTDIR)/myapp;\ echo "Install in $(INSTDIR)";\ else \ echo "sorry, $(INSTDIR) does not exist";\ fi
上面的makefile文件有幾點(diǎn)需要注意的。
(1)特殊目標(biāo)all只指定了myapp這個(gè)目標(biāo),因此,在執(zhí)行make命令時(shí)未指定目標(biāo),它的默認(rèn)行為就是創(chuàng)建目標(biāo)myapp。
(2)目標(biāo)clean用來測(cè)試編譯過程中產(chǎn)生的中間文件。
(3)目標(biāo)install用于將應(yīng)用程序安裝到指定目錄,它依賴于myapp,即執(zhí)行install前須先創(chuàng)建myapp。install目標(biāo)由shell腳本組成,由于make命令在執(zhí)行規(guī)則時(shí)會(huì)調(diào)用一個(gè)shell,并且會(huì)針對(duì)每個(gè)規(guī)則使用一個(gè)新的shell,所以必須在上面每行代碼的結(jié)尾加上一個(gè)\,讓所有的shell腳本都處于同一行。
腳本以@開頭,說明make在執(zhí)行這些規(guī)則之前不會(huì)在標(biāo)準(zhǔn)輸出顯示命令本身。
創(chuàng)建myapp:
$ make -f Makefile3 gcc -I. -g -Wall -ansi -c main.c gcc -I. -g -Wall -ansi -c 2.c gcc -I. -g -Wall -ansi -c 3.c gcc -o myapp main.o 2.o 3.o
將myapp安裝到指到目錄:
$ make -f Makefile3 install Install in /usr/local/bin
然后可以直接執(zhí)行myapp:
$ myapp function two function three
刪除中間文件:
$ make -f Makefile3 clean rm main.o 2.o 3.o
以上這篇基于make命令與makefile文件詳解就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
C語言+EasyX實(shí)現(xiàn)數(shù)字雨效果
這篇文章主要為大家詳細(xì)介紹了C語言+EasyX實(shí)現(xiàn)數(shù)字雨效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-11-11C語言中const,volatile,restrict的用法總結(jié)
以下是對(duì)C語言中const,volatile,restrict的用法進(jìn)行了詳細(xì)的總結(jié)介紹,需要的朋友可以過來參考下2013-10-10C++實(shí)現(xiàn)查詢本機(jī)信息的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用C++實(shí)現(xiàn)查詢本機(jī)信息,并且進(jìn)行上報(bào),文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,感興趣的可以了解一下2023-05-05C++深入淺出講解內(nèi)存四區(qū)與new關(guān)鍵字的使用
內(nèi)存四區(qū),一個(gè)非常重要的知識(shí)點(diǎn),搞懂了內(nèi)存四區(qū),才能更快的去搞懂指針。我們寫的C語言代碼,不夸張的說,都是直接或者間接的在操作內(nèi)存。C語言之所以能夠開發(fā)操作系統(tǒng),就是指針的存在,而指針說白了就是地址,內(nèi)存地址,指針變量說白了就是存儲(chǔ)地址的變量2022-05-05VC++實(shí)現(xiàn)添加文件關(guān)聯(lián)的方法示例
這篇文章主要介紹了VC++實(shí)現(xiàn)添加文件關(guān)聯(lián)的方法,涉及VC++針對(duì)注冊(cè)表的寫入與VC事件響應(yīng)相關(guān)操作技巧,需要的朋友可以參考下2017-08-08基于Qt播放器的實(shí)現(xiàn)詳解(支持Rgb,YUV格式)
這篇文章主要為大家詳細(xì)介紹了如何利用Qt實(shí)現(xiàn)簡(jiǎn)易的播放器,可以支持支持Rgb,YUV格式。文中的示例代碼講解詳細(xì),感興趣的小伙伴可以嘗試一下2022-12-12C++中priority_queue模擬實(shí)現(xiàn)的代碼示例
在c++語言中數(shù)據(jù)結(jié)構(gòu)中的堆結(jié)構(gòu)可以通過STL庫中的priority_queue 優(yōu)先隊(duì)列來實(shí)現(xiàn),這樣做極大地簡(jiǎn)化了我們的工作量,這篇文章主要給大家介紹了關(guān)于C++中priority_queue模擬實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2021-08-08