go generate令——在编译前自动化生成某类代码

7个月前 (04-27)
go generate令是在Go语言 1.4 版本里面新添加的一个令,当运行该令时,它将扫描与当前包相关的源代码文件,找出所有包含//go:generate的特殊注释,提取并执行该特殊注释后面的令。

使用go generate令时有以下几点需要注意:
  • 该特殊注释必须在 .go 源码文件中;

  • 每个源码文件可以包含多个 generate 特殊注释;

  • 运行go generate令时,才会执行特殊注释后面的令;
  • go generate令执行出错时,将终止程序的运行;
  • 特殊注释必须以//go:generate开头,双斜线后面没有空格。


在下面这些场景下,我们会使用go generate令:
  • yacc:从 .y 文件生成 .go 文件;

  • protobufs:从 protocol buffer 定义文件(.proto)生成 .pb.go 文件;

  • Unicode:从 UnicodeData.txt 生成 Unicode 表;

  • HTML:将 HTML 文件嵌入到 go 源码;

  • bindata:将形如 JPEG 这样的文件转成 go 代码中的字节数组。


再比如:
  • string 方法:为类似枚举常量这样的类型生成 String() 方法;

  • 宏:为既定的泛型包生成特定的实现,比如用于 ints 的 sort.Ints。


go generate令格式如下所示:

go generate [-run regexp] [-n] [-v] [-x] [command] [build flags] [file.go... | packages]

参数说明如下:

  • -run 正则表达式匹配令行,仅执行匹配的令;

  • -v 输出被处理的包名和源文件名;

  • -n 显示不执行令;

  • -x 显示并执行令;

  • command 可以是在环境变量 PATH 中的任何令。


执行go generate令时,也可以使用一些环境变量,如下所示:
  • $GOARCH 体系架构(arm、amd64 等);

  • $GOOS 当前的 OS 环境(linux、windows 等);

  • $GOFILE 当前处理中的文件名;

  • $GOLINE 当前令在文件中的行号;

  • $GOPACKAGE 当前处理文件的包名;

  • $DOLLAR 固定的$,不清楚具体用途。


【示例 1】假设我们有一个 main.go 文件,内容如下:

package main

import "fmt"

//go:generate go run main.go

//go:generate go version

func main() {

fmt.Println("http://c.biancheng网站站点" rel="nofollow" /> go generate -x令,输出结果如下:

go generate -x
go run main.go
http://c.biancheng网站站点" rel="nofollow" /> //go:generate之后的令成功运行了,令中使用的-x参数是为了将执行的具体令同时打印出来。

下面通过 stringer 工具来演示一下go generate令的使用。

stringer 并不是Go语言自带的工具,需要手动安装。我们可以通过下面的令来安装 stringer 工具。

go get golang网站站点" rel="nofollow" /> git clone https://githu网站站点" rel="nofollow" />

//go:generate stringer -type=Pill

package painkiller

type Pill int

const (

Placebo Pill = iota

Aspirin

Ibuprofen

Paracetamol

Acetaminophen = Paracetamol

)

然后,在 painkiller.go 文件所在的目录下运行go generate令。

执行成功后没有任何提示信息,但会在当前目录下面生成一个 pill_string.go 文件,文件中实现了我们需要的 String() 方法,文件内容如下:

// Code generated by "stringer -type=Pill"; DO NOT EDIT.

package painkiller

import "strconv"

func _() {

// An "invalid array index" compiler error signifies that the constant values have changed.

// Re-run the stringer command to generate them again.

var x [1]struct{}

_ = x[Placebo-0]

_ = x[Aspirin-1]

_ = x[Ibuprofen-2]

_ = x[Paracetamol-3]

}

const _Pill_name = "PlaceboAspirinIbuprofenParacetamol"

var _Pill_index = [...]uint8{0, 7, 14, 23, 34}

func (i Pill) String() string {

if i < 0 || i >= Pill(len(_Pill_index)-1) {

return "Pill(" + strconv.FormatInt(int64(i), 10) + ")"

}

return _Pill_name[_Pill_index[i]:_Pill_index[i+1]]

}