go generate介绍

介绍

go generate命令是go 1.4版本里面新添加的一个命令,当运行go generate时,它将扫描与当前包相关的源代码文件,找出所有包含"//go:generate"的特殊注释,提取并执行该特殊注释后面的命令,命令为可执行程序,形同shell下面执行。

有几点需要注意:

  • 该特殊注释必须在.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] [build flags] [file.go... | packages]

其中:

  • -run 正则表达式匹配命令行,仅执行匹配的命令
  • -v 输出被处理的包名和源文件名
  • -n 显示不执行命令
  • -x 显示并执行命令

执行go generate时,有一些环境变量可以使用:

$GOARCH
    体系架构 (arm、amd64等待)
$GOOS
    OS环境(linux、windows等)
$GOFILE
    当前处理中的文件名
$GOLINE
    当前命令在文件中的行号
$GOPACKAGE
    当前处理文件的包名
$DOLLAR
    固定的"$",不清楚用途

假设我们有个main.go文件,内容如下:

package main

import "fmt"

//go:generate echo hello
//go:generate go run main.go
//go:generate  echo file=$GOFILE pkg=$GOPACKAGE
func main() {
    fmt.Println("main func")
}

执行“go generate”后,输出如下:

$ go generate
hello
main func
file=main.go pkg=main

示例

现在我们来实践一下前面介绍的go generate。

String()方法

假设我们有一些代码,里面包含若干定义为Pill的整型常量:

package painkiller

type Pill int

const (
    Placebo Pill = iota
    Aspirin
    Ibuprofen
    Paracetamol
    Acetaminophen = Paracetamol
)

为了调试的需要,我们会为这些常量定义String()签名方法:

func (p Pill) String() string

一般情况下,我们可能会像下面这样写:

func (p Pill) String() string {
    switch p {
    case Placebo:
        return "Placebo"
    case Aspirin:
        return "Aspirin"
    case Ibuprofen:
        return "Ibuprofen"
    case Paracetamol: // == Acetaminophen
        return "Paracetamol"
    }
    return fmt.Sprintf("Pill(%d)", p)
}

这里,我们可以用go generate来实现String():

  • 首先,我这里创建一个painkiller.go文件,包含如下内容:
//go:generate stringer -type=Pill
package painkiller

type Pill int

const (
    Placebo Pill = iota
    Aspirin
    Ibuprofen
    Paracetamol
    Acetaminophen = Paracetamol
)

在文件的开头包含了一个"//go:generate stringer -type=Pill"特殊注释,其中stringer是个生成String方法的工具,为了使用stringer方法,在运行"go generate"命令前,我们需要安装stringer工具,命令如下:

$ go get golang.org/x/tools/cmd/stringer
  • 然后,在painkiller.go所在的目录下面运行"go generate"命令:
$ go generate

我们会发现当前目录下面生成一个pill_string.go文件,里面实现了我们需要的String()方法,文件内容如下:

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

package painkiller

import "fmt"

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 fmt.Sprintf("Pill(%d)", i)
    }
    return _Pill_name[_Pill_index[i]:_Pill_index[i+1]]
}

参考

[1]. Go学习笔记 附录
[2]. Generating code
[3]. Go generate: A Proposal
[4]. Generate Go files by processing source

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 116,401评论 15 132
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 28,973评论 18 399
  • Ubuntu的发音 Ubuntu,源于非洲祖鲁人和科萨人的语言,发作 oo-boon-too 的音。了解发音是有意...
    萤火虫de梦阅读 82,475评论 9 458
  • 今晚难得的失眠,虽然每晚就晚睡,饭今晚却是极为少见的亢奋到睡不着。自从选择了这条必须随时准备好忙碌的路,我总是陷入...
    江湖笑笑渔阅读 91评论 0 0
  • 假如你愿与我同行, 我们在阳光灿烂的天底下,放肆笑,放肆跑; 假如你愿与我同行, 我们攀登高山,坐立峰顶,一览众山...
    意兮阅读 196评论 0 2