Makefile的在工程中的一般性應(yīng)用

這幾天查看了一下unp的源代碼,發(fā)現(xiàn)makefile的書(shū)寫(xiě)真是一門(mén)學(xué)問(wèn),通過(guò)查看unpmakefile如何書(shū)寫(xiě),本人從中間學(xué)到了很多東西。


一般的做法

這里,我已自己的一個(gè)demo為例子,簡(jiǎn)單地記錄一下在大型的工程中如何使用makefile文件。

現(xiàn)在假設(shè)我有一個(gè)demo的項(xiàng)目,為了分類(lèi)文件,我在demo下面建立了一個(gè)bin文件夾,用來(lái)放置本工程經(jīng)常用到的庫(kù)源文件,然后是一個(gè)bin文件夾,這個(gè)文件夾里面才是我們的main程序所在的目錄。

---demo----lib
       |---bin

一般而言,我們?cè)?code>demo目錄下設(shè)定一些常用的參數(shù),在demo目錄下,我們建立一個(gè)Make.defines文件,文件內(nèi)容如下:

# 使用的是gcc編譯器
CC=gcc
# -I 選項(xiàng)告訴編譯器查找文件中所需要的.h文件請(qǐng)到../lib目錄下面去找
CFLAGS=-g -Wall -I../lib
# 這里主要是為了方便,將lib下的.o文件壓成了.a文件
LIBS=../lib.a

現(xiàn)在文件結(jié)構(gòu)如下:

---demo----lib
       |---bin
       |---Make.defines

我們一般先寫(xiě)幾個(gè)lib文件,讓函數(shù)調(diào)用,好了,我們?cè)?code>lib目錄下寫(xiě)幾個(gè)文件吧!

---demo----lib----lib.h
       |      |---lib.c
       |      |---Makefile
       |---bin
       |---Make.defines

下面是lib.h文件:

int add();

下面是lib.c文件:

#include <stdio.h>

int add()
{
    printf("我們現(xiàn)在在lib中調(diào)用add\n");
    return 0;
}

很簡(jiǎn)單的代碼,現(xiàn)在我們要將編譯這些lib,順便在該目錄下建立一個(gè)Makefile文件:

# 添加上級(jí)目錄下面的Make.defines文件,主要用到她里面的一些變量
include ../Make.defines
LIB_OBJS=lib.o

all:    ${LIB_OBJS}
    # 下面的命令主要是用于打包,將LIB_OBJS所代表的文件打包至LIBS(../lib.a)
    ar rcs ${LIBS} ${LIB_OBJS}
# 將所有的.c文件編譯成.o文件
%.o:%.c
    $(CC) $(CFLAGS) -c $< -o $@

然后執(zhí)行make命令,我們會(huì)發(fā)現(xiàn)上級(jí)目錄下多了一個(gè)lib.a文件,我們查看一下該文件下有什么:

這里寫(xiě)圖片描述

恰好是我們的lib.o文件。
現(xiàn)在的目錄結(jié)構(gòu)變成了下面的樣子:

---demo----lib----lib.h
       |      |---lib.c
       |      |---Makefile
       |---bin
       |---Make.defines
       |---lib.a

然后我們到bin目錄下建立我們的主程序,文件結(jié)構(gòu)如下:

---demo----lib----lib.h
       |      |---lib.c
       |      |---Makefile
       |---bin----demo.c
       |      |---Makefile
       |---Make.defines
       |---lib.a

下面是demo.c文件:

#include <stdio.h>
#include "lib.h"

int main()
{
    add();
    return 0;
}

然后是Makefile文件:

# 同上面,加載Make.defines文件,該文件記錄了各個(gè)目錄下都要用到的一次額公用變量,如CC,CFLAGS等。
include ../Make.defines
# 程序的名稱(chēng)
PROGS=demo
# all是偽目標(biāo)
all:$(PROGS)
# *.o依賴(lài)于*.c,將*.c-->*.o,這里的*的代表的東西是一致的
%.o:    %.c
    $(CC) $(CFLAGS) -c $< -o $@
# 下面指示如何生成demo程序
demo:   demo.o $(LIBS)
    $(CC) $(CFLAGS) -o $@ demo.o $(LIBS)
clean:
    rm -rf *.o ${PROGS}

然后make一下,程序便生成成功。
我們運(yùn)行demo一下:

這里寫(xiě)圖片描述

程序沒(méi)有問(wèn)題。

差不多這就是我從unp的源代碼中學(xué)到的如何在一個(gè)很大型的工程中應(yīng)用makefile文件的例子啦,高手莫見(jiàn)笑,這么干有什么優(yōu)點(diǎn)呢?

  • lib文件夾下的文件出現(xiàn)了變動(dòng),我們只需要在lib文件下下make一下,這樣,上級(jí)目錄下的lib.a文件就會(huì)更新,也就導(dǎo)致了依賴(lài)于該文件的的代碼也更新。

  • makefile并非寫(xiě)在一個(gè)文件里面,而是分文件夾書(shū)寫(xiě),更加有序,更加簡(jiǎn)潔。

  • 一些共用的變量可以放在一個(gè)類(lèi)似Make.defines的文件里面,像c語(yǔ)言調(diào)用庫(kù)一樣調(diào)用,這樣大大減少了書(shū)寫(xiě)量。

暫時(shí)能想到的就這么多吧。


腦洞大開(kāi)

如果我們?cè)赽in目錄下新建一個(gè)new_lib.c:

/*
 * /demo/bin/new_lib.c
 */
#include <stdio.h>

int add()
{
    printf("我正在新的lib中調(diào)用add函數(shù)\n");
    return 0;
}

然后修改一下Makefile文件:

include ../Make.defines

PROGS=demo

all:$(PROGS)

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

demo:   demo.o new_lib.o $(LIBS)
    $(CC) $(CFLAGS) -o $@ demo.o new_lib.o $(LIBS)
clean:
    rm -rf *.o ${PROGS}

需要注意的一點(diǎn)是:lib.o中有int add()函數(shù),new_lib.o中也有int add()函數(shù),那么gcc究竟會(huì)鏈接哪一個(gè)文件中的add程序呢?

這里寫(xiě)圖片描述

很有趣是吧,居然先鏈接在前面的,其實(shí)這與gcc的編譯方式有關(guān),感興趣的可以去看一看csapp,這一點(diǎn)在unpmakefile文件中用的很多。


如何加強(qiáng)?

我們來(lái)看一看lib目錄下的makefile:

include ../Make.defines
LIB_OBJS=lib.o lib1.o

${LIBS}:    ${LIB_OBJS}
    ar rcs ${LIBS} $?

# all:  ${LIB_OBJS}
#   ar rcs ${LIBS} $?
# 如果將上面的兩句換成注釋里的兩句,效果是相同的,但是效率是不同的
# all是偽目標(biāo),這意味著,all是一定會(huì)被執(zhí)行的,這就導(dǎo)致效率底下,不論變沒(méi)變,都會(huì)被更新
# 但是改用${LIBS}之后,效率絕對(duì)變高了,因?yàn)長(zhǎng)IB_OBJS里面的文件沒(méi)有變化的話(huà),LIBS是不會(huì)被創(chuàng)建的

# 即使沒(méi)有下面的語(yǔ)句,依然會(huì)有從.c文件生成.o文件,這是因?yàn)殡[含規(guī)則的緣故
# make的隱含規(guī)則是,將.o的目標(biāo)依賴(lài)文件置成.c文件,并使用 ${CC} -c ${CFLAGS} [.c]來(lái)生成.o文件
# %.o:%.c
#   $(CC) $(CFLAGS) -c $< -o $@

為了方便,我們?cè)?code>demo目錄下加一個(gè)總控的makefile,只要執(zhí)行這一個(gè)makefile,就可以實(shí)現(xiàn)整個(gè)項(xiàng)目的編譯,很方便。

SUBDIRS =bin lib # 兩個(gè)子目錄,一個(gè)bin,一個(gè)lib
.PHONY: subdirs ${SUBDIRS} # 兩個(gè)目標(biāo)都是偽目標(biāo)
subdirs:    ${SUBDIRS}
${SUBDIRS}:
    ${MAKE} -C $@ # 進(jìn)入子目錄下執(zhí)行make命令
bin:    lib # 依賴(lài),表示在進(jìn)入bin之前應(yīng)該先進(jìn)入lib目錄

此時(shí)文件的結(jié)構(gòu)如下:

---demo----lib----lib.h
       |      |---lib.c
       |      |---Makefile
       |---bin----demo.c
       |      |---Makefile
       |---Make.defines
       |---lib.a
       |---Makefile

差不多就是這樣啦,以后有新的發(fā)現(xiàn)再來(lái)補(bǔ)坑。

一個(gè)makefile模版

最近自己寫(xiě)了一個(gè)makefile模版,以后要編譯很小程序的話(huà),直接調(diào)用這個(gè)模版就可以了.

CC  := g++
CXXFLAGS:= -w -std=c++11 -c # 編譯的一些參數(shù)
LFLAGS  := -lpthread  # 這里放入要鏈接的庫(kù)的名稱(chēng)
BINS    := web_regular # 程序名,是main函數(shù)所在文件的名稱(chēng)(不要后綴)
SRCS    := $(wildcard *.cpp) # 當(dāng)前目錄下的所有的.cpp文件 
OBJS    := $(SRCS:.cpp=.o) # 將所有的.cc文件名替換為.o

.PHONY: all clean  # 表示all和clean是兩個(gè)偽目標(biāo)

all:$(BINS) # make每次編譯的時(shí)候總是認(rèn)為偽目標(biāo)改變了,會(huì)重新編譯

BINOS   = $(addsuffix .o, $(BINS))
TEMP_OBJ= $(filter-out $(BINOS), $^)

$(BINS):$(OBJS) 
    @echo "正在鏈接程序......"; \
    $(foreach BIN, $@, $(CC) $(TEMP_OBJ) $(BIN).o $(LFLAGS) -o $(BIN));   

%.d:%.cpp
    @echo "正在生成依賴(lài)中......"; \
    rm -f $@; \
    $(CC) -MM $< > $@.$$$$; \
    sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
    rm -f $@.$$$$

-include $(SRCS:.cpp=.d)

clean:
    rm -f *.o *.d
    rm -f $(BINS)

# makefile說(shuō)白了就是拼湊字符串

Makefile基礎(chǔ)

跟我一起寫(xiě)makefile!
Makefile偽目標(biāo)
深入學(xué)習(xí)Make命令和Makefile(上)
深入學(xué)習(xí)Make命令和Makefile(下)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容