讀一下skynet的Makefile

一直對Makefile感到很模糊。今天找到一份很好的資料,耗子叔的《陳皓_Makefile》。大體讀了一遍,爲了實踐,咱們來讀一份Makefile。linux

skynet是雲風團隊的開源服務器框架。skynet的地址git

https://github.com/cloudwu/skynet.git

簡單看下skynet的目錄結構github

首先我想應該對Makefile有個總的認識。算法

什麼是Makefile。macos

簡單的講,它是make命令的規則說明文件。當咱們在一個路徑下,敲make命令的時候,它會去找Makefile文件,按照Makefile裏的規則,來編譯連接生成目標文件。這一般是unix/linux系統的操做,windows咱們有vs。vim

vim Makefile來看一下。第一句就是windows

include platform.mk

做用是引用platform.mk(另外一份Makefile)。因此先來看paltform.mk這份Makefile。比較短,有40行。服務器

PLAT ?= none
PLATS = linux freebsd macosx

CC ?= gcc

.PHONY : none $(PLATS) clean all cleanall

#ifneq ($(PLAT), none)

.PHONY : default

default :
        $(MAKE) $(PLAT)

#endif

none :
        @echo "Please do 'make PLATFORM' where PLATFORM is one of these:"
        @echo "   $(PLATS)"

SKYNET_LIBS := -lpthread -lm
SHARED := -fPIC --shared
EXPORT := -Wl,-E

linux : PLAT = linux
macosx : PLAT = macosx
freebsd : PLAT = freebsd

macosx : SHARED := -fPIC -dynamiclib -Wl,-undefined,dynamic_lookup
macosx : EXPORT :=
macosx linux : SKYNET_LIBS += -ldl
linux freebsd : SKYNET_LIBS += -lrt

# Turn off jemalloc and malloc hook on macosx

macosx : MALLOC_STATICLIB :=
macosx : SKYNET_DEFINES :=-DNOUSE_JEMALLOC

linux macosx freebsd :
        $(MAKE) all PLAT=$@ SKYNET_LIBS="$(SKYNET_LIBS)" SHARED="$(SHARED)" EXPORT="$(EXPORT)" MALLOC_STATICLIB="$(MALLOC_STATICLIB)" SKYNET_DEFINES="$(SKYNET_DEFINES)"

先看前幾行:框架

PLAT ?= none
PLATS = linux freebsd macosx

CC ?= gcc

?=的意思是若是變量沒有,則定義,若是有則用原來的。=天然就是變量定義。PLATS變量就是linux freebsd macosx,可見skynet能夠在這三個平臺編譯。函數

編譯器用的是gcc。

接下來:

.PHONY : none $(PLATS) clean all cleanall

.PHONY是一個固定用法。:後面的表明的是僞目標(標籤)。僞目標是相對於目標而言的。$()則是引用變量。

Makefile的規則。

Makefile的規則簡單來說就是不少依賴關係,依賴關係包括目標文件,和依賴於目標文件的文件(前提)。再加上依賴關係上的命令。

target ... : prerequisites ...

[tab] command : ... 

好比一個Makefile

main : main.o
    cc -o main.o

main.o : main.c
    cc -c main.c

main是最後的目標文件,它依賴於main.o。經過命令cc -o main.o生成main。main.o的依賴同理。

Makefile裏有目標,同時有一種僞目標。它是一般是做爲make後面的參數,好比官方文檔裏編譯skynet的命令是make linux。

那麼Makefile裏就應該有相似這樣的代碼

linux :

  command

更典型的是clean,make clean是經常使用的標籤。

clean :
    rm -rf cur/src

可是這clean有多是一個目標文件,這樣就能夠在僞目標前面加一個.PHONY來顯式說明僞目標。

因此這份Makefile裏有多個僞目標(標籤)none,linux,freebsd,macosx,clean,all,cleanall。

接下來:

#ifneq ($(PLAT), none)

.PHONY : default

default :
        $(MAKE) $(PLAT)

#endif

條件判斷。#ifneq是若是不等於的意思。結合前面一塊兒看,若是$(PLAT)不等於none,意思就是其餘地方定義了PLAT,則定義僞目標default,來執行命令$(MAKE) $(PLAT)。$(MAKE)應該就是make。

接下來是若是輸入命令make none的話,會打印提示:

none :
        @echo "Please do 'make PLATFORM' where PLATFORM is one of these:"
        @echo "   $(PLATS)"

 接下來:

SKYNET_LIBS := -lpthread -lm
SHARED := -fPIC --shared
EXPORT := -Wl,-E

:=是變量賦值,=也是賦值。二者的區別是,使用=變量的推導能夠在後面,這樣可能會出現無限推導,形成報錯。使用:=使得前面的變量不能使用後面的變量。

linux : PLAT = linux
macosx : PLAT = macosx
freebsd : PLAT = freebsd

macosx : SHARED := -fPIC -dynamiclib -Wl,-undefined,dynamic_lookup
macosx : EXPORT :=
macosx linux : SKYNET_LIBS += -ldl
linux freebsd : SKYNET_LIBS += -lrt

其中+=是變量賦值追加的意思。這段另有個疑問。標籤後面的變量賦值語句能夠有多個?我寫一段Makefile驗證:

test : a = first
test : b = second

test :
        echo $(a); echo $(b)

make test試下:

可見a,b變量都賦值了。也就是同一個標籤後的賦值語句能夠有多句。

接下去最後的命令:

linux macosx freebsd :
        $(MAKE) all PLAT=$@ SKYNET_LIBS="$(SKYNET_LIBS)" SHARED="$(SHARED)" EXPORT="$(EXPORT)" MALLOC_STATICLIB="$(MALLOC_STATICLIB)" SKYNET_DEFINES="$(SKYNET_DEFINES)"

$@比較特殊,是一個自動化變量,表明全部目標集。這裏應該是對應相應的plat,linux就是linux,macosx就是macosx。

好了看完了platform.mk。再來看Makefile。

首先一段是關於編譯lua

LUA_CLIB_PATH ?= luaclib
CSERVICE_PATH ?= cservice

SKYNET_BUILD_PATH ?= .

CFLAGS = -g -O2 -Wall -I$(LUA_INC) $(MYCFLAGS)
# CFLAGS += -DUSE_PTHREAD_LOCK

# lua

LUA_STATICLIB := 3rd/lua/liblua.a
LUA_LIB ?= $(LUA_STATICLIB)
LUA_INC ?= 3rd/lua

$(LUA_STATICLIB) :
        cd 3rd/lua && $(MAKE) CC='$(CC) -std=gnu99' $(PLAT)

skynet應該是自帶了一份lua,放在3rd/lua下面,能夠看到目標文件是生成liblua.a。

其中有個$(MYCFLAGS)這個是什麼,我好像讀了上下文沒有發現……??

接下來關於jemalloc的:

# jemalloc 

JEMALLOC_STATICLIB := 3rd/jemalloc/lib/libjemalloc_pic.a
JEMALLOC_INC := 3rd/jemalloc/include/jemalloc

all : jemalloc
        
.PHONY : jemalloc update3rd

MALLOC_STATICLIB := $(JEMALLOC_STATICLIB)

$(JEMALLOC_STATICLIB) : 3rd/jemalloc/Makefile
        cd 3rd/jemalloc && $(MAKE) CC=$(CC) 

3rd/jemalloc/autogen.sh :
        git submodule update --init

3rd/jemalloc/Makefile : | 3rd/jemalloc/autogen.sh
        cd 3rd/jemalloc && ./autogen.sh --with-jemalloc-prefix=je_ --disable-valgrind

jemalloc : $(MALLOC_STATICLIB)

update3rd :
        rm -rf 3rd/jemalloc && git submodule update --init

jemalloc是一種內存分配算法,比c語言的malloc效率高。其實這段我有點凌亂……

jemalloc是其餘開源庫的代碼了,因此會執行git submodule update --init。

其實目標文件是3rd/jemalloc/lib/libjemalloc_pic.a,在3rd/jemalloc下面也有一份Makefile,這裏應該會根據這份Makefile來make。

有一段3rd/jemalloc/Makefile : | 3rd/jemalloc/autogen.sh,其中:|是什麼意思??

整體來講,這段意思是先git submodule update獲得autogen.sh,而後執行這個腳本,產生Makefile,接着make。

看一下autogen.sh:

#!/bin/sh

for i in autoconf; do
    echo "$i"
    $i
    if [ $? -ne 0 ]; then
        echo "Error $? in $i"
        exit 1
    fi
done

echo "./configure --enable-autogen $@"
./configure --enable-autogen $@
if [ $? -ne 0 ]; then
    echo "Error $? in ./configure"
    exit 1
fi

多是autoconf工具產生Makefile的操做了。略過不想。

再來:

all : \
  $(SKYNET_BUILD_PATH)/skynet \
  $(foreach v, $(CSERVICE), $(CSERVICE_PATH)/$(v).so) \
  $(foreach v, $(LUA_CLIB), $(LUA_CLIB_PATH)/$(v).so)

$(SKYNET_BUILD_PATH)/skynet就是./skynet。

foreach的語法是這樣$(foreach <var>,<list>,<text>), 它會從list中挨個取出var,做用於text。好比

names := a b c d  
files := $(foreach n,$(names),$(n).o)

$(file)的值是a.o b.o c.o d.o。

因此all後面其實一大推的目標.so文件。

$(SKYNET_BUILD_PATH)/skynet : $(foreach v, $(SKYNET_SRC), skynet-src/$(v)) $(LUA_LIB) $(MALLOC_STATICLIB)
        $(CC) $(CFLAGS) -o $@ $^ -Iskynet-src -I$(JEMALLOC_INC) $(LDFLAGS) $(EXPORT) $(SKYNET_LIBS) $(SKYNET_DEFINES)

核心目標skynet。依賴於一堆的src文件。下面的$@,$^兩個叫作自動化變量。$@上面講了,$^是全部的依賴集。

define CSERVICE_TEMP
  $$(CSERVICE_PATH)/$(1).so : service-src/service_$(1).c | $$(CSERVICE_PATH)
        $$(CC) $$(CFLAGS) $$(SHARED) $$< -o $$@ -Iskynet-src
endef

define是定義命令包,endif結尾,CSERVICE_TEMP是命令包的名字。$(1)的意思是第一個參數,這裏指的是?多是調用命令的時候傳進來的參數。另外$$的用法是,$$表明真實的$,而不是調用變量。

$(foreach v, $(CSERVICE), $(eval $(call CSERVICE_TEMP,$(v))))

$(LUA_CLIB_PATH)/skynet.so : $(addprefix lualib-src/,$(LUA_CLIB_SKYNET)) | $(LUA_CLIB_PATH)
        $(CC) $(CFLAGS) $(SHARED) $^ -o $@ -Iskynet-src -Iservice-src -Ilualib-src

用到了幾個函數,eval,call和addprefix。call可以建立參數,$(call<exp, param1, param2...>),例如:

reverse = $(1) $(2)  
foo = $(call reverse,a,b) #foo = a b

reverse = $(2) $(1)  
foo = $(call reverse,a,b) #foo = b a

addprefix增長前綴。eval是將後面的參數做爲makefile的一部分解析執行。define,$$,eval常常在一塊兒使用

cleanall: clean
ifneq (,$(wildcard 3rd/jemalloc/Makefile))
        cd 3rd/jemalloc && $(MAKE) clean && rm Makefile
endif
        cd 3rd/lua && $(MAKE) clean
        rm -f $(LUA_STATICLIB)

最後再看一下cleanall裏面有個wildcard函數。在變量的值裏面使用的通配符:

objects = *.o  #object = *.o

objects := $(wildcard *.o) #object = 全部.o文件

 

好了,先讀到這裏了。

 

引用:

1.《陳皓_Makefile》

相關文章
相關標籤/搜索