2010年9月17日 星期五

[GNU 程式設計] 使用 make 自動編譯 : 產生 make 檔案


前言 :
make 是 UNIX 環境最有用的工具之一. 基本上它是一種用來將大量編譯工作自動化的程式語言. 適當的使用 make, 可以減少花在編譯程式上的時間, 因為需多不必要的重複編譯都可以避免. 適當的使用 make, 還可以保證程式會以正確的選項編譯, 連結的也都是最新版的程式模組及程式庫.
make 主要構想是讓你不必重新編譯一個已有新版物件檔的原始程式. 假如物件檔是在原始程式最後修改之後編譯的, 它就是新版的. 以下列情況為例 : 我們剛剛修改了 stimulate.c , 現在要作編譯並和 inputs.c 及 outputs.c 兩個模組連結. 參考下面的命令 :
% gcc stimulate.c inputs.c outputs.c

可以正確的將三個模組正確的編譯及連結. 假如 inputs.o 及 outputs.o 存在而且是新版本(原始程式在物件模組編譯後沒有改變), 我們可以用下面命令編譯 :
% gcc stimulate.c inputs.o outputs.o

這樣 gcc 只編譯 stimulate.c , 再與兩個物件檔案連結. 因為這次編譯只做一次而非三次, 時間上可以大為節省. 但這樣做需要程序員多費心, 程式設計師必須記住 (或去檢查) 上次編譯 inputs.c 及 outputs.c 是什麼時候, 然後知道哪些模組是否為新版. 這樣做可能造成混淆 : 雖然只編譯 stimulate.c 然後與現有模組連結比較快, 前提是這些模組都必須正確的.
make 可以將這些作業自動化, 降低錯誤編譯的可能性, 同時減少編譯的資源需求. 它會決定相關物件檔是否存在, 以及版本是否為最新的, 然後以最少的編譯來產生你要的結果. 要了解 make 是如何完成這些工作的, 我們需要先定義一些用語 :
* 目標 (target) :
一件需要完成的工作. 在許多情況下, 目標就是你想要產生的檔案名稱; 或是一件工作的名稱.

* 依存關係(dependency) :
兩個目標之間的關係 : 假如改變目標 B 就必須改變目標 A, 則稱目標A與目標B 有依存關係.

* 新版 (up to date) :
一個檔案若是比所有其他依存的檔案的修改時間還要新, 就是新版. 舉例說假如 A.o 的修改時間晚於 A.c 及 A.h 就是新版. 因為所有原始程式的更改已經反映到 A.o 上了. 若A.o 修改時間早於A.c 或 A.h , 表示原始程式在上次編譯後又些改過了, 而A.o 並未包含這些變更.

* make 檔 (makefile) :
描述如何產生一個或多個目標的檔案. 其中表列這些目標依靠哪些檔案, 及正確的編譯這些目標所需的規則. 對於大部分的應用程式來說, 一個原始程式目錄中只要一個 make 檔來描述如何編譯目錄中的程式就好. 依照預設, make命令會先找名為 makefile 的 make 檔, 然後再找名為 Makefile 的 make 檔.

使用 make 之前, 你需要先產生 make 檔, 描述如何正確的產生一個或多個程式, 然後執行 make 來完成 make 檔中的指定工作. 首先我們會在接下來介紹如何產生 make 檔. (可以參考 O'Reilly 出版的 Managing Projects with make 一書 得到有關 make 更多相關細節).

產生make 檔案 :
任何的 make 檔不管有多複雜, 其實只是一組描述如何建立目標的指令. 目標通常是一個檔案, 也可能是一件工作. 請參考如下make 檔範例, 描述如何去建立分別為執行版與除錯版的 stimulate 程式 :
* make 範例檔 :
  1. #A very simple makefile.  
  2. #Lines beginning with # are comments  
  3. #targets begin at the left margin, followed by:  
  4. #Shell command lines must begin with a tab.  
  5. stimulate:  
  6. #One or more commands to create stimulate  
  7.         gcc -o stimulate -O stimulate.c inputs.c outputs.c  
  8. stimulate.db:  
  9. #One or more commands to create stimulate  
  10.         gcc -DDEBUG -g -o stimulate.db stimulate.c inputs.c outputs.c  

這個 make 檔列出兩個不同版本 stimulate 的命令 :
% gcc -o stimulate -O stimulate.c inputs.c outputs.c <正確編譯出 stimulate 並進行最佳化, 但不進行除錯>

gcc -DDEBUG -g -o stimulate.db stimulate.c inputs.c outputs.c <正確編譯可供除錯的版本 stimulate.db>

DEBUG 選項是定義給前處理器看的, 要求它把原始程式中任何的除錯指令納入編譯. 不進行最佳化, 編譯器會產生 gdb 除錯時需要的擴大符號表.
這個make 檔案並沒有發揮 make 的特色, 特別是 make 檔沒有減少多餘的編譯, 只是執行最簡單 (而且最慢) 的命令來完成編譯.
Ps. 命令行的第一個字元必須是定位鍵, 不可以是一或多個空白. 以空白字元起頭是 make 檔中最常犯的錯誤 ; 這種錯誤很難找, 因為 make 檔看起來沒錯. 若你以空白而非定位鍵起頭, make 命令會顯示錯誤訊息 "Missing seperator".

make 檔案會以新的一層 shell 來執行 make 檔中的每一行 UNIX 命令. 因此由shell 直接執行的命令, 效果只同一UNIX 命令行. 還有你可以使用連續自元(\) 來延長一個UNIX 命令行使其跨越多行. 這樣做的時候, '\' 後面不能跟隨任何非命令字元, 包括空白及定位鍵. 最後要執行 make 的命令如下 :
make target

target 是 make 檔中所定義的目標之一. 如果你沒給 target, make 會產生第一個目標. 例如我們前面的 make 檔可以由下面三種方式之一啟動 :
% make stimulate

% make stimulate.db

% make

第一個命令執行達成目標 stimulate 所需的命令, 第二個產生 stimulate.db, 第三個執行 make 檔中第一個目標 (本例中為 stimulate).

執行範例 :
[benjamin@localhost ~/src/test]$ make stimulate <執行 target : stimulate 產出程式 stimulate>
gcc -o stimulate -O stimulate.c inputs.c outputs.c
In file included from stimulate.c:1:
outputs.h:6:7: warning: no newline at end of file
In file included from stimulate.c:2:
inputs.h:5:7: warning: no newline at end of file
stimulate.c:8:2: warning: no newline at end of file
In file included from inputs.c:1:
inputs.h:5:7: warning: no newline at end of file
In file included from outputs.c:1:
outputs.h:6:7: warning: no newline at end of file
outputs.c:5:2: warning: no newline at end of file
[benjamin@localhost ~/src/test]$ ./stimulate <執行程式 stimulate>
Hello John
Hi, Peter
Byebye
[benjamin@localhost ~/src/test]$ make <執行 make 檔案第一個 target, 此處為 stimulate>
`stimulate' is up to date. <因為 stimulate 的修改時間晚於 stimulate.c inputs.c outputs.c 所以為 up to date> 
This message was edited 4 times. Last update was at 21/02/2010 18:06:26

沒有留言:

張貼留言

[Git 常見問題] error: The following untracked working tree files would be overwritten by merge

  Source From  Here 方案1: // x -----删除忽略文件已经对 git 来说不识别的文件 // d -----删除未被添加到 git 的路径中的文件 // f -----强制运行 #   git clean -d -fx 方案2: 今天在服务器上  gi...