Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion tools/goctl/internal/version/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
)

// BuildVersion is the version of goctl.
const BuildVersion = "1.10.0"
const BuildVersion = "1.11.0"

var tag = map[string]int{"pre-alpha": 0, "alpha": 1, "pre-beta": 2, "beta": 3, "released": 4, "": 5}

Expand Down
117 changes: 117 additions & 0 deletions tools/goctl/rpc/CHANGELOG-cn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# 变更日志

## 未发布

### 新功能

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

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

**涉及文件:**
- `generator/gen.go` — `ZRpcContext` 新增 `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.go` — `Proto` 结构体新增 `ImportedProtos []ImportedProto` 字段。
- `cli/cli.go` — `RPCNew` 传递 `ProtoPaths` 到 `ZRpcContext`。
- `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.go` — `genFunctions()` 调用 `resolveRPCTypeRef()` 解析请求/响应类型并收集额外导入路径。
- `generator/genlogic.go` — `genLogicFunction()` 使用 `resolveRPCTypeRef()`;新增 `addLogicImports()` 按需添加主 pb 导入和跨包导入。
- `generator/gencall.go` — `genFunction()` 和 `getInterfaceFuncs()` 使用 `resolveCallTypeRef()` 处理类型别名和额外导入;新增 `buildExtraImportLines()` 辅助函数。
- `generator/call.tpl` — 新增 `{{.extraImports}}` 占位符用于跨包导入行。

**前后对比:**

| Proto 类型 | 变更前 | 变更后 |
|---|---|---|
| `GetReq`(同文件) | `pb.GetReq` | `pb.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.go` — `resolveGoogleWKT()` + `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.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.Value` | `structpb.Value` |
| `google.protobuf.ListValue` | `structpb.ListValue` |
| `google.protobuf.FieldMask` | `fieldmaskpb.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.go` — `TestDefaultProtoParseCaseInvalidRequestType` 和 `TestDefaultProtoParseCaseInvalidResponseType` 重命名并更新,验证点号类型现在可以正常解析。
117 changes: 117 additions & 0 deletions tools/goctl/rpc/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Changelog

## Unreleased

### New Features

#### External Proto Import Support (`--proto_path` / `-I`)

Added support for importing proto files from external directories via `-I` / `--proto_path` flags, with full transitive dependency resolution.

**Affected files:**
- `generator/gen.go` — Added `ProtoPaths` field to `ZRpcContext`; added `resolveImportedProtos()` to populate `ImportedProtos` before code generation.
- `generator/genpb.go` — Added `buildProtocCmd()` to automatically discover and append transitively imported proto files to the `protoc` command; added `relativeToProtoPath()` to compute correct relative paths for protoc output.
- `parser/import.go` — New file (major addition). Implements `ResolveImports()` for recursive transitive import resolution, `ParseImportedProtos()` for extracting `go_package` / `package` metadata from imported protos, and `BuildProtoPackageMap()` for O(1) lookup by proto package name.
- `parser/proto.go` — Added `ImportedProtos []ImportedProto` field to the `Proto` struct.
- `cli/cli.go` — Passes `ProtoPaths` from `RPCNew` to `ZRpcContext`.
- `cli/zrpc.go` — Passes `VarStringSliceProtoPath` to `ZRpcContext.ProtoPaths`.

**Before vs After:**

| | Before | After |
|---|---|---|
| Proto imports from external dirs | ❌ Not supported, all types must be in the same file | ✅ Use `-I ./ext_protos` to add search paths |
| Transitive imports (A → B → C) | ❌ Only direct imports recognized | ✅ Recursively resolves all transitive dependencies |
| Imported proto `.pb.go` generation | ❌ Manual, must run protoc separately for each file | ✅ Automatic, imported protos appended to protoc command |
| Proto search paths | ❌ Only source file directory | ✅ Multiple `-I` paths, same as protoc |

**Behavior:**
- Transitively walks all `import` declarations in proto files, skipping `google/*` well-known types.
- Searches each `-I` directory for imported files, silently skipping system-level protos not found in user paths.
- Appends discovered proto files to the `protoc` command so their `.pb.go` files are generated alongside the main proto.

---

#### Cross-Package Type Resolution

When an imported proto has a **different** `go_package` from the main proto, goctl now automatically generates the correct Go import paths and qualified type references in server, logic, and client code.

**Affected files:**
- `generator/typeref.go` — New file. Core type resolution engine:
- `resolveRPCTypeRef()` — Resolves proto RPC types (simple, same-package dotted, cross-package dotted, Google WKT) to Go type references with correct import paths.
- `resolveCallTypeRef()` — Variant for client code generation with type alias support.
- `googleWKTTable` — Mapping table for all 16 Google well-known types to their Go equivalents.
- `generator/genserver.go` — `genFunctions()` now calls `resolveRPCTypeRef()` for request/response types and collects extra import paths.
- `generator/genlogic.go` — `genLogicFunction()` uses `resolveRPCTypeRef()`; added `addLogicImports()` to conditionally include main pb import and cross-package imports.
- `generator/gencall.go` — `genFunction()` and `getInterfaceFuncs()` use `resolveCallTypeRef()` for type aliases and extra imports; added `buildExtraImportLines()` helper.
- `generator/call.tpl` — Added `{{.extraImports}}` placeholder for cross-package import lines.

**Before vs After:**

| Proto type | Before | After |
|---|---|---|
| `GetReq` (same file) | `pb.GetReq` | `pb.GetReq` (unchanged) |
| `ext.ExtReq` (same `go_package`) | ❌ Error: "request type must defined in" | ✅ `pb.ExtReq` — merged into main package |
| `common.TypesReq` (different `go_package`) | ❌ Error: "request type must defined in" | ✅ `common.TypesReq` + auto-generated `import "example.com/demo/pb/common"` |
| `google.protobuf.Empty` | ❌ Error: "request type must defined in" | ✅ `emptypb.Empty` + auto-generated import |

**Behavior:**
- Simple types (e.g., `GetReq`) resolve to `pb.GetReq` with no extra import.
- Same-package dotted types (e.g., `ext.ExtReq` where `ext` has the same `go_package`) resolve to `pb.ExtReq`.
- Cross-package dotted types (e.g., `common.TypesReq` where `common` has a different `go_package`) resolve to `common.TypesReq` with the correct Go import path added automatically.

---

#### Google Well-Known Types as RPC Parameters

Google protobuf well-known types can now be used directly as RPC request/response types (not just as message fields).

**Affected files:**
- `generator/typeref.go` — `resolveGoogleWKT()` + `googleWKTTable` handles all standard types.

**Before vs After:**

| Proto Type | Before (as RPC param) | After (as RPC param) |
|---|---|---|
| `google.protobuf.Empty` | ❌ Error | ✅ `emptypb.Empty` |
| `google.protobuf.Timestamp` | ❌ Error | ✅ `timestamppb.Timestamp` |
| `google.protobuf.Duration` | ❌ Error | ✅ `durationpb.Duration` |
| `google.protobuf.Any` | ❌ Error | ✅ `anypb.Any` |
| `google.protobuf.Struct` | ❌ Error | ✅ `structpb.Struct` |
| `google.protobuf.FieldMask` | ❌ Error | ✅ `fieldmaskpb.FieldMask` |
| `google.protobuf.*Value` | ❌ Error | ✅ `wrapperspb.*Value` |

> Note: These types were already usable as **message fields** before. This change allows them as **RPC request/response types** directly.

**Supported types:**

| Proto Type | Go Type |
|---|---|
| `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.Value` | `structpb.Value` |
| `google.protobuf.ListValue` | `structpb.ListValue` |
| `google.protobuf.FieldMask` | `fieldmaskpb.FieldMask` |
| `google.protobuf.*Value` (wrappers) | `wrapperspb.*Value` |

---

### Breaking Changes

#### Dotted Type Names Now Allowed in RPC Definitions

Previously, goctl rejected any RPC request/response type containing a dot (e.g., `base.Req`), requiring all types to be defined in the same proto file. This restriction has been removed.

**Before vs After:**

| Proto Definition | Before | After |
|---|---|---|
| `rpc Fetch(base.Req) returns (base.Reply)` | ❌ Parse error: "request type must defined in xxx.proto" | ✅ Parsed successfully, `base.Req` resolved via imported proto |
| `rpc Ping(google.protobuf.Empty) returns (Reply)` | ❌ Parse error: "request type must defined in xxx.proto" | ✅ Parsed successfully, resolved to `emptypb.Empty` |

**Affected files:**
- `parser/service.go` — Removed the validation loop that rejected dotted type names with `"request type must defined in"` / `"returns type must defined in"` errors.
- `parser/parser_test.go` — `TestDefaultProtoParseCaseInvalidRequestType` and `TestDefaultProtoParseCaseInvalidResponseType` renamed and updated to verify that dotted types now parse successfully.
Loading
Loading