使用Go Makefile编译项目
GUN make文档:
https://www.gnu.org/software/make/manual/make.html
以前使用make编译C++的项目是比较常见的场景,如今go也可以使用make啦。
为什么要引入make?
当我们写完一个go项目后,可以使用go build来将项目编译为可执行文件,如下例:
1 | go build -o main main.go |
上述操作生成一个可执行的文件: main。
go build当然很简单也很好用,但我们在日常开发中经常会有以下需求:
1.项目需要一个版本号,直接把版本号硬编码到源码里是不是不太好,万一build时忘了改,生产的版本号不就和项目的tag版本号不一致了
2.我们想要每一个版本的bin文件都能包含诸如:编译时的go版本信息、git commit信息等等,这些也全部硬编码到源码里就很麻烦也很不灵活了
3.每个版本我们都需要编译出适合windows和linux版本运行的bin文件,在同一平台编译的话需要设置GOOS/GOARCH的值,很烦
4.编译出来的bin文件名可以使用go build -o选项自由定义,但我们还想让help信息里的bin文件名称随编译名称而变
等等等等.......
上述需求基本都可以通过go build -o或-ldflags来实现,但每次编译都需要多次手动设置这些配置项就很烦了。
为此Makefile出现了,可以把它看做一个简化版的shell脚本或者ansible playbook文件,里边包含了编译项目所需的全部信息。
假设我们在myapp项目中定义了一个如下源码文件用于存储help和version信息所需的变量:
1 2 3 4 5 6 7 8 9 10 | package vars // global vars with default values var ( AppName = "myapp" AppVersion = "unknown" GoVersion = "unknown" BuildTime = "unknown" GitCommit = "unknown" ) |
这些变量会在打印version信息、引用bin文件名称的逻辑中使用。
那我们可以编写一个如下的Makefile:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | BINARY_NAME = myapp VARS_PKG = myapp/vars BUILD_LDFLAGS = -X '${VARS_PKG}.AppVersion=$(shell git describe)' BUILD_LDFLAGS += -X '${VARS_PKG}.GoVersion=$(shell go version)' BUILD_LDFLAGS += -X '${VARS_PKG}.BuildTime=$(shell date +"%Y-%m-%d %H:%M:%S")' BUILD_LDFLAGS += -X '${VARS_PKG}.GitCommit=$(shell git rev-parse HEAD)' build: $(info ...Build Start!) GOARCH=amd64 GOOS=linux go build -ldflags= "${BUILD_LDFLAGS}" -o ${BINARY_NAME} main.go run: @./${BINARY_NAME} version all: clean build run clean: $(info ...Clean Start!) @go clean @rm -f ${BINARY_NAME} |
一些解释和需要注意的知识点:
-
上述文件中的build\clean等被称作target,target后可以接其他target表示依赖于其他target(Dependencies),例如all依赖其他3个target,具体的指令如go clean称作target的Recipe。
-
当执行make不带参数时,默认只执行第一个target。
-
Recipe缩进必须使用tab,请勿使用空格,多个tab缩进会当做一个处理。
-
命令之前的@表示make时不会打印这句命令本身,可以减少输出信息。
-
不建议使用windows环境下的make,linux环境下使用shell编写更简单。
-
输出日志可以使用如下方式,error会终止make。
1234$(info your_text) : Information. This doesn't stop the execution.
$(warning your_text) : Warning. This shows the text
as
a warning.
$(error your_text) : Fatal Error. This will stop the execution.
# 实测日志打印会先于Recipe执行,因此暂不能用于标识target结束,应当只用于标识target开始的信息
-
变量赋值的小知识:
1234567891011121314151617# 变量赋值可以用=或者:=,两者区别如下:
x = foo
y = $(x) bar
x = later
all:
echo $(y)
> later bar
x := foo
y := $(x) bar
x := later
all:
echo $(y)
> foo bar
-
关于go build ldflags选项
上述makefile中使用了go build -ldflags项,此项用于在编译之前替换源代码中的一些变量的值,如:
1 | BUILD_LDFLAGS = -X '${VARS_PKG}.AppVersion=$(shell git describe)' |
表示将myapp/vars包中的AppVersion变量的值替换为shell指令获得的结果,这样就可以实现在makefile中指定编译的app版本了。
最终使用本文的Makefile可编译出类似如下的结果:
1 2 3 4 5 | # ./myapp version AppVersion: v1.0.0-21-gcfd7d78 Go Version: go version go1.15.2 linux/amd64 Build Time: 2022-06-29 10:07:52 Git Commit: cfd7d7874232572fba88c997f875a7757cd5cc03 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix