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