pw_protobuf: Support codegen for StreamingEncoder
Introduces codegen support for the new StreamingEncoder and
MemoryEncoder classes. The MemoryEncoder will eventually replace what is
now the pw::protobuf::Encoder.
Bug: 384
Change-Id: Ib7e3c3def5b7e062d595db82c76455c491a08250
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/45362
Reviewed-by: Alexei Frolov <frolv@google.com>
Commit-Queue: Armando Montanez <amontanez@google.com>
diff --git a/pw_protobuf/codegen_test.cc b/pw_protobuf/codegen_test.cc
index b62ce14..bce8564 100644
--- a/pw_protobuf/codegen_test.cc
+++ b/pw_protobuf/codegen_test.cc
@@ -15,6 +15,7 @@
#include "gtest/gtest.h"
#include "pw_protobuf/encoder.h"
+#include "pw_stream/memory_stream.h"
// These header files contain the code generated by the pw_protobuf plugin.
// They are re-generated every time the tests are built and are used by the
@@ -36,47 +37,54 @@
TEST(Codegen, Codegen) {
std::byte encode_buffer[512];
- NestedEncoder<20, 20> encoder(encode_buffer);
+ std::byte temp_buffer[512];
+ stream::MemoryWriter writer(encode_buffer);
- Pigweed::Encoder pigweed(&encoder);
+ Pigweed::StreamEncoder pigweed(writer, temp_buffer);
pigweed.WriteMagicNumber(73);
pigweed.WriteZiggy(-111);
pigweed.WriteErrorMessage("not a typewriter");
pigweed.WriteBin(Pigweed::Protobuf::Binary::ZERO);
{
- Pigweed::Pigweed::Encoder pigweed_pigweed = pigweed.GetPigweedEncoder();
+ Pigweed::Pigweed::StreamEncoder pigweed_pigweed =
+ pigweed.GetPigweedEncoder();
pigweed_pigweed.WriteStatus(Bool::FILE_NOT_FOUND);
+
+ ASSERT_EQ(pigweed_pigweed.status(), OkStatus());
}
{
- Proto::Encoder proto = pigweed.GetProtoEncoder();
+ Proto::StreamEncoder proto = pigweed.GetProtoEncoder();
proto.WriteBin(Proto::Binary::OFF);
proto.WritePigweedPigweedBin(Pigweed::Pigweed::Binary::ZERO);
proto.WritePigweedProtobufBin(Pigweed::Protobuf::Binary::ZERO);
{
- Pigweed::Protobuf::Compiler::Encoder meta = proto.GetMetaEncoder();
+ Pigweed::Protobuf::Compiler::StreamEncoder meta = proto.GetMetaEncoder();
meta.WriteFileName("/etc/passwd");
meta.WriteStatus(Pigweed::Protobuf::Compiler::Status::FUBAR);
}
{
- Pigweed::Encoder nested_pigweed = proto.GetPigweedEncoder();
- pigweed.WriteErrorMessage("here we go again");
- pigweed.WriteMagicNumber(616);
+ Pigweed::StreamEncoder nested_pigweed = proto.GetPigweedEncoder();
+ nested_pigweed.WriteErrorMessage("here we go again");
+ nested_pigweed.WriteMagicNumber(616);
{
- DeviceInfo::Encoder device_info = nested_pigweed.GetDeviceInfoEncoder();
+ DeviceInfo::StreamEncoder device_info =
+ nested_pigweed.GetDeviceInfoEncoder();
{
- KeyValuePair::Encoder attributes = device_info.GetAttributesEncoder();
+ KeyValuePair::StreamEncoder attributes =
+ device_info.GetAttributesEncoder();
attributes.WriteKey("version");
attributes.WriteValue("5.3.1");
}
{
- KeyValuePair::Encoder attributes = device_info.GetAttributesEncoder();
+ KeyValuePair::StreamEncoder attributes =
+ device_info.GetAttributesEncoder();
attributes.WriteKey("chip");
attributes.WriteValue("left-soc");
}
@@ -87,7 +95,7 @@
}
for (int i = 0; i < 5; ++i) {
- Proto::ID::Encoder id = pigweed.GetIdEncoder();
+ Proto::ID::StreamEncoder id = pigweed.GetIdEncoder();
id.WriteId(5 * i * i + 3 * i + 49);
}
@@ -166,11 +174,10 @@
};
// clang-format on
- Result result = encoder.Encode();
- ASSERT_EQ(result.status(), OkStatus());
- EXPECT_EQ(result.value().size(), sizeof(expected_proto));
- EXPECT_EQ(std::memcmp(
- result.value().data(), expected_proto, sizeof(expected_proto)),
+ ConstByteSpan result = writer.WrittenData();
+ ASSERT_EQ(pigweed.status(), OkStatus());
+ EXPECT_EQ(result.size(), sizeof(expected_proto));
+ EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
0);
}
@@ -260,9 +267,7 @@
TEST(CodegenRepeated, NonScalar) {
std::byte encode_buffer[32];
- NestedEncoder encoder(encode_buffer);
-
- RepeatedTest::Encoder repeated_test(&encoder);
+ RepeatedTest::RamEncoder repeated_test(encode_buffer);
constexpr const char* strings[] = {"the", "quick", "brown", "fox"};
for (const char* s : strings) {
repeated_test.WriteStrings(s);
@@ -271,11 +276,10 @@
constexpr uint8_t expected_proto[] = {
0x1a, 0x03, 't', 'h', 'e', 0x1a, 0x5, 'q', 'u', 'i', 'c', 'k',
0x1a, 0x5, 'b', 'r', 'o', 'w', 'n', 0x1a, 0x3, 'f', 'o', 'x'};
- Result result = encoder.Encode();
- ASSERT_EQ(result.status(), OkStatus());
- EXPECT_EQ(result.value().size(), sizeof(expected_proto));
- EXPECT_EQ(std::memcmp(
- result.value().data(), expected_proto, sizeof(expected_proto)),
+ ConstByteSpan result(repeated_test);
+ ASSERT_EQ(repeated_test.status(), OkStatus());
+ EXPECT_EQ(result.size(), sizeof(expected_proto));
+ EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
0);
}
diff --git a/pw_protobuf/encoding.rst b/pw_protobuf/encoding.rst
index a7259db..340e711 100644
--- a/pw_protobuf/encoding.rst
+++ b/pw_protobuf/encoding.rst
@@ -120,3 +120,95 @@
While individual write calls on a proto encoder return pw::Status objects, the
encoder tracks all status returns and "latches" onto the first error
encountered. This status can be accessed via ``StreamingEncoder::status()``.
+
+Codegen
+-------
+pw_protobuf encoder codegen integration is supported in GN, Bazel, and CMake.
+The codegen is just a light wrapper around the ``StreamEncoder`` and
+``MemoryEncoder`` objects, providing named helper functions to write proto
+fields rather than requiring that field numbers are directly passed to an
+encoder. Namespaced proto enums are also generated, and used as the arguments
+when writing enum fields of a proto message.
+
+All generated messages provide a ``Fields`` enum that can be used directly for
+out-of-band encoding, or with the ``pw::protobuf::Decoder``.
+
+This module's codegen is available through the ``*.pwpb`` sub-target of a
+``pw_proto_library`` in GN, CMake, and Bazel. See :ref:`pw_protobuf_compiler's
+documentation <module-pw_protobuf_compiler>` for more information on build
+system integration for pw_protobuf codegen.
+
+Example ``BUILD.gn``:
+
+.. Code:: none
+
+ import("//build_overrides/pigweed.gni")
+
+ import("$dir_pw_build/target_types.gni")
+ import("$dir_pw_protobuf_compiler/proto.gni")
+
+ # This target controls where the *.pwpb.h headers end up on the include path.
+ # In this example, it's at "pet_daycare_protos/client.pwpb.h".
+ pw_proto_library("pet_daycare_protos") {
+ sources = [
+ "pet_daycare_protos/client.proto",
+ ]
+ }
+
+ pw_source_set("example_client") {
+ sources = [ "example_client.cc" ]
+ deps = [
+ ":pet_daycare_protos.pwpb",
+ dir_pw_bytes,
+ dir_pw_stream,
+ ]
+ }
+
+Example ``pet_daycare_protos/client.proto``:
+
+.. Code:: none
+
+ syntax = "proto3";
+ // The proto package controls the namespacing of the codegen. If this package
+ // were fuzzy.friends, the namespace for codegen would be fuzzy::friends::*.
+ package fuzzy_friends;
+
+ message Pet {
+ string name = 1;
+ string pet_type = 2;
+ }
+
+ message Client {
+ repeated Pet pets = 1;
+ }
+
+Example ``example_client.cc``:
+
+.. Code:: cpp
+
+ #include "pet_daycare_protos/client.pwpb.h"
+ #include "pw_protobuf/streaming_encoder.h"
+ #include "pw_stream/sys_io_stream.h"
+ #include "pw_bytes/span.h"
+
+ pw::stream::SysIoWriter sys_io_writer;
+ std::byte submessage_scratch_buffer[64];
+ // The constructor is the same as a pw::protobuf::StreamingEncoder.
+ fuzzy_friends::Client::StreamEncoder client(sys_io_writer,
+ submessage_scratch_buffer);
+
+ fuzzy_friends::Pet::StreamEncoder pet1 = client.GetPetsEncoder();
+
+ pet1.WriteName("Spot");
+ pet1.WritePetType("dog");
+ PW_CHECK_OK(pet1.Finalize());
+
+ { // Since pet2 is scoped, it will automatically Finalize() on destruction.
+ fuzzy_friends::Pet::StreamEncoder pet2 = client.GetPetsEncoder();
+ pet2.WriteName("Slippers");
+ pet2.WritePetType("rabbit");
+ }
+
+ if (!client.status().ok()) {
+ PW_LOG_INFO("Failed to encode proto; %s", client.status().str());
+ }
diff --git a/pw_protobuf/public/pw_protobuf/streaming_encoder.h b/pw_protobuf/public/pw_protobuf/streaming_encoder.h
index 2e3d761..c19a21f 100644
--- a/pw_protobuf/public/pw_protobuf/streaming_encoder.h
+++ b/pw_protobuf/public/pw_protobuf/streaming_encoder.h
@@ -81,12 +81,11 @@
~StreamingEncoder() { Finalize(); }
// Disallow copy/assign to avoid confusion about who owns the buffer.
- StreamingEncoder(const StreamingEncoder& other) = delete;
StreamingEncoder& operator=(const StreamingEncoder& other) = delete;
+ StreamingEncoder(const StreamingEncoder& other) = delete;
// It's not safe to move an encoder as it could cause another encoder's
// parent_ pointer to become invalid.
- StreamingEncoder(StreamingEncoder&& other) = delete;
StreamingEncoder& operator=(StreamingEncoder&& other) = delete;
// Forwards the conservative write limit of the underlying pw::stream::Writer.
@@ -362,6 +361,39 @@
return WriteBytes(field_number, std::as_bytes(std::span(value)));
}
+ // Writes a proto string key-value pair.
+ //
+ // Precondition: Encoder has no active child encoder.
+ Status WriteString(uint32_t field_number, const char* value, size_t len) {
+ return WriteBytes(field_number, std::as_bytes(std::span(value, len)));
+ }
+
+ // Writes a proto string key-value pair.
+ // TODO(384): This function is not safe and will be removed as part of the
+ // transition away from the old protobuf encoder.
+ //
+ // Precondition: Encoder has no active child encoder.
+ Status WriteString(uint32_t field_number, const char* value) {
+ return WriteBytes(field_number,
+ std::as_bytes(std::span(std::string_view(value))));
+ }
+
+ protected:
+ // We need this for codegen.
+ constexpr StreamingEncoder(StreamingEncoder&& other)
+ : writer_(&other.writer_ == &other.memory_writer_ ? memory_writer_
+ : other.writer_),
+ status_(other.status_),
+ parent_(other.parent_),
+ nested_field_number_(other.nested_field_number_),
+ memory_writer_(std::move(other.memory_writer_)) {
+ PW_ASSERT(nested_field_number_ == 0);
+ // Make the nested encoder look like it has an open child to block writes
+ // for the remainder of the object's life.
+ other.nested_field_number_ = kFirstReservedNumber;
+ other.parent_ = nullptr;
+ }
+
private:
friend class MemoryEncoder;
@@ -518,11 +550,14 @@
// It's not safe to move an encoder as it could cause another encoder's
// parent_ pointer to become invalid.
- MemoryEncoder(MemoryEncoder&& other) = delete;
MemoryEncoder& operator=(MemoryEncoder&& other) = delete;
const std::byte* data() const { return memory_writer_.data(); }
size_t size() const { return memory_writer_.bytes_written(); }
+
+ protected:
+ // This is needed by codegen.
+ MemoryEncoder(MemoryEncoder&& other) = default;
};
} // namespace pw::protobuf
diff --git a/pw_protobuf/py/pw_protobuf/codegen_pwpb.py b/pw_protobuf/py/pw_protobuf/codegen_pwpb.py
index e0b9293..d103018 100644
--- a/pw_protobuf/py/pw_protobuf/codegen_pwpb.py
+++ b/pw_protobuf/py/pw_protobuf/codegen_pwpb.py
@@ -15,6 +15,7 @@
import abc
from datetime import datetime
+import enum
import os
import sys
from typing import Dict, Iterable, List, Tuple
@@ -34,7 +35,35 @@
PROTO_CC_EXTENSION = '.pwpb.cc'
PROTOBUF_NAMESPACE = 'pw::protobuf'
-BASE_PROTO_CLASS = 'ProtoMessageEncoder'
+
+
+class EncoderType(enum.Enum):
+ MEMORY = 1
+ STREAMING = 2
+ LEGACY = 3
+
+ def base_class_name(self) -> str:
+ """Returns the base class used by this encoder type."""
+ if self is self.LEGACY:
+ return 'ProtoMessageEncoder'
+ if self is self.STREAMING:
+ return 'StreamingEncoder'
+ if self is self.MEMORY:
+ return 'MemoryEncoder'
+
+ raise ValueError('Unknown encoder type')
+
+ def codegen_class_name(self) -> str:
+ """Returns the base class used by this encoder type."""
+ if self is self.LEGACY:
+ return 'Encoder'
+ if self is self.STREAMING:
+ return 'StreamEncoder'
+ if self is self.MEMORY:
+ # TODO(pwbug/384): Make this the 'Encoder'
+ return 'RamEncoder'
+
+ raise ValueError('Unknown encoder type')
# protoc captures stdout, so we need to printf debug to stderr.
@@ -73,7 +102,7 @@
"""
@abc.abstractmethod
- def body(self) -> List[str]:
+ def body(self, encoder_type: EncoderType) -> List[str]:
"""Returns the method body as a list of source code lines.
e.g.
@@ -84,7 +113,9 @@
"""
@abc.abstractmethod
- def return_type(self, from_root: bool = False) -> str:
+ def return_type(self,
+ encoder_type: EncoderType,
+ from_root: bool = False) -> str:
"""Returns the return type of the method, e.g. int.
For non-primitive return types, the from_root argument determines
@@ -134,15 +165,27 @@
def name(self) -> str:
return 'Get{}Encoder'.format(self._field.name())
- def return_type(self, from_root: bool = False) -> str:
- return '{}::Encoder'.format(self._relative_type_namespace(from_root))
+ def return_type(self,
+ encoder_type: EncoderType,
+ from_root: bool = False) -> str:
+ if encoder_type == EncoderType.LEGACY:
+ return '{}::Encoder'.format(
+ self._relative_type_namespace(from_root))
+
+ return '{}::StreamEncoder'.format(
+ self._relative_type_namespace(from_root))
def params(self) -> List[Tuple[str, str]]:
return []
- def body(self) -> List[str]:
- line = 'return {}::Encoder(encoder_, {});'.format(
- self._relative_type_namespace(), self.field_cast())
+ def body(self, encoder_type: EncoderType) -> List[str]:
+ line: str = ''
+ if encoder_type == EncoderType.LEGACY:
+ line = 'return {}::Encoder(encoder_, {});'.format(
+ self._relative_type_namespace(), self.field_cast())
+ else:
+ line = 'return {}::StreamEncoder(GetNestedEncoder({}));'.format(
+ self._relative_type_namespace(), self.field_cast())
return [line]
# Submessage methods are not defined within the class itself because the
@@ -164,13 +207,19 @@
def name(self) -> str:
return 'Write{}'.format(self._field.name())
- def return_type(self, from_root: bool = False) -> str:
+ def return_type(self,
+ encoder_type: EncoderType,
+ from_root: bool = False) -> str:
return '::pw::Status'
- def body(self) -> List[str]:
+ def body(self, encoder_type: EncoderType) -> List[str]:
params = ', '.join([pair[1] for pair in self.params()])
- line = 'return encoder_->{}({}, {});'.format(self._encoder_fn(),
- self.field_cast(), params)
+ if encoder_type == EncoderType.LEGACY:
+ line = 'return encoder_->{}({}, {});'.format(
+ self._encoder_fn(), self.field_cast(), params)
+ else:
+ line = 'return {}({}, {});'.format(self._encoder_fn(),
+ self.field_cast(), params)
return [line]
def params(self) -> List[Tuple[str, str]]:
@@ -465,9 +514,14 @@
def params(self) -> List[Tuple[str, str]]:
return [(self._relative_type_namespace(), 'value')]
- def body(self) -> List[str]:
- line = 'return encoder_->WriteUint32(' \
- '{}, static_cast<uint32_t>(value));'.format(self.field_cast())
+ def body(self, encoder_type: EncoderType) -> List[str]:
+ line: str = ''
+ if encoder_type == EncoderType.LEGACY:
+ line = 'return encoder_->WriteUint32(' \
+ '{}, static_cast<uint32_t>(value));'.format(self.field_cast())
+ else:
+ line = 'return WriteUint32(' \
+ '{}, static_cast<uint32_t>(value));'.format(self.field_cast())
return [line]
def in_class_definition(self) -> bool:
@@ -514,20 +568,33 @@
def generate_code_for_message(message: ProtoMessage, root: ProtoNode,
- output: OutputFile) -> None:
+ output: OutputFile,
+ encoder_type: EncoderType) -> None:
"""Creates a C++ class for a protobuf message."""
assert message.type() == ProtoNode.Type.MESSAGE
+ base_class_name = encoder_type.base_class_name()
+ encoder_name = encoder_type.codegen_class_name()
+
# Message classes inherit from the base proto message class in codegen.h
# and use its constructor.
- base_class = f'{PROTOBUF_NAMESPACE}::{BASE_PROTO_CLASS}'
+ base_class = f'{PROTOBUF_NAMESPACE}::{base_class_name}'
output.write_line(
- f'class {message.cpp_namespace(root)}::Encoder : public {base_class} {{'
+ f'class {message.cpp_namespace(root)}::{encoder_name} ' \
+ f': public {base_class} {{'
)
output.write_line(' public:')
with output.indent():
- output.write_line(f'using {BASE_PROTO_CLASS}::{BASE_PROTO_CLASS};')
+ # Inherit the constructors from the base encoder.
+ output.write_line(f'using {base_class_name}::{base_class_name};')
+
+ # Declare a move constructor that takes a base encoder.
+ if encoder_type != EncoderType.LEGACY:
+ output.write_line(
+ f'constexpr {encoder_name}({base_class_name}&& parent) '\
+ f': {base_class_name}(std::move(parent)) {{}}'
+ )
# Generate methods for each of the message's fields.
for field in message.fields():
@@ -538,7 +605,7 @@
output.write_line()
method_signature = (
- f'{method.return_type()} '
+ f'{method.return_type(encoder_type)} '
f'{method.name()}({method.param_string()})')
if not method.in_class_definition():
@@ -549,7 +616,7 @@
output.write_line(f'{method_signature} {{')
with output.indent():
- for line in method.body():
+ for line in method.body(encoder_type):
output.write_line(line)
output.write_line('}')
@@ -557,7 +624,8 @@
def define_not_in_class_methods(message: ProtoMessage, root: ProtoNode,
- output: OutputFile) -> None:
+ output: OutputFile,
+ encoder_type: EncoderType) -> None:
"""Defines methods for a message class that were previously declared."""
assert message.type() == ProtoNode.Type.MESSAGE
@@ -568,25 +636,26 @@
continue
output.write_line()
- class_name = f'{message.cpp_namespace(root)}::Encoder'
+ class_name = (f'{message.cpp_namespace(root)}::'
+ f'{encoder_type.codegen_class_name()}')
method_signature = (
- f'inline {method.return_type(from_root=True)} '
+ f'inline {method.return_type(encoder_type, from_root=True)} '
f'{class_name}::{method.name()}({method.param_string()})')
output.write_line(f'{method_signature} {{')
with output.indent():
- for line in method.body():
+ for line in method.body(encoder_type):
output.write_line(line)
output.write_line('}')
-def generate_code_for_enum(enum: ProtoEnum, root: ProtoNode,
+def generate_code_for_enum(proto_enum: ProtoEnum, root: ProtoNode,
output: OutputFile) -> None:
"""Creates a C++ enum for a proto enum."""
- assert enum.type() == ProtoNode.Type.ENUM
+ assert proto_enum.type() == ProtoNode.Type.ENUM
- output.write_line(f'enum class {enum.cpp_namespace(root)} {{')
+ output.write_line(f'enum class {proto_enum.cpp_namespace(root)} {{')
with output.indent():
- for name, number in enum.values():
+ for name, number in proto_enum.values():
output.write_line(f'{name} = {number},')
output.write_line('};')
@@ -608,6 +677,9 @@
# Declare the message's encoder class and all of its enums.
output.write_line()
output.write_line('class Encoder;')
+ output.write_line('class StreamEncoder;')
+ output.write_line('class RamEncoder;')
+
for child in node.children():
if child.type() == ProtoNode.Type.ENUM:
output.write_line()
@@ -616,6 +688,23 @@
output.write_line(f'}} // namespace {namespace}')
+def generate_encoder_wrappers(package: ProtoNode, encoder_type: EncoderType,
+ output: OutputFile):
+ # Run through all messages in the file, generating a class for each.
+ for node in package:
+ if node.type() == ProtoNode.Type.MESSAGE:
+ output.write_line()
+ generate_code_for_message(cast(ProtoMessage, node), package,
+ output, encoder_type)
+
+ # Run a second pass through the classes, this time defining all of the
+ # methods which were previously only declared.
+ for node in package:
+ if node.type() == ProtoNode.Type.MESSAGE:
+ define_not_in_class_methods(cast(ProtoMessage, node), package,
+ output, encoder_type)
+
+
def _proto_filename_to_generated_header(proto_file: str) -> str:
"""Returns the generated C++ header name for a .proto file."""
return os.path.splitext(proto_file)[0] + PROTO_H_EXTENSION
@@ -635,6 +724,7 @@
output.write_line('#include <cstdint>')
output.write_line('#include <span>\n')
output.write_line('#include "pw_protobuf/codegen.h"')
+ output.write_line('#include "pw_protobuf/streaming_encoder.h"')
for imported_file in file_descriptor_proto.dependency:
generated_header = _proto_filename_to_generated_header(imported_file)
@@ -657,19 +747,9 @@
output.write_line()
generate_code_for_enum(cast(ProtoEnum, node), package, output)
- # Run through all messages in the file, generating a class for each.
- for node in package:
- if node.type() == ProtoNode.Type.MESSAGE:
- output.write_line()
- generate_code_for_message(cast(ProtoMessage, node), package,
- output)
-
- # Run a second pass through the classes, this time defining all of the
- # methods which were previously only declared.
- for node in package:
- if node.type() == ProtoNode.Type.MESSAGE:
- define_not_in_class_methods(cast(ProtoMessage, node), package,
- output)
+ generate_encoder_wrappers(package, EncoderType.LEGACY, output)
+ generate_encoder_wrappers(package, EncoderType.STREAMING, output)
+ generate_encoder_wrappers(package, EncoderType.MEMORY, output)
if package.cpp_namespace():
output.write_line(f'\n}} // namespace {package.cpp_namespace()}')
diff --git a/pw_protobuf_compiler/docs.rst b/pw_protobuf_compiler/docs.rst
index fc9ed7b..99e3d18 100644
--- a/pw_protobuf_compiler/docs.rst
+++ b/pw_protobuf_compiler/docs.rst
@@ -33,10 +33,9 @@
GN template
===========
-The ``pw_proto_library`` GN template is provided by the module.
-
-It defines a collection of protobuf files that should be compiled together. The
-template creates a sub-target for each supported generator, named
+This module provides a ``pw_proto_library`` GN template that defines a
+collection of protobuf files that should be compiled together. The template
+creates a sub-target for each supported generator, named
``<target_name>.<generator>``. These sub-targets generate their respective
protobuf code, and expose it to the build system appropriately (e.g. a
``pw_source_set`` for C/C++).
@@ -72,6 +71,20 @@
//path/to/my_protos:my_protos.python.lint
//path/to/my_protos:python.lint
+**Supported Codegen**
+
+GN supports the following compiled proto libraries via the specified
+sub-targets generated by a ``pw_proto_library``.
+
+* ``${target_name}.pwpb`` - Generated C++ pw_protobuf code
+* ``${target_name}.nanopb`` - Generated C++ nanopb code (requires Nanopb)
+* ``${target_name}.nanopb_rpc`` - Generated C++ Nanopb pw_rpc code (requires
+ Nanopb)
+* ``${target_name}.raw_rpc`` - Generated C++ raw pw_rpc code (no protobuf
+ library)
+* ``${target_name}.go`` - Generated GO protobuf libraries
+* ``${target_name}.python`` - Generated Python protobuf libraries
+
**Arguments**
* ``sources``: List of input .proto files.
@@ -303,6 +316,16 @@
#include "my_other_protos/baz.pwpb.h"
+**Supported Codegen**
+
+CMake supports the following compiled proto libraries via the specified
+sub-targets generated by a ``pw_proto_library``.
+
+* ``${NAME}.pwpb`` - Generated C++ pw_protobuf code
+* ``${NAME}.nanopb`` - Generated C++ nanopb code (requires Nanopb)
+* ``${NAME}.nanopb_rpc`` - Generated C++ Nanopb pw_rpc code (requires Nanopb)
+* ``${NAME}.raw_rpc`` - Generated C++ raw pw_rpc code (no protobuf library)
+
Bazel
=====
Bazel provides a ``pw_proto_library`` rule with similar features as the
@@ -356,4 +379,14 @@
.. code:: cpp
- #include "my_protos/bar.pwpb.h"
\ No newline at end of file
+ #include "my_protos/bar.pwpb.h"
+
+**Supported Codegen**
+
+Bazel supports the following compiled proto libraries via the specified
+sub-targets generated by a ``pw_proto_library``.
+
+* ``${NAME}.pwpb`` - Generated C++ pw_protobuf code
+* ``${NAME}.nanopb`` - Generated C++ nanopb code (requires Nanopb)
+* ``${NAME}.nanopb_rpc`` - Generated C++ Nanopb pw_rpc code (requires Nanopb)
+* ``${NAME}.raw_rpc`` - Generated C++ raw pw_rpc code (no protobuf library)