溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Redis中Makefile文件是什么

發(fā)布時間:2022-03-11 09:37:09 來源:億速云 閱讀:122 作者:小新 欄目:關(guān)系型數(shù)據(jù)庫

這篇文章給大家分享的是有關(guān)Redis中Makefile文件是什么的內(nèi)容。小編覺得挺實(shí)用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

1、前言

這里使用的源碼版本是redis-6.2.1的。

2、Makefile文件詳解

源代碼根目錄的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

3、src/Makefile文件詳解

該文件是真正起編譯作用的文件,內(nèi)容比較多,比較雜,而且為了兼容多種編譯器里面有不少分支選擇語法,我們這里只以Linux下的gcc編譯器為例去講解,其余的沒區(qū)別,就是通過判斷語句去改變某些編譯參數(shù)而已

3.1、Makefile.dep目標(biāo)

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ǔ)

  • Makefilefindstring函數(shù)的使用格式為$(findstring FIND, IN),表示在IN中查找FIND,如果查找到了就返回FIND,找不到就返回空

  • Makefilewords函數(shù)表示統(tǒng)計單詞數(shù)目,例如$(words, foo bar)的返回值為"2"

  • MakefileMAKECMDGOALS變量表示傳入的參數(shù)(全部)

  • MakefileCC默認(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

3.2、通用的生成目標(biāo)文件的target

代碼如下:

.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文件

3.3、all目標(biāo)所依賴的各個子目標(biāo)的名稱設(shè)置

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)

3.4、all目標(biāo)所依賴的各個子目標(biāo)的內(nèi)容

  • 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)

3.4.1、REDIS_SERVER_NAME目標(biāo)

該目標(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

3.4.2、REDIS_SENTINEL_NAME目標(biāo)

可以看到REDIS_SENTINEL_NAME目標(biāo)很簡單,只是簡單地使用install命令復(fù)制了REDIS_SERVER_NAME目標(biāo)生成的那個文件,即redis-server,從這里可以知道哨兵服務(wù)redis-sentinelRedis服務(wù)使用的是同一套代碼

3.4.3、REDIS_CHECK_RDB_NAME目標(biāo)

和前面的如出一轍,也是簡單復(fù)制了redis-server文件到redis-check-rdb文件去

3.4.4、REDIS_CHECK_AOF_NAME目標(biāo)

和前面的如出一轍,也是簡單復(fù)制了redis-server文件到redis-check-aof文件去

3.4.5、REDIS_CLI_NAME目標(biāo)

這個就不是簡單復(fù)制了,而是使用和REDIS_SERVER_NAME目標(biāo)相同的方法進(jìn)行直接編譯的,唯一的區(qū)別是REDIS_SERVER_NAME鏈接了…/deps/lua/src/liblua.a,而REDIS_CLI_NAME鏈接的是…/deps/linenoise/linenoise.o

3.4.6、REDIS_BENCHMARK_NAME目標(biā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

3.5、all目標(biā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 ""

3.6、安裝和卸載Redis的目標(biāo)

3.6.1、安裝Redis的目標(biāo)

這里邏輯很簡單,先創(chuàng)建一個用于存放Redis可執(zhí)行文件的文件夾(默認(rèn)是/usr/local/bin),然后將REDIS_SERVER_NAMEREDIS_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)

3.6.2、卸載Redis的目標(biāo)

這里就是刪除前面復(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)}

3.7、clean和distclean目標(biāo)

所有Makefileclean或者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

3.8、test目標(biāo)

執(zhí)行完Redis編譯之后,會有一段提示文字我們可以運(yùn)行make test測試功能是否正常,從代碼中我們可以看出其實(shí)不止一個test目標(biāo),還有另一個test-sentinel目標(biāo),這個是測試哨兵服務(wù)的。這兩個目標(biāo)分別運(yùn)行了根目錄的runtestruntest-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)

4、總結(jié)

本文詳細(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é)到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細(xì)節(jié)

免責(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)容。

AI