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