blob: 4a26bdc562d82722e6ce7a00facc6f73b6ebf4c3 [file] [log] [blame]
Alexei Frolove19ebb82020-05-14 17:21:20 -07001# Copyright 2020 The Pigweed Authors
Alexei Frolov942adf02019-12-11 17:07:28 -08002#
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
Alexei Frolov05d8ef22020-06-08 10:32:29 -070015import("$dir_pw_build/input_group.gni")
Alexei Frolov942adf02019-12-11 17:07:28 -080016import("$dir_pw_build/python_script.gni")
17
18# Python script that invokes protoc.
19_gen_script_path =
20 "$dir_pw_protobuf_compiler/py/pw_protobuf_compiler/generate_protos.py"
21
Wyatt Heplere0c4fdc2020-05-29 17:26:19 -070022_forwarded_vars = [
23 "testonly",
24 "visibility",
25]
26
Alexei Frolovf39cd8b2020-04-13 17:59:20 -070027# Generates pw_protobuf C++ code for proto files, creating a source_set of the
28# generated files. This is internal and should not be used outside of this file.
29# Use pw_proto_library instead.
Alexei Frolov942adf02019-12-11 17:07:28 -080030#
31# Args:
32# protos: List of input .proto files.
Alexei Frolovf39cd8b2020-04-13 17:59:20 -070033template("_pw_pwpb_proto_library") {
Alexei Frolov942adf02019-12-11 17:07:28 -080034 _proto_gen_dir = "$root_gen_dir/protos"
Alexei Frolove19ebb82020-05-14 17:21:20 -070035 _module_path = get_path_info(".", "abspath")
36 _relative_proto_paths = rebase_path(invoker.protos, _module_path)
37
38 _outputs = []
39 foreach(_proto, _relative_proto_paths) {
40 _output = string_replace(_proto, ".proto", ".pwpb.h")
41 _outputs += [ "$_proto_gen_dir/$_output" ]
42 }
Alexei Frolov942adf02019-12-11 17:07:28 -080043
44 _gen_target = "${target_name}_gen"
45 pw_python_script(_gen_target) {
Wyatt Heplere0c4fdc2020-05-29 17:26:19 -070046 forward_variables_from(invoker, _forwarded_vars)
Alexei Frolov942adf02019-12-11 17:07:28 -080047 script = _gen_script_path
48 args = [
49 "--language",
50 "cc",
51 "--module-path",
Alexei Frolove19ebb82020-05-14 17:21:20 -070052 _module_path,
53 "--include-file",
54 invoker.include_file,
Alexei Frolov942adf02019-12-11 17:07:28 -080055 "--out-dir",
56 _proto_gen_dir,
57 ] + get_path_info(invoker.protos, "abspath")
58 inputs = invoker.protos
59 outputs = _outputs
Alexei Frolove19ebb82020-05-14 17:21:20 -070060 deps = invoker.deps
Alexei Frolovbbf164c2019-12-16 12:51:59 -080061 if (defined(invoker.protoc_deps)) {
Alexei Frolove19ebb82020-05-14 17:21:20 -070062 deps += invoker.protoc_deps
Alexei Frolovbbf164c2019-12-16 12:51:59 -080063 }
Alexei Frolov942adf02019-12-11 17:07:28 -080064 }
65
66 # For C++ proto files, the generated proto directory is added as an include
67 # path for the code. This requires using "all_dependent_configs" to force the
68 # include on any code that transitively depends on the generated protos.
Alexei Frolov942adf02019-12-11 17:07:28 -080069 _include_config_target = "${target_name}_includes"
70 config(_include_config_target) {
Alexei Frolove19ebb82020-05-14 17:21:20 -070071 include_dirs = [ "$_proto_gen_dir" ]
Alexei Frolov942adf02019-12-11 17:07:28 -080072 }
73
74 # Create a library with the generated source files.
Alexei Frolov942adf02019-12-11 17:07:28 -080075 source_set(target_name) {
76 all_dependent_configs = [ ":$_include_config_target" ]
Alexei Frolove19ebb82020-05-14 17:21:20 -070077 deps = [ ":$_gen_target" ]
78 public_deps = [ dir_pw_protobuf ] + invoker.gen_deps
Alexei Frolov942adf02019-12-11 17:07:28 -080079 sources = get_target_outputs(":$_gen_target")
Alexei Frolovf39cd8b2020-04-13 17:59:20 -070080 public = filter_include(sources, [ "*.pwpb.h" ])
81 }
82}
83
84# Generates nanopb code for proto files, creating a source_set of the generated
85# files. This is internal and should not be used outside of this file. Use
86# pw_proto_library instead.
87#
88# Args:
89# protos: List of input .proto files.
90template("_pw_nanopb_proto_library") {
91 assert(defined(dir_third_party_nanopb) && dir_third_party_nanopb != "",
92 "\$dir_third_party_nanopb must be set to compile nanopb protobufs")
93
94 _proto_gen_dir = "$root_gen_dir/protos"
Alexei Frolove19ebb82020-05-14 17:21:20 -070095 _module_path = get_path_info(".", "abspath")
96 _relative_proto_paths = rebase_path(invoker.protos, _module_path)
97
98 _outputs = []
99 foreach(_proto, _relative_proto_paths) {
100 _output_h = string_replace(_proto, ".proto", ".pb.h")
101 _output_c = string_replace(_proto, ".proto", ".pb.c")
102 _outputs += [
103 "$_proto_gen_dir/$_output_h",
104 "$_proto_gen_dir/$_output_c",
105 ]
106 }
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700107
108 _nanopb_plugin = "$dir_third_party_nanopb/generator/protoc-gen-nanopb"
109 if (host_os == "win") {
110 _nanopb_plugin += ".bat"
111 }
112
113 # Create a target which runs protoc configured with the nanopb plugin to
114 # generate the C proto sources.
115 _gen_target = "${target_name}_gen"
116 pw_python_script(_gen_target) {
Wyatt Heplere0c4fdc2020-05-29 17:26:19 -0700117 forward_variables_from(invoker, _forwarded_vars)
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700118 script = _gen_script_path
119 args = [
120 "--language",
121 "nanopb",
122 "--module-path",
Alexei Frolove19ebb82020-05-14 17:21:20 -0700123 _module_path,
Alexei Frolovac5b6742020-05-01 13:28:14 -0700124 "--include-paths",
125 "$dir_third_party_nanopb/generator/proto",
Alexei Frolove19ebb82020-05-14 17:21:20 -0700126 "--include-file",
127 invoker.include_file,
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700128 "--out-dir",
129 _proto_gen_dir,
130 "--custom-plugin",
131 get_path_info(_nanopb_plugin, "abspath"),
132 ] + get_path_info(invoker.protos, "abspath")
133 inputs = invoker.protos
134 outputs = _outputs
135
Alexei Frolove19ebb82020-05-14 17:21:20 -0700136 deps = invoker.deps
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700137 if (defined(invoker.protoc_deps)) {
Alexei Frolove19ebb82020-05-14 17:21:20 -0700138 deps += invoker.protoc_deps
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700139 }
140 }
141
142 # For C++ proto files, the generated proto directory is added as an include
143 # path for the code. This requires using "all_dependent_configs" to force the
144 # include on any code that transitively depends on the generated protos.
145 _include_root = rebase_path(get_path_info(".", "abspath"), "//")
146 _include_config_target = "${target_name}_includes"
147 config(_include_config_target) {
148 include_dirs = [
149 "$_proto_gen_dir",
150 "$_proto_gen_dir/$_include_root",
151 ]
152 }
153
154 # Create a library with the generated source files.
155 source_set(target_name) {
156 all_dependent_configs = [ ":$_include_config_target" ]
Alexei Frolove19ebb82020-05-14 17:21:20 -0700157 deps = [ ":$_gen_target" ]
158 public_deps = [ dir_third_party_nanopb ] + invoker.gen_deps
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700159 sources = get_target_outputs(":$_gen_target")
160 public = filter_include(sources, [ "*.pb.h" ])
Alexei Frolov942adf02019-12-11 17:07:28 -0800161 }
162}
163
Alexei Frolovdef14712019-12-23 13:03:32 -0800164# Generates Go code for proto files, listing the proto output directory in the
165# metadata variable GOPATH. Internal use only.
166#
167# Args:
168# protos: List of input .proto files.
169template("_pw_go_proto_library") {
170 _proto_gopath = "$root_gen_dir/go"
171 _proto_gen_dir = "$_proto_gopath/src"
172 _rebased_gopath = rebase_path(_proto_gopath)
173
174 pw_python_script(target_name) {
Wyatt Heplere0c4fdc2020-05-29 17:26:19 -0700175 forward_variables_from(invoker, _forwarded_vars)
Alexei Frolovdef14712019-12-23 13:03:32 -0800176 metadata = {
177 gopath = [ "GOPATH+=$_rebased_gopath" ]
Alexei Frolovc15a9882019-12-23 14:29:02 -0800178 external_deps = [
179 "github.com/golang/protobuf/proto",
180 "google.golang.org/grpc",
181 ]
Alexei Frolovdef14712019-12-23 13:03:32 -0800182 }
183 script = _gen_script_path
184 args = [
185 "--language",
186 "go",
187 "--module-path",
188 "//",
Alexei Frolove19ebb82020-05-14 17:21:20 -0700189 "--include-file",
190 invoker.include_file,
Alexei Frolovdef14712019-12-23 13:03:32 -0800191 "--out-dir",
192 _proto_gen_dir,
193 ] + get_path_info(invoker.protos, "abspath")
194 inputs = invoker.protos
Alexei Frolove19ebb82020-05-14 17:21:20 -0700195 deps = invoker.deps + invoker.gen_deps
Alexei Frolovdef14712019-12-23 13:03:32 -0800196 stamp = true
197 }
198}
199
Alexei Frolov942adf02019-12-11 17:07:28 -0800200# Generates protobuf code from .proto definitions for various languages.
201#
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700202# The generators to use are defined in the pw_protobuf_generators build
203# variable. Each listed generator creates a generated code target called
Alexei Frolov942adf02019-12-11 17:07:28 -0800204#
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700205# <target_name>_<generator>
Alexei Frolov942adf02019-12-11 17:07:28 -0800206#
207# For example, with the following definitions:
208#
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700209# pw_protobuf_generators = [ "pwpb", "py" ]
Alexei Frolov942adf02019-12-11 17:07:28 -0800210#
211# pw_proto_library("my_protos") {
212# sources = [ "foo.proto" ]
213# }
214#
215# Two build targets will be created for the declared "my_protos" target.
216#
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700217# "my_protos_pwpb" <-- C++ source_set containing generated proto code
218# "my_protos_py" <-- Python module containing generated proto code
Alexei Frolov942adf02019-12-11 17:07:28 -0800219#
220# Args:
221# sources: List of input .proto files.
222# deps: List of other pw_proto_library dependencies.
Alexei Frolov05d8ef22020-06-08 10:32:29 -0700223# inputs: Other files on which the protos depend (e.g. nanopb .options files).
Alexei Frolov942adf02019-12-11 17:07:28 -0800224template("pw_proto_library") {
225 assert(defined(invoker.sources) && invoker.sources != [],
Wyatt Heplerb4b73a62020-05-27 15:17:27 -0700226 "pw_proto_library requires .proto source files")
Alexei Frolov942adf02019-12-11 17:07:28 -0800227
Alexei Frolove19ebb82020-05-14 17:21:20 -0700228 # For each proto target, create a file which collects the base directories of
229 # all of its dependencies to list as include paths to protoc.
230 _include_metadata_target = "${target_name}_include_paths"
231 _include_metadata_file = "${target_gen_dir}/${target_name}_includes.txt"
232 generated_file(_include_metadata_target) {
Alexei Frolov942adf02019-12-11 17:07:28 -0800233 if (defined(invoker.deps)) {
Alexei Frolove19ebb82020-05-14 17:21:20 -0700234 # Collect metadata from the include path files of each dependency.
235 deps = process_file_template(invoker.deps, "{{source}}_include_paths")
Alexei Frolov942adf02019-12-11 17:07:28 -0800236 } else {
Alexei Frolove19ebb82020-05-14 17:21:20 -0700237 deps = []
Alexei Frolov942adf02019-12-11 17:07:28 -0800238 }
Alexei Frolove19ebb82020-05-14 17:21:20 -0700239 data_keys = [ "protoc_includes" ]
240 outputs = [ _include_metadata_file ]
241
242 # Indicate this library's base directory for its dependents.
243 metadata = {
244 protoc_includes = [ rebase_path(".", root_out_dir) ]
245 }
246 }
247
Alexei Frolov05d8ef22020-06-08 10:32:29 -0700248 _deps = [ ":$_include_metadata_target" ]
249
250 if (defined(invoker.inputs)) {
251 # Toss any additional inputs into an input group dependency.
252 _input_target_name = "${target_name}_inputs"
253 pw_input_group(_input_target_name) {
254 inputs = invoker.inputs
255 }
256 _deps += [ ":$_input_target_name" ]
257 }
258
Alexei Frolove19ebb82020-05-14 17:21:20 -0700259 foreach(_gen, pw_protobuf_generators) {
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700260 _lang_target = "${target_name}_${_gen}"
Alexei Frolov8185c822020-06-12 10:45:04 -0700261 _gen_deps = []
262 if (defined(invoker.deps)) {
263 _gen_deps = process_file_template(invoker.deps, "{{source}}_${_gen}")
264 }
Alexei Frolov942adf02019-12-11 17:07:28 -0800265
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700266 if (_gen == "pwpb") {
267 _pw_pwpb_proto_library(_lang_target) {
Wyatt Heplere0c4fdc2020-05-29 17:26:19 -0700268 forward_variables_from(invoker, _forwarded_vars)
Alexei Frolov942adf02019-12-11 17:07:28 -0800269 protos = invoker.sources
Alexei Frolov05d8ef22020-06-08 10:32:29 -0700270 deps = _deps
Alexei Frolove19ebb82020-05-14 17:21:20 -0700271 include_file = _include_metadata_file
272 gen_deps = _gen_deps
Alexei Frolovbbf164c2019-12-16 12:51:59 -0800273
274 # List the pw_protobuf plugin's files as a dependency to recompile
275 # generated code if they are modified.
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700276 protoc_deps = [ "$dir_pw_protobuf:codegen_protoc_plugin" ]
Alexei Frolov942adf02019-12-11 17:07:28 -0800277 }
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700278 } else if (_gen == "nanopb") {
279 _pw_nanopb_proto_library(_lang_target) {
Wyatt Heplere0c4fdc2020-05-29 17:26:19 -0700280 forward_variables_from(invoker, _forwarded_vars)
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700281 protos = invoker.sources
Alexei Frolov05d8ef22020-06-08 10:32:29 -0700282 deps = _deps
Alexei Frolove19ebb82020-05-14 17:21:20 -0700283 include_file = _include_metadata_file
284 gen_deps = _gen_deps
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700285 }
286 } else if (_gen == "go") {
Alexei Frolovdef14712019-12-23 13:03:32 -0800287 _pw_go_proto_library(_lang_target) {
Wyatt Heplere0c4fdc2020-05-29 17:26:19 -0700288 forward_variables_from(invoker, _forwarded_vars)
Alexei Frolovdef14712019-12-23 13:03:32 -0800289 protos = invoker.sources
Alexei Frolov05d8ef22020-06-08 10:32:29 -0700290 deps = _deps
Alexei Frolove19ebb82020-05-14 17:21:20 -0700291 include_file = _include_metadata_file
292 gen_deps = _gen_deps
Alexei Frolovdef14712019-12-23 13:03:32 -0800293 }
Alexei Frolov942adf02019-12-11 17:07:28 -0800294 } else {
295 assert(false,
296 string_join(
297 " ",
298 [
Wyatt Heplerb4b73a62020-05-27 15:17:27 -0700299 "pw_proto_library doesn't know how to generate code for",
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700300 "generator '$_gen'. Please add support if you require it.",
Alexei Frolov942adf02019-12-11 17:07:28 -0800301 ]))
302 }
303 }
304
Wyatt Heplerb4b73a62020-05-27 15:17:27 -0700305 # All supported pw_protobuf generators.
306 _protobuf_generators = [
307 "pwpb",
308 "nanopb",
309 "go",
310 ]
311
312 # Create stub versions of the proto library for other protobuf generators.
313 foreach(_gen, _protobuf_generators - pw_protobuf_generators) {
314 pw_python_script("${target_name}_${_gen}") {
Wyatt Heplere0c4fdc2020-05-29 17:26:19 -0700315 forward_variables_from(invoker, _forwarded_vars)
Wyatt Heplerb4b73a62020-05-27 15:17:27 -0700316 script = string_join("/",
317 [
318 dir_pw_protobuf_compiler,
319 "py",
320 "pw_protobuf_compiler",
321 "generator_not_selected.py",
322 ])
323 args = [
324 "--library",
325 "${target_name}_${_gen}",
326 "--generator",
327 _gen,
328 ]
329 inputs = invoker.sources
330 stamp = true
331 }
332 }
333
Alexei Frolov942adf02019-12-11 17:07:28 -0800334 # If the user attempts to use the target directly instead of one of the
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700335 # generator targets, run a script which prints a nice error message.
Alexei Frolov942adf02019-12-11 17:07:28 -0800336 pw_python_script(target_name) {
337 script = string_join("/",
338 [
339 dir_pw_protobuf_compiler,
340 "py",
341 "pw_protobuf_compiler",
342 "proto_target_invalid.py",
343 ])
344 args = [
345 "--target",
346 target_name,
347 "--dir",
348 get_path_info(".", "abspath"),
349 "--root",
350 "//",
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700351 ] + pw_protobuf_generators
Alexei Frolov942adf02019-12-11 17:07:28 -0800352 stamp = true
353 }
354}