1. 程式人生 > >makefile(二):普通規則

makefile(二):普通規則

一.普通規則

規則是makefile的主要內容,它決定了編譯系統何時,如何來編譯指定的目標。規則的語法如下:

目標:依賴   
    指令
請特別注意,指令前面有一個tab空格。

同時指令可以直接寫在依賴後面,即同一行上,並且這種情況下指令前可以不用tab鍵,但各個指令以分號分隔,這種寫法常常用來寫一個空命令,如下,

target:;

普通命令請不要這樣書寫,因為這樣並不便於閱讀

另外,在GNU make手冊中提到,我們也可以更改.RECIPEPREFIX變數的值來指定指令前面的字元。例子如下:

.RECIPEPREFIX := >
all:
>@echo wanbiao

不過在我目前使用的GNU make 3.81下進行測試,並不能正確執行,這個可以作為一個擴充套件功能。(也或許是我自己的書寫錯誤,沒有繼續深究)

同時依賴也可以當成其他的規則的目標,如下例子:

all:a.o b.o
    gcc –o all a.o b.o 
a.o:a.c
    gcc –c a.c
b.o:b.c
    gcc –c b.c

那麼當我們直接執行make命令的時候,它的執行順序如下:
1.首先掃描到all目標,然後把它當作終極目標,即整個make的執行都是為了建立一個all目標出來,
2.檢視當前目錄下是否有all檔案,如果沒有,那麼就根據依賴進行生成,此時檢測依賴,a.o和b.o,發現在當前目錄下沒有這兩個檔案。那麼就將這兩個依賴作為目標,查詢規則。
3.此時查詢到a.o的規則,和b.o的規則,發現他們分別依賴a.c和b.c,然後繼續檢測a.c和b.c,看看當前目錄下面有沒有,發現有a.c和b.c,
4.那麼就開始生成目標a.o和b.o即,執行gcc –c a.c 和 gcc –c b.c命令生成a.o 和b.o此時有了a.o和b.o那麼就可以生成終極目標all了,
5.此時執行gcc –o all a.o b.o生成終極目標。整個makefile執行完畢。
對上面的一些補充說明:
make在沒有指定目標的時候,makefile遇到的第一個有效目標作為終極目標,因此終極目標應該寫在前面,而其他的目標順序則沒有要求。如果一個規則有多個目標,那麼將其第一目標作為終極目標。
另外,有效的目標指:1.不是以點號開頭的目標(當然如果點號後面是一個路徑,那麼這個目標也可以作為終極目標);2。也不能是模式目標,關於模式目標後面會詳細敘述。
當多個目標出現在同一條規則時,稱為:多目標規則

。這些規則具有同樣的依賴,同樣的命令列。相當於多條規則,寫在一起了,不推薦此種用法。
當一個目標出現在多條規則中時,稱為:多規則目標。這些規則中只能有一條規則有命令列,如果其他規則也有,那麼會報錯。make在執行時,會將所有規則的依賴合併在一起形成一個依賴列表,最終形成一個規則。

二.靜態模式規則

存在如下這種情況,某一類的目標,具有相類似的依賴。此時如果用普通規則,那麼需要為每個都這麼寫,因此,為了減少工作量,引入了靜態模式規則,這個和後面的模式規則有一定的區別,後面敘述模式規則。
他的語法規則如下:

目標列表:目標模式:依賴模式
    命令列

目標列表包含了這個規則的全部目標,注意不能包含不適用此條規則的其他目標。
目標模式:是一個包含%的模式字串。%匹配任意的字元。
依賴模式:也是一個包含%的模式字串。依賴中的%的具體值等於目標中的%的值。
因為這裡用到了模式字串,因此,在命令列中就不能將一些命令寫死,需要用到自動化變數,自動化變數的詳細說明,可以參考後面的敘述。
舉例如下:

objects := foo.o bar.o
all:$(objects)
$(objects):%.o:%.c
    $(CC) –c $(CFLAGS) $< -o [email protected]

對於foo.o來說,%的值為foo,那麼他的依賴中的%的值也為foo,那麼依賴為foo.c
如果需要使用%的值可以使用$*,$*在這種模式下代表%的值。
注意:
$(objects)不能包含不匹配%.o:%.c規則的檔案,如下。

objects := foo.o bar.o test.fle
all:$(objects)
    $(objects):%.o:%.c
    $(CC) –c $(CFLAGS) $< -o [email protected]

這個會得到一個錯誤的提示。改進版如下:

objects := foo.o bar.o test.fle
all:$(objects)
    $(filter %.o,$(objects)):%.o:%.c
    $(CC) –c $(CFLAGS) $< -o [email protected]

這裡使用了filter函式,他的作用是:從$(objects)過濾出.o結尾的檔案。

三.雙冒號規則

雙冒號規則,就是用::代替:的規則。這種規則和普通的規則的處理不太一樣。當同一個目標出現在多個規則中時,普通規則只允許一條規則有命令列,而雙冒號規則允許所有的規則都有命令列。make不會將這些規則進行合併,當不同的雙冒號規則需要更新的時候,可以執行不同的命令,舉例如下:

ob::foo.c
    $(CC) $(CFLAGS) $^ -o [email protected]
ob::bar.c
    $(CC) $(CFLAGS) -g $^ -o [email protected]

當foo.c比ob新時,則執行$(CC) $(CFLAGS) $^ -o $@命令,當bar.c比ob新時,執行$(CC) $(CFLAGS) -g $^ -o $@命令
那麼當這兩條規則為普通規則的時候,make將會提示錯誤資訊

優勢:
雙冒號規則給我們提供了:根據不同的依賴來執行不同命令的機制。

四:模式規則

考慮上面的靜態模式規則,如果我們能將目標列表增加到,所有makefile檔案裡面定義的目標,那麼寫出來的目標列表會變得非常冗餘。此時可以去掉這個目標列表只剩下,目標模式和依賴模式。如下:

目標模式字串:依賴模式字串
    命令列

這種規則稱為:模式規則
其使用跟靜態模式規則一模一樣,並且它沒有了目標列表這部分,也就沒有了目標列表不能包含不符合此規則的限制。
模式規則也可以用來覆蓋makefile的隱含規則,詳細細節可參考隱含規則一節。

五.自動化變數

當時用了靜態模式規則,和模式規則的時候,如果命令列不能依據目標的%進行相應的變化,那麼模式規則和靜態模式規則毫無意義。因此這裡需要使用自動化變數。
常見自動化變數的說明如下:
$@ 代表目標的依賴檔案。
$% 當規則的目標是一個靜態庫檔案的時候,代表靜態庫的一個成員
$< 代表第一個依賴檔案
$? 所有比目標更新的依賴檔案
$^ 代表所有依賴檔案,當依賴檔案重複時,去掉重複檔案
$+ 與$^相同,但是保留重複的依賴檔案
$* 在模式規則和靜態模式規則中,代表%的值。
在老版本的makefile中,還可以通過組合D或者F來,表示目錄和檔名,如下:
$(@D) 代表依賴檔案的目錄部分
$(@F) 代表依賴檔案的檔名(除去目錄部分)
對於其他的自動化變數有相同的用法,具體細節可參考GNU make官方文件。網址如下:
http://www.gnu.org/software/make/manual/make.html

六.偽目標

偽目標不代表一個真正的檔案。系統不會主動執行偽目標,只有手動指定偽目標之後,才進行相應的操作。如下:

clean:
    rm -rf *.o

考慮如下情況,當工作目錄下剛好有一個clean檔案,沒有依賴,此時目標clean被認為了最新,不去執行相應的操作,因此不符合我們的需求,因此,可以強制將clean定義為偽目標,使用.PHONY特殊目標,如下:

.PHONY : clean
clean:
    rm -rf *.o

這樣就避免了衝突。

七.依賴的型別

在上面介紹的所有規則中,依賴可以分為兩種,一種是:一旦依賴比目標新則更新目標,另外一類是:當依賴比目標新,不更新目標,只有當目標沒有的時候,才去更新目標。第一種依賴就是前面介紹的普通依賴,後面這種依賴稱為,命令依賴,即當目標沒有的時候,給命令更新目標的時候用。
命令依賴以|開頭,舉例如下:

libs := bar.a
test:foo.o | $(libs)
    $(CC) $(CFLAGS) $< -o [email protected] $(libs)

當foo.o比test新,那麼命令就會執行,反之當bar.o比test新,並不會執行命令。

七.其他的使用技巧

一.萬用字元的使用
我們可以在目標和依賴中使用%來進行相應的模式匹配,同樣我們也可以使用萬用字元在目標和依賴中進行更加靈活的匹配,如下

print:*.c
    lpr -p $?
    touch print

該規則指定了依賴當前目錄下的所有c檔案
可以使用的萬用字元有三種:*,?,[]
這三種可以使用在規則的目標和依賴中,也可以使用在規則的命令中,謹記不能使用在其他地方,比如變數的定義,如下:

objects := *.o

objects的值為*.o,並不是所有的.o檔案。

foo:$(objects)
    $(cc) -o foo $(CFLAGS) $(objects)

當工作目錄下沒有.o檔案的時候,則會提示:沒有建立*.o檔案的規則。改進方法如下:

objects := $(patsubst %.c,%.o,$(wildcard *.c))

wildcard函式表示在工作目錄下,使用萬用字元進行查詢相應的檔案,並返回。patsubst函式進行相應的替換並返回。
objects的值,則根據下面的步驟進行產生:
1.取得工作目錄下所有的.c檔案
2.將對應的.c檔案替換成.o,並返回給objects變數。
二.VPATH,vpath,GPATH的使用

VPATH = src:../headers

表示的是:當在工作目錄下沒有找到相應的檔案的時候,可以在VPATH裡面指定的目錄下進行查詢,上例中指定了兩個目錄,一個為src,一個為headers,多個目錄使用冒號分割。

vpath 模式字串 directory

表示的是:可以將符合模式字串的檔案,在directory資料夾中進行尋找,例子如下:

vpath %.h ../headers

表示的是:當在工作目錄中沒有找到某個.h檔案的時候,可以在../headers資料夾下進行查詢

vpath 模式字串

表示:清楚之前為模式字串進行的相應的資料夾設定。

vpath

表示清楚所有已被設定的檔案搜尋路徑。

當一個目標沒有在工作目錄下,但是在vapth,或者VPATH進行目錄搜尋之後被找到,當需要更新目標的時候,此時,更新的目標會被放置在工作目錄下,如果想要放置在以前找到的目錄下,需要使用GPATH變數,讓其值指定該目錄。
三.庫檔案通過目錄搜尋得到
程式連線使用的靜態庫,動態庫,同樣可以使用目錄搜尋進行找到。這個需要在依賴列表中,進行如下的寫法。-lname,name為庫的名字,而這個庫的名字,不包括前面的lib和後面的字尾。
舉例如下:

foo:foo.c -lcurses
    $(cc) $^ -o [email protected]

詳細過程如下:
1.make會在當前目錄下尋找libcurses.so,如果沒有,
2.搜尋vpath,VPATH路徑下,還是沒有,
3.搜尋系統預設的庫檔案存放目錄,順序為:/lib,/usr/lib,/usr/local/lib
4.如果還是沒有找到,則按上面的步驟搜尋libcurses.a庫檔案。
四.合理使用偽目標,提高編譯效率

SUBDIRS := foo bar baz
subdirs :
    for dir in $(SUBDIRS) ;do \
$(MAKE) -C $$dir;\
done

當執行subdirs目標時,會依次進行子目錄然後執行make,當我們使用-j引數之後,這個還是呈現一個單執行緒的執行。我們可以進行如下修改

SUBDIRS = foo bar baz
.PHONY: subdirs $(SUBDIRS)
subdirs: $(SUBDIRS)
$(SUBDIRS):
    $(MAKE) -C [email protected]
foo: baz

最後一條規則,表示baz比foo先生成。用來限制子目錄的make順序。
此時,當執行subdirs目標的時候,會去檢測foo bar baz,發現都沒有的時候,會使用

$(SUBDIRS):
    $(MAKE) -C [email protected]

規則進行相應的生成,此時可以發揮了make的多執行緒功能了,因為這三個檔案,可以放在不同的執行緒中進行生成,從而避免了shell命令列下,for的單執行緒操作,可以更好的發揮電腦效能。

相關推薦

makefile普通規則

一.普通規則 規則是makefile的主要內容,它決定了編譯系統何時,如何來編譯指定的目標。規則的語法如下: 目標:依賴 指令 請特別注意,指令前面有一個tab空格。 同時指令可以直接寫在依賴後面,即同一行上,並且這種情況下指令前可以

從頭開始寫專案Makefile基本規則

【版權宣告:轉載請保留出處:blog.csdn.net/gentleliu。Mail:shallnew at 163 dot com】 一般一個稍大的linux專案會有很多個原始檔組成,最終的可執行程式也是由這許多個原始檔編譯連結而成的。編譯是把一個.c或.cpp檔案編譯成

javascript資料結構與演算法筆記普通佇列

javascript資料結構與演算法筆記(二):普通佇列 一:簡介 二:ES6版Queue類 一:簡介 佇列是遵循FIFO( First In First Out, 先進先出,也稱為先來先服務)原則的一組有序的項。 佇

從頭開始寫專案Makefile模式規則

【版權宣告:轉載請保留出處:blog.csdn.net/gentleliu。Mail:shallnew at 163 dot com】     上一節講到目錄建立成功,目標檔案沒有生產到對應目錄下,這裡我們先給目標檔案加上對應目錄,這樣的話產生對應的目標檔案會直接生成到對應

移動推薦演算法基於簡單規則的預測

本文為轉載文章,來源為: https://blog.csdn.net/Snoopy_Yuan/article/details/72850601 一直在探索資料探勘、資料建模的案例,百度搜到這篇文章,收穫頗豐,轉載以作記錄。 前言:移動推薦演算法是阿里天池賽2015年賽題之

makefilemakefile規則中的命令

規則中的命令被傳遞給shell進行解析執行。除跟在依賴後面的第一條命令以外,其他命令必須與tab鍵開頭。如下: 目標:依賴;命令1 命令2 通常情況下也並不推薦這種寫法,推薦的寫法還是將命令1,另起一行,並以tab鍵開頭,這樣做的好處,就是便於觀

Spring-Cloud-Ribbon學習筆記自定義負載均衡規則

lan cse 重新啟動 ping for obi .config 流行 prope Ribbon自定義負載均衡策略有兩種方式,一是JavaConfig,一是通過配置文件(yml或properties文件)。 需求 假設我有包含A和B服務在內的多個微服務,它們均註冊在一個E

Javascript面向對象編程構造函數的繼承

沒有 cal type 這一 今天 nts 實現繼承 刪除 函數綁定 今天要介紹的是,對象之間的"繼承"的五種方法。 比如,現在有一個"動物"對象的構造函數。   function Animal(){     this.species = "動物";   } 還有一個

虛擬化虛擬化及vmware workstation產品使用

應該 server esxi aof 手機 text 產品 窗體 pass 虛擬化(一):虛擬化及vmware產品介紹 vmware workstation的最新版本號是10.0.2。相信大家也都使用過,當中的簡單的虛擬機的創建。刪除等,都非常easy

CSS3動畫波浪效果

col -1 loading ack css代碼 code load width ase 實現效果 如圖所示: 首先得準備三張圖,一張是淺黃色的背景圖loading_bg.png,一張是深紅色的圖loading.png,最後一張為bolang.png。 css代碼

設計模式 工廠模式

dem blank hibernate 執行 oid code 做出 void actor 工廠模式 工廠模式(Factory Pattern)是 Java 中最常用的設計模式之一。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。 在工廠模式中,我們在創建

iptables實用教程管理鏈和策略

否則 命令顯示 accept 目的 number cep 存在 當前 末尾 概念和原理請參考上一篇文章“iptables實用教程(一)”。 本文講解如果管理iptables中的鏈和策略。 下面的代碼格式中,下劃線表示是一個占位符,需要根據實際情況輸入參數,不帶下劃線的表示是

javascript學習筆記定義函數、調用函數、參數、返回值、局部和全局變量

兩個 cnblogs bsp 結果 value ava ase com 調用 定義函數、調用函數、參數、返回值 關鍵字function定義函數,格式如下: function 函數名(){ 函數體 } 調用函數、參數、返回值的規則和c語言規則類似。 1 <!DOC

Nginx實用教程配置文件入門

affinity type 服務 源碼編譯 設置時間 shutdown ber 可用 控制指令 Nginx配置文件結構 nginx配置文件由指令(directive)組成,指令分為兩種形式,簡單指令和區塊指令。 一條簡單指令由指令名、參數和結尾的分號(;)組成,例如:

Python和C|C++的混編利用Cython進行混編

cde uil 有時 當前 class def 將在 python 混編 還能夠使用Cython來實現混編 1 下載Cython。用python setup.py install進行安裝 2 一個實例 ① 創建helloworld文件夾創建hellowor

RabbitMQ消息隊列”Hello, World“

復雜 article ins don title apple lar github publish 本文將使用Python(pika 0.9.8)實現從Producer到Consumer傳遞數據”Hello, World“。 首先復習一下上篇所學:RabbitM

ASP.NET MVC5控制器、視圖與模型

script pcr 靜態 簡單 err ice message blog 控制器 前言   本篇博文主要介紹ASP.NET MVC中的三個核心元素:控制器、視圖與模型,以下思維導圖描述了本文的主要內容。 控制器 控制器簡介   在介紹控制器之前,簡單的介紹一下MVC工

CSS學習筆記特性

code 背景色 左移 line tex lin 安裝 其中 cas 一、顏色特性 1. 前景色:color 用種方式指定前景色,3種方式分別是rgb顏色,#16進制編碼,顏色名稱: color: rgb(100,100,100); color: #ee3e80; col

python中關於操作時間的方法使用datetime模塊

log time模塊 bsp lib .py nth mon target ear 使用datetime模塊來獲取當前的日期和時間 1 import datetime 2 i=datetime.datetime.now() 3 print ("當前的日期和時間是%

JS筆記隱式轉換

-1 筆記 總結 img 轉換 隱式轉換 基礎 blog com 最近剛開始復習JS的基礎知識,看到隱式轉換這一塊,發現它的規則很多,紅寶書上列出的框框又有些冗雜,所以這裏我借一個式子總結一下其中的規律以及一些有趣的現象。 JS筆記(二):隱式轉換