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