天天看點

Makefile 使用總結

Makefile 是和 make 指令一起配合使用的.

很多大型項目的編譯都是通過 Makefile 來組織的, 如果沒有 Makefile, 那很多項目中各種庫和代碼之間的依賴關系不知會多複雜.

Makefile的組織流程的能力如此之強, 不僅可以用來編譯項目, 還可以用來組織我們平時的一些日常操作. 這個需要大家發揮自己的想象力.

非常感謝 gunguymadman_cu 提供如此詳盡的Makefile介紹, 這正是我一直尋找的Makefile中文文檔.

Makefile基本格式如下:

其中,

target        - 目标檔案, 可以是 Object File, 也可以是可執行檔案

prerequisites - 生成 target 所需要的檔案或者目标

command       - make需要執行的指令 (任意的shell指令), Makefile中的指令必須以 [tab] 開頭

顯示規則 :: 說明如何生成一個或多個目标檔案(包括 生成的檔案, 檔案的依賴檔案, 生成的指令)

隐晦規則 :: make的自動推導功能所執行的規則

變量定義 :: Makefile中定義的變量

檔案訓示 :: Makefile中引用其他Makefile; 指定Makefile中有效部分; 定義一個多行指令

注釋     :: Makefile隻有行注釋 "#", 如果要使用或者輸出"#"字元, 需要進行轉義, "\#"

讀入主Makefile (主Makefile中可以引用其他Makefile)

讀入被include的其他Makefile

初始化檔案中的變量

推導隐晦規則, 并分析所有規則

為所有的目标檔案建立依賴關系鍊

根據依賴關系, 決定哪些目标要重新生成

執行生成指令

規則主要有2部分: 依賴關系 和 生成目标的方法.

文法有以下2種:

或者

*注* command太長, 可以用 "\" 作為換行符

*     :: 表示任意一個或多個字元

?     :: 表示任意一個字元

[...] :: ex. [abcd] 表示a,b,c,d中任意一個字元, [^abcd]表示除a,b,c,d以外的字元, [0-9]表示 0~9中任意一個數字

~     :: 表示使用者的home目錄

當一個Makefile中涉及到大量源檔案時(這些源檔案和Makefile極有可能不在同一個目錄中),

這時, 最好将源檔案的路徑明确在Makefile中, 便于編譯時查找. Makefile中有個特殊的變量 VPATH 就是完成這個功能的.

指定了 VPATH 之後, 如果目前目錄中沒有找到相應檔案或依賴的檔案, Makefile 回到 VPATH 指定的路徑中再去查找..

VPATH 使用方法:

vpath <directories>            :: 目前目錄中找不到檔案時, 就從<directories>中搜尋

vpath <pattern> <directories>  :: 符合<pattern>格式的檔案, 就從<directories>中搜尋

vpath <pattern>                :: 清除符合<pattern>格式的檔案搜尋路徑

vpath                          :: 清除所有已經設定好的檔案路徑

其中 = 和 := 的差別在于, := 隻能使用前面定義好的變量, = 可以使用後面定義的變量

測試 =

測試 :=

作用是使 Makefile中定義的變量能夠覆寫 make 指令參數中指定的變量

文法:

override <variable> = <value>

override <variable> := <value>

override <variable> += <value>

下面通過一個例子體會 override 的作用:

作用是使變量的作用域僅限于這個目标(target), 而不像之前例子中定義的變量, 對整個Makefile都有效.

<target ...> :: <variable-assignment>

<target ...> :: override <variable-assignment> (override作用參見 變量覆寫的介紹)

示例:

Makefile 中書寫shell指令時可以加2種字首 @ 和 -, 或者不用字首.

3種格式的shell指令差別如下:

不用字首 :: 輸出執行的指令以及指令執行的結果, 出錯的話停止執行

字首 @   :: 隻輸出指令執行的結果, 出錯的話停止執行

字首 -   :: 指令執行有錯的話, 忽略錯誤, 繼續執行

僞目标并不是一個"目标(target)", 不像真正的目标那樣會生成一個目标檔案.

典型的僞目标是 Makefile 中用來清理編譯過程中中間檔案的 clean 僞目标, 一般格式如下:

文法: include <filename>  (filename 可以包含通配符和路徑)

寫 Makefile 的時候, 需要确定每個目标的依賴關系.

GNU提供一個機制可以檢視C代碼檔案依賴那些檔案, 這樣我們在寫 Makefile 目标的時候就不用打開C源碼來看其依賴那些檔案了.

比如, 下面指令顯示核心源碼中 virt/kvm/kvm_main.c 中的依賴關系

Makefile的退出碼有以下3種:

0 :: 表示成功執行

1 :: 表示make指令出現了錯誤

2 :: 使用了 "-q" 選項, 并且make使得一些目标不需要更新

預設執行 make 指令時, GNU make在目前目錄下依次搜尋下面3個檔案 "GNUmakefile", "makefile", "Makefile",

找到對應檔案之後, 就開始執行此檔案中的第一個目标(target). 如果找不到這3個檔案就報錯.

非預設情況下, 可以在 make 指令中指定特定的 Makefile 和特定的 目标.

示例:

make 的參數有很多, 可以通過 make -h 去檢視, 下面隻介紹幾個我認為比較有用的.

參數

含義

--debug[=<options>]

輸出make的調試資訊, options 可以是 a, b, v

-j --jobs

同時運作的指令的個數, 也就是多線程執行 Makefile

-r --no-builtin-rules

禁止使用任何隐含規則

-R --no-builtin-variabes

禁止使用任何作用于變量上的隐含規則

-B --always-make

假設所有目标都有更新, 即強制重編譯

這裡隻列一個和編譯C相關的.

編譯C時,<n>.o 的目标會自動推導為 <n>.c

下面隻列出一些C相關的

變量名

RM

rm -f

AR

ar

CC

cc

CXX

g++

ARFLAGS

AR指令的參數

CFLAGS

C語言編譯器的參數

CXXFLAGS

C++語言編譯器的參數

示例: 下面以 CFLAGS 為例示範

Makefile 中很多時候通過自動變量來簡化書寫, 各個自動變量的含義如下:

自動變量

$@

目标集合

$%

當目标是函數庫檔案時, 表示其中的目标檔案名

$<

第一個依賴目标. 如果依賴目标是多個, 逐個表示依賴目标

$?

比目标新的依賴目标的集合

$^

所有依賴目标的集合, 會去除重複的依賴目标

$+

所有依賴目标的集合, 不會去除重複的依賴目标

$*

這個是GNU make特有的, 其它的make不一定支援

在 Makefile 初級文法中已經提到過引用其它 Makefile的方法. 這裡有另一種寫法, 并且可以向引用的其它 Makefile 傳遞參數.

示例: (不傳遞參數, 隻是調用子檔案夾 other 中的Makefile)

示例: (用export傳遞參數)

*補充* export 文法格式如下:

export variable = value

export variable := value

export variable += value

指令包有點像是個函數, 将連續的相同的指令合成一條, 減少 Makefile 中的代碼量, 便于以後維護.

條件判斷的關鍵字主要有 ifeq ifneq ifdef ifndef

示例: ifeq的例子, ifneq和ifeq的使用方法類似, 就是取反

示例: ifdef的例子, ifndef和ifdef的使用方法類似, 就是取反

Makefile 中自帶了一些函數, 利用這些函數可以簡化 Makefile 的編寫.

函數調用文法如下:

<function> 是函數名

<arguments> 是函數參數

字元串替換函數: $(subst <from>,<to>,<text>)

功能: 把字元串<text> 中的 <from> 替換為 <to>

傳回: 替換過的字元串

模式字元串替換函數: $(patsubst <pattern>,<replacement>,<text>)

功能: 查找<text>中的單詞(單詞以"空格", "tab", "換行"來分割) 是否符合 <pattern>, 符合的話, 用 <replacement> 替代.

去空格函數: $(strip <string>)

功能: 去掉 <string> 字元串中開頭和結尾的空字元

傳回: 被去掉空格的字元串值

查找字元串函數: $(findstring <find>,<in>)

功能: 在字元串 <in> 中查找 <find> 字元串

傳回: 如果找到, 傳回 <find> 字元串,  否則傳回空字元串

過濾函數: $(filter <pattern...>,<text>)

功能: 以 <pattern> 模式過濾字元串 <text>, *保留* 符合模式 <pattern> 的單詞, 可以有多個模式

傳回: 符合模式 <pattern> 的字元串

反過濾函數: $(filter-out <pattern...>,<text>)

功能: 以 <pattern> 模式過濾字元串 <text>, *去除* 符合模式 <pattern> 的單詞, 可以有多個模式

傳回: 不符合模式 <pattern> 的字元串

排序函數: $(sort <list>)

功能: 給字元串 <list> 中的單詞排序 (升序)

傳回: 排序後的字元串

取單詞函數: $(word <n>,<text>)

功能: 取字元串 <text> 中的 第<n>個單詞 (n從1開始)

傳回: <text> 中的第<n>個單詞, 如果<n> 比 <text> 中單詞個數要大, 則傳回空字元串

取單詞串函數: $(wordlist <s>,<e>,<text>)

功能: 從字元串<text>中取從<s>開始到<e>的單詞串. <s>和<e>是一個數字.

傳回: 從<s>到<e>的字元串

單詞個數統計函數: $(words <text>)

功能: 統計字元串 <text> 中單詞的個數

傳回: 單詞個數

首單詞函數: $(firstword <text>)

功能: 取字元串 <text> 中的第一個單詞

傳回: 字元串 <text> 中的第一個單詞

取目錄函數: $(dir <names...>)

功能: 從檔案名序列 <names> 中取出目錄部分

傳回: 檔案名序列 <names> 中的目錄部分

取檔案函數: $(notdir <names...>)

功能: 從檔案名序列 <names> 中取出非目錄部分

傳回: 檔案名序列 <names> 中的非目錄部分

取字尾函數: $(suffix <names...>)

功能: 從檔案名序列 <names> 中取出各個檔案名的字尾

傳回: 檔案名序列 <names> 中各個檔案名的字尾, 沒有字尾則傳回空字元串

取字首函數: $(basename <names...>)

功能: 從檔案名序列 <names> 中取出各個檔案名的字首

傳回: 檔案名序列 <names> 中各個檔案名的字首, 沒有字首則傳回空字元串

加字尾函數: $(addsuffix <suffix>,<names...>)

功能: 把字尾 <suffix> 加到 <names> 中的每個單詞後面

傳回: 加過字尾的檔案名序列

加字首函數: $(addprefix <prefix>,<names...>)

功能: 把字首 <prefix> 加到 <names> 中的每個單詞前面

傳回: 加過字首的檔案名序列

連接配接函數: $(join <list1>,<list2>)

功能: <list2> 中對應的單詞加到 <list1> 後面

傳回: 連接配接後的字元串

$(foreach <var>,<list>,<text>)

這裡的if是個函數, 和前面的條件判斷不一樣, 前面的條件判斷屬于Makefile的關鍵字

$(if <condition>,<then-part>)

$(if <condition>,<then-part>,<else-part>)

$(call <expression>,<parm1>,<parm2>,<parm3>...)

$(origin <variable>)

傳回值有如下類型:

類型

undefined

<variable> 沒有定義過

default

<variable> 是個預設的定義, 比如 CC 變量

environment

<variable> 是個環境變量, 并且 make時沒有使用 -e 參數

file

<variable> 定義在Makefile中

command line

<variable> 定義在指令行中

override

<variable> 被 override 重新定義過

automatic

<variable> 是自動化變量

$(shell <shell command>)

它的作用就是執行一個shell指令, 并将shell指令的結果作為函數的傳回.

作用和 `<shell command>` 一樣, ` 是反引号

産生一個緻命錯誤: $(error <text ...>)

功能: 輸出錯誤資訊, 停止Makefile的運作

輸出警告: $(warning <text ...>)

功能: 輸出警告資訊, Makefile繼續運作

如果有過在Linux上, 從源碼安裝軟體的經曆的話, 就會對 make clean, make install 比較熟悉.

像 clean, install 這些僞目标, 廣為人知, 不用解釋就大家知道是什麼意思了.

下面列舉一些常用的僞目标, 如果在自己項目的Makefile合理使用這些僞目标的話, 可以讓我們自己的Makefile看起來更專業, 呵呵 :)

僞目标

all

所有目标的目标,其功能一般是編譯所有的目标

clean

删除所有被make建立的檔案

install

安裝已編譯好的程式,其實就是把目标可執行檔案拷貝到指定的目錄中去

print

列出改變過的源檔案

tar

把源程式打包備份. 也就是一個tar檔案

dist

建立一個壓縮檔案, 一般是把tar檔案壓成Z檔案. 或是gz檔案

TAGS

更新所有的目标, 以備完整地重編譯使用

check 或 test

一般用來測試makefile的流程

本文轉自wang_yb部落格園部落格,原文連結:http://www.cnblogs.com/wang_yb/p/3990952.html,如需轉載請自行聯系原作者