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
Last change: 2023-11-03, commit: 5e87542