feat(grpcgen): 分层控制 —— 每一层是下一层的默认值,不是另一条路 - #4
Merged
Conversation
`generate_all()` 之外原本是断崖:两个旋钮(proto_dir / grpc)之后,需求一超出
就只能手写六十行 build.mcpp **绕开整个规则** —— 而那六十行要重新实现规则已经解决
过的东西(well-known types 定位、嵌套 .proto 建子目录、输入集合计算),并且会与
规则悄悄漂移、没有任何机制报出。**断崖本身就是设计缺陷**:它把「优雅」和「可控」
变成了二选一。
L0 generate_all()
L1 generate_all({.extra_dirs=…, .imports=…, .mock=…, .protoc_args=…})
L2 generate_all({.plugins={cpp(), my_plugin}})
L3 auto e = plan_all(); /* 改 */ ; submit(e);
它们不是并列选项:`generate_all(opt)` **就是** `submit(plan_all(opt))`,
`.grpc = true` **就是** `.plugins = {cpp()}`。L0 逐字节不变。
## extra_dirs 与 imports 是两件事
写示例时撞出来的,不是设计时想到的:protoc 会把 `#include "common/types.pb.h"`
写进任何 import 了它的文件,所以**只靠 -I 够到的共享树会产出一个没人生成的头**,
报错还离原因很远(`fatal error: 'common/types.pb.h' file not found`)。
于是分成两个概念:`extra_dirs` 既生成又搜索,`imports` 只搜索。
## 可观测性的分工由引擎定死
核实过:`mcpp:action=` 的边其完整 argv 原样落在 build.ninja 里
(`rule mcpp_action_N / command = …`),`ninja -t commands <output>` 即可取。
**规则因此不实现命令行 dump** —— 第二个真相来源只会漂移。规则负责的是「哪些旋钮
产生了它」,写进每次构建都打印的 description:
GENERATE protoc:orders (+grpc +mock, -Iproto -I../shared-proto)
## mock
`--grpc_out=generate_mock_code=true:<dir>` 是**插件参数**而非 protoc 顶层参数 ——
正是消费者不该被要求知道的拼写,所以做成具名选项而不是让人往 protoc_args 里塞。
生成的 mock 头 `#include <gmock/gmock.h>`,而本生态的 compat.gtest 只带
googletest、不带 gmock,所以示例**声明并产出**它、但不 include 它;CI 直接断言
文件存在。
## 示例
新增 examples/advanced(L1 + L3)与 examples/shared-proto(被跨根生成的共享树)。
greeter(L0)与 helloworld(手写展开)从零重建复验,输出不变。
设计:.agents/docs/2026-08-06-grpcgen-layered-control-design.md
* extra_dirs —— 设计里只有 imports(额外 -I),写示例时撞出来:protoc 会把 `#include "common/types.pb.h"` 写进 import 了它的文件,只靠 -I 够到的共享树 会产出一个没人生成的头。两个概念必须分开。这条是**示例**发现的,不是设计发现 的 —— 只写文档不写示例会把它漏掉。 * plan_entries / entry —— L3 的真正底层((root, name) 寻址),plan 与 plan_all 都汇入它。必须公开:那是「.proto 不在任何已知根下」的项目唯一的出路,否则它们 又回到自己写 build.mcpp,即本设计要消掉的断崖。 * 并如实记录 gmock 缺失这个生态限制。
L2(自定义插件)实现完就交了 PR、没跑过;写探针跑一次就照出来:传**绝对路径**
时 `lexically_relative` **会成功**,但返回一串比原路径还长的 `..`:
-I../../../../../../home/speak/workspace/github/mcpplibs/grpc-m/examples/shared-proto
我的回退条件只覆盖「算不出」,没覆盖「算出来更差」。改为:相对形式以 `../..`
开头就用原值。
顺带记下 L2 探针的结论(argv 构造正确,无需改动):
--foo_out=a=1,b=2:<out> protoc 的 params:dir 文法
--plugin=protoc-gen-foo=<binary>
outputs 含 .foo.cc / .foo.h 后缀进了输出集
desc 含 +grpc +foo 与内置 cpp() 共存
这是「只写文档不写示例会漏掉」的第二次印证(第一次是 extra_dirs)。
设计文档 §9 列了这条,实现时漏了:CI 只断言产物存在,没断言 description。
mcpp 已经把完整 argv 写进 build.ninja,所以规则拥有的那一半是「**哪些旋钮**产生
了它」——而那串字符是每次构建都会打印的东西。不断言它,它与实际选项漂移了也不会
有人发现。
description = GENERATE protoc:orders (+grpc +mock, -Iproto -I../shared-proto)
本地已验证该断言通过。
官方状态页已恢复 operational。此前各轮的失败均为 `Failed to resolve action download info: Service Unavailable` 与无日志的 cancelled,与改动无关。
linux 上四个重步骤全是串行的完整 gRPC 构建:module test 25min, 之后每个 example ~31min。三个 example 时已经 ~90min,加上 `advanced` 就越过了 120 分钟上限,job 在构建中途被取消 —— 看起来 像红,其实不是。 真正的成本是把同一个 gRPC 串行编了四遍;把 example 拆成自己的矩阵 维度(或让依赖缓存能跨兄弟工程命中)能把 ~2h 压到 ~35min。那是 独立的一件事,不该塞进发布 PR。注释已写明。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
实现
.agents/docs/2026-08-06-grpcgen-layered-control-design.md。问题不是「不够方便」,是断崖
generate_all()之外只有两个旋钮(proto_dir/grpc)。需求一超出,用户只能像examples/helloworld那样手写六十行 build.mcpp 绕开整个规则 —— 而那六十行要重新实现规则已经解决过的东西:well-known types 目录怎么定位、嵌套.proto的输出子目录怎么建、输入集合怎么算。它们会与规则悄悄漂移,且没有任何机制会报出漂移。断崖本身就是设计缺陷:它把「优雅」和「可控」变成了二选一。
四层,不是四条路
generate_all(opt)就是submit(plan_all(opt)),.grpc = true就是.plugins = {cpp()}。L0 逐字节不变。extra_dirs与imports是两件事 —— 写示例时撞出来的不是设计时想到的。protoc 会把
#include "common/types.pb.h"写进任何 import 了它的文件,所以只靠-I够到的共享树会产出一个没人生成的头:而且报错离原因很远。于是分成两个概念:
extra_dirs既生成又搜索,imports只搜索。这是 L1 里唯一一个不在原设计里的字段。可观测性的分工由引擎定死,不由规则发明
核实过(不是推断):
mcpp:action=的边其完整 argv 原样落在build.ninja里,ninja -t commands <output>即可取。所以规则不实现命令行 dump —— 第二个真相来源只会漂移。规则负责的是「哪些旋钮产生了它」,写进每次构建都打印的 description:mock:能生成,但本生态编不了,示例如实反映
--grpc_out=generate_mock_code=true:<dir>是插件参数而非 protoc 顶层参数 —— 正是消费者不该被要求知道的拼写,所以做成具名选项。生成的 mock 头
#include <gmock/gmock.h>,而compat.gtest只带 googletest、不带 gmock。因此示例声明并产出它、但不 include;plan 阶段断言它进了输出集,CI 再断言文件真的存在。命名:
codegen不改protoc只命名三者之一,且是 protobuf 那一半;buf存在,换生成器就成谎话。codegen指生成代码的运行时导出,C++ 里不存在对应物(生成的桩直接#include <grpcpp/...>并链同一个库)。codegen更站得住:它表达「这个包知道.proto怎么变成 C++,包括你后来加的插件」。真正欠的是文档,已补:codegen 是两步(
--cpp_out是 protobuf 的事,--grpc_out才是 gRPC 的),以及默认关闭的真实理由 —— 官方 Generic API(GenericStub+ByteBuffer)是真实存在的无 codegen 路径,代理/LB 类服务的推荐做法。本机验证
examples/advancedservice = orders.Orders/advanced: OK (messages + stubs, cross-root generation);产出含common/types.pb.*与两个*_mock.grpc.pb.hexamples/greeter(L0)helloworld: OK—— 加法没有改变默认行为examples/helloworldhelloworld: OK