一個適用於層級目錄結構的makefile模版

今天寫了個層次化的Makefile模版,用來自動化編譯項目,這個模版應當包含如下功能:shell

  • 適用於層次化結構,Makefile主要內容都放在頂層目錄下的Makefile.env中,子層Makefile包含這個Makefile.env,只要增長一些變量就能夠編譯,特別方便添加新的功能模塊
  • 自動解析頭文件依賴

個人程序的目錄結構是這樣的:spa

1. 源文件目錄src,模塊xxx放在src/xxx下,主程序在src/main下面code

2.公共頭文件放在include目錄下,模塊xxx的頭文件放在include/xxx目錄下blog

3.模塊輸出的連接庫放在lib目錄下進程

4.可執行文件放在bin目錄下it

先來看一下Makefile.env,這個相似於c的頭文件,包含了全部Makefile的公共部分,自動化

###########  MakeFile.env  ##########
# Top level pattern, include by Makefile of child directory
# in which variable like TOPDIR, TARGET or LIB may be needed

CC=gcc
MAKE=make

AR=ar cr
RM = -rm -rf

CFLAGS+=-Wall

dirs:=$(shell find . -maxdepth 1 -type d)
dirs:=$(basename $(patsubst ./%,%,$(dirs)))
dirs:=$(filter-out $(exclude_dirs),$(dirs))
SUBDIRS := $(dirs)

SRCS=$(wildcard *.c)
OBJS=$(SRCS:%.c=%.o)
DEPENDS=$(SRCS:%.c=%.d)


all:$(TARGET)  $(LIB) subdirs

$(LIB):$(OBJS) 
    $(AR)  $@  $^
    cp $@ $(LIBPATH) 

subdirs:$(SUBDIRS)
    for dir in $(SUBDIRS);\
    do $(MAKE) -C $$dir all||exit 1;\
    done

$(TARGET):$(OBJS)
    $(CC) -o $@ $^ $(LDFLAGS)
    cp $@ $(EXEPATH)


$(OBJS):%.o:%.c
    $(CC) -c $< -o $@ $(CFLAGS)


-include $(DEPENDS)

$(DEPENDS):%.d:%.c
    set -e; rm -f $@; \
    $(CC) -MM $(CFLAGS) $< > $@.$$$$; \
    sed 's,\($*\)\.o[:]*,\1.o $@:,g' < $@.$$$$ > $@; \
    rm $@.$$$$

clean:
    for dir in $(SUBDIRS);\
    do $(MAKE) -C $$dir clean||exit 1;\
    done
    $(RM) $(TARGET) $(LIB)  $(OBJS) $(DEPENDS)

當前目錄下的子目錄是經過shell命令自動獲得的,subdirs:$(SUBDIRS) 這塊會進入每一個子目錄執行make,固然有些子目錄並不須要編譯,能夠經過exclude_dirs指定,好比頂層目錄的exclude_dirs=bin lib include。編譯

$(DEPENDS):%.d:%.c 這塊做用是自動生成頭文件依賴,這部分包括5條命令,看起來很複雜,其實原理很簡單,假設main.c,包含頭文件depend.h, 解析過程以下:class

1. @set –e 命令設置當前Shell進程狀態爲:若是執行的任何一條命令的退出狀態非零則馬上終止當前進程。變量

2. rm -f $@ 刪除原來的main.d文件

3. gcc的-MM參數可以生成文件的依賴關係main.o:main.c depend.h,寫入文件main.d. $$$$,$$是進程號

4. sed命令做用是將main.o:main.c depend.h替換成main.o main.d:main.c depend.h, 並寫入main.d文件

5. rm -f $@.$$$$刪除臨時文件

Include $(SRCS:.c=.d)將main.d包含進來後,Makefile增長了如下依賴

main.o main.d:main.c depend.h

不論是main.c仍是depend.h的變化都會更新main.o 以及main.d,main.d的更新又反過來更新上面這條依賴關係。

這條依賴下面並無對應的命令,爲何會更新目標文件呢?這跟Makefile的運行步驟有關係,引用下陳浩先生的《跟我一塊兒寫Makefile》

GNU的 make 工做時的執行步驟以下:

一、讀入全部的 Makefile。

二、讀入被 include 的其它 Makefile。

三、初始化文件中的變量。

四、推導隱晦規則,並分析全部規則。

五、爲全部的目標文件建立依賴關係鏈。

六、根據依賴關係,決定哪些目標要從新生成。

七、執行生成命令。

因此1-5 步爲第一個階段,造成了全部的依賴關係鏈,6-7 爲第二個階段,決定了全部須要生成的目標文件後,執行對應的命令。上面的依賴關係雖然沒有命令,可是肯定了main.o要從新生成,就會找到如下編譯模塊生成目標文件

$(OBJS):%.o:%.c
    $(CC) -c $< -o $@ $(CFLAGS)

假設有一個模塊first,源文件都放在src/first下,Makefile以下

TOPDIR=./../..

LIB=libfirst.a

INCPATH=$(TOPDIR)/include/first
LIBPATH=$(TOPDIR)/lib
CFLAGS= -I$(INCPATH)

include $(TOPDIR)/Makefile.env

TOPDIR是相對於頂層目錄的相對路徑,LIB是要生成的連接庫,這樣只要幾行命令就能夠完成當前模塊的編譯了,並且first下面還能夠添加子模塊。

假設main.c在src/main目錄下,調用了first模塊,Makefile以下

TOPDIR=./../..

TARGET=main

LIBPATH=$(TOPDIR)/lib
EXEPATH=$(TOPDIR)/bin

CFLAGS= -I$(TOPDIR)/include/first 
LDFLAGS= -lfirst

include $(TOPDIR)/Makefile.env

TARGET是生成的可執行文件名,在LIBPATH目錄下尋找連接庫,生成的可執行文件會被mv到EXEPATH目錄下

src下沒有源文件,只有目錄,因此Makefile很是簡單

TOPDIR=./..

include $(TOPDIR)/Makefile.env

頂層目錄下的Makefile也很簡單,相對增長了exclude_dirs,排除不須要編譯的目錄

TOPDIR=.

exclude_dirs= include  bin  lib

include $(TOPDIR)/Makefile.env

如今只須要在頂層目錄下make一下,src下全部目錄都會編譯,生成的連接庫放在lib下,可執行文件在bin目錄中。若是要增長新的功能模塊,只要在src/目錄下新建目錄,增長一個相似first下的Makefile便可,是否是很方便?

相關文章
相關標籤/搜索