• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2020 The Pigweed Authors
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4# use this file except in compliance with the License. You may obtain a copy of
5# the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations under
13# the License.
14
15import("//build_overrides/pigweed.gni")
16import("//build_overrides/pigweed_environment.gni")
17
18import("$dir_pw_build/error.gni")
19import("$dir_pw_build/input_group.gni")
20import("$dir_pw_build/mirror_tree.gni")
21import("$dir_pw_build/python.gni")
22import("$dir_pw_build/python_action.gni")
23import("$dir_pw_build/python_gn_args.gni")
24import("$dir_pw_build/target_types.gni")
25import("$dir_pw_third_party/nanopb/nanopb.gni")
26import("toolchain.gni")
27
28# Variables forwarded from the public pw_proto_library template to the final
29# pw_source_set.
30_forwarded_vars = [
31  "testonly",
32  "visibility",
33]
34
35declare_args() {
36  # To override the protobuf compiler used set this to the GN target that builds
37  # the protobuf compiler.
38  pw_protobuf_compiler_PROTOC_TARGET = ""
39}
40
41if (pw_protobuf_compiler_PROTOC_TARGET == "" &&
42    defined(pw_env_setup_CIPD_PIGWEED)) {
43  _default_protc =
44      rebase_path("$pw_env_setup_CIPD_PIGWEED/bin/protoc", root_build_dir)
45  if (host_os == "win") {
46    _default_protc += ".exe"
47  }
48} else {
49  _default_protc = ""
50}
51
52declare_args() {
53  # To override the protobuf compiler used set this to the path, relative to the
54  # root_build_dir, to the protoc binary.
55  pw_protobuf_compiler_PROTOC_BINARY = _default_protc
56}
57
58# Internal template that invokes protoc with a pw_python_action. This should not
59# be used outside of this file; use pw_proto_library instead.
60#
61# This creates the internal GN target $target_name.$language._gen that compiles
62# proto files with protoc.
63template("_pw_invoke_protoc") {
64  if (current_toolchain == pw_protobuf_compiler_TOOLCHAIN) {
65    if (defined(invoker.out_dir)) {
66      _out_dir = invoker.out_dir
67    } else {
68      _out_dir = "${invoker.base_out_dir}/${invoker.language}"
69      if (defined(invoker.module_as_package) &&
70          invoker.module_as_package != "") {
71        assert(invoker.language == "python")
72        _out_dir = "$_out_dir/${invoker.module_as_package}"
73      }
74    }
75
76    _includes =
77        rebase_path(get_target_outputs(":${invoker.base_target}._includes"),
78                    root_build_dir)
79
80    pw_python_action("$target_name._gen") {
81      script =
82          "$dir_pw_protobuf_compiler/py/pw_protobuf_compiler/generate_protos.py"
83
84      # NOTE: A python_dep on "$dir_pw_protobuf_compiler/py" should not be
85      # included building that Python package which requires the build venv to
86      # be created. Instead, use python_metadata_deps to only add
87      # pw_protobuf_compiler to the PYTHONPATH.
88      python_deps = []
89
90      # Add pw_protobuf_compiler and its dependencies to the PYTHONPATH when
91      # running this action.
92      python_metadata_deps = [ "$dir_pw_protobuf_compiler/py" ]
93
94      python_deps = []
95      if (defined(invoker.python_deps)) {
96        python_deps += invoker.python_deps
97      }
98
99      deps = [
100        ":${invoker.base_target}._includes",
101        ":${invoker.base_target}._sources",
102      ]
103
104      foreach(dep, invoker.deps) {
105        deps += [ get_label_info(dep, "label_no_toolchain") + "._gen" ]
106      }
107
108      if (defined(invoker.other_deps)) {
109        deps += invoker.other_deps
110      }
111
112      args = []
113      if (!pw_protobuf_compiler_GENERATE_LEGACY_ENUM_SNAKE_CASE_NAMES) {
114        args += [ "--exclude-pwpb-legacy-snake-case-field-name-enums" ]
115      }
116      if (pw_protobuf_compiler_PROTOC_TARGET != "") {
117        assert(
118            pw_protobuf_compiler_PROTOC_BINARY != "",
119            "if pw_protobuf_compiler_PROTOC_TARGET is set then pw_protobuf_compiler_PROTOC_BINARY must be set to the path to protoc, relative to the root_build_dir.")
120        _protoc_src_dir =
121            get_label_info(pw_protobuf_compiler_PROTOC_TARGET, "dir") + "/src"
122
123        args += [
124          "--protoc",
125          pw_protobuf_compiler_PROTOC_BINARY,
126          "--proto-path",
127          rebase_path(_protoc_src_dir, root_build_dir),
128        ]
129        deps += [ pw_protobuf_compiler_PROTOC_TARGET ]
130      }
131      args += [
132                "--language",
133                invoker.language,
134                "--include-file",
135                _includes[0],
136                "--compile-dir",
137                rebase_path(invoker.compile_dir, root_build_dir),
138                "--out-dir",
139                rebase_path(_out_dir, root_build_dir),
140                "--sources",
141              ] + rebase_path(invoker.sources, root_build_dir)
142
143      if (defined(invoker.plugin)) {
144        inputs = [ invoker.plugin ]
145        args +=
146            [ "--plugin-path=" + rebase_path(invoker.plugin, root_build_dir) ]
147      }
148
149      if (defined(invoker.outputs)) {
150        outputs = invoker.outputs
151      } else {
152        stamp = true
153      }
154
155      if (defined(invoker.metadata)) {
156        metadata = invoker.metadata
157      }
158    }
159
160    # Output a .json file with information about this proto library.
161    _proto_info = {
162      label = get_label_info(":${invoker.target_name}", "label_no_toolchain")
163      protoc_outputs =
164          rebase_path(get_target_outputs(":$target_name._gen"), root_build_dir)
165      root = rebase_path(_out_dir, root_build_dir)
166      package = invoker.package
167
168      nested_in_python_package = ""
169      if (defined(invoker.python_package)) {
170        nested_in_python_package =
171            get_label_info(invoker.python_package, "label_no_toolchain")
172      }
173
174      dependencies = []
175      foreach(dep, invoker.deps) {
176        dependencies +=
177            rebase_path([ get_label_info(dep, "target_gen_dir") + "/" +
178                              get_label_info(dep, "name") + ".json" ],
179                        root_build_dir)
180      }
181    }
182    write_file("$target_gen_dir/$target_name.json", _proto_info, "json")
183  } else {
184    # protoc is only ever invoked from pw_protobuf_compiler_TOOLCHAIN.
185    not_needed([ "target_name" ])
186    not_needed(invoker, "*")
187  }
188}
189
190# Generates pw_protobuf C++ code for proto files, creating a source_set of the
191# generated files. This is internal and should not be used outside of this file.
192# Use pw_proto_library instead.
193template("_pw_pwpb_rpc_proto_library") {
194  # Create a target which runs protoc configured with the pwpb_rpc plugin to
195  # generate the C++ proto RPC headers.
196  _pw_invoke_protoc(target_name) {
197    forward_variables_from(invoker, "*", _forwarded_vars)
198    language = "pwpb_rpc"
199    plugin = "$dir_pw_rpc/py/pw_rpc/plugin_pwpb.py"
200    python_deps = [ "$dir_pw_rpc/py" ]
201  }
202
203  # Create a library with the generated source files.
204  config("$target_name._include_path") {
205    include_dirs = [ "${invoker.base_out_dir}/pwpb_rpc" ]
206    visibility = [ ":*" ]
207  }
208
209  pw_source_set(target_name) {
210    forward_variables_from(invoker, _forwarded_vars)
211    public_configs = [ ":$target_name._include_path" ]
212    deps = [ ":$target_name._gen($pw_protobuf_compiler_TOOLCHAIN)" ]
213    public_deps = [
214                    ":${invoker.base_target}.pwpb",
215                    "$dir_pw_protobuf",
216                    "$dir_pw_rpc:server",
217                    "$dir_pw_rpc/pwpb:client_api",
218                    "$dir_pw_rpc/pwpb:server_api",
219                  ] + invoker.deps
220    public = invoker.outputs
221    check_includes = false
222  }
223}
224
225template("_pw_pwpb_proto_library") {
226  _pw_invoke_protoc(target_name) {
227    forward_variables_from(invoker, "*", _forwarded_vars)
228    language = "pwpb"
229    plugin = "$dir_pw_protobuf/py/pw_protobuf/plugin.py"
230    python_deps = [ "$dir_pw_protobuf/py" ]
231  }
232
233  # Create a library with the generated source files.
234  config("$target_name._include_path") {
235    include_dirs = [ "${invoker.base_out_dir}/pwpb" ]
236    visibility = [ ":*" ]
237  }
238
239  pw_source_set(target_name) {
240    forward_variables_from(invoker, _forwarded_vars)
241    public_configs = [ ":$target_name._include_path" ]
242    deps = [ ":$target_name._gen($pw_protobuf_compiler_TOOLCHAIN)" ]
243    public_deps = [
244                    "$dir_pw_containers:vector",
245                    "$dir_pw_string:string",
246                    dir_pw_assert,
247                    dir_pw_function,
248                    dir_pw_preprocessor,
249                    dir_pw_protobuf,
250                    dir_pw_result,
251                    dir_pw_status,
252                  ] + invoker.deps
253    sources = invoker.outputs
254    public = filter_include(sources, [ "*.pwpb.h" ])
255  }
256}
257
258# Generates nanopb RPC code for proto files, creating a source_set of the
259# generated files. This is internal and should not be used outside of this file.
260# Use pw_proto_library instead.
261template("_pw_nanopb_rpc_proto_library") {
262  # Create a target which runs protoc configured with the nanopb_rpc plugin to
263  # generate the C++ proto RPC headers.
264  _pw_invoke_protoc(target_name) {
265    forward_variables_from(invoker, "*", _forwarded_vars)
266    language = "nanopb_rpc"
267    plugin = "$dir_pw_rpc/py/pw_rpc/plugin_nanopb.py"
268    python_deps = [ "$dir_pw_rpc/py" ]
269  }
270
271  # Create a library with the generated source files.
272  config("$target_name._include_path") {
273    include_dirs = [ "${invoker.base_out_dir}/nanopb_rpc" ]
274    visibility = [ ":*" ]
275  }
276
277  pw_source_set(target_name) {
278    forward_variables_from(invoker, _forwarded_vars)
279    public_configs = [ ":$target_name._include_path" ]
280    deps = [ ":$target_name._gen($pw_protobuf_compiler_TOOLCHAIN)" ]
281    public_deps = [
282                    ":${invoker.base_target}.nanopb",
283                    "$dir_pw_rpc:server",
284                    "$dir_pw_rpc/nanopb:client_api",
285                    "$dir_pw_rpc/nanopb:server_api",
286                    "$dir_pw_third_party/nanopb",
287                  ] + invoker.deps
288    public = invoker.outputs
289    check_includes = false
290  }
291}
292
293# Generates nanopb code for proto files, creating a source_set of the generated
294# files. This is internal and should not be used outside of this file. Use
295# pw_proto_library instead.
296template("_pw_nanopb_proto_library") {
297  # When compiling with the Nanopb plugin, the nanopb.proto file is already
298  # compiled internally, so skip recompiling it with protoc.
299  if (rebase_path(invoker.sources, invoker.compile_dir) == [ "nanopb.proto" ]) {
300    group("$target_name._gen") {
301      deps = [
302        ":${invoker.base_target}._sources($pw_protobuf_compiler_TOOLCHAIN)",
303      ]
304    }
305
306    group("$target_name") {
307      deps = invoker.deps +
308             [ ":$target_name._gen($pw_protobuf_compiler_TOOLCHAIN)" ]
309    }
310  } else {
311    # Create a target which runs protoc configured with the nanopb plugin to
312    # generate the C proto sources.
313    _pw_invoke_protoc(target_name) {
314      forward_variables_from(invoker, "*", _forwarded_vars)
315      language = "nanopb"
316      plugin = "$dir_pw_third_party_nanopb/generator/protoc-gen-nanopb"
317      other_deps = [ "$dir_pw_third_party/nanopb:generate_nanopb_proto.action" ]
318    }
319
320    # Create a library with the generated source files.
321    config("$target_name._include_path") {
322      include_dirs = [ "${invoker.base_out_dir}/nanopb" ]
323      visibility = [ ":*" ]
324    }
325
326    pw_source_set(target_name) {
327      forward_variables_from(invoker, _forwarded_vars)
328      public_configs = [ ":$target_name._include_path" ]
329      deps = [ ":$target_name._gen($pw_protobuf_compiler_TOOLCHAIN)" ]
330      public_deps = [ "$dir_pw_third_party/nanopb" ] + invoker.deps
331      sources = invoker.outputs
332      public = filter_include(sources, [ "*.pb.h" ])
333    }
334  }
335}
336
337# Generates raw RPC code for proto files, creating a source_set of the generated
338# files. This is internal and should not be used outside of this file. Use
339# pw_proto_library instead.
340template("_pw_raw_rpc_proto_library") {
341  # Create a target which runs protoc configured with the nanopb_rpc plugin to
342  # generate the C++ proto RPC headers.
343  _pw_invoke_protoc(target_name) {
344    forward_variables_from(invoker, "*", _forwarded_vars)
345    language = "raw_rpc"
346    plugin = "$dir_pw_rpc/py/pw_rpc/plugin_raw.py"
347    python_deps = [ "$dir_pw_rpc/py" ]
348  }
349
350  # Create a library with the generated source files.
351  config("$target_name._include_path") {
352    include_dirs = [ "${invoker.base_out_dir}/raw_rpc" ]
353    visibility = [ ":*" ]
354  }
355
356  pw_source_set(target_name) {
357    forward_variables_from(invoker, _forwarded_vars)
358    public_configs = [ ":$target_name._include_path" ]
359    deps = [ ":$target_name._gen($pw_protobuf_compiler_TOOLCHAIN)" ]
360    public_deps = [
361                    "$dir_pw_rpc:server",
362                    "$dir_pw_rpc/raw:client_api",
363                    "$dir_pw_rpc/raw:server_api",
364                  ] + invoker.deps
365    public = invoker.outputs
366    check_includes = false
367  }
368}
369
370# Generates Go code for proto files, listing the proto output directory in the
371# metadata variable GOPATH. Internal use only.
372template("_pw_go_proto_library") {
373  _proto_gopath = "$root_gen_dir/go"
374
375  _pw_invoke_protoc(target_name) {
376    forward_variables_from(invoker, "*")
377    language = "go"
378    metadata = {
379      gopath = [ "GOPATH+=" + rebase_path(_proto_gopath) ]
380      external_deps = [
381        "github.com/golang/protobuf/proto",
382        "google.golang.org/grpc",
383      ]
384    }
385
386    # Override the default "$base_out_dir/$language" output path.
387    out_dir = "$_proto_gopath/src"
388  }
389
390  group(target_name) {
391    deps =
392        invoker.deps + [ ":$target_name._gen($pw_protobuf_compiler_TOOLCHAIN)" ]
393  }
394}
395
396# Generates Python code for proto files, creating a pw_python_package containing
397# the generated files. This is internal and should not be used outside of this
398# file. Use pw_proto_library instead.
399template("_pw_python_proto_library") {
400  _pw_invoke_protoc(target_name) {
401    forward_variables_from(invoker, "*", _forwarded_vars + [ "python_package" ])
402    language = "python"
403
404    if (defined(invoker.python_package)) {
405      python_package = invoker.python_package
406    }
407  }
408
409  if (defined(invoker.python_package) && invoker.python_package != "") {
410    # This package is nested in another Python package. Depending on this
411    # its python subtarget is equivalent to depending on the Python package it
412    # is nested in.
413    pw_python_group(target_name) {
414      python_deps = [ invoker.python_package ]
415    }
416
417    # This proto library is merged into another package, but create a target to
418    # collect its dependencies that the other package can depend on.
419    pw_python_group(target_name + "._deps") {
420      python_deps = invoker.deps
421      other_deps =
422          [ ":${invoker.target_name}._gen($pw_protobuf_compiler_TOOLCHAIN)" ]
423    }
424  } else {
425    # Create a Python package with the generated source files.
426    pw_python_package(target_name) {
427      forward_variables_from(invoker, _forwarded_vars)
428      _target_dir =
429          get_path_info(get_label_info(":${invoker.base_target}", "dir"),
430                        "abspath")
431      generate_setup = {
432        metadata = {
433          # Default to a package name that include the full source path to avoid
434          # conflicts with other packages when collecting all the .whl files
435          # with pw_python_wheels().
436          name =
437              string_replace(string_replace(_target_dir, "//", ""), "/", "_") +
438              "_" + invoker.base_target
439
440          # The package name should match where the __init__.py lives. If
441          # module_as_package is specified use that for the Python package name.
442          if (defined(invoker.module_as_package) &&
443              invoker.module_as_package != "") {
444            name = invoker.module_as_package
445          }
446          version =
447              "0.0.1"  # TODO(hepler): Need to be able to set this verison.
448        }
449      }
450      sources = invoker.outputs
451      strip_prefix = "${invoker.base_out_dir}/python"
452      python_deps = invoker.deps
453      other_deps = [ ":$target_name._gen($pw_protobuf_compiler_TOOLCHAIN)" ]
454      static_analysis = []
455
456      _pw_module_as_package = invoker.module_as_package != ""
457    }
458  }
459}
460
461# Generates protobuf code from .proto definitions for various languages.
462# For each supported generator, creates a sub-target named:
463#
464#   <target_name>.<generator>
465#
466# GN permits using abbreviated labels when the target name matches the directory
467# name (e.g. //foo for //foo:foo). For consistency with this, the sub-targets
468# for each generator are aliased to the directory when the target name is the
469# same. For example, these two labels are equivalent:
470#
471#   //path/to/my_protos:my_protos.pwpb
472#   //path/to/my_protos:pwpb
473#
474# pw_protobuf_library targets generate Python packages. As such, they must have
475# globally unique package names. The first directory of the prefix or the first
476# common directory of the sources is used as the Python package.
477#
478# Args:
479#   sources: List of input .proto files.
480#   deps: List of other pw_proto_library dependencies.
481#   other_deps: List of other non-proto dependencies.
482#   inputs: Other files on which the protos depend (e.g. nanopb .options files).
483#   prefix: A prefix to add to the source protos prior to compilation. For
484#       example, a source called "foo.proto" with prefix = "nested" will be
485#       compiled with protoc as "nested/foo.proto".
486#   strip_prefix: Remove this prefix from the source protos. All source and
487#       input files must be nested under this path.
488#   python_package: Label of Python package to which to add the proto modules.
489#       The .python subtarget will redirect to this package.
490#
491template("pw_proto_library") {
492  assert(defined(invoker.sources) && invoker.sources != [],
493         "pw_proto_library requires .proto source files")
494
495  if (defined(invoker.python_module_as_package)) {
496    _module_as_package = invoker.python_module_as_package
497
498    _must_be_one_source = invoker.sources
499    assert([ _must_be_one_source[0] ] == _must_be_one_source,
500           "'python_module_as_package' requires exactly one source file")
501    assert(_module_as_package != "",
502           "'python_module_as_package' cannot be be empty")
503    assert(string_split(_module_as_package, "/") == [ _module_as_package ],
504           "'python_module_as_package' cannot contain slashes")
505    assert(!defined(invoker.prefix),
506           "'prefix' cannot be provided with 'python_module_as_package'")
507  } else {
508    _module_as_package = ""
509  }
510
511  if (defined(invoker.strip_prefix)) {
512    _source_root = get_path_info(invoker.strip_prefix, "abspath")
513  } else {
514    _source_root = get_path_info(".", "abspath")
515  }
516
517  if (defined(invoker.prefix)) {
518    _prefix = invoker.prefix
519  } else {
520    _prefix = ""
521  }
522
523  _root_dir_name = ""
524  _source_names = []
525
526  # Determine the Python package name to use for these protos. If there is no
527  # prefix, the first directory the sources are nested under is used.
528  foreach(source, rebase_path(invoker.sources, _source_root)) {
529    _path_components = []
530    _path_components = string_split(source, "/")
531
532    if (_root_dir_name == "") {
533      _root_dir_name = _path_components[0]
534    } else {
535      assert(_prefix != "" || _path_components[0] == _root_dir_name,
536             "Unless 'prefix' is supplied, all .proto sources in a " +
537                 "pw_proto_library must be in the same directory tree")
538    }
539
540    _source_names +=
541        [ get_path_info(source, "dir") + "/" + get_path_info(source, "name") ]
542  }
543
544  # If the 'prefix' was supplied, use that for the package directory.
545  if (_prefix != "") {
546    _prefix_path_components = string_split(_prefix, "/")
547    _root_dir_name = _prefix_path_components[0]
548  }
549
550  assert(
551      _root_dir_name != "" && _root_dir_name != "." && _root_dir_name != "..",
552      "Either a 'prefix' must be specified or all sources must be nested " +
553          "under a common directory")
554
555  if (defined(invoker.deps)) {
556    _deps = invoker.deps
557  } else {
558    _deps = []
559  }
560
561  _common = {
562    base_target = target_name
563
564    # This is the output directory for all files related to this proto library.
565    # Sources are mirrored to "$base_out_dir/sources" and protoc puts outputs in
566    # "$base_out_dir/$language" by default.
567    base_out_dir =
568        get_label_info(":$target_name($pw_protobuf_compiler_TOOLCHAIN)",
569                       "target_gen_dir") + "/$target_name.proto_library"
570
571    compile_dir = "$base_out_dir/sources"
572
573    # Refer to the source files as the are mirrored to the output directory.
574    sources = []
575    foreach(file, rebase_path(invoker.sources, _source_root)) {
576      sources += [ "$compile_dir/$_prefix/$file" ]
577    }
578
579    package = _root_dir_name
580  }
581
582  # For each proto target, create a file which collects the base directories of
583  # all of its dependencies to list as include paths to protoc.
584  generated_file("$target_name._includes") {
585    # Collect metadata from the include path files of each dependency.
586
587    deps = []
588    foreach(dep, _deps) {
589      _base = get_label_info(dep, "label_no_toolchain")
590      deps += [ "$_base._includes(" + get_label_info(dep, "toolchain") + ")" ]
591    }
592
593    data_keys = [ "protoc_includes" ]
594    outputs = [ "${_common.base_out_dir}/includes.txt" ]
595
596    # Indicate this library's base directory for its dependents.
597    metadata = {
598      protoc_includes = [ rebase_path(_common.compile_dir, root_build_dir) ]
599    }
600  }
601
602  # Mirror the proto sources to the output directory with the prefix added.
603  if (current_toolchain == pw_protobuf_compiler_TOOLCHAIN) {
604    pw_mirror_tree("$target_name._sources") {
605      source_root = _source_root
606      sources = invoker.sources
607      if (defined(invoker.other_deps)) {
608        deps = invoker.other_deps
609      }
610
611      if (defined(invoker.inputs)) {
612        sources += invoker.inputs
613      }
614
615      directory = "${_common.compile_dir}/$_prefix"
616    }
617  } else {
618    not_needed(invoker,
619               [
620                 "inputs",
621                 "other_deps",
622               ])
623  }
624
625  # Enumerate all of the protobuf generator targets.
626
627  _pw_pwpb_rpc_proto_library("$target_name.pwpb_rpc") {
628    forward_variables_from(invoker, _forwarded_vars)
629    forward_variables_from(_common, "*")
630
631    deps = []
632    foreach(dep, _deps) {
633      _base = get_label_info(dep, "label_no_toolchain")
634      deps += [ "$_base.pwpb_rpc(" + get_label_info(dep, "toolchain") + ")" ]
635    }
636
637    outputs = []
638    foreach(name, _source_names) {
639      outputs += [ "$base_out_dir/pwpb_rpc/$_prefix/${name}.rpc.pwpb.h" ]
640    }
641  }
642
643  _pw_pwpb_proto_library("$target_name.pwpb") {
644    forward_variables_from(invoker, _forwarded_vars)
645    forward_variables_from(_common, "*")
646
647    deps = []
648    foreach(dep, _deps) {
649      _base = get_label_info(dep, "label_no_toolchain")
650      deps += [ "$_base.pwpb(" + get_label_info(dep, "toolchain") + ")" ]
651    }
652
653    outputs = []
654    foreach(name, _source_names) {
655      outputs += [ "$base_out_dir/pwpb/$_prefix/${name}.pwpb.h" ]
656    }
657  }
658
659  if (dir_pw_third_party_nanopb != "") {
660    _pw_nanopb_rpc_proto_library("$target_name.nanopb_rpc") {
661      forward_variables_from(invoker, _forwarded_vars)
662      forward_variables_from(_common, "*")
663
664      deps = []
665      foreach(dep, _deps) {
666        _lbl = get_label_info(dep, "label_no_toolchain")
667        deps += [ "$_lbl.nanopb_rpc(" + get_label_info(dep, "toolchain") + ")" ]
668      }
669
670      outputs = []
671      foreach(name, _source_names) {
672        outputs += [ "$base_out_dir/nanopb_rpc/$_prefix/${name}.rpc.pb.h" ]
673      }
674    }
675
676    _pw_nanopb_proto_library("$target_name.nanopb") {
677      forward_variables_from(invoker, _forwarded_vars)
678      forward_variables_from(_common, "*")
679
680      deps = []
681      foreach(dep, _deps) {
682        _base = get_label_info(dep, "label_no_toolchain")
683        deps += [ "$_base.nanopb(" + get_label_info(dep, "toolchain") + ")" ]
684      }
685
686      outputs = []
687      foreach(name, _source_names) {
688        outputs += [
689          "$base_out_dir/nanopb/$_prefix/${name}.pb.h",
690          "$base_out_dir/nanopb/$_prefix/${name}.pb.c",
691        ]
692      }
693    }
694  } else {
695    pw_error("$target_name.nanopb_rpc") {
696      message =
697          "\$dir_pw_third_party_nanopb must be set to generate nanopb RPC code."
698    }
699
700    pw_error("$target_name.nanopb") {
701      message =
702          "\$dir_pw_third_party_nanopb must be set to compile nanopb protobufs."
703    }
704  }
705
706  _pw_raw_rpc_proto_library("$target_name.raw_rpc") {
707    forward_variables_from(invoker, _forwarded_vars)
708    forward_variables_from(_common, "*")
709
710    deps = []
711    foreach(dep, _deps) {
712      _base = get_label_info(dep, "label_no_toolchain")
713      deps += [ "$_base.raw_rpc(" + get_label_info(dep, "toolchain") + ")" ]
714    }
715
716    outputs = []
717    foreach(name, _source_names) {
718      outputs += [ "$base_out_dir/raw_rpc/$_prefix/${name}.raw_rpc.pb.h" ]
719    }
720  }
721
722  _pw_go_proto_library("$target_name.go") {
723    sources = _common.sources
724
725    deps = []
726    foreach(dep, _deps) {
727      _base = get_label_info(dep, "label_no_toolchain")
728      deps += [ "$_base.go(" + get_label_info(dep, "toolchain") + ")" ]
729    }
730
731    forward_variables_from(_common, "*")
732  }
733
734  _pw_python_proto_library("$target_name.python") {
735    forward_variables_from(_common, "*")
736    forward_variables_from(invoker, [ "python_package" ])
737    module_as_package = _module_as_package
738
739    deps = []
740    foreach(dep, _deps) {
741      _base = get_label_info(dep, "label_no_toolchain")
742      deps += [ "$_base.python(" + get_label_info(dep, "toolchain") + ")" ]
743    }
744
745    if (module_as_package == "") {
746      _python_prefix = "$base_out_dir/python/$_prefix"
747    } else {
748      _python_prefix = "$base_out_dir/python/$module_as_package"
749    }
750
751    outputs = []
752    foreach(name, _source_names) {
753      outputs += [
754        "$_python_prefix/${name}_pb2.py",
755        "$_python_prefix/${name}_pb2.pyi",
756      ]
757    }
758  }
759
760  # All supported pw_protobuf generators.
761  _protobuf_generators = [
762    "pwpb",
763    "pwpb_rpc",
764    "nanopb",
765    "nanopb_rpc",
766    "raw_rpc",
767    "go",
768    "python",
769  ]
770
771  # If the label matches the directory name, alias the subtargets to the
772  # directory (e.g. //foo:nanopb is an alias for //foo:foo.nanopb).
773  if (get_label_info(":$target_name", "name") ==
774      get_path_info(get_label_info(":$target_name", "dir"), "name")) {
775    foreach(_generator, _protobuf_generators - [ "python" ]) {
776      group(_generator) {
777        public_deps = [ ":${invoker.target_name}.$_generator" ]
778      }
779    }
780
781    pw_python_group("python") {
782      python_deps = [ ":${invoker.target_name}.python" ]
783    }
784  }
785
786  # If the user attempts to use the target directly instead of one of the
787  # generator targets, run a script which prints a nice error message.
788  pw_python_action(target_name) {
789    script = string_join("/",
790                         [
791                           dir_pw_protobuf_compiler,
792                           "py",
793                           "pw_protobuf_compiler",
794                           "proto_target_invalid.py",
795                         ])
796    args = [
797             "--target",
798             target_name,
799             "--dir",
800             get_path_info(".", "abspath"),
801             "--root",
802             "//",
803           ] + _protobuf_generators
804    stamp = true
805  }
806}
807