使用Go Makefile编译项目

GUN make文档:

https://www.gnu.org/software/make/manual/make.html

以前使用make编译C++的项目是比较常见的场景,如今go也可以使用make啦。

为什么要引入make?

当我们写完一个go项目后,可以使用go build来将项目编译为可执行文件,如下例:

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信息所需的变量:

package vars

// global vars with default values
var (
	AppName    = "myapp"
	AppVersion = "unknown"
	GoVersion  = "unknown"
	BuildTime  = "unknown"
	GitCommit  = "unknown"
)

这些变量会在打印version信息、引用bin文件名称的逻辑中使用。

那我们可以编写一个如下的Makefile:

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。

    $(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开始的信息
    
  • 变量赋值的小知识:

    # 变量赋值可以用=或者:=,两者区别如下:
    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项,此项用于在编译之前替换源代码中的一些变量的值,如:

BUILD_LDFLAGS = -X '${VARS_PKG}.AppVersion=$(shell git describe)'

表示将myapp/vars包中的AppVersion变量的值替换为shell指令获得的结果,这样就可以实现在makefile中指定编译的app版本了。

最终使用本文的Makefile可编译出类似如下的结果:

# ./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

 

posted @ 2022-07-29 15:40  realcp1018  阅读(1085)  评论(0编辑  收藏  举报