使用 Golang 已經(jīng)有一陣了鹿寨,在 Golang 的開發(fā)過程中新博,我已經(jīng)習(xí)慣于不斷重復(fù)地手動(dòng)執(zhí)行 go build
和 go test
這兩個(gè)命令. 不過,現(xiàn)在我已經(jīng)擺脫了這個(gè)習(xí)慣脚草。如果只用到了不帶參數(shù)的簡(jiǎn)單命令赫悄,直接這么操作可能并不可怕。但是在一些復(fù)雜的任務(wù)中馏慨,如果依舊是手動(dòng)執(zhí)行 go build
和 go test
埂淮,就可能會(huì)成為一個(gè)讓人頭疼的事情。
我們可以通過其他方式解決這個(gè)問題写隶。比如倔撞,可以用一個(gè) bash 腳本來完成這些工作,或者一個(gè)更好的選擇(至少對(duì)于我來說)是慕趴,寫一個(gè) makefile. make 這個(gè)工具生來就是為了做這些事情痪蝇,在 makefile 中我們可以將所有常見的任務(wù)都放在一起。我并不是一個(gè) makefile 專家冕房,所以可能不太能夠教大家如何寫一個(gè)好的 makefile. 但是在本文躏啰,我將向大家展示我所使用的 Makefile,我的大部分項(xiàng)目都使用了這些 makefile 耙册。讓我們開始吧:
# Go parameters
GOCMD=go
GOBUILD=$(GOCMD) build
GOCLEAN=$(GOCMD) clean
GOTEST=$(GOCMD) test
GOGET=$(GOCMD) get
BINARY_NAME=mybinary
BINARY_UNIX=$(BINARY_NAME)_unix
all: test build
build:
$(GOBUILD) -o $(BINARY_NAME) -v
test:
$(GOTEST) -v ./...
clean:
$(GOCLEAN)
rm -f $(BINARY_NAME)
rm -f $(BINARY_UNIX)
run:
$(GOBUILD) -o $(BINARY_NAME) -v ./...
./$(BINARY_NAME)
deps:
$(GOGET) github.com/markbates/goth
$(GOGET) github.com/markbates/pop
# Cross compilation
build-linux:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GOBUILD) -o $(BINARY_UNIX) -v
docker-build:
docker run --rm -it -v "$(GOPATH)":/go -w /go/src/bitbucket.org/rsohlich/makepost golang:latest go build -o "$(BINARY_UNIX)" -v
我比較喜歡 DRY(Don't Repeat Yourself) 原則给僵。所以,在 makefile
的開頭定義常用的命令和變量详拙,我們可以在后面方便地對(duì)定義的命令和變量進(jìn)行引用帝际。
# Basic go commands
GOCMD=go
GOBUILD=$(GOCMD) build
GOCLEAN=$(GOCMD) clean
GOTEST=$(GOCMD) test
GOGET=$(GOCMD) get
# Binary names
BINARY_NAME=mybinary
BINARY_UNIX=$(BINARY_NAME)_unix
在 :
前面的叫做 makefile 的目標(biāo),比如 build:
, build
就是一個(gè)目標(biāo)溪厘。如果在執(zhí)行 make
命令時(shí)指定目標(biāo)胡本,比如 make run
,那么 make 就會(huì)構(gòu)建該目標(biāo)畸悬。如果沒有提供任何參數(shù)侧甫,那么 make 默認(rèn)會(huì)執(zhí)行第一個(gè)目標(biāo)珊佣。在我們的示例中,也就是叫 all
的目標(biāo)會(huì)被構(gòu)建披粟。
$ make run ## call specific task
$ make ## make tool calls "all" task
基本命令
makefile 最關(guān)鍵的部分就是構(gòu)建咒锻。當(dāng) make 進(jìn)行執(zhí)行時(shí),定義的變量會(huì)被展開守屉,$(GOBUILD)
會(huì)被展開為 go build
, make 實(shí)際就會(huì)執(zhí)行 go build
命令惑艇。生成的二進(jìn)制文件被命名為 -o $(BINARY_NAME)
. 另外,我發(fā)現(xiàn)使用 -v
參數(shù)切換到 verbose mode 非常有用拇泛。在 verbose mode 中滨巴,你可以看到當(dāng)前正在構(gòu)建的包。
build:
$(GOBUILD) -o $(BINARY_NAME) -v ## expands to: "go build -o mybinary -v"
因?yàn)槲覀兇蟛糠秩硕己軕邪嘲龋跃陀辛艘粋€(gè)叫做 run
的目標(biāo)恭取。run
會(huì)構(gòu)建二進(jìn)制文件,并且在 build 完成后執(zhí)行這個(gè)二進(jìn)制文件熄守。
run:
$(GOBUILD) -o $(BINARY_NAME) -v ./...
./$(BINARY_NAME)
通常來講蜈垮,test
命令應(yīng)該是 makefile 的一部分。我個(gè)人總是喜歡使用 verbose mode 來更好地 debug 和觀測(cè) test 的運(yùn)行裕照。
test:
$(GOTEST) -v ./...
如果項(xiàng)目使用 CI(Continuous Integration)/CD(Continuous Delivery), 哪怕僅僅是為了一致性攒发,將一系列依賴維護(hù)在包里面也是一個(gè)非常好的做法。這可以通過 deps
目標(biāo)來完成晋南,它會(huì)通過 go get
命令獲取所有相關(guān)的依賴惠猿。
deps:
$(GOGET) github.com/markbates/goth
$(GOGET) github.com/markbates/pop
用 clean
來結(jié)束這一節(jié)的內(nèi)容。rm -f
命令被用來移除名為
$(BINARY_XXX)
的二進(jìn)制文件负间。
clean:
$(GOCLEAN)
rm -f $(BINARY_NAME)
rm -f $(BINARY_UNIX)
交叉編譯命令
如果項(xiàng)目開發(fā)是在一個(gè)系統(tǒng)上紊扬,而需要在另一個(gè)系統(tǒng)上運(yùn)行,那么在 makefile 中包含一個(gè)交叉編譯的命令是非常方便的唉擂。我通常在容器的 Linux 平臺(tái)上運(yùn)行二進(jìn)制餐屎,所以 makefile 包含了 Linux 構(gòu)建。
build-linux:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GOBUILD) -o $(BINARY_UNIX) -v
如果你的代碼使用了 C binding玩祟,你可能會(huì)遇到一些問題腹缩。CGO 的問題在于你需要一個(gè)與給定平臺(tái)兼容的 gcc. 如果開發(fā)在 OSX/Windows 上完成,那么你需要有一個(gè)能夠兼容 Linux 的 gcc. 至少對(duì)我來說空扎,在 OSX 上使用配置 gcc 交叉編譯 C 代碼并不容易藏鹊。如果需要 CGO, docker 鏡像是創(chuàng)建 Linux 構(gòu)建的最好方式。這種方式唯一的要求就是必須安裝 Docker转锈。
docker-build:
docker run --rm -it -v "$(GOPATH)":/go -w /go/src/bitbucket.org/rsohlich/makepost golang:latest go build -o "$(BINARY_UNIX)" -v
本文的 Makefile 示例可在 這里 找到盘寡。
譯者:原文使用的 Makefile 其實(shí)還可以更好,比如在原文下面的評(píng)論中指出撮慨,至少應(yīng)該指明 .PHONY:
, 另外 build
應(yīng)該是 run
的前提條件竿痰。不過脆粥,我們可以學(xué)習(xí)其中可取的部分。