1. 程式人生 > >Makefile的基本規則

Makefile的基本規則

一、首先了解make是如何工作的,在預設的方式下,也就是我們只輸入make命令。會依次執行:
    1、make會在當前目錄下找名字叫“Makefile”或“makefile”的檔案。
    2、如果找到,它會找檔案中的第一個目標檔案(target)。
    3、如果edit檔案不存在,或是edit所依賴的後面的 .o 檔案的檔案修改時間要比edit這個檔案新,那麼,他就會執行後面所定義的命令來生成edit這個檔案。
    4、如果edit所依賴的.o檔案也存在,那麼make會在當前檔案中找目標為.o檔案的依賴性,如果找到則再根據那一個規則生成.o檔案。(這有點像一個堆疊的過程)
    5、當然,你的C檔案和H檔案是存在的啦,於是make會生成 .o 檔案,然後再用 .o 檔案生命make的終極任務,也就是執行檔案edit了。

二、Makefile裡主要包含了五個東西:顯式規則、隱晦規則、變數定義、檔案指示和註釋。
1、顯式規則。顯式規則說明了,如何生成一個或多的的目標檔案。這是由Makefile的書寫者明顯指出,要生成的檔案,檔案的依賴檔案,生成的命令。
2、隱晦規則。由於我們的make有自動推導的功能,所以隱晦的規則可以讓我們比較粗糙地簡略地書寫Makefile,這是由make所支援的。
3、變數的定義。在Makefile中我們要定義一系列的變數,變數一般都是字串,這個有點你C語言中的巨集,當Makefile被執行時,其中的變數都會被擴充套件到相應的引用位置上。
4、檔案指示。其包括了三個部分,一個是在一個Makefile中引用另一個Makefile,就像C語言中的include一樣;另一個是指根據某些情況指定Makefile中的有效部分,就像C語言中的預編譯#if一樣;還有就是定義一個多行的命令。有關這一部分的內容,我會在後續的部分中講述。
5、註釋。Makefile中只有行註釋,和UNIX的Shell指令碼一樣,其註釋是用“#”字元,這個就像C/C++中的“//”一樣。如果你要在你的Makefile中使用“#”字元,可以用反斜框進行轉義,如:“\#”。
最後,還值得一提的是,在Makefile中的命令,必須要以[Tab]鍵開始。

三、自動化變數
所謂自動化變數,就是這種變數會把模式中所定義的一系列的檔案自動地挨個取出,直至所有的符合模式的檔案都取完了。
下面是所有的自動化變數及其說明:
[email protected]
    表示規則中的目標檔案集。
$%
    僅當目標是函式庫檔案中,表示規則中的目標成員名。例如,如果一個目標是"foo.a(bar.o)",那麼,"$%"就是"bar.o","[email protected]"就是"foo.a"。如果目標不是函式庫檔案(Unix下是[.a],Windows下是[.lib]),那麼,其值為空。
$<
    依賴目標中的第一個目標名字。如果依賴目標是以模式(即"%")定義的,那麼"$<"將是符合模式的一系列的檔案集。注意,其是一個一個取出來的。
$?
    所有比目標新的依賴目標的集合。以空格分隔。
$^
    所有的依賴目標的集合。以空格分隔。如果在依賴目標中有多個重複的,那個這個變數會去除重複的依賴目標,只保留一份。
$+
    這個變數很像"$^",也是所有依賴目標的集合。只是它不去除重複的依賴目標。
$* 
   這個變量表示目標模式中"%"及其之前的部分。如果目標是"dir/a.foo.b",並且目標的模式是"a.%.b",那麼,"$*"的值就是"dir/a.foo"。這

在上述所列出來的自動量變數中。四個變數([email protected]、$<、$%、$*)在擴充套件時只會有一個檔案,而另三個的值是一個檔案列表。我們使用函式"dir"或"notdir"就可以做到了。"D"的含義就是Directory,就是目錄,"F"的含義就是File,就是檔案。
最後想提醒一下的是,對於"$<",為了避免產生不必要的麻煩,我們最好給$後面的那個特定字元都加上圓括號,比如,"$(<)"就要比"$<"要好一些。
還得要注意的是,這些變數只使用在規則的命令中,而且一般都是"顯式規則"和"靜態模式規則"。其在隱含規則中並沒有意義。

四、注意的其他規則:

1、include:可以引用到其他makefile檔案,和C語言裡面的include有相似之處;

2、Vpath:清除所有已被設定好了的檔案搜尋目錄;

3、make支援四個萬用字元:“*”,“?”,“~”和“[...]”。這是和linux中的Shell是差不多的;

4、export:使變數傳遞到下級makefile中;

注意的是,有兩個變數,一個是SHELL,一個是MAKEFLAGS,這兩個變數不管你是否export,其總是要傳遞到下層Makefile中,特別是MAKEFILES變數,其中包含了make的引數資訊,如果我們執行總控Makefile”時有make引數或是在上層Makefile中定義了這個變數,那麼MAKEFILES變數將會是這些引數,並會傳遞到下層Makefile中,這是一個系統級的環境變數。

5、=、:=、+=和?=的區別;

是最基本的賦值
:= 是覆蓋之前的值

?= 是如果沒有被賦值過就賦予等號後面的值

+= 是追加等號後面的

特別注意:

 “=”
      make會將整個makefile展開後,再決定變數的值。也就是說,變數的值將會是整個makefile中最後被指定的值。看例子:
            x = foo
            y = $(x) bar
            x = xyz
      在上例中,y的值將會是 xyz bar ,而不是 foo bar 。
 “:=”
      “:=”表示變數的值決定於它在makefile中的位置,而不是整個makefile展開後的最終值。
            x :=foo
            y := $(x)bar
            x := xyz
      在上例中,y的值將會是 foo bar ,而不是 xyz bar 了。

Makefile中常使用函式來處理變數,函式的返回值可以當做變數來使用。

一、字串處理函式

$(subst<from>,<to>,<text>)

名稱:字串替換函式——subst功能:把字串<text>中的<from>字串替換成<to>返回:函式返回被替換過後的字串。

示例: $(subst ee,EE,feet on the street)“feet on the street”中的“ee”替換成“EE”,返回結果是“fEEt on the strEEt”

$(patsubst <pattern>,<replacement>,<text>)

名稱:模式字串替換函式——patsubst功能:查詢<text>中的單詞(單詞以空格“Tab”回車”“換行分隔)是否符合模式<pattern>,如果匹配的話,則以<replacement>替換。這裡,<pattern>可以包括萬用字元“%”,表示任意長度的字串。如果<replacement>中也包含“%”,那麼,<replacement>中的這個“%”將是<pattern>中的那個“%”所代表的字串。(可以用“\”來轉義,以“\%”來表示真實含義的“%”字元)返回:函式返回被替換過後的字串。

示例:$(patsubst %.c,%.o,x.c.c bar.c)

把字串“x.c.c bar.c”符合模式[%.c]的單詞替換成[%.o],返回結果是“x.c.o bar.o”

$(strip <string>)

名稱:去空格函式——strip功能:去掉<string>字串中開頭和結尾的空字元。返回:返回被去掉空格的字串值。示例: $(strip a b c )

把字串“a b c ”去到開頭和結尾的空格,結果是“a b c”

$(findstring<find>,<in>)

名稱:查詢字串函式——findstring功能:在字串<in>中查詢<find>字串。返回:如果找到,那麼返回<find>,否則返回空字串。示例: $(findstring a,a b c)

第一個函式返回“a”字串,第二個返回“”字串(空字串)

$(filter<pattern...>,<text>)

名稱:過濾函式——filter功能:以<pattern>模式過濾<text>字串中的單詞,保留符合模式<pattern>的單詞。可以有多個模式。返回:返回符合模式<pattern>的字串。示例:sources := foo.c bar.c baz.s ugh.h

        foo: $(sources)
               cc $(filter %.c %.s,$(sources)) -o foo

       $(filter %.c %.s,$(sources))返回的值是“foo.cbar.c baz.s”

$(filter-out<pattern...>,<text>)

名稱:反過濾函式——filter-out功能:以<pattern>模式過濾<text>字串中的單詞,去除符合模式<pattern>的單詞。可以有多個模式。返回:返回不符合模式<pattern>的字串。示例:objects=main1.o foo.o main2.o bar.o

        mains=main1.o main2.o
    
        $(filter-out $(mains),$(objects))
返回值是“foo.o bar.o”
       
$(sort <list>)

名稱:排序函式——sort功能:給字串<list>中的單詞排序(升序)。返回:返回排序後的字串。示例:$(sortfoo bar lose)返回“barfoo lose”備註:sort函式會去掉<list>中相同的單詞。

$(word <n>,<text>)

名稱:取單詞函式——word功能:取字串<text>中第<n>個單詞。(從一開始)返回:返回字串<text>中第<n>個單詞。如果<n><text>中的單詞數要大,那麼返回空字串。示例:$(word2, foo bar baz)返回值是“bar”

$(wordlist<s>,<e>,<text>)

名稱:取單詞串函式——wordlist功能:從字串<text>中取從<s>開始到<e>的單詞串。<s><e>是一個數字。返回:返回字串<text>中從<s><e>的單詞字串。如果<s><text>中的單詞數要大,那麼返回空字串。如果<e>大於<text>的單詞數,那麼返回從<s>開始,到<text>結束的單詞串。示例:$(wordlist 2, 3, foo bar baz)返回值是“barbaz”

$(words <text>)

名稱:單詞個數統計函式——words功能:統計<text>中字串中的單詞個數。返回:返回<text>中的單詞數。示例:$(words,foo bar baz)返回值是“3”備註:如果我們要取<text>中最後的一個單詞,我們可以這樣:$(word $(words<text>),<text>)

$(firstword <text>)

名稱:首單詞函式——firstword功能:取字串<text>中的第一個單詞。返回:返回字串<text>的第一個單詞。示例:$(firstwordfoo bar)返回值是“foo”備註:這個函式可以用word函式來實現:$(word 1,<text>)

以上,是所有的字串操作函式,如果搭配混合使用,可以完成比較複雜的功能。這裡,舉一個現實中應用的例子。我們知道,make使用“VPATH”變數來指定依賴檔案的搜尋路徑。於是,我們可以利用這個搜尋路徑來指定編譯器對標頭檔案的搜尋路徑引數CFLAGS,如:

    override CFLAGS +=$(patsubst %,-I%,$(subst :, ,$(VPATH)))

如果我們的“$(VPATH)”值是“src:../headers”,那麼“$(patsubst %,-I%,$(subst :,,$(VPATH)))”將返回“-Isrc-I../headers”,這正是ccgcc搜尋標頭檔案路徑的引數。

二、檔名操作函式

下面我們要介紹的函式主要是處理檔名的。每個函式的引數字串都會被當做一個或是一系列的檔名來對待。

$(dir <names...>)

名稱:取目錄函式——dir功能:從檔名序列<names>中取出目錄部分。目錄部分是指最後一個反斜槓(“/”)之前的部分。如果沒有反斜槓,那麼返回“./”返回:返回檔名序列<names>的目錄部分。示例:$(dir src/foo.c hacks)返回值是“src/./”

$(notdir <names...>)

名稱:取檔案函式——notdir功能:從檔名序列<names>中取出非目錄部分。非目錄部分是指最後一個反斜槓(“/”)之後的部分。返回:返回檔名序列<names>的非目錄部分。示例:$(notdir src/foo.c hacks)返回值是“foo.chacks”
 
$(suffix <names...>)
   
   
名稱:取字尾函式——suffix功能:從檔名序列<names>中取出各個檔名的字尾。返回:返回檔名序列<names>的字尾序列,如果檔案沒有後綴,則返回空字串。示例:$(suffixsrc/foo.c src-1.0/bar.c hacks)返回值是“.c.c”

$(basename <names...>)

名稱:取字首函式——basename功能:從檔名序列<names>中取出各個檔名的字首部分。返回:返回檔名序列<names>的字首序列,如果檔案沒有字首,則返回空字串。示例:$(basenamesrc/foo.c src-1.0/bar.c hacks)返回值是“src/foosrc-1.0/bar hacks”

$(addsuffix<suffix>,<names...>)

名稱:加字尾函式——addsuffix功能:把字尾<suffix>加到<names>中的每個單詞後面。返回:返回加過後綴的檔名序列。示例:$(addsuffix.c,foo bar)返回值是“foo.cbar.c”

$(addprefix<prefix>,<names...>)

名稱:加字首函式——addprefix功能:把字首<prefix>加到<names>中的每個單詞後面。返回:返回加過字首的檔名序列。示例:$(addprefixsrc/,foo bar)返回值是“src/foosrc/bar”

$(join<list1>,<list2>)

名稱:連線函式——join功能:把<list2>中的單詞對應地加到<list1>的單詞後面。如果<list1>的單詞個數要比<list2>的多,那麼,<list1>中的多出來的單詞將保持原樣。如果<list2>的單詞個數要比<list1>多,那麼,<list2>多出來的單詞將被複制到<list2>中。返回:返回連線過後的字串。示例:$(joinaaa bbb , 111 222 333)返回值是“aaa111bbb222 333”

三、其他函式

1)call函式是唯一一個可以用來建立新的引數化的函式:$(call<expression>,<parm1>,<parm2>,<parm3>...)

make執行這個函式時,<expression>引數中的變數,如$(1)$(2)$(3)等,會被引數<parm1><parm2><parm3>依次取代。而<expression>的返回值就是call函式的返回值。例如:

reverse =  $(1) $(2)
foo = $(call reverse,a,b)

那麼,foo的值就是ab”。當然,引數的次序是可以自定義的,不一定是順序的,如:

reverse = $(2) $(1)
foo = $(call reverse,a,b)

此時的foo的值就是ba”。

2)origin函式不像其它的函式,他並不操作變數的值,他只是告訴你你的這個變數是哪裡來的?其語法是:$(origin <variable>)

注意,<variable>是變數的名字,不應該是引用。所以你最好不要在<variable>中使用$”字元。Origin函式會以其返回值來告訴你這個變數的“出生情況”,下面,是origin函式的返回值:

undefined

如果<variable>從來沒有定義過,origin函式返回這個值undefined”。

default

如果<variable>是一個預設的定義,比如CC”這個變數,這種變數我們將在後面講述。

environment

如果<variable>是一個環境變數,並且當Makefile被執行時,-e”引數沒有被開啟。

file

如果<variable>這個變數被定義在Makefile中。

commandline

如果<variable>這個變數是被命令列定義的。

override

如果<variable>是被override指示符重新定義的。

automatic

如果<variable>是一個命令執行中的自動化變數。關於自動化變數將在後面講述。

這些資訊對於我們編寫Makefile是非常有用的,例如,假設我們有一個Makefile其包了一個定義檔案Make.def,在Make.def中定義了一個變數bletch”,而我們的環境中也有一個環境變數bletch”,此時,我們想判斷一下,如果變數來源於環境,那麼我們就把之重定義了,如果來源於Make.def或是命令列等非環境的,那麼我們就不重新定義它。於是,在我們的Makefile中,我們可以這樣寫:

    ifdef bletch
    ifeq "$(origin bletch)""environment"
    bletch = barf, gag, etc.
    endif
    endif

當然,你也許會說,使用override關鍵字不就可以重新定義環境中的變量了嗎?為什麼需要使用這樣的步驟?是的,我們用override是可以達到這樣的效果,可是override過於粗暴,它同時會把從命令列定義的變數也覆蓋了,而我們只想重新定義環境傳來的,而不想重新定義命令列傳來的。

3)shell函式也不像其它的函式。顧名思義,它的引數應該就是作業系統Shell的命令。它和反引號`”是相同的功能。這就是說,shell函式把執行作業系統命令後的輸出作為函式返回。於是,我們可以用作業系統命令以及字串處理命令awksed等等命令來生成一個變數,如:contents := $(shell cat foo)

注意:這個函式會新生成一個Shell程式來執行命令,所以你要注意其執行效能,如果你的Makefile中有一些比較複雜的規則,並大量使用了這個函式,那麼對於你的系統性能是有害的。特別是Makefile的隱晦的規則可能會讓你的shell函式執行的次數比你想像的多得多。

相關推薦

初學Makefile——基本規則和習慣

面試被問到關於Makefile的問題,除了讀u-boot和核心的Makefile等機會偶爾接觸,根本就不熟悉,說讀出個大概意思吧?很多細節不知道又不容易理解,沒辦法,還是從頭動手練一下的好,這個行當,任何時候,不動手都是不行的。 可是話說回來,沒什麼專案什麼的怎麼找機會練

【一】makefile基本規則

1、基本格式: target(目標):prerequisites(依賴) command ... ... 注意:command的縮排必須使用tab,使用空格時會error。例子: image_bs: image_bs.o ../common/profile.o

從頭開始寫專案Makefile(一):基本規則

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

Makefile基本規則

一、首先了解make是如何工作的,在預設的方式下,也就是我們只輸入make命令。會依次執行:     1、make會在當前目錄下找名字叫“Makefile”或“makefile”的檔案。     2、如果找到,它會找檔案中的第一個目標檔案(target)。     3、如

大數據學習之Scala中main函數的分析以及基本規則(2)

語言 python rgs 數字 popu 結束 圖片 區別 返回 一、main函數的分析 首先來看我們在上一節最後看到的這個程序,我們先來簡單的分析一下。有助於後面的學習 object HelloScala { def main(args:

ad 原件布局布線基本規則

發熱 印刷 可用 焊盤 接插件 pcb -c 頻率 經濟 一、原件布局基本規則   1、按照電路模塊進行布局,電路中的元件應該采用集中就近原則,同時數字電路和模擬電路分開;   2、定位孔、標準孔等周圍1.27mm內不得貼元器件,安裝孔周圍3.5mm不得特裝元件   3、臥

LabWindows/CVI第一章:基本規則

sta 控制 nbsp std class 整型 turn col 浮點型 一、 #include<stdio.h> //頭文件,#號是預處理指令,standard input output header的縮寫。 void main()

字典基本規則以及介紹

輸出 字典 介紹 ict pri 通過 動態 div for dict 1.鍵值對 2.字典的value可以是任何值,比如列表,元組,字典等等 3.列表,字典不能作為字典的key,因為列表是動態的 ,可修改,而元組可以 4.字典是無序的,通過多次print確認是否每次輸出的

寫作六條基本規則

萬能 character 表達 ppr collate isis led shu 今天 記住喬治·奧威爾(George Orwell)的六條基本規則(來自奧威爾《政治與英語》)。 - 絕不要使用在印刷物裏經常看到的隱喻、明喻和其他修辭方法。 - 如果

加速I/O的基本規則

訪問 大量 file 基本 調用 intro 緩沖 返回 範例 作為這個討論的開始,這裏有幾個如何加速I/O的基本規則: 1. 避免訪問磁盤 2. 避免訪問底層的操作系統 3. 避免方法調用 4. 避免個別的處理字節和字符 很明顯這些規則不能在所有的問題上避免,因

IDA Pro 權威指南學習筆記(四) - IDA 用戶界面的基本規則

上下 工具 庫文件 發生 執行 ida 基於 需要 位置 基本規則: IDA 不提供撤銷功能 如果由於不小心按下某個鍵,導致數據庫文件發生意外,這時需要將顯示窗口恢復到以前的狀態 幾乎所有的操作都有其對應的菜單項、熱鍵和工具欄按鈕 IDA 的工具欄高度可配置,就像熱

Perl語法的基本規則

調試 定義 引號 後綴 bin 函數 pre 通過 基本 perl腳本第一行使用#!。perl的後綴名一般為".plx",運行時使用perl NAME.plx即可 例如,1.plx內容如下: #!/usr/bin/perl print "h

【異常處理】之 基本規則基本規範

一、不要捕獲Java類庫中定義的繼承自 RuntimeException 的執行時異常類,比如空指標、比如陣列越界 二、try-catch 的用法,catch時,應該分清楚穩定程式碼和非穩定程式碼。注意對異常的細化 三、異常捕獲了,就要進行相應的處理。或者可以將異常進行上

rust學習筆記基礎篇2--基礎變數宣告的基本規則(霜之小刀)

rust學習筆記基礎篇2–基礎變數宣告的基本規則(霜之小刀) 歡迎轉載和引用,若有問題請聯絡 若有疑問,請聯絡 Email : [email protected] QQ:2279557541 Rust的對變數的宣告很嚴格,有嚴格的型別和必須在宣

【android】Android平臺編譯makefile編寫規則

Date: 2018.10.9 1、參考 https://www.ibm.com/developerworks/cn/opensource/os-cn-android-build/ https://blog.csdn.net/yuanjize1996/article/det

Java 異常處理基本規則,Java異常處理的基本規範

看了團隊中原來程式碼中的異常處理,心碎了一地,稍微對照阿里巴巴的異常處理規範整理了一遍,準備分享一下,Java的異常處理規範&約束。 一、執行異常的撲捉 不要捕獲   Java  類庫中定義的繼承自 RuntimeException&nbs

makefile 書寫規則

規則包含兩個部分,一個是依賴關係,一個是生成目標的方法。 在 Makefile 中,規則的順序是很重要的,因為,Makefile 中只應該有一個最終目標,其它的目標都是被這個目標所連帶出來的,所以一定要讓 make 知道你的最終目標是什麼。一般來說,定義在 Makefile

跟我一起寫Makefile:隱含規則

轉載: 隱含規則 在我們使用Makefile時,有一些我們會經常使用,而且使用頻率非常高的東西,比如,我們編譯C/C++的源程式為中間目標檔案(Unix下是[.o]檔案,Windows下是[.obj]檔案)。本章講述的就是一些在Makefile中的“隱含的”,早先約定

ES6模組化的基本規則

1  每一個模組只加載一次, 每一個JS只執行一次,如果下次載入相同的檔案, 只會從記憶體中直接讀取, 一個模組就是一個單例【???】 2 每個模組內宣告的變數都是區域性變數, 不會汙染全域性變數 3  模組內部的變數或者函式可以通過 export匯出 4 一個模組可

node——12-模組系統-基本規則

模組匯出——僅物件 main.js var fooExports = require('./foo'); // ReferenceError: foo is not defined // consol