build: Move cc_proto_descriptor_library into api/ (#29837)
Signed-off-by: Ryan Hamilton <rch@google.com> Mirrored from https://github.com/envoyproxy/envoy @ 56c88dd4ad33a611a2e622edf7eb3dbaadb59ce1main
parent
5e2e6438f4
commit
2e958bf11a
19 changed files with 1263 additions and 1 deletions
@ -0,0 +1,66 @@ |
|||||||
|
licenses(["notice"]) # Apache 2 |
||||||
|
|
||||||
|
cc_library( |
||||||
|
name = "file_descriptor_info", |
||||||
|
hdrs = ["file_descriptor_info.h"], |
||||||
|
visibility = ["//visibility:public"], |
||||||
|
deps = [ |
||||||
|
"@com_google_absl//absl/strings", |
||||||
|
], |
||||||
|
) |
||||||
|
|
||||||
|
cc_library( |
||||||
|
name = "text_format_transcoder", |
||||||
|
srcs = [ |
||||||
|
"create_dynamic_message.h", |
||||||
|
"text_format_transcoder.cc", |
||||||
|
], |
||||||
|
hdrs = ["text_format_transcoder.h"], |
||||||
|
visibility = ["//visibility:public"], |
||||||
|
deps = [ |
||||||
|
":file_descriptor_info", |
||||||
|
"@com_google_absl//absl/memory", |
||||||
|
"@com_google_absl//absl/strings", |
||||||
|
"@com_google_absl//absl/strings:str_format", |
||||||
|
"@com_google_protobuf//:protobuf", |
||||||
|
], |
||||||
|
) |
||||||
|
|
||||||
|
cc_library( |
||||||
|
name = "file_descriptor_generator_lib", |
||||||
|
srcs = ["file_descriptor_generator.cc"], |
||||||
|
hdrs = ["file_descriptor_generator.h"], |
||||||
|
deps = [ |
||||||
|
"@com_google_absl//absl/strings", |
||||||
|
"@com_google_absl//absl/strings:str_format", |
||||||
|
"@com_google_protobuf//:protobuf", |
||||||
|
"@com_google_protobuf//src/google/protobuf/compiler:code_generator", |
||||||
|
"@com_google_protobuf//src/google/protobuf/compiler:retention", |
||||||
|
], |
||||||
|
) |
||||||
|
|
||||||
|
cc_binary( |
||||||
|
name = "file_descriptor_generator", |
||||||
|
srcs = ["file_descriptor_generator_main.cc"], |
||||||
|
visibility = ["//visibility:public"], |
||||||
|
deps = [ |
||||||
|
":file_descriptor_generator_lib", |
||||||
|
"@com_google_protobuf//:protobuf", |
||||||
|
"@com_google_protobuf//:protoc_lib", |
||||||
|
"@com_google_protobuf//src/google/protobuf/compiler:code_generator", |
||||||
|
], |
||||||
|
) |
||||||
|
|
||||||
|
cc_library( |
||||||
|
name = "create_dynamic_message", |
||||||
|
srcs = ["create_dynamic_message.cc"], |
||||||
|
hdrs = ["create_dynamic_message.h"], |
||||||
|
visibility = ["//visibility:public"], |
||||||
|
deps = [ |
||||||
|
":text_format_transcoder", |
||||||
|
"@com_google_absl//absl/memory", |
||||||
|
"@com_google_absl//absl/strings", |
||||||
|
"@com_google_absl//absl/strings:str_format", |
||||||
|
"@com_google_protobuf//:protobuf", |
||||||
|
], |
||||||
|
) |
@ -0,0 +1,349 @@ |
|||||||
|
"""Public rules for using cc proto descriptor library with protos: |
||||||
|
- cc_proto_descriptor_library() |
||||||
|
""" |
||||||
|
|
||||||
|
load("@bazel_skylib//lib:paths.bzl", "paths") |
||||||
|
|
||||||
|
# begin:google_only |
||||||
|
# load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain", "use_cpp_toolchain") |
||||||
|
# end:google_only |
||||||
|
|
||||||
|
# begin:github_only |
||||||
|
# Compatibility code for Bazel 4.x. Remove this when we drop support for Bazel 4.x. |
||||||
|
load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain") |
||||||
|
|
||||||
|
def use_cpp_toolchain(): |
||||||
|
return ["@bazel_tools//tools/cpp:toolchain_type"] |
||||||
|
|
||||||
|
# end:github_only |
||||||
|
|
||||||
|
# Generic support code ######################################################### |
||||||
|
|
||||||
|
# begin:github_only |
||||||
|
_is_google3 = False |
||||||
|
# end:github_only |
||||||
|
|
||||||
|
# begin:google_only |
||||||
|
# _is_google3 = True |
||||||
|
# end:google_only |
||||||
|
|
||||||
|
def _get_real_short_path(file): |
||||||
|
# For some reason, files from other archives have short paths that look like: |
||||||
|
# ../com_google_protobuf/google/protobuf/descriptor.proto |
||||||
|
short_path = file.short_path |
||||||
|
if short_path.startswith("../"): |
||||||
|
second_slash = short_path.index("/", 3) |
||||||
|
short_path = short_path[second_slash + 1:] |
||||||
|
|
||||||
|
# Sometimes it has another few prefixes like: |
||||||
|
# _virtual_imports/any_proto/google/protobuf/any.proto |
||||||
|
# benchmarks/_virtual_imports/100_msgs_proto/benchmarks/100_msgs.proto |
||||||
|
# We want just google/protobuf/any.proto. |
||||||
|
virtual_imports = "_virtual_imports/" |
||||||
|
if virtual_imports in short_path: |
||||||
|
short_path = short_path.split(virtual_imports)[1].split("/", 1)[1] |
||||||
|
return short_path |
||||||
|
|
||||||
|
def _get_real_root(ctx, file): |
||||||
|
real_short_path = _get_real_short_path(file) |
||||||
|
root = file.path[:-len(real_short_path) - 1] |
||||||
|
|
||||||
|
if not _is_google3 and ctx.rule.attr.strip_import_prefix: |
||||||
|
root = paths.join(root, ctx.rule.attr.strip_import_prefix[1:]) |
||||||
|
return root |
||||||
|
|
||||||
|
def _generate_output_file(ctx, src, extension): |
||||||
|
package = ctx.label.package |
||||||
|
if not _is_google3: |
||||||
|
strip_import_prefix = ctx.rule.attr.strip_import_prefix |
||||||
|
if strip_import_prefix and strip_import_prefix != "/": |
||||||
|
if not package.startswith(strip_import_prefix[1:]): |
||||||
|
fail("%s does not begin with prefix %s" % (package, strip_import_prefix)) |
||||||
|
package = package[len(strip_import_prefix):] |
||||||
|
|
||||||
|
real_short_path = _get_real_short_path(src) |
||||||
|
real_short_path = paths.relativize(real_short_path, package) |
||||||
|
output_filename = paths.replace_extension(real_short_path, extension) |
||||||
|
ret = ctx.actions.declare_file(output_filename) |
||||||
|
return ret |
||||||
|
|
||||||
|
def _generate_include_path(src, out, extension): |
||||||
|
short_path = _get_real_short_path(src) |
||||||
|
short_path = paths.replace_extension(short_path, extension) |
||||||
|
if not out.path.endswith(short_path): |
||||||
|
fail("%s does not end with %s" % (out.path, short_path)) |
||||||
|
|
||||||
|
return out.path[:-len(short_path)] |
||||||
|
|
||||||
|
def _filter_none(elems): |
||||||
|
out = [] |
||||||
|
for elem in elems: |
||||||
|
if elem: |
||||||
|
out.append(elem) |
||||||
|
return out |
||||||
|
|
||||||
|
def _cc_library_func(ctx, name, hdrs, srcs, copts, includes, dep_ccinfos): |
||||||
|
"""Like cc_library(), but callable from rules. |
||||||
|
|
||||||
|
Args: |
||||||
|
ctx: Rule context. |
||||||
|
name: Unique name used to generate output files. |
||||||
|
hdrs: Public headers that can be #included from other rules. |
||||||
|
srcs: C/C++ source files. |
||||||
|
copts: Additional options for cc compilation. |
||||||
|
includes: Additional include paths. |
||||||
|
dep_ccinfos: CcInfo providers of dependencies we should build/link against. |
||||||
|
|
||||||
|
Returns: |
||||||
|
CcInfo provider for this compilation. |
||||||
|
""" |
||||||
|
|
||||||
|
compilation_contexts = [info.compilation_context for info in dep_ccinfos] |
||||||
|
linking_contexts = [info.linking_context for info in dep_ccinfos] |
||||||
|
toolchain = find_cpp_toolchain(ctx) |
||||||
|
feature_configuration = cc_common.configure_features( |
||||||
|
ctx = ctx, |
||||||
|
cc_toolchain = toolchain, |
||||||
|
requested_features = ctx.features, |
||||||
|
unsupported_features = ctx.disabled_features, |
||||||
|
) |
||||||
|
|
||||||
|
blaze_only_args = {} |
||||||
|
|
||||||
|
if _is_google3: |
||||||
|
blaze_only_args["grep_includes"] = ctx.file._grep_includes |
||||||
|
|
||||||
|
(compilation_context, compilation_outputs) = cc_common.compile( |
||||||
|
actions = ctx.actions, |
||||||
|
feature_configuration = feature_configuration, |
||||||
|
cc_toolchain = toolchain, |
||||||
|
name = name, |
||||||
|
srcs = srcs, |
||||||
|
includes = includes, |
||||||
|
public_hdrs = hdrs, |
||||||
|
user_compile_flags = copts, |
||||||
|
compilation_contexts = compilation_contexts, |
||||||
|
**blaze_only_args |
||||||
|
) |
||||||
|
|
||||||
|
# buildifier: disable=unused-variable |
||||||
|
(linking_context, linking_outputs) = cc_common.create_linking_context_from_compilation_outputs( |
||||||
|
actions = ctx.actions, |
||||||
|
name = name, |
||||||
|
feature_configuration = feature_configuration, |
||||||
|
cc_toolchain = toolchain, |
||||||
|
compilation_outputs = compilation_outputs, |
||||||
|
linking_contexts = linking_contexts, |
||||||
|
disallow_dynamic_library = cc_common.is_enabled(feature_configuration = feature_configuration, feature_name = "targets_windows"), |
||||||
|
**blaze_only_args |
||||||
|
) |
||||||
|
|
||||||
|
return CcInfo( |
||||||
|
compilation_context = compilation_context, |
||||||
|
linking_context = linking_context, |
||||||
|
) |
||||||
|
|
||||||
|
# Dummy rule to expose select() copts to aspects ############################## |
||||||
|
|
||||||
|
CcProtoDescriptorLibraryCoptsInfo = provider( |
||||||
|
"Provides copts for cc proto descriptor library targets", |
||||||
|
fields = { |
||||||
|
"copts": "copts for cc_proto_descriptor_library()", |
||||||
|
}, |
||||||
|
) |
||||||
|
|
||||||
|
def cc_proto_descriptor_library_copts_impl(ctx): |
||||||
|
return CcProtoDescriptorLibraryCoptsInfo(copts = ctx.attr.copts) |
||||||
|
|
||||||
|
cc_proto_descriptor_library_copts = rule( |
||||||
|
implementation = cc_proto_descriptor_library_copts_impl, |
||||||
|
attrs = {"copts": attr.string_list(default = [])}, |
||||||
|
) |
||||||
|
|
||||||
|
# cc_proto_descriptor_library shared code ################# |
||||||
|
|
||||||
|
GeneratedSrcsInfo = provider( |
||||||
|
"Provides generated headers and sources", |
||||||
|
fields = { |
||||||
|
"srcs": "list of srcs", |
||||||
|
"hdrs": "list of hdrs", |
||||||
|
"includes": "list of extra includes", |
||||||
|
}, |
||||||
|
) |
||||||
|
|
||||||
|
CcProtoDescriptorWrappedCcInfo = provider("Provider for cc_info for protos", fields = ["cc_info"]) |
||||||
|
_CcProtoDescriptorWrappedGeneratedSrcsInfo = provider("Provider for generated sources", fields = ["srcs"]) |
||||||
|
|
||||||
|
def _compile_protos(ctx, generator, proto_info, proto_sources): |
||||||
|
if len(proto_sources) == 0: |
||||||
|
return GeneratedSrcsInfo(srcs = [], hdrs = [], includes = []) |
||||||
|
|
||||||
|
ext = "_" + generator |
||||||
|
tool = getattr(ctx.executable, "_gen_" + generator) |
||||||
|
srcs = [_generate_output_file(ctx, name, ext + ".pb.cc") for name in proto_sources] |
||||||
|
hdrs = [_generate_output_file(ctx, name, ext + ".pb.h") for name in proto_sources] |
||||||
|
transitive_sets = proto_info.transitive_descriptor_sets.to_list() |
||||||
|
|
||||||
|
args = ctx.actions.args() |
||||||
|
args.use_param_file(param_file_arg = "@%s") |
||||||
|
args.set_param_file_format("multiline") |
||||||
|
|
||||||
|
args.add("--" + generator + "_out=" + _get_real_root(ctx, srcs[0])) |
||||||
|
args.add("--plugin=protoc-gen-" + generator + "=" + tool.path) |
||||||
|
args.add("--descriptor_set_in=" + ctx.configuration.host_path_separator.join([f.path for f in transitive_sets])) |
||||||
|
args.add_all(proto_sources, map_each = _get_real_short_path) |
||||||
|
|
||||||
|
ctx.actions.run( |
||||||
|
inputs = depset( |
||||||
|
direct = [proto_info.direct_descriptor_set], |
||||||
|
transitive = [proto_info.transitive_descriptor_sets], |
||||||
|
), |
||||||
|
tools = [tool], |
||||||
|
outputs = srcs + hdrs, |
||||||
|
executable = ctx.executable._protoc, |
||||||
|
arguments = [args], |
||||||
|
progress_message = "Generating descriptor protos for :" + ctx.label.name, |
||||||
|
mnemonic = "GenDescriptorProtos", |
||||||
|
) |
||||||
|
return GeneratedSrcsInfo( |
||||||
|
srcs = srcs, |
||||||
|
hdrs = hdrs, |
||||||
|
includes = [_generate_include_path(proto_sources[0], hdrs[0], "_descriptor.pb.h")], |
||||||
|
) |
||||||
|
|
||||||
|
def _cc_proto_descriptor_rule_impl(ctx): |
||||||
|
if len(ctx.attr.deps) != 1: |
||||||
|
fail("only one deps dependency allowed.") |
||||||
|
dep = ctx.attr.deps[0] |
||||||
|
|
||||||
|
if _CcProtoDescriptorWrappedGeneratedSrcsInfo in dep: |
||||||
|
srcs = dep[_CcProtoDescriptorWrappedGeneratedSrcsInfo].srcs |
||||||
|
else: |
||||||
|
fail("proto_library rule must generate _CcProtoDescriptorWrappedGeneratedSrcsInfo " + |
||||||
|
"(aspect should have handled this).") |
||||||
|
|
||||||
|
if CcProtoDescriptorWrappedCcInfo in dep: |
||||||
|
cc_info = dep[CcProtoDescriptorWrappedCcInfo].cc_info |
||||||
|
else: |
||||||
|
fail("proto_library rule must generate CcProtoDescriptorWrappedCcInfo " + |
||||||
|
"(aspect should have handled this).") |
||||||
|
|
||||||
|
lib = cc_info.linking_context.linker_inputs.to_list()[0].libraries[0] |
||||||
|
files = _filter_none([ |
||||||
|
lib.static_library, |
||||||
|
lib.pic_static_library, |
||||||
|
lib.dynamic_library, |
||||||
|
]) |
||||||
|
return [ |
||||||
|
DefaultInfo(files = depset(files + srcs.hdrs + srcs.srcs)), |
||||||
|
srcs, |
||||||
|
cc_info, |
||||||
|
] |
||||||
|
|
||||||
|
def _cc_proto_descriptor_aspect_impl(target, ctx, generator, cc_provider, file_provider, provide_cc_shared_library_hints = True): |
||||||
|
proto_info = target[ProtoInfo] |
||||||
|
files = _compile_protos(ctx, generator, proto_info, proto_info.direct_sources) |
||||||
|
deps = ctx.rule.attr.deps + getattr(ctx.attr, "_" + generator) |
||||||
|
dep_ccinfos = [dep[CcInfo] for dep in deps if CcInfo in dep] |
||||||
|
dep_ccinfos += [dep[CcProtoDescriptorWrappedCcInfo].cc_info for dep in deps if CcProtoDescriptorWrappedCcInfo in dep] |
||||||
|
name = ctx.rule.attr.name + "." + generator |
||||||
|
owners = [ctx.label.relative(name)] |
||||||
|
cc_info = _cc_library_func( |
||||||
|
ctx = ctx, |
||||||
|
name = name, |
||||||
|
hdrs = files.hdrs, |
||||||
|
srcs = files.srcs, |
||||||
|
includes = files.includes, |
||||||
|
#copts = ctx.attr._copts[CcProtoDescriptorLibraryCoptsInfo].copts, |
||||||
|
copts = [], |
||||||
|
dep_ccinfos = dep_ccinfos, |
||||||
|
) |
||||||
|
|
||||||
|
wrapped_cc_info = cc_provider( |
||||||
|
cc_info = cc_info, |
||||||
|
) |
||||||
|
providers = [ |
||||||
|
wrapped_cc_info, |
||||||
|
file_provider(srcs = files), |
||||||
|
] |
||||||
|
if provide_cc_shared_library_hints: |
||||||
|
if hasattr(cc_common, "CcSharedLibraryHintInfo"): |
||||||
|
providers.append(cc_common.CcSharedLibraryHintInfo(owners = owners)) |
||||||
|
elif hasattr(cc_common, "CcSharedLibraryHintInfo_6_X_constructor_do_not_use"): |
||||||
|
# This branch can be deleted once 6.X is not supported by rules |
||||||
|
providers.append(cc_common.CcSharedLibraryHintInfo_6_X_constructor_do_not_use(owners = owners)) |
||||||
|
return providers |
||||||
|
|
||||||
|
def cc_proto_descriptor_library_aspect_impl(target, ctx): |
||||||
|
return _cc_proto_descriptor_aspect_impl(target, ctx, "descriptor", CcProtoDescriptorWrappedCcInfo, _CcProtoDescriptorWrappedGeneratedSrcsInfo) |
||||||
|
|
||||||
|
def _maybe_add(d): |
||||||
|
if _is_google3: |
||||||
|
d["_grep_includes"] = attr.label( |
||||||
|
allow_single_file = True, |
||||||
|
cfg = "exec", |
||||||
|
default = "@bazel_tools//tools/cpp:grep-includes", |
||||||
|
) |
||||||
|
return d |
||||||
|
|
||||||
|
# cc_proto_descriptor_library() ########################################################## |
||||||
|
|
||||||
|
def _get_cc_proto_descriptor_library_aspect_provides(): |
||||||
|
provides = [ |
||||||
|
CcProtoDescriptorWrappedCcInfo, |
||||||
|
_CcProtoDescriptorWrappedGeneratedSrcsInfo, |
||||||
|
] |
||||||
|
|
||||||
|
if hasattr(cc_common, "CcSharedLibraryHintInfo"): |
||||||
|
provides.append(cc_common.CcSharedLibraryHintInfo) |
||||||
|
elif hasattr(cc_common, "CcSharedLibraryHintInfo_6_X_getter_do_not_use"): |
||||||
|
# This branch can be deleted once 6.X is not supported by upb rules |
||||||
|
provides.append(cc_common.CcSharedLibraryHintInfo_6_X_getter_do_not_use) |
||||||
|
|
||||||
|
return provides |
||||||
|
|
||||||
|
cc_proto_descriptor_library_aspect = aspect( |
||||||
|
attrs = _maybe_add({ |
||||||
|
#"_copts": attr.label( |
||||||
|
# default = "//:upb_proto_library_copts__for_generated_code_only_do_not_use", |
||||||
|
#), |
||||||
|
"_gen_descriptor": attr.label( |
||||||
|
executable = True, |
||||||
|
cfg = "exec", |
||||||
|
default = "//bazel/cc_proto_descriptor_library:file_descriptor_generator", |
||||||
|
), |
||||||
|
"_protoc": attr.label( |
||||||
|
executable = True, |
||||||
|
cfg = "exec", |
||||||
|
default = "@com_google_protobuf//:protoc", |
||||||
|
), |
||||||
|
"_cc_toolchain": attr.label( |
||||||
|
default = "@bazel_tools//tools/cpp:current_cc_toolchain", |
||||||
|
), |
||||||
|
"_descriptor": attr.label_list( |
||||||
|
default = [ |
||||||
|
Label("//bazel/cc_proto_descriptor_library:file_descriptor_info"), |
||||||
|
Label("@com_google_absl//absl/base:core_headers"), |
||||||
|
], |
||||||
|
), |
||||||
|
}), |
||||||
|
implementation = cc_proto_descriptor_library_aspect_impl, |
||||||
|
provides = _get_cc_proto_descriptor_library_aspect_provides(), |
||||||
|
attr_aspects = ["deps"], |
||||||
|
fragments = ["cpp"], |
||||||
|
toolchains = use_cpp_toolchain(), |
||||||
|
incompatible_use_toolchain_transition = True, |
||||||
|
) |
||||||
|
|
||||||
|
cc_proto_descriptor_library = rule( |
||||||
|
output_to_genfiles = True, |
||||||
|
implementation = _cc_proto_descriptor_rule_impl, |
||||||
|
attrs = { |
||||||
|
"deps": attr.label_list( |
||||||
|
aspects = [cc_proto_descriptor_library_aspect], |
||||||
|
allow_rules = ["proto_library"], |
||||||
|
providers = [ProtoInfo], |
||||||
|
), |
||||||
|
}, |
||||||
|
provides = [CcInfo], |
||||||
|
) |
@ -0,0 +1,31 @@ |
|||||||
|
#include "bazel/cc_proto_descriptor_library/create_dynamic_message.h" |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
|
||||||
|
#include "absl/memory/memory.h" |
||||||
|
#include "absl/strings/escaping.h" |
||||||
|
#include "absl/strings/str_format.h" |
||||||
|
#include "bazel/cc_proto_descriptor_library/text_format_transcoder.h" |
||||||
|
#include "google/protobuf/descriptor.h" |
||||||
|
#include "google/protobuf/descriptor.pb.h" |
||||||
|
#include "google/protobuf/dynamic_message.h" |
||||||
|
#include "google/protobuf/message.h" |
||||||
|
|
||||||
|
// NOLINT(namespace-envoy)
|
||||||
|
namespace cc_proto_descriptor_library { |
||||||
|
|
||||||
|
std::unique_ptr<google::protobuf::Message> |
||||||
|
createDynamicMessage(const TextFormatTranscoder& transcoder, |
||||||
|
const google::protobuf::MessageLite& message, |
||||||
|
google::protobuf::io::ErrorCollector* error_collector /*= nullptr*/) { |
||||||
|
auto dynamic_message = |
||||||
|
transcoder.createEmptyDynamicMessage(message.GetTypeName(), error_collector); |
||||||
|
|
||||||
|
if (dynamic_message) { |
||||||
|
dynamic_message->ParsePartialFromString(message.SerializePartialAsString()); |
||||||
|
} |
||||||
|
|
||||||
|
return dynamic_message; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace cc_proto_descriptor_library
|
@ -0,0 +1,26 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
|
||||||
|
#include "google/protobuf/io/tokenizer.h" |
||||||
|
#include "google/protobuf/io/zero_copy_stream.h" |
||||||
|
#include "google/protobuf/io/zero_copy_stream_impl_lite.h" |
||||||
|
#include "google/protobuf/message.h" |
||||||
|
#include "google/protobuf/message_lite.h" |
||||||
|
|
||||||
|
// NOLINT(namespace-envoy)
|
||||||
|
namespace cc_proto_descriptor_library { |
||||||
|
|
||||||
|
// Forward declare to make the friendship handshake easier.
|
||||||
|
class TextFormatTranscoder; |
||||||
|
|
||||||
|
// Creates a DynamicMessage based off the passed in MessageLite. DynamicMessage
|
||||||
|
// gives you access to the Descriptor for the message. The message must have
|
||||||
|
// been registered with the passed in TextFormatTranscoder. Returned messages
|
||||||
|
// cannot outlive transcoder.
|
||||||
|
std::unique_ptr<google::protobuf::Message> |
||||||
|
createDynamicMessage(const TextFormatTranscoder& transcoder, |
||||||
|
const google::protobuf::MessageLite& message, |
||||||
|
google::protobuf::io::ErrorCollector* error_collector = nullptr); |
||||||
|
|
||||||
|
} // namespace cc_proto_descriptor_library
|
@ -0,0 +1,176 @@ |
|||||||
|
#include "bazel/cc_proto_descriptor_library/file_descriptor_generator.h" |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
#include <sstream> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include "absl/strings/escaping.h" |
||||||
|
#include "absl/strings/str_cat.h" |
||||||
|
#include "absl/strings/str_format.h" |
||||||
|
#include "absl/strings/str_replace.h" |
||||||
|
#include "absl/strings/str_split.h" |
||||||
|
#include "absl/strings/string_view.h" |
||||||
|
#include "absl/strings/strip.h" |
||||||
|
#include "google/protobuf/compiler/retention.h" |
||||||
|
#include "google/protobuf/descriptor.pb.h" |
||||||
|
#include "google/protobuf/io/coded_stream.h" |
||||||
|
#include "google/protobuf/io/zero_copy_stream.h" |
||||||
|
|
||||||
|
// NOLINT(namespace-envoy)
|
||||||
|
namespace cc_proto_descriptor_library { |
||||||
|
|
||||||
|
namespace { |
||||||
|
|
||||||
|
absl::string_view getFileBaseName(const google::protobuf::FileDescriptor* file) { |
||||||
|
absl::string_view stripped_name = file->name(); |
||||||
|
if (absl::ConsumeSuffix(&stripped_name, ".proto")) { |
||||||
|
return stripped_name; |
||||||
|
} else if (absl::ConsumeSuffix(&stripped_name, ".protodevel")) { |
||||||
|
return stripped_name; |
||||||
|
} |
||||||
|
return stripped_name; |
||||||
|
} |
||||||
|
|
||||||
|
std::string getDescriptorHeaderName(const google::protobuf::FileDescriptor* file) { |
||||||
|
return absl::StrCat(getFileBaseName(file), "_descriptor.pb.h"); |
||||||
|
} |
||||||
|
|
||||||
|
std::string getDescriptorSourceName(const google::protobuf::FileDescriptor* file) { |
||||||
|
return absl::StrCat(getFileBaseName(file), "_descriptor.pb.cc"); |
||||||
|
} |
||||||
|
|
||||||
|
std::string getDescriptorNamespace(const google::protobuf::FileDescriptor* file) { |
||||||
|
return absl::AsciiStrToLower( |
||||||
|
absl::StrReplaceAll(getFileBaseName(file), {{"/", "_"}, {"-", "_"}})); |
||||||
|
} |
||||||
|
|
||||||
|
std::string getDependencyFileDescriptorInfoSymbol(const google::protobuf::FileDescriptor* file) { |
||||||
|
return absl::StrFormat("%s::kFileDescriptorInfo", getDescriptorNamespace(file)); |
||||||
|
} |
||||||
|
|
||||||
|
std::string getDependencyFileDescriptorHeaderGuard(const google::protobuf::FileDescriptor* file) { |
||||||
|
std::string header_path = getDescriptorHeaderName(file); |
||||||
|
return absl::AsciiStrToUpper( |
||||||
|
absl::StrReplaceAll(header_path, {{"/", "_"}, {".", "_"}, {"-", "_"}})); |
||||||
|
} |
||||||
|
|
||||||
|
bool generateHeader(const google::protobuf::FileDescriptor* file, |
||||||
|
google::protobuf::io::ZeroCopyOutputStream* output_stream) { |
||||||
|
auto header_guard = getDependencyFileDescriptorHeaderGuard(file); |
||||||
|
auto unique_namespace = getDescriptorNamespace(file); |
||||||
|
|
||||||
|
std::stringstream contents; |
||||||
|
contents << absl::StrFormat(R"text( |
||||||
|
#ifndef %s |
||||||
|
#define %s |
||||||
|
|
||||||
|
#include "absl/base/attributes.h" |
||||||
|
|
||||||
|
namespace cc_proto_descriptor_library { |
||||||
|
namespace internal { |
||||||
|
|
||||||
|
struct FileDescriptorInfo; |
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace cc_proto_descriptor_library
|
||||||
|
|
||||||
|
namespace protobuf { |
||||||
|
namespace reflection { |
||||||
|
namespace %s { |
||||||
|
|
||||||
|
extern const ::cc_proto_descriptor_library::internal::FileDescriptorInfo kFileDescriptorInfo; |
||||||
|
} // namespace %s
|
||||||
|
} // namespace reflection
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
#endif // %s
|
||||||
|
|
||||||
|
)text", |
||||||
|
header_guard, header_guard, unique_namespace, unique_namespace, |
||||||
|
header_guard); |
||||||
|
|
||||||
|
google::protobuf::io::CodedOutputStream output(output_stream); |
||||||
|
output.WriteString(contents.str()); |
||||||
|
output.Trim(); |
||||||
|
return !output.HadError(); |
||||||
|
} |
||||||
|
|
||||||
|
bool generateSource(const google::protobuf::FileDescriptor* file, |
||||||
|
google::protobuf::io::ZeroCopyOutputStream* output_stream) { |
||||||
|
auto unique_namespace = getDescriptorNamespace(file); |
||||||
|
std::stringstream contents; |
||||||
|
|
||||||
|
contents << absl::StrFormat("#include \"%s\"\n", getDescriptorHeaderName(file)); |
||||||
|
for (int i = 0; i < file->dependency_count(); ++i) { |
||||||
|
contents << absl::StrFormat("#include \"%s\"\n", getDescriptorHeaderName(file->dependency(i))); |
||||||
|
} |
||||||
|
contents << R"text(#include "bazel/cc_proto_descriptor_library/file_descriptor_info.h" |
||||||
|
)text"; |
||||||
|
|
||||||
|
google::protobuf::FileDescriptorProto file_descriptor_proto = |
||||||
|
google::protobuf::compiler::StripSourceRetentionOptions(*file); |
||||||
|
|
||||||
|
contents << absl::StrFormat( |
||||||
|
R"text( |
||||||
|
namespace protobuf { |
||||||
|
namespace reflection { |
||||||
|
namespace %s { |
||||||
|
|
||||||
|
)text", |
||||||
|
unique_namespace); |
||||||
|
|
||||||
|
contents << "static const" |
||||||
|
"::cc_proto_descriptor_library::internal::" |
||||||
|
"FileDescriptorInfo* kDeps[] = {\n"; |
||||||
|
for (int i = 0; i < file->dependency_count(); ++i) { |
||||||
|
contents << absl::StrFormat("&%s,\n", |
||||||
|
getDependencyFileDescriptorInfoSymbol(file->dependency(i))); |
||||||
|
} |
||||||
|
contents << "nullptr};\n"; |
||||||
|
|
||||||
|
contents << absl::StrFormat( |
||||||
|
R"text( |
||||||
|
|
||||||
|
const ::cc_proto_descriptor_library::internal::FileDescriptorInfo kFileDescriptorInfo{ |
||||||
|
"%s", |
||||||
|
"%s", |
||||||
|
kDeps |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace %s
|
||||||
|
} // namespace reflection
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
)text", |
||||||
|
file->name(), absl::Base64Escape(file_descriptor_proto.SerializeAsString()), |
||||||
|
unique_namespace); |
||||||
|
|
||||||
|
google::protobuf::io::CodedOutputStream output(output_stream); |
||||||
|
output.WriteString(contents.str()); |
||||||
|
output.Trim(); |
||||||
|
return !output.HadError(); |
||||||
|
} |
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool ProtoDescriptorGenerator::Generate( // NOLINT(readability-identifier-naming)
|
||||||
|
const google::protobuf::FileDescriptor* file, const std::string& parameter, |
||||||
|
google::protobuf::compiler::GeneratorContext* generator_context, std::string* error) const { |
||||||
|
std::string header_path = getDescriptorHeaderName(file); |
||||||
|
std::string source_path = getDescriptorSourceName(file); |
||||||
|
|
||||||
|
std::unique_ptr<google::protobuf::io::ZeroCopyOutputStream> header_output( |
||||||
|
generator_context->Open(header_path)); |
||||||
|
std::unique_ptr<google::protobuf::io::ZeroCopyOutputStream> source_output( |
||||||
|
generator_context->Open(source_path)); |
||||||
|
|
||||||
|
if (!generateHeader(file, header_output.get())) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (!generateSource(file, source_output.get())) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace cc_proto_descriptor_library
|
@ -0,0 +1,17 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include "google/protobuf/compiler/code_generator.h" |
||||||
|
|
||||||
|
// NOLINT(namespace-envoy)
|
||||||
|
namespace cc_proto_descriptor_library { |
||||||
|
|
||||||
|
class ProtoDescriptorGenerator : public google::protobuf::compiler::CodeGenerator { |
||||||
|
public: |
||||||
|
bool Generate(const google::protobuf::FileDescriptor* file, const std::string& parameter, |
||||||
|
google::protobuf::compiler::GeneratorContext* generator_context, |
||||||
|
std::string* error) const override; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace cc_proto_descriptor_library
|
@ -0,0 +1,10 @@ |
|||||||
|
#include <cstdio> |
||||||
|
|
||||||
|
#include "bazel/cc_proto_descriptor_library/file_descriptor_generator.h" |
||||||
|
#include "google/protobuf/compiler/plugin.h" |
||||||
|
|
||||||
|
// NOLINT(namespace-envoy)
|
||||||
|
int main(int argc, char* argv[]) { |
||||||
|
cc_proto_descriptor_library::ProtoDescriptorGenerator generator; |
||||||
|
return google::protobuf::compiler::PluginMain(argc, argv, &generator); |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
#include "bazel/cc_proto_descriptor_library/file_descriptor_info.h" |
||||||
|
|
||||||
|
#include "absl/strings/escaping.h" |
||||||
|
#include "google/protobuf/descriptor.h" |
||||||
|
#include "net/proto2/proto/descriptor.proto.h" |
||||||
|
|
||||||
|
// NOLINT(namespace-envoy)
|
||||||
|
namespace cc_proto_descriptor_library { |
||||||
|
namespace internal { |
||||||
|
|
||||||
|
void loadFileDescriptors(const FileDescriptorInfo& descriptor_info, |
||||||
|
google::protobuf::DescriptorPool* descriptor_pool) { |
||||||
|
if (descriptor_pool->FindFileByName(descriptor_info.file_name)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
for (int i = 0; descriptor_info.deps[i] != nullptr; ++i) { |
||||||
|
loadFileDescriptors(*descriptor_info.deps[i], descriptor_pool); |
||||||
|
} |
||||||
|
|
||||||
|
google::protobuf::FileDescriptorProto file_descriptor_proto; |
||||||
|
std::string file_descriptor_bytes; |
||||||
|
absl::Base64Unescape(descriptor_info.file_descriptor_bytes_base64, &file_descriptor_bytes); |
||||||
|
file_descriptor_proto.ParseFromString(file_descriptor_bytes); |
||||||
|
descriptor_pool->BuildFile(file_descriptor_proto); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace cc_proto_descriptor_library
|
@ -0,0 +1,21 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "absl/strings/string_view.h" |
||||||
|
|
||||||
|
// NOLINT(namespace-envoy)
|
||||||
|
namespace cc_proto_descriptor_library { |
||||||
|
namespace internal { |
||||||
|
|
||||||
|
struct FileDescriptorInfo final { |
||||||
|
// Path of the file this object represents.
|
||||||
|
const absl::string_view file_name; |
||||||
|
// The bytes of the FileDescriptorProto for this proto file.
|
||||||
|
const absl::string_view file_descriptor_bytes_base64; |
||||||
|
// File descriptors that this file is dependent on.
|
||||||
|
// TODO(b/143243097): absl::Span requires special casing to deal with zero
|
||||||
|
// length arrays and constexpr.
|
||||||
|
const FileDescriptorInfo** const deps; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace cc_proto_descriptor_library
|
@ -0,0 +1,135 @@ |
|||||||
|
load("//bazel/cc_proto_descriptor_library:builddefs.bzl", "cc_proto_descriptor_library") |
||||||
|
|
||||||
|
licenses(["notice"]) # Apache 2 |
||||||
|
|
||||||
|
# Tests for go/cc_proto_descriptor_library. |
||||||
|
# These need to be in a separate directory to test visibility restrictions. |
||||||
|
|
||||||
|
proto_library( |
||||||
|
name = "test_proto", |
||||||
|
testonly = True, |
||||||
|
srcs = ["test.proto"], |
||||||
|
visibility = ["//visibility:private"], |
||||||
|
deps = [ |
||||||
|
"@com_google_protobuf//:any_proto", |
||||||
|
], |
||||||
|
) |
||||||
|
|
||||||
|
proto_library( |
||||||
|
name = "test1_proto", |
||||||
|
testonly = True, |
||||||
|
srcs = ["test1.proto"], |
||||||
|
visibility = ["//visibility:private"], |
||||||
|
deps = [ |
||||||
|
":test_proto", |
||||||
|
"@com_google_protobuf//:any_proto", |
||||||
|
], |
||||||
|
) |
||||||
|
|
||||||
|
proto_library( |
||||||
|
name = "test_extension_proto", |
||||||
|
testonly = True, |
||||||
|
# Purprosely a dash to make sure they're handled properly in filenames |
||||||
|
srcs = ["test-extension.proto"], |
||||||
|
visibility = ["//visibility:private"], |
||||||
|
deps = [ |
||||||
|
":test_proto", |
||||||
|
"@com_google_protobuf//:any_proto", |
||||||
|
], |
||||||
|
) |
||||||
|
|
||||||
|
cc_proto_library( |
||||||
|
name = "test_cc_proto", |
||||||
|
testonly = True, |
||||||
|
visibility = ["//visibility:private"], |
||||||
|
deps = ["test_proto"], |
||||||
|
) |
||||||
|
|
||||||
|
cc_proto_library( |
||||||
|
name = "test1_cc_proto", |
||||||
|
testonly = True, |
||||||
|
visibility = ["//visibility:private"], |
||||||
|
deps = ["test1_proto"], |
||||||
|
) |
||||||
|
|
||||||
|
cc_proto_library( |
||||||
|
name = "test_extension_cc_proto", |
||||||
|
testonly = True, |
||||||
|
visibility = ["//visibility:private"], |
||||||
|
deps = ["test_extension_proto"], |
||||||
|
) |
||||||
|
|
||||||
|
cc_proto_descriptor_library( |
||||||
|
name = "test_descriptors", |
||||||
|
testonly = True, |
||||||
|
visibility = ["//visibility:private"], |
||||||
|
deps = [ |
||||||
|
":test_proto", |
||||||
|
], |
||||||
|
) |
||||||
|
|
||||||
|
cc_proto_descriptor_library( |
||||||
|
name = "test1_descriptors", |
||||||
|
testonly = True, |
||||||
|
visibility = ["//visibility:private"], |
||||||
|
deps = [ |
||||||
|
":test1_proto", |
||||||
|
], |
||||||
|
) |
||||||
|
|
||||||
|
cc_proto_descriptor_library( |
||||||
|
name = "test_extension_descriptors", |
||||||
|
testonly = True, |
||||||
|
visibility = ["//visibility:private"], |
||||||
|
deps = [ |
||||||
|
":test_extension_proto", |
||||||
|
], |
||||||
|
) |
||||||
|
|
||||||
|
cc_test( |
||||||
|
name = "text_format_transcoder_test", |
||||||
|
srcs = [ |
||||||
|
"text_format_transcoder_test.cc", |
||||||
|
], |
||||||
|
visibility = ["//visibility:private"], |
||||||
|
deps = [ |
||||||
|
"test1_descriptors", |
||||||
|
"test_cc_proto", |
||||||
|
"test_descriptors", |
||||||
|
"test_extension_cc_proto", |
||||||
|
"test_extension_descriptors", |
||||||
|
"//bazel/cc_proto_descriptor_library:text_format_transcoder", |
||||||
|
"@com_google_googletest//:gtest_main", |
||||||
|
"@com_google_protobuf//:protobuf", |
||||||
|
], |
||||||
|
) |
||||||
|
|
||||||
|
cc_test( |
||||||
|
name = "global_fallback_test", |
||||||
|
srcs = [ |
||||||
|
"global_fallback_test.cc", |
||||||
|
], |
||||||
|
visibility = ["//visibility:private"], |
||||||
|
deps = [ |
||||||
|
"test_cc_proto", |
||||||
|
"//bazel/cc_proto_descriptor_library:text_format_transcoder", |
||||||
|
"@com_google_googletest//:gtest_main", |
||||||
|
"@com_google_protobuf//:protobuf", |
||||||
|
], |
||||||
|
) |
||||||
|
|
||||||
|
cc_test( |
||||||
|
name = "create_dynamic_message_test", |
||||||
|
srcs = [ |
||||||
|
"create_dynamic_message_test.cc", |
||||||
|
], |
||||||
|
visibility = ["//visibility:private"], |
||||||
|
deps = [ |
||||||
|
"test_cc_proto", |
||||||
|
"test_descriptors", |
||||||
|
"//bazel/cc_proto_descriptor_library:create_dynamic_message", |
||||||
|
"//bazel/cc_proto_descriptor_library:text_format_transcoder", |
||||||
|
"@com_google_googletest//:gtest_main", |
||||||
|
"@com_google_protobuf//:protobuf", |
||||||
|
], |
||||||
|
) |
@ -0,0 +1,38 @@ |
|||||||
|
#include "bazel/cc_proto_descriptor_library/create_dynamic_message.h" |
||||||
|
#include "bazel/cc_proto_descriptor_library/testdata/test.pb.h" |
||||||
|
#include "bazel/cc_proto_descriptor_library/testdata/test_descriptor.pb.h" |
||||||
|
#include "bazel/cc_proto_descriptor_library/text_format_transcoder.h" |
||||||
|
#include "gmock/gmock.h" |
||||||
|
#include "gtest/gtest.h" |
||||||
|
|
||||||
|
// NOLINT(namespace-envoy)
|
||||||
|
namespace { |
||||||
|
|
||||||
|
using ::testing::Eq; |
||||||
|
using ::testing::NotNull; |
||||||
|
using ::testing::Test; |
||||||
|
|
||||||
|
TEST(TextFormatTranscoderTest, CreateDynamicMessage) { |
||||||
|
cc_proto_descriptor_library::TextFormatTranscoder reserializer; |
||||||
|
reserializer.loadFileDescriptors( |
||||||
|
protobuf::reflection::bazel_cc_proto_descriptor_library_testdata_test::kFileDescriptorInfo); |
||||||
|
|
||||||
|
testdata::dynamic_descriptors::Foo concrete_message; |
||||||
|
concrete_message.set_bar("hello world"); |
||||||
|
auto dynamic_message = |
||||||
|
cc_proto_descriptor_library::createDynamicMessage(reserializer, concrete_message); |
||||||
|
ASSERT_THAT(dynamic_message, NotNull()); |
||||||
|
|
||||||
|
// Access the descriptor.
|
||||||
|
auto descriptor = dynamic_message->GetDescriptor(); |
||||||
|
ASSERT_THAT(descriptor, NotNull()); |
||||||
|
ASSERT_THAT(descriptor->full_name(), Eq(concrete_message.GetTypeName())); |
||||||
|
|
||||||
|
// Access reflection.
|
||||||
|
auto reflection = dynamic_message->GetReflection(); |
||||||
|
const auto bar_feld_descriptor = descriptor->FindFieldByName("bar"); |
||||||
|
ASSERT_THAT(bar_feld_descriptor, NotNull()); |
||||||
|
ASSERT_THAT(reflection->GetString(*dynamic_message, bar_feld_descriptor), Eq("hello world")); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace
|
@ -0,0 +1,41 @@ |
|||||||
|
// Copyright 2019 Google Inc. All Rights Reserved.
|
||||||
|
// Author: kmensah@google.com (Kwasi Mensah)
|
||||||
|
|
||||||
|
#include "bazel/cc_proto_descriptor_library/testdata/test.pb.h" |
||||||
|
#include "bazel/cc_proto_descriptor_library/text_format_transcoder.h" |
||||||
|
#include "gmock/gmock.h" |
||||||
|
#include "google/protobuf/descriptor.h" |
||||||
|
#include "google/protobuf/text_format.h" |
||||||
|
#include "gtest/gtest.h" |
||||||
|
|
||||||
|
// NOLINT(namespace-envoy)
|
||||||
|
namespace { |
||||||
|
|
||||||
|
using ::testing::Eq; |
||||||
|
using ::testing::Test; |
||||||
|
|
||||||
|
TEST(TextFormatTranscoderTest, GlobalFallbackAllowed) { |
||||||
|
cc_proto_descriptor_library::TextFormatTranscoder reserializer( |
||||||
|
/*allow_global_fallback=*/true); |
||||||
|
|
||||||
|
testdata::dynamic_descriptors::Foo concrete_message; |
||||||
|
ASSERT_TRUE(reserializer.parseInto(R"text( |
||||||
|
bar: "hello world" |
||||||
|
)text", |
||||||
|
&concrete_message)); |
||||||
|
|
||||||
|
ASSERT_THAT(concrete_message.bar(), Eq("hello world")); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(TextFormatTranscoderTest, GlobalFallbackDisallowed) { |
||||||
|
cc_proto_descriptor_library::TextFormatTranscoder reserializer( |
||||||
|
/*allow_global_fallback=*/false); |
||||||
|
|
||||||
|
testdata::dynamic_descriptors::Foo concrete_message; |
||||||
|
ASSERT_FALSE(reserializer.parseInto(R"text( |
||||||
|
bar: "hello world" |
||||||
|
)text", |
||||||
|
&concrete_message)); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace
|
@ -0,0 +1,9 @@ |
|||||||
|
syntax = "proto2"; |
||||||
|
|
||||||
|
package testdata.dynamic_descriptors; |
||||||
|
|
||||||
|
import "bazel/cc_proto_descriptor_library/testdata/test.proto"; |
||||||
|
|
||||||
|
extend Foo { |
||||||
|
optional int64 number = 11; |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
syntax = "proto2"; |
||||||
|
|
||||||
|
import "google/protobuf/any.proto"; |
||||||
|
|
||||||
|
package testdata.dynamic_descriptors; |
||||||
|
|
||||||
|
message Foo { |
||||||
|
optional string bar = 1; |
||||||
|
optional google.protobuf.Any baz = 2; |
||||||
|
|
||||||
|
extensions 10 to 20; |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
syntax = "proto2"; |
||||||
|
|
||||||
|
package testdata.dynamic_descriptors; |
||||||
|
|
||||||
|
message FooCopy { |
||||||
|
optional string bar = 1; |
||||||
|
|
||||||
|
extensions 10 to 20; |
||||||
|
} |
@ -0,0 +1,100 @@ |
|||||||
|
// Copyright 2019 Google Inc. All Rights Reserved.
|
||||||
|
// Author: kmensah@google.com (Kwasi Mensah)
|
||||||
|
|
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include "absl/strings/substitute.h" |
||||||
|
#include "bazel/cc_proto_descriptor_library/testdata/test-extension.pb.h" |
||||||
|
#include "bazel/cc_proto_descriptor_library/testdata/test-extension_descriptor.pb.h" |
||||||
|
#include "bazel/cc_proto_descriptor_library/testdata/test.pb.h" |
||||||
|
#include "bazel/cc_proto_descriptor_library/testdata/test1_descriptor.pb.h" |
||||||
|
#include "bazel/cc_proto_descriptor_library/testdata/test_descriptor.pb.h" |
||||||
|
#include "bazel/cc_proto_descriptor_library/text_format_transcoder.h" |
||||||
|
#include "gmock/gmock.h" |
||||||
|
#include "google/protobuf/descriptor.h" |
||||||
|
#include "google/protobuf/dynamic_message.h" |
||||||
|
#include "google/protobuf/text_format.h" |
||||||
|
#include "gtest/gtest.h" |
||||||
|
|
||||||
|
// NOLINT(namespace-envoy)
|
||||||
|
namespace { |
||||||
|
|
||||||
|
using google::protobuf::io::ColumnNumber; |
||||||
|
|
||||||
|
class StringErrorCollector : public google::protobuf::io::ErrorCollector { |
||||||
|
public: |
||||||
|
// String error_text is unowned and must remain valid during the use of
|
||||||
|
// StringErrorCollector.
|
||||||
|
// If one_indexing is set to true, all line and column numbers will be
|
||||||
|
// increased by one for cases when provided indices are 0-indexed and
|
||||||
|
// 1-indexed error messages are desired.
|
||||||
|
// If ignore_warnings is set to true, warnings are discarded.
|
||||||
|
explicit StringErrorCollector(std::string& error_text, bool one_indexing = false, |
||||||
|
bool ignore_warnings = false); |
||||||
|
|
||||||
|
// google::protobuf::io::ErrorCollector:
|
||||||
|
void AddError(int line, ColumnNumber column, const std::string& message) override; |
||||||
|
void AddWarning(int line, ColumnNumber column, const std::string& message) override; |
||||||
|
|
||||||
|
private: |
||||||
|
std::string& error_text_; |
||||||
|
const int index_offset_; |
||||||
|
const bool ignore_warnings_; |
||||||
|
}; |
||||||
|
|
||||||
|
StringErrorCollector::StringErrorCollector(std::string& error_text, bool one_indexing, |
||||||
|
bool ignore_warnings) |
||||||
|
: error_text_(error_text), index_offset_(one_indexing ? 1 : 0), |
||||||
|
ignore_warnings_(ignore_warnings) {} |
||||||
|
|
||||||
|
void StringErrorCollector::AddError(int line, ColumnNumber column, const std::string& message) { |
||||||
|
absl::SubstituteAndAppend(&error_text_, "$0($1): $2\n", line + index_offset_, |
||||||
|
column + index_offset_, message); |
||||||
|
} |
||||||
|
|
||||||
|
void StringErrorCollector::AddWarning(int line, ColumnNumber column, const std::string& message) { |
||||||
|
if (ignore_warnings_) { |
||||||
|
return; |
||||||
|
} |
||||||
|
AddError(line, column, message); |
||||||
|
} |
||||||
|
|
||||||
|
using ::testing::Eq; |
||||||
|
using ::testing::Test; |
||||||
|
|
||||||
|
TEST(TextFormatTranscoderTest, TextFormatWorks) { |
||||||
|
cc_proto_descriptor_library::TextFormatTranscoder reserializer; |
||||||
|
reserializer.loadFileDescriptors( |
||||||
|
protobuf::reflection::bazel_cc_proto_descriptor_library_testdata_test::kFileDescriptorInfo); |
||||||
|
|
||||||
|
testdata::dynamic_descriptors::Foo concrete_message; |
||||||
|
ASSERT_TRUE(reserializer.parseInto(R"text( |
||||||
|
bar: "hello world" |
||||||
|
)text", |
||||||
|
&concrete_message)); |
||||||
|
|
||||||
|
ASSERT_THAT(concrete_message.bar(), Eq("hello world")); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(TextToBinaryReserializerTest, TextFormatWithExtensionWorks) { |
||||||
|
cc_proto_descriptor_library::TextFormatTranscoder reserializer; |
||||||
|
const auto& file_descriptor_info = protobuf::reflection:: |
||||||
|
bazel_cc_proto_descriptor_library_testdata_test_extension::kFileDescriptorInfo; |
||||||
|
reserializer.loadFileDescriptors(file_descriptor_info); |
||||||
|
|
||||||
|
std::string error_text; |
||||||
|
StringErrorCollector error_collector(error_text); |
||||||
|
testdata::dynamic_descriptors::Foo concrete_message; |
||||||
|
ASSERT_TRUE(reserializer.parseInto(R"text( |
||||||
|
bar: "hello world" |
||||||
|
[testdata.dynamic_descriptors.number]: 20 |
||||||
|
)text", |
||||||
|
&concrete_message, &error_collector)); |
||||||
|
|
||||||
|
ASSERT_THAT(error_text, Eq("")); |
||||||
|
|
||||||
|
ASSERT_THAT(concrete_message.bar(), Eq("hello world")); |
||||||
|
ASSERT_THAT(concrete_message.GetExtension(testdata::dynamic_descriptors::number), Eq(20)); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace
|
@ -0,0 +1,128 @@ |
|||||||
|
#include "bazel/cc_proto_descriptor_library/text_format_transcoder.h" |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include "absl/memory/memory.h" |
||||||
|
#include "absl/strings/escaping.h" |
||||||
|
#include "absl/strings/str_format.h" |
||||||
|
#include "absl/strings/string_view.h" |
||||||
|
#include "bazel/cc_proto_descriptor_library/file_descriptor_info.h" |
||||||
|
#include "google/protobuf/descriptor.h" |
||||||
|
#include "google/protobuf/descriptor.pb.h" |
||||||
|
#include "google/protobuf/dynamic_message.h" |
||||||
|
#include "google/protobuf/message.h" |
||||||
|
#include "google/protobuf/text_format.h" |
||||||
|
|
||||||
|
// NOLINT(namespace-envoy)
|
||||||
|
namespace cc_proto_descriptor_library { |
||||||
|
|
||||||
|
struct TextFormatTranscoder::InternalData { |
||||||
|
google::protobuf::DescriptorPool descriptor_pool; |
||||||
|
google::protobuf::DynamicMessageFactory message_factory; |
||||||
|
}; |
||||||
|
|
||||||
|
TextFormatTranscoder::TextFormatTranscoder(bool allow_global_fallback /*= true*/) |
||||||
|
: internals_(std::make_unique<InternalData>()), allow_global_fallback_(allow_global_fallback) {} |
||||||
|
|
||||||
|
// Needs to be defined here so std::unique_ptr doesn't complain about
|
||||||
|
// TextToBinaryProtoReserializerInternals being incomplete in it's
|
||||||
|
// destructor.
|
||||||
|
TextFormatTranscoder::~TextFormatTranscoder() = default; |
||||||
|
|
||||||
|
bool TextFormatTranscoder::parseInto(absl::string_view text_format, |
||||||
|
google::protobuf::MessageLite* msg, |
||||||
|
google::protobuf::io::ErrorCollector* error_collector) const { |
||||||
|
google::protobuf::io::ArrayInputStream input(text_format.data(), text_format.size()); |
||||||
|
return parseInto(&input, msg, error_collector); |
||||||
|
} |
||||||
|
|
||||||
|
bool TextFormatTranscoder::parseInto(google::protobuf::io::ZeroCopyInputStream* input_stream, |
||||||
|
google::protobuf::MessageLite* msg, |
||||||
|
google::protobuf::io::ErrorCollector* error_collector) const { |
||||||
|
std::string serialization; |
||||||
|
|
||||||
|
if (!toBinarySerializationInternal(msg->GetTypeName(), input_stream, &serialization, |
||||||
|
error_collector)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return msg->ParseFromString(serialization); |
||||||
|
} |
||||||
|
|
||||||
|
void TextFormatTranscoder::loadFileDescriptors( |
||||||
|
const internal::FileDescriptorInfo& file_descriptor_info) { |
||||||
|
if (internals_->descriptor_pool.FindFileByName(std::string(file_descriptor_info.file_name))) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
for (int i = 0; file_descriptor_info.deps[i] != nullptr; ++i) { |
||||||
|
loadFileDescriptors(*file_descriptor_info.deps[i]); |
||||||
|
} |
||||||
|
|
||||||
|
google::protobuf::FileDescriptorProto file_descriptor_proto; |
||||||
|
std::string file_descriptor_bytes; |
||||||
|
absl::Base64Unescape(file_descriptor_info.file_descriptor_bytes_base64, &file_descriptor_bytes); |
||||||
|
file_descriptor_proto.ParseFromString(file_descriptor_bytes); |
||||||
|
internals_->descriptor_pool.BuildFile(file_descriptor_proto); |
||||||
|
} |
||||||
|
|
||||||
|
bool TextFormatTranscoder::toBinarySerializationInternal( |
||||||
|
absl::string_view type_name, google::protobuf::io::ZeroCopyInputStream* input_stream, |
||||||
|
std::string* binary_serializtion, google::protobuf::io::ErrorCollector* error_collector) const { |
||||||
|
auto dynamic_message = createEmptyDynamicMessage(type_name, error_collector); |
||||||
|
if (!dynamic_message) { |
||||||
|
// createEmptyDynamicMessage already would have written to the
|
||||||
|
// error_collector.
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// TextFormat defaults to using the DescriptorPool of the passed in
|
||||||
|
// Message*. So we don't have to do anything special with TextFormat::Finder
|
||||||
|
// for it to use our DescriptorPool.
|
||||||
|
google::protobuf::TextFormat::Parser parser; |
||||||
|
parser.RecordErrorsTo(error_collector); |
||||||
|
if (!parser.Parse(input_stream, dynamic_message.get())) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return dynamic_message->SerializePartialToString(binary_serializtion); |
||||||
|
} |
||||||
|
|
||||||
|
std::unique_ptr<google::protobuf::Message> TextFormatTranscoder::createEmptyDynamicMessage( |
||||||
|
absl::string_view type_name, google::protobuf::io::ErrorCollector* error_collector) const { |
||||||
|
const google::protobuf::Descriptor* descriptor = |
||||||
|
internals_->descriptor_pool.FindMessageTypeByName(std::string(type_name)); |
||||||
|
// If you're built with the full runtime then embedding the descriptors and
|
||||||
|
// loading them would be information duplicated by the global descriptor
|
||||||
|
// pool which hurts builds like superroot that are near all the blaze/forge
|
||||||
|
// size limits. Teams that care about not silently falling into this fallback
|
||||||
|
// case should make sure their tests are also run in mobile configurations or
|
||||||
|
// set allow_global_fallback to false in TextFormatTranscoder's constructor.
|
||||||
|
if (allow_global_fallback_ && (descriptor == nullptr)) { |
||||||
|
const auto generated_pool = google::protobuf::DescriptorPool::generated_pool(); |
||||||
|
if (generated_pool != nullptr) { |
||||||
|
descriptor = generated_pool->FindMessageTypeByName(std::string(type_name)); |
||||||
|
} |
||||||
|
} |
||||||
|
if (descriptor == nullptr) { |
||||||
|
if (error_collector) { |
||||||
|
error_collector->RecordError(0, 0, |
||||||
|
absl::StrFormat("Could not find descriptor for: %s", type_name)); |
||||||
|
} |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
auto dynamic_message = |
||||||
|
absl::WrapUnique(internals_->message_factory.GetPrototype(descriptor)->New()); |
||||||
|
if (!dynamic_message) { |
||||||
|
if (error_collector) { |
||||||
|
error_collector->RecordError( |
||||||
|
0, 0, absl::StrFormat("Could not create dynamic message for: %s", type_name)); |
||||||
|
} |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
return dynamic_message; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace cc_proto_descriptor_library
|
@ -0,0 +1,65 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include "absl/strings/string_view.h" |
||||||
|
#include "bazel/cc_proto_descriptor_library/create_dynamic_message.h" |
||||||
|
#include "google/protobuf/io/tokenizer.h" |
||||||
|
#include "google/protobuf/io/zero_copy_stream.h" |
||||||
|
#include "google/protobuf/io/zero_copy_stream_impl_lite.h" |
||||||
|
#include "google/protobuf/message.h" |
||||||
|
|
||||||
|
// NOLINT(namespace-envoy)
|
||||||
|
namespace cc_proto_descriptor_library { |
||||||
|
namespace internal { |
||||||
|
|
||||||
|
struct FileDescriptorInfo; |
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// Takes a text formatted proto and translates it to a MessageLite proto so it
|
||||||
|
// can be used by the lite runtime. Requires the that text formatted protos and
|
||||||
|
// all of its extensions are registered with this object.
|
||||||
|
class TextFormatTranscoder final { |
||||||
|
public: |
||||||
|
explicit TextFormatTranscoder( |
||||||
|
// Whether to allow using the global descriptor pool as a fallback. See
|
||||||
|
// text_format_transcoder.cc for more information.
|
||||||
|
bool allow_global_fallback = true); |
||||||
|
~TextFormatTranscoder(); |
||||||
|
|
||||||
|
bool parseInto(absl::string_view text_format, google::protobuf::MessageLite* msg, |
||||||
|
google::protobuf::io::ErrorCollector* error_collector = nullptr) const; |
||||||
|
|
||||||
|
bool parseInto(google::protobuf::io::ZeroCopyInputStream* input_stream, |
||||||
|
google::protobuf::MessageLite* msg, |
||||||
|
google::protobuf::io::ErrorCollector* error_collector = nullptr) const; |
||||||
|
|
||||||
|
void loadFileDescriptors(const internal::FileDescriptorInfo& file_descriptor_info); |
||||||
|
|
||||||
|
private: |
||||||
|
// This function needs the member function CreateEmptyDynamicMessage. The
|
||||||
|
// target the defines the function has its own visibility whitelist which
|
||||||
|
// requires a dance with these declarations.
|
||||||
|
friend std::unique_ptr<google::protobuf::Message> |
||||||
|
createDynamicMessage(const TextFormatTranscoder& transcoder, |
||||||
|
const google::protobuf::MessageLite& message, |
||||||
|
google::protobuf::io::ErrorCollector* error_collector); |
||||||
|
|
||||||
|
std::unique_ptr<google::protobuf::Message> |
||||||
|
createEmptyDynamicMessage(absl::string_view type_name, |
||||||
|
google::protobuf::io::ErrorCollector* error_collector) const; |
||||||
|
|
||||||
|
bool toBinarySerializationInternal(absl::string_view type_name, |
||||||
|
google::protobuf::io::ZeroCopyInputStream* input_stream, |
||||||
|
std::string* binary_serializtion, |
||||||
|
google::protobuf::io::ErrorCollector* error_collector) const; |
||||||
|
|
||||||
|
struct InternalData; |
||||||
|
|
||||||
|
std::unique_ptr<InternalData> internals_; |
||||||
|
const bool allow_global_fallback_; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace cc_proto_descriptor_library
|
Loading…
Reference in new issue