這幾天查看了一下unp
的源代碼,發(fā)現(xiàn)makefile
的書(shū)寫(xiě)真是一門(mén)學(xué)問(wèn),通過(guò)查看unp
的makefile
如何書(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
文件,我們查看一下該文件下有什么:
恰好是我們的
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
一下:
程序沒(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
程序呢?
很有趣是吧,居然先鏈接在前面的,其實(shí)這與
gcc
的編譯方式有關(guān),感興趣的可以去看一看csapp
,這一點(diǎn)在unp
的makefile
文件中用的很多。
如何加強(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(下)