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