1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
|
# Copyright 2025 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# Generate extra functionality for protobuf messages, including:
# - Serialization to base::DictValue.
# - Stream operator support for C++ printing.
# - (future) equality operator support
# - (future) gtest matchers.
# This does not directly generate the protobuf bindings for any language, so
# callers must include the build target for C++ bindings in the deps.
#
# This is a wrapper around proto_library that adds the proto_extras plugin. It
# is intended to be used in conjunction with proto_library, not on its own.
#
# Conversion to base::Value support:
# - The generated method is `Serialize(const ProtoMessage& message)`, and
# resides in the namespace of the proto message.
# - Include via <name>.to_value.h.
# - Disable via `omit_to_value_serialization` (default is false).
#
# Stream operator support:
# - The generated operator resides in the namespace of the proto message.
# - Include via <name>.ostream.h.
# - Disable via `omit_stream_operators` (default is false).
#
# Equality operator support:
# - The generated operator resides in the namespace of the proto message.
# - Include via <name>.equal.h.
# - Disable via `omit_equality` (default is false).
#
# The proto_extras template supports the following other properties:
# - extras_deps: These are the proto_extras targets for any proto dependencies.
# - sources: The .proto files to generate from.
# - deps: These should be proto_library targets.
# - defines: This is forwarded to all generated targets.
# - visibility: This is forwarded to all generated targets.
#
# The functionality is split up per-file (instead of everything in a 'utils' or
# 'extras' file) as per go/no-utils guidance. This:
# - Helps prevent scope creep of lots of functionality in one file, and
# provides the infrastructure to easily make more functionality in a dedicated file.
# - Reduces header includes and build size.
# - Minimizes the build graph if users only need one piece of functionality.
#
# For cases where the message uses the full google::protobuf::Message type,
# the protobuf_full_support option can be used to ensure the generated code
# with the full protobuf library. Due to android build complications, this also
# requires the `use_fuzzing_engine_with_lpm` build flag to be set.
# This option is relevant for base::Value serialization and equality.
#
# Caveats:
# - Integer types in the proto that are not compatible with base::Value are
# serialized as strings (e.g. uint64_t).
#
# Example:
# proto_extras("foo_extras") {
# sources = [
# "foo.proto",
# ]
#
# deps = [
# ":foo", # The target generating foo.pb.h
# ":foo_dependency", # The target generating foo_dependency.pb.h
# ]
# extras_deps = [
# ":foo_dependency_extras", # The proto_extras target for foo_dependency.
# ]
# omit_to_value_serialization = true # default is false
# omit_stream_operators = true # default is false
# protobuf_full_support = true # default is false
# # Note: This target fails as there is no functionality being generated!
# }
#
# Consumers would then depend on ":foo_extras",
import("//testing/libfuzzer/fuzzer_test.gni")
import("//third_party/protobuf/proto_library.gni")
template("proto_extras") {
_generate_to_value = true
if (defined(invoker.omit_to_value_serialization) &&
invoker.omit_to_value_serialization) {
_generate_to_value = false
}
_generate_ostream = true
if (defined(invoker.omit_stream_operators) &&
!invoker.omit_stream_operators) {
_generate_ostream = false
}
_generate_equality = true
if (defined(invoker.omit_equality) && invoker.omit_equality) {
_generate_equality = false
}
_protobuf_full_support = false
if (defined(invoker.protobuf_full_support) && invoker.protobuf_full_support &&
use_fuzzing_engine_with_lpm) {
_protobuf_full_support = true
}
assert(
!_generate_ostream || _generate_to_value,
"Stream operator generation currently depends on ToValue " +
"serialization. Cannot set omit_to_value_serialization = true if " +
"omit_stream_operators = false. Target: ${target_name}")
assert(_generate_to_value || _generate_ostream || _generate_equality,
"There must be one generation type enabled. Target: ${target_name}")
_extras_deps = []
if (defined(invoker.extras_deps)) {
_extras_deps += invoker.extras_deps
}
_all_targets = []
_to_value_target_name = "${target_name}_to_value"
if (_generate_to_value) {
_all_targets += [ _to_value_target_name ]
proto_library(_to_value_target_name) {
proto_in_dir = "//"
sources = invoker.sources
forward_variables_from(invoker,
[
"deps",
"defines",
"visibility",
])
link_deps = [ "//components/proto_extras:proto_extras_lib" ]
if (_protobuf_full_support) {
link_deps += [ "//components/proto_extras:protobuf_full_support" ]
}
# Link the *_to_value targets for all extras_deps.
_extras_deps_to_value = []
foreach(_dep, _extras_deps) {
_extras_deps_to_value += [ "${_dep}_to_value" ]
}
link_deps += _extras_deps_to_value
generator_plugin_label = "//components/proto_extras:proto_extras_plugin"
generator_plugin_suffix = ".to_value"
generate_cc = false
generate_python = false
link_public_deps = [ "//base" ]
_to_value_options_list = [ "generate_to_value_serialization" ]
if (_protobuf_full_support) {
_to_value_options_list += [ "protobuf_full_support" ]
}
generator_plugin_options = string_join(",", _to_value_options_list)
}
}
if (_generate_ostream) {
_ostream_target_name = "${target_name}_ostream"
_all_targets += [ _ostream_target_name ]
proto_library(_ostream_target_name) {
proto_in_dir = "//"
sources = invoker.sources
forward_variables_from(invoker,
[
"defines",
"visibility",
])
deps = [ ":$_to_value_target_name" ]
if (defined(invoker.deps)) {
deps += invoker.deps
}
link_deps = [ "//base" ]
generator_plugin_label = "//components/proto_extras:proto_extras_plugin"
generator_plugin_suffix = ".ostream"
generate_cc = false
generate_python = false
generator_plugin_options = "generate_stream_operator"
}
}
if (_generate_equality) {
_equality_target_name = "${target_name}_equal"
_all_targets += [ _equality_target_name ]
proto_library(_equality_target_name) {
proto_in_dir = "//"
sources = invoker.sources
forward_variables_from(invoker,
[
"deps",
"defines",
"visibility",
])
# Link the *_to_value targets for all extras_deps.
_extras_deps_equality = []
foreach(_dep, _extras_deps) {
_extras_deps_equality += [ "${_dep}_equal" ]
}
link_deps = _extras_deps_equality
_equality_options_list = [ "generate_equality" ]
if (_protobuf_full_support) {
_equality_options_list += [ "protobuf_full_support" ]
link_deps += [ "//components/proto_extras:protobuf_full_support" ]
}
generator_plugin_label = "//components/proto_extras:proto_extras_plugin"
generator_plugin_suffix = ".equal"
generate_cc = false
generate_python = false
generator_plugin_options = string_join(",", _equality_options_list)
}
}
# Group all generated targets into one group for the target_name.
# Users can techncally depend on the generated target names for a more
# optimized build graph, but it is uncommon in the codebase to not depend on a
# target that is specifically named, so this ensures that the normal behavior
# works.
group(target_name) {
forward_variables_from(invoker,
[
"defines",
"visibility",
])
public_deps = []
foreach(_target, _all_targets) {
public_deps += [ ":$_target" ]
}
}
}
|