diff --git a/generator/router.go b/generator/router.go index 28f4136..24b58cd 100644 --- a/generator/router.go +++ b/generator/router.go @@ -194,6 +194,13 @@ func (routerNode *RouterNode) DFS(i int, hook func(layer int, node *RouterNode) var handlerPkgMap map[string]string +// ResetRouterState 清空路由生成的进程级累积状态(handler 包别名映射)。 +// 与 util.ResetUniqueNameSets 配合,使「单进程循环处理多个 idl」时,每个 idl 的 +// 命名都从干净状态起算,结果与旧版「逐文件单进程」生成完全一致。 +func ResetRouterState() { + handlerPkgMap = nil +} + func (routerNode *RouterNode) Insert(name string, method *HttpMethod, handlerType string, paths []string, handlerPkg string) { cur := routerNode for i, p := range paths { diff --git a/protobuf/plugin.go b/protobuf/plugin.go index 67ce849..563b88e 100644 --- a/protobuf/plugin.go +++ b/protobuf/plugin.go @@ -239,51 +239,65 @@ func (plugin *Plugin) Handle(req *pluginpb.CodeGeneratorRequest, args *config.Ar for _, file := range files { maps[file.GetName()] = file } - main := maps[gen.Request.FileToGenerate[len(gen.Request.FileToGenerate)-1]] - deps := make(map[string]*descriptorpb.FileDescriptorProto, len(main.GetDependency())) - for _, dep := range main.GetDependency() { - if f, ok := maps[dep]; !ok { - err := fmt.Errorf("dependency file not found: %s", dep) + // 为 FileToGenerate 中的「每一个」idl 生成 http package(handler/router/register/middleware)。 + // 原实现只取最后一个 idl,导致一次传入多个 --idl 时,除最后一个外其余 proto 都只生成 + // model、不生成 handler/router(hz_gen 批量脚本正是踩了这个坑)。 + // + // 关键点:handler 追加、register.go、middleware.go 都是「读磁盘现有内容再合并」。插件进程内 + // 默认不落盘(最终交给 protoc 写),因此若只在内存里循环,后一个 idl 读到的仍是旧文件,会丢掉 + // 前一个 idl 新增的路由/中间件注册。所以每生成完一个 idl 就「立即写盘」,保证下一轮合并读到的是 + // 累积后的最新内容。这样多个新服务同批生成也不会互相覆盖注册。 + for _, idlName := range gen.Request.FileToGenerate { + // 每个 idl 开始前重置进程级命名状态,使其行为与「逐文件单进程」一致, + // 避免中间件分组变量名带上跨 idl 的全局序号后缀(如 _v184)造成大面积无谓 diff。 + util.ResetUniqueNameSets() + generator.ResetRouterState() + + main := maps[idlName] + deps := make(map[string]*descriptorpb.FileDescriptorProto, len(main.GetDependency())) + for _, dep := range main.GetDependency() { + f, ok := maps[dep] + if !ok { + err := fmt.Errorf("dependency file not found: %s", dep) + gen.Error(err) + resp := gen.Response() + if err2 := plugin.Response(resp); err2 != nil { + return err + } + return nil + } + deps[dep] = f + } + + pkgFiles, err := plugin.genHttpPackage(main, deps, args) + if err != nil { + err := fmt.Errorf("generate package files failed: %s", err.Error()) gen.Error(err) resp := gen.Response() - err2 := plugin.Response(resp) - if err2 != nil { + if err2 := plugin.Response(resp); err2 != nil { return err } return nil - } else { - deps[dep] = f + } + + // 立即落盘,使后续 idl 的 register.go/middleware.go/handler 合并读到最新内容。 + for _, pkgFile := range pkgFiles { + absPath := pkgFile.Path + if !filepath.IsAbs(absPath) { + absPath = filepath.Join(args.OutDir, pkgFile.Path) + } + if err := os.MkdirAll(filepath.Dir(absPath), 0o755); err != nil { + return fmt.Errorf("create dir for '%s' failed: %s", absPath, err.Error()) + } + if err := ioutil.WriteFile(absPath, []byte(pkgFile.Content), 0o644); err != nil { + return fmt.Errorf("write http package file '%s' failed: %s", absPath, err.Error()) + } } } - pkgFiles, err := plugin.genHttpPackage(main, deps, args) - if err != nil { - err := fmt.Errorf("generate package files failed: %s", err.Error()) - gen.Error(err) - resp := gen.Response() - err2 := plugin.Response(resp) - if err2 != nil { - return err - } - return nil - } - - // construct plugin response + // http package 已直接写盘;响应里只回模型文件(交给 protoc 写)。 resp := gen.Response() - // all files that need to be generated are returned to protoc - for _, pkgFile := range pkgFiles { - filePath := pkgFile.Path - content := pkgFile.Content - renderFile := &pluginpb.CodeGeneratorResponse_File{ - Name: &filePath, - Content: &content, - } - resp.File = append(resp.File, renderFile) - } - - // plugin stop working - err = plugin.Response(resp) - if err != nil { + if err := plugin.Response(resp); err != nil { return fmt.Errorf("write response failed: %s", err.Error()) } diff --git a/util/data.go b/util/data.go index 26dbe6c..4293de8 100644 --- a/util/data.go +++ b/util/data.go @@ -364,6 +364,17 @@ var ( uniqueHandlerPackageName = map[string]bool{} ) +// ResetUniqueNameSets 清空「唯一变量名」累积状态。 +// 这些 set 原本假设「一个进程只处理一个 idl」(旧版逐文件生成即如此), +// 因此命名从干净的 _v1/_user 起算。当一个进程内循环处理多个 idl 时,必须在每个 +// idl 开始前重置,否则中间件分组变量名会带上全局序号后缀(如 _v184),导致与逐文件 +// 生成结果不一致、且依赖处理顺序而不稳定。 +func ResetUniqueNameSets() { + uniquePackageName = map[string]bool{} + uniqueMiddlewareName = map[string]bool{} + uniqueHandlerPackageName = map[string]bool{} +} + // GetPackageUniqueName can get a non-repeating variable name for package alias func GetPackageUniqueName(name string) (string, error) { name, err := getUniqueName(name, uniquePackageName)