前言 :
在 makefile 檔案中你可以定義若干變數以方便管理統一常用的參數或路徑. 其中最簡單的變數具有如下的語法 :
這代表我們要展開的變數名稱為 variable-name. 任何文字幾乎都可以含在變數之中. 一般來說你必須以 ${} 或 $() 將變數名稱括住, 這樣 make 才會認得, 但若變數是單一字符則不需要括號括住變數名稱. 通常 makefile 都會定義許多變數, 不過其中有許多特殊變數是 make 自動定義的. 這些變數可提供使用者來控制 make 的行為或是 make 與使用者的makefile 溝通的介面.
自動變數 :
當規則相符時, make 會設定自動變數. 透過它們你可以取用工作目標及必要條件中的元素, 所以你不必指明任何檔案名稱. 要避免重複, 自動變數相當有用, 它們也是定義較一般樣式規則不可或缺的項目. 下面是七個 "核心" 的自動變數 :
此外為了與其他版本的 make 相容, 以上這六個變數都具有變體. 其中一個變體只會傳回值的目錄部分. 它的指定方式就是在原有符號附加 D 這個字母, 比如 $(@D), $( 等等. 另一個變體只會傳回值的檔案部分. 它的指定方式就是在原有的符號之後附加 F 這個字母, 比如 $(@F), $( 等等.
make 會在它的工作目標和必要條件相符後設定自動變數, 所以變數只能應用在規則中的命令稿部分. 現在我們可以將範例 (如何建立一個簡單Makefile) 中的 makefile 置換成適當變數 :
* 檔案 makefile 內容 :
以 VPATH 和 vpath 來找尋檔案 :
到目前為止我們的 makefile 與 原始檔都擺在同一個目錄. 真實世界比較複雜, 現在讓我們重整 (refactor) 先前的範例 (如何建立一個簡單Makefile), 進行比較實際的檔案布局. 我們透過將 main 重整成一個呼叫 counter 的函式來修改我的的單字記數程式 :
* counter.c 代碼 :
* counter.h 代碼 :
* lexer.h 代碼 :
按照原碼樹的布局慣例, 標頭擋會被擺在 include 目錄中, 而原始檔案會被擺在 src 目錄裡. 我們會將 makefile 擺在他們的上層目錄. 範例程式布局如下圖 :
既然現在我們的原始檔中包含了標頭檔, 這些依存關係就應該記錄在我們的 makefile 檔中. 這樣當我們的標頭檔有所改變時, 才會更新相應的目的檔.
* makefile 內容 :
現在試著執行 make :
發生了什麼事? makefile 想更新 count_words.c, 但卻找不到對應的規則, 但實際上它應該是一個原始檔! 考慮我們的第一個必要條件是 count_words.o. 但我們並未看到這個檔案, 所以我們會去尋找一個規則以便建立此檔案. 用來建立 count_words.o 的自訂規則指向 count_words.c. 但何以 make 檔案找不到這個原始檔? 因為這個原始檔並非位於當前工作目錄中, 而是被擺在 src 目錄裡. 除非你告訴 make , 否則它只會在當前目錄中尋找工作目標以及必要條件. 我們要怎麼做才有辦法讓 make 到 src 目錄找尋原始檔? 也就是說要怎麼告訴 make 我們的原始檔擺在哪?
你可以使用 VPATH 或 vpath 來告訴 make 到不同的目錄去尋找原始檔. 要解決我們的問題可以在 makefile 檔中新增下面一行 :
所以新的 makefile 內容如下 :
現在執行 make 會看到下面結果 :
請注意現在 make 可以編譯第一個檔案, 因為它會為該檔案正確填入相對路徑. 使用自動變數的另一個理由是 : 如果你將檔名寫死, make 將無法為該檔案填上正確的路徑. 但是編譯仍然沒有成功, 因為 gcc 無法找到引入檔 (include file). 我們只要使用正確的 -I 選項來 "定製" 內定編譯規則就可以修正這個問題了 :
所以新的 makefile 會是 :
現在我們可以順利完成建造的工作 :
VPATH 變數的內容是一份目錄清單, 可供 make 搜尋其所需要的檔案. 這份目錄清單可用來搜尋工作目標以及必要條件, 但不包括命令稿中所提及的檔案. 這份目錄清單的分隔在 Unix 上可以是空格或冒號, 在 Windows 上可以是空格或分號. 建議使用空白, 這樣就不會有平台的依賴性. 雖然 VPATH 可以解決上述搜尋問題, 但仍有些限制. make 將會為它所需要的任何檔案搜尋 VPATH 清單中每個目錄, 如果在多個目錄出現相同檔名, make 只會擷取第一個被找尋到的檔案, 有時這會造成問題. 此時可以使用 vpath 指令, 語法如下 :
所以之前所此用的 VPATH 變數可以改成 :
所以原來的 makefile 變動成 :
執行該 makefile :
現在我們告訴了 make , 應該在 src 目錄搜尋 .c 檔案 與在 include 目錄找尋 .h 檔案 (所以我們可以從標頭檔的必要條件移除 include/ 字樣), 在複雜的應用程式中, 這項功能可以幫我們省去許多除錯時間.
在 makefile 檔案中你可以定義若干變數以方便管理統一常用的參數或路徑. 其中最簡單的變數具有如下的語法 :
這代表我們要展開的變數名稱為 variable-name. 任何文字幾乎都可以含在變數之中. 一般來說你必須以 ${} 或 $() 將變數名稱括住, 這樣 make 才會認得, 但若變數是單一字符則不需要括號括住變數名稱. 通常 makefile 都會定義許多變數, 不過其中有許多特殊變數是 make 自動定義的. 這些變數可提供使用者來控制 make 的行為或是 make 與使用者的makefile 溝通的介面.
自動變數 :
當規則相符時, make 會設定自動變數. 透過它們你可以取用工作目標及必要條件中的元素, 所以你不必指明任何檔案名稱. 要避免重複, 自動變數相當有用, 它們也是定義較一般樣式規則不可或缺的項目. 下面是七個 "核心" 的自動變數 :
此外為了與其他版本的 make 相容, 以上這六個變數都具有變體. 其中一個變體只會傳回值的目錄部分. 它的指定方式就是在原有符號附加 D 這個字母, 比如 $(@D), $(
make 會在它的工作目標和必要條件相符後設定自動變數, 所以變數只能應用在規則中的命令稿部分. 現在我們可以將範例 (如何建立一個簡單Makefile) 中的 makefile 置換成適當變數 :
* 檔案 makefile 內容 :
- count_words: count_words.o lexer.o -lfl
- gcc $^ -o $@
- count_words.o: count_words.c
- gcc -c $<
- lexer.o: lexer.c
- gcc -c $<
- lexer.c: lexer.l
- flex -t $< > $@
到目前為止我們的 makefile 與 原始檔都擺在同一個目錄. 真實世界比較複雜, 現在讓我們重整 (refactor) 先前的範例 (如何建立一個簡單Makefile), 進行比較實際的檔案布局. 我們透過將 main 重整成一個呼叫 counter 的函式來修改我的的單字記數程式 :
* counter.c 代碼 :
- #include
- #include
- void counter(int counts[4]) {
- while(yylex());
- counts[0] = fee_count;
- counts[1] = fie_count;
- counts[2] = foe_count;
- counts[3] = fum_count;
- }
- #ifndef COUNTER_H_
- #define COUNTER_H_
- extern void counter(int counts[4]);
- #endif
- #ifndef LEXER_H_
- #define LEXER_H_
- extern int fee_count, fie_count, foe_count, fum_count;
- extern int yylex(void);
- #endif
按照原碼樹的布局慣例, 標頭擋會被擺在 include 目錄中, 而原始檔案會被擺在 src 目錄裡. 我們會將 makefile 擺在他們的上層目錄. 範例程式布局如下圖 :
既然現在我們的原始檔中包含了標頭檔, 這些依存關係就應該記錄在我們的 makefile 檔中. 這樣當我們的標頭檔有所改變時, 才會更新相應的目的檔.
* makefile 內容 :
- count_words: count_words.o counter.o lexer.o -lfl
- gcc $^ -o $@
- count_words.o: count_words.c include/counter.h
- gcc -c $(CPPFLAGS) $<
- counter.o: counter.c include/counter.h include/lexer.h
- gcc -c $(CPPFLAGS) $<
- lexer.o: lexer.c include/lexer.h
- gcc -c $(CPPFLAGS) $<
- lexer.c: lexer.l
- flex -t $< > $@
現在試著執行 make :
發生了什麼事? makefile 想更新 count_words.c, 但卻找不到對應的規則, 但實際上它應該是一個原始檔! 考慮我們的第一個必要條件是 count_words.o. 但我們並未看到這個檔案, 所以我們會去尋找一個規則以便建立此檔案. 用來建立 count_words.o 的自訂規則指向 count_words.c. 但何以 make 檔案找不到這個原始檔? 因為這個原始檔並非位於當前工作目錄中, 而是被擺在 src 目錄裡. 除非你告訴 make , 否則它只會在當前目錄中尋找工作目標以及必要條件. 我們要怎麼做才有辦法讓 make 到 src 目錄找尋原始檔? 也就是說要怎麼告訴 make 我們的原始檔擺在哪?
你可以使用 VPATH 或 vpath 來告訴 make 到不同的目錄去尋找原始檔. 要解決我們的問題可以在 makefile 檔中新增下面一行 :
所以新的 makefile 內容如下 :
- VPATH = src
- count_words: count_words.o counter.o lexer.o -lfl
- gcc $^ -o $@
- count_words.o: count_words.c include/counter.h
- gcc -c $(CPPFLAGS) $<
- counter.o: counter.c include/counter.h include/lexer.h
- gcc -c $(CPPFLAGS) $<
- lexer.o: lexer.c include/lexer.h
- gcc -c $(CPPFLAGS) $<
- lexer.c: lexer.l
- flex -t $< > $@
現在執行 make 會看到下面結果 :
請注意現在 make 可以編譯第一個檔案, 因為它會為該檔案正確填入相對路徑. 使用自動變數的另一個理由是 : 如果你將檔名寫死, make 將無法為該檔案填上正確的路徑. 但是編譯仍然沒有成功, 因為 gcc 無法找到引入檔 (include file). 我們只要使用正確的 -I 選項來 "定製" 內定編譯規則就可以修正這個問題了 :
所以新的 makefile 會是 :
- VPATH = src include
- CPPFLAGS = -I include
- count_words: count_words.o counter.o lexer.o -lfl
- gcc $^ -o $@
- count_words.o: count_words.c include/counter.h
- gcc -c $(CPPFLAGS) $<
- counter.o: counter.c include/counter.h include/lexer.h
- gcc -c $(CPPFLAGS) $<
- lexer.o: lexer.c include/lexer.h
- gcc -c $(CPPFLAGS) $<
- lexer.c: lexer.l
- flex -t $< > $@
- clean:
- rm -f *.o lexer.c
現在我們可以順利完成建造的工作 :
VPATH 變數的內容是一份目錄清單, 可供 make 搜尋其所需要的檔案. 這份目錄清單可用來搜尋工作目標以及必要條件, 但不包括命令稿中所提及的檔案. 這份目錄清單的分隔在 Unix 上可以是空格或冒號, 在 Windows 上可以是空格或分號. 建議使用空白, 這樣就不會有平台的依賴性. 雖然 VPATH 可以解決上述搜尋問題, 但仍有些限制. make 將會為它所需要的任何檔案搜尋 VPATH 清單中每個目錄, 如果在多個目錄出現相同檔名, make 只會擷取第一個被找尋到的檔案, 有時這會造成問題. 此時可以使用 vpath 指令, 語法如下 :
所以之前所此用的 VPATH 變數可以改成 :
所以原來的 makefile 變動成 :
- VPATH = src include
- vpath %.l %.c src
- vpath %.h include
- CPPFLAGS = -I include
- count_words: count_words.o counter.o lexer.o -lfl
- gcc $^ -o $@
- count_words.o: count_words.c include/counter.h
- gcc -c $(CPPFLAGS) $<
- counter.o: counter.c include/counter.h include/lexer.h
- gcc -c $(CPPFLAGS) $<
- lexer.o: lexer.c include/lexer.h
- gcc -c $(CPPFLAGS) $<
- lexer.c: lexer.l
- flex -t $< > $@
- clean:
- rm -f *.o lexer.c
執行該 makefile :
現在我們告訴了 make , 應該在 src 目錄搜尋 .c 檔案 與在 include 目錄找尋 .h 檔案 (所以我們可以從標頭檔的必要條件移除 include/ 字樣), 在複雜的應用程式中, 這項功能可以幫我們省去許多除錯時間.
寫得很清楚, 謝謝!
回覆刪除Welcome!
刪除