?
相信大家在使用Linux環(huán)境編程的時候,一定接觸過Makefile這個玩意。Makefile在搭建自定義的編譯環(huán)境,尤其是自動化編譯、多功能一鍵編譯等功能上,還是發(fā)揮了很大的作用。如果接觸過Linux內核編譯的童鞋,一定會看到編譯內核中的各級Makefile中,有很多地方都會有 FORCE 這樣的字段出現,那么這個 FORCE 究竟是何方神圣呢?本文將給你答案,通過閱讀本文,你講了解到以下內容:
- Makefile的基本規(guī)則
- FORCE 在Makefile的含義
- FORCE在實際工程中的應用
Makefile的基本規(guī)則
Makefile的基本形式如下所示:
TARGET :DEPENDENCES
CMD
# TARGET:生成的目標,可以是一個文件,也可以是一個虛擬符號(非真實文件)
# DEPENDENCES: 生成目標的所有依賴,它是一個集合,可以只有一個文件,或者很多文件;也可以是虛擬符號
# CMD:把所有依賴生成目標的執(zhí)行命令,其中CMD前面是一個TAB鍵,而不能是空格
![poYBAGDYdXCAWkKMAAAAK8RNs4s030.png](https://file.elecfans.com/web2/M00/03/FB/poYBAGDYdXCAWkKMAAAAK8RNs4s030.png)
Makefile的基本規(guī)則,歸納起來就是一句話:【當一個TARGET (欲生成的目標)比它的任何一個DEPENDENCES(依賴的文件)舊時,這個TARGET就要重新生成】。
這句話讀起來很簡單,就是Makefile在執(zhí)行編譯的時候,會先去判斷TARGET和其依賴文件的時間,如果TARGET的時間比較舊,意味著依賴文件有更新了,所以TARGET要重新生成;反之,如果TARGET的時間比較新,意味著在TARGET生成后,依賴文件是沒有改變過的,自然就不用重新去生成TARGET。
FORCE 在Makefile的含義
有了上一小節(jié)中介紹的Makefile基本概念后,我們來進一步分析下Makefile中的FORCE,以下是FORCE在Makefile中出現的最簡化版本:
file = test.txt
all: generate-a-file
generate-a-file: $(file)
$(file):
@echo "Force to generate a test file for every make ..."
rm -rf $(file) && echo `date "+%Y-%m-%d %H:%M:%S"` > $(file)
FORCE:
.PHONY: FORCE
![poYBAGDYdXCAWkKMAAAAK8RNs4s030.png](https://file.elecfans.com/web2/M00/03/FB/poYBAGDYdXCAWkKMAAAAK8RNs4s030.png)
這個Makefile不是用于真正的編譯工程,而是提供一個很簡單的功能,生成一個test.txt,并且這個test.txt的內容是記錄每次編譯的時間。但是使用這個makefile執(zhí)行make時,發(fā)現只有第一次make的時候,才會生成test.txt,而其他時候只要test.txt還存在都不會重新生成,如下所示:
![](https://file.elecfans.com//web2/M00/59/21/pYYBAGLkx9WAdsvFAADh4cnDmjQ934.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N6dWxsYw==,size_16,color_FFFFFF,t_70)
![poYBAGDYdXCAWkKMAAAAK8RNs4s030.png](https://file.elecfans.com/web2/M00/03/FB/poYBAGDYdXCAWkKMAAAAK8RNs4s030.png)
?編輯
很明顯,它沒有達到我們期望的“每次編譯都重新生成test.txt”。這個時候 FORCE就發(fā)揮作用了,我們在 test.txt目標的后面添加 FORCE作為它的依賴試試看,即如下所示:
file = test.txt
all: generate-a-file
generate-a-file: $(file)
$(file): FORCE #FORCE表示每次這段都要執(zhí)行
@echo "Force to generate a test file for every make ..."
rm -rf $(file) && echo `date "+%Y-%m-%d %H:%M:%S"` > $(file)
FORCE:
.PHONY: FORCE
![poYBAGDYdXCAWkKMAAAAK8RNs4s030.png](https://file.elecfans.com/web2/M00/03/FB/poYBAGDYdXCAWkKMAAAAK8RNs4s030.png)
執(zhí)行輸出如下所示: 我們可以看到,這達到了我們的目的,每次test.txt都是重新生成了,它記錄了每次make的時間。
![](https://file.elecfans.com//web2/M00/58/83/poYBAGLkx9WATXwpAADJVLSfPU8135.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N6dWxsYw==,size_16,color_FFFFFF,t_70)
![poYBAGDYdXCAWkKMAAAAK8RNs4s030.png](https://file.elecfans.com/web2/M00/03/FB/poYBAGDYdXCAWkKMAAAAK8RNs4s030.png)
?編輯
這個就是要歸功于 FORCE 的功勞了。我們來分析下,為何加了 FORCE 就能實現這樣的功能。
我們可以注意到FORCE這個目標,它的DEPENDENCES是空的,CMD部分也是空的;這個比較特殊了,在Makefile里,像這樣依賴為空、執(zhí)行命令也為空的TARGET,則需要每次都重新生成,而這個TARGET不一定是一個文件,可以是任意的符號,而 FORCE 只是我們最常用的符號,理論上它可以換成任意符號,比如NO-FORCE、SOMETHING等等。
FORCE在實際工程中的應用
上一小節(jié),我們講到可以用Makefile配合shell命令來自動生成一些文件,自然我們很容易想到,在我們實際的編譯工程中,往往需要動態(tài)生成一些配置項,然后嵌入到代碼中,比如編譯版本號、編譯時間等。
假設我們有以下一個main.c:
#include
#include "build_info.h" #這個頭文件需要每次編譯時自動生成
int main(int argc, const char *argv[])
{
printf("%s >>> APP_TIME=%s\n", __func__, APP_TIME);
return 0;
}
![poYBAGDYdXCAWkKMAAAAK8RNs4s030.png](https://file.elecfans.com/web2/M00/03/FB/poYBAGDYdXCAWkKMAAAAK8RNs4s030.png)
示例代碼很簡單,就是再main函數中打印一個 build_info.h中的一個宏定義APP_TIME,這個build_info.h要求每次編譯的時候都重新生成。我們給出的Makefile示例如下:
##拷貝時,注意tab鍵
SHELL = /bin/bash #指定shell使用/bin/bash,否則echo -e可能會出問題
ECHO = echo
BIN = test
BUILG_INFO_H = build_info.h
SRC-C-y = main.c
SRC-O = $(patsubst %.c, $(O)%.o, $(SRC-C-y))
all: gen_build_info $(BIN)
$(BIN) : $(SRC-O)
gcc -o $(O)"$@" $(SRC-O)
%.o : %.c
gcc -c "$<" -o "$@"
gen_build_info: $(BUILG_INFO_H)
$(BUILG_INFO_H): FORCE #強制生成build_info.h
@$(RM) $@
@$(ECHO) ' GEN $@'
@$(ECHO) -e " #ifndef __BUILD_INFO_H__\n"\
"#define __BUILD_INFO_H__\n"\
"#define APP_TIME \"`date "+%Y-%m-%d %H:%M:%S"`\"\n"\
"#endif" > $@
FORCE:
.PHONY: FORCE
![poYBAGDYdXCAWkKMAAAAK8RNs4s030.png](https://file.elecfans.com/web2/M00/03/FB/poYBAGDYdXCAWkKMAAAAK8RNs4s030.png)
make執(zhí)行輸出測試如下:
![](https://file.elecfans.com//web2/M00/59/21/pYYBAGLkx9aAO6oPAACzQzpro2U242.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N6dWxsYw==,size_16,color_FFFFFF,t_70)
![poYBAGDYdXCAWkKMAAAAK8RNs4s030.png](https://file.elecfans.com/web2/M00/03/FB/poYBAGDYdXCAWkKMAAAAK8RNs4s030.png)
?編輯
從輸出的測試,我們可以看出,make的每次執(zhí)行都觸發(fā)了生成build_info.h,但是運行編譯出來的test可執(zhí)行程序,我們發(fā)現并不是每次生成的build_info.h的內容都傳遞到了test里面;也就是當build_info.h改變的時候,test沒有被重新編譯。這里先留下點疑問,為何會產生這樣的問題。博主將會在后續(xù)的文章中解決這個問題。
不管怎么樣,經過對上文的學習,我們至少掌握了 FORCE的基本用法,而在實際項目工程中,我們也見證了它的威力;那么,你學會了嗎?如果還有疑問,歡迎在評論席提出你的問題和看法,博主定會盡力解決你的困惑。
版權申明:本文為博主原創(chuàng)文章,轉載請注明出處!【Linux + Makefile】十分鐘教你學會Makefile的FORCE_架構師李肯-CSDN博客
原創(chuàng)作者:recan
電子郵箱:recan.szu@foxmail.com
延伸閱讀:
【Linux + Makefile】Makefile的高階用法:解決C文件包含的頭文件修改了,但C文件不重新編譯的問題
?審核編輯:湯梓紅
-
Linux
+關注
關注
87文章
11351瀏覽量
210512 -
Makefile
+關注
關注
1文章
125瀏覽量
19225 -
RT-Thread
+關注
關注
31文章
1306瀏覽量
40436
發(fā)布評論請先 登錄
相關推薦
RT-Thread 應用筆記 - RTC Alarm 組件的使用
![<b class='flag-5'>RT-Thread</b> 應用<b class='flag-5'>筆記</b> - RTC Alarm 組件的使用](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
RT-Thread 內核學習筆記 - 理解defunct僵尸線程
![<b class='flag-5'>RT-Thread</b> 內核<b class='flag-5'>學習</b><b class='flag-5'>筆記</b> - 理解defunct僵尸線程](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
RT-Thread 內核學習筆記 - 設備模型rt_device的理解
![<b class='flag-5'>RT-Thread</b> 內核<b class='flag-5'>學習</b><b class='flag-5'>筆記</b> - 設備模型<b class='flag-5'>rt</b>_device的理解](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
RT-Thread 內核學習筆記 - 內核對象鏈表結構深入理解
![<b class='flag-5'>RT-Thread</b> 內核<b class='flag-5'>學習</b><b class='flag-5'>筆記</b> - 內核對象鏈表結構深入理解](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
RT-Thread 內核學習筆記 - 內核對象初始化鏈表組織方式
![<b class='flag-5'>RT-Thread</b> 內核<b class='flag-5'>學習</b><b class='flag-5'>筆記</b> - 內核對象初始化鏈表組織方式](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
RT-Thread 內核學習筆記 - 內核對象操作API
![<b class='flag-5'>RT-Thread</b> 內核<b class='flag-5'>學習</b><b class='flag-5'>筆記</b> - 內核對象操作API](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
RT-Thread學習筆記 --(6)RT-Thread線程間通信學習過程總結
![<b class='flag-5'>RT-Thread</b><b class='flag-5'>學習</b><b class='flag-5'>筆記</b> --(6)<b class='flag-5'>RT-Thread</b>線程間通信<b class='flag-5'>學習</b>過程總結](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
RT-Thread學習筆記分享
RT-Thread學習筆記 RT-Thread的架構概述
![<b class='flag-5'>RT-Thread</b><b class='flag-5'>學習</b><b class='flag-5'>筆記</b> <b class='flag-5'>RT-Thread</b>的架構概述](https://file.elecfans.com/web2/M00/52/31/pYYBAGLKk5WAA__jAADjdAdXhIs410.jpg)
評論