问题
一个包无法把自己构建出的可执行文件交给它的消费者。 这让"库自带代码生成器"这一在 C++ 生态里非常普遍的形态在 mcpp 中无法表达。
具体场景:gRPC
mcpplibs.grpc(grpc-m)刚进入 mcpp-index。gRPC 的使用必然涉及代码生成——用户写 .proto,需要 protoc 与 grpc_cpp_plugin 生成 *.pb.cc / *.grpc.pb.cc 再编译。这两个工具本身就是 gRPC/protobuf 源码树里的 bin target,包在构建时完全有能力产出它们。
但消费者拿不到:
build.mcpp 本身是完全够用的(mcpp::generated() / mcpp::include_dir() 都在),唯一缺的就是"工具在哪"。
生态惯例:库自带工具
这不是 gRPC 特有的需求,C++ 生态在这一点上相当一致:
| 生态 |
形态 |
| CMake / vcpkg / Conan |
find_package(Protobuf) 导出 imported executable target protobuf::protoc;gRPC 导出 gRPC::grpc_cpp_plugin |
| Bazel |
@com_google_protobuf//:protoc 就是一个可依赖的构建目标 |
| Rust / prost-build |
早期依赖系统 protoc,现已自带;方向是往"库自带"走 |
npm grpc-tools |
直接分发 protoc + 插件二进制 |
同类需求不止 gRPC:flatbuffers(flatc)、Qt(moc/uic/rcc)、protobuf 单独使用、任何自带 IDL 编译器的库,都是同一个形状。
现有替代方案为什么不够
1. 把生成产物签入仓库(grpc-m 目前的做法)
对模板和 example 合适——开箱即用、零工具依赖。但对真实用户不成立:他们的 .proto 会改,每次都要手工重新生成,而且必须自己保证 protoc 版本与包里的 protobuf 运行时严格匹配(版本错配是运行期而非编译期错误)。
2. 做成 xim: 预编译包 + [xlings] deps
可行(与 xim:make / xim:cmake / xim:nasm 同一层),但代价明显:
- 需要为每个工具单独打包、托管、跨三平台维护——而这些二进制本来就能由包自己构建出来;
- 版本会漂:
xim:grpc-tools@X 与 mcpplibs.grpc@Y 是两个独立的版本轴,用户可能装出不匹配的组合,而 protoc 与 protobuf 运行时版本错配正是最难查的一类问题;
- 语义上也不对:它把"这个库的一部分"变成了"一个宿主工具"。
建议的形态
与 MCPP_DEP_<NAME>_DIR 对称:
// build.mcpp
import mcpp;
int main() {
auto protoc = mcpp::dep_bin("protobuf", "protoc");
auto plugin = mcpp::dep_bin("grpc", "grpc_cpp_plugin");
// 调用它们生成 *.pb.cc,然后
mcpp::generated("gen/helloworld.pb.cc");
mcpp::generated("gen/helloworld.grpc.pb.cc");
mcpp::include_dir("gen");
}
对应环境变量 MCPP_DEP_<SANITIZED_NAME>_BIN_DIR,沿用 MCPP_FEATURE_ 那套 sanitizer,与 #241 保持一致。
需要设计决策的点
-
触发构建的条件。 依赖的 bin target 默认不该被构建(多数消费者不需要 grpc_cpp_plugin,而它意味着额外的 libprotoc ~157 TU)。可能的形态:消费者显式声明需要哪个 bin,或由 feature 门控。
-
交叉编译下必须是 HOST 产物。 这一条我认为是本需求里最关键的语义:build.mcpp 按文档"运行在 host 上,即使 --target 是别的三元组"。代码生成器同理——mcpp build --target x86_64-windows-gnu 时,protoc 必须是能在当前机器上跑的 host 二进制,而不是 target 二进制。所以这不能简单复用普通依赖的构建产物,需要一条"为 host 构建该依赖的 bin"的路径(概念上接近 CMake 的 IMPORTED host tool 或 Bazel 的 exec configuration)。
-
与 [xlings] deps 的边界。 我理解 [xlings] deps 面向的是外部宿主工具(make/cmake/nasm),而本需求是索引内的包自己产出的工具。两者不重叠,不过文档里值得写清楚各自的适用面。
影响面
在这条落地之前,任何"自带代码生成器"的库都只能退回到"签入生成产物"或"另外维护一个 xim 工具包"。对 gRPC 这种 codegen 是必经步骤的库,这直接决定了用户体验:今天用户要么用签入的示例产物,要么自备 protoc + grpc_cpp_plugin 并自行保证版本匹配。
参考:mcpplibs/grpc-m 的 README「Code generation」一节记录了当前的临时做法与原因。
问题
一个包无法把自己构建出的可执行文件交给它的消费者。 这让"库自带代码生成器"这一在 C++ 生态里非常普遍的形态在 mcpp 中无法表达。
具体场景:gRPC
mcpplibs.grpc(grpc-m)刚进入 mcpp-index。gRPC 的使用必然涉及代码生成——用户写.proto,需要protoc与grpc_cpp_plugin生成*.pb.cc/*.grpc.pb.cc再编译。这两个工具本身就是 gRPC/protobuf 源码树里的bintarget,包在构建时完全有能力产出它们。但消费者拿不到:
mcpp::dep_dir("grpc")/MCPP_DEP_<NAME>_DIR([enhancement] build.mcpp 契约补 MCPP_DEP_<NAME>_DIR(依赖 payload 路径;数据资产包场景) #241)给的是依赖的源码/payload 目录,不是构建产物目录;kind = "bin"target 不会为消费者构建,也没有任何途径定位。build.mcpp本身是完全够用的(mcpp::generated()/mcpp::include_dir()都在),唯一缺的就是"工具在哪"。生态惯例:库自带工具
这不是 gRPC 特有的需求,C++ 生态在这一点上相当一致:
find_package(Protobuf)导出 imported executable targetprotobuf::protoc;gRPC 导出gRPC::grpc_cpp_plugin@com_google_protobuf//:protoc就是一个可依赖的构建目标grpc-tools同类需求不止 gRPC:flatbuffers(
flatc)、Qt(moc/uic/rcc)、protobuf 单独使用、任何自带 IDL 编译器的库,都是同一个形状。现有替代方案为什么不够
1. 把生成产物签入仓库(grpc-m 目前的做法)
对模板和 example 合适——开箱即用、零工具依赖。但对真实用户不成立:他们的
.proto会改,每次都要手工重新生成,而且必须自己保证 protoc 版本与包里的 protobuf 运行时严格匹配(版本错配是运行期而非编译期错误)。2. 做成
xim:预编译包 +[xlings] deps可行(与
xim:make/xim:cmake/xim:nasm同一层),但代价明显:xim:grpc-tools@X与mcpplibs.grpc@Y是两个独立的版本轴,用户可能装出不匹配的组合,而 protoc 与 protobuf 运行时版本错配正是最难查的一类问题;建议的形态
与
MCPP_DEP_<NAME>_DIR对称:对应环境变量
MCPP_DEP_<SANITIZED_NAME>_BIN_DIR,沿用MCPP_FEATURE_那套 sanitizer,与 #241 保持一致。需要设计决策的点
触发构建的条件。 依赖的
bintarget 默认不该被构建(多数消费者不需要 grpc_cpp_plugin,而它意味着额外的 libprotoc ~157 TU)。可能的形态:消费者显式声明需要哪个 bin,或由 feature 门控。交叉编译下必须是 HOST 产物。 这一条我认为是本需求里最关键的语义:
build.mcpp按文档"运行在 host 上,即使--target是别的三元组"。代码生成器同理——mcpp build --target x86_64-windows-gnu时,protoc 必须是能在当前机器上跑的 host 二进制,而不是 target 二进制。所以这不能简单复用普通依赖的构建产物,需要一条"为 host 构建该依赖的 bin"的路径(概念上接近 CMake 的IMPORTEDhost tool 或 Bazel 的 exec configuration)。与
[xlings] deps的边界。 我理解[xlings] deps面向的是外部宿主工具(make/cmake/nasm),而本需求是索引内的包自己产出的工具。两者不重叠,不过文档里值得写清楚各自的适用面。影响面
在这条落地之前,任何"自带代码生成器"的库都只能退回到"签入生成产物"或"另外维护一个 xim 工具包"。对 gRPC 这种 codegen 是必经步骤的库,这直接决定了用户体验:今天用户要么用签入的示例产物,要么自备 protoc + grpc_cpp_plugin 并自行保证版本匹配。
参考:
mcpplibs/grpc-m的 README「Code generation」一节记录了当前的临时做法与原因。