1. 程式人生 > >Shell指令碼中make命令的使用

Shell指令碼中make命令的使用

    (最近開發的專案中需要編寫Shell指令碼對整個工程進行自動化編譯,即在Shell指令碼中使用make命令來進行編譯,下面回顧一下Shell指令碼中如何使用make命令)

        在開發一個系統時,一般是將一個系統分成幾個模組,這樣做提高了系統的可維護性,但由於各個模組間不可避免存在關聯,所以當一個模組改動後,其他模組也許會有所更新,當然對小系統來說,手工編譯連線是沒問題,但是如果是一個大系統,存在很多個模組,那麼手工編譯的方法就不適用了。為此,在Linux系統中,專門提供了一個make命令來自動維護目標檔案,與手工編譯和連線相比,make命令的優點在於他只更新修改過的檔案(在Linux中,一個檔案被建立或更新後有一個最後修改時間,make命令就是通過這個最後修改時間來判斷此檔案是否被修改),而對沒修改的檔案則置之不理,並且make命令不會漏掉一個需要更新的檔案。

       檔案和檔案間或模組或模組間有可能存在倚賴關係,make命令也是依據這種依賴關係來進行維護的,所以我們有必要了解什麼是依賴關係;make命令當然不會自己知道這些依賴關係,而需要程式設計師將這些依賴關係寫入一個叫makefile的檔案中。

       下面是詳細解析:

一、Make命令

1、make命令基礎概念

    Make這個詞,英語的意思是”製作”。Make命令直接用了這個意思,就是要做出某個檔案。比如,要做出檔案a.txt,就可以執行下面的命令。

$ make a.txt
       但是,如果你真的輸入這條命令,它並不會起作用。因為Make命令本身並不知道,如何做出a.txt,需要有人告訴它,如何呼叫其他命令完成這個目標。
       比如,假設檔案a.txt 依賴於b.txt 和 c.txt ,是後面兩個檔案連線(cat命令)的產物。那麼,make 需要知道下面的規則。

a.txt: b.txt c.txt
    cat b.txt c.txt > a.txt
       也就是說,make a.txt 這條命令的背後,實際上分成兩步:第一步,確認 b.txt 和 c.txt 必須已經存在,第二步使用 cat 命令 將這個兩個檔案合併,輸出為新檔案。
       像這樣的規則,都寫在一個叫做Makefile的檔案中,Make命令依賴這個檔案進行構建。Makefile檔案也可以寫為makefile, 或者用命令列引數指定為其他檔名。

$ make -f rules.txt # 或者 $ make --file=rules.txt
      上面程式碼指定make命令依據rules.txt檔案中的規則,進行構建。


2、make命令使用方法

     Make命本身可帶有四種引數:標誌、巨集定義、描述檔名和目標檔名。其標準形式為:

  Make [flags] [macro definitions] [targets]

  Unix系統下標誌位flags選項及其含義為:
 
  -f file  指定file檔案為描述檔案,如果file引數為"-"符,那麼描述檔案指向標準輸入。如果沒有"-f"引數,則系統將預設當前目錄下名為makefile或者名為Makefile的檔案為描述檔案。在Linux中, GNU make 工具在當前工作目錄中按照GNUmakefile、makefile、Makefile的順序搜尋 makefile檔案。

  -i   忽略命令執行返回的出錯資訊。 
  -s   沉默模式,在執行之前不輸出相應的命令列資訊。 
  -r   禁止使用build-in規則。 
  -n   非執行模式,輸出所有執行命令,但並不執行。 
  -t   更新目標檔案。 
  -q   make操作將根據目標檔案是否已經更新返回"0"或非"0"的狀態資訊。 
  -p   輸出所有巨集定義和目標檔案描述。 
  -d   Debug模式,輸出有關檔案和檢測時間的詳細資訊。

  Linux下make標誌位的常用選項與Unix系統中稍有不同,下面我們只列出了不同部分:

  -c dir   在讀取 makefile 之前改變到指定的目錄dir。 

  -I dir   當包含其他 makefile檔案時,利用該選項指定搜尋目錄。 
  -h   help文擋,顯示所有的make選項。 
  -w   在處理 makefile 之前和之後,都顯示工作目錄。

  通過命令列引數中的target ,可指定make要編譯的目標,並且允許同時定義編譯多個目標,操作時按照從左向右的順序依次編譯target選項中指定的目標檔案。如果命令列中沒有指定目標,則系統預設target指向描述檔案中第一個目標檔案。

  通常,makefile 中還定義有 clean 目標,可用來清除編譯過程中的中間檔案,例如:

  clean: 
  rm -f *.o

  執行 make clean 時,將執行 rm -f *.o 命令,最終刪除所有編譯過程中產生的所有中間檔案。

  隱含規則

  在make 工具中包含有一些內建的或隱含的規則,這些規則定義瞭如何從不同的依賴檔案建立特定型別的目標。Unix系統通常支援一種基於副檔名即檔名字尾的隱含規則。這種字尾規則定義瞭如何將一個具有特定檔名字尾的檔案(例如.c檔案),轉換成為具有另一種檔名字尾的檔案(例如.o檔案):
 
  .c:.o 
  $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< 

  系統中預設的常用副檔名及其含義為: 

  .o  目標檔案 
  .c  C原始檔 
  .f  FORTRAN原始檔 
  .s  彙編原始檔 
  .y  Yacc-C源語法 
  .l  Lex源語法

  在早期的Unix系統系統中還支援Yacc-C源語法和Lex源語法。在編譯過程中,系統會首先在makefile檔案中尋找與目標檔案相關的.C檔案,如果還有與之相依賴的.y和.l檔案,則首先將其轉換為.c檔案後再編譯生成相應的.o檔案;如果沒有與目標相關的.c檔案而只有相關的.y檔案,則系統將直接編譯.y檔案。
  而GNU make 除了支援字尾規則外還支援另一種型別的隱含規則--模式規則。這種規則更加通用,因為可以利用模式規則定義更加複雜的依賴性規則。模式規則看起來非常類似於正則規則,但在目標名稱的前面多了一個 % 號,同時可用來定義目標和依賴檔案之間的關係,例如下面的模式規則定義瞭如何將任意一個 file.c 檔案轉換為 file.o 檔案:

  %.c:%.o 
  $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< 

二、Makefile檔案

1、概述

     Makefile檔案由一系列規則(rules)構成。每條規則的形式如下。

<target> : <prerequisites> [tab] <commands>

     上面第一行冒號前面的部分,叫做”目標”(target),冒號後面的部分叫做”前置條件”(prerequisites);第二行必須由一個tab鍵起首,後面跟著”命令”(commands)。

     “目標”是必需的,不可省略;”前置條件”和”命令”都是可選的,但是兩者之中必須至少存在一個。

      每條規則就明確兩件事:構建目標的前置條件是什麼,以及如何構建。

2、 目標(target)

      一個目標(target)就構成一條規則。目標通常是檔名,指明Make命令所要構建的物件,比如 a.txt 。目標可以是一個檔名,也可以是多個檔名,之間用空格分隔

      除了檔名,目標還可以是某個操作的名字,這稱為”偽目標”(phony target)。

clean: rm *.o
    上面程式碼的目標是clean,它不是檔名,而是一個操作的名字,屬於”偽目標 “,作用是刪除物件檔案。

$ make  clean
    但是,如果當前目錄中,正好有一個檔案叫做clean,那麼這個命令不會執行。因為Make發現clean檔案已經存在,就認為沒有必要重新構建了,就不會執行指定的rm命令

    為了避免這種情況,可以明確宣告clean是”偽目標”,寫法如下。

.PHONY: clean
clean: rm *.o temp
    宣告clean是”偽目標”之後,make就不會去檢查是否存在一個叫做clean的檔案,而是每次執行都執行對應的命令。像.PHONY這樣的內建目標名還有不少,可以檢視手冊。
    如果Make命令執行時沒有指定目標,預設會執行Makefile檔案的第一個目標。

    $ make

    上面程式碼執行Makefile檔案的第一個目標。

例:執行多個目標

.PHONY: cleanall cleanobj cleandiff
 
cleanall : cleanobj cleandiff
        rm program
 
cleanobj : rm *.o
 
cleandiff : rm *.diff


三、shell 指令碼各種執行方式(source ./*.sh, . ./*.sh, ./*.sh)的區別

結論一: ./*.sh的執行方式等價於sh ./*.sh或者bash ./*.sh,此三種執行指令碼的方式都是重新啟動一個子shell,在子shell中執行此指令碼。

結論二: .source ./*.sh和 . ./*.sh的執行方式是等價的,即兩種執行方式都是在當前shell程序中執行此指令碼,而不是重新啟動一個shell 而在子shell程序中執行此指令碼。

驗證依據:沒有被export匯出的變數(即非環境變數)是不能被子shell繼承的

驗證結果:

[root@localhost ~]#name=dangxu       //定義一般變數  
[root@localhost ~]# echo ${name}  
dangxu  
[root@localhost ~]# cat test.sh      //驗證指令碼,例項化標題中的./*.sh  
#!/bin/sh  
echo ${name}  
[root@localhost ~]# ls -l test.sh    //驗證指令碼可執行  
-rwxr-xr-x 1 root root 23 Feb  6 11:09 test.sh  
[root@localhost ~]# ./test.sh        //以下三個命令證明了結論一  
  
[root@localhost ~]# sh ./test.sh  
  
[root@localhost ~]# bash ./test.sh  
  
[root@localhost ~]# . ./test.sh     //以下兩個命令證明了結論二  
dangxu  
[root@localhost ~]# source ./test.sh  
dangxu  
[root@localhost ~]# 
--------------------- 
作者:zqixiao_09 
來源:CSDN 
原文:https://blog.csdn.net/zqixiao_09/article/details/51417138 
版權宣告:本文為博主原創文章,轉載請