您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關(guān)Redis中Makefile文件是什么的內(nèi)容。小編覺得挺實(shí)用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
這里使用的源碼版本是redis-6.2.1
的。
源代碼根目錄的Makefile
文件內(nèi)容如下:
default: all .DEFAULT: cd src && $(MAKE) $@install: cd src && $(MAKE) $@.PHONY: install
從代碼中可以看出以下幾點(diǎn)信息:
該文件的第一個目標(biāo)是default
,該目標(biāo)沒有實(shí)際作用,依賴于all
目標(biāo)
代碼中并沒有所謂的all
目標(biāo),所以當(dāng)我們直接使用make
時,首先會調(diào)用default
目標(biāo),然后調(diào)用all
目標(biāo),由于all
目標(biāo)不存在,所以會調(diào)用.DEFAULT
目標(biāo)來替代,在Makefile的執(zhí)行語句中,$@
代表的就是目標(biāo)的意思,$(MAKE)
代表的就是make
,所以展開之后的代碼如下,讀者可以自行編譯一下,看看第一條輸出語句是否與我們分析的相同
cd src && make all
install目標(biāo)和前面的類似,最終也是進(jìn)去src/
目錄,然后調(diào)用該目錄下的Makefile
文件,區(qū)別只在于此時調(diào)用的目標(biāo)變成了install
而已,展開后的代碼如下:
cd src && make install
當(dāng)傳入?yún)?shù)是其他是,調(diào)用的都會轉(zhuǎn)到.DEFAULT
去,然后去調(diào)用子目錄下的Makefile
的對應(yīng)的目標(biāo),以clean
為例,代碼如下:
cd src && make clean
該文件是真正起編譯作用的文件,內(nèi)容比較多,比較雜,而且為了兼容多種編譯器里面有不少分支選擇語法,我們這里只以Linux
下的gcc
編譯器為例去講解,其余的沒區(qū)別,就是通過判斷語句去改變某些編譯參數(shù)而已
Makefile
在執(zhí)行對應(yīng)的目標(biāo)之前,會先把非目標(biāo)的指令給執(zhí)行了,比如變量賦值、Shell
語句等等,所以我們會發(fā)現(xiàn),Makefile
文件并不會完全按照順序去執(zhí)行的
相關(guān)代碼如下:
NODEPS:=clean distclean# FINAL_CFLAGS里的各個變量原型STD=-pedantic -DREDIS_STATIC=''WARN=-Wall -W -Wno-missing-field-initializers OPTIMIZATION?=-O2 OPT=$(OPTIMIZATION)DEBUG=-g -ggdb#CFLAGS 根據(jù)條件選擇的,不重要的參數(shù),忽略#REDIS_CFLAGS 根據(jù)條件選擇的,不重要的參數(shù),忽略FINAL_CFLAGS=$(STD) $(WARN) $(OPT) $(DEBUG) $(CFLAGS) $(REDIS_CFLAGS)REDIS_CC=$(QUIET_CC)$(CC) $(FINAL_CFLAGS)all: $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) @echo "" @echo "Hint: It's a good idea to run 'make test' ;)" @echo ""Makefile.dep: -$(REDIS_CC) -MM *.c > Makefile.dep 2> /dev/null || trueifeq (0, $(words $(findstring $(MAKECMDGOALS), $(NODEPS))))-include Makefile.dep endif
首先先補(bǔ)充以下幾點(diǎn)Makefile
的基礎(chǔ)
Makefile
的findstring
函數(shù)的使用格式為$(findstring FIND, IN)
,表示在IN
中查找FIND
,如果查找到了就返回FIND
,找不到就返回空
Makefile
的words
函數(shù)表示統(tǒng)計單詞數(shù)目,例如$(words, foo bar)
的返回值為"2"
Makefile
的MAKECMDGOALS
變量表示傳入的參數(shù)(全部)
Makefile
的CC
默認(rèn)值是cc
Makefile
的-MM
是輸出一個用于make
的規(guī)則,該規(guī)則描述了源文件的依賴關(guān)系,但是不包含系統(tǒng)頭文件
則可以總結(jié)出以下幾點(diǎn)信息:
里面的all
目標(biāo)正是我們前一節(jié)說到的那個默認(rèn)的編譯目標(biāo),但是我們可以自己試著去編譯一下,會發(fā)現(xiàn)先生成的是Makefile.dep
文件,因?yàn)樗葓?zhí)行了最下面那個判斷語句,里面調(diào)用了Makefile.dep
目標(biāo)
由于此時MAKECMDGOALS
的值為all
,不在NODEPS
范圍里,所以上面那個ifeq
語句成立,會調(diào)用Makefile.dep
目標(biāo)
REDIS_CC
的值由三個變量組成,QUIET_CC
是打印調(diào)試信息的,讀者可以自己去源碼看相關(guān)內(nèi)容,這部分不重要,我們忽略,CC
的值代表的是編譯器,FINAL_CFLAGS
里面的值則是編譯的一些參數(shù),這些值在上面的代碼中都已經(jīng)摘錄出來了
綜上所述Makefile.dep
目標(biāo)的作用就是生成當(dāng)前目錄下所有以.c
結(jié)尾的文件的依賴關(guān)系,并寫入Makefile.dep
文件中,編譯之后生成的文件內(nèi)容如下所示,看起來挺亂,但是里面的內(nèi)容其實(shí)將每個源文件最終生成的目標(biāo)文件給列出來,并且將它需要的依賴列出來而已
acl.o: acl.c server.h fmacros.h config.h solarisfixes.h rio.h sds.h \ connection.h atomicvar.h ../deps/lua/src/lua.h ../deps/lua/src/luaconf.h \ ae.h monotonic.h dict.h mt19937-64.h adlist.h zmalloc.h anet.h ziplist.h \ intset.h version.h util.h latency.h sparkline.h quicklist.h rax.h \ redismodule.h zipmap.h sha1.h endianconv.h crc64.h stream.h listpack.h \ rdb.h sha256.h adlist.o: adlist.c adlist.h zmalloc.h ae.o: ae.c ae.h monotonic.h fmacros.h anet.h zmalloc.h config.h \ ae_epoll.c ae_epoll.o: ae_epoll.c... zipmap.o: zipmap.c zmalloc.h endianconv.h config.h zmalloc.o: zmalloc.c config.h zmalloc.h atomicvar.h
代碼如下:
.make-prerequisites: @touch $@ifneq ($(strip $(PREV_FINAL_CFLAGS)), $(strip $(FINAL_CFLAGS))).make-prerequisites: persist-settings endif ifneq ($(strip $(PREV_FINAL_LDFLAGS)), $(strip $(FINAL_LDFLAGS))).make-prerequisites: persist-settings endif %.o: %.c .make-prerequisites $(REDIS_CC) -MMD -o $@ -c $<
以下是對這部分代碼的解析:
這部分是通用的根據(jù)源文件生成目標(biāo)文件的target
,Makefile
中%
表示通配符,所以只要符合格式要求的都可以借助這段代碼來生成對應(yīng)的目標(biāo)文件
.make-prerequisites
沒啥用忽略,而REDIS_CC
的值在上一小節(jié)有說明了,是用于編譯文件的指令
gcc
的-MMD
參數(shù)與前面說的那個-MM
是基本一致的,只不過這個會將輸出內(nèi)容導(dǎo)入到對應(yīng)的%.d
文件中
Makefile
中$@
表示目標(biāo),$<
表示第一個依賴,$^
表示全部依賴
綜上,這個target
的作用是依賴于一個源文件,然后根據(jù)這個源文件生成對應(yīng)的目標(biāo)文件,并且將依賴關(guān)系導(dǎo)入到對應(yīng)的%.d
文件中
下面是一個簡單的例子:
# 假設(shè)生成的目標(biāo)文件為acl.o,則代入可得acl.o: acl.c .make-prerequisites $(REDIS_CC) -MMD -o acl.o -c acl.c # 執(zhí)行完成后在該目錄下會生成一個acl.o文件和acl.d文件
PROG_SUFFIX
的值默認(rèn)為空,可以忽略。這里設(shè)置的六個目標(biāo)名都是會被all
這個目標(biāo)引用的,從名字可以看出這六個目標(biāo)是對應(yīng)著Redis
不同的功能,依次是服務(wù)、哨兵、客戶端、基礎(chǔ)檢測、rdf持久化以及aof持久化。
代碼如下:
REDIS_SERVER_NAME=redis-server$(PROG_SUFFIX) REDIS_SENTINEL_NAME=redis-sentinel$(PROG_SUFFIX) REDIS_CLI_NAME=redis-cli$(PROG_SUFFIX) REDIS_BENCHMARK_NAME=redis-benchmark$(PROG_SUFFIX) REDIS_CHECK_RDB_NAME=redis-check-rdb$(PROG_SUFFIX) REDIS_CHECK_AOF_NAME=redis-check-aof$(PROG_SUFFIX)
REDIS_LD
也是一個編譯指令,和前面那個REDIS_CC
有點(diǎn)像,只不過這個指定了另外的一些編譯參數(shù),比如設(shè)置了某些依賴的動態(tài)庫、靜態(tài)庫的路徑,讀者有興趣的話可以去看一下代碼,看看REDIS_LD
的詳細(xì)內(nèi)容
FINAL_LIBS
是一系列動態(tài)庫鏈接參數(shù),讀者有興趣可以自行去Makefile
里面查看該變量的內(nèi)容,限于篇幅原因這里就不展開講了
將QUIET_INSTALL
忽略(這個是自定義打印編譯信息的),可以看出REDIS_INSTALL
的值其實(shí)就是install
,Linux
下的install
命令是用于安裝或升級軟件或備份數(shù)據(jù)的,這個命令與cp
類似,但是install
允許你控制目標(biāo)文件的屬性,這里不作深入分析了,有興趣的讀者可以自行查閱相關(guān)的介紹install
命令的文章?;居梅椋?code>install src des,表示將src
文件復(fù)制到des
文件去
代碼如下:
REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crcspeed.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o lolwut6.o acl.o gopher.o tracking.o connection.o tls.o sha256.o timeout.o setcpuaffinity.o monotonic.o mt19937-64.o REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o zmalloc.o release.o ae.o crcspeed.o crc64.o siphash.o crc16.o monotonic.o cli_common.o mt19937-64.o REDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o adlist.o dict.o zmalloc.o release.o crcspeed.o crc64.o siphash.o crc16.o monotonic.o cli_common.o mt19937-64.o DEP = $(REDIS_SERVER_OBJ:%.o=%.d) $(REDIS_CLI_OBJ:%.o=%.d) $(REDIS_BENCHMARK_OBJ:%.o=%.d)-include $(DEP)INSTALL=install REDIS_INSTALL=$(QUIET_INSTALL)$(INSTALL)# redis-server$(REDIS_SERVER_NAME): $(REDIS_SERVER_OBJ) $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/lua/src/liblua.a $(FINAL_LIBS)# redis-sentinel$(REDIS_SENTINEL_NAME): $(REDIS_SERVER_NAME) $(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME)# redis-check-rdb$(REDIS_CHECK_RDB_NAME): $(REDIS_SERVER_NAME) $(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_CHECK_RDB_NAME)# redis-check-aof$(REDIS_CHECK_AOF_NAME): $(REDIS_SERVER_NAME) $(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_CHECK_AOF_NAME)# redis-cli$(REDIS_CLI_NAME): $(REDIS_CLI_OBJ) $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(FINAL_LIBS)# redis-benchmark$(REDIS_BENCHMARK_NAME): $(REDIS_BENCHMARK_OBJ) $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/hdr_histogram/hdr_histogram.o $(FINAL_LIBS)
該目標(biāo)依賴于REDIS_SERVER_OBJ
,而REDIS_SERVER_OBJ
的內(nèi)容都是一些目標(biāo)文件(上面代碼有給出),這些目標(biāo)文件最終都會通過3.2小節(jié)
介紹的那個target
來生成??梢钥吹?code>REDIS_SERVER_NAME這個target
需要使用REDIS_SERVER_OBJ
、…/deps/hiredis/libhiredis.a
、…/deps/lua/src/liblua.a
以及FINAL_LIBS
這些來編譯鏈接生成最終的目標(biāo)文件,即redis-server
可以看到REDIS_SENTINEL_NAME
目標(biāo)很簡單,只是簡單地使用install
命令復(fù)制了REDIS_SERVER_NAME
目標(biāo)生成的那個文件,即redis-server
,從這里可以知道哨兵服務(wù)redis-sentinel
與Redis
服務(wù)使用的是同一套代碼
和前面的如出一轍,也是簡單復(fù)制了redis-server
文件到redis-check-rdb
文件去
和前面的如出一轍,也是簡單復(fù)制了redis-server
文件到redis-check-aof
文件去
這個就不是簡單復(fù)制了,而是使用和REDIS_SERVER_NAME
目標(biāo)相同的方法進(jìn)行直接編譯的,唯一的區(qū)別是REDIS_SERVER_NAME
鏈接了…/deps/lua/src/liblua.a
,而REDIS_CLI_NAME
鏈接的是…/deps/linenoise/linenoise.o
這個也是使用和REDIS_SERVER_NAME
目標(biāo)相同的方法進(jìn)行直接編譯的,唯一的區(qū)別是REDIS_SERVER_NAME
鏈接了…/deps/lua/src/liblua.a
,而REDIS_BENCHMARK_NAME
鏈接的是…/deps/hdr_histogram/hdr_histogram.o
經(jīng)過前面的介紹,all
目標(biāo)的作用也就一目了然了,最終會生成六個可執(zhí)行文件,以及輸出相應(yīng)的調(diào)試信息
代碼如下:
all: $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) @echo "" @echo "Hint: It's a good idea to run 'make test' ;)" @echo ""
這里邏輯很簡單,先創(chuàng)建一個用于存放Redis
可執(zhí)行文件的文件夾(默認(rèn)是/usr/local/bin
),然后將REDIS_SERVER_NAME
、REDIS_BENCHMARK_NAME
、REDIS_CLI_NAME
對應(yīng)的可執(zhí)行文件復(fù)制到/usr/local/bin
中去,這里可以看到前面那幾個照葫蘆畫瓢的文件并沒有復(fù)制過去,而是直接通過創(chuàng)建軟連接的方式去生成對應(yīng)的可執(zhí)行文件(內(nèi)容相同,復(fù)制過去浪費(fèi)空間)
代碼如下:
PREFIX?=/usr/local INSTALL_BIN=$(PREFIX)/bin install: all @mkdir -p $(INSTALL_BIN) $(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(INSTALL_BIN) $(REDIS_INSTALL) $(REDIS_BENCHMARK_NAME) $(INSTALL_BIN) $(REDIS_INSTALL) $(REDIS_CLI_NAME) $(INSTALL_BIN) @ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_CHECK_RDB_NAME) @ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_CHECK_AOF_NAME) @ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_SENTINEL_NAME)
這里就是刪除前面復(fù)制的那些文件了,比較簡單,就不細(xì)講了
代碼如下:
uninstall: rm -f $(INSTALL_BIN)/{$(REDIS_SERVER_NAME),$(REDIS_BENCHMARK_NAME),$(REDIS_CLI_NAME),$(REDIS_CHECK_RDB_NAME),$(REDIS_CHECK_AOF_NAME),$(REDIS_SENTINEL_NAME)}
所有Makefile
的clean
或者distclean
目標(biāo)的作用都是大致相同的,就是刪除編譯過程中產(chǎn)生的那些中間文件,以及最終編譯生成的動態(tài)庫、靜態(tài)庫、可執(zhí)行文件等等內(nèi)容,代碼比較簡單,就不作過多的分析了
代碼如下:
clean: rm -rf $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) *.o *.gcda *.gcno *.gcov redis.info lcov-html Makefile.dep dict-benchmark rm -f $(DEP).PHONY: clean distclean: clean -(cd ../deps && $(MAKE) distclean) -(rm -f .make-*).PHONY: distclean
執(zhí)行完Redis
編譯之后,會有一段提示文字我們可以運(yùn)行make test
測試功能是否正常,從代碼中我們可以看出其實(shí)不止一個test
目標(biāo),還有另一個test-sentinel
目標(biāo),這個是測試哨兵服務(wù)的。這兩個目標(biāo)分別運(yùn)行了根目錄的runtest
和runtest-sentinel
文件,這兩個是腳本文件,里面會繼續(xù)調(diào)用其他腳本來完成整個功能的測試,并輸出測試信息到控制臺。具體怎么測試的就不分析了,大家有興趣的可以去看一下。
代碼如下:
test: $(REDIS_SERVER_NAME) $(REDIS_CHECK_AOF_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) @(cd ..; ./runtest)test-sentinel: $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) @(cd ..; ./runtest-sentinel)
本文詳細(xì)地分析了與Redis
編譯相關(guān)的Makefile
文件,通過學(xué)習(xí)Makefile
文件里的內(nèi)容,我們可以更為全面地了解Redis
的編譯過程,因?yàn)?code>Makefile文件中將很多編譯命令用@
給取消顯示了,轉(zhuǎn)而使用它自己特制的編譯信息輸出給我們看,代碼如下:
ifndef V QUIET_CC = @printf ' %b %b\n' $(CCCOLOR)CC$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR) 1>&2; QUIET_LINK = @printf ' %b %b\n' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1>&2; QUIET_INSTALL = @printf ' %b %b\n' $(LINKCOLOR)INSTALL$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1>&2; endif
所以我們直接去編譯的話很多細(xì)節(jié)會看不到,可以自己嘗試修改Makefile
文件,在前面這段代碼之前定義V
變量,這樣就可以看到完整的編譯信息了。修改如下:
V = 'good' ifndef V QUIET_CC = @printf ' %b %b\n' $(CCCOLOR)CC$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR) 1>&2; QUIET_LINK = @printf ' %b %b\n' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1>&2; QUIET_INSTALL = @printf ' %b %b\n' $(LINKCOLOR)INSTALL$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1>&2; endif
本人之前也寫過Nginx
編譯相關(guān)的文章,下面總結(jié)兩者的幾點(diǎn)區(qū)別:
Nginx
使用了大量的Shell
相關(guān)的技術(shù),而Redis
則很少使用這些
Nginx
跨平臺的相關(guān)參數(shù)是通過配置腳本進(jìn)行配置的,而Redis
則是直接在Makefile
文件中將這件事給做了,這兩者沒有什么優(yōu)劣之分,Nginx
主要是為了可擴(kuò)展性強(qiáng)才使用那么多配置腳本的,而Redis
基本不用考慮這些,所以簡單一點(diǎn)實(shí)現(xiàn)就行了
由于Redis
將其一些邏輯都放在了Makefile
文件中了,所以看起來Nginx
最終生成的Makefile
文件要比Redis
簡單易懂很多(Nginx
復(fù)雜邏輯在那些配置腳本里)
Nginx
生成的配置文件足有1000多行,代碼量比Redis
的400多行要大很多,因?yàn)?code>Nginx把全部依賴的生成方式全部列舉了出來,而Redis
借助了Makefile.dep
、各種%.d
文件來將依賴信息分散到中間文件中去,極大地減少了Makefile
的代碼量
感謝各位的閱讀!關(guān)于“Redis中Makefile文件是什么”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。