CMake: Support protobuf code gen; add pw_rpc

- Add proto.cmake and the pw_proto_library function. Like the GN
  version, this generates code for .proto files.
- Provide CMakeLists.txt for pw_protobuf, pw_rpc, and pw_hdlc_lite.
- Add a Nanopb wrapper CMakeLists.txt with a cache variable for Nanopb,
  similar to the variable for GN.

Change-Id: I15d09af8aa3575a86fc360787b117988a75f569e
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/22841
Reviewed-by: Zoltan Szatmary-Ban <szatmz@google.com>
Reviewed-by: Alexei Frolov <frolv@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
Commit-Queue: Wyatt Hepler <hepler@google.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 18d47db..1199180 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -27,15 +27,18 @@
 add_subdirectory(pw_containers)
 add_subdirectory(pw_cpu_exception)
 add_subdirectory(pw_cpu_exception_armv7m)
+add_subdirectory(pw_hdlc_lite)
 add_subdirectory(pw_kvs)
 add_subdirectory(pw_log)
 add_subdirectory(pw_log_basic)
 add_subdirectory(pw_log_tokenized)
 add_subdirectory(pw_minimal_cpp_stdlib)
 add_subdirectory(pw_polyfill)
+add_subdirectory(pw_protobuf)
 add_subdirectory(pw_preprocessor)
 add_subdirectory(pw_random)
 add_subdirectory(pw_result)
+add_subdirectory(pw_rpc)
 add_subdirectory(pw_span)
 add_subdirectory(pw_status)
 add_subdirectory(pw_stream)
@@ -46,3 +49,5 @@
 add_subdirectory(pw_trace)
 add_subdirectory(pw_unit_test)
 add_subdirectory(pw_varint)
+
+add_subdirectory(third_party/nanopb)
diff --git a/pw_build/docs.rst b/pw_build/docs.rst
index d1504b4..b2b5b5c 100644
--- a/pw_build/docs.rst
+++ b/pw_build/docs.rst
@@ -366,19 +366,29 @@
 
 CMake / Ninja
 =============
+Pigweed's `CMake`_ support is provided primarily for projects that have an
+existing CMake build and wish to integrate Pigweed without switching to a new
+build system.
 
-Pigweed's CMake support is provided primarily for projects that have an existing
-CMake build and wish to integrate Pigweed without switching to a new build
-system.
-
-The following command generates Ninja build files in the out/cmake directory.
+The following command generates Ninja build files for a host build in the
+``out/cmake_host`` directory:
 
 .. code-block:: sh
 
-  cmake -B out/cmake -S /path/to/pigweed -G Ninja
+  cmake -B out/cmake_host -S /path/to/pigweed -G Ninja -DCMAKE_TOOLCHAIN_FILE=pw_toolchain/host_clang/toolchain.cmake
 
-Tests can be executed with the ``pw_run_tests_GROUP`` targets. To run the basic
-Pigweed tests, run ``ninja -C out/cmake pw_run_tests_modules``.
+Tests can be executed with the ``pw_run_tests.GROUP`` targets. To run Pigweed
+module tests, execute ``pw_run_tests.modules``:
+
+.. code-block:: sh
+
+  ninja -C out/cmake_host pw_run_tests.modules
+
+:ref:`module-pw_watch` supports CMake, so you can also run
+
+.. code-block:: sh
+
+  pw watch out/cmake_host pw_run_tests.modules
 
 CMake functions
 ---------------
@@ -386,7 +396,9 @@
 
 * ``pw_auto_add_simple_module`` -- For modules with only one library,
   automatically declare the library and its tests.
+* ``pw_auto_add_module_tests`` -- Create test targets for all tests in a module.
 * ``pw_add_facade`` -- Declare a module facade.
+* ``pw_set_backend`` -- Set the backend library to use for a facade.
 * ``pw_add_module_library`` -- Add a library that is part of a module.
 * ``pw_add_test`` -- Declare a test target.
 
@@ -396,6 +408,44 @@
 Special libraries that do not fit well with these functions are created with the
 standard CMake functions, such as ``add_library`` and ``target_link_libraries``.
 
+Facades and backends
+--------------------
+The CMake build uses CMake cache variables for configuring
+:ref:`facades<docs-module-structure-facades>` and backends. Cache variables are
+similar to GN's build args set with ``gn args``. Unlike GN, CMake does not
+support multi-toolchain builds, so these variables have a single global value
+per build directory.
+
+The ``pw_add_facade`` function declares a cache variable named
+``<module_name>_BACKEND`` for each facade. Cache variables can be awkward to
+work with, since their values only change when they're assigned, but then
+persist accross CMake invocations. It is recommended set these variables as
+follows:
+
+* Use ``pw_set_backend`` to set backends appropriate for the target in the
+  target's toolchain file. The toolchain file is provided to ``cmake`` with
+  ``-DCMAKE_TOOLCHAIN_FILE=<toolchain file>``.
+* To temporarily override a backend, set it interactively with ``ccmake`` or
+  ``cmake-gui``.
+* To force to a backend to a particular value globally, call ``pw_set_backend``
+  in the top-level ``CMakeLists.txt`` before any other CMake code is executed.
+
+Third party libraries
+---------------------
+The CMake build includes third-party libraries similarly to the GN build. A
+``dir_pw_third_party_<library>`` cache variable is defined for each third-party
+dependency. This variable can have one of three values:
+
+* ``""`` (empty) -- the dependency is not available
+* ``PRESENT`` -- the dependency is available and is already included in the
+  build
+* ``</path/to/the/dependency>`` -- the dependency is available and will be
+  automatically imported from this path using ``add_subdirectory``.
+
+If the variable is empty (``if("${dir_pw_third_party_<library>}" STREQUAL
+"")``), the dependency is not available. Otherwise, it is available and
+libraries declared by it can be referenced.
+
 Use Pigweed from an existing CMake project
 ------------------------------------------
 To use Pigweed libraries form a CMake-based project, simply include the Pigweed
@@ -412,8 +462,6 @@
 
 .. code-block:: cmake
 
-  include(path/to/pigweed/pw_build/pigweed.cmake)
-
   add_subdirectory(path/to/pigweed/pw_some_module pw_some_module)
   add_subdirectory(path/to/pigweed/pw_another_module pw_another_module)
 
diff --git a/pw_hdlc_lite/CMakeLists.txt b/pw_hdlc_lite/CMakeLists.txt
new file mode 100644
index 0000000..e61c309
--- /dev/null
+++ b/pw_hdlc_lite/CMakeLists.txt
@@ -0,0 +1,28 @@
+# Copyright 2020 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.
+
+include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+
+pw_auto_add_simple_module(pw_hdlc_lite
+  PUBLIC_DEPS
+    pw_bytes
+    pw_result
+    pw_rpc.common
+    pw_status
+    pw_stream
+    pw_sys_io
+  PRIVATE_DEPS
+    pw_checksum
+    pw_log
+)
diff --git a/pw_protobuf/CMakeLists.txt b/pw_protobuf/CMakeLists.txt
new file mode 100644
index 0000000..66fc333
--- /dev/null
+++ b/pw_protobuf/CMakeLists.txt
@@ -0,0 +1,36 @@
+# Copyright 2020 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.
+
+include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+include($ENV{PW_ROOT}/pw_protobuf_compiler/proto.cmake)
+
+pw_auto_add_simple_module(pw_protobuf
+  PUBLIC_DEPS
+    pw_bytes
+    pw_result
+    pw_status
+    pw_varint
+  TEST_DEPS
+    pw_protobuf.codegen_test_protos.pwpb
+)
+
+pw_proto_library(pw_protobuf.codegen_test_protos
+  SOURCES
+    pw_protobuf_protos/test_protos/full_test.proto
+    pw_protobuf_protos/test_protos/imported.proto
+    pw_protobuf_protos/test_protos/importer.proto
+    pw_protobuf_protos/test_protos/non_pw_package.proto
+    pw_protobuf_protos/test_protos/proto2.proto
+    pw_protobuf_protos/test_protos/repeated.proto
+)
diff --git a/pw_protobuf_compiler/proto.cmake b/pw_protobuf_compiler/proto.cmake
new file mode 100644
index 0000000..50e05e8
--- /dev/null
+++ b/pw_protobuf_compiler/proto.cmake
@@ -0,0 +1,225 @@
+# Copyright 2020 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.
+include_guard(GLOBAL)
+
+# Declares a protocol buffers library. This function creates a library for each
+# supported protocol buffer implementation:
+#
+#   ${NAME}.pwpb - pw_protobuf generated code
+#   ${NAME}.nanopb - Nanopb generated code (requires Nanopb)
+#
+# This function also creates libraries for generating pw_rpc code:
+#
+#   ${NAME}.nanopb_rpc - generates Nanopb pw_rpc code
+#   ${NAME}.raw_rpc - generates raw pw_rpc (no protobuf library) code
+#   ${NAME}.pwpb_rpc - (Not implemented) generates pw_protobuf pw_rpc code
+#
+# Args:
+#
+#   NAME - the base name of the libraries to create
+#   SOURCES - .proto source files
+#   DEPS - dependencies on other pw_proto_library targets
+#
+function(pw_proto_library NAME)
+  cmake_parse_arguments(PARSE_ARGV 1 arg "" "" "SOURCES;DEPS")
+
+  set(out_dir "${CMAKE_CURRENT_BINARY_DIR}/protos")
+
+  # Use INTERFACE libraries to track the proto include paths that are passed to
+  # protoc.
+  set(include_deps "${arg_DEPS}")
+  list(TRANSFORM include_deps APPEND ._includes)
+
+  add_library("${NAME}._includes" INTERFACE)
+  target_include_directories("${NAME}._includes" INTERFACE ".")
+  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")
+  file(GENERATE OUTPUT "${include_file}"
+     CONTENT
+       "$<TARGET_PROPERTY:${NAME}._includes,INTERFACE_INCLUDE_DIRECTORIES>")
+
+  # Create a protobuf target for each supported protobuf library.
+  _pw_pwpb_library(
+      "${NAME}" "${arg_SOURCES}" "${arg_DEPS}" "${include_file}" "${out_dir}")
+  _pw_raw_rpc_library(
+      "${NAME}" "${arg_SOURCES}" "${arg_DEPS}" "${include_file}" "${out_dir}")
+  _pw_nanopb_library(
+      "${NAME}" "${arg_SOURCES}" "${arg_DEPS}" "${include_file}" "${out_dir}")
+  _pw_nanopb_rpc_library(
+      "${NAME}" "${arg_SOURCES}" "${arg_DEPS}" "${include_file}" "${out_dir}")
+endfunction(pw_proto_library)
+
+# Internal function that invokes protoc through generate_protos.py.
+function(_pw_generate_protos
+      TARGET LANGUAGE INCLUDE_FILE OUT_DIR SOURCES OUTPUTS DEPS)
+  set(script "$ENV{PW_ROOT}/pw_protobuf_compiler/py/pw_protobuf_compiler/generate_protos.py")
+  add_custom_command(
+    COMMAND
+      python
+      "${script}"
+      --language "${LANGUAGE}"
+      --module-path "${CMAKE_CURRENT_SOURCE_DIR}"
+      --include-file "${INCLUDE_FILE}"
+      --out-dir "${OUT_DIR}"
+      ${ARGN}
+      ${SOURCES}
+    DEPENDS
+      ${SOURCES}
+      ${script}
+      ${DEPS}
+    OUTPUT
+      ${outputs}
+  )
+  add_custom_target("${TARGET}" DEPENDS ${outputs})
+endfunction(_pw_generate_protos)
+
+# Internal function that creates a pwpb proto library.
+function(_pw_pwpb_library NAME SOURCES DEPS INCLUDE_FILE OUT_DIR)
+  # Determine the names of the output files.
+  set(outputs "${SOURCES}")
+  list(TRANSFORM outputs REPLACE "\.proto$" ".pwpb.h")
+  list(TRANSFORM outputs PREPEND "${OUT_DIR}/")
+
+  # Make the source paths absolute since they are passed to a script.
+  list(TRANSFORM SOURCES PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/")
+
+  list(TRANSFORM DEPS APPEND .pwpb)
+
+  _pw_generate_protos("${NAME}.generate.pwpb"
+      cc
+      "${INCLUDE_FILE}"
+      "${OUT_DIR}"
+      "${SOURCES}"
+      "${outputs}"
+      "${DEPS}"
+  )
+
+  # Create the library with the generated source files.
+  add_library("${NAME}.pwpb" INTERFACE)
+  target_include_directories("${NAME}.pwpb" INTERFACE "${OUT_DIR}")
+  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)
+  # Determine the names of the output files.
+  set(outputs "${SOURCES}")
+  list(TRANSFORM outputs REPLACE "\.proto$" ".raw_rpc.pb.h")
+  list(TRANSFORM outputs PREPEND "${OUT_DIR}/")
+
+  # Make the source paths absolute since they are passed to a script.
+  list(TRANSFORM SOURCES PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/")
+
+  list(TRANSFORM DEPS APPEND .raw_rpc)
+
+  _pw_generate_protos("${NAME}.generate.raw_rpc"
+      raw_rpc
+      "${INCLUDE_FILE}"
+      "${OUT_DIR}"
+      "${SOURCES}"
+      "${outputs}"
+      "${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_link_libraries("${NAME}.raw_rpc"
+    INTERFACE
+      pw_rpc.raw
+      pw_rpc.server
+      ${DEPS}
+  )
+  add_dependencies("${NAME}.raw_rpc" "${NAME}.generate.raw_rpc")
+endfunction(_pw_raw_rpc_library)
+
+# Internal function that creates a nanopb proto library.
+function(_pw_nanopb_library NAME SOURCES DEPS INCLUDE_FILE OUT_DIR)
+  # Determine the names of the output files.
+  set(outputs_h "${SOURCES}")
+  list(TRANSFORM outputs_h REPLACE "\.proto$" ".pb.h")
+  list(TRANSFORM outputs_h PREPEND "${OUT_DIR}/")
+
+  set(outputs_c "${SOURCES}")
+  list(TRANSFORM outputs_c REPLACE "\.proto$" ".pb.c")
+  list(TRANSFORM outputs_c PREPEND "${OUT_DIR}/")
+
+  set(outputs ${outputs_c} ${outputs_h})
+
+  # Make the source paths absolute since they are passed to a script.
+  list(TRANSFORM SOURCES PREPEND "${CMAKE_CURRENT_SOURCE_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>")
+  set(nanopb_plugin
+      "$<IF:$<TARGET_EXISTS:protobuf-nanopb-static>,${nanopb_dir}/generator/protoc-gen-nanopb,COULD_NOT_FIND_protobuf-nanopb-static_TARGET_PLEASE_SET_UP_NANOPB>")
+  if(WIN32)
+    set(nanopb_plugin "${nanopb_plugin}.bat")
+  endif()
+
+  _pw_generate_protos("${NAME}.generate.nanopb"
+      nanopb
+      "${INCLUDE_FILE}"
+      "${OUT_DIR}"
+      "${SOURCES}"
+      "${outputs}"
+      "${DEPS}"
+      --custom-plugin "${nanopb_plugin}"
+      --include-paths "${nanopb_dir}/generator/proto"
+  )
+
+  # Create the library with the generated source files.
+  add_library("${NAME}.nanopb" EXCLUDE_FROM_ALL ${outputs})
+  target_include_directories("${NAME}.nanopb" PUBLIC "${OUT_DIR}")
+  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)
+  # Determine the names of the output files.
+  set(outputs "${SOURCES}")
+  list(TRANSFORM outputs REPLACE "\.proto$" ".rpc.pb.h")
+  list(TRANSFORM outputs PREPEND "${OUT_DIR}/")
+
+  # Make the source paths absolute since they are passed to a script.
+  list(TRANSFORM SOURCES PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/")
+
+  list(TRANSFORM DEPS APPEND .nanopb_rpc)
+
+  _pw_generate_protos("${NAME}.generate.nanopb_rpc"
+      nanopb_rpc
+      "${INCLUDE_FILE}"
+      "${OUT_DIR}"
+      "${SOURCES}"
+      "${outputs}"
+      "${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_link_libraries("${NAME}.nanopb_rpc"
+    INTERFACE
+      "${NAME}.nanopb"
+      pw_rpc.server
+      pw_rpc.nanopb
+      ${DEPS}
+  )
+  add_dependencies("${NAME}.nanopb_rpc" "${NAME}.generate.nanopb_rpc")
+endfunction(_pw_nanopb_rpc_library)
diff --git a/pw_rpc/CMakeLists.txt b/pw_rpc/CMakeLists.txt
index b4687ef..bbaf74b 100644
--- a/pw_rpc/CMakeLists.txt
+++ b/pw_rpc/CMakeLists.txt
@@ -13,9 +13,70 @@
 # the License.
 
 include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+include($ENV{PW_ROOT}/pw_protobuf_compiler/proto.cmake)
 
-pw_auto_add_simple_module(pw_rpc
+if(NOT "${dir_pw_third_party_nanopb}" STREQUAL "")
+  add_subdirectory(nanopb)
+endif()
+
+add_subdirectory(raw)
+
+pw_add_module_library(pw_rpc.server
+  SOURCES
+    base_server_writer.cc
+    server.cc
+    service.cc
+  PUBLIC_DEPS
+    pw_rpc.common
+  PRIVATE_DEPS
+    pw_log
+)
+
+pw_add_module_library(pw_rpc.client
+  SOURCES
+    base_client_call.cc
+    client.cc
+  PUBLIC_DEPS
+    pw_rpc.common
+  PRIVATE_DEPS
+    pw_log
+)
+
+pw_add_module_library(pw_rpc.common
+  SOURCES
+    channel.cc
+    packet.cc
   PUBLIC_DEPS
     pw_assert
+    pw_bytes
+    pw_containers
     pw_span
+    pw_status
+    pw_rpc.protos.pwpb
+  PRIVATE_DEPS
+    pw_log
+)
+
+add_library(pw_rpc.test_utils INTERFACE)
+target_include_directories(pw_rpc.test_utils INTERFACE .)
+
+pw_proto_library(pw_rpc.protos
+  SOURCES
+    pw_rpc_protos/packet.proto
+)
+
+pw_proto_library(pw_rpc.echo_proto
+  SOURCES
+    pw_rpc_protos/echo.proto
+)
+
+pw_proto_library(pw_rpc.test_protos
+  SOURCES
+    pw_rpc_test_protos/test.proto
+)
+
+pw_auto_add_module_tests(pw_rpc
+  PRIVATE_DEPS
+    pw_rpc.client
+    pw_rpc.server
 )
diff --git a/pw_rpc/nanopb/CMakeLists.txt b/pw_rpc/nanopb/CMakeLists.txt
new file mode 100644
index 0000000..9e1365b
--- /dev/null
+++ b/pw_rpc/nanopb/CMakeLists.txt
@@ -0,0 +1,29 @@
+# Copyright 2020 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.
+
+include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+
+pw_auto_add_simple_module(pw_rpc.nanopb
+  PUBLIC_DEPS
+    pw_rpc.client
+    pw_rpc.common
+    pw_rpc.raw
+    pw_rpc.server
+  PRIVATE_DEPS
+    pw_third_party.nanopb
+  TEST_DEPS
+    pw_rpc.echo_proto.nanopb_rpc
+    pw_rpc.test_protos.nanopb_rpc
+    pw_rpc.test_utils
+)
diff --git a/pw_rpc/raw/CMakeLists.txt b/pw_rpc/raw/CMakeLists.txt
new file mode 100644
index 0000000..f0e1225
--- /dev/null
+++ b/pw_rpc/raw/CMakeLists.txt
@@ -0,0 +1,26 @@
+# Copyright 2020 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.
+
+include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+
+pw_auto_add_simple_module(pw_rpc.raw
+  PUBLIC_DEPS
+    pw_rpc.client
+    pw_rpc.common
+    pw_rpc.server
+  TEST_DEPS
+    pw_rpc.test_protos.pwpb
+    pw_rpc.test_protos.raw_rpc
+    pw_rpc.test_utils
+)
diff --git a/pw_watch/docs.rst b/pw_watch/docs.rst
index 9b8ee45..f5f0c6c 100644
--- a/pw_watch/docs.rst
+++ b/pw_watch/docs.rst
@@ -14,7 +14,7 @@
 
 .. note::
 
-  ``pw_watch`` currently only works with Pigweed's GN build.
+  ``pw_watch`` currently only works with Pigweed's GN and CMake builds.
 
 Module Usage
 ============
diff --git a/third_party/nanopb/CMakeLists.txt b/third_party/nanopb/CMakeLists.txt
new file mode 100644
index 0000000..1341403
--- /dev/null
+++ b/third_party/nanopb/CMakeLists.txt
@@ -0,0 +1,30 @@
+# Copyright 2020 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.
+
+set(dir_pw_third_party_nanopb "" CACHE PATH
+    "Path to a Nanopb installation to import. Set to PRESENT if Nanopb is already present in the build."
+)
+
+if("${dir_pw_third_party_nanopb}" STREQUAL "")
+  return()
+elseif(NOT "${dir_pw_third_party_nanopb}" STREQUAL PRESENT)
+  add_subdirectory("${dir_pw_third_party_nanopb}" third_party/nanopb)
+endif()
+
+add_library(pw_third_party.nanopb INTERFACE)
+target_link_libraries(pw_third_party.nanopb INTERFACE protobuf-nanopb-static)
+target_include_directories(pw_third_party.nanopb
+  INTERFACE
+    $<TARGET_PROPERTY:protobuf-nanopb-static,SOURCE_DIR>
+)