next up previous contents
Next: makefile 的變數 Up: 開發工具 - make 與 Previous: 編譯器與可執行檔   Contents

make 命令和 makefile

  • 何謂 makefile ?
    1. make 命令雖然有很多內建的功能,但它也無法知道如何建立應用程式。故必須提供一個檔案,即 makefile,告訴 make 如何建立應用程式。
    2. makefile 與專案的原始碼檔案,通常放在同一個目錄中。
    3. 可以同時有很多不同的 makefile 管理專案的不同部分。
    4. make 命令和 makefile 的結合,不僅控制原始碼的編譯,也可以用來準備使用手冊文件、安裝應用程式到目的目錄中。
  • make 使用 makefile 的好處:
    1. 簡化編譯時所需要下達的指令;
    2. 若在編譯完成之後,修改了某個原始碼檔案,則 make 僅會針對被修改了的檔案進行編譯,其他的 object file 不會被更動;
    3. 最後可以依照相依性來更新( update )執行檔。
  • make 與 configure
    1. 當執行 make 時,make 會在當時的目錄下搜尋文字檔 makefile ( or Makefile ),其記錄了原始碼如何編譯的詳細資訊。
    2. 偵測程式 configure
      1. 軟體開發者會寫一支偵測程式來偵測使用者的作業環境,以及該作業環境是否有軟體開發商所需要的其他功能;
      2. 偵測完畢後,會主動的建立 Makefile 的規則檔案。
      3. 偵測程式的檔名為 configure 或者是 config。
  • 為什麼要用 make ?
    1. 假設有一些標頭檔案 a.h、b.h 和 c.h,以及 C 原始碼檔案 main.c、2.c 和 3.c。 Souce codes download
      /* main.c */
      #include "a.h"
      ...
      /* 2.c */
      #include "a.h"
      #include "b.h"
      ...
      /* 3.c */
      #include "b.h"
      #include "c.h"
      ...
      
      1. 如果程式設計人員改變 c.h,檔案 main.c 和 2.c 不需要重新編譯; 3.c 需要被重新編譯。
      2. 如果 b.h 被改變,程式設計人員忘了重新編譯 2.c,最後的程式可能不會正確運作。
    2. 假設執行檔包含了四個原始碼檔案,分別是 main.c haha.c sin_value.c cos_value.c ,如何讓這個程式可以執行?
      1. 不使用 makefile
        #%* 1. 先製作出四個目標檔: *)
        [guest@test guest]# gcc -c main.c 
        [guest@test guest]# gcc -c haha.c 
        [guest@test guest]# gcc -c sin_value.c 
        [guest@test guest]# gcc -c cos_value.c  
        
        #%* 2. 製作執行檔  main:*)
        [guest@test guest]# gcc -o main main.o haha.o sin_value.o \ 
        > cos_value.o -lm -L/usr/lib -L/lib 
        
        #%* 3. 執行:*)
        [guest@test guest]# ./main 
        HaHa! I'm the King of the world 
        0.706825 
        0.707388
        
      2. 使用 makefile ,以一個步驟完成所有動作:
        #%* 1. 先建立編譯的規則*)
        [root@linux ~]# vi makefile
        main: main.o haha.o sin_value.o cos_value.o
                gcc -o main main.o haha.o sin_value.o cos_value.o -lm
        #%* 第二行的 gcc 之前是 <tab> 按鍵產生的空格*)
        
        #%* 2. make 會主動讀取 makefile 內容,並編譯相關的執行檔*)
        [root@linux ~]# rm -f main *.o   %*<==先將之前的目標檔去除*)
        [root@linux ~]# make
        cc    -c -o main.o main.c
        cc    -c -o haha.o haha.c
        cc    -c -o sin_value.o sin_value.c
        cc    -c -o cos_value.o cos_value.c
        gcc -o main main.o haha.o sin_value.o cos_value.o -lm
        
        #%* 3. 再執行一次 make *)
        [root@linux ~]# make
        make: `main' is up to date.
        

  • makefile 的語法(syntax)
    標的(target): 目標檔1 目標檔2 
    <tab>  gcc -o 欲建立的執行檔 目標檔1 目標檔2
    
    1. 標的 (target) 與相依檔案(就是目標檔)之間需以『:』隔開。
    2. <tab> 需要在命令行的第一個字元;
    3. makefile 語法中之 <tab> 與空白:
      1. 所有的法則必須在同一行,而且行首必須為 <tab>;不能為空白。
      2. 在 makefile 中,行尾如果有一個空白,會造成 make 命令執行錯誤。
    4. makefile 的註解(comment):
      1. 如同 C 原始碼檔案一般,在 makefile 中,以 # 為行首的文字都是註解。
      2. makefile 中的註解只是協助作者和其它人,了解 makefile 的內容。

  • makefile 的語法規則:
    1. makefile 是由很多相依性項目(dependencies)和法則(rules)所組成。
    2. 相依性項目,描述目標項目(target,要產生的檔案)和產生該檔案之相關的原始碼檔案。
    3. 法則是說明如何根據相依性檔案,來建立目標項目。
    4. make 命令利用 makefile,先決定依序建立哪些目標項目,再決定依序喚起哪些法則。
  • 相依性項目(dependency)
    1. 例題:目標項目 myapp 與 main.o、2.o、3.o 相關,main.o 與 main.c、a.h 相關,以此類推。
      myapp: main.o 2.o 3.o
      main.o: main.c a.h
      2.o: 2.c a.h b.h
      3.o: 3.c b.h c.h
      
      1. makefile 中依序為目標項目、冒號、空白或 tab、隨後就是以空白或 tab 區隔的相關檔案。
      2. 改變 b.h,需要重建 2.o 和 3.o,因為 2.o 和 3.o 被改變,又需要重建 myapp。
    2. 以 gcc 的 -MM 選項以 makefile 的格式,輸出相依性項目。
      $ gcc -MM main.c 2.c 3.c
      main.o: main.c a.h
      2.o: 2.c a.h b.h
      3.o: 3.c b.h c.h
      
      1. 輸出結果可插入 makefile 中,成為相依性的法則。
      2. makedepend 工具,類似 -MM 選項,但會將相依性項目附加到 makefile 之後。

    3. 利用假造的目標項目 all,建立多個目標檔案。例如:產生二進位執行檔 myapp 和使用手冊文件 myapp.1。
      all: myapp myapp.1
      
      1. 通常設定 all 為 makefile 的第一個目標項目,隨後再一一列出 all 的相依性項目。
      2. 如果不指定 all 目標項目,make 就會產生 makefile 中的第一個目標項目。
  • 法則(rule):說明如何產生目標項目。
    2.o: 2.c a.h b.h
       gcc -c 2.c
    #,以法則 gcc -c 2.c 產生目標項目 2.o
    
  • make [ -f makefile ] [ options ] ... [ targets ] ...,常用的選項及參數:
    1. -j N:讓 make 在同一個時間執行 N 個命令,以加速編譯的時間。
    2. -k:讓 make 在遇到錯誤時,仍然繼續運行,不停止在第一個問題點。
    3. -n:告訴 make 只印出將會進行的工作,而不真正去編譯。
    4. -f <filename>:告訴 make 該使用的 makefile 檔案。如果不使用這個選項, make 會依序尋找目錄中的 GNUmakefile, makefile, Makefile。
    5. 直接於 make 時,指定目標項目名稱
      [root@dywOffice ~]# make targetfile
      

  • 實作-簡單的 makefile 範例
    1. 產生 makefile-檔名 Makefile1:
      myapp: main.o 2.o 3.o
           gcc -o myapp main.o 2.o 3.o
      main.o: main.c a.h
           gcc -c main.c
      2.o: 2.c a.h b.h
           gcc -c 2.c
      3.o: 3.c b.h c.h
           gcc -c 3.c
      
    2. 執行 make 命令,因為程式碼不存在,故得到以下訊息:
      $ make -f Makefile1
      make: *** No rule to make target 'main.c', needed by 'main.o'. Stop.
      $
      
    3. 利用 touch 產生空白的標頭檔案。
      $ touch a.h
      $ touch b.h
      $ touch c.h
      
    4. 編輯程式 main.c, 2.c 和 3.c。
      /* main.c */
      #include <stdlib.h>
      /* 引用的標頭檔案,在 makefile 中,會有相依性關係。
      #include "a.h"
      extern void function_two();
      extern void function_three();
      int main()
      {
      
      /* 呼叫 function_two 和 function_three。 */
          function_two();
          function_three();
          exit (EXIT_SUCCESS);
      }
      
      /* 2.c  定義 function_two 函式 */
      #include "a.h"
      #include "b.h"
      void function_two() {
      }
      
      /* 3.c  定義 function_three 函式 */
      #include "b.h"
      #include "c.h"
      void function_three() {
      }
      
    5. 再次測試 make 成功:
      $ make -f Makefile1
      gcc -c main.c
      gcc -c 2.c
      gcc -c 3.c
      gcc -o myapp main.o 2.o 3.o
      $
      
    6. b.h 修改後再 make:
      $ touch b.h
      $ make -f Makefile1
      gcc -c 2.c
      gcc -c 3.c
      gcc -o myapp main.o 2.o 3.o
      $
      
    7. 刪除目的檔案後再 make:
      $ rm 2.o
      $ make -f Makefile1
      gcc -c 2.c
      gcc -o myapp main.o 2.o 3.o
      $
      
練習題
  1. 使用 make 時,會在當時的目錄下搜尋那個文字檔,以得知如何建立應用程式?
    Sol. makefile 或 Makefile
  2. 軟體開發者會寫一支偵測程式來建立 makefile 的規則檔案,其檔名為何?
    Sol. configure 或 config (較少用)
  3. 假設有一些標頭檔案 a.h、b.h 和 c.h,以及 C 原始碼檔案 main.c、2.c 和 3.c。
    /* main.c */
    #include "a.h"
    ...
    /* 2.c */
    #include "a.h"
    #include "b.h"
    ...
    /* 3.c */
    #include "b.h"
    #include "c.h"
    ...
    

    如果程式設計人員改變 a.h,則那些檔案需要重新編譯?那些檔案則不需要被重新編譯?
    Sol. main.c 及 2.c 需重新編譯;3.c 不需重新編譯。
  4. 假設執行檔包含了三個原始碼檔案,分別是 main.c ha1.c ha2.c ,不使用 makefile 情況下,如何建立可執行檔 main?
    Sol. 逐步執行以下動作:gcc -c main.c; gcc -c ha1.c; gcc -c ha2.c; gcc -o main main.o ha1.o ha2.o
  5. 假設執行檔包含了三個原始碼檔案,分別是 main.c ha1.c ha2.c ,使用 makefile 情況下,如何建立可執行檔 main?
    Sol.
    #%* 1. 先建立編譯的規則*)
    $ vi makefile
    main: main.o ha1.o ha2.o
       →  gcc -o main main.o ha1.o ha2.o
    #%* 2. make 會主動讀取 makefile 內容,並編譯相關的執行檔*)
    $ make
    

  6. 請寫出 makefile 的語法。
    Sol.
    標的: 目標檔1 目標檔2 
    <tab>  gcc -o 欲建立的執行檔 目標檔1 目標檔2
    

  7. 某一 makefile 中,有如下語法,其中 □ 代表空白字元,請問是否有錯?若有請說明原因。
    2.o:□2.c□a.h□b.h
    □□□□gcc□-c□2.c
    Sol. 有錯。gcc 前為 <tab> ,不可為空白。
  8. 某一 makefile 中,有如下語法,其中 □ 代表空白字元, → 代表 <tab>。請問是否有錯?若有請說明原因。
    2.o:□2.c□a.h□b.h
    → gcc□-c□2.c□
    Sol. 有錯。行尾不可為空白。
  9. 某一 makefile 中,有如下語法,其中 □ 代表空白字元, → 代表 <tab>。請問是否有錯?若有請說明原因。
    2.o:□2.c□a.h□b.h□
    → gcc□-c□2.c
    Sol. 有錯。行尾不可為空白。
  10. 在 makefile 中,註解要如何處理?
    Sol. 行首以 # 開頭。
  11. makefile 主要由那兩部分組成?
    Sol. 相依性項目(dependencies)和法則(rules)所組成。
  12. 請說明 makefile 中的相依性項目?
    Sol. 描述目標項目(target,要產生的檔案)和產生該檔案之相關的原始碼檔案。
  13. 如何以 gcc 指令,以 makefile 格式輸出 main.c, 2.c 及 3.c 的相依性項目?
    Sol. gcc -MM main.c 2.c 3.c
  14. 請說明 makefile 中的法則?
    Sol. 說明如何根據相依性檔案,來建立目標項目。
  15. 請說明 make 指令如何使用 makefile?
    Sol. 利用 makefile,先決定依序建立哪些目標項目,再決定依序喚起哪些法則。
  16. 若要讓 make 在同一個時間執行 4 個命令,以加速編譯的時間。如何下指令?
    Sol. make -j 4
  17. 若要讓 make 在遇到錯誤之時,仍然繼續運行,而不會停止在第一個問題點。如何下指令?
    Sol. make -k
  18. 若要讓 make 只印出將會進行的工作,而不會真正去編譯。如何下指令?
    Sol. make -n
  19. 若要指定 makefile1 來進行 make。如何下指令?
    Sol. make -f makefile1
  20. 在沒指定 makefile 情況下,make 會依序搜尋那些檔名的 makefile?
    Sol. GNUmakefile, makefile, Makefile
  21. 請撰寫一 makefile,目標檔及產生之可執行檔,檔名為 myapp,其相依性項目有 main.o 及 2.o,而目標項目 main.o 及 2.o 之相依性項目則分別為 main.h, a.h 及 2.c, a.h, b.h。
    Sol.
    myapp:□main.o□2.o
    → gcc□-o□myapp□main.o□2.o
    main.o:□main.c□a.h
    → gcc□-c□main.c
    2.o:□2.c□a.h□b.h
    → gcc□-c□2.c
  22. 執行 make -f Makefile1 時出現訊息『make: *** No rule to make target 'main.c', needed by 'main.o'. Stop』,代表意義為何?
    Sol. 產生目標檔 main.o 需要的檔案 main.c 不存在,故停止。
  23. 若程式 2.c 中有兩行 #include "a.h" 及 #include "b.h",則 makefile 中目標項目 2.o 要如何撰寫?
    Sol.
    2.o:□2.c□a.h□b.h
    → gcc□-c□2.c
  24. 若程式 3.c 中有兩行 #include "b.h" 及 #include "c.h",則 makefile 中目標項目 3.o 要如何撰寫?
    Sol.
    3.o:□3.c□b.h□c.h
    → gcc□-c□3.c


next up previous contents
Next: makefile 的變數 Up: 開發工具 - make 與 Previous: 編譯器與可執行檔   Contents
2017-06-14