1. 程式人生 > >Makefile巢狀執行

Makefile巢狀執行

轉自:http://blog.csdn.net/shallnet/article/details/37595465?utm_source=tuicool&utm_medium=referral

=================================================

在大一些的專案裡面,所有原始碼不會只放在同一個目錄,一般各個功能模組的原始碼都是分開的,各自放在各自目錄下,並且標頭檔案和.c原始檔也會有各自的目錄,這樣便於專案程式碼的維護。這樣我們可以在每個功能模組目錄下都寫一個Makefile,各自Makefile處理各自功能的編譯連結工作,這樣我們就不必把所有功能的編譯連結都放在同一個Makefile裡面,這可使得我們的Makefile變得更加簡潔,並且編譯的時候可選擇編譯哪一個模組,這對分塊編譯有很大的好處。

現在我所處於工程目錄樹如下:

  1. .  
  2. ├── include  
  3. │   ├── common.h  
  4. │   ├── ipc  
  5. │   │   └── ipc.h  
  6. │   └── tools  
  7. │       ├── base64.h  
  8. │       ├── md5.h  
  9. │       └── tools.h  
  10. ├── Makefile  
  11. ├── src  
  12. │   ├── ipc  
  13. │   │   ├── inc  
  14. │   │   ├── Makefile  
  15. │   │   └── src  
  16. │   │       └── ipc.c  
  17. │   ├── main  
  18. │   │   ├── inc  
  19. │   │   ├── Makefile  
  20. │   │   └── src  
  21. │   │       ├── main.c  
  22. │   │       └── main.c~  
  23. │   └── tools  
  24. │       ├── inc  
  25. │       ├── Makefile  
  26. │       └── src  
  27. │           ├── base64.c  
  28. │           ├── md5.c  
  29. │           └── tools.c  
  30. └── tags  
  31. 13 directories, 16 files  

這樣組織專案原始碼要比之前合理一些,那這樣怎麼來寫Makefile呢?我們可以在每個目錄下寫一個Makefile,通過最頂層的Makefile一層一層的向下巢狀執行各層Makefile。那麼我們最頂層的Makefile簡單點的話可以這樣寫:

  1. # top Makefile for xxx  
  2. all :  
  3. >---$(MAKE) -C src  
  4. tags:  
  5. >---ctags -R  
  6. clean :  
  7. >---$(MAKE) -C src clean  
  8. .PHONY : all clean tags  

命令:

>---$(MAKE) -C src

就是進入src目錄繼續執行該目錄下的Makefile。然後src目錄下的Makefile在使用同樣的方法進入下一級目錄tools、main、ipc,再執行該目錄下的Makefile。其實這樣有些麻煩,我們可以直接從頂層目錄進入最後的目錄執行make。再加入一些偽目標完善下,我們的頂層Makefile就出來了:

  1. # Top Makefile for C program  
  2. # Copyright (C) 2014 shallnew \at 163 \dot com  
  3. all :  
  4. >---$(MAKE) -C src/ipc  
  5. >---$(MAKE) -C src/tools  
  6. >---$(MAKE) -C src/main  
  7. tags:  
  8. >---ctags -R  
  9. help:  
  10. >[email protected] "===============A common Makefilefor c programs=============="  
  11. >[email protected] "Copyright (C) 2014 liuy0711 \at 163\dot com"  
  12. >[email protected] "The following targets aresupport:"  
  13. >[email protected]  
  14. >[email protected] " all              - (==make) compile and link"  
  15. >[email protected] " obj              - just compile, withoutlink"  
  16. >[email protected] " clean            - clean target"  
  17. >[email protected] " distclean        - clean target and otherinformation"  
  18. >[email protected] " tags             - create ctags for vimeditor"  
  19. >[email protected] " help             - print help information"  
  20. >[email protected]  
  21. >[email protected] "To make a target, do 'make[target]'"  
  22. >[email protected] "========================= Version2.0 ======================="  
  23. obj:  
  24. >---$(MAKE) -C src/ipc obj  
  25. >---$(MAKE) -C src/tools obj  
  26. >---$(MAKE) -C src/main obj  
  27. clean :  
  28. >---$(MAKE) -C src/ipc clean  
  29. >---$(MAKE) -C src/tools clean  
  30. >---$(MAKE) -C src/main clean  
  31. distclean:  
  32. >---$(MAKE) -C src/ipc distclean  
  33. >---$(MAKE) -C src/tools distclean  
  34. >---$(MAKE) -C src/main distclean  
  35. .PHONY : all clean distclean tags help  
當我們這樣組織原始碼時,最下面層次的Makefile怎麼寫呢?肯定不可以將我們上一節的Makefile(version 1.1)直接拷貝到功能模組目錄下,需要稍作修改。不能所有的模組都最終生成各自的可執行檔案吧,我們目前是一個工程,所以最後只會生成一個可執行程式。我們這樣做,讓主模組目錄生成可執行檔案,其他模組目錄生成靜態庫檔案,主模組連結時要用其他模組編譯產生的庫檔案來生成最終的程式。將上一節Makefile稍作修改得出編譯庫檔案Makefile和編譯可執行檔案Makefile分別如下:
  1. # A Makefile to generate archive file  
  2. # Copyright (C) 2014 shallnew \at 163 \dot com  
  3. CFLAGS += -g -Wall -Werror -O2  
  4. CPPFLAGS += -I. -I./inc -I../../include  
  5. # SRC_OBJ = $(patsubst %.c, %.o, $(wildcard *.c))  
  6. SRC_FILES = $(wildcard src/*.c)  
  7. SRC_OBJ = $(SRC_FILES:.c=.o)  
  8. SRC_LIB = libtools.a  
  9. all : $(SRC_LIB)  
  10. $(SRC_LIB) : $(SRC_OBJ)  
  11. >---$(AR) rcs [email protected] $^  
  12. >---cp [email protected] ../../libs  
  13. obj : $(SRC_OBJ)  
  14. # clean target  
  15. clean:  
  16. >---$(RM) $(SRC_OBJ) $(SRC_LIB)  
  17. distclean:  
  18. >---$(RM) $(SRC_OBJ) $(SRC_LIB) tags *~  
  19. .PHONY : all obj clean disclean  
==========================================================================
  1. # A Makefile to generate executive file                                                                                                                                                     
  2. # Copyright (C) 2014 shallnew \at 163 \dot com  
  3. CFLAGS += -g -Wall -Werror -O2  
  4. CPPFLAGS += -I. -I./inc -I../../include  
  5. LDFLAGS += -lpthread -L../../libs -ltools -lipc  
  6. # SRC_OBJ = $(patsubst %.c, %.o, $(wildcard *.c))  
  7. SRC_FILES = $(wildcard src/*.c)  
  8. SRC_OBJ = $(SRC_FILES:.c=.o)    
  9. SRC_BIN = target_bin            
  10. all : $(SRC_BIN)  
  11. $(SRC_BIN) : $(SRC_OBJ)         
  12. >---$(CC) -o [email protected] $^ $(LDFLAGS)   
  13. obj : $(SRC_OBJ)  
  14. # clean target  
  15. clean:  
  16. >---$(RM) $(SRC_OBJ) $(SRC_BIN) $(SRC_BIN).exe  
  17. distclean:  
  18. >---$(RM) $(SRC_OBJ) $(SRC_BIN) $(SRC_BIN).exe tags*~  
  19. .PHONY : all obj clean disclean  

最後在頂層執行:

  1. # make clean  
  2. make -C src/ipc clean  
  3. make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/ipc'  
  4. rm -f src/ipc.o libipc.a  
  5. make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/ipc'  
  6. make -C src/tools clean  
  7. make[1]: Entering directory `/home/Myprojects/example_make/version-3.0/src/tools'  
  8. rm -f src/base64.o src/md5.o src/tools.o libtools.a  
  9. make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/tools'  
  10. make -C src/main clean  
  11. make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/main'  
  12. rm -f src/main.o target_bin target_bin.exe  
  13. make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/main'  
  14. # make  
  15. make -C src/ipc  
  16. make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/ipc'  
  17. cc -g -Wall -Werror -O2 -I. -I./inc-I../../include  -c -o src/ipc.osrc/ipc.c  
  18. ar rcs libipc.a src/ipc.o  
  19. cp libipc.a ../../libs  
  20. make[1]: Leaving directory `/home/Myprojects/example_make/version-3.0/src/ipc'  
  21. make -C src/tools  
  22. make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/tools'  
  23. cc -g -Wall -Werror -O2 -I. -I./inc-I../../include  -c -o src/base64.osrc/base64.c  
  24. cc -g -Wall -Werror -O2 -I. -I./inc -I../../include  -c -o src/md5.o src/md5.c  
  25. cc -g -Wall -Werror -O2 -I. -I./inc-I../../include  -c -o src/tools.osrc/tools.c  
  26. ar rcs libtools.a src/base64.o src/md5.o src/tools.o  
  27. cp libtools.a ../../libs  
  28. make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/tools'  
  29. make -C src/main  
  30. make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/main'  
  31. cc -g -Wall -Werror -O2 -I. -I./inc-I../../include  -c -o src/main.osrc/main.c  
  32. cc -o target_bin src/main.o -lpthread -L../../libs -ltools-lipc  
  33. make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/main'  
  34. #  

最後生成了可執行程式檔案。這樣的話一個工程的各個模組就變得獨立出來了,不但原始碼分開了,而且各自有各自的Makefile,並且各個功能模組是可獨立編譯的。

我們發現頂層Makefile還有可以改進的地方,就是在進入下一層目錄是要重複寫多次,如下:

  1. >---$(MAKE) -C src/ipc  
  2. >---$(MAKE) -C src/tools  
  3. >---$(MAKE) -C src/main  

每增加一個目錄都要在多個偽目標裡面加入一行,這樣不夠自動化啊,於是我們想到shell的迴圈語 句,我們可以在每條規則的命令處使用for迴圈。如下:

  1. DIR = src  
  2. SUBDIRS = $(shell ls $(DIR))  
  3. all :  
  4. >[email protected] subdir in $(SUBDIRS); \  
  5. >---do $(MAKE) -C $(DIR)/$$subdir; \                                                                                                                                             
  6. >---done  

這樣懶人有可以高興很久了。不過還有問題:

上面for迴圈會依次進入系統命令ls列出的目錄,但我們對每個目錄的make順序可能有要求,在該專案當中,main目錄下的Makefile必須最後執行,因為最終的連結需要其他目錄編譯生成的庫檔案,否則會執行失敗。並且在當前的Makefile中,當子目錄執行make出現錯誤時,make不會退出。在最終執行失敗的情況下,我們很難根據錯誤的提示定位出具體是是那個目錄下的Makefile出現錯誤。這給問題定位造成了很大的困難。為了避免這樣的問題,在命令執行錯誤後make退出。

所以將剛才的Makefile修改為如下

  1. DIR = src  
  2. SUBDIRS = $(shell ls $(DIR))  
  3. all :  
  4. >[email protected] subdir in $(SUBDIRS); \  
  5. >---do $(MAKE) -C $(DIR)/$$subdir || exit 1; \                                                                                                                                             
  6. >---done  

這樣在執行出錯時立馬退出,但這樣還是沒有解決問題,編譯錯誤還是會出現。那怎麼解決呢?

我們可以通過增加規則來限制make執行順序,這樣就要用到偽目標,對每一個模組我們都為他寫一條規則,每個模組名稱是目標,最後需要執行的模組目標又是其他模組的目標,這樣就限制了make順序。在執行到最後需要執行的目標時,發現存在依賴,於是先更新依賴的目標,這樣就不會出錯了。並且這樣的話,我們還可以對指定模組進行編譯,比如我只修改了tools模組,我只想看看我修改的這個模組程式碼是否可以編譯通過,我可以在編譯時這樣:

  1. # make tools  
  2. make -C src/tools  
  3. make[1]: Entering directory`/home/Myprojects/example_make/version-2.1/src/tools'  
  4. cc -g -Wall -Werror -O2 -I. -I./inc-I../../include  -c -o src/base64.o src/base64.c  
  5. cc -g -Wall -Werror -O2 -I. -I./inc-I../../include  -c -o src/md5.osrc/md5.c  
  6. cc -g -Wall -Werror -O2 -I. -I./inc-I../../include  -c -o src/tools.osrc/tools.c  
  7. ar rcs libtools.a src/base64.o src/md5.o src/tools.o  
  8. cp libtools.a ../../libs  
  9. make[1]: Leaving directory`/home/Myprojects/example_make/version-2.1/src/tools'  
  10. #  
還有另外一種方法也可以解決此問題,就是手動列出需要進入執行的模組名稱(這裡就是目錄了),把最後需要執行的模組放在最後,這樣for迴圈執行時最後需要編譯連結的模組就放在最後了,不會像我們之前那樣make是按照使用系統命令ls列出模組目錄的順序來執行。ls列出目錄是按照每個目錄的名稱來排序的,我們總不能要求寫程式碼的時候最後執行的模組的名稱必須是以z開頭的吧,總之不現實。

 我們的頂層Makefile又進化了,也是這一節最終Makefile:

  1. # Top Makefile for C program  
  2. # Copyright (C) 2014 shallnew \at 163 \dot com  
  3. DIR = src  
  4. MODULES = $(shell ls $(DIR))  
  5. # MODULES = ipc main tools  
  6. all : $(MODULES)  
  7. $(MODULES):  
  8. >---$(MAKE) -C $(DIR)/[email protected]  
  9. main:tools ipc  
  10. obj:  
  11. 相關推薦

    Makefile執行

    轉自:http://blog.csdn.net/shallnet/article/details/37595465?utm_source=tuicool&utm_medium=referral ====================================

    從頭開始寫專案Makefile(五):執行(轉)

    【版權宣告:轉載請保留出處:blog.csdn.net/gentleliu。Mail:shallnew at 163 dot com】 在大一些的專案裡面,所有原始碼不會只放在同一個目錄,一般各個功能模組的原始碼都是分開的,各自放在各自目錄下,並且標頭檔案

    makefile

      目錄結構:   dynamic 和 static 兩個目錄實現加法功能, 分別生成動態庫和靜態庫,   main.c 主檔案連結加法功能,  終端命令執行:   make dynamic=1  則編譯動態庫

    redux-saga generator執行的阻塞與非阻塞

    1.generator呼叫generator 在one中yield另一個generatoranother function*another(params){ // ... } function*one(params,{ call, put }){ // ...

    寫了個執行make

    跟我一起寫 Makefile ------------------------------陳皓 #VPATH = BaseRender:shaderUtil:testApp:glad:. PROJECT_PATH = $(shell pwd) OBJSD

    執行MAKE $(MAKE) -C subdir

    subsystem:             cd subdir && $(MAKE) 其等價於:     subs

    shell指令碼執行expect命令

    好吧,發現漏了一篇在草稿箱 )^-^( 為避免反覆呼叫,可以巢狀執行 相關命令: spawn:啟動一個程式或程序 send:給程序或程式返回結果 expect:接受程式或程序輸出 inter

    迴圈如何執行和switch break區別

    for語句裡巢狀if語句 當for語句裡第一個條件不滿足if語句時,則繼續執行for迴圈,若滿足,執行if語句 例: for(i=0;i<=4;i++) for(j=0;j<=4;j++) if(i!=j) { for(k=0;k<

    迴圈如何執行和switch break區別

    for語句裡巢狀if語句 當for語句裡第一個條件不滿足if語句時,則繼續執行for迴圈,若滿足,執行if語句 例: for(i=0;i<=4;i++) for(j=0;j<=4;j++) if(i

    JSP中在JS函式中Java程式碼的執行問題

    function exitSystem() {       var ok = confirm("您確定要退出該系統嗎?");       if(ok){            <%sess

    SQL語句時的執行順序

    1.原語句:SELECT ID,NAME,STATES,(SELECT STATES FROM ITEM  WHERE ID=T.ITEM_ID) AS ITEM_END_FLAGFROM RESULT TWHERE 1=12.ITEM表中實際沒有STATES列,但執行時語句

    跟我一起寫Makefile(10)--- 變數(變數+追加變數+overrid+多行變數+環境變數+目標變數+模式變數)

    使用變數 ———— 在Makefile中的定義的變數,就像是C/C++語言中的巨集一樣,他代表了一個文字字串,在Makefile中執行的時候其會自動原模原樣地展開在所使用的地方。其與C/C++所不同的是,你可以在Makefile中改變其值。在Makefile中,變數可以

    dll引用缺失會造成執行時異常

    在這次例項模仿中,我遇到了一個很煩人的問題,就是我拿到的原始碼能夠正確的執行,得到資料。而我模仿的例項卻在執行中老是會出異常。經過兩三天的糾結,終於在我的一個小demo的測試中發現了這個問題,那就是:有的時候,我們寫程

    如何等到的block執行完後再執行其他程式碼?

    現在有一個單例 Manage.h、Manage.m  裡面有你封裝的一個請求伺服器資料的方法。 例如:-(void)manageRequestWithUrl:(NSURL*)urlName handleBlock(void(^)(NSData *data,NSError*

    shellexpect執行screen命令

    start.sh #!/usr/bin/env bash screen_name1=$"online" screen -dmS $screen_name1 cmd1=$"php pushOnlineN

    makefileshell命令

    參考:http://blog.csdn.net/yusiguyuan/article/details/16951413 在Makefile中寫shell程式碼有點詭異,和不同的shell語法不太一樣,如果不瞭解,看Makefile會莫名其妙。下面總結了一些。 1:盡在Mak

    迴圈裡面一個非同步請求,所有迴圈執行完畢後再返回請求的資料

    場景介紹: 使用es6實現爬蟲,先遍歷完列表,然後迴圈列表的根據url去查詢每篇文章的詳細內容,最後將所有爬取完的資料寫入資料庫(只寫一次,避免資料庫頻繁的開啟的關閉,影響效能)。 原始實現方式和痛點分析: var list =["url1","url2","url3",.

    黑馬程式設計師————java基礎-----for迴圈的執行順序

    ------<a href="http://www.itheima.com" target="blank">Java培訓、Android培訓、iOS培訓、.Net培訓</a>、期待與您交流! ------- 先寫一個for巢狀的小程式,輸出一個矩形,

    (實驗)Java一個執行緒用synchronized鎖多個物件時呼叫wait()只釋放wait函式關聯的所物件還是釋放所有鎖物件

    實驗是在JDK1.8下做的。 題目起的比較拗口,其實用程式碼說明起來更簡單,如下所示: public class MultiSynchronizedTest { private static Object lock1 = new Object(); p

    容器 —— 在 Podman 容器內構建並執行 Buildah

    開發十年,就只剩下這套架構體系了! >>>