1. 程式人生 > >從頭開始寫專案Makefile(五):巢狀執行(轉)

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

【版權宣告:轉載請保留出處:blog.csdn.net/gentleliu。Mail:shallnew at 163 dot com】

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

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

.

├── include
│   ├── common.h
│   ├── ipc
│   │   └── ipc.h
│   └── tools
│       ├── base64.h
│       ├── md5.h
│       └── tools.h
├── Makefile
├── src
│   ├── ipc
│   │   ├── inc
│   │   ├── Makefile
│   │   └── src
│   │       └── ipc.c
│   ├── main
│   │   ├── inc
│   │   ├── Makefile
│   │   └── src
│   │       ├── main.c
│   │       └── main.c~
│   └── tools
│       ├── inc
│       ├── Makefile
│       └── src
│           ├── base64.c
│           ├── md5.c
│           └── tools.c
└── tags

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

# top Makefile for xxx

all :
>---$(MAKE) -C src

tags:
>---ctags -R

clean :
>---$(MAKE) -C src clean

.PHONY : all clean tags


命令:

>---$(MAKE) -C src

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

# Top Makefile for C program

# Copyright (C) 2014 shallnew \at 163 \dot com

all :
>---$(MAKE) -C src/ipc
>---$(MAKE) -C src/tools
>---$(MAKE) -C src/main

tags:
>---ctags -R

help:
>[email protected] "===============A common Makefilefor c programs=============="
>[email protected] "Copyright (C) 2014 liuy0711 \at 163\dot com"
>[email protected] "The following targets aresupport:"
>[email protected]
>[email protected] " all              - (==make) compile and link"
>[email protected] " obj              - just compile, withoutlink"
>[email protected] " clean            - clean target"
>[email protected] " distclean        - clean target and otherinformation"
>[email protected] " tags             - create ctags for vimeditor"
>[email protected] " help             - print help information"
>[email protected]
>[email protected] "To make a target, do 'make[target]'"
>[email protected] "========================= Version2.0 ======================="

obj:
>---$(MAKE) -C src/ipc obj
>---$(MAKE) -C src/tools obj
>---$(MAKE) -C src/main obj

clean :
>---$(MAKE) -C src/ipc clean
>---$(MAKE) -C src/tools clean
>---$(MAKE) -C src/main clean

distclean:
>---$(MAKE) -C src/ipc distclean
>---$(MAKE) -C src/tools distclean
>---$(MAKE) -C src/main distclean

.PHONY : all clean distclean tags help
當我們這樣組織原始碼時,最下面層次的Makefile怎麼寫呢?肯定不可以將我們上一節的Makefile(version 1.1)直接拷貝到功能模組目錄下,需要稍作修改。不能所有的模組都最終生成各自的可執行檔案吧,我們目前是一個工程,所以最後只會生成一個可執行程式。我們這樣做,讓主模組目錄生成可執行檔案,其他模組目錄生成靜態庫檔案,主模組連結時要用其他模組編譯產生的庫檔案來生成最終的程式。將上一節Makefile稍作修改得出編譯庫檔案Makefile和編譯可執行檔案Makefile分別如下:
# A Makefile to generate archive file
# Copyright (C) 2014 shallnew \at 163 \dot com


CFLAGS += -g -Wall -Werror -O2
CPPFLAGS += -I. -I./inc -I../../include

# SRC_OBJ = $(patsubst %.c, %.o, $(wildcard *.c))
SRC_FILES = $(wildcard src/*.c)
SRC_OBJ = $(SRC_FILES:.c=.o)
SRC_LIB = libtools.a

all : $(SRC_LIB)

$(SRC_LIB) : $(SRC_OBJ)
>---$(AR) rcs [email protected] $^
>---cp [email protected] ../../libs

obj : $(SRC_OBJ)

# clean target
clean:
>---$(RM) $(SRC_OBJ) $(SRC_LIB)

distclean:
>---$(RM) $(SRC_OBJ) $(SRC_LIB) tags *~

.PHONY : all obj clean disclean
==========================================================================
# A Makefile to generate executive file                                                                                                                                                   
# Copyright (C) 2014 shallnew \at 163 \dot com

CFLAGS += -g -Wall -Werror -O2
CPPFLAGS += -I. -I./inc -I../../include
LDFLAGS += -lpthread -L../../libs -ltools -lipc


# SRC_OBJ = $(patsubst %.c, %.o, $(wildcard *.c))
SRC_FILES = $(wildcard src/*.c)
SRC_OBJ = $(SRC_FILES:.c=.o)  
SRC_BIN = target_bin          

all : $(SRC_BIN)

$(SRC_BIN) : $(SRC_OBJ)       
>---$(CC) -o [email protected] $^ $(LDFLAGS) 

obj : $(SRC_OBJ)

# clean target
clean:
>---$(RM) $(SRC_OBJ) $(SRC_BIN) $(SRC_BIN).exe

distclean:
>---$(RM) $(SRC_OBJ) $(SRC_BIN) $(SRC_BIN).exe tags*~

.PHONY : all obj clean disclean
最後在頂層執行:

# make clean

make -C src/ipc clean
make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/ipc'
rm -f src/ipc.o libipc.a
make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/ipc'
make -C src/tools clean
make[1]: Entering directory `/home/Myprojects/example_make/version-3.0/src/tools'
rm -f src/base64.o src/md5.o src/tools.o libtools.a
make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/tools'
make -C src/main clean
make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/main'
rm -f src/main.o target_bin target_bin.exe
make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/main'
# make
make -C src/ipc
make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/ipc'
cc -g -Wall -Werror -O2 -I. -I./inc-I../../include  -c -o src/ipc.osrc/ipc.c
ar rcs libipc.a src/ipc.o
cp libipc.a ../../libs
make[1]: Leaving directory `/home/Myprojects/example_make/version-3.0/src/ipc'
make -C src/tools
make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/tools'
cc -g -Wall -Werror -O2 -I. -I./inc-I../../include  -c -o src/base64.osrc/base64.c
cc -g -Wall -Werror -O2 -I. -I./inc -I../../include  -c -o src/md5.o src/md5.c
cc -g -Wall -Werror -O2 -I. -I./inc-I../../include  -c -o src/tools.osrc/tools.c
ar rcs libtools.a src/base64.o src/md5.o src/tools.o
cp libtools.a ../../libs
make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/tools'
make -C src/main
make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/main'
cc -g -Wall -Werror -O2 -I. -I./inc-I../../include  -c -o src/main.osrc/main.c
cc -o target_bin src/main.o -lpthread -L../../libs -ltools-lipc
make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/main'
#

最後生成了可執行程式檔案。這樣的話一個工程的各個模組就變得獨立出來了,不但原始碼分開了,而且各自有各自的Makefile,並且各個功能模組是可獨立編譯的。
我們發現頂層Makefile還有可以改進的地方,就是在進入下一層目錄是要重複寫多次,如下:

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

DIR = src
SUBDIRS = $(shell ls $(DIR))

all :
>[email protected] subdir in $(SUBDIRS); \
>---do $(MAKE) -C $(DIR)/$$subdir; \                                                                                                                                           
>---done
這樣懶人有可以高興很久了。不過還有問題:

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

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

 

DIR = src
SUBDIRS = $(shell ls $(DIR))
 
all :
>[email protected] subdir in $(SUBDIRS); \
>---do $(MAKE) -C $(DIR)/$$subdir || exit 1; \                                                                                                                                           
>---done
這樣在執行出錯時立馬退出,但這樣還是沒有解決問題,編譯錯誤還是會出現。那怎麼解決呢?

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

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

# Top Makefile for C program
# Copyright (C) 2014 shallnew \at 163 \dot com

DIR = src
MODULES = $(shell ls $(DIR))
# MODULES = ipc main tools

all : $(MODULES)
 
$(MODULES):
>---$(MAKE) -C $(DIR)/[email protected]
 
main:tools ipc
 
obj:
>[email protected] subdir in $(MODULES); \
>---do $(MAKE) -C $(DIR)/$$subdir [email protected]; \
>---done
 
clean :
>[email protected] subdir in $(MODULES); \
>---do $(MAKE) -C $(DIR)/$$subdir [email protected]; \
>---done
 
distclean:
>[email protected] subdir in $(MODULES); \
>---do $(MAKE) -C $(DIR)/$$subdir [email protected]; \
>---done
 
 
tags:
>---ctags -R

help:
>[email protected] "===============A common Makefilefor c programs=============="
>[email protected] "Copyright (C) 2014 liuy0711 \at 163\dot com"
>[email protected] "The following targets aresupport:"
>[email protected]
>[email protected] " all              - (==make) compile and link"
>[email protected] " obj              - just compile, withoutlink"
>[email protected] " clean            - clean target"
>[email protected] " distclean        - clean target and otherinformation"
>[email protected] " tags             - create ctags for vimeditor"
>[email protected] " help             - print help information"
>[email protected]
>[email protected] "To make a target, do 'make[target]'"
>[email protected] "========================= Version2.0 ======================="
 
.PHONY : all clean distclean tags help
--------------------- 
作者:shallnet 
來源:CSDN 
原文:https://blog.csdn.net/shallnet/article/details/37595465 
版權宣告:本文為博主原創文章,轉載請附上博文連結!