Back to Go Zero

变更日志

tools/goctl/rpc/CHANGELOG-cn.md

1.10.16.6 KB
Original Source

变更日志

未发布

新功能

外部 Proto 导入支持(--proto_path / -I

新增通过 -I / --proto_path 标志导入外部目录中的 proto 文件,支持完整的传递性依赖解析。

涉及文件:

  • generator/gen.goZRpcContext 新增 ProtoPaths 字段;新增 resolveImportedProtos() 在代码生成前填充 ImportedProtos
  • generator/genpb.go — 新增 buildProtocCmd() 自动发现并追加传递性导入的 proto 文件到 protoc 命令;新增 relativeToProtoPath() 计算正确的相对路径。
  • parser/import.go — 新文件(主要新增)。实现 ResolveImports() 递归解析传递性导入,ParseImportedProtos() 提取导入 proto 的 go_package / package 元数据,BuildProtoPackageMap() 构建按 proto 包名的 O(1) 查找表。
  • parser/proto.goProto 结构体新增 ImportedProtos []ImportedProto 字段。
  • cli/cli.goRPCNew 传递 ProtoPathsZRpcContext
  • cli/zrpc.go — 将 VarStringSliceProtoPath 传递到 ZRpcContext.ProtoPaths

前后对比:

变更前变更后
从外部目录导入 Proto❌ 不支持,所有类型必须在同一文件中定义✅ 使用 -I ./ext_protos 添加搜索路径
传递性导入(A → B → C)❌ 仅识别直接导入✅ 递归解析所有传递性依赖
导入 proto 的 .pb.go 生成❌ 需手动为每个文件单独运行 protoc✅ 自动将导入的 proto 追加到 protoc 命令
Proto 搜索路径❌ 仅源文件所在目录✅ 支持多个 -I 路径,与 protoc 一致

行为说明:

  • 递归遍历 proto 文件中的所有 import 声明,跳过 google/* 知名类型。
  • 在每个 -I 目录中搜索被导入的文件,未找到的系统级 proto 静默跳过。
  • 将发现的 proto 文件追加到 protoc 命令,使其 .pb.go 文件与主 proto 一同生成。

跨包类型解析

当导入的 proto 与主 proto 具有不同go_package 时,goctl 现在能够自动在 server、logic 和 client 代码中生成正确的 Go 导入路径和限定类型引用。

涉及文件:

  • generator/typeref.go — 新文件,核心类型解析引擎:
    • resolveRPCTypeRef() — 将 proto RPC 类型(简单类型、同包点号类型、跨包点号类型、Google WKT)解析为带正确导入路径的 Go 类型引用。
    • resolveCallTypeRef() — 客户端代码生成变体,支持类型别名。
    • googleWKTTable — 全部 16 种 Google 知名类型到 Go 等价类型的映射表。
  • generator/genserver.gogenFunctions() 调用 resolveRPCTypeRef() 解析请求/响应类型并收集额外导入路径。
  • generator/genlogic.gogenLogicFunction() 使用 resolveRPCTypeRef();新增 addLogicImports() 按需添加主 pb 导入和跨包导入。
  • generator/gencall.gogenFunction()getInterfaceFuncs() 使用 resolveCallTypeRef() 处理类型别名和额外导入;新增 buildExtraImportLines() 辅助函数。
  • generator/call.tpl — 新增 {{.extraImports}} 占位符用于跨包导入行。

前后对比:

Proto 类型变更前变更后
GetReq(同文件)pb.GetReqpb.GetReq(无变化)
ext.ExtReq(相同 go_package❌ 报错:"request type must defined in"pb.ExtReq — 合并到主包
common.TypesReq(不同 go_package❌ 报错:"request type must defined in"common.TypesReq + 自动生成 import "example.com/demo/pb/common"
google.protobuf.Empty❌ 报错:"request type must defined in"emptypb.Empty + 自动生成导入

行为说明:

  • 简单类型(如 GetReq)解析为 pb.GetReq,无额外导入。
  • 同包点号类型(如 ext.ExtReq,其中 ext 与主 proto 有相同的 go_package)解析为 pb.ExtReq
  • 跨包点号类型(如 common.TypesReq,其中 common 有不同的 go_package)解析为 common.TypesReq,并自动添加正确的 Go 导入路径。

Google 知名类型作为 RPC 参数

Google protobuf 知名类型现在可以直接用作 RPC 的请求/响应类型(而不仅仅是消息字段)。

涉及文件:

  • generator/typeref.goresolveGoogleWKT() + googleWKTTable 处理所有标准类型。

前后对比:

Proto 类型变更前(作为 RPC 参数)变更后(作为 RPC 参数)
google.protobuf.Empty❌ 报错emptypb.Empty
google.protobuf.Timestamp❌ 报错timestamppb.Timestamp
google.protobuf.Duration❌ 报错durationpb.Duration
google.protobuf.Any❌ 报错anypb.Any
google.protobuf.Struct❌ 报错structpb.Struct
google.protobuf.FieldMask❌ 报错fieldmaskpb.FieldMask
google.protobuf.*Value❌ 报错wrapperspb.*Value

注:这些类型此前已可用作消息字段。本次变更使其可直接用作 RPC 请求/响应类型

完整类型映射表:

Proto 类型Go 类型
google.protobuf.Emptyemptypb.Empty
google.protobuf.Timestamptimestamppb.Timestamp
google.protobuf.Durationdurationpb.Duration
google.protobuf.Anyanypb.Any
google.protobuf.Structstructpb.Struct
google.protobuf.Valuestructpb.Value
google.protobuf.ListValuestructpb.ListValue
google.protobuf.FieldMaskfieldmaskpb.FieldMask
google.protobuf.*Value(包装类型)wrapperspb.*Value

不兼容变更

RPC 定义中允许使用点号类型名

此前 goctl 会拒绝 RPC 请求/响应类型中包含点号的情况(如 base.Req),要求所有类型必须定义在同一个 proto 文件中。此限制已移除。

前后对比:

Proto 定义变更前变更后
rpc Fetch(base.Req) returns (base.Reply)❌ 解析错误:"request type must defined in xxx.proto"✅ 解析成功,base.Req 通过导入的 proto 解析
rpc Ping(google.protobuf.Empty) returns (Reply)❌ 解析错误:"request type must defined in xxx.proto"✅ 解析成功,解析为 emptypb.Empty

涉及文件:

  • parser/service.go — 移除了拒绝点号类型名的验证循环(原错误信息为 "request type must defined in" / "returns type must defined in")。
  • parser/parser_test.goTestDefaultProtoParseCaseInvalidRequestTypeTestDefaultProtoParseCaseInvalidResponseType 重命名并更新,验证点号类型现在可以正常解析。