pw_protobuf_compiler: Ensure nanopb_pb2.py is generated
Import the Nanopb Python package to ensure that nanopb_pb2.py is
generated prior generating any Nanopb protos. This prevents race
conditions in clean builds.
Change-Id: I4b07ceb5665c49d0bd73a35759af18ec275094ed
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/43741
Commit-Queue: Wyatt Hepler <hepler@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
diff --git a/pw_build/python.gni b/pw_build/python.gni
index 50d247f..a68da03 100644
--- a/pw_build/python.gni
+++ b/pw_build/python.gni
@@ -655,6 +655,7 @@
_python_deps = invoker.python_deps
} else {
_python_deps = []
+ not_needed([ "invoker" ]) # Allow empty groups.
}
group(target_name) {
diff --git a/pw_env_setup/BUILD.gn b/pw_env_setup/BUILD.gn
index 2a4528f..04d2752 100644
--- a/pw_env_setup/BUILD.gn
+++ b/pw_env_setup/BUILD.gn
@@ -52,6 +52,7 @@
# Standalone scripts
"$dir_pw_hdlc/rpc_example:example_script",
+ "$dir_pw_third_party/nanopb:generate_nanopb_proto",
]
}
diff --git a/pw_protobuf_compiler/proto.cmake b/pw_protobuf_compiler/proto.cmake
index d05b960..2410c5d 100644
--- a/pw_protobuf_compiler/proto.cmake
+++ b/pw_protobuf_compiler/proto.cmake
@@ -246,6 +246,11 @@
"${INPUTS}"
"${DEPS}"
)
+
+ # Ensure that nanopb_pb2.py is generated to avoid race conditions.
+ add_dependencies("${NAME}._generate.nanopb"
+ pw_third_party.nanopb.generate_proto
+ )
endif()
# Create the library with the generated source files.
diff --git a/pw_protobuf_compiler/proto.gni b/pw_protobuf_compiler/proto.gni
index c6348c9..51d443f 100644
--- a/pw_protobuf_compiler/proto.gni
+++ b/pw_protobuf_compiler/proto.gni
@@ -69,6 +69,10 @@
deps += [ get_label_info(dep, "label_no_toolchain") + "._gen" ]
}
+ if (defined(invoker.other_deps)) {
+ deps += invoker.other_deps
+ }
+
args = [
"--language",
invoker.language,
@@ -192,6 +196,7 @@
forward_variables_from(invoker, "*", _forwarded_vars)
language = "nanopb"
plugin = "$dir_pw_third_party_nanopb/generator/protoc-gen-nanopb"
+ other_deps = [ "$dir_pw_third_party/nanopb:generate_nanopb_proto.action" ]
}
# Create a library with the generated source files.
diff --git a/third_party/nanopb/BUILD.gn b/third_party/nanopb/BUILD.gn
index 77e2453..35620ac 100644
--- a/third_party/nanopb/BUILD.gn
+++ b/third_party/nanopb/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2020 The Pigweed Authors
+# Copyright 2021 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
@@ -14,6 +14,7 @@
import("//build_overrides/pigweed.gni")
+import("$dir_pw_build/python.gni")
import("$dir_pw_build/target_types.gni")
import("$dir_pw_protobuf_compiler/proto.gni")
import("nanopb.gni")
@@ -47,7 +48,19 @@
sources = [ "$dir_pw_third_party_nanopb/generator/proto/nanopb.proto" ]
python_module_as_package = "nanopb_pb2"
}
+
+ # Generates nanopb_pb2.py, which is needed to compile protobufs with Nanopb.
+ pw_python_script("generate_nanopb_proto") {
+ sources = [ "generate_nanopb_proto.py" ]
+ pylintrc = "$dir_pigweed/.pylintrc"
+ action = {
+ args = [ rebase_path(dir_pw_third_party_nanopb) ]
+ stamp = true
+ }
+ }
} else {
group("nanopb") {
}
+ pw_python_group("generate_nanopb_proto") {
+ }
}
diff --git a/third_party/nanopb/CMakeLists.txt b/third_party/nanopb/CMakeLists.txt
index 7b5745e..3c2927b 100644
--- a/third_party/nanopb/CMakeLists.txt
+++ b/third_party/nanopb/CMakeLists.txt
@@ -31,3 +31,15 @@
INTERFACE
"${dir_pw_third_party_nanopb}"
)
+
+# Generates nanopb_pb2.py, which is needed to compile protobufs with Nanopb.
+add_custom_command(
+ COMMAND
+ python "${CMAKE_CURRENT_LIST_DIR}/generate_nanopb_proto.py" "${dir_pw_third_party_nanopb}"
+ OUTPUT
+ "${dir_pw_third_party_nanopb}/generator/proto/nanopb_pb2.py"
+)
+add_custom_target(pw_third_party.nanopb.generate_proto
+ DEPENDS
+ "${dir_pw_third_party_nanopb}/generator/proto/nanopb_pb2.py"
+)
diff --git a/third_party/nanopb/generate_nanopb_proto.py b/third_party/nanopb/generate_nanopb_proto.py
new file mode 100644
index 0000000..d7b84d1
--- /dev/null
+++ b/third_party/nanopb/generate_nanopb_proto.py
@@ -0,0 +1,49 @@
+# Copyright 2021 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.
+"""Generates nanopb_pb2.py by importing the Nanopb proto module.
+
+The Nanopb repository generates nanopb_pb2.py dynamically when its Python
+package is imported if it does not exist. If multiple processes try to use
+Nanopb to compile simultaneously on a clean build, they can interfere with each
+other. One process might rewrite nanopb_pb2.py as another process is trying to
+access it, resulting in import errors.
+
+This script imports the Nanopb module so that nanopb_pb2.py is generated if it
+doesn't exist. All Nanopb proto compilation targets depend on this script so
+that nanopb_pb2.py is guaranteed to exist before they need it.
+"""
+
+import argparse
+import importlib.util
+from pathlib import Path
+import sys
+
+
+def generate_nanopb_proto(root: Path) -> None:
+ sys.path.append(str(root / 'generator'))
+
+ spec = importlib.util.spec_from_file_location(
+ 'proto', root / 'generator' / 'proto' / '__init__.py')
+ proto_module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(proto_module) # type: ignore[union-attr]
+
+
+def _parse_args() -> argparse.Namespace:
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument('root', type=Path, help='Nanopb root')
+ return parser.parse_args()
+
+
+if __name__ == '__main__':
+ generate_nanopb_proto(**vars(_parse_args()))