blob: a6ec15e03f0dbafaf6795f30b333eb0ba2abaaad [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
Armando Montanezfb3d3fb2020-06-09 18:12:12 -070015import("//build_overrides/pigweed.gni")
16
Alexei Frolovb499d3f2020-10-28 13:00:08 -070017import("$dir_pw_build/error.gni")
Alexei Frolov05d8ef22020-06-08 10:32:29 -070018import("$dir_pw_build/input_group.gni")
Wyatt Hepler752d7d32021-03-02 09:02:23 -080019import("$dir_pw_build/mirror_tree.gni")
Alexei Frolova4c0aee2020-12-01 13:48:48 -080020import("$dir_pw_build/python.gni")
Wyatt Hepler51ded742020-10-19 14:45:27 -070021import("$dir_pw_build/python_action.gni")
Alexei Frolovedd2f142020-06-09 19:11:27 -070022import("$dir_pw_build/target_types.gni")
Alexei Frolovdd6fa5c2020-08-11 10:04:01 -070023import("$dir_pw_third_party/nanopb/nanopb.gni")
Wyatt Heplerd49f8fe2020-10-15 10:13:47 -070024
Wyatt Heplerd9336a42020-11-10 09:47:30 -080025# Variables forwarded from the public pw_proto_library template to the final
26# pw_source_set.
Wyatt Heplere0c4fdc2020-05-29 17:26:19 -070027_forwarded_vars = [
28 "testonly",
29 "visibility",
30]
31
Wyatt Heplerd9336a42020-11-10 09:47:30 -080032# Internal template that invokes protoc with a pw_python_action. This should not
33# be used outside of this file; use pw_proto_library instead.
34#
35# This creates the internal GN target $target_name.$language._gen that compiles
36# proto files with protoc.
37template("_pw_invoke_protoc") {
Wyatt Heplera0377642021-03-09 08:37:10 -080038 if (current_toolchain == default_toolchain) {
39 if (defined(invoker.out_dir)) {
40 _out_dir = invoker.out_dir
Wyatt Hepler752d7d32021-03-02 09:02:23 -080041 } else {
Wyatt Heplera0377642021-03-09 08:37:10 -080042 _out_dir = "${invoker.base_out_dir}/${invoker.language}"
43 if (defined(invoker.module_as_package) &&
44 invoker.module_as_package != "") {
45 assert(invoker.language == "python")
46 _out_dir = "$_out_dir/${invoker.module_as_package}"
Wyatt Heplerdcfcecf2021-03-01 08:36:19 -080047 }
48 }
Wyatt Heplera0377642021-03-09 08:37:10 -080049
50 _includes =
51 rebase_path(get_target_outputs(":${invoker.base_target}._includes"))
52
53 pw_python_action("$target_name._gen") {
54 script =
55 "$dir_pw_protobuf_compiler/py/pw_protobuf_compiler/generate_protos.py"
56
57 python_deps = [ "$dir_pw_protobuf_compiler/py" ]
58 if (defined(invoker.python_deps)) {
59 python_deps += invoker.python_deps
60 }
61
62 deps = [
63 ":${invoker.base_target}._includes",
64 ":${invoker.base_target}._sources",
65 ]
66
67 foreach(dep, invoker.deps) {
68 deps += [ get_label_info(dep, "label_no_toolchain") + "._gen" ]
69 }
70
71 args = [
72 "--language",
73 invoker.language,
74 "--include-file",
75 _includes[0],
76 "--compile-dir",
77 rebase_path(invoker.compile_dir),
78 "--out-dir",
79 rebase_path(_out_dir),
80 "--sources",
81 ] + rebase_path(invoker.sources)
82
83 if (defined(invoker.plugin)) {
84 inputs = [ invoker.plugin ]
85 args += [ "--plugin-path=" + rebase_path(invoker.plugin) ]
86 }
87
88 if (defined(invoker.outputs)) {
89 outputs = invoker.outputs
90 } else {
91 stamp = true
92 }
93
94 if (defined(invoker.metadata)) {
95 metadata = invoker.metadata
96 } else {
97 metadata = {
98 protoc_outputs = rebase_path(outputs)
99 root = [ rebase_path(_out_dir) ]
100 }
101 }
102 }
103 } else {
104 # protoc is only ever invoked from the default toolchain.
105 not_needed([ "target_name" ])
106 not_needed(invoker, "*")
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800107 }
108}
109
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700110# Generates pw_protobuf C++ code for proto files, creating a source_set of the
111# generated files. This is internal and should not be used outside of this file.
112# Use pw_proto_library instead.
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700113template("_pw_pwpb_proto_library") {
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800114 _pw_invoke_protoc(target_name) {
115 forward_variables_from(invoker, "*", _forwarded_vars)
116 language = "pwpb"
117 plugin = "$dir_pw_protobuf/py/pw_protobuf/plugin.py"
Wyatt Hepler438caa02021-01-15 17:13:11 -0800118 python_deps = [ "$dir_pw_protobuf/py" ]
Alexei Frolov942adf02019-12-11 17:07:28 -0800119 }
120
121 # Create a library with the generated source files.
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800122 config("$target_name._include_path") {
123 include_dirs = [ "${invoker.base_out_dir}/pwpb" ]
124 visibility = [ ":*" ]
125 }
126
Alexei Frolovedd2f142020-06-09 19:11:27 -0700127 pw_source_set(target_name) {
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800128 forward_variables_from(invoker, _forwarded_vars)
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800129 public_configs = [ ":$target_name._include_path" ]
130 deps = [ ":$target_name._gen($default_toolchain)" ]
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800131 public_deps = [ dir_pw_protobuf ] + invoker.deps
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800132 sources = invoker.outputs
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700133 public = filter_include(sources, [ "*.pwpb.h" ])
134 }
135}
136
Alexei Frolov79b7cb02020-07-06 13:51:43 -0700137# Generates nanopb RPC code for proto files, creating a source_set of the
138# generated files. This is internal and should not be used outside of this file.
139# Use pw_proto_library instead.
Alexei Frolov79b7cb02020-07-06 13:51:43 -0700140template("_pw_nanopb_rpc_proto_library") {
Alexei Frolov79b7cb02020-07-06 13:51:43 -0700141 # Create a target which runs protoc configured with the nanopb_rpc plugin to
142 # generate the C++ proto RPC headers.
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800143 _pw_invoke_protoc(target_name) {
144 forward_variables_from(invoker, "*", _forwarded_vars)
145 language = "nanopb_rpc"
146 plugin = "$dir_pw_rpc/py/pw_rpc/plugin_nanopb.py"
Wyatt Hepler438caa02021-01-15 17:13:11 -0800147 python_deps = [ "$dir_pw_rpc/py" ]
Alexei Frolov79b7cb02020-07-06 13:51:43 -0700148 }
149
150 # Create a library with the generated source files.
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800151 config("$target_name._include_path") {
152 include_dirs = [ "${invoker.base_out_dir}/nanopb_rpc" ]
153 visibility = [ ":*" ]
154 }
155
Alexei Frolov79b7cb02020-07-06 13:51:43 -0700156 pw_source_set(target_name) {
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800157 forward_variables_from(invoker, _forwarded_vars)
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800158 public_configs = [ ":$target_name._include_path" ]
159 deps = [ ":$target_name._gen($default_toolchain)" ]
Alexei Frolov79b7cb02020-07-06 13:51:43 -0700160 public_deps = [
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800161 ":${invoker.base_target}.nanopb",
Wyatt Heplercbd09c22020-09-15 11:17:24 -0700162 "$dir_pw_rpc:server",
Alexei Frolova4d71502020-10-14 12:43:14 -0700163 "$dir_pw_rpc/nanopb:method_union",
Alexei Frolovdd6fa5c2020-08-11 10:04:01 -0700164 "$dir_pw_third_party/nanopb",
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800165 ] + invoker.deps
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800166 public = invoker.outputs
Alexei Frolov79b7cb02020-07-06 13:51:43 -0700167 }
168}
169
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700170# Generates nanopb code for proto files, creating a source_set of the generated
171# files. This is internal and should not be used outside of this file. Use
172# pw_proto_library instead.
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700173template("_pw_nanopb_proto_library") {
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800174 # When compiling with the Nanopb plugin, the nanopb.proto file is already
175 # compiled internally, so skip recompiling it with protoc.
176 if (rebase_path(invoker.sources, invoker.compile_dir) == [ "nanopb.proto" ]) {
177 group("$target_name._gen") {
178 deps = [ ":${invoker.base_target}._sources" ]
179 }
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700180
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800181 group("$target_name") {
182 deps = invoker.deps + [ ":$target_name._gen($default_toolchain)" ]
183 }
184 } else {
185 # Create a target which runs protoc configured with the nanopb plugin to
186 # generate the C proto sources.
187 _pw_invoke_protoc(target_name) {
188 forward_variables_from(invoker, "*", _forwarded_vars)
189 language = "nanopb"
190 plugin = "$dir_pw_third_party_nanopb/generator/protoc-gen-nanopb"
191 }
192
193 # Create a library with the generated source files.
194 config("$target_name._include_path") {
195 include_dirs = [ "${invoker.base_out_dir}/nanopb" ]
196 visibility = [ ":*" ]
197 }
198
199 pw_source_set(target_name) {
200 forward_variables_from(invoker, _forwarded_vars)
201 public_configs = [ ":$target_name._include_path" ]
202 deps = [ ":$target_name._gen($default_toolchain)" ]
203 public_deps = [ "$dir_pw_third_party/nanopb" ] + invoker.deps
204 sources = invoker.outputs
205 public = filter_include(sources, [ "*.pb.h" ])
206 }
Alexei Frolov942adf02019-12-11 17:07:28 -0800207 }
208}
209
Alexei Frolovc912ea72020-10-26 08:43:27 -0700210# Generates raw RPC code for proto files, creating a source_set of the generated
211# files. This is internal and should not be used outside of this file. Use
212# pw_proto_library instead.
Alexei Frolovc912ea72020-10-26 08:43:27 -0700213template("_pw_raw_rpc_proto_library") {
Alexei Frolovc912ea72020-10-26 08:43:27 -0700214 # Create a target which runs protoc configured with the nanopb_rpc plugin to
215 # generate the C++ proto RPC headers.
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800216 _pw_invoke_protoc(target_name) {
217 forward_variables_from(invoker, "*", _forwarded_vars)
218 language = "raw_rpc"
219 plugin = "$dir_pw_rpc/py/pw_rpc/plugin_raw.py"
Wyatt Hepler438caa02021-01-15 17:13:11 -0800220 python_deps = [ "$dir_pw_rpc/py" ]
Alexei Frolovc912ea72020-10-26 08:43:27 -0700221 }
222
223 # Create a library with the generated source files.
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800224 config("$target_name._include_path") {
225 include_dirs = [ "${invoker.base_out_dir}/raw_rpc" ]
226 visibility = [ ":*" ]
227 }
228
Alexei Frolovc912ea72020-10-26 08:43:27 -0700229 pw_source_set(target_name) {
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800230 forward_variables_from(invoker, _forwarded_vars)
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800231 public_configs = [ ":$target_name._include_path" ]
232 deps = [ ":$target_name._gen($default_toolchain)" ]
Alexei Frolovc912ea72020-10-26 08:43:27 -0700233 public_deps = [
234 "$dir_pw_rpc:server",
235 "$dir_pw_rpc/raw:method_union",
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800236 ] + invoker.deps
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800237 public = invoker.outputs
Alexei Frolovc912ea72020-10-26 08:43:27 -0700238 }
239}
240
Alexei Frolovdef14712019-12-23 13:03:32 -0800241# Generates Go code for proto files, listing the proto output directory in the
242# metadata variable GOPATH. Internal use only.
Alexei Frolovdef14712019-12-23 13:03:32 -0800243template("_pw_go_proto_library") {
244 _proto_gopath = "$root_gen_dir/go"
Alexei Frolovdef14712019-12-23 13:03:32 -0800245
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800246 _pw_invoke_protoc(target_name) {
247 forward_variables_from(invoker, "*")
248 language = "go"
Alexei Frolovdef14712019-12-23 13:03:32 -0800249 metadata = {
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800250 gopath = [ "GOPATH+=" + rebase_path(_proto_gopath) ]
Alexei Frolovc15a9882019-12-23 14:29:02 -0800251 external_deps = [
252 "github.com/golang/protobuf/proto",
253 "google.golang.org/grpc",
254 ]
Alexei Frolovdef14712019-12-23 13:03:32 -0800255 }
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800256
257 # Override the default "$base_out_dir/$language" output path.
258 out_dir = "$_proto_gopath/src"
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800259 }
260
261 group(target_name) {
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800262 deps = invoker.deps + [ ":$target_name._gen($default_toolchain)" ]
Alexei Frolovdef14712019-12-23 13:03:32 -0800263 }
264}
265
Alexei Frolova4c0aee2020-12-01 13:48:48 -0800266# Generates Python code for proto files, creating a pw_python_package containing
267# the generated files. This is internal and should not be used outside of this
268# file. Use pw_proto_library instead.
269template("_pw_python_proto_library") {
Alexei Frolova4c0aee2020-12-01 13:48:48 -0800270 _pw_invoke_protoc(target_name) {
Wyatt Heplerdcfcecf2021-03-01 08:36:19 -0800271 forward_variables_from(invoker, "*", _forwarded_vars + [ "python_package" ])
Alexei Frolova4c0aee2020-12-01 13:48:48 -0800272 language = "python"
Wyatt Hepler4b921c12021-03-05 15:33:29 -0800273 python_deps = [ "$dir_pw_protobuf_compiler:protobuf_requirements" ]
Alexei Frolova4c0aee2020-12-01 13:48:48 -0800274 }
275
Wyatt Heplerdcfcecf2021-03-01 08:36:19 -0800276 if (defined(invoker.python_package) && invoker.python_package != "") {
277 # If nested in a Python package, write the package's name to a file so
278 # pw_python_package can check that the dependencies are correct.
279 write_file("${invoker.base_out_dir}/python_package.txt",
280 get_label_info(invoker.python_package, "label_no_toolchain"))
Alexei Frolova4c0aee2020-12-01 13:48:48 -0800281
Wyatt Heplerdcfcecf2021-03-01 08:36:19 -0800282 # If anyone attempts to depend on this Python package, print an error.
283 pw_error(target_name) {
284 _pkg = get_label_info(invoker.python_package, "label_no_toolchain")
285 message_lines = [
286 "This proto Python package is embedded in the $_pkg Python package.",
287 "It cannot be used directly; instead, depend on $_pkg.",
288 ]
Wyatt Heplerd517afc2021-02-03 19:40:08 -0800289 }
Wyatt Heplerdcfcecf2021-03-01 08:36:19 -0800290 foreach(subtarget, pw_python_package_subtargets) {
291 group("$target_name.$subtarget") {
292 deps = [ ":${invoker.target_name}" ]
293 }
294 }
295 } else {
296 write_file("${invoker.base_out_dir}/python_package.txt", "")
Wyatt Heplerd517afc2021-02-03 19:40:08 -0800297
Wyatt Heplerdcfcecf2021-03-01 08:36:19 -0800298 # Create a Python package with the generated source files.
299 pw_python_package(target_name) {
300 forward_variables_from(invoker, _forwarded_vars)
301 generate_setup = {
302 name = invoker._package_dir
303 version = "0.0.1" # TODO(hepler): Need to be able to set this verison.
304 }
305 sources = invoker.outputs
306 strip_prefix = "${invoker.base_out_dir}/python"
307 python_deps = invoker.deps
308 other_deps = [ ":$target_name._gen($default_toolchain)" ]
Wyatt Heplerc2ce5242021-03-17 08:42:14 -0700309 static_analysis = []
Alexei Frolova4c0aee2020-12-01 13:48:48 -0800310
Wyatt Heplerdcfcecf2021-03-01 08:36:19 -0800311 _pw_module_as_package = invoker.module_as_package != ""
312 }
Alexei Frolova4c0aee2020-12-01 13:48:48 -0800313 }
314}
315
Alexei Frolov942adf02019-12-11 17:07:28 -0800316# Generates protobuf code from .proto definitions for various languages.
Alexei Frolovb499d3f2020-10-28 13:00:08 -0700317# For each supported generator, creates a sub-target named:
Alexei Frolov942adf02019-12-11 17:07:28 -0800318#
Alexei Frolov8e30d462020-10-22 13:54:36 -0700319# <target_name>.<generator>
Alexei Frolov942adf02019-12-11 17:07:28 -0800320#
Wyatt Hepler4cdc3e72021-03-24 08:41:08 -0700321# GN permits using abbreviated labels when the target name matches the directory
322# name (e.g. //foo for //foo:foo). For consistency with this, the sub-targets
323# for each generator are aliased to the directory when the target name is the
324# same. For example, these two labels are equivalent:
325#
326# //path/to/my_protos:my_protos.pwpb
327# //path/to/my_protos:pwpb
328#
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800329# pw_protobuf_library targets generate Python packages. As such, they must have
330# globally unique package names. The first directory of the prefix or the first
331# common directory of the sources is used as the Python package.
332#
Alexei Frolov942adf02019-12-11 17:07:28 -0800333# Args:
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800334# sources: List of input .proto files.
335# deps: List of other pw_proto_library dependencies.
336# inputs: Other files on which the protos depend (e.g. nanopb .options files).
337# prefix: A prefix to add to the source protos prior to compilation. For
338# example, a source called "foo.proto" with prefix = "nested" will be
339# compiled with protoc as "nested/foo.proto".
340# strip_prefix: Remove this prefix from the source protos. All source and
341# input files must be nested under this path.
Wyatt Hepler4cdc3e72021-03-24 08:41:08 -0700342# python_package: Label of Python package to which to add the proto modules.
Alexei Frolovb499d3f2020-10-28 13:00:08 -0700343#
Alexei Frolov942adf02019-12-11 17:07:28 -0800344template("pw_proto_library") {
345 assert(defined(invoker.sources) && invoker.sources != [],
Wyatt Heplerb4b73a62020-05-27 15:17:27 -0700346 "pw_proto_library requires .proto source files")
Alexei Frolov942adf02019-12-11 17:07:28 -0800347
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800348 if (defined(invoker.python_module_as_package)) {
349 _module_as_package = invoker.python_module_as_package
350
351 _must_be_one_source = invoker.sources
352 assert([ _must_be_one_source[0] ] == _must_be_one_source,
353 "'python_module_as_package' requires exactly one source file")
354 assert(_module_as_package != "",
355 "'python_module_as_package' cannot be be empty")
356 assert(string_split(_module_as_package, "/") == [ _module_as_package ],
357 "'python_module_as_package' cannot contain slashes")
358 assert(!defined(invoker.prefix),
359 "'prefix' cannot be provided with 'python_module_as_package'")
360 } else {
361 _module_as_package = ""
362 }
363
364 if (defined(invoker.strip_prefix)) {
365 _source_root = get_path_info(invoker.strip_prefix, "abspath")
366 } else {
367 _source_root = get_path_info(".", "abspath")
368 }
369
370 if (defined(invoker.prefix)) {
371 _prefix = invoker.prefix
372 } else {
373 _prefix = ""
374 }
375
Wyatt Heplerd517afc2021-02-03 19:40:08 -0800376 _common = {
377 base_target = target_name
Wyatt Heplerd517afc2021-02-03 19:40:08 -0800378
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800379 # This is the output directory for all files related to this proto library.
380 # Sources are mirrored to "$base_out_dir/sources" and protoc puts outputs in
381 # "$base_out_dir/$language" by default.
Wyatt Heplerdcfcecf2021-03-01 08:36:19 -0800382 base_out_dir =
383 get_label_info(":$target_name($default_toolchain)", "target_gen_dir") +
384 "/$target_name.proto_library"
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800385
386 compile_dir = "$base_out_dir/sources"
387
388 # Refer to the source files as the are mirrored to the output directory.
389 sources = []
390 foreach(file, rebase_path(invoker.sources, _source_root)) {
391 sources += [ "$compile_dir/$_prefix/$file" ]
Wyatt Heplerd517afc2021-02-03 19:40:08 -0800392 }
393 }
394
Wyatt Hepler91741472021-02-03 08:45:10 -0800395 _package_dir = ""
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800396 _source_names = []
Wyatt Hepler91741472021-02-03 08:45:10 -0800397
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800398 # Determine the Python package name to use for these protos. If there is no
399 # prefix, the first directory the sources are nested under is used.
400 foreach(source, rebase_path(invoker.sources, _source_root)) {
Wyatt Hepler91741472021-02-03 08:45:10 -0800401 _path_components = []
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800402 _path_components = string_split(source, "/")
Wyatt Hepler91741472021-02-03 08:45:10 -0800403
404 if (_package_dir == "") {
405 _package_dir = _path_components[0]
406 } else {
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800407 assert(_prefix != "" || _path_components[0] == _package_dir,
408 "Unless 'prefix' is supplied, all .proto sources in a " +
409 "pw_proto_library must be in the same directory tree")
Wyatt Hepler91741472021-02-03 08:45:10 -0800410 }
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800411
412 _source_names +=
413 [ get_path_info(source, "dir") + "/" + get_path_info(source, "name") ]
Wyatt Hepler91741472021-02-03 08:45:10 -0800414 }
415
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800416 # If the 'prefix' was supplied, use that for the package directory.
417 if (_prefix != "") {
418 _prefix_path_components = string_split(_prefix, "/")
419 _package_dir = _prefix_path_components[0]
420 }
421
422 assert(_package_dir != "" && _package_dir != "." && _package_dir != "..",
423 "Either a 'prefix' must be specified or all sources must be nested " +
424 "under a common directory")
425
426 # Define an action that is never executed to prevent duplicate proto packages
427 # from being declared. The target name and the output file include only the
428 # package directory, so different targets that use the same proto package name
429 # will conflict.
430 action("pw_proto_library.$_package_dir") {
431 script = "$dir_pw_build/py/pw_build/nop.py"
Wyatt Hepler91741472021-02-03 08:45:10 -0800432 visibility = []
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800433
434 # Place an error message in the output path (which is never created). If the
435 # package name conflicts occur in different BUILD.gn files, this results in
436 # an otherwise cryptic Ninja error, rather than a GN error.
437 outputs = [ "$root_out_dir/ " +
438 "ERROR - Multiple pw_proto_library targets create the " +
439 "'$_package_dir' package. Change the package name by setting " +
440 "the \"prefix\" arg or move the protos to a different " +
441 "directory, then re-run gn gen." ]
Wyatt Hepler91741472021-02-03 08:45:10 -0800442 }
443
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800444 if (defined(invoker.deps)) {
445 _deps = invoker.deps
446 } else {
447 _deps = []
448 }
449
Alexei Frolove19ebb82020-05-14 17:21:20 -0700450 # For each proto target, create a file which collects the base directories of
451 # all of its dependencies to list as include paths to protoc.
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800452 generated_file("$target_name._includes") {
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800453 # Collect metadata from the include path files of each dependency.
Wyatt Hepler4cdc3e72021-03-24 08:41:08 -0700454
455 deps = []
456 foreach(dep, _deps) {
457 _base = get_label_info(dep, "label_no_toolchain")
458 deps += [ "$_base._includes(" + get_label_info(dep, "toolchain") + ")" ]
459 }
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800460
Alexei Frolove19ebb82020-05-14 17:21:20 -0700461 data_keys = [ "protoc_includes" ]
Wyatt Heplerdcfcecf2021-03-01 08:36:19 -0800462 outputs = [ "${_common.base_out_dir}/includes.txt" ]
Alexei Frolove19ebb82020-05-14 17:21:20 -0700463
464 # Indicate this library's base directory for its dependents.
465 metadata = {
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800466 protoc_includes = [ rebase_path(_common.compile_dir) ]
Alexei Frolove19ebb82020-05-14 17:21:20 -0700467 }
468 }
469
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800470 # Mirror the proto sources to the output directory with the prefix added.
471 pw_mirror_tree("$target_name._sources") {
472 source_root = _source_root
473 sources = invoker.sources
Alexei Frolov05d8ef22020-06-08 10:32:29 -0700474
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800475 if (defined(invoker.inputs)) {
476 sources += invoker.inputs
477 }
478
479 directory = "${_common.compile_dir}/$_prefix"
Alexei Frolov79b7cb02020-07-06 13:51:43 -0700480 }
481
Alexei Frolovb499d3f2020-10-28 13:00:08 -0700482 # Enumerate all of the protobuf generator targets.
Alexei Frolovc4b62ec2020-07-13 08:35:10 -0700483
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800484 _pw_pwpb_proto_library("$target_name.pwpb") {
Alexei Frolovb499d3f2020-10-28 13:00:08 -0700485 forward_variables_from(invoker, _forwarded_vars)
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800486 forward_variables_from(_common, "*")
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800487
488 deps = []
489 foreach(dep, _deps) {
490 _base = get_label_info(dep, "label_no_toolchain")
491 deps += [ "$_base.pwpb(" + get_label_info(dep, "toolchain") + ")" ]
492 }
493
494 outputs = []
495 foreach(name, _source_names) {
496 outputs += [ "$base_out_dir/pwpb/$_prefix/${name}.pwpb.h" ]
497 }
Alexei Frolovb499d3f2020-10-28 13:00:08 -0700498 }
499
500 if (dir_pw_third_party_nanopb != "") {
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800501 _pw_nanopb_rpc_proto_library("$target_name.nanopb_rpc") {
Alexei Frolovb499d3f2020-10-28 13:00:08 -0700502 forward_variables_from(invoker, _forwarded_vars)
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800503 forward_variables_from(_common, "*")
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800504
505 deps = []
506 foreach(dep, _deps) {
507 _lbl = get_label_info(dep, "label_no_toolchain")
508 deps += [ "$_lbl.nanopb_rpc(" + get_label_info(dep, "toolchain") + ")" ]
509 }
510
511 outputs = []
512 foreach(name, _source_names) {
513 outputs += [ "$base_out_dir/nanopb_rpc/$_prefix/${name}.rpc.pb.h" ]
514 }
Alexei Frolovc4b62ec2020-07-13 08:35:10 -0700515 }
516
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800517 _pw_nanopb_proto_library("$target_name.nanopb") {
518 forward_variables_from(invoker, _forwarded_vars)
519 forward_variables_from(_common, "*")
520
521 deps = []
522 foreach(dep, _deps) {
523 _base = get_label_info(dep, "label_no_toolchain")
524 deps += [ "$_base.nanopb(" + get_label_info(dep, "toolchain") + ")" ]
Wyatt Heplerd517afc2021-02-03 19:40:08 -0800525 }
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800526
527 outputs = []
528 foreach(name, _source_names) {
529 outputs += [
530 "$base_out_dir/nanopb/$_prefix/${name}.pb.h",
531 "$base_out_dir/nanopb/$_prefix/${name}.pb.c",
532 ]
Wyatt Heplerd517afc2021-02-03 19:40:08 -0800533 }
Alexei Frolovb499d3f2020-10-28 13:00:08 -0700534 }
535 } else {
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800536 pw_error("$target_name.nanopb_rpc") {
Alexei Frolovb499d3f2020-10-28 13:00:08 -0700537 message =
538 "\$dir_pw_third_party_nanopb must be set to generate nanopb RPC code."
Alexei Frolov8185c822020-06-12 10:45:04 -0700539 }
Alexei Frolov942adf02019-12-11 17:07:28 -0800540
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800541 pw_error("$target_name.nanopb") {
Alexei Frolovb499d3f2020-10-28 13:00:08 -0700542 message =
543 "\$dir_pw_third_party_nanopb must be set to compile nanopb protobufs."
Alexei Frolov942adf02019-12-11 17:07:28 -0800544 }
545 }
546
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800547 _pw_raw_rpc_proto_library("$target_name.raw_rpc") {
Alexei Frolovb499d3f2020-10-28 13:00:08 -0700548 forward_variables_from(invoker, _forwarded_vars)
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800549 forward_variables_from(_common, "*")
550
551 deps = []
552 foreach(dep, _deps) {
553 _base = get_label_info(dep, "label_no_toolchain")
554 deps += [ "$_base.raw_rpc(" + get_label_info(dep, "toolchain") + ")" ]
555 }
556
557 outputs = []
558 foreach(name, _source_names) {
559 outputs += [ "$base_out_dir/raw_rpc/$_prefix/${name}.raw_rpc.pb.h" ]
560 }
Alexei Frolovb499d3f2020-10-28 13:00:08 -0700561 }
562
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800563 _pw_go_proto_library("$target_name.go") {
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800564 sources = _common.sources
565
566 deps = []
567 foreach(dep, _deps) {
568 _base = get_label_info(dep, "label_no_toolchain")
569 deps += [ "$_base.go(" + get_label_info(dep, "toolchain") + ")" ]
570 }
571
572 forward_variables_from(_common, "*")
Alexei Frolovb499d3f2020-10-28 13:00:08 -0700573 }
574
Alexei Frolov38cad0c2020-12-03 12:36:24 -0800575 _pw_python_proto_library("$target_name.python") {
Alexei Frolov38cad0c2020-12-03 12:36:24 -0800576 forward_variables_from(_common, "*")
Wyatt Heplerdcfcecf2021-03-01 08:36:19 -0800577 forward_variables_from(invoker, [ "python_package" ])
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800578 module_as_package = _module_as_package
579
580 deps = []
581 foreach(dep, _deps) {
582 _base = get_label_info(dep, "label_no_toolchain")
583 deps += [ "$_base.python(" + get_label_info(dep, "toolchain") + ")" ]
584 }
585
586 if (module_as_package == "") {
587 _python_prefix = "$base_out_dir/python/$_prefix"
588 } else {
589 _python_prefix = "$base_out_dir/python/$module_as_package"
590 }
591
592 outputs = []
593 foreach(name, _source_names) {
594 outputs += [
595 "$_python_prefix/${name}_pb2.py",
596 "$_python_prefix/${name}_pb2.pyi",
597 ]
598 }
Alexei Frolova4c0aee2020-12-01 13:48:48 -0800599 }
600
Wyatt Heplerb4b73a62020-05-27 15:17:27 -0700601 # All supported pw_protobuf generators.
602 _protobuf_generators = [
603 "pwpb",
604 "nanopb",
Alexei Frolov79b7cb02020-07-06 13:51:43 -0700605 "nanopb_rpc",
Alexei Frolovc912ea72020-10-26 08:43:27 -0700606 "raw_rpc",
Wyatt Heplerb4b73a62020-05-27 15:17:27 -0700607 "go",
Alexei Frolova4c0aee2020-12-01 13:48:48 -0800608 "python",
Wyatt Heplerb4b73a62020-05-27 15:17:27 -0700609 ]
610
Wyatt Hepler4cdc3e72021-03-24 08:41:08 -0700611 # If the label matches the directory name, alias the subtargets to the
612 # directory (e.g. //foo:nanopb is an alias for //foo:foo.nanopb).
613 if (get_label_info(":$target_name", "name") ==
614 get_path_info(get_label_info(":$target_name", "dir"), "name")) {
Wyatt Hepler2c6c0ba2021-04-07 09:50:23 -0700615 foreach(_generator, _protobuf_generators - [ "python" ]) {
616 group(_generator) {
Wyatt Hepler4cdc3e72021-03-24 08:41:08 -0700617 public_deps = [ ":${invoker.target_name}.$_generator" ]
618 }
619 }
Wyatt Hepler2c6c0ba2021-04-07 09:50:23 -0700620
621 pw_python_group("python") {
622 python_deps = [ ":${invoker.target_name}.python" ]
623 }
Wyatt Hepler4cdc3e72021-03-24 08:41:08 -0700624 }
625
Alexei Frolov942adf02019-12-11 17:07:28 -0800626 # If the user attempts to use the target directly instead of one of the
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700627 # generator targets, run a script which prints a nice error message.
Wyatt Heplerc8e05a42020-10-19 14:49:39 -0700628 pw_python_action(target_name) {
Alexei Frolov942adf02019-12-11 17:07:28 -0800629 script = string_join("/",
630 [
631 dir_pw_protobuf_compiler,
632 "py",
633 "pw_protobuf_compiler",
634 "proto_target_invalid.py",
635 ])
636 args = [
637 "--target",
638 target_name,
639 "--dir",
640 get_path_info(".", "abspath"),
641 "--root",
642 "//",
Alexei Frolovb499d3f2020-10-28 13:00:08 -0700643 ] + _protobuf_generators
Alexei Frolov942adf02019-12-11 17:07:28 -0800644 stamp = true
645 }
646}