pw_protobuf_compiler: Decouple proto packaging from directory
- Allow specifying prefix and strip_prefix arguments for proto files.
The proto directory tree is built as specified in the out directory.
- Only invoke protoc from the default toolchain. This prevents duplicate
protoc invocations.
- Prevent duplicate pw_proto_library Python package definitions anywhere
in the build.
- Replace implicit handling of standalone external protos with a
python_package_as_module option.
Change-Id: Id37d8b4d83294f7d3142a389e74ceea96dd4d620
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/34640
Reviewed-by: Alexei Frolov <frolv@google.com>
diff --git a/docs/BUILD.gn b/docs/BUILD.gn
index 23f527c..3941310 100644
--- a/docs/BUILD.gn
+++ b/docs/BUILD.gn
@@ -137,5 +137,6 @@
":core_docs",
":module_docs",
":target_docs",
+ "$dir_pw_env_setup:python.install",
]
}
diff --git a/pw_build/python.gni b/pw_build/python.gni
index 031a7ba..9ecae0e 100644
--- a/pw_build/python.gni
+++ b/pw_build/python.gni
@@ -76,6 +76,15 @@
_test_sources = []
}
+ # The Python targets are always instantiated in the default toolchain. Use
+ # fully qualified labels so that the toolchain is not lost.
+ _other_deps = []
+ if (defined(invoker.other_deps)) {
+ foreach(dep, invoker.other_deps) {
+ _other_deps += [ get_label_info(dep, "label_with_toolchain") ]
+ }
+ }
+
_all_py_files += _test_sources
# pw_python_script uses pw_python_package, but with a limited set of features.
@@ -171,11 +180,7 @@
inputs += invoker.inputs
}
- deps = _python_deps
-
- if (defined(invoker.other_deps)) {
- deps += invoker.other_deps
- }
+ deps = _python_deps + _other_deps
}
if (_is_package) {
@@ -199,7 +204,8 @@
deps = [ ":$_internal_target" ]
foreach(dep, _python_deps) {
- deps += [ "$dep.install" ]
+ _subtarget = get_label_info(dep, "label_no_toolchain") + ".install"
+ deps += [ "$_subtarget(" + get_label_info(dep, "toolchain") + ")" ]
}
}
diff --git a/pw_doctor/py/BUILD.gn b/pw_doctor/py/BUILD.gn
index 82c2d86..bec5e59 100644
--- a/pw_doctor/py/BUILD.gn
+++ b/pw_doctor/py/BUILD.gn
@@ -22,5 +22,6 @@
"pw_doctor/__init__.py",
"pw_doctor/doctor.py",
]
+ python_deps = [ "$dir_pw_cli/py" ]
pylintrc = "$dir_pigweed/.pylintrc"
}
diff --git a/pw_protobuf_compiler/docs.rst b/pw_protobuf_compiler/docs.rst
index 30bb37a..ac659fb 100644
--- a/pw_protobuf_compiler/docs.rst
+++ b/pw_protobuf_compiler/docs.rst
@@ -57,12 +57,19 @@
**Arguments**
-* ``sources``: List of ``.proto`` files.
-* ``deps``: Other ``pw_proto_library`` targets that this one depends on.
+* ``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).
+* ``prefix``: A prefix to add to the source protos prior to compilation. For
+ example, a source called ``"foo.proto"`` with ``prefix = "nested"`` will be
+ compiled with protoc as ``"nested/foo.proto"``.
+* ``strip_prefix``: Remove this prefix from the source protos. All source and
+ input files must be nested under this path.
**Example**
-.. code::
+.. code-block::
import("$dir_pw_protobuf_compiler/proto.gni")
@@ -74,7 +81,13 @@
}
pw_proto_library("my_other_protos") {
- sources = [ "my_other_protos/baz.proto" ] # imports foo.proto
+ sources = [ "some/other/path/baz.proto" ] # imports foo.proto
+
+ # This removes the "some/other/path" prefix from the proto files.
+ strip_prefix = "some/other/path"
+
+ # This adds the "my_other_protos/" prefix to the proto files.
+ prefix = "my_other_protos"
# Proto libraries depend on other proto libraries directly.
deps = [ ":my_protos" ]
@@ -91,21 +104,146 @@
deps = [ ":my_other_protos.pwpb" ]
}
+From C++, ``baz.proto`` included as follows:
+
+.. code-block:: cpp
+
+ #include "my_other_protos/baz.pwpb.h"
+
+From Python, ``baz.proto`` is imported as follows:
+
+.. code-block:: python
+
+ from my_other_protos import baz_pb2
+
Proto file structure
--------------------
Protobuf source files must be nested under another directory when they are
-listed in sources. This ensures that they can be packaged properly in Python.
-The first directory is used as the Python package name.
+compiled. This ensures that they can be packaged properly in Python. The first
+directory is used as the Python package name, so must be unique across the
+build. The ``prefix`` option may be used to set this directory.
-The requirements for proto file structure in the source tree will be relaxed in
-future updates.
+Using ``prefix`` and ``strip_prefix`` together allows remapping proto files to
+a completely different path. This can be useful when working with protos defined
+in external libraries. For example, consider this proto library:
+
+.. code-block::
+
+ pw_proto_library("external_protos") {
+ sources = [
+ "//other/external/some_library/src/protos/alpha.proto",
+ "//other/external/some_library/src/protos/beta.proto,
+ "//other/external/some_library/src/protos/internal/gamma.proto",
+ ]
+ strip_prefix = "//other/external/some_library/src/protos"
+ prefix = "some_library"
+ }
+
+These protos will be compiled by protoc as if they were in this file structure:
+
+.. code-block::
+
+ some_library/
+ ├── alpha.proto
+ ├── beta.proto
+ └── internal
+ └── gamma.proto
Working with externally defined protos
--------------------------------------
``pw_proto_library`` targets may be used to build ``.proto`` sources from
existing projects. In these cases, it may be necessary to supply the
-``include_path`` argument, which specifies the protobuf include path to use for
+``strip_prefix`` argument, which specifies the protobuf include path to use for
``protoc``. If only a single external protobuf is being compiled, the
-requirement that the protobuf be nested under a directory is waived. This
-exception should only be used when absolutely necessary -- for example, to
-support proto files that includes ``import "nanopb.proto"`` in them.
+``python_module_as_package`` option can be used to override the requirement that
+the protobuf be nested under a directory. This option generates a Python package
+with the same name as the proto file, so that the generated proto can be
+imported as if it were a standalone Python module.
+
+For example, the ``pw_proto_library`` target for Nanopb sets
+``python_module_as_package`` to ``nanopb_pb2``.
+
+.. code-block::
+
+ pw_proto_library("proto") {
+ strip_prefix = "$dir_pw_third_party_nanopb/generator/proto"
+ sources = [ "$dir_pw_third_party_nanopb/generator/proto/nanopb.proto" ]
+ python_module_as_package = "nanopb_pb2"
+ }
+
+In Python, this makes ``nanopb.proto`` available as ``import nanopb_pb2`` via
+the ``nanopb_pb2`` Python package. In C++, ``nanopb.proto`` is accessed as
+``#include "nanopb.pwpb.h"``.
+
+The ``python_module_as_package`` feature should only be used when absolutely
+necessary --- for example, to support proto files that include
+``import "nanopb.proto"``.
+
+CMake
+=====
+CMake provides a ``pw_proto_library`` function with similar features as the
+GN template. The CMake build only supports building firmware code, so
+``pw_proto_library`` does not generate a Python package.
+
+**Arguments**
+
+* ``NAME``: the base name of the libraries to create
+* ``SOURCES``: .proto source files
+* ``DEPS``: dependencies on other ``pw_proto_library`` targets
+* ``PREFIX``: prefix add to the proto files
+* ``STRIP_PREFIX``: prefix to remove from the proto files
+* ``INPUTS``: files to include along with the .proto files (such as Nanopb
+ .options files)
+
+**Example**
+
+ .. code-block:: cmake
+
+ include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+ include($ENV{PW_ROOT}/pw_protobuf_compiler/proto.cmake)
+
+ pw_proto_library(my_module.my_protos
+ SOURCES
+ my_protos/foo.proto
+ my_protos/bar.proto
+ )
+
+ pw_proto_library(my_module.my_protos
+ SOURCES
+ my_protos/foo.proto
+ my_protos/bar.proto
+ )
+
+ pw_proto_library(my_module.my_other_protos
+ SOURCES
+ some/other/path/baz.proto # imports foo.proto
+
+ # This removes the "some/other/path" prefix from the proto files.
+ STRIP_PREFIX
+ some/other/path
+
+ # This adds the "my_other_protos/" prefix to the proto files.
+ PREFIX
+ my_other_protos
+
+ # Proto libraries depend on other proto libraries directly.
+ DEPS
+ my_module.my_protos
+ )
+
+ add_library(my_module.my_cc_code
+ foo.cc
+ bar.cc
+ baz.cc
+ )
+
+ # When depending on protos in a source_set, specify the generator suffix.
+ target_link_libraries(my_module.my_cc_code PUBLIC
+ my_module.my_other_protos.pwpb
+ )
+
+These proto files are accessed in C++ the same as in the GN build:
+
+.. code-block:: cpp
+
+ #include "my_other_protos/baz.pwpb"
diff --git a/pw_protobuf_compiler/proto.cmake b/pw_protobuf_compiler/proto.cmake
index 73b06b6..cc07ce0 100644
--- a/pw_protobuf_compiler/proto.cmake
+++ b/pw_protobuf_compiler/proto.cmake
@@ -30,11 +30,16 @@
# NAME - the base name of the libraries to create
# SOURCES - .proto source files
# DEPS - dependencies on other pw_proto_library targets
+# PREFIX - prefix add to the proto files
+# STRIP_PREFIX - prefix to remove from the proto files
+# INPUTS - files to include along with the .proto files (such as Nanopb
+# .options files
#
function(pw_proto_library NAME)
- cmake_parse_arguments(PARSE_ARGV 1 arg "" "" "SOURCES;DEPS")
+ cmake_parse_arguments(PARSE_ARGV 1 arg "" "STRIP_PREFIX;PREFIX"
+ "SOURCES;INPUTS;DEPS")
- set(out_dir "${CMAKE_CURRENT_BINARY_DIR}/protos")
+ set(out_dir "${CMAKE_CURRENT_BINARY_DIR}/${NAME}")
# Use INTERFACE libraries to track the proto include paths that are passed to
# protoc.
@@ -46,33 +51,77 @@
target_link_libraries("${NAME}._includes" INTERFACE ${include_deps})
# Generate a file with all include paths needed by protoc.
- set(include_file "${out_dir}/${NAME}.include_paths.txt")
+ set(include_file "${out_dir}/include_paths.txt")
file(GENERATE OUTPUT "${include_file}"
CONTENT
"$<TARGET_PROPERTY:${NAME}._includes,INTERFACE_INCLUDE_DIRECTORIES>")
+ if("${arg_STRIP_PREFIX}" STREQUAL "")
+ set(arg_STRIP_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}")
+ endif()
+
+ foreach(path IN LISTS arg_SOURCES arg_INPUTS)
+ get_filename_component(abspath "${path}" ABSOLUTE)
+ list(APPEND files_to_mirror "${abspath}")
+ endforeach()
+
+ # Mirror the sources to the output directory with the specified prefix.
+ _pw_rebase_paths(
+ sources "${out_dir}/sources/${arg_PREFIX}" "${arg_STRIP_PREFIX}" "${arg_SOURCES}" "")
+ _pw_rebase_paths(
+ inputs "${out_dir}/sources/${arg_PREFIX}" "${arg_STRIP_PREFIX}" "${arg_INPUTS}" "")
+
+ add_custom_command(
+ COMMAND
+ python
+ "$ENV{PW_ROOT}/pw_build/py/pw_build/mirror_tree.py"
+ --source-root "${arg_STRIP_PREFIX}"
+ --directory "${out_dir}/sources/${arg_PREFIX}"
+ ${files_to_mirror}
+ DEPENDS
+ "$ENV{PW_ROOT}/pw_build/py/pw_build/mirror_tree.py"
+ ${files_to_mirror}
+ ${arg_DEPS}
+ OUTPUT
+ ${sources} ${inputs}
+ )
+
# Create a protobuf target for each supported protobuf library.
_pw_pwpb_library(
- "${NAME}" "${arg_SOURCES}" "${arg_DEPS}" "${include_file}" "${out_dir}")
+ "${NAME}" "${sources}" "${inputs}" "${arg_DEPS}" "${include_file}" "${out_dir}")
_pw_raw_rpc_library(
- "${NAME}" "${arg_SOURCES}" "${arg_DEPS}" "${include_file}" "${out_dir}")
+ "${NAME}" "${sources}" "${inputs}" "${arg_DEPS}" "${include_file}" "${out_dir}")
_pw_nanopb_library(
- "${NAME}" "${arg_SOURCES}" "${arg_DEPS}" "${include_file}" "${out_dir}")
+ "${NAME}" "${sources}" "${inputs}" "${arg_DEPS}" "${include_file}" "${out_dir}")
_pw_nanopb_rpc_library(
- "${NAME}" "${arg_SOURCES}" "${arg_DEPS}" "${include_file}" "${out_dir}")
+ "${NAME}" "${sources}" "${inputs}" "${arg_DEPS}" "${include_file}" "${out_dir}")
endfunction(pw_proto_library)
+function(_pw_rebase_paths VAR OUT_DIR ROOT FILES EXTENSIONS)
+ foreach(file IN LISTS FILES)
+ get_filename_component(file "${file}" ABSOLUTE)
+ file(RELATIVE_PATH file "${ROOT}" "${file}")
+
+ if ("${EXTENSIONS}" STREQUAL "")
+ list(APPEND mirrored_files "${OUT_DIR}/${file}")
+ else()
+ foreach(ext IN LISTS EXTENSIONS)
+ get_filename_component(dir "${file}" DIRECTORY)
+ get_filename_component(name "${file}" NAME_WE)
+ list(APPEND mirrored_files "${OUT_DIR}/${dir}/${name}${ext}")
+ endforeach()
+ endif()
+ endforeach()
+
+ set("${VAR}" "${mirrored_files}" PARENT_SCOPE)
+endfunction(_pw_rebase_paths)
+
# Internal function that invokes protoc through generate_protos.py.
function(_pw_generate_protos
- TARGET LANGUAGE PLUGIN OUTPUT_EXTS INCLUDE_FILE OUT_DIR SOURCES DEPS)
- # Determine the names of the output files.
- foreach(extension IN LISTS OUTPUT_EXTS)
- foreach(source_file IN LISTS SOURCES)
- get_filename_component(dir "${source_file}" DIRECTORY)
- get_filename_component(name "${source_file}" NAME_WE)
- list(APPEND outputs "${OUT_DIR}/${dir}/${name}${extension}")
- endforeach()
- endforeach()
+ TARGET LANGUAGE PLUGIN OUTPUT_EXTS INCLUDE_FILE OUT_DIR SOURCES INPUTS DEPS)
+ # Determine the names of the compiled output files.
+ _pw_rebase_paths(outputs
+ "${OUT_DIR}/${LANGUAGE}" "${OUT_DIR}/sources" "${SOURCES}" "${OUTPUT_EXTS}")
# Export the output files to the caller's scope so it can use them if needed.
set(generated_outputs "${outputs}" PARENT_SCOPE)
@@ -90,14 +139,14 @@
"${script}"
--language "${LANGUAGE}"
--plugin-path "${PLUGIN}"
- --include-path "${CMAKE_CURRENT_SOURCE_DIR}"
--include-file "${INCLUDE_FILE}"
- --out-dir "${OUT_DIR}"
- ${ARGN}
- ${SOURCES}
+ --compile-dir "${OUT_DIR}/sources"
+ --out-dir "${OUT_DIR}/${LANGUAGE}"
+ --sources ${SOURCES}
DEPENDS
- ${SOURCES}
${script}
+ ${SOURCES}
+ ${INPUTS}
${DEPS}
OUTPUT
${outputs}
@@ -106,7 +155,7 @@
endfunction(_pw_generate_protos)
# Internal function that creates a pwpb proto library.
-function(_pw_pwpb_library NAME SOURCES DEPS INCLUDE_FILE OUT_DIR)
+function(_pw_pwpb_library NAME SOURCES INPUTS DEPS INCLUDE_FILE OUT_DIR)
list(TRANSFORM DEPS APPEND .pwpb)
_pw_generate_protos("${NAME}.generate.pwpb"
@@ -116,18 +165,19 @@
"${INCLUDE_FILE}"
"${OUT_DIR}"
"${SOURCES}"
+ "${INPUTS}"
"${DEPS}"
)
# Create the library with the generated source files.
add_library("${NAME}.pwpb" INTERFACE)
- target_include_directories("${NAME}.pwpb" INTERFACE "${OUT_DIR}")
+ target_include_directories("${NAME}.pwpb" INTERFACE "${OUT_DIR}/pwpb")
target_link_libraries("${NAME}.pwpb" INTERFACE pw_protobuf ${DEPS})
add_dependencies("${NAME}.pwpb" "${NAME}.generate.pwpb")
endfunction(_pw_pwpb_library)
# Internal function that creates a raw_rpc proto library.
-function(_pw_raw_rpc_library NAME SOURCES DEPS INCLUDE_FILE OUT_DIR)
+function(_pw_raw_rpc_library NAME SOURCES INPUTS DEPS INCLUDE_FILE OUT_DIR)
list(TRANSFORM DEPS APPEND .raw_rpc)
_pw_generate_protos("${NAME}.generate.raw_rpc"
@@ -137,12 +187,13 @@
"${INCLUDE_FILE}"
"${OUT_DIR}"
"${SOURCES}"
+ "${INPUTS}"
"${DEPS}"
)
# Create the library with the generated source files.
add_library("${NAME}.raw_rpc" INTERFACE)
- target_include_directories("${NAME}.raw_rpc" INTERFACE "${OUT_DIR}")
+ target_include_directories("${NAME}.raw_rpc" INTERFACE "${OUT_DIR}/raw_rpc")
target_link_libraries("${NAME}.raw_rpc"
INTERFACE
pw_rpc.raw
@@ -153,7 +204,7 @@
endfunction(_pw_raw_rpc_library)
# Internal function that creates a nanopb proto library.
-function(_pw_nanopb_library NAME SOURCES DEPS INCLUDE_FILE OUT_DIR)
+function(_pw_nanopb_library NAME SOURCES INPUTS DEPS INCLUDE_FILE OUT_DIR)
list(TRANSFORM DEPS APPEND .nanopb)
set(nanopb_dir "$<TARGET_PROPERTY:$<IF:$<TARGET_EXISTS:protobuf-nanopb-static>,protobuf-nanopb-static,pw_build.empty>,SOURCE_DIR>")
@@ -167,18 +218,19 @@
"${INCLUDE_FILE}"
"${OUT_DIR}"
"${SOURCES}"
+ "${INPUTS}"
"${DEPS}"
)
# Create the library with the generated source files.
add_library("${NAME}.nanopb" EXCLUDE_FROM_ALL ${generated_outputs})
- target_include_directories("${NAME}.nanopb" PUBLIC "${OUT_DIR}")
+ target_include_directories("${NAME}.nanopb" PUBLIC "${OUT_DIR}/nanopb")
target_link_libraries("${NAME}.nanopb" PUBLIC pw_third_party.nanopb ${DEPS})
add_dependencies("${NAME}.nanopb" "${NAME}.generate.nanopb")
endfunction(_pw_nanopb_library)
# Internal function that creates a nanopb_rpc library.
-function(_pw_nanopb_rpc_library NAME SOURCES DEPS INCLUDE_FILE OUT_DIR)
+function(_pw_nanopb_rpc_library NAME SOURCES INPUTS DEPS INCLUDE_FILE OUT_DIR)
# Determine the names of the output files.
list(TRANSFORM DEPS APPEND .nanopb_rpc)
@@ -189,12 +241,16 @@
"${INCLUDE_FILE}"
"${OUT_DIR}"
"${SOURCES}"
+ "${INPUTS}"
"${DEPS}"
)
# Create the library with the generated source files.
add_library("${NAME}.nanopb_rpc" INTERFACE)
- target_include_directories("${NAME}.nanopb_rpc" INTERFACE "${OUT_DIR}")
+ target_include_directories("${NAME}.nanopb_rpc"
+ INTERFACE
+ "${OUT_DIR}/nanopb_rpc"
+ )
target_link_libraries("${NAME}.nanopb_rpc"
INTERFACE
"${NAME}.nanopb"
diff --git a/pw_protobuf_compiler/proto.gni b/pw_protobuf_compiler/proto.gni
index ce297c6..1e8e598 100644
--- a/pw_protobuf_compiler/proto.gni
+++ b/pw_protobuf_compiler/proto.gni
@@ -16,6 +16,7 @@
import("$dir_pw_build/error.gni")
import("$dir_pw_build/input_group.gni")
+import("$dir_pw_build/mirror_tree.gni")
import("$dir_pw_build/python.gni")
import("$dir_pw_build/python_action.gni")
import("$dir_pw_build/target_types.gni")
@@ -34,7 +35,18 @@
# This creates the internal GN target $target_name.$language._gen that compiles
# proto files with protoc.
template("_pw_invoke_protoc") {
- _output = rebase_path(get_target_outputs(":${invoker.base_target}._metadata"))
+ if (defined(invoker.out_dir)) {
+ _out_dir = invoker.out_dir
+ } else {
+ _out_dir = "${invoker.base_out_dir}/${invoker.language}"
+ if (defined(invoker.module_as_package) && invoker.module_as_package != "") {
+ assert(invoker.language == "python")
+ _out_dir = "$_out_dir/${invoker.module_as_package}"
+ }
+ }
+
+ _includes =
+ rebase_path(get_target_outputs(":${invoker.base_target}._includes"))
pw_python_action("$target_name._gen") {
forward_variables_from(invoker, [ "metadata" ])
@@ -47,43 +59,40 @@
}
deps = [
- ":${invoker.base_target}._metadata",
- ":${invoker.base_target}._inputs",
- ] + invoker.deps
+ ":${invoker.base_target}._includes",
+ ":${invoker.base_target}._sources",
+ ]
+
+ foreach(dep, invoker.deps) {
+ deps += [ get_label_info(dep, "label_no_toolchain") + "._gen" ]
+ }
+
+ if (defined(invoker.plugin_deps)) {
+ deps += invoker.plugin_deps
+ }
args = [
"--language",
invoker.language,
- "--include-path",
- rebase_path(invoker.include_path),
"--include-file",
- _output[0],
+ _includes[0],
+ "--compile-dir",
+ rebase_path(invoker.compile_dir),
"--out-dir",
- rebase_path(invoker.gen_dir),
+ rebase_path(_out_dir),
+ "--sources",
] + rebase_path(invoker.sources)
- inputs = invoker.sources
-
if (defined(invoker.plugin)) {
- inputs += [ invoker.plugin ]
+ inputs = [ invoker.plugin ]
args += [ "--plugin-path=" + rebase_path(invoker.plugin) ]
}
- outputs = []
- foreach(extension, invoker.output_extensions) {
- foreach(proto,
- rebase_path(invoker.sources,
- get_path_info(invoker.include_path, "abspath"))) {
- _output = string_replace(proto, ".proto", extension)
- outputs += [ "${invoker.gen_dir}/$_output" ]
- }
- }
-
- if (outputs == []) {
+ if (defined(invoker.outputs)) {
+ outputs = invoker.outputs
+ } else {
stamp = true
}
-
- visibility = [ ":*" ]
}
}
@@ -96,16 +105,20 @@
language = "pwpb"
plugin = "$dir_pw_protobuf/py/pw_protobuf/plugin.py"
python_deps = [ "$dir_pw_protobuf/py" ]
- output_extensions = [ ".pwpb.h" ]
}
# Create a library with the generated source files.
+ config("$target_name._include_path") {
+ include_dirs = [ "${invoker.base_out_dir}/pwpb" ]
+ visibility = [ ":*" ]
+ }
+
pw_source_set(target_name) {
forward_variables_from(invoker, _forwarded_vars)
- public_configs = [ ":${invoker.base_target}._include_path" ]
- deps = [ ":$target_name._gen" ]
+ public_configs = [ ":$target_name._include_path" ]
+ deps = [ ":$target_name._gen($default_toolchain)" ]
public_deps = [ dir_pw_protobuf ] + invoker.deps
- sources = get_target_outputs(":$target_name._gen")
+ sources = invoker.outputs
public = filter_include(sources, [ "*.pwpb.h" ])
}
}
@@ -121,21 +134,25 @@
language = "nanopb_rpc"
plugin = "$dir_pw_rpc/py/pw_rpc/plugin_nanopb.py"
python_deps = [ "$dir_pw_rpc/py" ]
- output_extensions = [ ".rpc.pb.h" ]
}
# Create a library with the generated source files.
+ config("$target_name._include_path") {
+ include_dirs = [ "${invoker.base_out_dir}/nanopb_rpc" ]
+ visibility = [ ":*" ]
+ }
+
pw_source_set(target_name) {
forward_variables_from(invoker, _forwarded_vars)
- public_configs = [ ":${invoker.base_target}._include_path" ]
- deps = [ ":$target_name._gen" ]
+ public_configs = [ ":$target_name._include_path" ]
+ deps = [ ":$target_name._gen($default_toolchain)" ]
public_deps = [
":${invoker.base_target}.nanopb",
"$dir_pw_rpc:server",
"$dir_pw_rpc/nanopb:method_union",
"$dir_pw_third_party/nanopb",
] + invoker.deps
- public = get_target_outputs(":$target_name._gen")
+ public = invoker.outputs
}
}
@@ -143,26 +160,39 @@
# files. This is internal and should not be used outside of this file. Use
# pw_proto_library instead.
template("_pw_nanopb_proto_library") {
- # Create a target which runs protoc configured with the nanopb plugin to
- # generate the C proto sources.
- _pw_invoke_protoc(target_name) {
- forward_variables_from(invoker, "*", _forwarded_vars)
- language = "nanopb"
- plugin = "$dir_pw_third_party_nanopb/generator/protoc-gen-nanopb"
- output_extensions = [
- ".pb.h",
- ".pb.c",
- ]
- }
+ # When compiling with the Nanopb plugin, the nanopb.proto file is already
+ # compiled internally, so skip recompiling it with protoc.
+ if (rebase_path(invoker.sources, invoker.compile_dir) == [ "nanopb.proto" ]) {
+ group("$target_name._gen") {
+ deps = [ ":${invoker.base_target}._sources" ]
+ }
- # Create a library with the generated source files.
- pw_source_set(target_name) {
- forward_variables_from(invoker, _forwarded_vars)
- public_configs = [ ":${invoker.base_target}._include_path" ]
- deps = [ ":$target_name._gen" ]
- public_deps = [ "$dir_pw_third_party/nanopb" ] + invoker.deps
- sources = get_target_outputs(":$target_name._gen")
- public = filter_include(sources, [ "*.pb.h" ])
+ group("$target_name") {
+ deps = invoker.deps + [ ":$target_name._gen($default_toolchain)" ]
+ }
+ } else {
+ # Create a target which runs protoc configured with the nanopb plugin to
+ # generate the C proto sources.
+ _pw_invoke_protoc(target_name) {
+ forward_variables_from(invoker, "*", _forwarded_vars)
+ language = "nanopb"
+ plugin = "$dir_pw_third_party_nanopb/generator/protoc-gen-nanopb"
+ }
+
+ # Create a library with the generated source files.
+ config("$target_name._include_path") {
+ include_dirs = [ "${invoker.base_out_dir}/nanopb" ]
+ visibility = [ ":*" ]
+ }
+
+ pw_source_set(target_name) {
+ forward_variables_from(invoker, _forwarded_vars)
+ public_configs = [ ":$target_name._include_path" ]
+ deps = [ ":$target_name._gen($default_toolchain)" ]
+ public_deps = [ "$dir_pw_third_party/nanopb" ] + invoker.deps
+ sources = invoker.outputs
+ public = filter_include(sources, [ "*.pb.h" ])
+ }
}
}
@@ -177,19 +207,23 @@
language = "raw_rpc"
plugin = "$dir_pw_rpc/py/pw_rpc/plugin_raw.py"
python_deps = [ "$dir_pw_rpc/py" ]
- output_extensions = [ ".raw_rpc.pb.h" ]
}
# Create a library with the generated source files.
+ config("$target_name._include_path") {
+ include_dirs = [ "${invoker.base_out_dir}/raw_rpc" ]
+ visibility = [ ":*" ]
+ }
+
pw_source_set(target_name) {
forward_variables_from(invoker, _forwarded_vars)
- public_configs = [ ":${invoker.base_target}._include_path" ]
- deps = [ ":$target_name._gen" ]
+ public_configs = [ ":$target_name._include_path" ]
+ deps = [ ":$target_name._gen($default_toolchain)" ]
public_deps = [
"$dir_pw_rpc:server",
"$dir_pw_rpc/raw:method_union",
] + invoker.deps
- public = get_target_outputs(":$target_name._gen")
+ public = invoker.outputs
}
}
@@ -208,12 +242,13 @@
"google.golang.org/grpc",
]
}
- output_extensions = [] # Don't enumerate the generated .go files.
- gen_dir = "$_proto_gopath/src"
+
+ # Override the default "$base_out_dir/$language" output path.
+ out_dir = "$_proto_gopath/src"
}
group(target_name) {
- deps = [ ":$target_name._gen" ]
+ deps = invoker.deps + [ ":$target_name._gen($default_toolchain)" ]
}
}
@@ -223,28 +258,13 @@
template("_pw_python_proto_library") {
_target = target_name
- # For standalone protos (e.g. `import "nanopb.proto"`), nest the proto file in
- # a directory with the same name for Python packaging purposes.
- if (invoker.standalone_proto) {
- _source_name = get_path_info(invoker.sources, "name")
- _proto_gen_dir = "${invoker.gen_dir}/${_source_name[0]}_pb2"
- } else {
- _proto_gen_dir = invoker.gen_dir
- }
-
_pw_invoke_protoc(target_name) {
forward_variables_from(invoker, "*", _forwarded_vars)
- gen_dir = _proto_gen_dir
language = "python"
- output_extensions = [
- "_pb2.py",
- "_pb2.pyi",
- ]
- deps += [ "$dir_pw_protobuf_compiler:protobuf_requirements.install" ]
+ plugin_deps = [ "$dir_pw_protobuf_compiler:protobuf_requirements.install" ]
}
- _setup_py = "${invoker.gen_dir}/setup.py"
- _generated_files = get_target_outputs(":$target_name._gen")
+ _setup_py = "${invoker.base_out_dir}/python/setup.py"
# Create the setup and init files for the Python package.
action(target_name + "._package_gen") {
@@ -254,13 +274,13 @@
rebase_path(_setup_py),
"--package",
invoker._package_dir,
- ] + rebase_path(_generated_files, invoker.gen_dir)
+ ] + rebase_path(invoker.outputs, "${invoker.base_out_dir}/python")
- if (invoker.standalone_proto) {
- args += [ "--standalone" ]
+ if (invoker.module_as_package != "") {
+ args += [ "--module-as-package" ]
}
- public_deps = [ ":$_target._gen" ]
+ public_deps = [ ":$_target._gen($default_toolchain)" ]
outputs = [ _setup_py ]
}
@@ -268,9 +288,9 @@
pw_python_package(target_name) {
forward_variables_from(invoker, _forwarded_vars)
setup = [ _setup_py ]
- sources = get_target_outputs(":$target_name._gen")
+ sources = invoker.outputs
python_deps = invoker.deps
- other_deps = [ ":$_target._package_gen" ]
+ other_deps = [ ":$_target._package_gen($default_toolchain)" ]
_pw_generated = true
}
}
@@ -280,74 +300,117 @@
#
# <target_name>.<generator>
#
+# pw_protobuf_library targets generate Python packages. As such, they must have
+# globally unique package names. The first directory of the prefix or the first
+# common directory of the sources is used as the Python package.
+#
# 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).
-# include_path: Sets the proto include path. The default is ".". It is not
-# recommended to set this, unless pulling in an externally defined proto.
+# 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).
+# prefix: A prefix to add to the source protos prior to compilation. For
+# example, a source called "foo.proto" with prefix = "nested" will be
+# compiled with protoc as "nested/foo.proto".
+# strip_prefix: Remove this prefix from the source protos. All source and
+# input files must be nested under this path.
#
template("pw_proto_library") {
assert(defined(invoker.sources) && invoker.sources != [],
"pw_proto_library requires .proto source files")
+ if (defined(invoker.python_module_as_package)) {
+ _module_as_package = invoker.python_module_as_package
+
+ _must_be_one_source = invoker.sources
+ assert([ _must_be_one_source[0] ] == _must_be_one_source,
+ "'python_module_as_package' requires exactly one source file")
+ assert(_module_as_package != "",
+ "'python_module_as_package' cannot be be empty")
+ assert(string_split(_module_as_package, "/") == [ _module_as_package ],
+ "'python_module_as_package' cannot contain slashes")
+ assert(!defined(invoker.prefix),
+ "'prefix' cannot be provided with 'python_module_as_package'")
+ } else {
+ _module_as_package = ""
+ }
+
+ if (defined(invoker.strip_prefix)) {
+ _source_root = get_path_info(invoker.strip_prefix, "abspath")
+ } else {
+ _source_root = get_path_info(".", "abspath")
+ }
+
+ if (defined(invoker.prefix)) {
+ _prefix = invoker.prefix
+ } else {
+ _prefix = ""
+ }
+
_common = {
base_target = target_name
- gen_dir = "$target_gen_dir/$target_name"
- sources = invoker.sources
- if (defined(invoker.include_path)) {
- include_path = invoker.include_path
- } else {
- include_path = "."
+ # This is the output directory for all files related to this proto library.
+ # Sources are mirrored to "$base_out_dir/sources" and protoc puts outputs in
+ # "$base_out_dir/$language" by default.
+ base_out_dir = get_label_info(":$target_name($default_toolchain)",
+ "target_gen_dir") + "/$target_name"
+
+ compile_dir = "$base_out_dir/sources"
+
+ # Refer to the source files as the are mirrored to the output directory.
+ sources = []
+ foreach(file, rebase_path(invoker.sources, _source_root)) {
+ sources += [ "$compile_dir/$_prefix/$file" ]
}
}
- _rebased_sources = rebase_path(invoker.sources, _common.include_path)
-
- # The pw_proto_library GN target requires protos to be nested under the
- # include directory unless three conditions are met:
- #
- # 1. There is only one .proto file.
- # 2. The file is in a different directory (an externally defined proto).
- # 3. The include path is the .proto's include directory. Since there are no
- # nested directories, the proto cannot be packaged properly in Python.
- #
- # When these conditions are met, the proto library is allowed, even though the
- # proto file is not nested. The Python package for it uses the Python module's
- # name. This is a special exception to the typical pattern to allow for
- # working with single, external, standalone protobuf not set up for Python
- # packaging (such as nanopb.proto).
- _standalone_proto =
- _rebased_sources == [ _rebased_sources[0] ] &&
- _common.include_path != "." &&
- string_split(_rebased_sources[0], "/") == [ _rebased_sources[0] ]
-
_package_dir = ""
+ _source_names = []
- foreach(_rebased_source, _rebased_sources) {
+ # Determine the Python package name to use for these protos. If there is no
+ # prefix, the first directory the sources are nested under is used.
+ foreach(source, rebase_path(invoker.sources, _source_root)) {
_path_components = []
- _path_components = string_split(_rebased_source, "/")
-
- assert((_standalone_proto || _path_components != [ _rebased_source ]) &&
- _path_components[0] != "..",
- "Sources in a pw_proto_library must live in subdirectories " +
- "of where it is defined")
+ _path_components = string_split(source, "/")
if (_package_dir == "") {
_package_dir = _path_components[0]
} else {
- assert(_path_components[0] == _package_dir,
- "All .proto sources in a pw_proto_library must live in the same " +
- "directory tree")
+ assert(_prefix != "" || _path_components[0] == _package_dir,
+ "Unless 'prefix' is supplied, all .proto sources in a " +
+ "pw_proto_library must be in the same directory tree")
}
+
+ _source_names +=
+ [ get_path_info(source, "dir") + "/" + get_path_info(source, "name") ]
}
- # Create a group with the package directory in the name. This prevents
- # multiple pw_proto_libraries from generating the same setup.py file, which
- # results in awkward ninja errors that require manually re-running gn gen.
- group("pw_proto_library.$_package_dir") {
+ # If the 'prefix' was supplied, use that for the package directory.
+ if (_prefix != "") {
+ _prefix_path_components = string_split(_prefix, "/")
+ _package_dir = _prefix_path_components[0]
+ }
+
+ assert(_package_dir != "" && _package_dir != "." && _package_dir != "..",
+ "Either a 'prefix' must be specified or all sources must be nested " +
+ "under a common directory")
+
+ # Define an action that is never executed to prevent duplicate proto packages
+ # from being declared. The target name and the output file include only the
+ # package directory, so different targets that use the same proto package name
+ # will conflict.
+ action("pw_proto_library.$_package_dir") {
+ script = "$dir_pw_build/py/pw_build/nop.py"
visibility = []
+
+ # Place an error message in the output path (which is never created). If the
+ # package name conflicts occur in different BUILD.gn files, this results in
+ # an otherwise cryptic Ninja error, rather than a GN error.
+ outputs = [ "$root_out_dir/ " +
+ "ERROR - Multiple pw_proto_library targets create the " +
+ "'$_package_dir' package. Change the package name by setting " +
+ "the \"prefix\" arg or move the protos to a different " +
+ "directory, then re-run gn gen." ]
}
if (defined(invoker.deps)) {
@@ -358,35 +421,29 @@
# For each proto target, create a file which collects the base directories of
# all of its dependencies to list as include paths to protoc.
- generated_file("$target_name._metadata") {
+ generated_file("$target_name._includes") {
# Collect metadata from the include path files of each dependency.
- deps = process_file_template(_deps, "{{source}}._metadata")
+ deps = process_file_template(_deps, "{{source}}._includes")
data_keys = [ "protoc_includes" ]
- outputs = [ "$target_gen_dir/${_common.base_target}_includes.txt" ]
+ outputs = [ "$target_gen_dir/${_common.base_target}/includes.txt" ]
# Indicate this library's base directory for its dependents.
metadata = {
- protoc_includes = [ rebase_path(_common.include_path) ]
+ protoc_includes = [ rebase_path(_common.compile_dir) ]
}
}
- # Toss any additional inputs into an input group dependency.
- if (defined(invoker.inputs)) {
- pw_input_group("$target_name._inputs") {
- inputs = invoker.inputs
- visibility = [ ":*" ]
- }
- } else {
- group("$target_name._inputs") {
- visibility = [ ":*" ]
- }
- }
+ # Mirror the proto sources to the output directory with the prefix added.
+ pw_mirror_tree("$target_name._sources") {
+ source_root = _source_root
+ sources = invoker.sources
- # Create a config with the generated proto directory, which is used for C++.
- config("$target_name._include_path") {
- include_dirs = [ _common.gen_dir ]
- visibility = [ ":*" ]
+ if (defined(invoker.inputs)) {
+ sources += invoker.inputs
+ }
+
+ directory = "${_common.compile_dir}/$_prefix"
}
# Enumerate all of the protobuf generator targets.
@@ -394,28 +451,52 @@
_pw_pwpb_proto_library("$target_name.pwpb") {
forward_variables_from(invoker, _forwarded_vars)
forward_variables_from(_common, "*")
- deps = process_file_template(_deps, "{{source}}.pwpb")
+
+ deps = []
+ foreach(dep, _deps) {
+ _base = get_label_info(dep, "label_no_toolchain")
+ deps += [ "$_base.pwpb(" + get_label_info(dep, "toolchain") + ")" ]
+ }
+
+ outputs = []
+ foreach(name, _source_names) {
+ outputs += [ "$base_out_dir/pwpb/$_prefix/${name}.pwpb.h" ]
+ }
}
if (dir_pw_third_party_nanopb != "") {
_pw_nanopb_rpc_proto_library("$target_name.nanopb_rpc") {
forward_variables_from(invoker, _forwarded_vars)
forward_variables_from(_common, "*")
- deps = process_file_template(_deps, "{{source}}.nanopb_rpc")
+
+ deps = []
+ foreach(dep, _deps) {
+ _lbl = get_label_info(dep, "label_no_toolchain")
+ deps += [ "$_lbl.nanopb_rpc(" + get_label_info(dep, "toolchain") + ")" ]
+ }
+
+ outputs = []
+ foreach(name, _source_names) {
+ outputs += [ "$base_out_dir/nanopb_rpc/$_prefix/${name}.rpc.pb.h" ]
+ }
}
- # When compiling with the Nanopb plugin, the nanopb.proto file is already
- # compiled internally, so skip recompiling it here.
- if (invoker.sources ==
- [ "$dir_pw_third_party_nanopb/generator/proto/nanopb.proto" ]) {
- pw_input_group("$target_name.nanopb") {
- sources = invoker.sources
+ _pw_nanopb_proto_library("$target_name.nanopb") {
+ forward_variables_from(invoker, _forwarded_vars)
+ forward_variables_from(_common, "*")
+
+ deps = []
+ foreach(dep, _deps) {
+ _base = get_label_info(dep, "label_no_toolchain")
+ deps += [ "$_base.nanopb(" + get_label_info(dep, "toolchain") + ")" ]
}
- } else {
- _pw_nanopb_proto_library("$target_name.nanopb") {
- forward_variables_from(invoker, _forwarded_vars)
- forward_variables_from(_common, "*")
- deps = process_file_template(_deps, "{{source}}.nanopb")
+
+ outputs = []
+ foreach(name, _source_names) {
+ outputs += [
+ "$base_out_dir/nanopb/$_prefix/${name}.pb.h",
+ "$base_out_dir/nanopb/$_prefix/${name}.pb.c",
+ ]
}
}
} else {
@@ -432,23 +513,55 @@
_pw_raw_rpc_proto_library("$target_name.raw_rpc") {
forward_variables_from(invoker, _forwarded_vars)
- forward_variables_from(_common, "*", [ "deps" ])
- deps = process_file_template(_deps, "{{source}}.raw_rpc")
+ forward_variables_from(_common, "*")
+
+ deps = []
+ foreach(dep, _deps) {
+ _base = get_label_info(dep, "label_no_toolchain")
+ deps += [ "$_base.raw_rpc(" + get_label_info(dep, "toolchain") + ")" ]
+ }
+
+ outputs = []
+ foreach(name, _source_names) {
+ outputs += [ "$base_out_dir/raw_rpc/$_prefix/${name}.raw_rpc.pb.h" ]
+ }
}
_pw_go_proto_library("$target_name.go") {
- sources = invoker.sources
- deps = process_file_template(_deps, "{{source}}.go")
- base_target = _common.base_target
- include_path = _common.include_path
+ sources = _common.sources
+
+ deps = []
+ foreach(dep, _deps) {
+ _base = get_label_info(dep, "label_no_toolchain")
+ deps += [ "$_base.go(" + get_label_info(dep, "toolchain") + ")" ]
+ }
+
+ forward_variables_from(_common, "*")
}
_pw_python_proto_library("$target_name.python") {
- sources = invoker.sources
forward_variables_from(_common, "*")
- deps = process_file_template(_deps, "{{source}}.python")
- base_target = _common.base_target
- standalone_proto = _standalone_proto
+ module_as_package = _module_as_package
+
+ deps = []
+ foreach(dep, _deps) {
+ _base = get_label_info(dep, "label_no_toolchain")
+ deps += [ "$_base.python(" + get_label_info(dep, "toolchain") + ")" ]
+ }
+
+ if (module_as_package == "") {
+ _python_prefix = "$base_out_dir/python/$_prefix"
+ } else {
+ _python_prefix = "$base_out_dir/python/$module_as_package"
+ }
+
+ outputs = []
+ foreach(name, _source_names) {
+ outputs += [
+ "$_python_prefix/${name}_pb2.py",
+ "$_python_prefix/${name}_pb2.pyi",
+ ]
+ }
}
# All supported pw_protobuf generators.
diff --git a/pw_protobuf_compiler/py/pw_protobuf_compiler/generate_protos.py b/pw_protobuf_compiler/py/pw_protobuf_compiler/generate_protos.py
index 5db2f2c..10c0bb2 100644
--- a/pw_protobuf_compiler/py/pw_protobuf_compiler/generate_protos.py
+++ b/pw_protobuf_compiler/py/pw_protobuf_compiler/generate_protos.py
@@ -20,8 +20,7 @@
import subprocess
import sys
import tempfile
-
-from typing import Callable, Dict, Optional, Tuple
+from typing import Callable, Dict, Optional, Tuple, Union
# Make sure dependencies are optional, since this script may be run when
# installing Python package dependencies through GN.
@@ -35,13 +34,10 @@
_COMMON_FLAGS = ('--experimental_allow_proto3_optional', )
-def argument_parser(
- parser: Optional[argparse.ArgumentParser] = None
-) -> argparse.ArgumentParser:
+def _argument_parser() -> argparse.ArgumentParser:
"""Registers the script's arguments on an argument parser."""
- if parser is None:
- parser = argparse.ArgumentParser(description=__doc__)
+ parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--language',
required=True,
@@ -50,17 +46,19 @@
parser.add_argument('--plugin-path',
type=Path,
help='Path to the protoc plugin')
- parser.add_argument('--include-path',
- required=True,
- help='Include path for proto compilation')
parser.add_argument('--include-file',
type=argparse.FileType('r'),
help='File containing additional protoc include paths')
parser.add_argument('--out-dir',
+ type=Path,
required=True,
help='Output directory for generated code')
- parser.add_argument('protos',
- metavar='PROTO',
+ parser.add_argument('--compile-dir',
+ type=Path,
+ required=True,
+ help='Root path for compilation')
+ parser.add_argument('--sources',
+ type=Path,
nargs='+',
help='Input protobuf files')
@@ -89,8 +87,10 @@
'--plugin',
f'protoc-gen-nanopb={args.plugin_path}',
# nanopb_opt provides the flags to use for nanopb_out. Windows doesn't
- # like when you merge the two using the `flag,...:out` syntax.
- f'--nanopb_opt=-I{args.include_path}',
+ # like when you merge the two using the `flag,...:out` syntax. Use
+ # Posix-style paths since backslashes on Windows are treated like
+ # escape characters.
+ f'--nanopb_opt=-I{args.compile_dir.as_posix()}',
f'--nanopb_out={args.out_dir}',
)
@@ -142,14 +142,14 @@
def main() -> int:
"""Runs protoc as configured by command-line arguments."""
- parser = argument_parser()
+ parser = _argument_parser()
args = parser.parse_args()
if args.plugin_path is None and args.language not in BUILTIN_PROTOC_LANGS:
parser.error(
f'--plugin-path is required for --language {args.language}')
- os.makedirs(args.out_dir, exist_ok=True)
+ args.out_dir.mkdir(parents=True, exist_ok=True)
include_paths = [f'-I{line.strip()}' for line in args.include_file]
@@ -169,23 +169,25 @@
args.plugin_path = wrapper_script = Path(file.name)
_LOG.debug('Using generated plugin wrapper %s', args.plugin_path)
+ cmd: Tuple[Union[str, Path], ...] = (
+ 'protoc',
+ f'-I{args.compile_dir}',
+ *include_paths,
+ *DEFAULT_PROTOC_ARGS[args.language](args),
+ *args.sources,
+ )
+
try:
- process = subprocess.run(
- [
- 'protoc',
- f'-I{args.include_path}',
- *include_paths,
- *DEFAULT_PROTOC_ARGS[args.language](args),
- *args.protos,
- ],
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT,
- )
+ process = subprocess.run(cmd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
finally:
if wrapper_script:
wrapper_script.unlink()
if process.returncode != 0:
+ _LOG.error('Protocol buffer compilation failed!\n%s',
+ ' '.join(str(c) for c in cmd))
sys.stderr.buffer.write(process.stdout)
sys.stderr.flush()
diff --git a/pw_protobuf_compiler/py/pw_protobuf_compiler/generate_python_package.py b/pw_protobuf_compiler/py/pw_protobuf_compiler/generate_python_package.py
index b79d770..9147f08 100644
--- a/pw_protobuf_compiler/py/pw_protobuf_compiler/generate_python_package.py
+++ b/pw_protobuf_compiler/py/pw_protobuf_compiler/generate_python_package.py
@@ -54,7 +54,7 @@
required=True,
type=Path,
help='Path to setup.py file')
- parser.add_argument('--standalone',
+ parser.add_argument('--module-as-package',
action='store_true',
help='The package is a standalone external proto')
parser.add_argument('sources',
@@ -64,10 +64,10 @@
return parser.parse_args()
-def main(package: str, setup: Path, standalone: bool,
+def main(package: str, setup: Path, module_as_package: bool,
sources: List[Path]) -> int:
"""Generates __init__.py and py.typed files and a setup.py."""
- assert not standalone or len(sources) == 2
+ assert not module_as_package or len(sources) == 2
base = setup.parent.resolve()
base.mkdir(exist_ok=True)
@@ -95,7 +95,7 @@
package_name = pkg.relative_to(base).as_posix().replace('/', '.')
pkg_data[package_name].append(mypy_stub.name)
- if standalone:
+ if module_as_package:
pkg.joinpath('__init__.py').write_text(
f'from {mypy_stub.stem}.{mypy_stub.stem} import *\n')
diff --git a/pw_rpc/BUILD.gn b/pw_rpc/BUILD.gn
index 06ba545..0424f1e 100644
--- a/pw_rpc/BUILD.gn
+++ b/pw_rpc/BUILD.gn
@@ -134,17 +134,18 @@
pw_proto_library("protos") {
sources = [
- "pw_rpc_protos/echo.proto",
- "pw_rpc_protos/internal/packet.proto",
+ "echo.proto",
+ "internal/packet.proto",
]
- inputs = [ "pw_rpc_protos/echo.options" ]
+ inputs = [ "echo.options" ]
+ prefix = "pw_rpc_protos"
}
pw_doc_group("docs") {
sources = [ "docs.rst" ]
inputs = [
- "pw_rpc_protos/echo.proto",
- "pw_rpc_protos/internal/packet.proto",
+ "echo.proto",
+ "internal/packet.proto",
]
group_deps = [
"nanopb:docs",
diff --git a/pw_rpc/CMakeLists.txt b/pw_rpc/CMakeLists.txt
index cba62ca..a832b07 100644
--- a/pw_rpc/CMakeLists.txt
+++ b/pw_rpc/CMakeLists.txt
@@ -69,8 +69,12 @@
pw_proto_library(pw_rpc.protos
SOURCES
- pw_rpc_protos/internal/packet.proto
- pw_rpc_protos/echo.proto
+ internal/packet.proto
+ echo.proto
+ INPUTS
+ echo.options
+ PREFIX
+ pw_rpc_protos
)
pw_proto_library(pw_rpc.test_protos
diff --git a/pw_rpc/docs.rst b/pw_rpc/docs.rst
index 4f76398..9ec4c77 100644
--- a/pw_rpc/docs.rst
+++ b/pw_rpc/docs.rst
@@ -246,9 +246,9 @@
============================
After setting up a ``pw_rpc`` server in your project, you can test that it is
working as intended by registering the provided ``EchoService``, defined in
-``pw_rpc_protos/echo.proto``, which echoes back a message that it receives.
+``echo.proto``, which echoes back a message that it receives.
-.. literalinclude:: pw_rpc_protos/echo.proto
+.. literalinclude:: echo.proto
:language: protobuf
:lines: 14-
@@ -282,7 +282,7 @@
encoded as protocol buffers. The full packet format is described in
``pw_rpc/pw_rpc_protos/internal/packet.proto``.
-.. literalinclude:: pw_rpc_protos/internal/packet.proto
+.. literalinclude:: internal/packet.proto
:language: protobuf
:lines: 14-
diff --git a/pw_rpc/pw_rpc_protos/echo.options b/pw_rpc/echo.options
similarity index 100%
rename from pw_rpc/pw_rpc_protos/echo.options
rename to pw_rpc/echo.options
diff --git a/pw_rpc/pw_rpc_protos/echo.proto b/pw_rpc/echo.proto
similarity index 100%
rename from pw_rpc/pw_rpc_protos/echo.proto
rename to pw_rpc/echo.proto
diff --git a/pw_rpc/pw_rpc_protos/internal/packet.proto b/pw_rpc/internal/packet.proto
similarity index 100%
rename from pw_rpc/pw_rpc_protos/internal/packet.proto
rename to pw_rpc/internal/packet.proto
diff --git a/pw_trace_tokenized/BUILD.gn b/pw_trace_tokenized/BUILD.gn
index 8468e8a..86403f8 100644
--- a/pw_trace_tokenized/BUILD.gn
+++ b/pw_trace_tokenized/BUILD.gn
@@ -64,7 +64,7 @@
]
public_deps = [
":config",
- ":pw_trace_tokenized_core",
+ ":core",
"$dir_pw_tokenizer",
]
if (pw_trace_tokenizer_time != "") {
@@ -77,7 +77,7 @@
pw_test("trace_tokenized_test") {
enable_if = pw_trace_tokenizer_time != ""
deps = [
- ":pw_trace_tokenized_core",
+ ":core",
"$dir_pw_trace",
]
@@ -97,6 +97,7 @@
public_configs = [ ":public_include_path" ]
public_deps = [ ":trace_rpc_service_proto.nanopb_rpc" ]
deps = [
+ ":core",
":tokenized_trace_buffer",
"$dir_pw_log",
"$dir_pw_trace",
@@ -108,7 +109,7 @@
}
pw_source_set("tokenized_trace_buffer") {
- deps = [ ":pw_trace_tokenized_core" ]
+ deps = [ ":core" ]
public_deps = [
":config",
"$dir_pw_ring_buffer",
@@ -154,16 +155,16 @@
}
pw_source_set("fake_trace_time") {
- deps = [ ":pw_trace_tokenized_core" ]
+ deps = [ ":core" ]
sources = [ "fake_trace_time.cc" ]
}
pw_source_set("host_trace_time") {
- deps = [ ":pw_trace_tokenized_core" ]
+ deps = [ ":core" ]
sources = [ "host_trace_time.cc" ]
}
-pw_source_set("pw_trace_tokenized_core") {
+pw_source_set("core") {
public_configs = [
":backend_config",
":public_include_path",
@@ -185,6 +186,7 @@
"public/pw_trace_tokenized/trace_tokenized.h",
]
sources = [ "trace.cc" ]
+ visibility = [ ":*" ]
}
pw_doc_group("docs") {
diff --git a/third_party/nanopb/BUILD.gn b/third_party/nanopb/BUILD.gn
index 695ce18..77e2453 100644
--- a/third_party/nanopb/BUILD.gn
+++ b/third_party/nanopb/BUILD.gn
@@ -43,8 +43,9 @@
}
pw_proto_library("proto") {
- include_path = "$dir_pw_third_party_nanopb/generator/proto"
+ strip_prefix = "$dir_pw_third_party_nanopb/generator/proto"
sources = [ "$dir_pw_third_party_nanopb/generator/proto/nanopb.proto" ]
+ python_module_as_package = "nanopb_pb2"
}
} else {
group("nanopb") {