makefile 簡單使用

 

規則

makefile基本格式

target ... : prerequisites ...
command
...
...

格式說明

  • target: 個目標文件或者標籤(Label)
  • prerequisites: 生成 target 所需要的文件
  • command: make 需要執行的命令(任意的Shell命令)

這是一個文件的依賴關係,target 這個目標文件依賴於 prerequisites 中的文件,其生成規則定義在 command 中.prerequisites 中如果有一個以上的文件比 target 文件要新的話,command 所定義的命令就會被執行 (makefile中最核心的內容)

例子:

a : b.o c.o d.o
	gcc -o a b.o c.o d.o
b.o : b.c b.h
	gcc -c b.c
c.o : c.c c.h
	gcc -c c.c
d.o : d.c d.h
	gcc -c d.c
clean :
	rm a b.o c.o d.o

 

文件名規則

默認的情況下,make 命令會在當前目錄下按順序找尋文件名爲 “GNUmakefile”、“makefile”、“Makefile” 的文件,找到了解釋這個文件.在這三個文件名中,最好使用 “Makefile” 這個文件名,因爲,這個文件名第一個字符爲大寫,這樣有一種顯目的感覺.最好不要用 “GNUmakefile”,這個文件是 GNU 的 make 識別的.有另外一些 make 只對全小寫的 “makefile” 文件名敏感,但是基本上來說,大多數的 make 都支持 “makefile” 和 “Makefile” 這兩種默認文件名.

當然,你可以使用別的文件名來書寫 Makefile,比如:“Make.Linux”,“Make.Solaris”,“Make.AIX” 等,如果要指定特定的 Makefile,你可以使用 make 的 “-f” 和 “--file” 參數,如:make -f Make.Linuxmake --file Make.AIX.

 

內容

makefile裏主要包含了五個東西:顯式規則、隱晦規則、變量定義、文件指示和註釋.

  • 顯式規則:顯式規則說明了,如何生成一個或多的的目標文件.這是由 makefile 的書寫者明顯指出,要生成的文件,文件的依賴文件,生成的命令.
  • 隱晦規則:由於我們的 make 有自動推導的功能,所以隱晦的規則可以讓我們比較簡略地書寫 makefile, 這是由 make 所支持的.
  • 變量定義:在 makefile 中我們要定義一系列的變量,變量一般都是字符串,這個有點像你 C 語言中的宏,當 makefile 被執行時,其中的變量都會被擴展到相應的引用位置上.
  • 文件指示:其包括了三個部分,一個是在一個 makefile 中引用另一個 makefile, 就像 C 語言中的 include 一樣;另一個是指根據某些情況指定 makefile 中的有效部分,就像 C 語言中的預編譯 #if 一樣;還有就是定義一個多行的命令.有關這一部分的內容,我會在後續的部分中講述.
  • 註釋:makefile中只有行註釋,和UNIX的Shell腳本一樣,其註釋是用“#”字符,這個就像C/C++中的“//”一樣.如果你要在你的makefile中使用“#”字符,可以用反斜框進行轉義,如:“\#”.

變量

makefile中使用變量

說明:

  1. 變量在聲明時需要給與初值,而在使用時,需要給在變量名前家上 "$" 符號,但最好用小括號 "()" 或者大括號 "{}" 把變量給括起來,如果你要使用真實的 "$ "字符,那麼你需要用 "$$" 來表示
  2. 在 Makefile 中的定義的變量,就像是 C/C++ 語言中的宏一樣,他代表了一個文本字串,在 Makefile 中執行的時候其會自動原模原樣地展開在所使用的地方.其與 C/C++ 所不同的是,你可以在 Makefile 中改變其值.
  3. 變量的命名字可以包含字符、數字,下劃線 (可以是數字開頭),但不應該含有 ":" 、"#"、"=" 或是空字符(空格、回車等).變量是大小寫敏感.

使用:

  • 變量的定義:變量名 = 變量實際內容
  • 使用變量:$(變量名)

例子:

objects = b.o c.o d.o

a : $(objects)
	cc -o a $(objects)
b.o : b.c b.h
	cc -c b.c
c.o : c.c c.h
	cc -c c.c
d.o : d.c d.h
	cc -c d.c
clean :
	rm a $(objects)

自動化變量

  1. $@  
    表示規則的目標文件名.如果目標是一個文檔文件(Linux中,一般稱.a文件爲文檔文件,也稱爲靜態庫文件),那麼它代表這個文檔的文件名.在多目標模式規則中,它代表的是哪個觸發規則被執行的目標文件名.
  2. $%  
    當規則的目標文件是一個靜態庫文件時,代表靜態庫的一個成員名.例如,規則的目標是“foo.a(bar.o)”,那麼,“$%”的值就爲“bar.o”,“$@”的值爲“foo.a”.如果目標不是靜態庫文件,其值爲空.
  3. $<  
    規則的第一個依賴文件名.如果是一個目標文件使用隱含規則來重建,則它代表由隱含規則加入的第一個依賴文件.
  4. $?  
    所有比目標文件更新的依賴文件列表,空格分割.如果目標是靜態庫文件名,代表的是庫成員(.o文件).
  5. $^  
    規則的所有依賴文件列表,使用空格分隔.如果目標是靜態庫文件,它所代表的只能是所有庫成員(.o文件)名.一個文件可重複的出現在目標的依賴中,變量“$^”只記錄它的一次引用情況.就是說變量“$^”會去掉重複的依賴文件.
  6. $+  
    類似“$^”,但是它保留了依賴文件中重複出現的文件.主要用在程序鏈接時庫的交叉引用場合.
  7. $*
    這個變量表示目標模式中"%"及其之前的部分.如果目標是"dir/a.foo.b",並且目標的模式是"a.%.b",那麼,"$*"的值就是"dir/a.foo".這個變量對於構造有關聯的文件名是比較有較.如果目標中沒有模式的定義,那麼"$*"也就不能被推導出,但是,如果目標文件的後綴是make所識別的,那麼"$*"就是除了後綴的那一部分.例如:如果目標是"foo.c",因爲".c"是make所能識別的後綴名,所以,"$*"的值就是"foo".這個特性是GNU make的,很有可能不兼容於其它版本的make,所以,你應該儘量避免使用"$*",除非是在隱含規則或是靜態模式中.如果目標中的後綴是make所不能識別的,那麼"$*"就是空值.

例子:

test:main.o hello.o
	g++ -o $@ main.o hello.o
main.o:main.cpp hello.h
	g++ -c $< -Iinclude
hello.o:hello.cpp hello.h
	g++ -c $< -Iinclude
.PHONY:clean
clean:
	 -rm test hello.o

 

僞目標

說明

爲了讓目標不是作爲目標文件而是一個標籤而已,從而不生成目標文件,讓 make 無法生成它的依賴關係和決定它是否執行,只有顯示第指明這個"目標"才能讓其生效.當然"僞目標"的取名不能和文件名重名,不然就失去了"僞目標"的意義

爲了避免和文件重名的這種情況,可以使用一個特殊的表示 .PHONY 來顯示指明一個目標是"僞目標",向 make 說明不給是否有這個文件,這個目標就是 "僞目標"

例子

.PHONY : clean
clean :
	-rm edit $(objects)

前面說過,.PHONY 意思表示 clean 是一個“僞目標”,而在 rm 命令前面加了一個小減號的意思就是,也許某些文件出現問題,但不要管,繼續做後面的事.當然,clean 的規則不要放在文件的開頭,不然,這就會變成 make 的默認目標,相信誰也不願意這樣.不成文的規矩是——“clean 從來都是放在文件的最後”.

工作

引用其它的Makefile

說明

在 Makefile 使用 include 關鍵字把別的 Makefile 包含進來,被包含的文件會原模原樣的放在當前文件的包含位置

語法格式

include file

註解:file 可以是當前操作系統 Shell 的文件模式(可含路徑和通配符)在 include 前可有一些空字符,但絕不能是 [Tab] 鍵開始.include 和 file 可以用一個或多個空格隔開

例子:

目錄結構 -------------------------------------------
| -- makefile
| -- a.mk
| -- b.mk
`-- src
    | -- c.mk
    `-- d.mk

makefile--------------------------------------------

include *.mk src/*.mk
#等價於:include a.mk b.mk src/c.mk src/d.mk

工作方式:

make 命令開始時,會把找尋 include 所指出的其它 Makefile,並把其內容安置在當前的位置.如果文件都沒有指定絕對路徑或是相對路徑的話,make 會在當前目錄下首先尋找,如果當前目錄下沒有找到,那麼,make 還會在下面的幾個目錄下找:

    1. 如果 make 執行時,有 “-I” 或 “--include-dir” 參數,那麼make就會在這個參數所指定的目錄下去尋找.
    2. 如果目錄 <prefix>/include(一般是:/usr/local/bin 或 /usr/include)存在的話,make 也會去找.

如果有文件沒有找到的話,make 會生成一條警告信息,但不會馬上出現致命錯誤.它會繼續載入其它的文件,一旦完成 makefile 的讀取,make 會再重試這些沒有找到,或是不能讀取的文件,如果還是不行,make 纔會出現一條致命信息.如果你想讓 make 不理那些無法讀取的文件,而繼續執行,你可以在 include 前加一個減號 "-" .如:

-include <filename>

其表示,無論 include 過程中出現什麼錯誤,都不要報錯繼續執行.和其它版本 make 兼容的相關命令是 sinclude,其作用和這一個是一樣的.

文件搜索

VPATH

說明:

makefile 文件中有個特殊的變量 VPATH ,這個變量的作用是在 make 在但前目錄找不到的情況下,VPATH 變量中保存的路徑中去找尋文件,若沒有指明這個變量, make 只會在當前的目錄下去找尋依賴文件和目標文件,如果有多個目錄可以以 ":" 分隔多個目錄

例子:

  VPATH = src:../headers   #在本目錄下的 src 目錄下去找和在上級目錄的 headers 目錄下去找依賴文件

vpath

說明:

vpath 是 make 中的關鍵字,它的作用和上面的 VPATH 作用相似,但是它更爲靈活.它可以在不同的搜索目錄中指定不同的文件

使用方法:

  1. vpath pattern dir  爲符合模式 pattern 的文件指定搜索目錄 dir ,如果有多個目錄可以以 ":" 分隔多個目錄
  2. vpath pattern  清除符合模式 pattern 的文件搜索目錄
  3. vpath  清除所有已被設置好了的文件搜索目錄

例子:

vpath %.h ../headers    #到上級目錄下的 headers 目錄下去找尋所有以 ".h" 表示的頭文件, "%" 字符表示匹配一個以上的字符

例子:

文件目錄結構--------------------------------------------------

|-- Makefile
|-- main.cpp
|-- include
|   `-- hello.h
`-- src
    `-- hello.cpp

程序源碼--------------------------------------------------

/****************************************/
//hello.h:
#ifndef _HELLO_H__
#define _HELLO_H__
#include<iostream>
using namespace std;
void hello();
#endif

/****************************************/
//hello.cpp:
#include"hello.h"
void hello()
{
        cout<<"Hello world"<<endl;
}

/****************************************/
//main.cpp:
#include"hello.h"
int main()
{
          hello();
          return 0;
}

makefile內容 --------------------------------------------------

#VPATH=include:src
vpath %.cpp src
vpath %.h include
test:main.o hello.o
	g++ -o $@ main.o hello.o
main.o:main.cpp hello.h
	g++ -c $< -I include
	#-----------------------------------------------
	#"$<" 爲自動化變量,它的作用是把 make 找到的源文件用正確的路徑形式表示在 g++ 的命令中
	#如這裏的 "$<" 表示的就是 "main.ccp" 而下面一個命令中的 "$<" 表示的就是 "src/hello.cpp"
	#-----------------------------------------------
	#雖然前面 vpath %.h include 告訴le make ,所依賴的頭文件在在本目錄和子目錄 include 下找
	#源文件在本目錄和子目錄 src 下找,但是不會把這個消息告訴 gcc 或 g++
	#所以還是要手動把頭文件所在目錄告訴給 g++ 或 gcc ,即: I -inlude
hello.o:hello.cpp hello.h
	g++ -c $< -I include
.PHONY:clean
clean:
	-rm test hello.o

參考文獻

Makefile中自動化變量 http://blog.sina.com.cn/s/blog_45497dfa0100jk09.html

Makefile VPATH和vpath的使用 http://blog.csdn.net/changli_90/article/details/7881905

跟我一起寫 Makefile(四)http://blog.csdn.net/haoel/article/details/2889

使用變量 http://wiki.ubuntu.org.cn/index.php?title=%E8%B7%9F%E6%88%91%E4%B8%80%E8%B5%B7%E5%86%99Makefile:%E4%BD%BF%E7%94%A8%E5%8F%98%E9%87%8F&variant=zh-hant

make工作流程

objects = b.o c.o d.o

a : $(objects)
	cc -o a $(objects)
b.o : b.c b.h
	cc -c b.c
c.o : c.c c.h
	cc -c c.c
d.o : d.c d.h
	cc -c d.c
clean :
	rm a $(objects)
  1. make 會在當前目錄下找名字叫“makefile”或“makefile”的文件.
  2. 如果找到,它會找文件中的第一個目標文件(target),在上面的例子中,他會找到 “a” 這個文件,並把這個文件作爲最終的目標文件.
  3. 如果 a 文件不存在,或是 a 所依賴的後面的 .o 文件的文件修改時間要比 a 這個文件新,那麼,他就會執行後面所定義的命令來生成 a 這個文件.
  4. 如果 a 所依賴的.o文件也不存在,那麼 make 會在當前文件中找目標爲 .o 文件的依賴性,如果找到再根據那一個規則生成 .o 文件.(這有點像一個堆棧的過程)
  5. 當然,你的 C 文件和 H 文件存在的,於是 make 會生成 .o 文件,然後再用 .o 文件生成 make 的終極任務,也就是執行文件 a 了. 

這就是整個 make 的依賴性, make 會一層又一層地去找文件的依賴關係,直到最終編譯出第一個目標文件.在找尋的過程中,如果出現錯誤,比如最後被依賴的文件找不到,那麼 make 就會直接退出,並報錯,而對於所定義的命令的錯誤,或是編譯不成功,make 根本不理.make 只管文件的依賴性,即,如果在我找了依賴關係之後,冒號後面的文件還是不在,那麼 make 就不會工作.

make 工作執行步驟

  1. 讀入所有的 makefile.
  2. 讀入被 include 的其它 makefile.
  3. 初始化文件中的變量.
  4. 推導隱晦規則,並分析所有規則.
  5. 爲所有的目標文件創建依賴關係鏈.
  6. 根據依賴關係,決定哪些目標要重新生成.
  7. 執行生成命令.

make自動推導

只要 make 看到一個[.o]文件,它就會自動的把[.c]文件加在依賴關係中,如果 make 找到一個 b.o,那麼 b.c 就會是 b.o的依賴文件.並且 gcc -c b.c 也會被推導出來,於是,我們的 makefile 再也不用寫得這麼複雜.

objects = b.o c.o d.o

a : $(objects)
	cc -o a $(objects)
b.o : b.c b.h
c.o : c.c c.h
d.o : d.c d.h

clean :
	rm a $(objects) 

make命令

make的參數

下面列舉了所有GNU make 3.80版的參數定義.其它版本和產商的make大同小異,不過其它產商的make的具體參數還是請參考各自的產品文檔.

-b
“-m”
這兩個參數的作用是忽略和其它版本make的兼容性.

-B
--always-make
認爲所有的目標都需要更新(重編譯).

-C <dir>
--directory=<dir>
指定讀取makefile的目錄.如果有多個“-C”參數,make的解釋是後面的路徑以前面的作爲相對路徑,並以最後的目錄作爲被指定目錄.如:“make –C ~hchen/test –C prog”等價於“make –C ~hchen/test/prog”.

—debug[=<options>]
輸出make的調試信息.它有幾種不同的級別可供選擇,如果沒有參數,那就是輸出最簡單的調試信息.下面是<options>的取值:
    a —— 也就是all,輸出所有的調試信息.(會非常的多)
    b —— 也就是basic,只輸出簡單的調試信息.即輸出不需要重編譯的目標.
    v —— 也就是verbose,在b選項的級別之上.輸出的信息包括哪個makefile被解析,不需要被重編譯的依賴文件(或是依賴目標)等.
    i —— 也就是implicit,輸出所以的隱含規則.
    j —— 也就是jobs,輸出執行規則中命令的詳細信息,如命令的PID、返回碼等.
    m —— 也就是makefile,輸出make讀取makefile,更新makefile,執行makefile的信息.

-d
相當於“--debug=a”.

-e
--environment-overrides
指明環境變量的值覆蓋makefile中定義的變量的值.

-f=<file>
--file=<file>
--makefile=<file>
指定需要執行的makefile.

-h
--help
顯示幫助信息.

-i
--ignore-errors
在執行時忽略所有的錯誤.

-I <dir>
--include-dir=<dir>
指定一個被包含makefile的搜索目標.可以使用多個“-I”參數來指定多個目錄.

-j [<jobsnum>]
--jobs[=<jobsnum>]
指同時運行命令的個數.如果沒有這個參數,make運行命令時能運行多少就運行多少.如果有一個以上的“-j”參數,那麼僅最後一個“-j”纔是有效的.(注意這個參數在MS-DOS中是無用的)

-k
--keep-going
出錯也不停止運行.如果生成一個目標失敗了,那麼依賴於其上的目標就不會被執行了.

-l <load>
--load-average[=<load]
—max-load[=<load>]
指定make運行命令的負載.

-n
--just-print
--dry-run
--recon
僅輸出執行過程中的命令序列,但並不執行.

-o <file>
--old-file=<file>
--assume-old=<file>
不重新生成的指定的<file>,即使這個目標的依賴文件新於它.

-p
--print-data-base
輸出makefile中的所有數據,包括所有的規則和變量.這個參數會讓一個簡單的makefile都會輸出一堆信息.如果你只是想輸出信息而不想執行makefile,你可以使用“make -qp”命令.如果你想查看執行makefile前的預設變量和規則,你可以使用“make –p –f /dev/null”.這個參數輸出的信息會包含着你的makefile文件的文件名和行號,所以,用這個參數來調試你的makefile會是很有用的,特別是當你的環境變量很複雜的時候.

-q
--question
不運行命令,也不輸出.僅僅是檢查所指定的目標是否需要更新.如果是0則說明要更新,如果是2則說明有錯誤發生.

-r
--no-builtin-rules
禁止make使用任何隱含規則.

-R
--no-builtin-variabes
禁止make使用任何作用於變量上的隱含規則.

-s
--silent
--quiet
在命令運行時不輸出命令的輸出.

-S
--no-keep-going
--stop
取消“-k”選項的作用.因爲有些時候,make的選項是從環境變量“MAKEFLAGS”中繼承下來的.所以你可以在命令行中使用這個參數來讓環境變量中的“-k”選項失效.

-t
--touch
相當於UNIX的touch命令,只是把目標的修改日期變成最新的,也就是阻止生成目標的命令運行.

-v
--version
輸出make程序的版本、版權等關於make的信息.

-w
--print-directory
輸出運行makefile之前和之後的信息.這個參數對於跟蹤嵌套式調用make時很有用.

--no-print-directory
禁止“-w”選項.

-W <file>
--what-if=<file>
--new-file=<file>
--assume-file=<file>
假定目標<file>需要更新,如果和“-n”選項使用,那麼這個參數會輸出該目標更新時的運行動作.如果沒有“-n”那麼就像運行UNIX的“touch”命令一樣,使得<file>的修改時間爲當前時間.

--warn-undefined-variables
只要make發現有未定義的變量,那麼就輸出警告信息.

 

 

參考鏈接:

跟我一起寫 Makefile(十一)http://blog.csdn.net/haoel/article/details/2896

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章