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