|  | # Copyright 2020 The Pigweed Authors | 
|  | # | 
|  | # Licensed under the Apache License, Version 2.0 (the "License"); you may not | 
|  | # use this file except in compliance with the License. You may obtain a copy of | 
|  | # the License at | 
|  | # | 
|  | #     https://www.apache.org/licenses/LICENSE-2.0 | 
|  | # | 
|  | # Unless required by applicable law or agreed to in writing, software | 
|  | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | 
|  | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | 
|  | # License for the specific language governing permissions and limitations under | 
|  | # the License. | 
|  |  | 
|  | # gn-format disable | 
|  | import("//build_overrides/pigweed.gni") | 
|  |  | 
|  | import("$dir_pw_build/input_group.gni") | 
|  | import("$dir_pw_build/python_script.gni") | 
|  | import("$dir_pw_build/target_types.gni") | 
|  | import("nanopb.gni") | 
|  | declare_args() { | 
|  | # Generators with which to compile protobuf code. These are used by the | 
|  | # pw_proto_library template to determine which build targets to create. | 
|  | # | 
|  | # Supported generators: | 
|  | #   "pwpb", "nanopb", "go" | 
|  | pw_protobuf_GENERATORS = [ | 
|  | "pwpb", | 
|  | "go", | 
|  | ] | 
|  | } | 
|  |  | 
|  | # Python script that invokes protoc. | 
|  | _gen_script_path = | 
|  | "$dir_pw_protobuf_compiler/py/pw_protobuf_compiler/generate_protos.py" | 
|  |  | 
|  | _forwarded_vars = [ | 
|  | "testonly", | 
|  | "visibility", | 
|  | ] | 
|  |  | 
|  | # Generates pw_protobuf C++ code for proto files, creating a source_set of the | 
|  | # generated files. This is internal and should not be used outside of this file. | 
|  | # Use pw_proto_library instead. | 
|  | # | 
|  | # Args: | 
|  | #  protos: List of input .proto files. | 
|  | template("_pw_pwpb_proto_library") { | 
|  | _proto_gen_dir = "$root_gen_dir/protos" | 
|  | _module_path = get_path_info(".", "abspath") | 
|  | _relative_proto_paths = rebase_path(invoker.protos, _module_path) | 
|  |  | 
|  | _outputs = [] | 
|  | foreach(_proto, _relative_proto_paths) { | 
|  | _output = string_replace(_proto, ".proto", ".pwpb.h") | 
|  | _outputs += [ "$_proto_gen_dir/$_output" ] | 
|  | } | 
|  |  | 
|  | _gen_target = "${target_name}_gen" | 
|  | pw_python_script(_gen_target) { | 
|  | forward_variables_from(invoker, _forwarded_vars) | 
|  | script = _gen_script_path | 
|  | args = [ | 
|  | "--language", | 
|  | "cc", | 
|  | "--module-path", | 
|  | _module_path, | 
|  | "--include-file", | 
|  | invoker.include_file, | 
|  | "--out-dir", | 
|  | _proto_gen_dir, | 
|  | ] + get_path_info(invoker.protos, "abspath") | 
|  | inputs = invoker.protos | 
|  | outputs = _outputs | 
|  | deps = invoker.deps | 
|  | if (defined(invoker.protoc_deps)) { | 
|  | deps += invoker.protoc_deps | 
|  | } | 
|  | } | 
|  |  | 
|  | # For C++ proto files, the generated proto directory is added as an include | 
|  | # path for the code. This requires using "all_dependent_configs" to force the | 
|  | # include on any code that transitively depends on the generated protos. | 
|  | _include_config_target = "${target_name}_includes" | 
|  | config(_include_config_target) { | 
|  | include_dirs = [ "$_proto_gen_dir" ] | 
|  | } | 
|  |  | 
|  | # Create a library with the generated source files. | 
|  | pw_source_set(target_name) { | 
|  | all_dependent_configs = [ ":$_include_config_target" ] | 
|  | deps = [ ":$_gen_target" ] | 
|  | public_deps = [ dir_pw_protobuf ] + invoker.gen_deps | 
|  | sources = get_target_outputs(":$_gen_target") | 
|  | public = filter_include(sources, [ "*.pwpb.h" ]) | 
|  | } | 
|  | } | 
|  |  | 
|  | # Generates nanopb code for proto files, creating a source_set of the generated | 
|  | # files. This is internal and should not be used outside of this file. Use | 
|  | # pw_proto_library instead. | 
|  | # | 
|  | # Args: | 
|  | #  protos: List of input .proto files. | 
|  | template("_pw_nanopb_proto_library") { | 
|  | assert(defined(dir_pw_third_party_nanopb) && dir_pw_third_party_nanopb != "", | 
|  | "\$dir_pw_third_party_nanopb must be set to compile nanopb protobufs") | 
|  |  | 
|  | _proto_gen_dir = "$root_gen_dir/protos" | 
|  | _module_path = get_path_info(".", "abspath") | 
|  | _relative_proto_paths = rebase_path(invoker.protos, _module_path) | 
|  |  | 
|  | _outputs = [] | 
|  | foreach(_proto, _relative_proto_paths) { | 
|  | _output_h = string_replace(_proto, ".proto", ".pb.h") | 
|  | _output_c = string_replace(_proto, ".proto", ".pb.c") | 
|  | _outputs += [ | 
|  | "$_proto_gen_dir/$_output_h", | 
|  | "$_proto_gen_dir/$_output_c", | 
|  | ] | 
|  | } | 
|  |  | 
|  | _nanopb_plugin = "$dir_pw_third_party_nanopb/generator/protoc-gen-nanopb" | 
|  | if (host_os == "win") { | 
|  | _nanopb_plugin += ".bat" | 
|  | } | 
|  |  | 
|  | # Create a target which runs protoc configured with the nanopb plugin to | 
|  | # generate the C proto sources. | 
|  | _gen_target = "${target_name}_gen" | 
|  | pw_python_script(_gen_target) { | 
|  | forward_variables_from(invoker, _forwarded_vars) | 
|  | script = _gen_script_path | 
|  | args = [ | 
|  | "--language", | 
|  | "nanopb", | 
|  | "--module-path", | 
|  | _module_path, | 
|  | "--include-paths", | 
|  | "$dir_pw_third_party_nanopb/generator/proto", | 
|  | "--include-file", | 
|  | invoker.include_file, | 
|  | "--out-dir", | 
|  | _proto_gen_dir, | 
|  | "--custom-plugin", | 
|  | get_path_info(_nanopb_plugin, "abspath"), | 
|  | ] + get_path_info(invoker.protos, "abspath") | 
|  | inputs = invoker.protos | 
|  | outputs = _outputs | 
|  |  | 
|  | deps = invoker.deps | 
|  | if (defined(invoker.protoc_deps)) { | 
|  | deps += invoker.protoc_deps | 
|  | } | 
|  | } | 
|  |  | 
|  | # For C++ proto files, the generated proto directory is added as an include | 
|  | # path for the code. This requires using "all_dependent_configs" to force the | 
|  | # include on any code that transitively depends on the generated protos. | 
|  | _include_root = rebase_path(get_path_info(".", "abspath"), "//") | 
|  | _include_config_target = "${target_name}_includes" | 
|  | config(_include_config_target) { | 
|  | include_dirs = [ | 
|  | "$_proto_gen_dir", | 
|  | "$_proto_gen_dir/$_include_root", | 
|  | ] | 
|  | } | 
|  |  | 
|  | # Create a library with the generated source files. | 
|  | pw_source_set(target_name) { | 
|  | all_dependent_configs = [ ":$_include_config_target" ] | 
|  | deps = [ ":$_gen_target" ] | 
|  | public_deps = [ dir_pw_third_party_nanopb ] + invoker.gen_deps | 
|  | sources = get_target_outputs(":$_gen_target") | 
|  | public = filter_include(sources, [ "*.pb.h" ]) | 
|  | } | 
|  | } | 
|  |  | 
|  | # Generates Go code for proto files, listing the proto output directory in the | 
|  | # metadata variable GOPATH. Internal use only. | 
|  | # | 
|  | # Args: | 
|  | #  protos: List of input .proto files. | 
|  | template("_pw_go_proto_library") { | 
|  | _proto_gopath = "$root_gen_dir/go" | 
|  | _proto_gen_dir = "$_proto_gopath/src" | 
|  | _rebased_gopath = rebase_path(_proto_gopath) | 
|  |  | 
|  | pw_python_script(target_name) { | 
|  | forward_variables_from(invoker, _forwarded_vars) | 
|  | metadata = { | 
|  | gopath = [ "GOPATH+=$_rebased_gopath" ] | 
|  | external_deps = [ | 
|  | "github.com/golang/protobuf/proto", | 
|  | "google.golang.org/grpc", | 
|  | ] | 
|  | } | 
|  | script = _gen_script_path | 
|  | args = [ | 
|  | "--language", | 
|  | "go", | 
|  | "--module-path", | 
|  | "//", | 
|  | "--include-file", | 
|  | invoker.include_file, | 
|  | "--out-dir", | 
|  | _proto_gen_dir, | 
|  | ] + get_path_info(invoker.protos, "abspath") | 
|  | inputs = invoker.protos | 
|  | deps = invoker.deps + invoker.gen_deps | 
|  | stamp = true | 
|  | } | 
|  | } | 
|  |  | 
|  | # Generates protobuf code from .proto definitions for various languages. | 
|  | # | 
|  | # The generators to use are defined in the pw_protobuf_GENERATORS build | 
|  | # variable. Each listed generator creates a generated code target called | 
|  | # | 
|  | #   <target_name>_<generator> | 
|  | # | 
|  | # For example, with the following definitions: | 
|  | # | 
|  | #   pw_protobuf_GENERATORS = [ "pwpb", "py" ] | 
|  | # | 
|  | #   pw_proto_library("my_protos") { | 
|  | #     sources = [ "foo.proto" ] | 
|  | #   } | 
|  | # | 
|  | # Two build targets will be created for the declared "my_protos" target. | 
|  | # | 
|  | #   "my_protos_pwpb"  <-- C++ source_set containing generated proto code | 
|  | #   "my_protos_py"    <-- Python module containing generated proto code | 
|  | # | 
|  | # Args: | 
|  | #  sources: List of input .proto files. | 
|  | #  deps: List of other pw_proto_library dependencies. | 
|  | #  inputs: Other files on which the protos depend (e.g. nanopb .options files). | 
|  | template("pw_proto_library") { | 
|  | assert(defined(invoker.sources) && invoker.sources != [], | 
|  | "pw_proto_library requires .proto source files") | 
|  |  | 
|  | # For each proto target, create a file which collects the base directories of | 
|  | # all of its dependencies to list as include paths to protoc. | 
|  | _include_metadata_target = "${target_name}_include_paths" | 
|  | _include_metadata_file = "${target_gen_dir}/${target_name}_includes.txt" | 
|  | generated_file(_include_metadata_target) { | 
|  | if (defined(invoker.deps)) { | 
|  | # Collect metadata from the include path files of each dependency. | 
|  | deps = process_file_template(invoker.deps, "{{source}}_include_paths") | 
|  | } else { | 
|  | deps = [] | 
|  | } | 
|  | data_keys = [ "protoc_includes" ] | 
|  | outputs = [ _include_metadata_file ] | 
|  |  | 
|  | # Indicate this library's base directory for its dependents. | 
|  | metadata = { | 
|  | protoc_includes = [ rebase_path(".", root_out_dir) ] | 
|  | } | 
|  | } | 
|  |  | 
|  | _deps = [ ":$_include_metadata_target" ] | 
|  |  | 
|  | if (defined(invoker.inputs)) { | 
|  | # Toss any additional inputs into an input group dependency. | 
|  | _input_target_name = "${target_name}_inputs" | 
|  | pw_input_group(_input_target_name) { | 
|  | inputs = invoker.inputs | 
|  | } | 
|  | _deps += [ ":$_input_target_name" ] | 
|  | } | 
|  |  | 
|  | foreach(_gen, pw_protobuf_GENERATORS) { | 
|  | _lang_target = "${target_name}_${_gen}" | 
|  | _gen_deps = [] | 
|  | if (defined(invoker.deps)) { | 
|  | _gen_deps = process_file_template(invoker.deps, "{{source}}_${_gen}") | 
|  | } | 
|  |  | 
|  | if (_gen == "pwpb") { | 
|  | _pw_pwpb_proto_library(_lang_target) { | 
|  | forward_variables_from(invoker, _forwarded_vars) | 
|  | protos = invoker.sources | 
|  | deps = _deps | 
|  | include_file = _include_metadata_file | 
|  | gen_deps = _gen_deps | 
|  |  | 
|  | # List the pw_protobuf plugin's files as a dependency to recompile | 
|  | # generated code if they are modified. | 
|  | protoc_deps = [ "$dir_pw_protobuf:codegen_protoc_plugin" ] | 
|  | } | 
|  | } else if (_gen == "nanopb") { | 
|  | _pw_nanopb_proto_library(_lang_target) { | 
|  | forward_variables_from(invoker, _forwarded_vars) | 
|  | protos = invoker.sources | 
|  | deps = _deps | 
|  | include_file = _include_metadata_file | 
|  | gen_deps = _gen_deps | 
|  | } | 
|  | } else if (_gen == "go") { | 
|  | _pw_go_proto_library(_lang_target) { | 
|  | forward_variables_from(invoker, _forwarded_vars) | 
|  | protos = invoker.sources | 
|  | deps = _deps | 
|  | include_file = _include_metadata_file | 
|  | gen_deps = _gen_deps | 
|  | } | 
|  | } else { | 
|  | assert(false, | 
|  | string_join( | 
|  | " ", | 
|  | [ | 
|  | "pw_proto_library doesn't know how to generate code for", | 
|  | "generator '$_gen'. Please add support if you require it.", | 
|  | ])) | 
|  | } | 
|  | } | 
|  |  | 
|  | # All supported pw_protobuf generators. | 
|  | _protobuf_generators = [ | 
|  | "pwpb", | 
|  | "nanopb", | 
|  | "go", | 
|  | ] | 
|  |  | 
|  | # Create stub versions of the proto library for other protobuf generators. | 
|  | foreach(_gen, _protobuf_generators - pw_protobuf_GENERATORS) { | 
|  | pw_python_script("${target_name}_${_gen}") { | 
|  | forward_variables_from(invoker, _forwarded_vars) | 
|  | script = string_join("/", | 
|  | [ | 
|  | dir_pw_protobuf_compiler, | 
|  | "py", | 
|  | "pw_protobuf_compiler", | 
|  | "generator_not_selected.py", | 
|  | ]) | 
|  | args = [ | 
|  | "--library", | 
|  | "${target_name}_${_gen}", | 
|  | "--generator", | 
|  | _gen, | 
|  | ] | 
|  | inputs = invoker.sources | 
|  | stamp = true | 
|  | } | 
|  | } | 
|  |  | 
|  | # If the user attempts to use the target directly instead of one of the | 
|  | # generator targets, run a script which prints a nice error message. | 
|  | pw_python_script(target_name) { | 
|  | script = string_join("/", | 
|  | [ | 
|  | dir_pw_protobuf_compiler, | 
|  | "py", | 
|  | "pw_protobuf_compiler", | 
|  | "proto_target_invalid.py", | 
|  | ]) | 
|  | args = [ | 
|  | "--target", | 
|  | target_name, | 
|  | "--dir", | 
|  | get_path_info(".", "abspath"), | 
|  | "--root", | 
|  | "//", | 
|  | ] + pw_protobuf_GENERATORS | 
|  | stamp = true | 
|  | } | 
|  | } |