軟件開發工具——Make

掌握Makefile的使用方法和工做流程;linux

掌握make工具變量的相關知識,包括其引用、定義及分類等;c++

掌握Makefile常見的函數含義;shell

掌握Makefile與shell命令行的通訊方法;編程

掌握Makefile的常見語法規則,包括顯式規則、隱式規則及靜態模式規則;編程語言

瞭解autotools的用法,瞭解autotools中經常使用的工具鏈以及如何使用工具鏈自動建立Makefile文件。編輯器

一、Make工具概述函數

  Makefile帶來的好處就是「自動化編譯」,一旦寫好,只須要在shell命令行中輸入一個make命令,整個工程徹底自動編譯,能夠極大提升軟件開發的效率。工具

  make是一個命令工具,它解釋Makefile中的語法規則。Makefile有本身的書寫格式、關鍵字和函數,這個文件告訴make以何種方式編譯源代碼和連接程序。典型地,可執行文件可由一些.o文件按照必定的順序編譯,若是在工程中已經存在Makefile,當對工程中的若干源文件修改之後,可自動根據修改狀況完成源文件對應.o文件的更新、庫文件的更新、最終的可執行程序的更新。this

  那麼若是來判斷一個大型工程中哪些文件發生了改變,哪些沒發生變化呢?spa

  Linun系統判斷工程中文件是否發生改變的方法,是判斷文件的創建和修改時間,即「文件時間戳」,Makefile文件就是基於這種「時間戳」來運行的。若是系統判斷「文件時間戳」晚於上次編譯的「時間戳」,就代表此文件在上次編譯以後從新被修改過,須要從新編譯;若是時間戳同上次同樣沒有變化,就代表沒有修改,不須要從新編譯。

二、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(輸出發生改變的源文件)等。

 三、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「選項。若是要添加多個目錄,通常使用「:」做爲分隔。 

四、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做爲函數的表達式 

五、Makefile與shell 

  Makefile文件經過shell函數與外部進行通訊。它實現的功能同shell中的引用(``)相似,其返回結果是該命令在shell中執行的結果。Make命令僅對它的返回值看成字符串對待,若是函數返回結果中存在換行符,那麼將其替換爲空格,並去掉末尾的回車符號。在大多數狀況下,make命令時在讀取解析Makefile時完成對函數shell的展開。

  shell函數的格式以下:

  $(shell shell-command)

  此函數的做用是在Makefile中執行shell-command命令,並將它的執行結果返回Makefile。  

六、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中一個較長的行,咱們可使用反斜槓「\」將其書寫到幾個獨立的行上。 

七、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(清楚以前編譯的全部可執行文件及目標文件)等目標文件。

相關文章
相關標籤/搜索