Merge pull request #500 from silvergasp/master

Add bazel rules for nanopb generation
diff --git a/.github/workflows/bazel.yml b/.github/workflows/bazel.yml
new file mode 100644
index 0000000..3f7bd19
--- /dev/null
+++ b/.github/workflows/bazel.yml
@@ -0,0 +1,31 @@
+name: Bazel 
+
+on:
+  push:
+  pull_request:
+    branches:
+      - master
+
+jobs:
+  build_embedded:
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ubuntu-20.04, macos-10.15]
+
+    steps:
+      - uses: actions/checkout@v2
+
+      - name: Mount bazel cache
+        uses: actions/cache@v2
+        with:
+          path: "/home/runner/.cache/bazel"
+          key: ${{ runner.os }}-bazel
+
+      - name: Test
+        run: |
+          bazelisk test //...
+
+      - name: Build
+        run: |
+          bazelisk build //...
diff --git a/BUILD.bazel b/BUILD.bazel
index becdc57..a05cdf4 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -1,9 +1,16 @@
+load("@rules_python//python:defs.bzl", "py_binary")
+load("@nanopb_pypi//:requirements.bzl", "requirement")
+load("@rules_proto_grpc//:defs.bzl", "proto_plugin")
+load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
+load("//extra/bazel:nanopb_cc_proto_library.bzl", "cc_nanopb_proto_library")
+load("@rules_python//python/pip_install:requirements.bzl", "compile_pip_requirements")
+
+package(default_visibility = ["//visibility:public"])
+
 licenses(["notice"])
 
 exports_files(["LICENSE.txt"])
 
-package(default_visibility = ["//visibility:public"])
-
 cc_library(
     name = "nanopb",
     srcs = [
@@ -19,3 +26,56 @@
     ],
     visibility = ["//visibility:public"],
 )
+
+copy_file(
+    name = "protoc-gen-nanopb.py",
+    src = "generator/protoc-gen-nanopb",
+    out = "generator/protoc-gen-nanopb.py",
+    allow_symlink = True,
+)
+
+py_binary(
+    name = "protoc-gen-nanopb",
+    srcs = [
+        ":protoc-gen-nanopb.py",
+        "generator/nanopb_generator.py",
+    ],
+    deps = [
+        requirement("grpcio-tools"),
+    ],
+)
+
+proto_plugin(
+    name = "nanopb_plugin",
+    options = [
+        "--library-include-format='#include\"%s\"'",
+    ],
+    outputs = [
+        "{protopath}.pb.h",
+        "{protopath}.pb.c",
+    ],
+    separate_options_flag = True,
+    tool = ":protoc-gen-nanopb",
+    visibility = ["//visibility:public"],
+)
+
+proto_library(
+    name = "descriptor",
+    srcs = [
+        "generator/proto/google/protobuf/descriptor.proto",
+    ],
+)
+
+cc_nanopb_proto_library(
+    name = "test_compilation",
+    protos = [":descriptor"],
+    visibility = ["//visibility:private"],
+)
+
+# Run `bazel run //:requirements.update` if you want to update the requirements.
+compile_pip_requirements(
+    name = "requirements",
+    extra_args = ["--allow-unsafe"],
+    requirements_in = "extra/requirements.txt",
+    requirements_txt = "extra/requirements_lock.txt",
+)
diff --git a/WORKSPACE b/WORKSPACE
index e23271b..899ab50 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1 +1,22 @@
 workspace(name = "com_github_nanopb_nanopb")
+
+load("//extra/bazel:nanopb_deps.bzl", "nanopb_deps")
+
+nanopb_deps()
+
+load("@rules_python//python:repositories.bzl", "python_register_toolchains")
+
+python_register_toolchains(
+    name = "python3_9",
+    python_version = "3.9",
+)
+
+load("//extra/bazel:python_deps.bzl", "nanopb_python_deps")
+
+load("@python3_9//:defs.bzl", "interpreter")
+
+nanopb_python_deps(interpreter)
+
+load("//extra/bazel:nanopb_workspace.bzl", "nanopb_workspace")
+
+nanopb_workspace()
diff --git a/docs/bazel_build.md b/docs/bazel_build.md
new file mode 100644
index 0000000..dc08a8f
--- /dev/null
+++ b/docs/bazel_build.md
@@ -0,0 +1,61 @@
+# Nanopb: Bazel build
+The Bazel build system, is designed to be fast and correct. Nanopb provides a
+set of plugins for the Bazel build system allowing Nanopb to be integrated 
+into the build.
+
+## Getting started
+Add the following to your WORKSPACE file.
+``` py 
+# WORKSPACE
+git_repository(
+    name = "com_github_nanopb_nanopb",
+    remote = "https://github.com/nanopb/nanopb.git"
+    commit = "<TODO:Enter your desired commit>",
+)
+
+load("@com_github_nanopb_nanopb//extra/bazel:nanopb_deps.bzl", "nanopb_deps")
+
+nanopb_deps()
+
+load("@com_github_nanopb_nanopb//extra/bazel:python_deps.bzl", 
+    "nanopb_python_deps")
+
+nanopb_python_deps()
+
+load("@com_github_nanopb_nanopb//extra/bazel:nanopb_workspace.bzl", 
+    "nanopb_workspace")
+
+nanopb_workspace()
+```
+
+To use the Nanopb rules with in your build you can use the 
+`cc_nanopb_proto_library` which works in a similar way to the native
+`cc_proto_library` rule.
+```  py
+# BUILD.bazel
+load("@com_github_nanopb_nanopb//extra/bazel:nanopb_cc_proto_library.bzl", 
+    "nanopb_cc_proto_library")
+
+# Your native proto_library.
+proto_library(
+    name = "descriptor",
+    srcs = [
+        "generator/proto/google/protobuf/descriptor.proto",
+    ],
+)
+
+# Generated library.
+cc_nanopb_proto_library(
+    name = "descriptor_nanopb",
+    protos = [":descriptor"],
+    visibility = ["//visibility:private"],
+)
+
+# Depend directly on the generated code using a cc_library.
+cc_library(
+    name = "uses_generated_descriptors",
+    deps = [":descriptor_nanopb"],
+    hdrs = ["my_header.h"],
+)
+```
+
diff --git a/extra/bazel/BUILD.bazel b/extra/bazel/BUILD.bazel
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/extra/bazel/BUILD.bazel
diff --git a/extra/bazel/nanopb_cc_proto_library.bzl b/extra/bazel/nanopb_cc_proto_library.bzl
new file mode 100644
index 0000000..478ab58
--- /dev/null
+++ b/extra/bazel/nanopb_cc_proto_library.bzl
@@ -0,0 +1,76 @@
+# Apache License, Version 2.0, January 2004, http://www.apache.org/licenses/
+# Adapted from: https://github.com/rules-proto-grpc/rules_proto_grpc/
+
+load("@rules_proto_grpc//internal:filter_files.bzl", "filter_files")
+load("@rules_cc//cc:defs.bzl", "cc_library")
+load(
+    "@rules_proto_grpc//:defs.bzl",
+    "ProtoPluginInfo",
+    "proto_compile_attrs",
+    "proto_compile_impl",
+)
+
+# Create compile rule
+cc_nanopb_proto_compile = rule(
+    implementation = proto_compile_impl,
+    attrs = dict(
+        proto_compile_attrs,
+        _plugins = attr.label_list(
+            providers = [ProtoPluginInfo],
+            default = [
+                Label("@com_github_nanopb_nanopb//:nanopb_plugin"),
+            ],
+            doc = "List of protoc plugins to apply",
+        ),
+    ),
+    toolchains = [str(Label("@rules_proto_grpc//protobuf:toolchain_type"))],
+)
+
+def cc_nanopb_proto_library(name, **kwargs):  # buildifier: disable=function-docstring
+    # Compile protos
+    name_pb = name + "_pb"
+    cc_nanopb_proto_compile(
+        name = name_pb,
+        **{
+            k: v
+            for (k, v) in kwargs.items()
+            if k in proto_compile_attrs.keys()
+        }  # Forward args
+    )
+
+    # Filter files to sources and headers
+    filter_files(
+        name = name_pb + "_srcs",
+        target = name_pb,
+        extensions = ["c"],
+    )
+
+    filter_files(
+        name = name_pb + "_hdrs",
+        target = name_pb,
+        extensions = ["h"],
+    )
+
+    # Create c library
+    cc_library(
+        name = name,
+        srcs = [name_pb + "_srcs"],
+        deps = PROTO_DEPS + kwargs.get("deps", []),
+        hdrs = [name_pb + "_hdrs"],
+        includes = [name_pb],
+        alwayslink = kwargs.get("alwayslink"),
+        copts = kwargs.get("copts"),
+        defines = kwargs.get("defines"),
+        include_prefix = kwargs.get("include_prefix"),
+        linkopts = kwargs.get("linkopts"),
+        linkstatic = kwargs.get("linkstatic"),
+        local_defines = kwargs.get("local_defines"),
+        nocopts = kwargs.get("nocopts"),
+        strip_include_prefix = kwargs.get("strip_include_prefix"),
+        visibility = kwargs.get("visibility"),
+        tags = kwargs.get("tags"),
+    )
+
+PROTO_DEPS = [
+    "@com_github_nanopb_nanopb//:nanopb",
+]
diff --git a/extra/bazel/nanopb_deps.bzl b/extra/bazel/nanopb_deps.bzl
new file mode 100644
index 0000000..aaed90c
--- /dev/null
+++ b/extra/bazel/nanopb_deps.bzl
@@ -0,0 +1,38 @@
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+def nanopb_deps():
+    # Setup proto rules.
+    # Used by: com_github_nanopb_nanopb, rules_proto_grpc.
+    # Used in modules: root.
+    if "rules_proto" not in native.existing_rules():
+        http_archive(
+            name = "rules_proto",
+            sha256 = "66bfdf8782796239d3875d37e7de19b1d94301e8972b3cbd2446b332429b4df1",
+            strip_prefix = "rules_proto-4.0.0",
+            urls = [
+                "https://mirror.bazel.build/github.com/bazelbuild/rules_proto/archive/refs/tags/4.0.0.tar.gz",
+                "https://github.com/bazelbuild/rules_proto/archive/refs/tags/4.0.0.tar.gz",
+            ],
+        )
+
+    # Required for plugin rules.
+    # Used by: com_github_nanopb_nanopb.
+    # Used in modules: generator.
+    if "rules_python" not in native.existing_rules():
+        http_archive(
+            name = "rules_python",
+            sha256 = "5fa3c738d33acca3b97622a13a741129f67ef43f5fdfcec63b29374cc0574c29",
+            strip_prefix = "rules_python-0.9.0",
+            url = "https://github.com/bazelbuild/rules_python/archive/refs/tags/0.9.0.tar.gz",
+        )
+
+    # Setup grpc tools.
+    # Used by: com_github_nanopb_nanopb.
+    # Used in modules: generator/proto.
+    if "rules_proto_grpc" not in native.existing_rules():
+        http_archive(
+            name = "rules_proto_grpc",
+            sha256 = "507e38c8d95c7efa4f3b1c0595a8e8f139c885cb41a76cab7e20e4e67ae87731",
+            strip_prefix = "rules_proto_grpc-4.1.1",
+            urls = ["https://github.com/rules-proto-grpc/rules_proto_grpc/archive/4.1.1.tar.gz"],
+        )
diff --git a/extra/bazel/nanopb_workspace.bzl b/extra/bazel/nanopb_workspace.bzl
new file mode 100644
index 0000000..1955607
--- /dev/null
+++ b/extra/bazel/nanopb_workspace.bzl
@@ -0,0 +1,10 @@
+load("@nanopb_pypi//:requirements.bzl", "install_deps")
+load("@rules_proto_grpc//:repositories.bzl", "rules_proto_grpc_repos", "rules_proto_grpc_toolchains")
+load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains")
+
+def nanopb_workspace():
+    install_deps()
+    rules_proto_grpc_toolchains()
+    rules_proto_grpc_repos()
+    rules_proto_dependencies()
+    rules_proto_toolchains()
diff --git a/extra/bazel/python_deps.bzl b/extra/bazel/python_deps.bzl
new file mode 100644
index 0000000..2327e26
--- /dev/null
+++ b/extra/bazel/python_deps.bzl
@@ -0,0 +1,12 @@
+load("@rules_python//python:pip.bzl", "pip_parse")
+
+def nanopb_python_deps(interpreter=None):
+    # Required for python deps for generator plugin.
+    # Used by: com_github_nanopb_nanopb.
+    # Used in modules: generator.
+    if "nanopb_pypi" not in native.existing_rules():
+        pip_parse(
+            name = "nanopb_pypi",
+            requirements_lock = "@com_github_nanopb_nanopb//:extra/requirements_lock.txt",
+            python_interpreter_target = interpreter,
+        )
diff --git a/extra/requirements.txt b/extra/requirements.txt
new file mode 100644
index 0000000..e3266f9
--- /dev/null
+++ b/extra/requirements.txt
@@ -0,0 +1 @@
+grpcio-tools==1.47.0
\ No newline at end of file
diff --git a/extra/requirements_lock.txt b/extra/requirements_lock.txt
new file mode 100644
index 0000000..acecdc4
--- /dev/null
+++ b/extra/requirements_lock.txt
@@ -0,0 +1,138 @@
+#
+# This file is autogenerated by pip-compile with python 3.9
+# To update, run:
+#
+#    bazel run //:requirements.update
+#
+grpcio==1.47.0 \
+    --hash=sha256:0425b5577be202d0a4024536bbccb1b052c47e0766096e6c3a5789ddfd5f400d \
+    --hash=sha256:06c0739dff9e723bca28ec22301f3711d85c2e652d1c8ae938aa0f7ad632ef9a \
+    --hash=sha256:08307dc5a6ac4da03146d6c00f62319e0665b01c6ffe805cfcaa955c17253f9c \
+    --hash=sha256:090dfa19f41efcbe760ae59b34da4304d4be9a59960c9682b7eab7e0b6748a79 \
+    --hash=sha256:0a24b50810aae90c74bbd901c3f175b9645802d2fbf03eadaf418ddee4c26668 \
+    --hash=sha256:0cd44d78f302ff67f11a8c49b786c7ccbed2cfef6f4fd7bb0c3dc9255415f8f7 \
+    --hash=sha256:0d8a7f3eb6f290189f48223a5f4464c99619a9de34200ce80d5092fb268323d2 \
+    --hash=sha256:14d2bc74218986e5edf5527e870b0969d63601911994ebf0dce96288548cf0ef \
+    --hash=sha256:1bb9afa85e797a646bfcd785309e869e80a375c959b11a17c9680abebacc0cb0 \
+    --hash=sha256:1ec63bbd09586e5cda1bdc832ae6975d2526d04433a764a1cc866caa399e50d4 \
+    --hash=sha256:2061dbe41e43b0a5e1fd423e8a7fb3a0cf11d69ce22d0fac21f1a8c704640b12 \
+    --hash=sha256:324e363bad4d89a8ec7124013371f268d43afd0ac0fdeec1b21c1a101eb7dafb \
+    --hash=sha256:35dfd981b03a3ec842671d1694fe437ee9f7b9e6a02792157a2793b0eba4f478 \
+    --hash=sha256:43857d06b2473b640467467f8f553319b5e819e54be14c86324dad83a0547818 \
+    --hash=sha256:4706c78b0c183dca815bbb4ef3e8dd2136ccc8d1699f62c585e75e211ad388f6 \
+    --hash=sha256:4d9ad7122f60157454f74a850d1337ba135146cef6fb7956d78c7194d52db0fe \
+    --hash=sha256:544da3458d1d249bb8aed5504adf3e194a931e212017934bf7bfa774dad37fb3 \
+    --hash=sha256:55782a31ec539f15b34ee56f19131fe1430f38a4be022eb30c85e0b0dcf57f11 \
+    --hash=sha256:55cd8b13c5ef22003889f599b8f2930836c6f71cd7cf3fc0196633813dc4f928 \
+    --hash=sha256:5dbba95fab9b35957b4977b8904fc1fa56b302f9051eff4d7716ebb0c087f801 \
+    --hash=sha256:5f57b9b61c22537623a5577bf5f2f970dc4e50fac5391090114c6eb3ab5a129f \
+    --hash=sha256:64e097dd08bb408afeeaee9a56f75311c9ca5b27b8b0278279dc8eef85fa1051 \
+    --hash=sha256:664a270d3eac68183ad049665b0f4d0262ec387d5c08c0108dbcfe5b351a8b4d \
+    --hash=sha256:668350ea02af018ca945bd629754d47126b366d981ab88e0369b53bc781ffb14 \
+    --hash=sha256:67cd275a651532d28620eef677b97164a5438c5afcfd44b15e8992afa9eb598c \
+    --hash=sha256:68b5e47fcca8481f36ef444842801928e60e30a5b3852c9f4a95f2582d10dcb2 \
+    --hash=sha256:7191ffc8bcf8a630c547287ab103e1fdf72b2e0c119e634d8a36055c1d988ad0 \
+    --hash=sha256:815089435d0f113719eabf105832e4c4fa1726b39ae3fb2ca7861752b0f70570 \
+    --hash=sha256:8dbef03853a0dbe457417c5469cb0f9d5bf47401b49d50c7dad3c495663b699b \
+    --hash=sha256:91cd292373e85a52c897fa5b4768c895e20a7dc3423449c64f0f96388dd1812e \
+    --hash=sha256:9298d6f2a81f132f72a7e79cbc90a511fffacc75045c2b10050bb87b86c8353d \
+    --hash=sha256:96cff5a2081db82fb710db6a19dd8f904bdebb927727aaf4d9c427984b79a4c1 \
+    --hash=sha256:9e63e0619a5627edb7a5eb3e9568b9f97e604856ba228cc1d8a9f83ce3d0466e \
+    --hash=sha256:a278d02272214ec33f046864a24b5f5aab7f60f855de38c525e5b4ef61ec5b48 \
+    --hash=sha256:a6b2432ac2353c80a56d9015dfc5c4af60245c719628d4193ecd75ddf9cd248c \
+    --hash=sha256:b821403907e865e8377af3eee62f0cb233ea2369ba0fcdce9505ca5bfaf4eeb3 \
+    --hash=sha256:b88bec3f94a16411a1e0336eb69f335f58229e45d4082b12d8e554cedea97586 \
+    --hash=sha256:bfdb8af4801d1c31a18d54b37f4e49bb268d1f485ecf47f70e78d56e04ff37a7 \
+    --hash=sha256:c79996ae64dc4d8730782dff0d1daacc8ce7d4c2ba9cef83b6f469f73c0655ce \
+    --hash=sha256:cc34d182c4fd64b6ff8304a606b95e814e4f8ed4b245b6d6cc9607690e3ef201 \
+    --hash=sha256:d0d481ff55ea6cc49dab2c8276597bd4f1a84a8745fedb4bc23e12e9fb9d0e45 \
+    --hash=sha256:e9723784cf264697024778dcf4b7542c851fe14b14681d6268fb984a53f76df1 \
+    --hash=sha256:f4508e8abd67ebcccd0fbde6e2b1917ba5d153f3f20c1de385abd8722545e05f \
+    --hash=sha256:f515782b168a4ec6ea241add845ccfebe187fc7b09adf892b3ad9e2592c60af1 \
+    --hash=sha256:f89de64d9eb3478b188859214752db50c91a749479011abd99e248550371375f \
+    --hash=sha256:fcd5d932842df503eb0bf60f9cc35e6fe732b51f499e78b45234e0be41b0018d
+    # via grpcio-tools
+grpcio-tools==1.47.0 \
+    --hash=sha256:058060fbc5a60a1c6cc2cbb3d99f730825ba249917978d48b7d0fd8f2caf01da \
+    --hash=sha256:05b495ed997a9afc9016c696ed7fcd35678a7276fe0bd8b95743a382363ad2b4 \
+    --hash=sha256:0b32002ff4ae860c85feb2aca1b752eb4518e7781c5770b869e7b2dfa9d92cbe \
+    --hash=sha256:0eced69e159b3fdd7597d85950f56990e0aa81c11a20a7785fb66f0e47c46b57 \
+    --hash=sha256:156b5f6654fea51983fd9257d47f1ad7bfb2a1d09ed471e610a7b34b97d40802 \
+    --hash=sha256:18548f35b0657422d5d40e6fa89994469f4bb77df09f8133ecdccec0e31fc72c \
+    --hash=sha256:1a0a91941f6f2a4d97e843a5d9ad7ccccf702af2d9455932f18cf922e65af95e \
+    --hash=sha256:2364ac3bd7266752c9971dbef3f79d21cd958777823512faa93473cbd973b8f1 \
+    --hash=sha256:2a6a6e5e08866d643b84c89140bbe504f864f11b87bfff7a5f2af94c5a2be18d \
+    --hash=sha256:2c5c50886e6e79af5387c6514eb19f1f6b1a0b4eb787f1b7a8f21a74e2444102 \
+    --hash=sha256:3edb04d102e0d6f0149d93fe8cf69a38c20a2259a913701a4c35c119049c8404 \
+    --hash=sha256:3fccc282ee97211a33652419dcdfd24a9a60bbd2d56f5c5dd50c7186a0f4d978 \
+    --hash=sha256:441a0a378117447c089b944f325f11039329d8aa961ecdb8226c5dd84af6f003 \
+    --hash=sha256:45ceb73a97e2d7ff719fc12c02f1ef13014c47bad60a864313da88ccd90cdf36 \
+    --hash=sha256:498c0bae4975683a5a33b72cf1bd64703b34c826871fd3ee8d295407cd5211ec \
+    --hash=sha256:4eced9e0674bfb5c528a3bf2ea2b8596da133148b3e0718915792074204ea226 \
+    --hash=sha256:51352070f13ea3346b5f5ca825f2203528b8218fffc6ac6d951216f812272d8b \
+    --hash=sha256:53c47b08ee2f59a89e8df5f3c09850d7fac264754cbaeabae65f6fbf78d80536 \
+    --hash=sha256:5c8ab9b541a869d3b4ef34c291fbfb6ec78ad728e04737fddd91eac3c2193459 \
+    --hash=sha256:6804cbd92b9069ae9189d65300e456bcc3945f6ae196d2af254e9635b9c3ef0d \
+    --hash=sha256:6c66094fd79ee98bcb504e9f1a3fa6e7ebfd246b4e3d8132227e5020b5633988 \
+    --hash=sha256:6d41ec06f2ccc8adcd400a63508ea8e008fb03f270e0031ff2de047def2ada9d \
+    --hash=sha256:74f607b9084b5325a997d9ae57c0814955e19311111568d029b2a6a66f4869ec \
+    --hash=sha256:7589d6f56e633378047274223f0a75534b2cd7c598f9f2894cb4854378b8b00b \
+    --hash=sha256:759064fc8439bbfe5402b2fd3b0685f4ffe07d7cc6a64908c2f88a7c80449ce4 \
+    --hash=sha256:7be45d69f0eed912df2e92d94958d1a3e72617469ec58ffcac3e2eb153a7057e \
+    --hash=sha256:7fd10683f4f03400536e7a026de9929430ee198c2cbdf2c584edfa909ccc8993 \
+    --hash=sha256:818fca1c7dd4ad1c9c01f91ba37006964f4c57c93856fa4ebd7d5589132844d6 \
+    --hash=sha256:84e38f46af513a6f62a3d482160fcb94063dbc9fdd1452d09f8010422f144de1 \
+    --hash=sha256:93d08c02bd82e423353399582f22493a191db459c3f34031b583f13bcf42b95e \
+    --hash=sha256:94114e01c4508d904825bd984e3d2752c0b0e6eb714ac08b99f73421691cf931 \
+    --hash=sha256:9ab78cd16b4ac7c6b79c8be194c67e03238f6378694133ce3ce9b123caf24ed5 \
+    --hash=sha256:9dd6e26e3e0555deadcb52b087c6064e4fd02c09180b42e96c66260137d26b50 \
+    --hash=sha256:a93263955da8d6e449d7ceb84af4e84b82fa760fd661b4ef4549929d9670ab8e \
+    --hash=sha256:ac5c6aef72618ebc5ee9ad725dd53e1c145ef420b79d21a7c43ca80658d3d8d4 \
+    --hash=sha256:ae53ae35a9761ceea50a502addb7186c5188969d63ad21cf12e00d939db5b967 \
+    --hash=sha256:b2fa3c545c8aa1e8c33ca04b1424be3ff77da631faf37db3350d7459c3bdedde \
+    --hash=sha256:c2c280197d68d5a28f5b90adf755bd9e28c99f3e47ad4edcfe20497cf3456e1d \
+    --hash=sha256:ca548afcfa0ffc47c3cf9eeede81adde15c321bfe897085e90ce8913615584ae \
+    --hash=sha256:ccc8ce33bd31bf12649541b5857fabfee7dd84b04138336a27bf46a28d150c11 \
+    --hash=sha256:dc6567d652c6b70d8c03f4e450a694e62b4d69a400752f8b9c3c8b659dd6b06a \
+    --hash=sha256:dd5d330230038374e64fc652fc4c1b25d457a8b67b9069bfce83a17ab675650b \
+    --hash=sha256:e1de1f139f05ab6bbdabc58b06f6ebb5940a92214bbc7246270299387d0af2ae \
+    --hash=sha256:f19191460435f8bc72450cf26ac0559726f98c49ad9b0969db3db8ba51be98c8 \
+    --hash=sha256:f64b5378484be1d6ce59311f86174be29c8ff98d8d90f589e1c56d5acae67d3c \
+    --hash=sha256:fb44ae747fd299b6513420cb6ead50491dc3691d17da48f28fcc5ebf07f47741
+    # via -r extra/requirements.txt
+protobuf==3.19.1 \
+    --hash=sha256:038daf4fa38a7e818dd61f51f22588d61755160a98db087a046f80d66b855942 \
+    --hash=sha256:28ccea56d4dc38d35cd70c43c2da2f40ac0be0a355ef882242e8586c6d66666f \
+    --hash=sha256:36d90676d6f426718463fe382ec6274909337ca6319d375eebd2044e6c6ac560 \
+    --hash=sha256:3cd0458870ea7d1c58e948ac8078f6ba8a7ecc44a57e03032ed066c5bb318089 \
+    --hash=sha256:5935c8ce02e3d89c7900140a8a42b35bc037ec07a6aeb61cc108be8d3c9438a6 \
+    --hash=sha256:615b426a177780ce381ecd212edc1e0f70db8557ed72560b82096bd36b01bc04 \
+    --hash=sha256:62a8e4baa9cb9e064eb62d1002eca820857ab2138440cb4b3ea4243830f94ca7 \
+    --hash=sha256:655264ed0d0efe47a523e2255fc1106a22f6faab7cc46cfe99b5bae085c2a13e \
+    --hash=sha256:6e8ea9173403219239cdfd8d946ed101f2ab6ecc025b0fda0c6c713c35c9981d \
+    --hash=sha256:71b0250b0cfb738442d60cab68abc166de43411f2a4f791d31378590bfb71bd7 \
+    --hash=sha256:74f33edeb4f3b7ed13d567881da8e5a92a72b36495d57d696c2ea1ae0cfee80c \
+    --hash=sha256:77d2fadcf369b3f22859ab25bd12bb8e98fb11e05d9ff9b7cd45b711c719c002 \
+    --hash=sha256:8b30a7de128c46b5ecb343917d9fa737612a6e8280f440874e5cc2ba0d79b8f6 \
+    --hash=sha256:8e51561d72efd5bd5c91490af1f13e32bcba8dab4643761eb7de3ce18e64a853 \
+    --hash=sha256:a529e7df52204565bcd33738a7a5f288f3d2d37d86caa5d78c458fa5fabbd54d \
+    --hash=sha256:b691d996c6d0984947c4cf8b7ae2fe372d99b32821d0584f0b90277aa36982d3 \
+    --hash=sha256:d80f80eb175bf5f1169139c2e0c5ada98b1c098e2b3c3736667f28cbbea39fc8 \
+    --hash=sha256:d83e1ef8cb74009bebee3e61cc84b1c9cd04935b72bca0cbc83217d140424995 \
+    --hash=sha256:d8919368410110633717c406ab5c97e8df5ce93020cfcf3012834f28b1fab1ea \
+    --hash=sha256:db3532d9f7a6ebbe2392041350437953b6d7a792de10e629c1e4f5a6b1fe1ac6 \
+    --hash=sha256:e7b24c11df36ee8e0c085e5b0dc560289e4b58804746fb487287dda51410f1e2 \
+    --hash=sha256:e7e8d2c20921f8da0dea277dfefc6abac05903ceac8e72839b2da519db69206b \
+    --hash=sha256:e813b1c9006b6399308e917ac5d298f345d95bb31f46f02b60cd92970a9afa17 \
+    --hash=sha256:fd390367fc211cc0ffcf3a9e149dfeca78fecc62adb911371db0cec5c8b7472d
+    # via grpcio-tools
+six==1.16.0 \
+    --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
+    --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
+    # via grpcio
+
+# The following packages are considered to be unsafe in a requirements file:
+setuptools==60.2.0 \
+    --hash=sha256:5c89b1a14a67ac5f0956f1cb0aeb7d1d3f4c8ba4e4e1ab7bf1af4933f9a2f0fe \
+    --hash=sha256:675fcebecb43c32eb930481abf907619137547f4336206e4d673180242e1a278
+    # via grpcio-tools
diff --git a/generator/protoc-gen-nanopb b/generator/protoc-gen-nanopb
index 471a620..20a36c7 100755
--- a/generator/protoc-gen-nanopb
+++ b/generator/protoc-gen-nanopb
@@ -1,13 +1,11 @@
-#!/bin/sh
-
+#!/usr/bin/env python3
 # This file is used to invoke nanopb_generator.py as a plugin
 # to protoc on Linux and other *nix-style systems.
 # Use it like this:
 # protoc --plugin=protoc-gen-nanopb=..../protoc-gen-nanopb --nanopb_out=dir foo.proto
-#
-# Note that if you use the binary package of nanopb, the protoc
-# path is already set up properly and there is no need to give
-# --plugin= on the command line.
 
-MYPATH=$(dirname "$0")
-exec "$MYPATH/nanopb_generator.py" --protoc-plugin
+from nanopb_generator import *
+
+if __name__ == '__main__':
+    # Assume we are running as a plugin under protoc.
+    main_plugin()