• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1.. _module-pw_protobuf_compiler:
2
3--------------------
4pw_protobuf_compiler
5--------------------
6The Protobuf compiler module provides build system integration and wrapper
7scripts for generating source code for Protobuf definitions.
8
9Generator support
10=================
11Protobuf code generation is currently supported for the following generators:
12
13+-------------+----------------+-----------------------------------------------+
14| Generator   | Code           | Notes                                         |
15+-------------+----------------+-----------------------------------------------+
16| pw_protobuf | ``pwpb``       | Compiles using ``pw_protobuf``.               |
17+-------------+----------------+-----------------------------------------------+
18| Nanopb      | ``nanopb``     | Compiles using Nanopb. The build argument     |
19|             |                | ``dir_pw_third_party_nanopb`` must be set to  |
20|             |                | point to a local nanopb installation.         |
21+-------------+----------------+-----------------------------------------------+
22| Nanopb RPC  | ``nanopb_rpc`` | Compiles pw_rpc service and client code for   |
23|             |                | nanopb. Requires a nanopb installation.       |
24+-------------+----------------+-----------------------------------------------+
25| Raw RPC     | ``raw_rpc``    | Compiles raw binary pw_rpc service code.      |
26+-------------+----------------+-----------------------------------------------+
27| Go          | ``go``         | Compiles using the standard Go protobuf       |
28|             |                | plugin with gRPC service support.             |
29+-------------+----------------+-----------------------------------------------+
30| Python      | ``python``     | Compiles using the standard Python protobuf   |
31|             |                | plugin, creating a ``pw_python_package``.     |
32+-------------+----------------+-----------------------------------------------+
33| Typescript  | ``typescript`` | Compilation is supported in Bazel via         |
34|             |                | @rules_proto_grpc. ProtoCollection provides   |
35|             |                | convience methods for proto descriptors.      |
36+-------------+----------------+-----------------------------------------------+
37
38GN template
39===========
40This module provides a ``pw_proto_library`` GN template that defines a
41collection of protobuf files that should be compiled together. The template
42creates a sub-target for each supported generator, named
43``<target_name>.<generator>``. These sub-targets generate their respective
44protobuf code, and expose it to the build system appropriately (e.g. a
45``pw_source_set`` for C/C++).
46
47For example, given the following target:
48
49.. code-block::
50
51  pw_proto_library("test_protos") {
52    sources = [ "my_test_protos/test.proto" ]
53  }
54
55``test_protos.pwpb`` compiles code for pw_protobuf, and ``test_protos.nanopb``
56compiles using Nanopb (if it's installed).
57
58Protobuf code is only generated when a generator sub-target is listed as a
59dependency of another GN target.
60
61GN permits using abbreviated labels when the target name matches the directory
62name (e.g. ``//foo`` for ``//foo:foo``). For consistency with this, the
63sub-targets for each generator are aliased to the directory when the target name
64is the same. For example, these two labels are equivalent:
65
66.. code-block::
67
68  //path/to/my_protos:my_protos.pwpb
69  //path/to/my_protos:pwpb
70
71``pw_python_package`` subtargets are also available on the ``python`` subtarget:
72
73.. code-block::
74
75  //path/to/my_protos:my_protos.python.lint
76  //path/to/my_protos:python.lint
77
78**Supported Codegen**
79
80GN supports the following compiled proto libraries via the specified
81sub-targets generated by a ``pw_proto_library``.
82
83* ``${target_name}.pwpb`` - Generated C++ pw_protobuf code
84* ``${target_name}.nanopb`` - Generated C++ nanopb code (requires Nanopb)
85* ``${target_name}.nanopb_rpc`` - Generated C++ Nanopb pw_rpc code (requires
86  Nanopb)
87* ``${target_name}.raw_rpc`` - Generated C++ raw pw_rpc code (no protobuf
88  library)
89* ``${target_name}.go`` - Generated GO protobuf libraries
90* ``${target_name}.python`` - Generated Python protobuf libraries
91
92**Arguments**
93
94* ``sources``: List of input .proto files.
95* ``deps``: List of other pw_proto_library dependencies.
96* ``inputs``: Other files on which the protos depend (e.g. nanopb ``.options``
97  files).
98* ``prefix``: A prefix to add to the source protos prior to compilation. For
99  example, a source called ``"foo.proto"`` with ``prefix = "nested"`` will be
100  compiled with protoc as ``"nested/foo.proto"``.
101* ``strip_prefix``: Remove this prefix from the source protos. All source and
102  input files must be nested under this path.
103* ``python_package``: Label of Python package to which to add the proto modules.
104  The .python subtarget will redirect to this package.
105
106**Example**
107
108.. code-block::
109
110  import("$dir_pw_protobuf_compiler/proto.gni")
111
112  pw_proto_library("my_protos") {
113    sources = [
114      "my_protos/foo.proto",
115      "my_protos/bar.proto",
116    ]
117  }
118
119  pw_proto_library("my_other_protos") {
120    sources = [ "some/other/path/baz.proto" ]  # imports foo.proto
121
122    # This removes the "some/other/path" prefix from the proto files.
123    strip_prefix = "some/other/path"
124
125    # This adds the "my_other_protos/" prefix to the proto files.
126    prefix = "my_other_protos"
127
128    # Proto libraries depend on other proto libraries directly.
129    deps = [ ":my_protos" ]
130  }
131
132  source_set("my_cc_code") {
133    sources = [
134      "foo.cc",
135      "bar.cc",
136      "baz.cc",
137    ]
138
139    # When depending on protos in a source_set, specify the generator suffix.
140    deps = [ ":my_other_protos.pwpb" ]
141  }
142
143From C++, ``baz.proto`` included as follows:
144
145.. code-block:: cpp
146
147  #include "my_other_protos/baz.pwpb.h"
148
149From Python, ``baz.proto`` is imported as follows:
150
151.. code-block:: python
152
153  from my_other_protos import baz_pb2
154
155Proto file structure
156--------------------
157Protobuf source files must be nested under another directory when they are
158compiled. This ensures that they can be packaged properly in Python. The first
159directory is used as the Python package name, so must be unique across the
160build. The ``prefix`` option may be used to set this directory.
161
162Using ``prefix`` and ``strip_prefix`` together allows remapping proto files to
163a completely different path. This can be useful when working with protos defined
164in external libraries. For example, consider this proto library:
165
166.. code-block::
167
168  pw_proto_library("external_protos") {
169    sources = [
170      "//other/external/some_library/src/protos/alpha.proto",
171      "//other/external/some_library/src/protos/beta.proto,
172      "//other/external/some_library/src/protos/internal/gamma.proto",
173    ]
174    strip_prefix = "//other/external/some_library/src/protos"
175    prefix = "some_library"
176  }
177
178These protos will be compiled by protoc as if they were in this file structure:
179
180.. code-block::
181
182  some_library/
183  ├── alpha.proto
184  ├── beta.proto
185  └── internal
186      └── gamma.proto
187
188.. _module-pw_protobuf_compiler-add-to-python-package:
189
190Adding Python proto modules to an existing package
191--------------------------------------------------
192By default, generated Python proto modules are organized into their own Python
193package. These proto modules can instead be added to an existing Python package
194declared with ``pw_python_package``. This is done by setting the
195``python_package`` argument on the ``pw_proto_library`` and the
196``proto_library`` argument on the ``pw_python_package``.
197
198For example, the protos declared in ``my_protos`` will be nested in the Python
199package declared by ``my_package``.
200
201.. code-block::
202
203  pw_proto_library("my_protos") {
204    sources = [ "hello.proto ]
205    prefix = "foo"
206    python_package = ":my_package"
207  }
208
209  pw_python_pacakge("my_package") {
210    generate_setup = {
211      metadata = {
212        name = "foo"
213        version = "1.0"
214      }
215    }
216
217    sources = [ "foo/cool_module.py" ]
218    proto_library = ":my_protos"
219  }
220
221The ``hello_pb2.py`` proto module can be used alongside other files in the
222``foo`` package.
223
224.. code-block:: python
225
226  from foo import cool_module, hello_pb2
227
228Working with externally defined protos
229--------------------------------------
230``pw_proto_library`` targets may be used to build ``.proto`` sources from
231existing projects. In these cases, it may be necessary to supply the
232``strip_prefix`` argument, which specifies the protobuf include path to use for
233``protoc``. If only a single external protobuf is being compiled, the
234``python_module_as_package`` option can be used to override the requirement that
235the protobuf be nested under a directory. This option generates a Python package
236with the same name as the proto file, so that the generated proto can be
237imported as if it were a standalone Python module.
238
239For example, the ``pw_proto_library`` target for Nanopb sets
240``python_module_as_package`` to ``nanopb_pb2``.
241
242.. code-block::
243
244  pw_proto_library("proto") {
245    strip_prefix = "$dir_pw_third_party_nanopb/generator/proto"
246    sources = [ "$dir_pw_third_party_nanopb/generator/proto/nanopb.proto" ]
247    python_module_as_package = "nanopb_pb2"
248  }
249
250In Python, this makes ``nanopb.proto`` available as ``import nanopb_pb2`` via
251the ``nanopb_pb2`` Python package. In C++, ``nanopb.proto`` is accessed as
252``#include "nanopb.pwpb.h"``.
253
254The ``python_module_as_package`` feature should only be used when absolutely
255necessary --- for example, to support proto files that include
256``import "nanopb.proto"``.
257
258CMake
259=====
260CMake provides a ``pw_proto_library`` function with similar features as the
261GN template. The CMake build only supports building firmware code, so
262``pw_proto_library`` does not generate a Python package.
263
264**Arguments**
265
266* ``NAME``: the base name of the libraries to create
267* ``SOURCES``: .proto source files
268* ``DEPS``: dependencies on other ``pw_proto_library`` targets
269* ``PREFIX``: prefix add to the proto files
270* ``STRIP_PREFIX``: prefix to remove from the proto files
271* ``INPUTS``: files to include along with the .proto files (such as Nanopb
272  .options files)
273
274**Example**
275
276 .. code-block:: cmake
277
278  include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
279  include($ENV{PW_ROOT}/pw_protobuf_compiler/proto.cmake)
280
281  pw_proto_library(my_module.my_protos
282    SOURCES
283      my_protos/foo.proto
284      my_protos/bar.proto
285  )
286
287  pw_proto_library(my_module.my_protos
288    SOURCES
289      my_protos/foo.proto
290      my_protos/bar.proto
291  )
292
293  pw_proto_library(my_module.my_other_protos
294    SOURCES
295      some/other/path/baz.proto  # imports foo.proto
296
297    # This removes the "some/other/path" prefix from the proto files.
298    STRIP_PREFIX
299      some/other/path
300
301    # This adds the "my_other_protos/" prefix to the proto files.
302    PREFIX
303      my_other_protos
304
305    # Proto libraries depend on other proto libraries directly.
306    DEPS
307      my_module.my_protos
308  )
309
310  add_library(my_module.my_cc_code
311      foo.cc
312      bar.cc
313      baz.cc
314  )
315
316  # When depending on protos in a source_set, specify the generator suffix.
317  target_link_libraries(my_module.my_cc_code PUBLIC
318    my_module.my_other_protos.pwpb
319  )
320
321These proto files are accessed in C++ the same as in the GN build:
322
323.. code-block:: cpp
324
325  #include "my_other_protos/baz.pwpb.h"
326
327**Supported Codegen**
328
329CMake supports the following compiled proto libraries via the specified
330sub-targets generated by a ``pw_proto_library``.
331
332* ``${NAME}.pwpb`` - Generated C++ pw_protobuf code
333* ``${NAME}.nanopb`` - Generated C++ nanopb code (requires Nanopb)
334* ``${NAME}.nanopb_rpc`` - Generated C++ Nanopb pw_rpc code (requires Nanopb)
335* ``${NAME}.raw_rpc`` - Generated C++ raw pw_rpc code (no protobuf library)
336
337Bazel
338=====
339Bazel provides a ``pw_proto_library`` rule with similar features as the
340GN template. The Bazel build only supports building firmware code, so
341``pw_proto_library`` does not generate a Python package. The Bazel rules differ
342slightly compared to the GN build to be more in line with what would be
343considered idiomatic in Bazel.
344
345To use Pigweeds Protobuf rules you must first pull in the required dependencies
346into your Bazel WORKSPACE file. e.g.
347
348.. code-block:: python
349
350  # WORKSPACE ...
351  load("@pigweed//pw_protobuf_compiler:deps.bzl", "pw_protobuf_dependencies")
352  pw_protobuf_dependencies()
353
354Bazel uses a different set of rules to manage proto files than it does to
355compile them. e.g.
356
357.. code-block:: python
358
359  # BUILD ...
360  load("@rules_proto//proto:defs.bzl", "proto_library")
361  load("@pigweed//pw_protobuf_compiler:proto.bzl", "pw_proto_library")
362
363  # Manages proto sources and dependencies.
364  proto_library(
365    name = "my_proto",
366    srcs = [
367      "my_protos/foo.proto",
368      "my_protos/bar.proto",
369    ]
370  )
371
372  # Compiles dependant protos to C++.
373  pw_proto_library(
374    name = "my_cc_proto",
375    deps = [":my_proto"],
376  )
377
378  # Library that depends on only pw_protobuf generated proto targets.
379  pw_cc_library(
380    name = "my_proto_only_lib",
381    srcs = ["my/proto_only.cc"],
382    deps = [":my_cc_proto.pwpb"],
383  )
384
385  # Library that depends on only Nanopb generated proto targets.
386  pw_cc_library(
387    name = "my_nanopb_only_lib",
388    srcs = ["my/nanopb_only.cc"],
389    deps = [":my_cc_proto.nanopb"],
390  )
391
392  # Library that depends on pw_protobuf and pw_rpc/raw.
393  pw_cc_library(
394    name = "my_raw_rpc_lib",
395    srcs = ["my/raw_rpc.cc"],
396    deps = [
397      ":my_cc_proto.pwpb",
398      ":my_cc_proto.raw_rpc",
399    ],
400  )
401  pw_cc_library(
402    name = "my_nanopb_rpc_lib",
403    srcs = ["my/proto_only.cc"],
404    deps = [
405      ":my_cc_proto.nanopb_rpc",
406    ],
407  )
408
409
410  # Library that depends on generated proto targets. Prefer to depend only on
411  # those generated targets ("my_lib.pwpb", "my_lib.nanopb") that are actually
412  # required. Note that the .nanopb target may not compile for some proto
413  # messages, e.g. self-referring messages;
414  # see https://github.com/nanopb/nanopb/issues/433.
415  pw_cc_library(
416    name = "my_lib",
417    srcs = ["my/lib.cc"],
418    # This target depends on all generated proto targets
419    # e.g. name.{pwpb, nanopb, raw_rpc, nanopb_rpc}
420    deps = [":my_cc_proto"],
421  )
422
423
424From ``my/lib.cc`` you can now include the generated headers.
425e.g.
426
427.. code:: cpp
428
429  #include "my_protos/bar.pwpb.h"
430  // and/or RPC headers
431  #include "my_protos/bar.raw_rpc.pb.h
432  // or
433  #include "my_protos/bar.nanopb_rpc.pb.h"
434
435
436
437**Supported Codegen**
438
439Bazel supports the following compiled proto libraries via the specified
440sub-targets generated by a ``pw_proto_library``.
441
442* ``${NAME}.pwpb`` - Generated C++ pw_protobuf code
443* ``${NAME}.nanopb`` - Generated C++ nanopb code
444* ``${NAME}.raw_rpc`` - Generated C++ raw pw_rpc code (no protobuf library)
445* ``${NAME}.nanopb_rpc`` - Generated C++ Nanopb pw_rpc code
446
447
448