怎么進行Makefile分析,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。
創(chuàng)新互聯(lián)建站專業(yè)為企業(yè)提供綏陽網(wǎng)站建設(shè)、綏陽做網(wǎng)站、綏陽網(wǎng)站設(shè)計、綏陽網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計與制作、綏陽企業(yè)網(wǎng)站模板建站服務(wù),十年綏陽做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡(luò)服務(wù)。
Makefile是一個名為GNU-Make軟件所需要的腳本文件,該腳本文件可以指導(dǎo)Make軟件控制arm-gcc等工具鏈去編譯工程文件最終得到可執(zhí)行文件,幾乎所有的Linux發(fā)行版都內(nèi)置了GNU-Make軟件,VScode等多種IED也內(nèi)置了Make程序。
你見到的xxx.mk文件或者Makefile都統(tǒng)稱為Makefile腳本文件。
Makefile的規(guī)則如下,這里的[TAB]指鍵盤上的TAB按鍵,不是空格,如果在命令前輸入了空格則會造成錯誤,并且在Makefile中TAB鍵不能隨意使用:
目標 : 依賴
[TAB]命令
例如:
Hello :
@echo “Hello”
這時執(zhí)行make命令就會輸出一條語句”Hello”,Hello是目標,依賴為空,為了生成目標,需要執(zhí)行echo “Hello”語句,從而導(dǎo)致輸出Hello。
例如:假設(shè)我們有一個Hello.c C語言源文件,需要將其編譯不鏈接為Hello.o文件,最后在進行連接,Makefile內(nèi)容如下:
Hello.out : Hello.o
gcc -o Hello.out Hello.o
Hello.o : Hello.c
gcc -c -o Hello.o Hello.c
這時執(zhí)行make命令,make解釋器發(fā)現(xiàn)目標為“Hello.out”,但是生成Hello.out需要Hello.o,發(fā)現(xiàn)目錄下找不到“Hello.o”,就向下查找是否有生成Hello.o的規(guī)則,找到了,發(fā)現(xiàn)”Hello.o”依賴于”Hello.c”,在目錄下也找到了Hello.c,就執(zhí)行語句“gcc -c -o Hello.o Hello.c”生成”Hello.o”,只要編譯過程不出錯,即可得到”Hello.o”,這時可以執(zhí)行“gcc -o Hello.out Hello.o“生成”Hello.out”
這里可以用分析一個C語言或Java語言程序來類比,一般都是根據(jù)程序是執(zhí)行流來進行分析,也就是先找到main函數(shù),因為main函數(shù)是程序的執(zhí)行入口,Makefile也有執(zhí)行入口,在執(zhí)行make命令時,make解釋器默認搜索當(dāng)前目錄下名為“Makefile”的文件,找到后,執(zhí)行生成第一個目標的命令及生成其依賴所需的命令。
這里選擇在SDK/Targets目錄中STM32L431_BearPi工程中的GCC目錄下的Makefile開始分析。
第1行到第140行都是設(shè)置一些變量和導(dǎo)入一些makefile文件(其中也沒有任何規(guī)則,都是進行一些變量的設(shè)置),第143行是第一條規(guī)則
當(dāng)我們執(zhí)行make或make all時,就開始生成all目標,其依賴于BUILD_DIR(GCC/build)目錄中的TARGET(Huawei_LiteOS).elf文件,BUILD_DIR和TARGET為兩個變量,一開始就被賦值,如下圖所示,實際使用時$(變量)會被替換為變量的值,例如$(TARGET).elf最終會被替換為Huawei_LiteOS.elf。
可是Huawei_LiteOS.elf還不存在,make只好繼續(xù)向下查找是否有生成Huawei_LiteOS.elf的規(guī)則,好在第147行的目標為Huawei_LiteOS.elf,這就是生成Huawei_LiteOS.elf的規(guī)則,該規(guī)則依賴為$(OBJ_DIRS) $(C_OBJ) $(S_OBJ)分別對應(yīng)三個目錄,這三個目錄都不存在,所以make只好繼續(xù)向下查找,它發(fā)現(xiàn)第152行正好為目標是該目錄的規(guī)則,就創(chuàng)建了該目錄,解決了$(OBJ_DIRS)這個依賴,接著該處理$(C_OBJ)這個依賴
Make向下查找依賴發(fā)現(xiàn)位于第156行出現(xiàn)生成這個以來的規(guī)則,這里的$(C_OBJ):$(BUILD_DIR)/%.o:%.c對應(yīng)makefile中的靜態(tài)模式,我這里簡單的說一下,大家如果想深入了解可以自行百度。
靜態(tài)模型的格式如下:
目標列表: 與目標列表相匹配的模型: 與依賴相匹配的模型
[TAB]命令
來看一個例子,
目標列表中有foo.o、bar.o和test.s三個值,首先將匹配%.o的模型找出來,test.s不匹配就被遺棄了,將匹配的foo.o和bar.o替換為foo.c和bar.c,最終這一條規(guī)則等于執(zhí)行了下列這兩條規(guī)則,為什么要這樣做呢?你可以試想以下,假設(shè)目標列表中有幾千個文件,這樣做的話是不是就可以少寫很多規(guī)則:
回到LiteOS_Lab的Makefile上來,156行將C_OBJ變量中的符合build/xxx.o格式的文件作為xxx.c格式的依賴,C_OBJ變量的賦值如下圖所示:
$(patsubst PATTERN, REPLACEMENT, TEXT)函數(shù)的作用是模式替換,將TEXT中以空格隔開的每個單詞(文件名),符合PATTERN格式的替換為REPLACEMENT格式,例如第124行,將所有的C_SOURCE變量中的文件名,凡是只要在SDK/ iot_link目錄下的都替換為.o后綴,SDK/ iotlink目錄中有一個符合該模型的文件,link_main.c,在執(zhí)行該規(guī)則對應(yīng)的命令時,目標文件為link_main.o,第125和126行同上。
這里以link_main.c為例向大家講解指令,經(jīng)過模式替換后規(guī)則如下:
link_main.o: link_main.c
$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(@:%.o=%.lst) $< -o $@
這里的CC是指arm-none-eabi-gcc,CFLAGS是指各類編譯參數(shù),例如-MMD -MP -Wno-missing-braces,$(@:%.o=%.lst)函數(shù)的作用是將$@目標符合%.o模型的值替換為%. lst,這里就將link_main.o替換link_main. lst,$<是指第一個依賴,$@是指目標,組合的命令后如下:
arm-none-eabi-gcc -c -MMD -MP -Wno-missing-braces -Wa,-a,-ad,-alms=link_main. lst link_main.c -o link_main.o。一句話來說就是將所有的.c源文件編譯不鏈接生成.o文件和.lst文件,等待后續(xù)進一步操作。
第160和161行的操作類似于上面的操作,將所有的匯編文件都編譯不鏈接生成.o文件等待后續(xù)進一步操作。
第147到149行規(guī)則所需的依賴全部都生成好了,可以開始執(zhí)行該規(guī)則的命令,將所有依賴通過LDFLAGS變量中的值鏈接生成Huawei_LiteOS.elf文件并列出程序文件中各段的大小。
LDFLAGS變量中通過MCU變量定義了內(nèi)核相關(guān)參數(shù),例如ARM架構(gòu)的版本以及是否支持硬件浮點數(shù)運算等參數(shù),如下圖所示,如果你需要將工程移植到不是STM32L431系類的MCU上,就需要修改MCU變量的值。
LDFLAGS變量中通過LDSCRIPT變量讀取os.ld鏈接腳本來控制程序該如何鏈接,每個段應(yīng)該存放在程序中的何處,在os.ld鏈接腳本中還指明了MCU的RAM和FLASH大小及起始位置,我們在進行移植時也需要修改。
Huawei_LiteOS.elf文件是第143至145行規(guī)則的依賴,將該elf文件轉(zhuǎn)換為Huawei_LiteOS.hex和Huawei_LiteOS.bin文件,即可燒錄。
現(xiàn)在大家應(yīng)該明白make控制下的整個程序編譯過程了吧,以及Makefile文件起到的作用,我們再來看看前面的include導(dǎo)入的文件,如下圖所示:
首先導(dǎo)入了.config文件,這由Kconfig軟件讀取用戶通過圖形化配置的各項參數(shù)信息生成的,其中包含了對SDK中各組件參數(shù)的配置信息,如下圖所示:
以AT組件為例,CONFIG_AT_ENABLE=y代表使能AT組件;CONFIG_AT_DEVNAME="atdev"將AT組件用到的串口以"atdev"注冊到driver層中;CONFIG_AT_OOBTABLEN=6 OOB表的長度配置為6個,這時用于接收異步數(shù)據(jù)的結(jié)構(gòu)體,意味著我們最多能配置6個特定字符串,當(dāng)這時字符串出現(xiàn)時調(diào)用相應(yīng)處理函數(shù)進行處理;CONFIG_AT_RECVMAXLEN=1024將AT框架中的接收緩存區(qū)大小配置為1024字節(jié),如果你的MCU資源受限可以減少這里的大?。籆ONFIG_AT_TASKPRIOR=10將AT任務(wù)的優(yōu)先級配置為10,注意:這里只有第35行這條語句會影響Make的編譯,其余語句是為了記錄用戶做了哪些配置和生成iot_config.h所用。
.config文件中所有的組件配置都和上面分析的一致,如果組件沒有被使能如下圖所示:
相信大家看到這里又有新的疑問了,這些配置是如何影響到程序的編譯呢?回到前面的第70行include $(SDK_DIR)/iot_link/iot.mk,來看看這個SDK/iot_link目錄下的iot.mk文件中有什么你就有答案了,如下圖所示:
?
該Makefile將每個組件所屬文件夾下的Makefile也導(dǎo)入進來了,我們還是以AT框架為例,第31行,導(dǎo)入at目錄下的at.mk文件,該Makefile內(nèi)容如下圖所示:
看到了吧,第7行與前面的CONFIG_AT_ENABLE=y變量相對應(yīng),ifeq ($(CONFIG_AT_ENABLE),y)語句的意思是如果CONFIG_AT_ENABLE變量的值為y,則將ifeq到endif之間的語句全部執(zhí)行。
第8至9行將at目錄下所有.c文件添加到C_SOURCES變量中,注意這里用的是+=是追加上去。
第11至12行將at目錄下所有.h文件所在路徑(注意是路徑,通過-I參數(shù)指定頭文件所在的路徑,這樣編譯器才能找到頭文件,否則會因為找不到頭文件導(dǎo)致編譯失?。┨砑拥紺_INCLUDES變量中,注意這里用的是+=是追加上去。
第14至14行將-D CONFIG_AT_ENABLE=1文本追加到C_DEFS變量中。
這三個變量大家都很眼熟吧,這就是工程目錄/GCC目錄中Makefile中的那三個變量,如下圖所示:
這樣AT組件中的所有源文件和頭文件就參與了編譯。
回到第三個include,include $(MAKEFILE_DIR)/project.mk,這是用于包含(引入)工程目錄/GCC目錄下的project.mk,該Makefile部分內(nèi)容如下圖所示:
主要用于包含Hal庫中的文件以及用戶自己添加進去的文件,這也是移植時需要進行修改的文件之一,大家可以仿照我前面分析的方法自己分析一下。最終所有被添加進入的.c源文件會被追加到C_SOURCES變量中,所有.h頭文件所在的路徑會被追加到C_INCLUDES變量中。
SDK中所有的Makefile文件都不需要也不能進行修改,只需要修改工程中的三個Makefile,.config(這個不用手動修改,可以通過圖形化配置進行修改),Makefile(根據(jù)目標MCU修改MCU相關(guān)的參數(shù)即可,也就是MCU這個變量的值),project.mk(根據(jù)目標MCU修改、添加或刪除庫文件以及用戶文件以及最后的C_DEFS變量即可)。
看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進一步的了解或閱讀更多相關(guān)文章,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對創(chuàng)新互聯(lián)的支持。