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