blob: f27f76dffb9be0ecf6236ac49ba43ddf701cf3dd [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 Heplerf6f74f42021-04-13 19:26:20 -070024import("toolchain.gni")
Wyatt Heplerd49f8fe2020-10-15 10:13:47 -070025
Wyatt Heplerd9336a42020-11-10 09:47:30 -080026# Variables forwarded from the public pw_proto_library template to the final
27# pw_source_set.
Wyatt Heplere0c4fdc2020-05-29 17:26:19 -070028_forwarded_vars = [
29 "testonly",
30 "visibility",
31]
32
Wyatt Heplerd9336a42020-11-10 09:47:30 -080033# 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") {
Wyatt Heplerf6f74f42021-04-13 19:26:20 -070039 if (current_toolchain == pw_protobuf_compiler_TOOLCHAIN) {
Wyatt Heplera0377642021-03-09 08:37:10 -080040 if (defined(invoker.out_dir)) {
41 _out_dir = invoker.out_dir
Wyatt Hepler752d7d32021-03-02 09:02:23 -080042 } else {
Wyatt Heplera0377642021-03-09 08:37:10 -080043 _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}"
Wyatt Heplerdcfcecf2021-03-01 08:36:19 -080048 }
49 }
Wyatt Heplera0377642021-03-09 08:37:10 -080050
51 _includes =
Michael Spangc8b93902021-05-30 15:53:56 -040052 rebase_path(get_target_outputs(":${invoker.base_target}._includes"),
53 root_build_dir)
Wyatt Heplera0377642021-03-09 08:37:10 -080054
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
Wyatt Heplera3ca62a2021-05-04 16:21:43 -070073 if (defined(invoker.other_deps)) {
74 deps += invoker.other_deps
75 }
76
Wyatt Heplera0377642021-03-09 08:37:10 -080077 args = [
78 "--language",
79 invoker.language,
80 "--include-file",
81 _includes[0],
82 "--compile-dir",
Michael Spangc8b93902021-05-30 15:53:56 -040083 rebase_path(invoker.compile_dir, root_build_dir),
Wyatt Heplera0377642021-03-09 08:37:10 -080084 "--out-dir",
Michael Spangc8b93902021-05-30 15:53:56 -040085 rebase_path(_out_dir, root_build_dir),
Wyatt Heplera0377642021-03-09 08:37:10 -080086 "--sources",
Michael Spangc8b93902021-05-30 15:53:56 -040087 ] + rebase_path(invoker.sources, root_build_dir)
Wyatt Heplera0377642021-03-09 08:37:10 -080088
89 if (defined(invoker.plugin)) {
90 inputs = [ invoker.plugin ]
Michael Spangc8b93902021-05-30 15:53:56 -040091 args +=
92 [ "--plugin-path=" + rebase_path(invoker.plugin, root_build_dir) ]
Wyatt Heplera0377642021-03-09 08:37:10 -080093 }
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
Wyatt Heplera0377642021-03-09 08:37:10 -0800103 }
104 }
Wyatt Heplercf184e82021-05-11 18:15:35 -0700105
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")
Michael Spangc8b93902021-05-30 15:53:56 -0400109 protoc_outputs =
110 rebase_path(get_target_outputs(":$target_name._gen"), root_build_dir)
111 root = rebase_path(_out_dir, root_build_dir)
Wyatt Heplercf184e82021-05-11 18:15:35 -0700112 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") + "/" +
Michael Spangc8b93902021-05-30 15:53:56 -0400124 get_label_info(dep, "name") + ".json" ],
125 root_build_dir)
Wyatt Heplercf184e82021-05-11 18:15:35 -0700126 }
127 }
128 write_file("$target_gen_dir/$target_name.json", _proto_info, "json")
Wyatt Heplera0377642021-03-09 08:37:10 -0800129 } else {
Wyatt Heplerf6f74f42021-04-13 19:26:20 -0700130 # protoc is only ever invoked from pw_protobuf_compiler_TOOLCHAIN.
Wyatt Heplera0377642021-03-09 08:37:10 -0800131 not_needed([ "target_name" ])
132 not_needed(invoker, "*")
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800133 }
134}
135
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700136# 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.
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700139template("_pw_pwpb_proto_library") {
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800140 _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"
Wyatt Hepler438caa02021-01-15 17:13:11 -0800144 python_deps = [ "$dir_pw_protobuf/py" ]
Alexei Frolov942adf02019-12-11 17:07:28 -0800145 }
146
147 # Create a library with the generated source files.
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800148 config("$target_name._include_path") {
149 include_dirs = [ "${invoker.base_out_dir}/pwpb" ]
150 visibility = [ ":*" ]
151 }
152
Alexei Frolovedd2f142020-06-09 19:11:27 -0700153 pw_source_set(target_name) {
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800154 forward_variables_from(invoker, _forwarded_vars)
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800155 public_configs = [ ":$target_name._include_path" ]
Wyatt Heplerf6f74f42021-04-13 19:26:20 -0700156 deps = [ ":$target_name._gen($pw_protobuf_compiler_TOOLCHAIN)" ]
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800157 public_deps = [ dir_pw_protobuf ] + invoker.deps
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800158 sources = invoker.outputs
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700159 public = filter_include(sources, [ "*.pwpb.h" ])
160 }
161}
162
Alexei Frolov79b7cb02020-07-06 13:51:43 -0700163# Generates nanopb RPC code for proto files, creating a source_set of the
164# generated files. This is internal and should not be used outside of this file.
165# Use pw_proto_library instead.
Alexei Frolov79b7cb02020-07-06 13:51:43 -0700166template("_pw_nanopb_rpc_proto_library") {
Alexei Frolov79b7cb02020-07-06 13:51:43 -0700167 # Create a target which runs protoc configured with the nanopb_rpc plugin to
168 # generate the C++ proto RPC headers.
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800169 _pw_invoke_protoc(target_name) {
170 forward_variables_from(invoker, "*", _forwarded_vars)
171 language = "nanopb_rpc"
172 plugin = "$dir_pw_rpc/py/pw_rpc/plugin_nanopb.py"
Wyatt Hepler438caa02021-01-15 17:13:11 -0800173 python_deps = [ "$dir_pw_rpc/py" ]
Alexei Frolov79b7cb02020-07-06 13:51:43 -0700174 }
175
176 # Create a library with the generated source files.
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800177 config("$target_name._include_path") {
178 include_dirs = [ "${invoker.base_out_dir}/nanopb_rpc" ]
179 visibility = [ ":*" ]
180 }
181
Alexei Frolov79b7cb02020-07-06 13:51:43 -0700182 pw_source_set(target_name) {
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800183 forward_variables_from(invoker, _forwarded_vars)
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800184 public_configs = [ ":$target_name._include_path" ]
Wyatt Heplerf6f74f42021-04-13 19:26:20 -0700185 deps = [ ":$target_name._gen($pw_protobuf_compiler_TOOLCHAIN)" ]
Alexei Frolov79b7cb02020-07-06 13:51:43 -0700186 public_deps = [
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800187 ":${invoker.base_target}.nanopb",
Wyatt Heplercbd09c22020-09-15 11:17:24 -0700188 "$dir_pw_rpc:server",
Alexei Frolov2d737bc2021-04-27 23:03:09 -0700189 "$dir_pw_rpc/nanopb:client",
Alexei Frolova4d71502020-10-14 12:43:14 -0700190 "$dir_pw_rpc/nanopb:method_union",
Alexei Frolovdd6fa5c2020-08-11 10:04:01 -0700191 "$dir_pw_third_party/nanopb",
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800192 ] + invoker.deps
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800193 public = invoker.outputs
Wyatt Heplerd69309b2021-06-11 14:00:24 -0700194 check_includes = false
Alexei Frolov79b7cb02020-07-06 13:51:43 -0700195 }
196}
197
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700198# Generates nanopb code for proto files, creating a source_set of the generated
199# files. This is internal and should not be used outside of this file. Use
200# pw_proto_library instead.
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700201template("_pw_nanopb_proto_library") {
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800202 # When compiling with the Nanopb plugin, the nanopb.proto file is already
203 # compiled internally, so skip recompiling it with protoc.
204 if (rebase_path(invoker.sources, invoker.compile_dir) == [ "nanopb.proto" ]) {
205 group("$target_name._gen") {
Wyatt Heplerf6f74f42021-04-13 19:26:20 -0700206 deps = [
207 ":${invoker.base_target}._sources($pw_protobuf_compiler_TOOLCHAIN)",
208 ]
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800209 }
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700210
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800211 group("$target_name") {
Wyatt Heplerf6f74f42021-04-13 19:26:20 -0700212 deps = invoker.deps +
213 [ ":$target_name._gen($pw_protobuf_compiler_TOOLCHAIN)" ]
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800214 }
215 } else {
216 # Create a target which runs protoc configured with the nanopb plugin to
217 # generate the C proto sources.
218 _pw_invoke_protoc(target_name) {
219 forward_variables_from(invoker, "*", _forwarded_vars)
220 language = "nanopb"
221 plugin = "$dir_pw_third_party_nanopb/generator/protoc-gen-nanopb"
Wyatt Heplera3ca62a2021-05-04 16:21:43 -0700222 other_deps = [ "$dir_pw_third_party/nanopb:generate_nanopb_proto.action" ]
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800223 }
224
225 # Create a library with the generated source files.
226 config("$target_name._include_path") {
227 include_dirs = [ "${invoker.base_out_dir}/nanopb" ]
228 visibility = [ ":*" ]
229 }
230
231 pw_source_set(target_name) {
232 forward_variables_from(invoker, _forwarded_vars)
233 public_configs = [ ":$target_name._include_path" ]
Wyatt Heplerf6f74f42021-04-13 19:26:20 -0700234 deps = [ ":$target_name._gen($pw_protobuf_compiler_TOOLCHAIN)" ]
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800235 public_deps = [ "$dir_pw_third_party/nanopb" ] + invoker.deps
236 sources = invoker.outputs
237 public = filter_include(sources, [ "*.pb.h" ])
238 }
Alexei Frolov942adf02019-12-11 17:07:28 -0800239 }
240}
241
Alexei Frolovc912ea72020-10-26 08:43:27 -0700242# Generates raw RPC code for proto files, creating a source_set of the generated
243# files. This is internal and should not be used outside of this file. Use
244# pw_proto_library instead.
Alexei Frolovc912ea72020-10-26 08:43:27 -0700245template("_pw_raw_rpc_proto_library") {
Alexei Frolovc912ea72020-10-26 08:43:27 -0700246 # Create a target which runs protoc configured with the nanopb_rpc plugin to
247 # generate the C++ proto RPC headers.
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800248 _pw_invoke_protoc(target_name) {
249 forward_variables_from(invoker, "*", _forwarded_vars)
250 language = "raw_rpc"
251 plugin = "$dir_pw_rpc/py/pw_rpc/plugin_raw.py"
Wyatt Hepler438caa02021-01-15 17:13:11 -0800252 python_deps = [ "$dir_pw_rpc/py" ]
Alexei Frolovc912ea72020-10-26 08:43:27 -0700253 }
254
255 # Create a library with the generated source files.
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800256 config("$target_name._include_path") {
257 include_dirs = [ "${invoker.base_out_dir}/raw_rpc" ]
258 visibility = [ ":*" ]
259 }
260
Alexei Frolovc912ea72020-10-26 08:43:27 -0700261 pw_source_set(target_name) {
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800262 forward_variables_from(invoker, _forwarded_vars)
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800263 public_configs = [ ":$target_name._include_path" ]
Wyatt Heplerf6f74f42021-04-13 19:26:20 -0700264 deps = [ ":$target_name._gen($pw_protobuf_compiler_TOOLCHAIN)" ]
Alexei Frolovc912ea72020-10-26 08:43:27 -0700265 public_deps = [
266 "$dir_pw_rpc:server",
267 "$dir_pw_rpc/raw:method_union",
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800268 ] + invoker.deps
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800269 public = invoker.outputs
Wyatt Heplerb93749b2021-05-11 09:38:22 -0700270 check_includes = false
Alexei Frolovc912ea72020-10-26 08:43:27 -0700271 }
272}
273
Alexei Frolovdef14712019-12-23 13:03:32 -0800274# Generates Go code for proto files, listing the proto output directory in the
275# metadata variable GOPATH. Internal use only.
Alexei Frolovdef14712019-12-23 13:03:32 -0800276template("_pw_go_proto_library") {
277 _proto_gopath = "$root_gen_dir/go"
Alexei Frolovdef14712019-12-23 13:03:32 -0800278
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800279 _pw_invoke_protoc(target_name) {
280 forward_variables_from(invoker, "*")
281 language = "go"
Alexei Frolovdef14712019-12-23 13:03:32 -0800282 metadata = {
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800283 gopath = [ "GOPATH+=" + rebase_path(_proto_gopath) ]
Alexei Frolovc15a9882019-12-23 14:29:02 -0800284 external_deps = [
285 "github.com/golang/protobuf/proto",
286 "google.golang.org/grpc",
287 ]
Alexei Frolovdef14712019-12-23 13:03:32 -0800288 }
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800289
290 # Override the default "$base_out_dir/$language" output path.
291 out_dir = "$_proto_gopath/src"
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800292 }
293
294 group(target_name) {
Wyatt Heplerf6f74f42021-04-13 19:26:20 -0700295 deps =
296 invoker.deps + [ ":$target_name._gen($pw_protobuf_compiler_TOOLCHAIN)" ]
Alexei Frolovdef14712019-12-23 13:03:32 -0800297 }
298}
299
Alexei Frolova4c0aee2020-12-01 13:48:48 -0800300# Generates Python code for proto files, creating a pw_python_package containing
301# the generated files. This is internal and should not be used outside of this
302# file. Use pw_proto_library instead.
303template("_pw_python_proto_library") {
Alexei Frolova4c0aee2020-12-01 13:48:48 -0800304 _pw_invoke_protoc(target_name) {
Wyatt Heplerdcfcecf2021-03-01 08:36:19 -0800305 forward_variables_from(invoker, "*", _forwarded_vars + [ "python_package" ])
Alexei Frolova4c0aee2020-12-01 13:48:48 -0800306 language = "python"
Wyatt Hepler4b921c12021-03-05 15:33:29 -0800307 python_deps = [ "$dir_pw_protobuf_compiler:protobuf_requirements" ]
Wyatt Heplercf184e82021-05-11 18:15:35 -0700308
309 if (defined(invoker.python_package)) {
310 python_package = invoker.python_package
311 }
Alexei Frolova4c0aee2020-12-01 13:48:48 -0800312 }
313
Wyatt Heplerdcfcecf2021-03-01 08:36:19 -0800314 if (defined(invoker.python_package) && invoker.python_package != "") {
Wyatt Heplerb2062fd2021-06-01 21:33:07 -0700315 # This package is nested in another Python package. Depending on this
316 # its python subtarget is equivalent to depending on the Python package it
317 # is nested in.
318 pw_python_group(target_name) {
319 python_deps = [ invoker.python_package ]
Wyatt Heplerdcfcecf2021-03-01 08:36:19 -0800320 }
Wyatt Heplerd517afc2021-02-03 19:40:08 -0800321
Wyatt Heplercf184e82021-05-11 18:15:35 -0700322 # This proto library is merged into another package, but create a target to
323 # collect its dependencies that the other package can depend on.
324 pw_python_group(target_name + "._deps") {
325 python_deps = invoker.deps
326 other_deps =
327 [ ":${invoker.target_name}._gen($pw_protobuf_compiler_TOOLCHAIN)" ]
328 }
329 } else {
Wyatt Heplerdcfcecf2021-03-01 08:36:19 -0800330 # Create a Python package with the generated source files.
331 pw_python_package(target_name) {
332 forward_variables_from(invoker, _forwarded_vars)
333 generate_setup = {
Anthony DiGirolamoafb861c2021-08-12 16:48:04 -0700334 metadata = {
335 name = invoker._package_dir
336 version =
337 "0.0.1" # TODO(hepler): Need to be able to set this verison.
338 }
Wyatt Heplerdcfcecf2021-03-01 08:36:19 -0800339 }
340 sources = invoker.outputs
341 strip_prefix = "${invoker.base_out_dir}/python"
342 python_deps = invoker.deps
Wyatt Heplerf6f74f42021-04-13 19:26:20 -0700343 other_deps = [ ":$target_name._gen($pw_protobuf_compiler_TOOLCHAIN)" ]
Wyatt Heplerc2ce5242021-03-17 08:42:14 -0700344 static_analysis = []
Alexei Frolova4c0aee2020-12-01 13:48:48 -0800345
Wyatt Heplerdcfcecf2021-03-01 08:36:19 -0800346 _pw_module_as_package = invoker.module_as_package != ""
347 }
Alexei Frolova4c0aee2020-12-01 13:48:48 -0800348 }
349}
350
Alexei Frolov942adf02019-12-11 17:07:28 -0800351# Generates protobuf code from .proto definitions for various languages.
Alexei Frolovb499d3f2020-10-28 13:00:08 -0700352# For each supported generator, creates a sub-target named:
Alexei Frolov942adf02019-12-11 17:07:28 -0800353#
Alexei Frolov8e30d462020-10-22 13:54:36 -0700354# <target_name>.<generator>
Alexei Frolov942adf02019-12-11 17:07:28 -0800355#
Wyatt Hepler4cdc3e72021-03-24 08:41:08 -0700356# GN permits using abbreviated labels when the target name matches the directory
357# name (e.g. //foo for //foo:foo). For consistency with this, the sub-targets
358# for each generator are aliased to the directory when the target name is the
359# same. For example, these two labels are equivalent:
360#
361# //path/to/my_protos:my_protos.pwpb
362# //path/to/my_protos:pwpb
363#
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800364# pw_protobuf_library targets generate Python packages. As such, they must have
365# globally unique package names. The first directory of the prefix or the first
366# common directory of the sources is used as the Python package.
367#
Alexei Frolov942adf02019-12-11 17:07:28 -0800368# Args:
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800369# sources: List of input .proto files.
370# deps: List of other pw_proto_library dependencies.
371# inputs: Other files on which the protos depend (e.g. nanopb .options files).
372# prefix: A prefix to add to the source protos prior to compilation. For
373# example, a source called "foo.proto" with prefix = "nested" will be
374# compiled with protoc as "nested/foo.proto".
375# strip_prefix: Remove this prefix from the source protos. All source and
376# input files must be nested under this path.
Wyatt Hepler4cdc3e72021-03-24 08:41:08 -0700377# python_package: Label of Python package to which to add the proto modules.
Wyatt Heplerb2062fd2021-06-01 21:33:07 -0700378# The .python subtarget will redirect to this package.
Alexei Frolovb499d3f2020-10-28 13:00:08 -0700379#
Alexei Frolov942adf02019-12-11 17:07:28 -0800380template("pw_proto_library") {
381 assert(defined(invoker.sources) && invoker.sources != [],
Wyatt Heplerb4b73a62020-05-27 15:17:27 -0700382 "pw_proto_library requires .proto source files")
Alexei Frolov942adf02019-12-11 17:07:28 -0800383
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800384 if (defined(invoker.python_module_as_package)) {
385 _module_as_package = invoker.python_module_as_package
386
387 _must_be_one_source = invoker.sources
388 assert([ _must_be_one_source[0] ] == _must_be_one_source,
389 "'python_module_as_package' requires exactly one source file")
390 assert(_module_as_package != "",
391 "'python_module_as_package' cannot be be empty")
392 assert(string_split(_module_as_package, "/") == [ _module_as_package ],
393 "'python_module_as_package' cannot contain slashes")
394 assert(!defined(invoker.prefix),
395 "'prefix' cannot be provided with 'python_module_as_package'")
396 } else {
397 _module_as_package = ""
398 }
399
400 if (defined(invoker.strip_prefix)) {
401 _source_root = get_path_info(invoker.strip_prefix, "abspath")
402 } else {
403 _source_root = get_path_info(".", "abspath")
404 }
405
406 if (defined(invoker.prefix)) {
407 _prefix = invoker.prefix
408 } else {
409 _prefix = ""
410 }
411
Wyatt Hepler91741472021-02-03 08:45:10 -0800412 _package_dir = ""
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800413 _source_names = []
Wyatt Hepler91741472021-02-03 08:45:10 -0800414
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800415 # Determine the Python package name to use for these protos. If there is no
416 # prefix, the first directory the sources are nested under is used.
417 foreach(source, rebase_path(invoker.sources, _source_root)) {
Wyatt Hepler91741472021-02-03 08:45:10 -0800418 _path_components = []
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800419 _path_components = string_split(source, "/")
Wyatt Hepler91741472021-02-03 08:45:10 -0800420
421 if (_package_dir == "") {
422 _package_dir = _path_components[0]
423 } else {
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800424 assert(_prefix != "" || _path_components[0] == _package_dir,
425 "Unless 'prefix' is supplied, all .proto sources in a " +
426 "pw_proto_library must be in the same directory tree")
Wyatt Hepler91741472021-02-03 08:45:10 -0800427 }
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800428
429 _source_names +=
430 [ get_path_info(source, "dir") + "/" + get_path_info(source, "name") ]
Wyatt Hepler91741472021-02-03 08:45:10 -0800431 }
432
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800433 # If the 'prefix' was supplied, use that for the package directory.
434 if (_prefix != "") {
435 _prefix_path_components = string_split(_prefix, "/")
436 _package_dir = _prefix_path_components[0]
437 }
438
439 assert(_package_dir != "" && _package_dir != "." && _package_dir != "..",
440 "Either a 'prefix' must be specified or all sources must be nested " +
441 "under a common directory")
442
443 # Define an action that is never executed to prevent duplicate proto packages
444 # from being declared. The target name and the output file include only the
445 # package directory, so different targets that use the same proto package name
446 # will conflict.
447 action("pw_proto_library.$_package_dir") {
448 script = "$dir_pw_build/py/pw_build/nop.py"
Wyatt Hepler91741472021-02-03 08:45:10 -0800449 visibility = []
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800450
451 # Place an error message in the output path (which is never created). If the
452 # package name conflicts occur in different BUILD.gn files, this results in
453 # an otherwise cryptic Ninja error, rather than a GN error.
454 outputs = [ "$root_out_dir/ " +
455 "ERROR - Multiple pw_proto_library targets create the " +
456 "'$_package_dir' package. Change the package name by setting " +
457 "the \"prefix\" arg or move the protos to a different " +
458 "directory, then re-run gn gen." ]
Wyatt Hepler91741472021-02-03 08:45:10 -0800459 }
460
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800461 if (defined(invoker.deps)) {
462 _deps = invoker.deps
463 } else {
464 _deps = []
465 }
466
Wyatt Heplercf184e82021-05-11 18:15:35 -0700467 _common = {
468 base_target = target_name
469
470 # This is the output directory for all files related to this proto library.
471 # Sources are mirrored to "$base_out_dir/sources" and protoc puts outputs in
472 # "$base_out_dir/$language" by default.
473 base_out_dir =
474 get_label_info(":$target_name($pw_protobuf_compiler_TOOLCHAIN)",
475 "target_gen_dir") + "/$target_name.proto_library"
476
477 compile_dir = "$base_out_dir/sources"
478
479 # Refer to the source files as the are mirrored to the output directory.
480 sources = []
481 foreach(file, rebase_path(invoker.sources, _source_root)) {
482 sources += [ "$compile_dir/$_prefix/$file" ]
483 }
484
485 package = _package_dir
486 }
487
Alexei Frolove19ebb82020-05-14 17:21:20 -0700488 # For each proto target, create a file which collects the base directories of
489 # all of its dependencies to list as include paths to protoc.
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800490 generated_file("$target_name._includes") {
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800491 # Collect metadata from the include path files of each dependency.
Wyatt Hepler4cdc3e72021-03-24 08:41:08 -0700492
493 deps = []
494 foreach(dep, _deps) {
495 _base = get_label_info(dep, "label_no_toolchain")
496 deps += [ "$_base._includes(" + get_label_info(dep, "toolchain") + ")" ]
497 }
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800498
Alexei Frolove19ebb82020-05-14 17:21:20 -0700499 data_keys = [ "protoc_includes" ]
Wyatt Heplerdcfcecf2021-03-01 08:36:19 -0800500 outputs = [ "${_common.base_out_dir}/includes.txt" ]
Alexei Frolove19ebb82020-05-14 17:21:20 -0700501
502 # Indicate this library's base directory for its dependents.
503 metadata = {
Michael Spangc8b93902021-05-30 15:53:56 -0400504 protoc_includes = [ rebase_path(_common.compile_dir, root_build_dir) ]
Alexei Frolove19ebb82020-05-14 17:21:20 -0700505 }
506 }
507
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800508 # Mirror the proto sources to the output directory with the prefix added.
Wyatt Heplerf6f74f42021-04-13 19:26:20 -0700509 if (current_toolchain == pw_protobuf_compiler_TOOLCHAIN) {
510 pw_mirror_tree("$target_name._sources") {
511 source_root = _source_root
512 sources = invoker.sources
Alexei Frolov05d8ef22020-06-08 10:32:29 -0700513
Wyatt Heplerf6f74f42021-04-13 19:26:20 -0700514 if (defined(invoker.inputs)) {
515 sources += invoker.inputs
516 }
517
518 directory = "${_common.compile_dir}/$_prefix"
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800519 }
Wyatt Heplerf6f74f42021-04-13 19:26:20 -0700520 } else {
521 not_needed(invoker, [ "inputs" ])
Alexei Frolov79b7cb02020-07-06 13:51:43 -0700522 }
523
Alexei Frolovb499d3f2020-10-28 13:00:08 -0700524 # Enumerate all of the protobuf generator targets.
Alexei Frolovc4b62ec2020-07-13 08:35:10 -0700525
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800526 _pw_pwpb_proto_library("$target_name.pwpb") {
Alexei Frolovb499d3f2020-10-28 13:00:08 -0700527 forward_variables_from(invoker, _forwarded_vars)
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800528 forward_variables_from(_common, "*")
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800529
530 deps = []
531 foreach(dep, _deps) {
532 _base = get_label_info(dep, "label_no_toolchain")
533 deps += [ "$_base.pwpb(" + get_label_info(dep, "toolchain") + ")" ]
534 }
535
536 outputs = []
537 foreach(name, _source_names) {
538 outputs += [ "$base_out_dir/pwpb/$_prefix/${name}.pwpb.h" ]
539 }
Alexei Frolovb499d3f2020-10-28 13:00:08 -0700540 }
541
542 if (dir_pw_third_party_nanopb != "") {
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800543 _pw_nanopb_rpc_proto_library("$target_name.nanopb_rpc") {
Alexei Frolovb499d3f2020-10-28 13:00:08 -0700544 forward_variables_from(invoker, _forwarded_vars)
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800545 forward_variables_from(_common, "*")
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800546
547 deps = []
548 foreach(dep, _deps) {
549 _lbl = get_label_info(dep, "label_no_toolchain")
550 deps += [ "$_lbl.nanopb_rpc(" + get_label_info(dep, "toolchain") + ")" ]
551 }
552
553 outputs = []
554 foreach(name, _source_names) {
555 outputs += [ "$base_out_dir/nanopb_rpc/$_prefix/${name}.rpc.pb.h" ]
556 }
Alexei Frolovc4b62ec2020-07-13 08:35:10 -0700557 }
558
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800559 _pw_nanopb_proto_library("$target_name.nanopb") {
560 forward_variables_from(invoker, _forwarded_vars)
561 forward_variables_from(_common, "*")
562
563 deps = []
564 foreach(dep, _deps) {
565 _base = get_label_info(dep, "label_no_toolchain")
566 deps += [ "$_base.nanopb(" + get_label_info(dep, "toolchain") + ")" ]
Wyatt Heplerd517afc2021-02-03 19:40:08 -0800567 }
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800568
569 outputs = []
570 foreach(name, _source_names) {
571 outputs += [
572 "$base_out_dir/nanopb/$_prefix/${name}.pb.h",
573 "$base_out_dir/nanopb/$_prefix/${name}.pb.c",
574 ]
Wyatt Heplerd517afc2021-02-03 19:40:08 -0800575 }
Alexei Frolovb499d3f2020-10-28 13:00:08 -0700576 }
577 } else {
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800578 pw_error("$target_name.nanopb_rpc") {
Alexei Frolovb499d3f2020-10-28 13:00:08 -0700579 message =
580 "\$dir_pw_third_party_nanopb must be set to generate nanopb RPC code."
Alexei Frolov8185c822020-06-12 10:45:04 -0700581 }
Alexei Frolov942adf02019-12-11 17:07:28 -0800582
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800583 pw_error("$target_name.nanopb") {
Alexei Frolovb499d3f2020-10-28 13:00:08 -0700584 message =
585 "\$dir_pw_third_party_nanopb must be set to compile nanopb protobufs."
Alexei Frolov942adf02019-12-11 17:07:28 -0800586 }
587 }
588
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800589 _pw_raw_rpc_proto_library("$target_name.raw_rpc") {
Alexei Frolovb499d3f2020-10-28 13:00:08 -0700590 forward_variables_from(invoker, _forwarded_vars)
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800591 forward_variables_from(_common, "*")
592
593 deps = []
594 foreach(dep, _deps) {
595 _base = get_label_info(dep, "label_no_toolchain")
596 deps += [ "$_base.raw_rpc(" + get_label_info(dep, "toolchain") + ")" ]
597 }
598
599 outputs = []
600 foreach(name, _source_names) {
601 outputs += [ "$base_out_dir/raw_rpc/$_prefix/${name}.raw_rpc.pb.h" ]
602 }
Alexei Frolovb499d3f2020-10-28 13:00:08 -0700603 }
604
Wyatt Heplerd9336a42020-11-10 09:47:30 -0800605 _pw_go_proto_library("$target_name.go") {
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800606 sources = _common.sources
607
608 deps = []
609 foreach(dep, _deps) {
610 _base = get_label_info(dep, "label_no_toolchain")
611 deps += [ "$_base.go(" + get_label_info(dep, "toolchain") + ")" ]
612 }
613
614 forward_variables_from(_common, "*")
Alexei Frolovb499d3f2020-10-28 13:00:08 -0700615 }
616
Alexei Frolov38cad0c2020-12-03 12:36:24 -0800617 _pw_python_proto_library("$target_name.python") {
Alexei Frolov38cad0c2020-12-03 12:36:24 -0800618 forward_variables_from(_common, "*")
Wyatt Heplerdcfcecf2021-03-01 08:36:19 -0800619 forward_variables_from(invoker, [ "python_package" ])
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800620 module_as_package = _module_as_package
621
622 deps = []
623 foreach(dep, _deps) {
624 _base = get_label_info(dep, "label_no_toolchain")
625 deps += [ "$_base.python(" + get_label_info(dep, "toolchain") + ")" ]
626 }
627
628 if (module_as_package == "") {
629 _python_prefix = "$base_out_dir/python/$_prefix"
630 } else {
631 _python_prefix = "$base_out_dir/python/$module_as_package"
632 }
633
634 outputs = []
635 foreach(name, _source_names) {
636 outputs += [
637 "$_python_prefix/${name}_pb2.py",
638 "$_python_prefix/${name}_pb2.pyi",
639 ]
640 }
Alexei Frolova4c0aee2020-12-01 13:48:48 -0800641 }
642
Wyatt Heplerb4b73a62020-05-27 15:17:27 -0700643 # All supported pw_protobuf generators.
644 _protobuf_generators = [
645 "pwpb",
646 "nanopb",
Alexei Frolov79b7cb02020-07-06 13:51:43 -0700647 "nanopb_rpc",
Alexei Frolovc912ea72020-10-26 08:43:27 -0700648 "raw_rpc",
Wyatt Heplerb4b73a62020-05-27 15:17:27 -0700649 "go",
Alexei Frolova4c0aee2020-12-01 13:48:48 -0800650 "python",
Wyatt Heplerb4b73a62020-05-27 15:17:27 -0700651 ]
652
Wyatt Hepler4cdc3e72021-03-24 08:41:08 -0700653 # If the label matches the directory name, alias the subtargets to the
654 # directory (e.g. //foo:nanopb is an alias for //foo:foo.nanopb).
655 if (get_label_info(":$target_name", "name") ==
656 get_path_info(get_label_info(":$target_name", "dir"), "name")) {
Wyatt Hepler2c6c0ba2021-04-07 09:50:23 -0700657 foreach(_generator, _protobuf_generators - [ "python" ]) {
658 group(_generator) {
Wyatt Hepler4cdc3e72021-03-24 08:41:08 -0700659 public_deps = [ ":${invoker.target_name}.$_generator" ]
660 }
661 }
Wyatt Hepler2c6c0ba2021-04-07 09:50:23 -0700662
663 pw_python_group("python") {
664 python_deps = [ ":${invoker.target_name}.python" ]
665 }
Wyatt Hepler4cdc3e72021-03-24 08:41:08 -0700666 }
667
Alexei Frolov942adf02019-12-11 17:07:28 -0800668 # If the user attempts to use the target directly instead of one of the
Alexei Frolovf39cd8b2020-04-13 17:59:20 -0700669 # generator targets, run a script which prints a nice error message.
Wyatt Heplerc8e05a42020-10-19 14:49:39 -0700670 pw_python_action(target_name) {
Alexei Frolov942adf02019-12-11 17:07:28 -0800671 script = string_join("/",
672 [
673 dir_pw_protobuf_compiler,
674 "py",
675 "pw_protobuf_compiler",
676 "proto_target_invalid.py",
677 ])
678 args = [
679 "--target",
680 target_name,
681 "--dir",
682 get_path_info(".", "abspath"),
683 "--root",
684 "//",
Alexei Frolovb499d3f2020-10-28 13:00:08 -0700685 ] + _protobuf_generators
Alexei Frolov942adf02019-12-11 17:07:28 -0800686 stamp = true
687 }
688}