Golang备忘录
Makefile
基础数据
# 从Git获取最新tag,或者从环境变量CI_COMMIT_TAG里面拿
VERSION:=$(shell git describe --tags 2>/dev/null || echo $${CI_COMMIT_TAG:-"unknown"})
# 从Git获取当前提交ID,取前8位
COMMIT:=$(shell git rev-parse HEAD 2>/dev/null | head -c8)
# RFC3339Nano格式的编译时间
MAKEDATE:=$(shell date '+%FT%T%:z')
# 从go.mod里面提取项目名,作为编译文件名
FILENAME:=$(shell head -1 go.mod | awk -F '[/ ]' '{print $$NF}' | cut -d. -f1)
# 定义输出目录(加上 "_" 避免 go 扫描)
DISTDIR:=_dist
# 定义最终输出路径
BIN_FILE:=${DISTDIR}/${FILENAME}
# 编译命令
BUILD_CMD:=go build -trimpath -ldflags "-s -w -X main.version=${VERSION} -X main.buildDate=${MAKEDATE} -X main.commit=${COMMIT}"
# 入口文件或文件夹
MAIN:=./cmd
# 关闭CGO
export CGO_ENABLED := 0
测试
# 查找所有有单元测试用例的模块
TESTLIST:=$(shell find pkg -name '*_test.go' | xargs grep -E '^func Test' | cut -d: -f1 | xargs dirname | sort | uniq | cut -d/ -f2- | tr / .)
# 查找所有有性能测试用例的模块
BENCHLIST:=$(shell find pkg -name '*_test.go' | xargs grep -E '^func Benchmark' | cut -d: -f1 | xargs dirname | sort | uniq | cut -d/ -f2- | tr / .)
# 定义测试不使用缓存
ifdef TEST_VERBOSE
TESTAVGS:=${TESTAVGS} -count=1 -v
endif
# 定义 make test 为执行所有单元测试用例
.PHONY: test
test: SHELL:=/bin/bash -o pipefail
test:
$(eval fullTests := $(addsuffix ", $(addprefix "./pkg/, $(foreach dir,$(TESTLIST),$(shell echo "$(dir)" | tr . /)))))
go test -timeout 30s ${TESTAVGS} ${fullTests}
# 定义 make test.* 为执行单元测试用例
.PHONY: test.%
test.%: SHELL:=/bin/bash -o pipefail
test.%:
$(eval testModule := $(shell echo "$*" | tr . /))
go test -timeout 30s ${TESTAVGS} "./pkg/${testModule}"
# 定义 make bench 为执行所有性能测试用例
.PHONY: bench
bench:
$(eval fullBenchs := $(addsuffix ", $(addprefix "./pkg/, $(foreach dir,$(BENCHLIST),$(shell echo "$(dir)" | tr . /)))))
go test -run '^$$' -bench=. -benchtime=5s -benchmem -timeout 30s -cpu 1 ${TESTAVGS} ${fullBenchs}
# 定义 make bench.* 为执行性能测试用例
.PHONY: bench.%
bench.%:
$(eval testModule := $(shell echo "$*" | tr . /))
go test -run '^$$' -bench=. -benchtime=5s -benchmem -timeout 30s -cpu 1 "./pkg/${testModule}" \
-cpuprofile="$*.cpu.prof" -memprofile="$*.mem.prof" -mutexprofile="$*.lock.prof"
编译
编译这里不依赖 deps 和 test 是为了快,跑CI的时候需要加上
- make deps
- make test
# 默认命令:根据编译当前系统当前架构的版本,不添加 系统-架构 后缀
.PHONY: dist
dist:
$(BUILD_CMD) -o $(BIN_FILE) $(MAIN)
# 获取支持的所有系统和架构,排除掉不想要的,作为编译目标
DISTLIST:=$(shell go tool dist list | grep -E '^(darwin|linux|windows)/' | grep -E '64|390' | sed 's~/~.~g')
# 从 系统.架构 列表中提取支持的系统(用于创建make入口)
OSLIST:=$(shell for t in ${DISTLIST}; do echo $${t}; done | cut -d. -f1 | sort | uniq)
# 定义make all操作为:make 所有系统
.PHONY: all
all: $(OSLIST)
# 定义 make 系统 操作为 dist.系统.所有架构
define OS_template
.PHONY: ${1}
${1}: $(addprefix dist., $(shell for t in ${DISTLIST}; do echo $${t}; done | grep ${1}))
endef
# 渲染所有 make dist.系统.架构
$(foreach os,$(OSLIST),$(eval $(call OS_template,$(os))))
# 定义 make dist.*,从输入中获取 GOOS=系统 和 GOARCH=架构。
# 且当 GOOS == windows 时,定义后缀为 .exe
.PHONY: dist.%
dist.%:
$(eval GOOS := $(word 1,$(subst ., ,$*)))
$(eval GOARCH := $(word 2,$(subst ., ,$*)))
@sh -c '[ "$(GOOS)" = "windows" ] && EXT=.exe; export GOOS=$(GOOS) GOARCH=$(GOARCH); set -x; \
$(BUILD_CMD) -o $(BIN_FILE)-$(GOOS)-$(GOARCH)$${EXT} $(MAIN)'
# 定义 make deps.* 为执行 go mod 的操作
.PHONY: deps.%
deps.%:
go mod $(word 1,$(subst ., ,$*))
# 定义 make deps 为执行依赖检查、校验、下载
deps: deps.tidy deps.verify deps.download
# 定义 make clean 为清理目录和编译缓存
clean:
-rm -rf -- ${DISTDIR}
go tool dist clean
# # 如果你想清理掉所有缓存
# go clean -cache