源码先锋

源码先锋

清华学神尹成带你学习golang2021(55)在编译前自动化生成某类代

admin 26 130

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

使用gogenerate命令时有以下几点需要注意:

该特殊注释必须在.go源码文件中;

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

运行gogenerate命令时,才会执行特殊注释后面的命令;

当gogenerate命令执行出错时,将终止程序的运行;

特殊注释必须以//go:generate开头,双斜线后面没有空格。

在下面这些场景下,我们会使用gogenerate命令:

yacc:从.y文件生成.go文件;

protobufs:从protocolbuffer定义文件(.proto)生成.文件;

Unicode:从生成Unicode表;

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

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

再比如:

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

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

gogenerate命令格式如下所示:

gogenerate[-runregexp][-n][-v][-x][command][buildflags][|packages]

参数说明如下:

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

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

-n显示不执行命令;

-x显示并执行命令;

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

执行gogenerate命令时,也可以使用一些环境变量,如下所示:

$GOARCH体系架构(arm、amd64等);

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

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

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

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

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

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

packagemain

import"fmt"

//go:
//go:generategoversion
funcmain(){
("http://清华尹成大神/golang/")
}

执行gogenerate-x命令,输出结果如下:

gogenerate-x

http://清华尹成大神/golang/

goversion

/amd64

通过运行结果可以看出//go:generate之后的命令成功运行了,命令中使用的-x参数是为了将执行的具体命令同时打印出来。

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

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

/x/tools/cmd/stringer

上面的命令需要翻墙。条件不允许的话也可以通过Github上的镜像来安装,安装方法如下:

gitclone$GOPATH/src//x/tools

/x/tools/cmd/stringer

安装好的stringer工具位于GOPATH/bin目录下,想要正常使用它,需要先将GOPATH/bin目录添加到系统的环境变量PATH中。

【示例2】使用stringer工具实现String()方法:

首先,在项目目录下新建一个painkiller文件夹,并在该文件夹中创建文件,文件内容如下:

//go:generatestringer-type=Pill
packagepainkiller

typePillint

const(
PlaceboPill=iota
Aspirin
Ibuprofen
Paracetamol
Acetaminophen=Paracetamol
)

然后,在文件所在的目录下运行gogenerate命令。

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

//Codegeneratedby"stringer-type=Pill";DONOTEDIT.

packagepainkiller

import"strconv"

func_(){
//An"invalidarrayindex"compilererrorsignifiesthattheconstantvalueshavechanged.
//Re-runthestringercommandtogeneratethemagain.
varx[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(iPill)String()string{
ifi0||i=Pill(len(_Pill_index)-1){
return"Pill("+(int64(i),10)+")"
}
return_Pill_name[_Pill_index[i]:_Pill_index[i+1]]
}