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("生成操作成功完成")
}

注意事项

  1. 正则表达式需要正确转义,特别是在命令行中使用时
  2. 工具会递归扫描指定目录下的所有文件
  3. 可以通过正则表达式精确控制哪些文件需要执行go generate
  4. 支持忽略特定目录或文件模式

许可证

该工具基于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是一个强大的代码生成工具,但默认情况下它不支持递归执行和正则过滤。下面我将介绍如何实现一个支持这些功能的插件。

基本实现思路

  1. 递归遍历目录结构
  2. 在每个目录中查找go generate指令
  3. 根据正则表达式过滤需要处理的文件
  4. 执行匹配文件的生成命令

示例代码

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

使用方法

  1. 将上述代码保存为generate.go
  2. 编译:go build generate.go
  3. 运行:
    • 递归处理所有.go文件:./generate
    • 只处理匹配特定模式的文件:./generate ".*_gen\.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()

注意事项

  1. 确保所有生成命令是幂等的,避免重复生成导致问题
  2. 大型项目中使用并行执行时要注意资源消耗
  3. 考虑添加-dry-run标志来预览将要执行的命令
  4. 处理符号链接时要小心,避免无限循环

这个工具可以大大简化大型项目中代码生成的管理,特别是当项目结构复杂、有多个需要生成的代码文件时。

回到顶部