Skip to content

feat: 让依赖的 kind="bin" 产物可被消费者访问(codegen 工具链缺口) #355

Description

@speak-agent

问题

一个包无法把自己构建出的可执行文件交给它的消费者。 这让"库自带代码生成器"这一在 C++ 生态里非常普遍的形态在 mcpp 中无法表达。

具体场景:gRPC

mcpplibs.grpc(grpc-m)刚进入 mcpp-index。gRPC 的使用必然涉及代码生成——用户写 .proto,需要 protocgrpc_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@Xmcpplibs.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 保持一致。

需要设计决策的点

  1. 触发构建的条件。 依赖的 bin target 默认不该被构建(多数消费者不需要 grpc_cpp_plugin,而它意味着额外的 libprotoc ~157 TU)。可能的形态:消费者显式声明需要哪个 bin,或由 feature 门控。

  2. 交叉编译下必须是 HOST 产物。 这一条我认为是本需求里最关键的语义:build.mcpp 按文档"运行在 host 上,即使 --target 是别的三元组"。代码生成器同理——mcpp build --target x86_64-windows-gnu 时,protoc 必须是能在当前机器上跑的 host 二进制,而不是 target 二进制。所以这不能简单复用普通依赖的构建产物,需要一条"为 host 构建该依赖的 bin"的路径(概念上接近 CMake 的 IMPORTED host tool 或 Bazel 的 exec configuration)。

  3. [xlings] deps 的边界。 我理解 [xlings] deps 面向的是外部宿主工具(make/cmake/nasm),而本需求是索引内的包自己产出的工具。两者不重叠,不过文档里值得写清楚各自的适用面。

影响面

在这条落地之前,任何"自带代码生成器"的库都只能退回到"签入生成产物"或"另外维护一个 xim 工具包"。对 gRPC 这种 codegen 是必经步骤的库,这直接决定了用户体验:今天用户要么用签入的示例产物,要么自备 protoc + grpc_cpp_plugin 并自行保证版本匹配。

参考:mcpplibs/grpc-m 的 README「Code generation」一节记录了当前的临时做法与原因。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions