golang递归执行go generate并支持正则过滤的插件generate的使用
Golang递归执行go generate并支持正则过滤的插件generate的使用
包介绍
Package generate是一个可以在指定路径或环境变量(如$GOPATH)上递归运行go generate的工具,并支持通过正则表达式进行过滤。
为什么使用它?
当准备编译应用程序时,您可能需要执行一系列设置、运行一些脚本甚至嵌入静态资源。与其将所有内容编程到构建脚本中,不如将这些内容添加到//go:generate语句中,然后运行此工具递归执行所有设置。
安装
使用go get安装:
go get github.com/go-playground/generate
或更新:
go get -u github.com/go-playground/generate
然后在代码中导入:
import "github.com/go-playground/generate"
使用示例
命令行使用
从bash提示符运行(注意转义):
generate -i=$GOPATH -ignore=/\. -match=/github.com/MyOrganizationOrUser
运行generate -h
查看所有选项。
Go代码示例
package main
import (
"github.com/go-playground/generate"
"log"
)
func main() {
// 创建生成器实例
gen := generate.New()
// 设置输入路径(这里使用GOPATH)
gen.Input = "$GOPATH"
// 设置忽略模式(正则表达式)
gen.IgnoreRegex = "/\\."
// 设置匹配模式(正则表达式)
gen.MatchRegex = "/github.com/MyOrganizationOrUser"
// 递归执行go generate
if err := gen.Run(); err != nil {
log.Fatalf("执行go generate失败: %v", err)
}
log.Println("所有go generate命令执行完成")
}
更复杂的示例
package main
import (
"github.com/go-playground/generate"
"log"
"regexp"
)
func main() {
// 初始化生成器
g := generate.New()
// 设置自定义选项
options := &generate.Options{
Input: "./myproject", // 指定项目目录
MatchRegex: ".*_gen.go$", // 只匹配以_gen.go结尾的文件
IgnoreRegex: "vendor/.*", // 忽略vendor目录
Verbose: true, // 显示详细输出
}
// 编译正则表达式进行验证
if _, err := regexp.Compile(options.MatchRegex); err != nil {
log.Fatalf("匹配正则表达式无效: %v", err)
}
if _, err := regexp.Compile(options.IgnoreRegex); err != nil {
log.Fatalf("忽略正则表达式无效: %v", err)
}
// 应用选项
if err := g.SetOptions(options); err != nil {
log.Fatalf("设置选项失败: %v", err)
}
// 执行生成
if err := g.Run(); err != nil {
log.Fatalf("执行失败: %v", err)
}
log.Println("生成操作成功完成")
}
注意事项
- 正则表达式需要正确转义,特别是在命令行中使用时
- 工具会递归扫描指定目录下的所有文件
- 可以通过正则表达式精确控制哪些文件需要执行go generate
- 支持忽略特定目录或文件模式
许可证
该工具基于MIT许可证分发,更多细节请参见代码中的许可证文件。
更多关于golang递归执行go generate并支持正则过滤的插件generate的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang递归执行go generate并支持正则过滤的插件generate的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang递归执行go generate并支持正则过滤的插件
在Go语言中,go generate
是一个强大的代码生成工具,但默认情况下它不支持递归执行和正则过滤。下面我将介绍如何实现一个支持这些功能的插件。
基本实现思路
- 递归遍历目录结构
- 在每个目录中查找
go generate
指令 - 根据正则表达式过滤需要处理的文件
- 执行匹配文件的生成命令
示例代码
package main
import (
"fmt"
"io/fs"
"os"
"os/exec"
"path/filepath"
"regexp"
)
func main() {
// 获取当前工作目录
root, err := os.Getwd()
if err != nil {
fmt.Printf("获取当前目录失败: %v\n", err)
os.Exit(1)
}
// 定义正则表达式过滤条件
pattern := `.*\.go$` // 默认匹配所有.go文件
if len(os.Args) > 1 {
pattern = os.Args[1]
}
// 编译正则表达式
re, err := regexp.Compile(pattern)
if err != nil {
fmt.Printf("无效的正则表达式: %v\n", err)
os.Exit(1)
}
// 递归处理目录
err = filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
// 只处理.go文件
if !d.IsDir() && re.MatchString(path) {
processGoFile(path)
}
return nil
})
if err != nil {
fmt.Printf("遍历目录失败: %v\n", err)
os.Exit(1)
}
}
func processGoFile(filePath string) {
// 读取文件内容
content, err := os.ReadFile(filePath)
if err != nil {
fmt.Printf("读取文件 %s 失败: %v\n", filePath, err)
return
}
// 查找go:generate指令
generateRe := regexp.MustCompile(`//go:generate (.+)`)
matches := generateRe.FindAllStringSubmatch(string(content), -1)
if len(matches) == 0 {
return
}
// 获取文件所在目录
dir := filepath.Dir(filePath)
// 执行每个generate命令
for _, match := range matches {
if len(match) < 2 {
continue
}
cmdStr := match[1]
fmt.Printf("在 %s 中执行: %s\n", dir, cmdStr)
// 创建命令
cmd := exec.Command("sh", "-c", cmdStr)
cmd.Dir = dir
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
// 执行命令
if err := cmd.Run(); err != nil {
fmt.Printf("执行命令失败: %v\n", err)
}
}
}
使用方法
- 将上述代码保存为
generate.go
- 编译:
go build generate.go
- 运行:
- 递归处理所有.go文件:
./generate
- 只处理匹配特定模式的文件:
./generate ".*_gen\.go$"
- 递归处理所有.go文件:
高级用法
1. 集成到项目中的Makefile
.PHONY: generate
generate:
go build -o bin/generate ./tools/generate
./bin/generate ".*_gen\.go$"
2. 支持自定义命令
可以修改代码,支持更多参数:
// 修改main函数开头
if len(os.Args) > 2 {
fmt.Println("用法: generate [正则表达式] [命令]")
os.Exit(1)
}
var customCmd string
if len(os.Args) > 2 {
customCmd = os.Args[2]
}
// 修改processGoFile函数
if customCmd != "" {
cmdStr = customCmd
}
3. 并行执行
对于大型项目,可以并行执行生成命令:
func processGoFile(filePath string, wg *sync.WaitGroup) {
defer wg.Done()
// ...原有代码...
}
// 在main函数中
var wg sync.WaitGroup
err = filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
// ...
if !d.IsDir() && re.MatchString(path) {
wg.Add(1)
go processGoFile(path, &wg)
}
// ...
})
wg.Wait()
注意事项
- 确保所有生成命令是幂等的,避免重复生成导致问题
- 大型项目中使用并行执行时要注意资源消耗
- 考虑添加
-dry-run
标志来预览将要执行的命令 - 处理符号链接时要小心,避免无限循环
这个工具可以大大简化大型项目中代码生成的管理,特别是当项目结构复杂、有多个需要生成的代码文件时。