
跨平台图形学前端【免费下载链接】engineThe Flutter engine项目地址https://gitcode.com/gh_mirrors/eng/engine点击查看免费下载Protocol Buffersprotobuf是 Flutter Engine 依赖链中重要的序列化基础设施而本仓库的 build/secondary/third_party/protobuf 目录保存的正是让 protobuf 能够被 GN 构建系统完整驱动的胶水层它定义了proto_library模板、protoc 调用封装脚本以及protobuf_lite/protobuf_full/protoc等 GN 目标的构建规则。本文将以该 README 为骨架结合仓库中的 proto_library.gni、BUILD.gn、protoc_wrapper.py 等实现讲清楚这套构建支持的设计动机、目录约定、模板参数以及 protoc 生成链路的底层原理。读完本文你将能在自己的 GN 工程中正确配置 protobuf 依赖、书写proto_library目标并理解生成代码与静态库之间的完整装配流程。一、为什么 protobuf 的 GN 支持要自成一体与普通的第三方库不同protobuf 的 GN 构建支持被设计成一个可被多个大型仓库共享的独立组件。README 明确说明它独立成仓的原因在于需要同时被 Fuchsia 与 Cobalt 两个项目复用这两个项目都使用 GN 作为构建系统也都在各自的依赖树中携带 protobuf因此将GN 如何构建 protobuf这一套规则抽离出来可以避免在每个宿主仓库中重复维护一份几乎相同的构建脚本。在 Flutter Engine 中这套支持被镜像到了build/secondary/third_party/protobuf/目录下目录内包含的 proto_library.gni声明proto_library模板、BUILD.gnprotobuf 库与 protoc 编译器的构建目标、protoc_wrapper.pyprotoc 调用与输出后处理、gen.py自动生成 BUILD.gn 的脚本以及 BUILD.input.gnBUILD.gn 的模板源共同构成了完整的 protobuf 构建支持体系。二、目录布局约定与 secondary_source 机制README 给出了这套支持正常工作的三个前提条件这是任何工程接入时的第一道门槛支持仓位于//build/secondary/third_party/protobuf即当前仓库中的build/secondary/third_party/protobuf目录protobuf 本体位于//third_party/protobuf即 protobuf 的源码含src/google/protobuf/下的实现被检出在third_party/protobuf根目录//.gn中声明secondary_source //build/secondary/这是 GN 提供的secondary_source机制——当 GN 在某个目录找不到.gni文件如proto_library.gni时它会退回secondary_source指定的根目录继续查找。这样任何模块都可以通过import(//third_party/protobuf/proto_library.gni)的方式引用模板而实际文件却存放在build/secondary/下。需要注意的是README 描述的是该组件在 Fuchsia/Cobalt 等宿主仓库中的标准布局。而在当前 Flutter Engine 镜像中BUILD.gn 内部实际引用的 protobuf 源码路径写的是//flutter/third_party/protobuf/src见其protobuf_config与using_proto配置与 BUILD.input.gn 中相对路径src的写法存在差异。从源码结构看这应当是生成脚本在移植到不同宿主仓库时经过了额外的路径改写所致。因此在阅读或移植时务必以自己仓库中 protobuf 的实际检出位置为准保持secondary_source声明、import路径与源码 include 路径三者一致。三、proto_library模板一个.proto文件的完整构建入口proto_library是这套支持的核心模板定义于 proto_library.gni。它接收一个或多个.proto文件自动完成调用 protoc 生成代码 → 将生成代码编译为静态库 → 对外暴露 group 目标的全过程。其最简单的用法只需一个sources参数import(//third_party/protobuf/proto_library.gni) proto_library(mylib) { sources [ foo.proto, ] }模板内部结构可以拆成三个层次${target_name}_protoc_outputsgenerated_file把待生成的输出文件清单写入${target_gen_dir}/${target_name}.protoc_output_info供后续生成动作使用${target_name}_genaction以 protoc_wrapper.py 为脚本携带--protoc、--proto-in-dir、各输出目录参数实际调用 protoc${target_name}_static_libstatic_library收集 action 产出的.h/.cc文件编译为同名静态库若处于 component build 且设置了component_build_force_source_set true则降级为source_set${target_name}group将 action 与静态库聚合作为对外的统一目标供上层deps引用。3.1 核心参数一览根据模板头部注释与实现proto_library支持以下参数带默认值的均为可选参数默认值作用sources必填待编译的.proto文件列表缺失会触发断言proto_in_dir自动推断.proto文件所在目录相对当前 BUILD.gn目录结构由此处起算存在嵌套目录时必须显式指定否则断言报错proto_out_dir自动推断输出文件在root_gen_dir下的后缀路径Python stub 则输出到root_out_dir/pyproto下generate_cctrue生成 C stub*.pb.h/*.pb.ccgenerate_pythontrue生成 Python stub*_pb2.pygenerate_descriptor_setfalse在默认位置${target_out_dir}/${target_name}.desc.pb生成 descriptor set 文件generate_descriptor在 generated files 目录下指定位置生成 descriptor set与上一项互斥generate_gofalse生成 Go stub.pb.gogenerate_go_grpcfalse生成 Go gRPC stub_grpc.pb.go与generate_go互斥cc_generator_options无传给 C 生成器的额外选项如dllexport_declFOO_EXPORT:注意结尾冒号用于给生成的 C 头文件注入导出宏cc_include无需要额外#include的头文件如foo/bar.h配合导出宏使用generator_plugin_label/generator_plugin_script无自定义生成插件前者给 GN label默认 host toolchain后者给脚本路径二者互斥generator_plugin_suffix[es]无插件产出的文件后缀扩展名前支持单个或列表generator_plugin_options无传给插件的额外参数deps无追加的依赖会在 protoc 运行前就绪use_protobuf_full无为true时生成的 C 代码依赖protobuf_full而非protobuf_liteimport_protobuf_full无允许.protoimport protobuf_full 中的.proto文件但不引入其 C 依赖import_dirs无额外的 protoc import 目录列表可重复默认只有proto_in_dirdefines/extra_configs无编译生成代码的 source set 时追加的宏定义与配置3.2 目录推导与输出文件命名当省略proto_in_dir时模板取第一个sources文件的目录作为基准并逐一断言所有源文件都在同一目录否则要求显式声明proto_in_dir。proto_out_dir默认取当前 BUILD.gn 的目录rebase_path(., //)若proto_in_dir非根则继续拼接。最终各语言输出路径的规则为C$root_gen_dir/proto_out_dir/foo.pb.{h,cc}Python$root_out_dir/pyproto/proto_out_dir/foo_pb2.pyGo$root_gen_dir/go-proto-gen/src/proto_out_dir/foo.pb.gogRPC 时为_grpc.pb.go另外模板还通过 depfile 机制保证增量构建的正确性生成 action 的depfile指向${target_gen_dir}/${target_name}.d并将protoc_output_info文件作为--depfile-outputs传给 wrapper使任何.proto或 import 文件的变更都能精确触发重新生成。四、protoc 封装protoc_wrapper.py 的职责protoc_wrapper.py 是 protoc 与 GN 之间的翻译层。它并不只是简单转发命令行而是承担了四项关键职责1. 统一生成器选项格式。FormatGeneratorOptions确保传给--cpp_out/--plugin_out的选项以冒号结尾自动补:符合 protoc 的生成器选项语法。2. 拦截非法命名。VerifyProtoNames会检查所有.proto文件名禁止出现连字符-历史上会导致生成代码命名问题参见仓库注释中引用的 crbug 记录。3. 生成并维护 depfile。ExtractImports递归解析.proto文件中的import/import public语句结合--import-dir找到被引用文件最终写出输出文件: 依赖文件格式的 depfile供 Ninja 做增量判断。4. 注入额外 include。当配置了--include时WriteIncludes会在生成的.pb.h中找到// protoc_insertion_point(includes)标记行protoc 生成代码预留的插入点在其后写入#include ...。这是cc_include/cc_generator_options如导出宏得以生效的机制因此生成的头文件中若找不到该标记会直接报错。此外wrapper 还支持插件模式通过--plugin protoc-gen-plugin路径把自定义生成器注册给 protoc并支持--plugin-depfile系列参数单独维护插件的依赖信息。五、protobuf 库与 protoc 编译器的构建目标BUILD.gn 定义了运行期与编译期所需的所有目标protobuf_litestatic_library轻量运行时编译message_lite.cc、wire_format_lite.cc、coded_stream.cc等约 30 个源文件并对外暴露protobuf_config含GOOGLE_PROTOBUF_NO_RTTI、HAVE_PTHREAD宏与 include 目录与protobuf_warnings一组针对 protobuf 源码的 clang 警告抑制参数兼容至 v3.21.12 版本线。默认的proto_library目标链接的就是它体积更小protobuf_fullstatic_library完整运行时追加descriptor.cc、reflection_ops.cc、text_format.cc、JSON 工具等重型实现deps [:protobuf_lite]。凡.proto未声明option optimize_for LITE_RUNTIME的 C 代码包括 protoc 自身都需要它protoc_libstatic_libraryprotoc 编译器本体command_line_interface.cc、各语言生成器实现不含main()拆成库是为了支持需要链接 libprotoc 的插件protocexecutableprotoc_lib加上compiler/main.cc得到的可执行文件。注意它被包裹在if (current_toolchain host_toolchain)条件中——protoc 永远以 host 工具链构建因为代码生成动作必须运行在编译机上。在 proto_library.gni 的生成 action 中protoc_label同样被显式指定为//third_party/protobuf:protoc($host_toolchain)并且在 protoc_wrapper.py 的设计上刻意绝不使用系统 protocwrapper 注释与 BUILD.gn 的--protoc ./...参数均印证了这一点从而保证跨平台、跨工具链环境下生成代码行为的一致性与可复现性。六、BUILD.gn 的自动生成gen.py 与 BUILD.input.gn值得注意的一个维护细节是BUILD.gn 顶部明确写着 THIS FILE IS GENERATED FROM BUILD.input.gn BY gen.py。也就是说protobuf 库的源文件清单并不靠人工维护开发者只需编辑 BUILD.input.gn把需要自动枚举的位置替换为PROTOBUF_LITE_PUBLIC、PROTOBUF_FULL_PUBLIC、PROTOC_LIB_SOURCES占位符然后运行 gen.py。gen.py 的逻辑很直接借助git ls-files在 protobuf 源码仓库中枚举符合条件的文件如src/google/protobuf/*.h并排除compiler、testing、util等目录再经sed转成带引号的 GN 列表替换模板占位符最后调用gn format格式化后写入 BUILD.gn。这保证了升级 protobuf 版本后库的编译清单能自动与上游源码对齐避免手写清单漏文件或引用已删除文件。七、在 Flutter Engine 中接入这套支持的关键要点综合上述实现在类似 Flutter Engine 这样的 GN 工程中使用 protobuf 构建支持需要注意以下几点三处路径必须对齐//.gn的secondary_source、proto_library.gni的实际存放目录、protobuf 源码的检出位置任何一处错位都会导致 import 或 include 失败默认用protobuf_lite控制体积只有确实需要完整反射、TextFormat 等能力的.proto如 protoc 自身才设置use_protobuf_full仅需 import 上游.proto定义时用import_protobuf_full避免拖入完整 C 依赖代码生成动作绑定 host toolchainprotoc 与自定义插件generator_plugin_label自动追加($host_toolchain)都以 host 工具链构建输出目录通过rebase_path统一换算跨平台含 Windows 的.exe后缀均有处理利用 depfile 保证增量wrapper 会递归收集.proto的 import 依赖并生成 depfile因此只要改动被 import 的.proto相关目标就会被准确重跑命名与后处理约束.proto文件名禁止连字符若需为生成头文件注入额外 include如导出宏依赖的正是 wrapper 对// protoc_insertion_point(includes)插入点的改写逻辑。对于希望深挖的读者建议以 proto_library.gni 的模板主体为起点对照 protoc_wrapper.py 的命令行参数逐一还原 protoc 的真实调用形态再回到 BUILD.gn 查看protobuf_lite与protobuf_full的源码装配差异即可完整掌握从.proto定义到链接进最终二进制的全链路。赞分享跨平台图形学前端【免费下载链接】engineThe Flutter engine项目地址https://gitcode.com/gh_mirrors/eng/engine点击查看免费下载相关推荐Apiato实战案例如何构建一个完整的电商API系统Apiato实战案例如何构建一个完整的电商API系统 Apiato是一个基于Laravel构建的PHP框架专门用于创建可扩展、可测试的API中心化应用程序。后端软件架构Protocol Buffersprotobuf构建与安装指南Bazel/Bzlmod 集成、protoc 编译与多语言运行时部署实战Protocol Buffersprotobuf构建与安装指南Bazel/Bzlmod 集成、protoc 编译与多语言运行时部署实战 Protocol数据库文档数据库后端终极指南深入理解protoc-gen-doc插件架构与Google Protocol Buffers文档生成原理终极指南深入理解protoc gen doc插件架构与Google Protocol Buffers文档生成原理 protoc gen doc是一款强大的Do开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考