go generate
todo…
1 介绍
Tip
- Go 语言的标准库中的 cmd/go/internal/generate
- go generate 命令会遍历指定的包路径下的所有 Go 源文件,并解析这些源文件中的 //go:generate 注释,这些注释包含了代码生成指令
- //go:generate 后面就是会执行的命令,比如
//go:generate echo hello执行go generate会显示 hello
2 例子
提醒
早期笔记, 下面的例子粗糙,{==给struct类型生成getset方法==}, 等整理了ast后, 我会再完善一下.
建议去学习 ==stringer==
//go:generate echo 123
// goto hell
// xyz
package main
//go:generate go run codegen/gen.go
//go:generate echo hello--world
//helloworld33
type Person struct { // this is person
Name string // this is name
Age int32
}
//go:generate echo 99
type Cup struct {
Price float64
Origin string
}codegen/gen.go
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/printer"
"go/token"
"os"
"strings"
"text/template"
)
type Field struct {
Name string
Type string
}
type StructInfo struct {
Name string
Fields []Field
PackageName string
}
func generateCode() {
fset := token.NewFileSet()
// 解析当前目录下所有的 Go 源文件,不会递归解析子目录里的文件的.
pkgs, err := parser.ParseDir(fset, ".", nil, parser.ParseComments)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to parse directory: %v\n", err)
os.Exit(1)
}
for _, pkg := range pkgs {
for _, file := range pkg.Files {
packageName := file.Name.Name
// for _, l := range file.Doc.List {
// fmt.Println("file-doc", l.Text)
// }
// fmt.Println("---", packageName, file.Doc.Text(), "===")
// for _, c := range file.Comments {
// // 挨在一起的2行注释算一个file的Comment
// fmt.Print("[")
// for _, l := range c.List {
// fmt.Println("cc", l.Text)
// }
// fmt.Println("]")
// }
// for _, d := range file.Decls {
// x := d.(*ast.GenDecl)
// if x.Doc != nil {
// fmt.Println(len(x.Doc.List))
// for _, l := range x.Doc.List {
// fmt.Println("ooo", l.Text)
// }
// fmt.Println("gg", x.Doc.Text(), "--")
// }
// ts := x.Specs[0].(*ast.TypeSpec)
// fmt.Println(ts.Name)
// }
// continue
ast.Inspect(file, func(n ast.Node) bool {
switch x := n.(type) {
// 注释写在type struct 上面的注释 是算一个节点. 不是属于type struct的内部东西
case *ast.GenDecl:
// fmt.Println("注释内容2:", x.Doc.Text())
case *ast.CommentGroup:
// fmt.Println("注释内容:", x.Text())
case *ast.TypeSpec:
if structType, ok := x.Type.(*ast.StructType); ok {
fmt.Println("结构体名称:", x.Name.Name)
structInfo := StructInfo{
Name: x.Name.Name,
PackageName: packageName,
}
// 提取结构体字段信息
for _, field := range structType.Fields.List {
for _, ident := range field.Names {
structInfo.Fields = append(structInfo.Fields, Field{
Name: ident.Name,
Type: fileSetToString(fset, field.Type),
})
}
}
// 生成 Get 和 Set 方法的代码
generateGetSetMethods(structInfo)
}
}
// ast.Print(fset, n)
return true
})
}
}
}
func generateGetSetMethods(structInfo StructInfo) {
tmpl := template.Must(template.New("").Parse(getSetMethodsTemplate))
fileName := structInfo.Name + "_getset.go"
fmt.Println(fileName)
file, err := os.Create(fileName)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to create file: %v\n", err)
os.Exit(1)
}
defer file.Close()
err = tmpl.Execute(file, structInfo)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to generate code: %v\n", err)
os.Exit(1)
}
fmt.Printf("生成的文件 %s\n", fileName)
}
func fileSetToString(fset *token.FileSet, node ast.Node) string {
var buf strings.Builder
if err := printer.Fprint(&buf, fset, node); err != nil {
return ""
}
return buf.String()
}
const getSetMethodsTemplate = `
// Code generated by go generate; DO NOT EDIT.
package {{.PackageName}}
{{range .Fields}}
func (s *{{$.Name}}) Get{{.Name}}() {{.Type}} {
return s.{{.Name}}
}
func (s *{{$.Name}}) Set{{.Name}}(value {{.Type}}) {
s.{{.Name}} = value
}
{{end}}
`
func main() {
fmt.Println(111)
generateCode()
}