generate of Go tools

Go language provides a series of powerful tools, flexible use of which can make our project development easier. The tool set includes the following.

bug         start a bug report
build       compile packages and dependencies
clean       remove object files and cached files
doc         show documentation for package or symbol
env         print Go environment information
fix         update packages to use new APIs
fmt         gofmt (reformat) package sources
generate    generate Go files by processing source
get         add dependencies to current module and install them
install     compile and install packages and dependencies
list        list packages or modules
mod         module maintenance
run         compile and run Go program
test        test packages
tool        run specified go tool
version     print Go version
vet         report likely mistakes in packages

The source code of the tool is located in $GOPATH/src/cmd/internal. This article focuses on the Go tool generate.

Article catalog

go language automation tool

Go generate is often used to generate code automatically. It can generate code from source code before code compilation. When you run go generate, it will scan the source code files related to the current package, find out all comment statements containing "/ / go:generate", extract and execute the command after the comment. The command is an executable program. This process is similar to a call to execute a shell script.

usage method

  • Add special note
//go:generate command argument...
  • Execute the generate command
$ go generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages]

matters needing attention

  • This special comment must be included in the. go source file.
  • Each source file can contain multiple generate special comments.
  • go generate will not be triggered by commands like go build, go get, go test, etc. it must be used explicitly by developers.
  • Command execution is serial. If there is an error, subsequent commands will not be executed.
  • Special comments must begin with "/ / go:generate" with no spaces after the double slash.
  • The execution command must be an executable program under system PATH (echo $PATH).

Use example

package main

import "fmt"

//go:generate echo GoGoGo!
//go:generate go run main.go
//go:generate echo $GOARCH $GOOS $GOFILE $GOLINE $GOPACKAGE

func main() {
 fmt.Println("go rum main.go!")
}

Execute the go generate command

$ go generate
GoGoGo!
go rum main.go!
amd64 darwin main.go 7 main

Implementing the String method for enumeration constants

After reading the brief introduction of generate, the reader may not feel the power of the tool. The small vegetable knife provides a classic application scenario of the tool: implementing String method for enumeration constants.

We need to mention another official tool, stringer, which can automatically write String() methods for integer constant sets. Since stringer is not in the official Go distribution toolset, we need to install it ourselves and execute the following command.

go get golang.org/x/tools/cmd/stringer

Here's an example from a stringer document. The code is as follows, which defines a set of integer constants of different kill types.

package painkiller

type Pill int

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

For debugging or other reasons, we want these constants to print out, which means that hill has to have a signed method.

func (p Pill) String() string

To implement it, it's very simple.

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)
}

Imagine that if we add a batch of drug product names to the roll list, each time we add or modify the drug name, it needs to be changed in the corresponding signature function. Isn't it troublesome and likely to miss or make mistakes? At this time, we can solve this problem through the solution of go generate + stringer. Simply add a comment statement to the code that defines hill.

//go:generate stringer -type=Pill

The above command, on behalf of running the stringer tool to generate String methods for the ball type, is output to the ball by default_ String.go In the file, execute as follows.

$ go generate
$ cat pill_string.go
// Code generated by stringer -type Pill pill.go; 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+1 >= Pill(len(_Pill_index)) {
        return fmt.Sprintf("Pill(%d)", i)
    }
    return _Pill_name[_Pill_index[i]:_Pill_index[i+1]]
}

In this way, every time we make a change to the hill type, all we need to do is run the following statement.

$ go generate

Of course, if you find this troublesome, or worry about forgetting to execute the generate statement. Then, the go generate statement can be written into the Makefile and placed before the go build command to realize the automation of code generation and compilation.

It is worth mentioning that in the Go source document, the go generate+stringer scheme is widely used to implement the String method for enumeration constants. Under the source code of Go 1.14.1, there are 23 places to use it, as follows.

summary

This article mainly introduces what generate is and what it can do. If you want to have a deep understanding of its internal implementation logic, you can see the detailed process of generating code in the Go source code, for example, through the genzfunc.go realization zfuncversion.go Generation of. In the Go source repository, you can find many similar implementation logic, as follows.

They use the libraries provided by the Go compiler, including go/ast to define the abstract syntax tree, go/parser to parse the abstract syntax tree, go/format to format code, go/token to use the Go lexical mark, etc. Parsing the source file and generating new code according to the existing template is similar to generating HTML file by using template in Web service.

Conclusion: reduce code repetition and protect hair!!

reference resources

https://golang.org/cmd/go/
https://blog.golang.org/generate
https://godoc.org/golang.org/x/tools/cmd/stringer
https://docs.google.com/document/d/1V03LUfjSADDooDMhe-_K59EgpTEm3V8uvQRuNMAEnjg/edit#
https://mp.weixin.qq.com/s/vz-Qpt9c5WhyynpBrJLmGA

Tags: Go shell Makefile Google

Posted on Thu, 25 Jun 2020 22:36:07 -0400 by sdjensen