软件开发工具——Make
掌握Makefile的使用方法和工作流程;
掌握make工具變量的相關知識,包括其引用、定義及分類等;
掌握Makefile常見的函數含義;
掌握Makefile與shell命令行的通信方法;
掌握Makefile的常見語法規則,包括顯式規則、隱式規則及靜態模式規則;
了解autotools的用法,了解autotools中常用的工具鏈以及如何使用工具鏈自動創建Makefile文件。
1、Make工具概述
Makefile帶來的好處就是“自動化編譯”,一旦寫好,只需要在shell命令行中輸入一個make命令,整個工程完全自動編譯,可以極大提高軟件開發的效率。
make是一個命令工具,它解釋Makefile中的語法規則。Makefile有自己的書寫格式、關鍵字和函數,這個文件告訴make以何種方式編譯源代碼和鏈接程序。典型地,可執行文件可由一些.o文件按照一定的順序編譯,如果在工程中已經存在Makefile,當對工程中的若干源文件修改以后,可自動根據修改情況完成源文件對應.o文件的更新、庫文件的更新、最終的可執行程序的更新。
那么如果來判斷一個大型工程中哪些文件發生了改變,哪些沒發生變化呢?
Linun系統判斷工程中文件是否發生改變的方法,是判斷文件的建立和修改時間,即“文件時間戳”,Makefile文件就是基于這種“時間戳”來運行的。如果系統判斷“文件時間戳”晚于上次編譯的“時間戳”,就表明此文件在上次編譯之后重新被修改過,需要重新編譯;如果時間戳同上次一樣沒有變化,就表明沒有修改,不需要重新編譯。
2、Makefile起步
我們的規則是:
(1)如果工程沒有編譯過,那么所有C文件都要編譯并被鏈接;
(2)如果工程的某幾個C文件被修改,那么只編譯被修改的C文件,并鏈接目標工程;
(3)如果工程的頭文件被改變了,那么需要編譯引用了這幾個頭文件的C文件,并鏈接目標程序。
Makefile的大致結構:
TRAGET: Dependency file
<TAB> COMMAND
TRAGET: Dependency file
<TAB> COMMAND
......
TRAGET: Dependency file
<TAB> COMMAND
在Makefile結構中,TARGET、Dependency file、COMMAND是相互關聯、密切聯系的3部分。
TARGET:表示make工具創建的目標體,通常是最后需要生成的文件名或者為了實現這個目的而必需的中間過程文件名。可以是.o文件,也可以是最后的可執行程序的文件名等。
Dependency file:表示要創建目標體需要的依賴文件,通常一個目標依賴于一個或者多個文件。如果其中一個文件比目標文件的“時間戳”新,這個目標就認為是“過時的”,需要重新編譯生成。
COMMAND:表示創建目標體時需要運行的命令,它限定了make執行這條規則時需要的動作,通常可由一行或者多行命令組成。如果COMMAND不與“TARGET: Dependency ifle”在一行,那么它必須以[TAB]字符作為本行的開頭,[TAB]字符告訴make此行是一個命令行;如果與"TARGET: Dependency file“在一行,那么可以使用分號作為分隔符,如果COMMAND命令太長,可使用反斜杠()作為換行符。
注:Makefile中“#”字符后的內容被當作是注釋內容。如果此行的第一個非空字符為“#”,表示此行為注釋行。當在Makefile中使用真實的字符“#”時,可以使用反斜杠加“#”(#)來實現,它表示將“#”作為一個普通字符而不是注釋的開始標志。
Makefile的一般工作過程描述:
(1)讀取Makefile。根據make的執行選項,查找當前的目錄或者其他目錄要執行的Makefile。
(2)初始化Makefile。將制定的Makefile中的變量進行替換,如果該Makefile中包含其他的文件,則將其加載。
(3)解釋規則。將Makefile中的執行規則進行解析,同時推導文件中的隱藏規則,其次,查找文件中目標、依賴、命令之間的關系,為創建目標建立關系鏈。
(4)分析變更。根據依賴關系和“時間戳”,判斷是否有依賴文件發生變化,如果有變化,則進行重新編譯;如果沒有變化,當前的目標不需要重新編譯。
(5)執行。執行Makefile中的命令。
Makefile編寫完畢,就可以執行make命令進行編譯操作。make的執行同其他命令一樣,也有豐富的選項供用戶選擇,可以完成不同的功能。make工具的常用選項:
-f file 將指定當前目錄下的file作為Makefile
-I dir 將dir作為被包含的Makefile所在目錄
-C dir 將指定目錄下的file作為Makefile
-i 忽略所有命令執行錯誤
-j 輸出規則中命令的詳細信息
-n 只打印要執行的命令,但不執行這些命令
-s 在執行命令時不顯示命令
-d 除打印正常的操作信息外,還打印調試信息
一個目標可以沒有依賴文件,只有命令,比如Makefile中的偽命令“clean“表示刪除make過程中的中間文件,它就沒有依賴,只有命令。偽命令是為其他命令服務的,不是強制性的。偽命令一般包括clean(刪除中間文件)、install(安裝編譯好的程序)、uninstall(卸載已安裝的程序)以及print(輸出發生改變的源文件)等。
3、Makefile變量
在Makefile中,變量是一個名字,它不僅可以代表一個文本字符串,而且可以用來代表文件名、編譯選項、程序運行的選項參數、搜索源文件的目錄,以及編譯輸出的目錄。在Makefile的目標、依賴、命令中任意引用變量的地方,在執行make命令后,都會被變量定義的值所取代。
在Makefile中變量有以下幾個特征:
(1)Makefile中變量和函數的展開(除規則命令行中的變量和函數以外),是在make讀取Makefile文件時進行的,這里的變量不僅包括使用“=”定義的變量,而且包含使用指示符“define”定義的。
(2)變量的命名可以包含字符、數字、下劃線,但絕對不可以使用含有“:”、“#”、“=”或是空字符(空格、回車等)的字符,同時變量中字母、數字以及下劃線以外的字符,用戶應盡量避免使用,因為它們可能賦予其他特別的含義。
(3)變量中的大小寫也是非常敏感的。推薦的方法是對于內部定義的一般 變量使用小寫方式,而對于一些參數列表(例如:編譯選項CFLAGS)采用大寫方式。
(4)有一些變量名只包含一個或者很少的幾個特殊字符,稱它們為自動化變量。像“$”、“$@”、“$?”、“$*”等,這些變量用戶在定義中也不可以使用。
當定義了一個變量后,就可以在Makefile的很多地方使用這個變量。變量的引用方式是:
$(VARIABLE_ANME) 或者 ${VARIABLE_NAME}
美元符號“$”在Makefile中有特殊的含義,所有在命令或者文件中使用“$”時需要用兩個美元符號“$$”來表示。
在Makefile中對一些簡單變量的引用,也可以不使用“()”和“{}”來標記變量,而直接使用“$x”的格式來實現,此種用法僅限于變量名為單字符的情況。
Makefile文件在進行變量定義時通常可以采用兩種方式,第一種是遞歸展開定義法,另一種是直接展開定義法。兩者雖然都可以對需要的變量進行定義,但是也存在一些差異,主要的不同在于定義的方式和展開的時機不同,遞歸展開定義法可以使用之前沒有定義過的變量,而直接展開定義法不允許引用變量之后定義過的變量。
(1)遞歸展開定義
這種變量定義法是通過“=”或者指示符“define”來定義變量。其格式如下:
Var=variable
其中,Var是變量名,variable是賦予變量Var的值。
對使用遞歸展開定義的變量,其引用的地方是嚴格的文本替換過程。變量將會原樣地被字符串替代。如果此變量定義中存在對其他變量的引用,那么被引用的變量會在此變量被展開的同時被展開。
(2)直接展開定義
為避免“遞歸展開法”定義變量出現的死循環和效率低的問題,Makefile中可使用另外一種變量定義的方式,稱為直接展開定義。這種方式使用“:=”定義變量。其格式如下:
Var := variable
同遞歸定義法不同,直接展開定義法在調用變量時,變量值對另外變量的引用在定義時被展開。所以在變量被定義后就是一個實際所需要定義的文本串,不再包含任何其他變量的引用。其次,需要注意的是,使用直接定義法定義變量時,不能對其后定義的變量進行引用。
一般而言,在復雜的Makefile中,推薦使用直接展開式變量,因為這種變量的使用方式和大多數編程語言中的變量使用方式基本相同。它可以使一個比較復雜的Makefile在一定程度上具有可預測性,而且這種變量允許用戶利用之前定義的值來重新定義。因此,在Makefile變量定義時,應盡量避免和減少遞歸方式的使用。
(3)變量嵌套定義
在Makefile中還有一種變量高級使用方法,稱為“變量嵌套定義”。它表示在一個變量中可以包含對其他變量的引用。
例如:
variable1 = variable2
varialbe2 = variable3
w := $($(variable1))
(4)替換引用定義
對于一個已經定義的變量,可以使用“替換引用”將其值中的后綴字符(串)使用指定的字符(串)替換。格式是:
$(VAR:A=B) 或者 ${VAR:A=B}
意思是,替換變量VAR中所有A字符結尾的字為B結尾的字。“結尾”的含義是空格之前(變量值多個字之間使用空格分開)。而對于變量其他部分的“A"字符不進行替換。例如:
foo := a.o b.o c.o
bar := $(foo:.o=.c)
變量分類:
除用戶自己定義的變量(稱為自定義變量)外,Makefile文件中還存在3中重要的變量,它們分別是:預定義變量、自動變量和環境變量。這3中變量是系統級變量,都有其默認值,用戶一般不需要在Makefile中重新定義,可在Makefile中直接引用(也可根據需要替換其默認值)。
(1)預定義變量
預定義變量是進行程序預編譯時經常使用的變量。有時候使用GCC進行程序預編譯時,通常對某些編譯的選項多次使用,預編譯變量使復雜的編譯選項能夠 更條理、更直觀。
AR 庫文件維護程序的名稱,默認值為ar
AS 匯編程序的名稱,默認值為as
CC c編譯器的名稱
CPP c預編譯器的名稱,默認值為$(CC) -E
CXX c++編譯器的名稱,默認值為g++
PC Pascal編譯器的名稱
FC Fortran編譯器的名稱,默認值為f77
RM 文件刪除程序的名稱,默認值為rm -f
ARFLAGS 庫文件維護程序的選項,無默認值
ASFLAGS 匯編程序的選項,無默認值
CFLAGS c編譯器的選項,無默認值
CPPFLAGS c預編譯的選項,無默認值
CXXFLAGS c++編譯器的選項,無默認值
FFLAGS Fortran編譯器的選項,無默認值
其中,最重要也是最經常使用的變量是CC和CFLAGS。由于CC沒有默認值,因此經常要把“CC=gcc”、“CC=arm-linux-gcc”等放到變量定義中。
(2)自動變量
為了簡化Makefile的編寫,Makefile引入自動變量,自動變量可以代表編譯語句中出現的目標文件和依賴文件等。使用自動變量可以為Makefile的編寫提供方便。
$@ 表示當前規則中的完整目標文件名
$? 新修改過的依賴文件列表,即所有時間戳比目標文件晚的依賴文件,并以空格分開
$* 不包含擴展名的目標文件名
$< 當前規則中的第一個依賴文件名
$% 當目標文件為庫文件時,該變量為庫文件名
$^ 所有依賴文件,以空格分開,不包含重復的依賴文件
$+ 所有依賴文件,以空格分開,并以出現的先后為序,可能包含重復的依賴文件
(3)環境變量
make命令在運行時,系統中所有的環境變量對它都是可見的。在Makefile中,可以引用任何已定義的系統環境變量。(這里我們區分系統環境變量和make環境變量,系統環境變量是這個系統所有用戶所擁有的,而make的環境變量只是對于make的一次執行過程有效。)
Makefile中最常見的環境變量是VPATH。通常在一些大的工程中,有大量的源文件,并放在不同的目錄中。所以,當make需要尋找文件依賴關系時,就要在文件前加上路徑。Makefile文件中的環境變量“VPATH”就是完成添加路徑的功能,如果沒有指明這個變量,make只會在當前目錄下尋找依賴關系和目標文件。如果定義了“VPATH”變量make就會在在當前目錄下找不到所需文件的情況下,到指定的目錄中尋找文件。這有點類似gcc的目錄選項中的“-I”和“-L“選項。如果要添加多個目錄,一般使用“:”作為分隔。
4、Makefile常用函數
$(subst A, B, text) 將文本text中的每個A字符用B字符替換
$(patsubst A, B, text) 將文本text中符合格式為A的字符,用格式B替換。
$(strip text) 將text中多余的空格進行壓縮(包括前導或者結尾的空格字符),并將多個空格變為單個空格
$(findstring A, text) 在text中查找字符串A,如果找到返回值為A,否則為空
$(sort text) 將text中的字按字母順序排序,并去掉其中重復的單詞。其輸出為單個空格隔開的單詞列表。如果第一個字母相同則比較第二個,依此類推
$(word N, text) 將text中第N個單詞取出,并返回這個單詞。如果不存在第N個單詞,則返回值為空
$(wordlist N1, N2, text) 取出text中第N1到第N2個單詞,其中N1,N2表示單詞在text的位置數字
$(words text) 此函數表示統計text中的單詞數目
$(filter A..., text) 在text中尋找由空格隔開并且匹配格式為A的字,去除不符合格式A的字符
$(filter-out A..., text) 返回由空格隔開的 并且不匹配格式為A的字符,除去格式為A的字符
$(dir text) 取出text中每個文件的路徑部分
$(notdir text) 取出text中的每個文件名
$(addsuffix A, text) 將text中每個文件名后添加后綴“A”
$(addprefix A, text) 將text中每個文件名后添加前綴“A”
$(if A, B, C) 判斷A,對變量A展開后,如果A的結果非空,則條件為真,同時將B作為函數的表達式;如果條件為假,則將C作為函數的表達式
5、Makefile與shell
Makefile文件通過shell函數與外部進行通信。它實現的功能同shell中的引用(``)類似,其返回結果是該命令在shell中執行的結果。Make命令僅對它的返回值當作字符串對待,如果函數返回結果中存在換行符,那么將其替換為空格,并去掉末尾的回車符號。在大多數情況下,make命令時在讀取解析Makefile時完成對函數shell的展開。
shell函數的格式如下:
$(shell shell-command)
此函數的作用是在Makefile中執行shell-command命令,并將它的執行結果返回Makefile。
6、Makefile規則語法
Makefile中常見的規則包括顯示規則、隱式規則以及靜態模式規則3類。
(1)顯示規則
顯示規則描述了如何將“依賴文件”轉變為“目標文件”,書寫這種規則的Makefile需要用戶明確地給出目標文件、目標依賴文件的列表,以及更新目標文件所需要的命令。
(2)隱式規則
隱式規則就像其名,它會把一部分規則“隱藏”,不要求用戶將所有的規則列出,也不需要詳細指定編譯的具體細節,甚至有時候不需要任何規則,系統會根據要產生的可執行文件及其依賴文件(典型的是根據文件名的后綴),自動推導出其依賴文件時如何使用默認命令編寫規則的。例如:典型地,make命令對c文件的編譯過程是由.c源文件編譯成.o目標文件。
另外,在make命令執行時,根據需要也可能用多個隱含規則。比如:make命令將從一個.y文件生成對應的.c文件,再生成最終的.o文件。也就是說,只要目標文件名中除后綴以外的其他部分相同,make命令就能夠使用若干個隱含規則來最終產生這個目標文件(當然,最原始的那個文件必須存在)。
在Makefile中常見的隱式規則:
c編譯:將file.c變為file.o $(CC)$(CPPFLAGS)$(CFLAGS) -c file.c -o file.o
c++編譯:將file.cc變為file.o $(CXX)$(CPPFLAGS) -c file.cc -o file.o
Pascal編譯:將file.p變為file.o $(CP)$(PFLAGS) -c file.p -o file.o
Fortran編譯:將file.r變為file.o $(CP)$(FFLAGS) -c file.r -o file.o
注:以上file均表示任意文件名
(3)靜態模式規則
模式規則是用來定義具有相同處理規則的多個文件。
模式規則類似于普通規則,只是在模式規則中,目標名需要包含模式字符“%”,該模式字符“%”被用來匹配一個文件名,可以匹配任何非空字符串。在依賴文件中同樣可以使用“%”,依賴文件中的模式字符“%”的取值由目標中的“%”來決定。例如:對于模式規則“%.o:%.c”,它表示的含義是:所有的.o文件依賴于對應的.c文件,“%.c"可以匹配到所有以.c結尾的文件,“s%.c"可以匹配到所有第一個字母為“s”,而且必須以.c結尾的文件。
要注意的是:模式字符“%”的匹配和替換發生在規則中所有變量和函數引用展開之后,變量和函數的展開一般發生在make讀取Makefile時,而模式規則中“%”的匹配和替換則發生在make執行時。
c語言的模式規則一般可描述為:
%.o : %.c
COMMAND
這個模式規則指定了所有的文件“.c"都用來創建文件“.o",文件“.c"應該是已存在的或者可被創建的。
模式規則中的依賴文件也可以不包含模式字符“%”。當依賴文件名中不包含模式字符“%”時,其含義是所有符合目標模式的目標文件都依賴于一個指定的文件(例如:“%.o : debug.h ”表示所有的".o"文件都依賴于頭文件"debug.h")。這樣的模式規則在很多場合是非常有用的。
注:
(1)在使用模式規則時,指定的目標必須和目標模式相匹配,否則執行make時將會得到一個錯誤提示。
(2)相比隱式規則,模式規則更能體現其優點。對于無法確定工作目錄內容或者不能確定是否存在無關文件,使用隱含規則可能會導致make命令失敗。其次,當存在多個適合此文件的隱含規則時,系統將不能正確判斷使用何種規則也可能導致make失敗。在這兩種情況下使用模式規則,就可以避免這些不確定因素,因為靜態模式中,指定的目標文件有明確的規則描述其依賴關系。
除了以上3中規則外,Makefile中還存在一些基礎規則,包括:
(1)在Makefile文件中,除“終極目標”所在的規則外,其余規則的順序在Makefile中沒有意義(終極目標是指Makefile文件第一個規則的目標,一般是執行make后生成的可執行文件)。如果在Makefile中第一個規則有多個目標的話,那么多個目標中的第一個將會被作為make的“終極目標”
(2)規則的命令部分有兩種書寫方式,一種是把命令和依賴文件放在一行,中間使用分號(:)隔開,另一種是命令在依賴文件的下一行。當作獨立的命令行時,此行必須以Tab開始。
(3)對于Makefile中一個較長的行,我們可以使用反斜杠“”將其書寫到幾個獨立的行上。
7、Makefile自動編寫工具
autotool工具是當今Linux世界中比較常用的Makefile自動生成工具。autotool是一系列工具,主要包括autoscan、autoconf、aclocal、autoheader、automake等。
下面用一個簡單的程序exautomake.c來講述autotool編寫Makefile文件的方法。該文件的代碼如下:
#include <stdio.h>
int main()
{
printf( "this is the first automake example!" );
}
第一步:autoscan
autoscan工具用來掃描當前目錄下是否有生成Makefile的源文件(configure.scan、autoscan.log),如果沒有這兩個文件,系統會自動生成configure.scan、autoscan.log這兩個文件。
因此,在命令行下輸入:
#autoscan
autom4te: configure.ac: no such file or directory
autoscan: /usr/bin/autom4te failed with exit status: 1
#ls
autoscan.log configure.scan exautomake.c exautomake.c~
其中,configure.scan是configure.in的原型,(configure.in是autoconf的腳步配置文件),所以下一步工作就是要對configure.scan進行修改,將其轉化為configure.in。
第二步:aclocal、autoconf及autoheader
使用編輯器打開configure.scan文件,其內容如下:
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59) #命令行1
AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS) #命令行2
AC_CONFIG_SRCDIR([exautomake.c]) #命令行3
AC_CONFIG_HEADER([config.h]) #命令行4
# Checks for programs.
AC_PROG_CC
# Checks for libraries.
# Checks for header files.
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
AC_OUTPUT #命令行5
命令行1中,AC_PREREQ用來聲明本文件要求的autoconf版本
命令行2,用來定義軟件的名稱和版本等信息
命令行3,AC_CONFIG_SRCDIR用來檢查所指定的源碼文件是否存在,來確定源碼目錄的有效性。這個參數一般不需要修改
命令行4,AC_CONFIG_HEADER用于生成config.h文件,以便autoheader使用
將configure.scan文件修改為configure.in,修改的地方共有4處:
第一,將命令行2注釋掉,并在命令行2后添加一新行,并鍵入"AC_INIT(exautomake,1.0)",表示編寫該程序的名字及版本信息。
第二,在命令行3前添加新行,并鍵入"AM_INIT_AUTOMAKE(exautomake,1.0)",這是automake必備的一行,同前面一樣,"exautomake"是所要產生的軟件的名字,"1.0"表示版本號,一般而言,第一次編寫版本號都可定為"1.0".
第三,將命令行4修改為"AM_CONFIG_HEADER([config.h])".(實踐證明:改此處或者不改都可以)
修改完畢后,將configure.scan在當前目錄下另存為configure.in。接下來要運行aclocal,系統會自動生成一個aclocal.m4文件,該文件的主要作用是處理當前的定義行;隨后運行autoconf命令,該命令同樣會在當前目錄下生成一個名為configure的文件;最后執行autoheader,它負責生成config.h.in文件。
第四,將命令行5修改為"AC_OUTPUT([Makefile])".
#aclocal
#autoconf
#autoheader
第三步:automake
automake的執行至關重要。automake需要使用腳本配置文件Makefile.am,對于這個文件用戶需要自己創建。
使用編輯器編寫程序Makefile.am如下:
AUTOMAKE_OPTIONS=foreign #命令行1
bin_PROGRAMS=exautomake #命令行2
exautomake_SOURCES=exautomake.c #命令行3
其中,命令行1中,AUTOMAKE_OPTIONS表示為automake進行設置的選項。automake提供了3種軟件等級:foreign、gnu、gnits,讓用戶選擇使用,默認等級是gnu,在本例中使用foreign等級,只檢查必須的文件。
命令行2中,bin_PROGRAMS表示要產生的可執行文件名。如果要產生多個可執行文件,每個文件名用空格隔開。
命令行3中的exautomake_SOURCES是用來定義exautomake這個執行程序的依賴文件。如果文件需要多個源文件,就要在后面添加生成它的源文件。例如:若生成的exautomake除需要exautomake.c外,還需要wth.c、wang.c,就要使用多個源文件定義方法"exautomake_SOURCES=exautomake.c wth.c wang.c".
接下來就是使用automake生成configure.in文件,一般而言,在執行automake時,要在后面添加選項"--add-missing",表示讓automake自動添加一些必要的腳步文件。
#automake --add-missing
第四步:運行configure
通過運行configure,就可以把Makefile變成最終的Makefile
#./configure
到此為止,makefile就可以自動生成了。
第五步:執行make
執行make命令將默認執行make all,即編譯所有目標體,執行完畢,會在當前目錄下生成exautomake的可執行文件。
使用autotools工具除生成exautomake目標之外,還默認生成install(安裝該程序到系統中),clean(清楚之前編譯的所有可執行文件及目標文件)等目標文件。
總結
以上是生活随笔為你收集整理的软件开发工具——Make的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: xxxx(三)“黑吃黑”: 破解别人外挂
- 下一篇: 英文qq网名130个