Repository: conan-io/conan-center-index

Issue:
Add Protobuf protoc 3.9.1
Specify library name and version:  **protoc/3.9.1**

This is the second part of Google Protobuf. It requires #64 first!

Also, Protoc provides CMake with specific macros, which can't be exported by Conan, so this package requires an exception in the whitelist.

I've added the path to builddirs as required by KB-H019.

- [x] I've read the [guidelines](https://github.com/conan-io/conan-center-index/wiki#how-to-submit-a-pull-request) for contributing.
- [x] I've followed the [PEP8](https://www.python.org/dev/peps/pep-0008/) style guides for Python code in the recipes.
- [x] I've used the [latest](https://github.com/conan-io/conan/releases/latest) Conan client version.
- [x] I've tried at least one configuration locally with the
      [conan-center hook](https://github.com/conan-io/hooks.git) activated.




Discussion:
protoc/3.9.1:

-   **Error processing recipe: Linux x86_64, Release, gcc 5, libstdc++**
You are depending on 'protobuf/3.9.1' but it is not in the repository
Some configurations of 'protoc/3.9.1' have failed:
-   **Linux x86_64, Release, gcc 5, libstdc++**
    - Hooks errors detected:
        - `[HOOK - conan-center.py] post_package(): ERROR: [CMAKE-MODULES-CONFIG-FILES (KB-H016)] The conan-center repository doesn't allow the packages to contain CMake find modules or config files. The packages have to be located using generators and the declared `cpp_info` information (https://github.com/conan-io/conan-center-index/wiki/Error-Knowledge-Base#KB-H016) `
        - `[HOOK - conan-center.py] post_package(): ERROR: [CMAKE-MODULES-CONFIG-FILES (KB-H016)] Found files:`
    - [Log download](https://c3i.jfrog.io/c3i/misc/logs/pr/65/2/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/create_stdout.txt)
    - [Log error Download](https://c3i.jfrog.io/c3i/misc/logs/pr/65/2/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/create_stderr.txt)
    - [Profile](https://c3i.jfrog.io/c3i/misc/logs/pr/65/2/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/profile.txt)
- [Access to all the logs](https://c3i.jfrog.io/c3i/misc/logs/pr/65/2/protoc/3.9.1/)
We need to put protoc in the whitelist, since the macros are declared in protoc-config.cmake
Some configurations of 'protoc/3.9.1' have failed:
-   **Linux x86_64, Release, gcc 5, libstdc++**
    - Hooks errors detected:
        - `[HOOK - conan-center.py] post_package(): ERROR: [CMAKE-MODULES-CONFIG-FILES (KB-H016)] The conan-center repository doesn't allow the packages to contain CMake find modules or config files. The packages have to be located using generators and the declared `cpp_info` information (https://github.com/conan-io/conan-center-index/wiki/Error-Knowledge-Base#KB-H016) `
        - `[HOOK - conan-center.py] post_package(): ERROR: [CMAKE-MODULES-CONFIG-FILES (KB-H016)] Found files:`
    - [Log download](https://c3i.jfrog.io/c3i/misc/logs/pr/65/3/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/create_stdout.txt)
    - [Log error Download](https://c3i.jfrog.io/c3i/misc/logs/pr/65/3/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/create_stderr.txt)
    - [Profile](https://c3i.jfrog.io/c3i/misc/logs/pr/65/3/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/profile.txt)
- [Access to all the logs](https://c3i.jfrog.io/c3i/misc/logs/pr/65/3/protoc/3.9.1/)
> We need to put protoc in the whitelist, since the macros are declared in protoc-config.cmake

No, that cannot be whitelisted. We need a mechanism to declare macros to be inherited: https://github.com/conan-io/conan/issues/5798  
Some configurations of 'protoc/3.9.1' have failed:
-   **Linux x86_64, Release, gcc 5, libstdc++**
    - Hooks errors detected:
        - `[HOOK - conan-center.py] post_package(): ERROR: [CMAKE-MODULES-CONFIG-FILES (KB-H016)] The conan-center repository doesn't allow the packages to contain CMake find modules or config files. The packages have to be located using generators and the declared `cpp_info` information (https://github.com/conan-io/conan-center-index/wiki/Error-Knowledge-Base#KB-H016) `
        - `[HOOK - conan-center.py] post_package(): ERROR: [CMAKE-MODULES-CONFIG-FILES (KB-H016)] Found files:`
    - [Log download](https://c3i.jfrog.io/c3i/misc/logs/pr/65/4/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/create_stdout.txt)
    - [Log error Download](https://c3i.jfrog.io/c3i/misc/logs/pr/65/4/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/create_stderr.txt)
    - [Profile](https://c3i.jfrog.io/c3i/misc/logs/pr/65/4/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/profile.txt)
- [Access to all the logs](https://c3i.jfrog.io/c3i/misc/logs/pr/65/4/protoc/3.9.1/)
Could we add a new tag for this PR, like `blocked` ?
Yeah, it is reasonable
Some configurations of 'protoc/3.9.1' have failed:
-   **Linux x86_64, Release, gcc 5, libstdc++**
    - Hooks errors detected:
        - `[HOOK - conan-center.py] pre_export(): ERROR: [META LINES (KB-H025)] PEP 263 (encoding) is not allowed in the conanfile. Remove the line 1 (https://github.com/conan-io/conan-center-index/wiki/Error-Knowledge-Base#KB-H025) `
    - [Log download](https://c3i.jfrog.io/c3i/misc/logs/pr/65/5/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/create_stdout.txt)
    - [Log error Download](https://c3i.jfrog.io/c3i/misc/logs/pr/65/5/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/create_stderr.txt)
    - [Profile](https://c3i.jfrog.io/c3i/misc/logs/pr/65/5/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/profile.txt)
- [Access to all the logs](https://c3i.jfrog.io/c3i/misc/logs/pr/65/5/protoc/3.9.1/)
Some configurations of 'protoc/3.9.1' have failed:
-   **Linux x86_64, Release, gcc 5, libstdc++**
    - [Log download](https://c3i.jfrog.io/c3i/misc/logs/pr/65/6/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/create_stdout.txt)
    - [Log error Download](https://c3i.jfrog.io/c3i/misc/logs/pr/65/6/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/create_stderr.txt)
    - [Profile](https://c3i.jfrog.io/c3i/misc/logs/pr/65/6/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/profile.txt)
- [Access to all the logs](https://c3i.jfrog.io/c3i/misc/logs/pr/65/6/protoc/3.9.1/)
Some configurations of 'protoc/3.9.1' have failed:
-   **Linux x86_64, Release, gcc 5, libstdc++**
    - [Log download](https://c3i.jfrog.io/c3i/misc/logs/pr/65/7/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/create_stdout.txt)
    - [Log error Download](https://c3i.jfrog.io/c3i/misc/logs/pr/65/7/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/create_stderr.txt)
    - [Profile](https://c3i.jfrog.io/c3i/misc/logs/pr/65/7/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/profile.txt)
- [Access to all the logs](https://c3i.jfrog.io/c3i/misc/logs/pr/65/7/protoc/3.9.1/)
Some configurations of 'protoc/3.9.1' have failed:
-   **Linux x86_64, Release, gcc 5, libstdc++**
    - [Log download](https://c3i.jfrog.io/c3i/misc/logs/pr/65/8/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/create_stdout.txt)
    - [Log error Download](https://c3i.jfrog.io/c3i/misc/logs/pr/65/8/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/create_stderr.txt)
    - [Profile](https://c3i.jfrog.io/c3i/misc/logs/pr/65/8/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/profile.txt)
- [Access to all the logs](https://c3i.jfrog.io/c3i/misc/logs/pr/65/8/protoc/3.9.1/)
Some configurations of 'protoc/3.9.1' have failed:
-   **Linux x86_64, Release, gcc 5, libstdc++**
    - [Log download](https://c3i.jfrog.io/c3i/misc/logs/pr/65/9/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/create_stdout.txt)
    - [Log error Download](https://c3i.jfrog.io/c3i/misc/logs/pr/65/9/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/create_stderr.txt)
    - [Profile](https://c3i.jfrog.io/c3i/misc/logs/pr/65/9/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/profile.txt)
- [Access to all the logs](https://c3i.jfrog.io/c3i/misc/logs/pr/65/9/protoc/3.9.1/)
Some configurations of 'protoc/3.9.1' have failed:
-   **Linux x86_64, Release, gcc 5, libstdc++**
    - [Log download](https://c3i.jfrog.io/c3i/misc/logs/pr/65/10/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/create_stdout.txt)
    - [Log error Download](https://c3i.jfrog.io/c3i/misc/logs/pr/65/10/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/create_stderr.txt)
    - [Profile](https://c3i.jfrog.io/c3i/misc/logs/pr/65/10/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/profile.txt)
- [Access to all the logs](https://c3i.jfrog.io/c3i/misc/logs/pr/65/10/protoc/3.9.1/)
@danimtb check my last [commit](https://github.com/conan-io/conan-center-index/pull/65/commits/d9dc4373825610f2c3b3592f66eabac6d1cc7259).

The Conan generator `cmake_find_package` does not append env vars (e.g. PATH), which were exported by `self.env_info`, this behavior is a problem for those macros exported by Protobuf files. As the macro don't know where protoc is installed, it will fail, so we need to use the regular `cmake` generator and patch the protobuf cmake file. 

As you can see, it's fragile, and don't like to force anyone using the `cmake` generator, when `cmake_find_package` should enough. I can't find a smart solution for this situation, I only can think about changing the `cmake_find_package` when building an installer package.


Some configurations of 'protoc/3.9.1' have failed:
-   **Linux x86_64, Release, gcc 5, libstdc++**
    - Hooks errors detected:
        - `[HOOK - conan-center.py] post_package(): ERROR: [CMAKE-MODULES-CONFIG-FILES (KB-H016)] The conan-center repository doesn't allow the packages to contain CMake find modules or config files. The packages have to be located using generators and the declared `cpp_info` information (https://github.com/conan-io/conan-center-index/wiki/Error-Knowledge-Base#KB-H016) `
        - `[HOOK - conan-center.py] post_package(): ERROR: [CMAKE-MODULES-CONFIG-FILES (KB-H016)] Found files:`
    - [Log download](https://c3i.jfrog.io/c3i/misc/logs/pr/65/11/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/create_stdout.txt)
    - [Log error Download](https://c3i.jfrog.io/c3i/misc/logs/pr/65/11/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/create_stderr.txt)
    - [Profile](https://c3i.jfrog.io/c3i/misc/logs/pr/65/11/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/profile.txt)
- [Access to all the logs](https://c3i.jfrog.io/c3i/misc/logs/pr/65/11/protoc/3.9.1/)
Do we have any kind of plan how to progress with Protobuf/Protoc? 🤔 
Some configurations of 'protoc/3.9.1' failed in build 12 (`d9dc4373825610f2c3b3592f66eabac6d1cc7259`):
-   **Linux x86_64, Release, gcc 5, libstdc++**
    - [Log download](https://c3i.jfrog.io/c3i/misc/logs/pr/65/12/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/create_stdout.txt)
    - [Log error Download](https://c3i.jfrog.io/c3i/misc/logs/pr/65/12/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/create_stderr.txt)
    - [Profile](https://c3i.jfrog.io/c3i/misc/logs/pr/65/12/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/profile.txt)
- [Access to all the logs](https://c3i.jfrog.io/c3i/misc/logs/pr/65/12/protoc/3.9.1/)
An unexpected error happened and has been reported. Help is on its way! :horse_racing: 
@uilianries Check https://github.com/uilianries/conan-center-index/pull/1
Some configurations of 'protoc/3.9.1' failed in build 14 (`92058e26db2cfa8d591823d40acaa9e75cb130cf`):
-   **Linux x86_64, Release, gcc 5, libstdc++**
    - [Log download](https://c3i.jfrog.io/c3i/misc/logs/pr/65/14/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/create_stdout.txt)
    - [Log error Download](https://c3i.jfrog.io/c3i/misc/logs/pr/65/14/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/create_stderr.txt)
    - [Profile](https://c3i.jfrog.io/c3i/misc/logs/pr/65/14/protoc/3.9.1/c0c1ef10e3d0ded44179e28b669d6aed0277ca6a/profile.txt)
- [Access to all the logs](https://c3i.jfrog.io/c3i/misc/logs/pr/65/14/protoc/3.9.1/)
Some configurations of 'protoc/3.9.1' failed in build 15 (`d700f580320adf0550e572f1dce19e1288b443e9`):
-   **Windows x86_64, Debug, Visual Studio 14,  MTd**
    - [Log download](https://c3i.jfrog.io/c3i/misc/logs/pr/65/15/protoc/3.9.1/9e449b1ac4341c7bdc87f8a7efe81200c68c405b/create_stdout.txt)
    - [Log error Download](https://c3i.jfrog.io/c3i/misc/logs/pr/65/15/protoc/3.9.1/9e449b1ac4341c7bdc87f8a7efe81200c68c405b/create_stderr.txt)
    - [Profile](https://c3i.jfrog.io/c3i/misc/logs/pr/65/15/protoc/3.9.1/9e449b1ac4341c7bdc87f8a7efe81200c68c405b/profile.txt)
- [Access to all the logs](https://c3i.jfrog.io/c3i/misc/logs/pr/65/15/protoc/3.9.1/)
@uilianries 
Do you have an idea?
```
CMake Error at conanbuildinfo.cmake:461 (message):
  Detected a mismatch for the compiler version between your conan profile
  settings and CMake:

  Compiler version specified in your conan profile: 14

  Compiler version detected in CMake: 19.24

  Please check your conan profile settings (conan profile show
```
Coming from https://github.com/conan-io/conan-center-index/issues/1136

we might want to add the protoc binaries directly to the protobuf package now
@Croydon thanks for pinging! 
FYI.
I'm working on it atm.
I need it for a recipe.

#1179
Looking at the release page of protobuf, they release protobuf and protoc binaries seperately.
See https://github.com/protocolbuffers/protobuf/releases
I gonna clone this PR since we are moving to a new approach

Resolving patch:
diff --git a/recipes/protobuf/all/conanfile.py b/recipes/protobuf/all/conanfile.py
--- a/recipes/protobuf/all/conanfile.py
+++ b/recipes/protobuf/all/conanfile.py
@@ -16,10 +16,10 @@ class ProtobufConan(ConanFile):
     generators = "cmake"
     short_paths = True
     settings = "os", "arch", "compiler", "build_type"
-    options = {"shared": [True, False], "with_zlib": [
-        True, False], "fPIC": [True, False], "lite": [True, False]}
-    default_options = {"with_zlib": False,
-                       "shared": False, "fPIC": True, "lite": False}
+    options = {"shared": [True, False], "with_zlib": [True, False], "fPIC": [True, False], "lite": [True, False]}
+    default_options = {"with_zlib": False, "shared": False, "fPIC": True, "lite": False}
+
+    _cmake = None
 
     @property
     def _source_subfolder(self):
@@ -50,16 +50,23 @@ def requirements(self):
         if self.options.with_zlib:
             self.requires("zlib/1.2.11")
 
+    @property
+    def _cmake_install_base_path(self):
+        return os.path.join("lib", "cmake", "protobuf")
+
     def _configure_cmake(self):
-        cmake = CMake(self)
-        cmake.definitions["protobuf_BUILD_TESTS"] = False
-        cmake.definitions["protobuf_WITH_ZLIB"] = self.options.with_zlib
-        cmake.definitions["protobuf_BUILD_PROTOC_BINARIES"] = not self.options.lite
-        cmake.definitions["protobuf_BUILD_PROTOBUF_LITE"] = self.options.lite
+        if self._cmake:
+            return self._cmake
+        self._cmake = CMake(self)
+        self._cmake.definitions["CMAKE_INSTALL_CMAKEDIR"] = self._cmake_install_base_path.replace("\\", "/")
+        self._cmake.definitions["protobuf_BUILD_TESTS"] = False
+        self._cmake.definitions["protobuf_WITH_ZLIB"] = self.options.with_zlib
+        self._cmake.definitions["protobuf_BUILD_PROTOC_BINARIES"] = not self.options.lite
+        self._cmake.definitions["protobuf_BUILD_PROTOBUF_LITE"] = self.options.lite
         if self.settings.compiler == "Visual Studio":
-            cmake.definitions["protobuf_MSVC_STATIC_RUNTIME"] = "MT" in self.settings.compiler.runtime
-        cmake.configure(build_folder=self._build_subfolder)
-        return cmake
+            self._cmake.definitions["protobuf_MSVC_STATIC_RUNTIME"] = "MT" in self.settings.compiler.runtime
+        self._cmake.configure(build_folder=self._build_subfolder)
+        return self._cmake
 
     def build(self):
         for patch in self.conan_data["patches"][self.version]:
@@ -71,8 +78,6 @@ def package(self):
         self.copy("LICENSE", dst="licenses", src=self._source_subfolder)
         cmake = self._configure_cmake()
         cmake.install()
-        tools.rmdir(os.path.join(self.package_folder, "lib", "cmake"))
-        tools.rmdir(os.path.join(self.package_folder, "cmake"))
         tools.rmdir(os.path.join(self.package_folder, "lib", "pkgconfig"))
 
     def package_info(self):
@@ -89,3 +94,15 @@ def package_info(self):
                 self.cpp_info.defines = ["PROTOBUF_USE_DLLS"]
         self.cpp_info.names["cmake_find_package"] = "Protobuf"
         self.cpp_info.names["cmake_find_package_multi"] = "Protobuf"
+        self.cpp_info.builddirs = [
+            self._cmake_install_base_path,
+        ]
+        self.cpp_info.build_modules = [
+            os.path.join(self._cmake_install_base_path, "protobuf-generate.cmake"),
+            os.path.join(self._cmake_install_base_path, "protobuf-module.cmake"),
+            os.path.join(self._cmake_install_base_path, "protobuf-options.cmake"),
+        ]
+        bindir = os.path.join(self.package_folder, "bin")
+        self.output.info("Appending PATH environment variable: {}".format(bindir))
+        self.env_info.PATH.append(bindir)
+


Test patch:
diff --git a/recipes/protobuf/all/test_package/CMakeLists.txt b/recipes/protobuf/all/test_package/CMakeLists.txt
--- a/recipes/protobuf/all/test_package/CMakeLists.txt
+++ b/recipes/protobuf/all/test_package/CMakeLists.txt
@@ -1,70 +1,22 @@
 cmake_minimum_required(VERSION 2.8.12)
 project(test_package)
 
-include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
+include("${CMAKE_BINARY_DIR}/../conanbuildinfo.cmake")
 conan_basic_setup()
 
-set(CMAKE_VERBOSE_MAKEFILE ON)
+option(PROTOC_AVAILABLE "protobuf is available")
 
-# Download and build .proto
-file(READ ${CMAKE_BINARY_DIR}/conaninfo.txt CONAN_INFO)
-string(REGEX MATCH "([0-9]+\\.[0-9]+\\.[0-9]+)" PACKAGE_VERSION ${CONAN_INFO})
-if(NOT DEFINED PACKAGE_VERSION)
-  message(FATAL_ERROR "Protobuf version not found")
-endif()
-string(TOLOWER ${CONAN_SETTINGS_OS} OS)
-set(FILENAME "${CMAKE_CURRENT_SOURCE_DIR}/protoc.zip")
-set(EXT "")
-if(OS STREQUAL "windows")
-  if(CONAN_SETTINGS_ARCH STREQUAL "x86_64")
-    set(ARCH "64")
-  elseif(CONAN_SETTINGS_ARCH STREQUAL "x86_32")
-    set(ARCH "32")
-  endif()  
-  set(EXT ".exe")
-  set(OS_ARCH "win${ARCH}")
-elseif(OS STREQUAL "macos")
-  set(OS_ARCH "osx-${CONAN_SETTINGS_ARCH}")
-else()
-  set(OS_ARCH "${OS}-${CONAN_SETTINGS_ARCH}")
-endif(OS STREQ "Windows")
-
-set(URL "https://github.com/protocolbuffers/protobuf/releases/download/v${PACKAGE_VERSION}/protoc-${PACKAGE_VERSION}-${OS_ARCH}.zip")
-message("Download protoc binary from ${URL}")
-file(DOWNLOAD ${URL} ${FILENAME} 
-	      TIMEOUT 10
-        STATUS res)
-if(NOT res EQUAL 0)
-  message(FATAL_ERROR "Download of protoc failed: ${res}")
-endif()
-
-execute_process(COMMAND "${CMAKE_COMMAND}" -E tar xzf "${FILENAME}"
-                    WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
-                    RESULT_VARIABLE res)
-if(NOT res EQUAL 0)
-  message(FATAL_ERROR "Unpack of protoc failed: ${res}")
-endif()
-
-execute_process(COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/bin/protoc${EXT}" "--proto_path=${CMAKE_CURRENT_SOURCE_DIR}" "--cpp_out=${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/addressbook.proto"
-                    WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
-                    RESULT_VARIABLE res)
-if(NOT res EQUAL 0)
-  message(FATAL_ERROR "Protoc compilation failed: ${res}")
-endif()
+add_executable(${PROJECT_NAME} test_package.cpp addressbook.proto)
+target_link_libraries(${PROJECT_NAME} ${CONAN_LIBS})
+set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 11)
+target_include_directories(${PROJECT_NAME} PRIVATE "${CMAKE_BINARY_DIR}")
 
-file(REMOVE_RECURSE ${FILENAME} "${CMAKE_CURRENT_SOURCE_DIR}/bin" "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/readme.txt")
+if(PROTOC_AVAILABLE)
+    find_package(Protobuf REQUIRED)
+    protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS TARGET ${PROJECT_NAME})
 
-# http://www.cmake.org/Wiki/CMake_FAQ#How_can_I_build_my_MSVC_application_with_a_static_runtime.3F
-if(MSVC AND protobuf_MSVC_STATIC_RUNTIME)
-  foreach(flag_var
-      CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
-      CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
-    if(${flag_var} MATCHES "/MD")
-      string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
-    endif(${flag_var} MATCHES "/MD")
-  endforeach()
+    message(STATUS "PROTO_SRCS: ${PROTO_SRCS}")
+    message(STATUS "PROTO_HDRS: ${PROTO_HDRS}")
+else()
+    target_sources(${PROJECT_NAME} PRIVATE "${CMAKE_BINARY_DIR}/addressbook.pb.cc" "${CMAKE_BINARY_DIR}/addressbook.pb.h")
 endif()
-
-add_executable(${PROJECT_NAME} test_package.cpp addressbook.pb.cc addressbook.pb.h)
-target_link_libraries(${PROJECT_NAME} ${CONAN_LIBS})
-set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 11)
diff --git a/recipes/protobuf/all/test_package/addressbook.3.11.4.pb.cc b/recipes/protobuf/all/test_package/addressbook.3.11.4.pb.cc
new file mode 100644
--- /dev/null
+++ b/recipes/protobuf/all/test_package/addressbook.3.11.4.pb.cc
@@ -0,0 +1,958 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: addressbook.proto
+
+#include "addressbook.pb.h"
+
+#include <algorithm>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/reflection_ops.h>
+#include <google/protobuf/wire_format.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+extern PROTOBUF_INTERNAL_EXPORT_google_2fprotobuf_2ftimestamp_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_Timestamp_google_2fprotobuf_2ftimestamp_2eproto;
+extern PROTOBUF_INTERNAL_EXPORT_addressbook_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_Person_addressbook_2eproto;
+extern PROTOBUF_INTERNAL_EXPORT_addressbook_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_Person_PhoneNumber_addressbook_2eproto;
+namespace tutorial {
+class Person_PhoneNumberDefaultTypeInternal {
+ public:
+  ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<Person_PhoneNumber> _instance;
+} _Person_PhoneNumber_default_instance_;
+class PersonDefaultTypeInternal {
+ public:
+  ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<Person> _instance;
+} _Person_default_instance_;
+class AddressBookDefaultTypeInternal {
+ public:
+  ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<AddressBook> _instance;
+} _AddressBook_default_instance_;
+}  // namespace tutorial
+static void InitDefaultsscc_info_AddressBook_addressbook_2eproto() {
+  GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+  {
+    void* ptr = &::tutorial::_AddressBook_default_instance_;
+    new (ptr) ::tutorial::AddressBook();
+    ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr);
+  }
+  ::tutorial::AddressBook::InitAsDefaultInstance();
+}
+
+::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_AddressBook_addressbook_2eproto =
+    {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 1, 0, InitDefaultsscc_info_AddressBook_addressbook_2eproto}, {
+      &scc_info_Person_addressbook_2eproto.base,}};
+
+static void InitDefaultsscc_info_Person_addressbook_2eproto() {
+  GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+  {
+    void* ptr = &::tutorial::_Person_default_instance_;
+    new (ptr) ::tutorial::Person();
+    ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr);
+  }
+  ::tutorial::Person::InitAsDefaultInstance();
+}
+
+::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_Person_addressbook_2eproto =
+    {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 2, 0, InitDefaultsscc_info_Person_addressbook_2eproto}, {
+      &scc_info_Person_PhoneNumber_addressbook_2eproto.base,
+      &scc_info_Timestamp_google_2fprotobuf_2ftimestamp_2eproto.base,}};
+
+static void InitDefaultsscc_info_Person_PhoneNumber_addressbook_2eproto() {
+  GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+  {
+    void* ptr = &::tutorial::_Person_PhoneNumber_default_instance_;
+    new (ptr) ::tutorial::Person_PhoneNumber();
+    ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr);
+  }
+  ::tutorial::Person_PhoneNumber::InitAsDefaultInstance();
+}
+
+::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_Person_PhoneNumber_addressbook_2eproto =
+    {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, 0, InitDefaultsscc_info_Person_PhoneNumber_addressbook_2eproto}, {}};
+
+static ::PROTOBUF_NAMESPACE_ID::Metadata file_level_metadata_addressbook_2eproto[3];
+static const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* file_level_enum_descriptors_addressbook_2eproto[1];
+static constexpr ::PROTOBUF_NAMESPACE_ID::ServiceDescriptor const** file_level_service_descriptors_addressbook_2eproto = nullptr;
+
+const ::PROTOBUF_NAMESPACE_ID::uint32 TableStruct_addressbook_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::tutorial::Person_PhoneNumber, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  PROTOBUF_FIELD_OFFSET(::tutorial::Person_PhoneNumber, number_),
+  PROTOBUF_FIELD_OFFSET(::tutorial::Person_PhoneNumber, type_),
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::tutorial::Person, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  PROTOBUF_FIELD_OFFSET(::tutorial::Person, name_),
+  PROTOBUF_FIELD_OFFSET(::tutorial::Person, id_),
+  PROTOBUF_FIELD_OFFSET(::tutorial::Person, email_),
+  PROTOBUF_FIELD_OFFSET(::tutorial::Person, phones_),
+  PROTOBUF_FIELD_OFFSET(::tutorial::Person, last_updated_),
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::tutorial::AddressBook, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  PROTOBUF_FIELD_OFFSET(::tutorial::AddressBook, people_),
+};
+static const ::PROTOBUF_NAMESPACE_ID::internal::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+  { 0, -1, sizeof(::tutorial::Person_PhoneNumber)},
+  { 7, -1, sizeof(::tutorial::Person)},
+  { 17, -1, sizeof(::tutorial::AddressBook)},
+};
+
+static ::PROTOBUF_NAMESPACE_ID::Message const * const file_default_instances[] = {
+  reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::Message*>(&::tutorial::_Person_PhoneNumber_default_instance_),
+  reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::Message*>(&::tutorial::_Person_default_instance_),
+  reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::Message*>(&::tutorial::_AddressBook_default_instance_),
+};
+
+const char descriptor_table_protodef_addressbook_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) =
+  "\n\021addressbook.proto\022\010tutorial\032\037google/pr"
+  "otobuf/timestamp.proto\"\207\002\n\006Person\022\014\n\004nam"
+  "e\030\001 \001(\t\022\n\n\002id\030\002 \001(\005\022\r\n\005email\030\003 \001(\t\022,\n\006ph"
+  "ones\030\004 \003(\0132\034.tutorial.Person.PhoneNumber"
+  "\0220\n\014last_updated\030\005 \001(\0132\032.google.protobuf"
+  ".Timestamp\032G\n\013PhoneNumber\022\016\n\006number\030\001 \001("
+  "\t\022(\n\004type\030\002 \001(\0162\032.tutorial.Person.PhoneT"
+  "ype\"+\n\tPhoneType\022\n\n\006MOBILE\020\000\022\010\n\004HOME\020\001\022\010"
+  "\n\004WORK\020\002\"/\n\013AddressBook\022 \n\006people\030\001 \003(\0132"
+  "\020.tutorial.PersonBP\n\024com.example.tutoria"
+  "lB\021AddressBookProtos\252\002$Google.Protobuf.E"
+  "xamples.AddressBookb\006proto3"
+  ;
+static const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable*const descriptor_table_addressbook_2eproto_deps[1] = {
+  &::descriptor_table_google_2fprotobuf_2ftimestamp_2eproto,
+};
+static ::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase*const descriptor_table_addressbook_2eproto_sccs[3] = {
+  &scc_info_AddressBook_addressbook_2eproto.base,
+  &scc_info_Person_addressbook_2eproto.base,
+  &scc_info_Person_PhoneNumber_addressbook_2eproto.base,
+};
+static ::PROTOBUF_NAMESPACE_ID::internal::once_flag descriptor_table_addressbook_2eproto_once;
+static bool descriptor_table_addressbook_2eproto_initialized = false;
+const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_addressbook_2eproto = {
+  &descriptor_table_addressbook_2eproto_initialized, descriptor_table_protodef_addressbook_2eproto, "addressbook.proto", 467,
+  &descriptor_table_addressbook_2eproto_once, descriptor_table_addressbook_2eproto_sccs, descriptor_table_addressbook_2eproto_deps, 3, 1,
+  schemas, file_default_instances, TableStruct_addressbook_2eproto::offsets,
+  file_level_metadata_addressbook_2eproto, 3, file_level_enum_descriptors_addressbook_2eproto, file_level_service_descriptors_addressbook_2eproto,
+};
+
+// Force running AddDescriptors() at dynamic initialization time.
+static bool dynamic_init_dummy_addressbook_2eproto = (  ::PROTOBUF_NAMESPACE_ID::internal::AddDescriptors(&descriptor_table_addressbook_2eproto), true);
+namespace tutorial {
+const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* Person_PhoneType_descriptor() {
+  ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_addressbook_2eproto);
+  return file_level_enum_descriptors_addressbook_2eproto[0];
+}
+bool Person_PhoneType_IsValid(int value) {
+  switch (value) {
+    case 0:
+    case 1:
+    case 2:
+      return true;
+    default:
+      return false;
+  }
+}
+
+#if (__cplusplus < 201703) && (!defined(_MSC_VER) || _MSC_VER >= 1900)
+constexpr Person_PhoneType Person::MOBILE;
+constexpr Person_PhoneType Person::HOME;
+constexpr Person_PhoneType Person::WORK;
+constexpr Person_PhoneType Person::PhoneType_MIN;
+constexpr Person_PhoneType Person::PhoneType_MAX;
+constexpr int Person::PhoneType_ARRAYSIZE;
+#endif  // (__cplusplus < 201703) && (!defined(_MSC_VER) || _MSC_VER >= 1900)
+
+// ===================================================================
+
+void Person_PhoneNumber::InitAsDefaultInstance() {
+}
+class Person_PhoneNumber::_Internal {
+ public:
+};
+
+Person_PhoneNumber::Person_PhoneNumber()
+  : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) {
+  SharedCtor();
+  // @@protoc_insertion_point(constructor:tutorial.Person.PhoneNumber)
+}
+Person_PhoneNumber::Person_PhoneNumber(const Person_PhoneNumber& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message(),
+      _internal_metadata_(nullptr) {
+  _internal_metadata_.MergeFrom(from._internal_metadata_);
+  number_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  if (!from._internal_number().empty()) {
+    number_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.number_);
+  }
+  type_ = from.type_;
+  // @@protoc_insertion_point(copy_constructor:tutorial.Person.PhoneNumber)
+}
+
+void Person_PhoneNumber::SharedCtor() {
+  ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_Person_PhoneNumber_addressbook_2eproto.base);
+  number_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  type_ = 0;
+}
+
+Person_PhoneNumber::~Person_PhoneNumber() {
+  // @@protoc_insertion_point(destructor:tutorial.Person.PhoneNumber)
+  SharedDtor();
+}
+
+void Person_PhoneNumber::SharedDtor() {
+  number_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+
+void Person_PhoneNumber::SetCachedSize(int size) const {
+  _cached_size_.Set(size);
+}
+const Person_PhoneNumber& Person_PhoneNumber::default_instance() {
+  ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_Person_PhoneNumber_addressbook_2eproto.base);
+  return *internal_default_instance();
+}
+
+
+void Person_PhoneNumber::Clear() {
+// @@protoc_insertion_point(message_clear_start:tutorial.Person.PhoneNumber)
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  number_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  type_ = 0;
+  _internal_metadata_.Clear();
+}
+
+const char* Person_PhoneNumber::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    ::PROTOBUF_NAMESPACE_ID::uint32 tag;
+    ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);
+    CHK_(ptr);
+    switch (tag >> 3) {
+      // string number = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) {
+          auto str = _internal_mutable_number();
+          ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "tutorial.Person.PhoneNumber.number"));
+          CHK_(ptr);
+        } else goto handle_unusual;
+        continue;
+      // .tutorial.Person.PhoneType type = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 16)) {
+          ::PROTOBUF_NAMESPACE_ID::uint64 val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+          CHK_(ptr);
+          _internal_set_type(static_cast<::tutorial::Person_PhoneType>(val));
+        } else goto handle_unusual;
+        continue;
+      default: {
+      handle_unusual:
+        if ((tag & 7) == 4 || tag == 0) {
+          ctx->SetLastTag(tag);
+          goto success;
+        }
+        ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx);
+        CHK_(ptr != nullptr);
+        continue;
+      }
+    }  // switch
+  }  // while
+success:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto success;
+#undef CHK_
+}
+
+::PROTOBUF_NAMESPACE_ID::uint8* Person_PhoneNumber::_InternalSerialize(
+    ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:tutorial.Person.PhoneNumber)
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // string number = 1;
+  if (this->number().size() > 0) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->_internal_number().data(), static_cast<int>(this->_internal_number().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "tutorial.Person.PhoneNumber.number");
+    target = stream->WriteStringMaybeAliased(
+        1, this->_internal_number(), target);
+  }
+
+  // .tutorial.Person.PhoneType type = 2;
+  if (this->type() != 0) {
+    target = stream->EnsureSpace(target);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnumToArray(
+      2, this->_internal_type(), target);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields(), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:tutorial.Person.PhoneNumber)
+  return target;
+}
+
+size_t Person_PhoneNumber::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:tutorial.Person.PhoneNumber)
+  size_t total_size = 0;
+
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // string number = 1;
+  if (this->number().size() > 0) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->_internal_number());
+  }
+
+  // .tutorial.Person.PhoneType type = 2;
+  if (this->type() != 0) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::EnumSize(this->_internal_type());
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    return ::PROTOBUF_NAMESPACE_ID::internal::ComputeUnknownFieldsSize(
+        _internal_metadata_, total_size, &_cached_size_);
+  }
+  int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);
+  SetCachedSize(cached_size);
+  return total_size;
+}
+
+void Person_PhoneNumber::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {
+// @@protoc_insertion_point(generalized_merge_from_start:tutorial.Person.PhoneNumber)
+  GOOGLE_DCHECK_NE(&from, this);
+  const Person_PhoneNumber* source =
+      ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated<Person_PhoneNumber>(
+          &from);
+  if (source == nullptr) {
+  // @@protoc_insertion_point(generalized_merge_from_cast_fail:tutorial.Person.PhoneNumber)
+    ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this);
+  } else {
+  // @@protoc_insertion_point(generalized_merge_from_cast_success:tutorial.Person.PhoneNumber)
+    MergeFrom(*source);
+  }
+}
+
+void Person_PhoneNumber::MergeFrom(const Person_PhoneNumber& from) {
+// @@protoc_insertion_point(class_specific_merge_from_start:tutorial.Person.PhoneNumber)
+  GOOGLE_DCHECK_NE(&from, this);
+  _internal_metadata_.MergeFrom(from._internal_metadata_);
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  if (from.number().size() > 0) {
+
+    number_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.number_);
+  }
+  if (from.type() != 0) {
+    _internal_set_type(from._internal_type());
+  }
+}
+
+void Person_PhoneNumber::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {
+// @@protoc_insertion_point(generalized_copy_from_start:tutorial.Person.PhoneNumber)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+void Person_PhoneNumber::CopyFrom(const Person_PhoneNumber& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:tutorial.Person.PhoneNumber)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool Person_PhoneNumber::IsInitialized() const {
+  return true;
+}
+
+void Person_PhoneNumber::InternalSwap(Person_PhoneNumber* other) {
+  using std::swap;
+  _internal_metadata_.Swap(&other->_internal_metadata_);
+  number_.Swap(&other->number_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+    GetArenaNoVirtual());
+  swap(type_, other->type_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata Person_PhoneNumber::GetMetadata() const {
+  return GetMetadataStatic();
+}
+
+
+// ===================================================================
+
+void Person::InitAsDefaultInstance() {
+  ::tutorial::_Person_default_instance_._instance.get_mutable()->last_updated_ = const_cast< PROTOBUF_NAMESPACE_ID::Timestamp*>(
+      PROTOBUF_NAMESPACE_ID::Timestamp::internal_default_instance());
+}
+class Person::_Internal {
+ public:
+  static const PROTOBUF_NAMESPACE_ID::Timestamp& last_updated(const Person* msg);
+};
+
+const PROTOBUF_NAMESPACE_ID::Timestamp&
+Person::_Internal::last_updated(const Person* msg) {
+  return *msg->last_updated_;
+}
+void Person::clear_last_updated() {
+  if (GetArenaNoVirtual() == nullptr && last_updated_ != nullptr) {
+    delete last_updated_;
+  }
+  last_updated_ = nullptr;
+}
+Person::Person()
+  : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) {
+  SharedCtor();
+  // @@protoc_insertion_point(constructor:tutorial.Person)
+}
+Person::Person(const Person& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message(),
+      _internal_metadata_(nullptr),
+      phones_(from.phones_) {
+  _internal_metadata_.MergeFrom(from._internal_metadata_);
+  name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  if (!from._internal_name().empty()) {
+    name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.name_);
+  }
+  email_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  if (!from._internal_email().empty()) {
+    email_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.email_);
+  }
+  if (from._internal_has_last_updated()) {
+    last_updated_ = new PROTOBUF_NAMESPACE_ID::Timestamp(*from.last_updated_);
+  } else {
+    last_updated_ = nullptr;
+  }
+  id_ = from.id_;
+  // @@protoc_insertion_point(copy_constructor:tutorial.Person)
+}
+
+void Person::SharedCtor() {
+  ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_Person_addressbook_2eproto.base);
+  name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  email_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  ::memset(&last_updated_, 0, static_cast<size_t>(
+      reinterpret_cast<char*>(&id_) -
+      reinterpret_cast<char*>(&last_updated_)) + sizeof(id_));
+}
+
+Person::~Person() {
+  // @@protoc_insertion_point(destructor:tutorial.Person)
+  SharedDtor();
+}
+
+void Person::SharedDtor() {
+  name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  email_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  if (this != internal_default_instance()) delete last_updated_;
+}
+
+void Person::SetCachedSize(int size) const {
+  _cached_size_.Set(size);
+}
+const Person& Person::default_instance() {
+  ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_Person_addressbook_2eproto.base);
+  return *internal_default_instance();
+}
+
+
+void Person::Clear() {
+// @@protoc_insertion_point(message_clear_start:tutorial.Person)
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  phones_.Clear();
+  name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  email_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  if (GetArenaNoVirtual() == nullptr && last_updated_ != nullptr) {
+    delete last_updated_;
+  }
+  last_updated_ = nullptr;
+  id_ = 0;
+  _internal_metadata_.Clear();
+}
+
+const char* Person::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    ::PROTOBUF_NAMESPACE_ID::uint32 tag;
+    ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);
+    CHK_(ptr);
+    switch (tag >> 3) {
+      // string name = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) {
+          auto str = _internal_mutable_name();
+          ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "tutorial.Person.name"));
+          CHK_(ptr);
+        } else goto handle_unusual;
+        continue;
+      // int32 id = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 16)) {
+          id_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+          CHK_(ptr);
+        } else goto handle_unusual;
+        continue;
+      // string email = 3;
+      case 3:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 26)) {
+          auto str = _internal_mutable_email();
+          ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "tutorial.Person.email"));
+          CHK_(ptr);
+        } else goto handle_unusual;
+        continue;
+      // repeated .tutorial.Person.PhoneNumber phones = 4;
+      case 4:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 34)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_phones(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<34>(ptr));
+        } else goto handle_unusual;
+        continue;
+      // .google.protobuf.Timestamp last_updated = 5;
+      case 5:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 42)) {
+          ptr = ctx->ParseMessage(_internal_mutable_last_updated(), ptr);
+          CHK_(ptr);
+        } else goto handle_unusual;
+        continue;
+      default: {
+      handle_unusual:
+        if ((tag & 7) == 4 || tag == 0) {
+          ctx->SetLastTag(tag);
+          goto success;
+        }
+        ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx);
+        CHK_(ptr != nullptr);
+        continue;
+      }
+    }  // switch
+  }  // while
+success:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto success;
+#undef CHK_
+}
+
+::PROTOBUF_NAMESPACE_ID::uint8* Person::_InternalSerialize(
+    ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:tutorial.Person)
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // string name = 1;
+  if (this->name().size() > 0) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->_internal_name().data(), static_cast<int>(this->_internal_name().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "tutorial.Person.name");
+    target = stream->WriteStringMaybeAliased(
+        1, this->_internal_name(), target);
+  }
+
+  // int32 id = 2;
+  if (this->id() != 0) {
+    target = stream->EnsureSpace(target);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(2, this->_internal_id(), target);
+  }
+
+  // string email = 3;
+  if (this->email().size() > 0) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->_internal_email().data(), static_cast<int>(this->_internal_email().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "tutorial.Person.email");
+    target = stream->WriteStringMaybeAliased(
+        3, this->_internal_email(), target);
+  }
+
+  // repeated .tutorial.Person.PhoneNumber phones = 4;
+  for (unsigned int i = 0,
+      n = static_cast<unsigned int>(this->_internal_phones_size()); i < n; i++) {
+    target = stream->EnsureSpace(target);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+      InternalWriteMessage(4, this->_internal_phones(i), target, stream);
+  }
+
+  // .google.protobuf.Timestamp last_updated = 5;
+  if (this->has_last_updated()) {
+    target = stream->EnsureSpace(target);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+      InternalWriteMessage(
+        5, _Internal::last_updated(this), target, stream);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields(), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:tutorial.Person)
+  return target;
+}
+
+size_t Person::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:tutorial.Person)
+  size_t total_size = 0;
+
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .tutorial.Person.PhoneNumber phones = 4;
+  total_size += 1UL * this->_internal_phones_size();
+  for (const auto& msg : this->phones_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  // string name = 1;
+  if (this->name().size() > 0) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->_internal_name());
+  }
+
+  // string email = 3;
+  if (this->email().size() > 0) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->_internal_email());
+  }
+
+  // .google.protobuf.Timestamp last_updated = 5;
+  if (this->has_last_updated()) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+        *last_updated_);
+  }
+
+  // int32 id = 2;
+  if (this->id() != 0) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(
+        this->_internal_id());
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    return ::PROTOBUF_NAMESPACE_ID::internal::ComputeUnknownFieldsSize(
+        _internal_metadata_, total_size, &_cached_size_);
+  }
+  int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);
+  SetCachedSize(cached_size);
+  return total_size;
+}
+
+void Person::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {
+// @@protoc_insertion_point(generalized_merge_from_start:tutorial.Person)
+  GOOGLE_DCHECK_NE(&from, this);
+  const Person* source =
+      ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated<Person>(
+          &from);
+  if (source == nullptr) {
+  // @@protoc_insertion_point(generalized_merge_from_cast_fail:tutorial.Person)
+    ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this);
+  } else {
+  // @@protoc_insertion_point(generalized_merge_from_cast_success:tutorial.Person)
+    MergeFrom(*source);
+  }
+}
+
+void Person::MergeFrom(const Person& from) {
+// @@protoc_insertion_point(class_specific_merge_from_start:tutorial.Person)
+  GOOGLE_DCHECK_NE(&from, this);
+  _internal_metadata_.MergeFrom(from._internal_metadata_);
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  phones_.MergeFrom(from.phones_);
+  if (from.name().size() > 0) {
+
+    name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.name_);
+  }
+  if (from.email().size() > 0) {
+
+    email_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.email_);
+  }
+  if (from.has_last_updated()) {
+    _internal_mutable_last_updated()->PROTOBUF_NAMESPACE_ID::Timestamp::MergeFrom(from._internal_last_updated());
+  }
+  if (from.id() != 0) {
+    _internal_set_id(from._internal_id());
+  }
+}
+
+void Person::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {
+// @@protoc_insertion_point(generalized_copy_from_start:tutorial.Person)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+void Person::CopyFrom(const Person& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:tutorial.Person)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool Person::IsInitialized() const {
+  return true;
+}
+
+void Person::InternalSwap(Person* other) {
+  using std::swap;
+  _internal_metadata_.Swap(&other->_internal_metadata_);
+  phones_.InternalSwap(&other->phones_);
+  name_.Swap(&other->name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+    GetArenaNoVirtual());
+  email_.Swap(&other->email_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+    GetArenaNoVirtual());
+  swap(last_updated_, other->last_updated_);
+  swap(id_, other->id_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata Person::GetMetadata() const {
+  return GetMetadataStatic();
+}
+
+
+// ===================================================================
+
+void AddressBook::InitAsDefaultInstance() {
+}
+class AddressBook::_Internal {
+ public:
+};
+
+AddressBook::AddressBook()
+  : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) {
+  SharedCtor();
+  // @@protoc_insertion_point(constructor:tutorial.AddressBook)
+}
+AddressBook::AddressBook(const AddressBook& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message(),
+      _internal_metadata_(nullptr),
+      people_(from.people_) {
+  _internal_metadata_.MergeFrom(from._internal_metadata_);
+  // @@protoc_insertion_point(copy_constructor:tutorial.AddressBook)
+}
+
+void AddressBook::SharedCtor() {
+  ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_AddressBook_addressbook_2eproto.base);
+}
+
+AddressBook::~AddressBook() {
+  // @@protoc_insertion_point(destructor:tutorial.AddressBook)
+  SharedDtor();
+}
+
+void AddressBook::SharedDtor() {
+}
+
+void AddressBook::SetCachedSize(int size) const {
+  _cached_size_.Set(size);
+}
+const AddressBook& AddressBook::default_instance() {
+  ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_AddressBook_addressbook_2eproto.base);
+  return *internal_default_instance();
+}
+
+
+void AddressBook::Clear() {
+// @@protoc_insertion_point(message_clear_start:tutorial.AddressBook)
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  people_.Clear();
+  _internal_metadata_.Clear();
+}
+
+const char* AddressBook::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    ::PROTOBUF_NAMESPACE_ID::uint32 tag;
+    ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);
+    CHK_(ptr);
+    switch (tag >> 3) {
+      // repeated .tutorial.Person people = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(_internal_add_people(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<10>(ptr));
+        } else goto handle_unusual;
+        continue;
+      default: {
+      handle_unusual:
+        if ((tag & 7) == 4 || tag == 0) {
+          ctx->SetLastTag(tag);
+          goto success;
+        }
+        ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx);
+        CHK_(ptr != nullptr);
+        continue;
+      }
+    }  // switch
+  }  // while
+success:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto success;
+#undef CHK_
+}
+
+::PROTOBUF_NAMESPACE_ID::uint8* AddressBook::_InternalSerialize(
+    ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+  // @@protoc_insertion_point(serialize_to_array_start:tutorial.AddressBook)
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // repeated .tutorial.Person people = 1;
+  for (unsigned int i = 0,
+      n = static_cast<unsigned int>(this->_internal_people_size()); i < n; i++) {
+    target = stream->EnsureSpace(target);
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+      InternalWriteMessage(1, this->_internal_people(i), target, stream);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields(), target, stream);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:tutorial.AddressBook)
+  return target;
+}
+
+size_t AddressBook::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:tutorial.AddressBook)
+  size_t total_size = 0;
+
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .tutorial.Person people = 1;
+  total_size += 1UL * this->_internal_people_size();
+  for (const auto& msg : this->people_) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+  }
+
+  if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+    return ::PROTOBUF_NAMESPACE_ID::internal::ComputeUnknownFieldsSize(
+        _internal_metadata_, total_size, &_cached_size_);
+  }
+  int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);
+  SetCachedSize(cached_size);
+  return total_size;
+}
+
+void AddressBook::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {
+// @@protoc_insertion_point(generalized_merge_from_start:tutorial.AddressBook)
+  GOOGLE_DCHECK_NE(&from, this);
+  const AddressBook* source =
+      ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated<AddressBook>(
+          &from);
+  if (source == nullptr) {
+  // @@protoc_insertion_point(generalized_merge_from_cast_fail:tutorial.AddressBook)
+    ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this);
+  } else {
+  // @@protoc_insertion_point(generalized_merge_from_cast_success:tutorial.AddressBook)
+    MergeFrom(*source);
+  }
+}
+
+void AddressBook::MergeFrom(const AddressBook& from) {
+// @@protoc_insertion_point(class_specific_merge_from_start:tutorial.AddressBook)
+  GOOGLE_DCHECK_NE(&from, this);
+  _internal_metadata_.MergeFrom(from._internal_metadata_);
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  people_.MergeFrom(from.people_);
+}
+
+void AddressBook::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {
+// @@protoc_insertion_point(generalized_copy_from_start:tutorial.AddressBook)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+void AddressBook::CopyFrom(const AddressBook& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:tutorial.AddressBook)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool AddressBook::IsInitialized() const {
+  return true;
+}
+
+void AddressBook::InternalSwap(AddressBook* other) {
+  using std::swap;
+  _internal_metadata_.Swap(&other->_internal_metadata_);
+  people_.InternalSwap(&other->people_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata AddressBook::GetMetadata() const {
+  return GetMetadataStatic();
+}
+
+
+// @@protoc_insertion_point(namespace_scope)
+}  // namespace tutorial
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_NOINLINE ::tutorial::Person_PhoneNumber* Arena::CreateMaybeMessage< ::tutorial::Person_PhoneNumber >(Arena* arena) {
+  return Arena::CreateInternal< ::tutorial::Person_PhoneNumber >(arena);
+}
+template<> PROTOBUF_NOINLINE ::tutorial::Person* Arena::CreateMaybeMessage< ::tutorial::Person >(Arena* arena) {
+  return Arena::CreateInternal< ::tutorial::Person >(arena);
+}
+template<> PROTOBUF_NOINLINE ::tutorial::AddressBook* Arena::CreateMaybeMessage< ::tutorial::AddressBook >(Arena* arena) {
+  return Arena::CreateInternal< ::tutorial::AddressBook >(arena);
+}
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+#include <google/protobuf/port_undef.inc>
diff --git a/recipes/protobuf/all/test_package/addressbook.3.11.4.pb.h b/recipes/protobuf/all/test_package/addressbook.3.11.4.pb.h
new file mode 100644
--- /dev/null
+++ b/recipes/protobuf/all/test_package/addressbook.3.11.4.pb.h
@@ -0,0 +1,1018 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: addressbook.proto
+
+#ifndef GOOGLE_PROTOBUF_INCLUDED_addressbook_2eproto
+#define GOOGLE_PROTOBUF_INCLUDED_addressbook_2eproto
+
+#include <limits>
+#include <string>
+
+#include <google/protobuf/port_def.inc>
+#if PROTOBUF_VERSION < 3011000
+#error This file was generated by a newer version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please update
+#error your headers.
+#endif
+#if 3011004 < PROTOBUF_MIN_PROTOC_VERSION
+#error This file was generated by an older version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please
+#error regenerate this file with a newer version of protoc.
+#endif
+
+#include <google/protobuf/port_undef.inc>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/generated_message_table_driven.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/inlined_string_field.h>
+#include <google/protobuf/metadata.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/repeated_field.h>  // IWYU pragma: export
+#include <google/protobuf/extension_set.h>  // IWYU pragma: export
+#include <google/protobuf/generated_enum_reflection.h>
+#include <google/protobuf/unknown_field_set.h>
+#include <google/protobuf/timestamp.pb.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+#define PROTOBUF_INTERNAL_EXPORT_addressbook_2eproto
+PROTOBUF_NAMESPACE_OPEN
+namespace internal {
+class AnyMetadata;
+}  // namespace internal
+PROTOBUF_NAMESPACE_CLOSE
+
+// Internal implementation detail -- do not use these members.
+struct TableStruct_addressbook_2eproto {
+  static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTableField entries[]
+    PROTOBUF_SECTION_VARIABLE(protodesc_cold);
+  static const ::PROTOBUF_NAMESPACE_ID::internal::AuxillaryParseTableField aux[]
+    PROTOBUF_SECTION_VARIABLE(protodesc_cold);
+  static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTable schema[3]
+    PROTOBUF_SECTION_VARIABLE(protodesc_cold);
+  static const ::PROTOBUF_NAMESPACE_ID::internal::FieldMetadata field_metadata[];
+  static const ::PROTOBUF_NAMESPACE_ID::internal::SerializationTable serialization_table[];
+  static const ::PROTOBUF_NAMESPACE_ID::uint32 offsets[];
+};
+extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_addressbook_2eproto;
+namespace tutorial {
+class AddressBook;
+class AddressBookDefaultTypeInternal;
+extern AddressBookDefaultTypeInternal _AddressBook_default_instance_;
+class Person;
+class PersonDefaultTypeInternal;
+extern PersonDefaultTypeInternal _Person_default_instance_;
+class Person_PhoneNumber;
+class Person_PhoneNumberDefaultTypeInternal;
+extern Person_PhoneNumberDefaultTypeInternal _Person_PhoneNumber_default_instance_;
+}  // namespace tutorial
+PROTOBUF_NAMESPACE_OPEN
+template<> ::tutorial::AddressBook* Arena::CreateMaybeMessage<::tutorial::AddressBook>(Arena*);
+template<> ::tutorial::Person* Arena::CreateMaybeMessage<::tutorial::Person>(Arena*);
+template<> ::tutorial::Person_PhoneNumber* Arena::CreateMaybeMessage<::tutorial::Person_PhoneNumber>(Arena*);
+PROTOBUF_NAMESPACE_CLOSE
+namespace tutorial {
+
+enum Person_PhoneType : int {
+  Person_PhoneType_MOBILE = 0,
+  Person_PhoneType_HOME = 1,
+  Person_PhoneType_WORK = 2,
+  Person_PhoneType_Person_PhoneType_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::min(),
+  Person_PhoneType_Person_PhoneType_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::max()
+};
+bool Person_PhoneType_IsValid(int value);
+constexpr Person_PhoneType Person_PhoneType_PhoneType_MIN = Person_PhoneType_MOBILE;
+constexpr Person_PhoneType Person_PhoneType_PhoneType_MAX = Person_PhoneType_WORK;
+constexpr int Person_PhoneType_PhoneType_ARRAYSIZE = Person_PhoneType_PhoneType_MAX + 1;
+
+const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* Person_PhoneType_descriptor();
+template<typename T>
+inline const std::string& Person_PhoneType_Name(T enum_t_value) {
+  static_assert(::std::is_same<T, Person_PhoneType>::value ||
+    ::std::is_integral<T>::value,
+    "Incorrect type passed to function Person_PhoneType_Name.");
+  return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum(
+    Person_PhoneType_descriptor(), enum_t_value);
+}
+inline bool Person_PhoneType_Parse(
+    const std::string& name, Person_PhoneType* value) {
+  return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum<Person_PhoneType>(
+    Person_PhoneType_descriptor(), name, value);
+}
+// ===================================================================
+
+class Person_PhoneNumber :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:tutorial.Person.PhoneNumber) */ {
+ public:
+  Person_PhoneNumber();
+  virtual ~Person_PhoneNumber();
+
+  Person_PhoneNumber(const Person_PhoneNumber& from);
+  Person_PhoneNumber(Person_PhoneNumber&& from) noexcept
+    : Person_PhoneNumber() {
+    *this = ::std::move(from);
+  }
+
+  inline Person_PhoneNumber& operator=(const Person_PhoneNumber& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline Person_PhoneNumber& operator=(Person_PhoneNumber&& from) noexcept {
+    if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) {
+      if (this != &from) InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return GetMetadataStatic().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return GetMetadataStatic().reflection;
+  }
+  static const Person_PhoneNumber& default_instance();
+
+  static void InitAsDefaultInstance();  // FOR INTERNAL USE ONLY
+  static inline const Person_PhoneNumber* internal_default_instance() {
+    return reinterpret_cast<const Person_PhoneNumber*>(
+               &_Person_PhoneNumber_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    0;
+
+  friend void swap(Person_PhoneNumber& a, Person_PhoneNumber& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(Person_PhoneNumber* other) {
+    if (other == this) return;
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  inline Person_PhoneNumber* New() const final {
+    return CreateMaybeMessage<Person_PhoneNumber>(nullptr);
+  }
+
+  Person_PhoneNumber* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {
+    return CreateMaybeMessage<Person_PhoneNumber>(arena);
+  }
+  void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;
+  void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;
+  void CopyFrom(const Person_PhoneNumber& from);
+  void MergeFrom(const Person_PhoneNumber& from);
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize(
+      ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _cached_size_.Get(); }
+
+  private:
+  inline void SharedCtor();
+  inline void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(Person_PhoneNumber* other);
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "tutorial.Person.PhoneNumber";
+  }
+  private:
+  inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const {
+    return nullptr;
+  }
+  inline void* MaybeArenaPtr() const {
+    return nullptr;
+  }
+  public:
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+  private:
+  static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() {
+    ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_addressbook_2eproto);
+    return ::descriptor_table_addressbook_2eproto.file_level_metadata[kIndexInFileMessages];
+  }
+
+  public:
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kNumberFieldNumber = 1,
+    kTypeFieldNumber = 2,
+  };
+  // string number = 1;
+  void clear_number();
+  const std::string& number() const;
+  void set_number(const std::string& value);
+  void set_number(std::string&& value);
+  void set_number(const char* value);
+  void set_number(const char* value, size_t size);
+  std::string* mutable_number();
+  std::string* release_number();
+  void set_allocated_number(std::string* number);
+  private:
+  const std::string& _internal_number() const;
+  void _internal_set_number(const std::string& value);
+  std::string* _internal_mutable_number();
+  public:
+
+  // .tutorial.Person.PhoneType type = 2;
+  void clear_type();
+  ::tutorial::Person_PhoneType type() const;
+  void set_type(::tutorial::Person_PhoneType value);
+  private:
+  ::tutorial::Person_PhoneType _internal_type() const;
+  void _internal_set_type(::tutorial::Person_PhoneType value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:tutorial.Person.PhoneNumber)
+ private:
+  class _Internal;
+
+  ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_;
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr number_;
+  int type_;
+  mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  friend struct ::TableStruct_addressbook_2eproto;
+};
+// -------------------------------------------------------------------
+
+class Person :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:tutorial.Person) */ {
+ public:
+  Person();
+  virtual ~Person();
+
+  Person(const Person& from);
+  Person(Person&& from) noexcept
+    : Person() {
+    *this = ::std::move(from);
+  }
+
+  inline Person& operator=(const Person& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline Person& operator=(Person&& from) noexcept {
+    if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) {
+      if (this != &from) InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return GetMetadataStatic().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return GetMetadataStatic().reflection;
+  }
+  static const Person& default_instance();
+
+  static void InitAsDefaultInstance();  // FOR INTERNAL USE ONLY
+  static inline const Person* internal_default_instance() {
+    return reinterpret_cast<const Person*>(
+               &_Person_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    1;
+
+  friend void swap(Person& a, Person& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(Person* other) {
+    if (other == this) return;
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  inline Person* New() const final {
+    return CreateMaybeMessage<Person>(nullptr);
+  }
+
+  Person* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {
+    return CreateMaybeMessage<Person>(arena);
+  }
+  void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;
+  void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;
+  void CopyFrom(const Person& from);
+  void MergeFrom(const Person& from);
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize(
+      ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _cached_size_.Get(); }
+
+  private:
+  inline void SharedCtor();
+  inline void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(Person* other);
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "tutorial.Person";
+  }
+  private:
+  inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const {
+    return nullptr;
+  }
+  inline void* MaybeArenaPtr() const {
+    return nullptr;
+  }
+  public:
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+  private:
+  static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() {
+    ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_addressbook_2eproto);
+    return ::descriptor_table_addressbook_2eproto.file_level_metadata[kIndexInFileMessages];
+  }
+
+  public:
+
+  // nested types ----------------------------------------------------
+
+  typedef Person_PhoneNumber PhoneNumber;
+
+  typedef Person_PhoneType PhoneType;
+  static constexpr PhoneType MOBILE =
+    Person_PhoneType_MOBILE;
+  static constexpr PhoneType HOME =
+    Person_PhoneType_HOME;
+  static constexpr PhoneType WORK =
+    Person_PhoneType_WORK;
+  static inline bool PhoneType_IsValid(int value) {
+    return Person_PhoneType_IsValid(value);
+  }
+  static constexpr PhoneType PhoneType_MIN =
+    Person_PhoneType_PhoneType_MIN;
+  static constexpr PhoneType PhoneType_MAX =
+    Person_PhoneType_PhoneType_MAX;
+  static constexpr int PhoneType_ARRAYSIZE =
+    Person_PhoneType_PhoneType_ARRAYSIZE;
+  static inline const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor*
+  PhoneType_descriptor() {
+    return Person_PhoneType_descriptor();
+  }
+  template<typename T>
+  static inline const std::string& PhoneType_Name(T enum_t_value) {
+    static_assert(::std::is_same<T, PhoneType>::value ||
+      ::std::is_integral<T>::value,
+      "Incorrect type passed to function PhoneType_Name.");
+    return Person_PhoneType_Name(enum_t_value);
+  }
+  static inline bool PhoneType_Parse(const std::string& name,
+      PhoneType* value) {
+    return Person_PhoneType_Parse(name, value);
+  }
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kPhonesFieldNumber = 4,
+    kNameFieldNumber = 1,
+    kEmailFieldNumber = 3,
+    kLastUpdatedFieldNumber = 5,
+    kIdFieldNumber = 2,
+  };
+  // repeated .tutorial.Person.PhoneNumber phones = 4;
+  int phones_size() const;
+  private:
+  int _internal_phones_size() const;
+  public:
+  void clear_phones();
+  ::tutorial::Person_PhoneNumber* mutable_phones(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person_PhoneNumber >*
+      mutable_phones();
+  private:
+  const ::tutorial::Person_PhoneNumber& _internal_phones(int index) const;
+  ::tutorial::Person_PhoneNumber* _internal_add_phones();
+  public:
+  const ::tutorial::Person_PhoneNumber& phones(int index) const;
+  ::tutorial::Person_PhoneNumber* add_phones();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person_PhoneNumber >&
+      phones() const;
+
+  // string name = 1;
+  void clear_name();
+  const std::string& name() const;
+  void set_name(const std::string& value);
+  void set_name(std::string&& value);
+  void set_name(const char* value);
+  void set_name(const char* value, size_t size);
+  std::string* mutable_name();
+  std::string* release_name();
+  void set_allocated_name(std::string* name);
+  private:
+  const std::string& _internal_name() const;
+  void _internal_set_name(const std::string& value);
+  std::string* _internal_mutable_name();
+  public:
+
+  // string email = 3;
+  void clear_email();
+  const std::string& email() const;
+  void set_email(const std::string& value);
+  void set_email(std::string&& value);
+  void set_email(const char* value);
+  void set_email(const char* value, size_t size);
+  std::string* mutable_email();
+  std::string* release_email();
+  void set_allocated_email(std::string* email);
+  private:
+  const std::string& _internal_email() const;
+  void _internal_set_email(const std::string& value);
+  std::string* _internal_mutable_email();
+  public:
+
+  // .google.protobuf.Timestamp last_updated = 5;
+  bool has_last_updated() const;
+  private:
+  bool _internal_has_last_updated() const;
+  public:
+  void clear_last_updated();
+  const PROTOBUF_NAMESPACE_ID::Timestamp& last_updated() const;
+  PROTOBUF_NAMESPACE_ID::Timestamp* release_last_updated();
+  PROTOBUF_NAMESPACE_ID::Timestamp* mutable_last_updated();
+  void set_allocated_last_updated(PROTOBUF_NAMESPACE_ID::Timestamp* last_updated);
+  private:
+  const PROTOBUF_NAMESPACE_ID::Timestamp& _internal_last_updated() const;
+  PROTOBUF_NAMESPACE_ID::Timestamp* _internal_mutable_last_updated();
+  public:
+
+  // int32 id = 2;
+  void clear_id();
+  ::PROTOBUF_NAMESPACE_ID::int32 id() const;
+  void set_id(::PROTOBUF_NAMESPACE_ID::int32 value);
+  private:
+  ::PROTOBUF_NAMESPACE_ID::int32 _internal_id() const;
+  void _internal_set_id(::PROTOBUF_NAMESPACE_ID::int32 value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:tutorial.Person)
+ private:
+  class _Internal;
+
+  ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_;
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person_PhoneNumber > phones_;
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_;
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr email_;
+  PROTOBUF_NAMESPACE_ID::Timestamp* last_updated_;
+  ::PROTOBUF_NAMESPACE_ID::int32 id_;
+  mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  friend struct ::TableStruct_addressbook_2eproto;
+};
+// -------------------------------------------------------------------
+
+class AddressBook :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:tutorial.AddressBook) */ {
+ public:
+  AddressBook();
+  virtual ~AddressBook();
+
+  AddressBook(const AddressBook& from);
+  AddressBook(AddressBook&& from) noexcept
+    : AddressBook() {
+    *this = ::std::move(from);
+  }
+
+  inline AddressBook& operator=(const AddressBook& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline AddressBook& operator=(AddressBook&& from) noexcept {
+    if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) {
+      if (this != &from) InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return GetMetadataStatic().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return GetMetadataStatic().reflection;
+  }
+  static const AddressBook& default_instance();
+
+  static void InitAsDefaultInstance();  // FOR INTERNAL USE ONLY
+  static inline const AddressBook* internal_default_instance() {
+    return reinterpret_cast<const AddressBook*>(
+               &_AddressBook_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    2;
+
+  friend void swap(AddressBook& a, AddressBook& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(AddressBook* other) {
+    if (other == this) return;
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  inline AddressBook* New() const final {
+    return CreateMaybeMessage<AddressBook>(nullptr);
+  }
+
+  AddressBook* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {
+    return CreateMaybeMessage<AddressBook>(arena);
+  }
+  void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;
+  void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;
+  void CopyFrom(const AddressBook& from);
+  void MergeFrom(const AddressBook& from);
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize(
+      ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _cached_size_.Get(); }
+
+  private:
+  inline void SharedCtor();
+  inline void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(AddressBook* other);
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "tutorial.AddressBook";
+  }
+  private:
+  inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const {
+    return nullptr;
+  }
+  inline void* MaybeArenaPtr() const {
+    return nullptr;
+  }
+  public:
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+  private:
+  static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() {
+    ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_addressbook_2eproto);
+    return ::descriptor_table_addressbook_2eproto.file_level_metadata[kIndexInFileMessages];
+  }
+
+  public:
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kPeopleFieldNumber = 1,
+  };
+  // repeated .tutorial.Person people = 1;
+  int people_size() const;
+  private:
+  int _internal_people_size() const;
+  public:
+  void clear_people();
+  ::tutorial::Person* mutable_people(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person >*
+      mutable_people();
+  private:
+  const ::tutorial::Person& _internal_people(int index) const;
+  ::tutorial::Person* _internal_add_people();
+  public:
+  const ::tutorial::Person& people(int index) const;
+  ::tutorial::Person* add_people();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person >&
+      people() const;
+
+  // @@protoc_insertion_point(class_scope:tutorial.AddressBook)
+ private:
+  class _Internal;
+
+  ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_;
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person > people_;
+  mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  friend struct ::TableStruct_addressbook_2eproto;
+};
+// ===================================================================
+
+
+// ===================================================================
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic push
+  #pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#endif  // __GNUC__
+// Person_PhoneNumber
+
+// string number = 1;
+inline void Person_PhoneNumber::clear_number() {
+  number_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline const std::string& Person_PhoneNumber::number() const {
+  // @@protoc_insertion_point(field_get:tutorial.Person.PhoneNumber.number)
+  return _internal_number();
+}
+inline void Person_PhoneNumber::set_number(const std::string& value) {
+  _internal_set_number(value);
+  // @@protoc_insertion_point(field_set:tutorial.Person.PhoneNumber.number)
+}
+inline std::string* Person_PhoneNumber::mutable_number() {
+  // @@protoc_insertion_point(field_mutable:tutorial.Person.PhoneNumber.number)
+  return _internal_mutable_number();
+}
+inline const std::string& Person_PhoneNumber::_internal_number() const {
+  return number_.GetNoArena();
+}
+inline void Person_PhoneNumber::_internal_set_number(const std::string& value) {
+  
+  number_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value);
+}
+inline void Person_PhoneNumber::set_number(std::string&& value) {
+  
+  number_.SetNoArena(
+    &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value));
+  // @@protoc_insertion_point(field_set_rvalue:tutorial.Person.PhoneNumber.number)
+}
+inline void Person_PhoneNumber::set_number(const char* value) {
+  GOOGLE_DCHECK(value != nullptr);
+  
+  number_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+  // @@protoc_insertion_point(field_set_char:tutorial.Person.PhoneNumber.number)
+}
+inline void Person_PhoneNumber::set_number(const char* value, size_t size) {
+  
+  number_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+      ::std::string(reinterpret_cast<const char*>(value), size));
+  // @@protoc_insertion_point(field_set_pointer:tutorial.Person.PhoneNumber.number)
+}
+inline std::string* Person_PhoneNumber::_internal_mutable_number() {
+  
+  return number_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline std::string* Person_PhoneNumber::release_number() {
+  // @@protoc_insertion_point(field_release:tutorial.Person.PhoneNumber.number)
+  
+  return number_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline void Person_PhoneNumber::set_allocated_number(std::string* number) {
+  if (number != nullptr) {
+    
+  } else {
+    
+  }
+  number_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), number);
+  // @@protoc_insertion_point(field_set_allocated:tutorial.Person.PhoneNumber.number)
+}
+
+// .tutorial.Person.PhoneType type = 2;
+inline void Person_PhoneNumber::clear_type() {
+  type_ = 0;
+}
+inline ::tutorial::Person_PhoneType Person_PhoneNumber::_internal_type() const {
+  return static_cast< ::tutorial::Person_PhoneType >(type_);
+}
+inline ::tutorial::Person_PhoneType Person_PhoneNumber::type() const {
+  // @@protoc_insertion_point(field_get:tutorial.Person.PhoneNumber.type)
+  return _internal_type();
+}
+inline void Person_PhoneNumber::_internal_set_type(::tutorial::Person_PhoneType value) {
+  
+  type_ = value;
+}
+inline void Person_PhoneNumber::set_type(::tutorial::Person_PhoneType value) {
+  _internal_set_type(value);
+  // @@protoc_insertion_point(field_set:tutorial.Person.PhoneNumber.type)
+}
+
+// -------------------------------------------------------------------
+
+// Person
+
+// string name = 1;
+inline void Person::clear_name() {
+  name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline const std::string& Person::name() const {
+  // @@protoc_insertion_point(field_get:tutorial.Person.name)
+  return _internal_name();
+}
+inline void Person::set_name(const std::string& value) {
+  _internal_set_name(value);
+  // @@protoc_insertion_point(field_set:tutorial.Person.name)
+}
+inline std::string* Person::mutable_name() {
+  // @@protoc_insertion_point(field_mutable:tutorial.Person.name)
+  return _internal_mutable_name();
+}
+inline const std::string& Person::_internal_name() const {
+  return name_.GetNoArena();
+}
+inline void Person::_internal_set_name(const std::string& value) {
+  
+  name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value);
+}
+inline void Person::set_name(std::string&& value) {
+  
+  name_.SetNoArena(
+    &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value));
+  // @@protoc_insertion_point(field_set_rvalue:tutorial.Person.name)
+}
+inline void Person::set_name(const char* value) {
+  GOOGLE_DCHECK(value != nullptr);
+  
+  name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+  // @@protoc_insertion_point(field_set_char:tutorial.Person.name)
+}
+inline void Person::set_name(const char* value, size_t size) {
+  
+  name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+      ::std::string(reinterpret_cast<const char*>(value), size));
+  // @@protoc_insertion_point(field_set_pointer:tutorial.Person.name)
+}
+inline std::string* Person::_internal_mutable_name() {
+  
+  return name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline std::string* Person::release_name() {
+  // @@protoc_insertion_point(field_release:tutorial.Person.name)
+  
+  return name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline void Person::set_allocated_name(std::string* name) {
+  if (name != nullptr) {
+    
+  } else {
+    
+  }
+  name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), name);
+  // @@protoc_insertion_point(field_set_allocated:tutorial.Person.name)
+}
+
+// int32 id = 2;
+inline void Person::clear_id() {
+  id_ = 0;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Person::_internal_id() const {
+  return id_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Person::id() const {
+  // @@protoc_insertion_point(field_get:tutorial.Person.id)
+  return _internal_id();
+}
+inline void Person::_internal_set_id(::PROTOBUF_NAMESPACE_ID::int32 value) {
+  
+  id_ = value;
+}
+inline void Person::set_id(::PROTOBUF_NAMESPACE_ID::int32 value) {
+  _internal_set_id(value);
+  // @@protoc_insertion_point(field_set:tutorial.Person.id)
+}
+
+// string email = 3;
+inline void Person::clear_email() {
+  email_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline const std::string& Person::email() const {
+  // @@protoc_insertion_point(field_get:tutorial.Person.email)
+  return _internal_email();
+}
+inline void Person::set_email(const std::string& value) {
+  _internal_set_email(value);
+  // @@protoc_insertion_point(field_set:tutorial.Person.email)
+}
+inline std::string* Person::mutable_email() {
+  // @@protoc_insertion_point(field_mutable:tutorial.Person.email)
+  return _internal_mutable_email();
+}
+inline const std::string& Person::_internal_email() const {
+  return email_.GetNoArena();
+}
+inline void Person::_internal_set_email(const std::string& value) {
+  
+  email_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value);
+}
+inline void Person::set_email(std::string&& value) {
+  
+  email_.SetNoArena(
+    &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value));
+  // @@protoc_insertion_point(field_set_rvalue:tutorial.Person.email)
+}
+inline void Person::set_email(const char* value) {
+  GOOGLE_DCHECK(value != nullptr);
+  
+  email_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+  // @@protoc_insertion_point(field_set_char:tutorial.Person.email)
+}
+inline void Person::set_email(const char* value, size_t size) {
+  
+  email_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+      ::std::string(reinterpret_cast<const char*>(value), size));
+  // @@protoc_insertion_point(field_set_pointer:tutorial.Person.email)
+}
+inline std::string* Person::_internal_mutable_email() {
+  
+  return email_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline std::string* Person::release_email() {
+  // @@protoc_insertion_point(field_release:tutorial.Person.email)
+  
+  return email_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline void Person::set_allocated_email(std::string* email) {
+  if (email != nullptr) {
+    
+  } else {
+    
+  }
+  email_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), email);
+  // @@protoc_insertion_point(field_set_allocated:tutorial.Person.email)
+}
+
+// repeated .tutorial.Person.PhoneNumber phones = 4;
+inline int Person::_internal_phones_size() const {
+  return phones_.size();
+}
+inline int Person::phones_size() const {
+  return _internal_phones_size();
+}
+inline void Person::clear_phones() {
+  phones_.Clear();
+}
+inline ::tutorial::Person_PhoneNumber* Person::mutable_phones(int index) {
+  // @@protoc_insertion_point(field_mutable:tutorial.Person.phones)
+  return phones_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person_PhoneNumber >*
+Person::mutable_phones() {
+  // @@protoc_insertion_point(field_mutable_list:tutorial.Person.phones)
+  return &phones_;
+}
+inline const ::tutorial::Person_PhoneNumber& Person::_internal_phones(int index) const {
+  return phones_.Get(index);
+}
+inline const ::tutorial::Person_PhoneNumber& Person::phones(int index) const {
+  // @@protoc_insertion_point(field_get:tutorial.Person.phones)
+  return _internal_phones(index);
+}
+inline ::tutorial::Person_PhoneNumber* Person::_internal_add_phones() {
+  return phones_.Add();
+}
+inline ::tutorial::Person_PhoneNumber* Person::add_phones() {
+  // @@protoc_insertion_point(field_add:tutorial.Person.phones)
+  return _internal_add_phones();
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person_PhoneNumber >&
+Person::phones() const {
+  // @@protoc_insertion_point(field_list:tutorial.Person.phones)
+  return phones_;
+}
+
+// .google.protobuf.Timestamp last_updated = 5;
+inline bool Person::_internal_has_last_updated() const {
+  return this != internal_default_instance() && last_updated_ != nullptr;
+}
+inline bool Person::has_last_updated() const {
+  return _internal_has_last_updated();
+}
+inline const PROTOBUF_NAMESPACE_ID::Timestamp& Person::_internal_last_updated() const {
+  const PROTOBUF_NAMESPACE_ID::Timestamp* p = last_updated_;
+  return p != nullptr ? *p : *reinterpret_cast<const PROTOBUF_NAMESPACE_ID::Timestamp*>(
+      &PROTOBUF_NAMESPACE_ID::_Timestamp_default_instance_);
+}
+inline const PROTOBUF_NAMESPACE_ID::Timestamp& Person::last_updated() const {
+  // @@protoc_insertion_point(field_get:tutorial.Person.last_updated)
+  return _internal_last_updated();
+}
+inline PROTOBUF_NAMESPACE_ID::Timestamp* Person::release_last_updated() {
+  // @@protoc_insertion_point(field_release:tutorial.Person.last_updated)
+  
+  PROTOBUF_NAMESPACE_ID::Timestamp* temp = last_updated_;
+  last_updated_ = nullptr;
+  return temp;
+}
+inline PROTOBUF_NAMESPACE_ID::Timestamp* Person::_internal_mutable_last_updated() {
+  
+  if (last_updated_ == nullptr) {
+    auto* p = CreateMaybeMessage<PROTOBUF_NAMESPACE_ID::Timestamp>(GetArenaNoVirtual());
+    last_updated_ = p;
+  }
+  return last_updated_;
+}
+inline PROTOBUF_NAMESPACE_ID::Timestamp* Person::mutable_last_updated() {
+  // @@protoc_insertion_point(field_mutable:tutorial.Person.last_updated)
+  return _internal_mutable_last_updated();
+}
+inline void Person::set_allocated_last_updated(PROTOBUF_NAMESPACE_ID::Timestamp* last_updated) {
+  ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual();
+  if (message_arena == nullptr) {
+    delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(last_updated_);
+  }
+  if (last_updated) {
+    ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+      reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(last_updated)->GetArena();
+    if (message_arena != submessage_arena) {
+      last_updated = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+          message_arena, last_updated, submessage_arena);
+    }
+    
+  } else {
+    
+  }
+  last_updated_ = last_updated;
+  // @@protoc_insertion_point(field_set_allocated:tutorial.Person.last_updated)
+}
+
+// -------------------------------------------------------------------
+
+// AddressBook
+
+// repeated .tutorial.Person people = 1;
+inline int AddressBook::_internal_people_size() const {
+  return people_.size();
+}
+inline int AddressBook::people_size() const {
+  return _internal_people_size();
+}
+inline void AddressBook::clear_people() {
+  people_.Clear();
+}
+inline ::tutorial::Person* AddressBook::mutable_people(int index) {
+  // @@protoc_insertion_point(field_mutable:tutorial.AddressBook.people)
+  return people_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person >*
+AddressBook::mutable_people() {
+  // @@protoc_insertion_point(field_mutable_list:tutorial.AddressBook.people)
+  return &people_;
+}
+inline const ::tutorial::Person& AddressBook::_internal_people(int index) const {
+  return people_.Get(index);
+}
+inline const ::tutorial::Person& AddressBook::people(int index) const {
+  // @@protoc_insertion_point(field_get:tutorial.AddressBook.people)
+  return _internal_people(index);
+}
+inline ::tutorial::Person* AddressBook::_internal_add_people() {
+  return people_.Add();
+}
+inline ::tutorial::Person* AddressBook::add_people() {
+  // @@protoc_insertion_point(field_add:tutorial.AddressBook.people)
+  return _internal_add_people();
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person >&
+AddressBook::people() const {
+  // @@protoc_insertion_point(field_list:tutorial.AddressBook.people)
+  return people_;
+}
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic pop
+#endif  // __GNUC__
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+
+// @@protoc_insertion_point(namespace_scope)
+
+}  // namespace tutorial
+
+PROTOBUF_NAMESPACE_OPEN
+
+template <> struct is_proto_enum< ::tutorial::Person_PhoneType> : ::std::true_type {};
+template <>
+inline const EnumDescriptor* GetEnumDescriptor< ::tutorial::Person_PhoneType>() {
+  return ::tutorial::Person_PhoneType_descriptor();
+}
+
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+
+#include <google/protobuf/port_undef.inc>
+#endif  // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_addressbook_2eproto
diff --git a/recipes/protobuf/all/test_package/addressbook.3.9.1.pb.cc b/recipes/protobuf/all/test_package/addressbook.3.9.1.pb.cc
new file mode 100644
--- /dev/null
+++ b/recipes/protobuf/all/test_package/addressbook.3.9.1.pb.cc
@@ -0,0 +1,1269 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: addressbook.proto
+
+#include "addressbook.pb.h"
+
+#include <algorithm>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/reflection_ops.h>
+#include <google/protobuf/wire_format.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+extern PROTOBUF_INTERNAL_EXPORT_google_2fprotobuf_2ftimestamp_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_Timestamp_google_2fprotobuf_2ftimestamp_2eproto;
+extern PROTOBUF_INTERNAL_EXPORT_addressbook_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_Person_addressbook_2eproto;
+extern PROTOBUF_INTERNAL_EXPORT_addressbook_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_Person_PhoneNumber_addressbook_2eproto;
+namespace tutorial {
+class Person_PhoneNumberDefaultTypeInternal {
+ public:
+  ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<Person_PhoneNumber> _instance;
+} _Person_PhoneNumber_default_instance_;
+class PersonDefaultTypeInternal {
+ public:
+  ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<Person> _instance;
+} _Person_default_instance_;
+class AddressBookDefaultTypeInternal {
+ public:
+  ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<AddressBook> _instance;
+} _AddressBook_default_instance_;
+}  // namespace tutorial
+static void InitDefaultsscc_info_AddressBook_addressbook_2eproto() {
+  GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+  {
+    void* ptr = &::tutorial::_AddressBook_default_instance_;
+    new (ptr) ::tutorial::AddressBook();
+    ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr);
+  }
+  ::tutorial::AddressBook::InitAsDefaultInstance();
+}
+
+::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_AddressBook_addressbook_2eproto =
+    {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 1, InitDefaultsscc_info_AddressBook_addressbook_2eproto}, {
+      &scc_info_Person_addressbook_2eproto.base,}};
+
+static void InitDefaultsscc_info_Person_addressbook_2eproto() {
+  GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+  {
+    void* ptr = &::tutorial::_Person_default_instance_;
+    new (ptr) ::tutorial::Person();
+    ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr);
+  }
+  ::tutorial::Person::InitAsDefaultInstance();
+}
+
+::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_Person_addressbook_2eproto =
+    {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 2, InitDefaultsscc_info_Person_addressbook_2eproto}, {
+      &scc_info_Person_PhoneNumber_addressbook_2eproto.base,
+      &scc_info_Timestamp_google_2fprotobuf_2ftimestamp_2eproto.base,}};
+
+static void InitDefaultsscc_info_Person_PhoneNumber_addressbook_2eproto() {
+  GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+  {
+    void* ptr = &::tutorial::_Person_PhoneNumber_default_instance_;
+    new (ptr) ::tutorial::Person_PhoneNumber();
+    ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr);
+  }
+  ::tutorial::Person_PhoneNumber::InitAsDefaultInstance();
+}
+
+::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_Person_PhoneNumber_addressbook_2eproto =
+    {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, InitDefaultsscc_info_Person_PhoneNumber_addressbook_2eproto}, {}};
+
+static ::PROTOBUF_NAMESPACE_ID::Metadata file_level_metadata_addressbook_2eproto[3];
+static const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* file_level_enum_descriptors_addressbook_2eproto[1];
+static constexpr ::PROTOBUF_NAMESPACE_ID::ServiceDescriptor const** file_level_service_descriptors_addressbook_2eproto = nullptr;
+
+const ::PROTOBUF_NAMESPACE_ID::uint32 TableStruct_addressbook_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::tutorial::Person_PhoneNumber, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  PROTOBUF_FIELD_OFFSET(::tutorial::Person_PhoneNumber, number_),
+  PROTOBUF_FIELD_OFFSET(::tutorial::Person_PhoneNumber, type_),
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::tutorial::Person, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  PROTOBUF_FIELD_OFFSET(::tutorial::Person, name_),
+  PROTOBUF_FIELD_OFFSET(::tutorial::Person, id_),
+  PROTOBUF_FIELD_OFFSET(::tutorial::Person, email_),
+  PROTOBUF_FIELD_OFFSET(::tutorial::Person, phones_),
+  PROTOBUF_FIELD_OFFSET(::tutorial::Person, last_updated_),
+  ~0u,  // no _has_bits_
+  PROTOBUF_FIELD_OFFSET(::tutorial::AddressBook, _internal_metadata_),
+  ~0u,  // no _extensions_
+  ~0u,  // no _oneof_case_
+  ~0u,  // no _weak_field_map_
+  PROTOBUF_FIELD_OFFSET(::tutorial::AddressBook, people_),
+};
+static const ::PROTOBUF_NAMESPACE_ID::internal::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
+  { 0, -1, sizeof(::tutorial::Person_PhoneNumber)},
+  { 7, -1, sizeof(::tutorial::Person)},
+  { 17, -1, sizeof(::tutorial::AddressBook)},
+};
+
+static ::PROTOBUF_NAMESPACE_ID::Message const * const file_default_instances[] = {
+  reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::Message*>(&::tutorial::_Person_PhoneNumber_default_instance_),
+  reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::Message*>(&::tutorial::_Person_default_instance_),
+  reinterpret_cast<const ::PROTOBUF_NAMESPACE_ID::Message*>(&::tutorial::_AddressBook_default_instance_),
+};
+
+const char descriptor_table_protodef_addressbook_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) =
+  "\n\021addressbook.proto\022\010tutorial\032\037google/pr"
+  "otobuf/timestamp.proto\"\207\002\n\006Person\022\014\n\004nam"
+  "e\030\001 \001(\t\022\n\n\002id\030\002 \001(\005\022\r\n\005email\030\003 \001(\t\022,\n\006ph"
+  "ones\030\004 \003(\0132\034.tutorial.Person.PhoneNumber"
+  "\0220\n\014last_updated\030\005 \001(\0132\032.google.protobuf"
+  ".Timestamp\032G\n\013PhoneNumber\022\016\n\006number\030\001 \001("
+  "\t\022(\n\004type\030\002 \001(\0162\032.tutorial.Person.PhoneT"
+  "ype\"+\n\tPhoneType\022\n\n\006MOBILE\020\000\022\010\n\004HOME\020\001\022\010"
+  "\n\004WORK\020\002\"/\n\013AddressBook\022 \n\006people\030\001 \003(\0132"
+  "\020.tutorial.PersonBP\n\024com.example.tutoria"
+  "lB\021AddressBookProtos\252\002$Google.Protobuf.E"
+  "xamples.AddressBookb\006proto3"
+  ;
+static const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable*const descriptor_table_addressbook_2eproto_deps[1] = {
+  &::descriptor_table_google_2fprotobuf_2ftimestamp_2eproto,
+};
+static ::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase*const descriptor_table_addressbook_2eproto_sccs[3] = {
+  &scc_info_AddressBook_addressbook_2eproto.base,
+  &scc_info_Person_addressbook_2eproto.base,
+  &scc_info_Person_PhoneNumber_addressbook_2eproto.base,
+};
+static ::PROTOBUF_NAMESPACE_ID::internal::once_flag descriptor_table_addressbook_2eproto_once;
+static bool descriptor_table_addressbook_2eproto_initialized = false;
+const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_addressbook_2eproto = {
+  &descriptor_table_addressbook_2eproto_initialized, descriptor_table_protodef_addressbook_2eproto, "addressbook.proto", 467,
+  &descriptor_table_addressbook_2eproto_once, descriptor_table_addressbook_2eproto_sccs, descriptor_table_addressbook_2eproto_deps, 3, 1,
+  schemas, file_default_instances, TableStruct_addressbook_2eproto::offsets,
+  file_level_metadata_addressbook_2eproto, 3, file_level_enum_descriptors_addressbook_2eproto, file_level_service_descriptors_addressbook_2eproto,
+};
+
+// Force running AddDescriptors() at dynamic initialization time.
+static bool dynamic_init_dummy_addressbook_2eproto = (  ::PROTOBUF_NAMESPACE_ID::internal::AddDescriptors(&descriptor_table_addressbook_2eproto), true);
+namespace tutorial {
+const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* Person_PhoneType_descriptor() {
+  ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_addressbook_2eproto);
+  return file_level_enum_descriptors_addressbook_2eproto[0];
+}
+bool Person_PhoneType_IsValid(int value) {
+  switch (value) {
+    case 0:
+    case 1:
+    case 2:
+      return true;
+    default:
+      return false;
+  }
+}
+
+#if (__cplusplus < 201703) && (!defined(_MSC_VER) || _MSC_VER >= 1900)
+constexpr Person_PhoneType Person::MOBILE;
+constexpr Person_PhoneType Person::HOME;
+constexpr Person_PhoneType Person::WORK;
+constexpr Person_PhoneType Person::PhoneType_MIN;
+constexpr Person_PhoneType Person::PhoneType_MAX;
+constexpr int Person::PhoneType_ARRAYSIZE;
+#endif  // (__cplusplus < 201703) && (!defined(_MSC_VER) || _MSC_VER >= 1900)
+
+// ===================================================================
+
+void Person_PhoneNumber::InitAsDefaultInstance() {
+}
+class Person_PhoneNumber::_Internal {
+ public:
+};
+
+Person_PhoneNumber::Person_PhoneNumber()
+  : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) {
+  SharedCtor();
+  // @@protoc_insertion_point(constructor:tutorial.Person.PhoneNumber)
+}
+Person_PhoneNumber::Person_PhoneNumber(const Person_PhoneNumber& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message(),
+      _internal_metadata_(nullptr) {
+  _internal_metadata_.MergeFrom(from._internal_metadata_);
+  number_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  if (!from.number().empty()) {
+    number_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.number_);
+  }
+  type_ = from.type_;
+  // @@protoc_insertion_point(copy_constructor:tutorial.Person.PhoneNumber)
+}
+
+void Person_PhoneNumber::SharedCtor() {
+  ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_Person_PhoneNumber_addressbook_2eproto.base);
+  number_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  type_ = 0;
+}
+
+Person_PhoneNumber::~Person_PhoneNumber() {
+  // @@protoc_insertion_point(destructor:tutorial.Person.PhoneNumber)
+  SharedDtor();
+}
+
+void Person_PhoneNumber::SharedDtor() {
+  number_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+
+void Person_PhoneNumber::SetCachedSize(int size) const {
+  _cached_size_.Set(size);
+}
+const Person_PhoneNumber& Person_PhoneNumber::default_instance() {
+  ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_Person_PhoneNumber_addressbook_2eproto.base);
+  return *internal_default_instance();
+}
+
+
+void Person_PhoneNumber::Clear() {
+// @@protoc_insertion_point(message_clear_start:tutorial.Person.PhoneNumber)
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  number_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  type_ = 0;
+  _internal_metadata_.Clear();
+}
+
+#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER
+const char* Person_PhoneNumber::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    ::PROTOBUF_NAMESPACE_ID::uint32 tag;
+    ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);
+    CHK_(ptr);
+    switch (tag >> 3) {
+      // string number = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) {
+          ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_number(), ptr, ctx, "tutorial.Person.PhoneNumber.number");
+          CHK_(ptr);
+        } else goto handle_unusual;
+        continue;
+      // .tutorial.Person.PhoneType type = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 16)) {
+          ::PROTOBUF_NAMESPACE_ID::uint64 val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+          CHK_(ptr);
+          set_type(static_cast<::tutorial::Person_PhoneType>(val));
+        } else goto handle_unusual;
+        continue;
+      default: {
+      handle_unusual:
+        if ((tag & 7) == 4 || tag == 0) {
+          ctx->SetLastTag(tag);
+          goto success;
+        }
+        ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx);
+        CHK_(ptr != nullptr);
+        continue;
+      }
+    }  // switch
+  }  // while
+success:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto success;
+#undef CHK_
+}
+#else  // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER
+bool Person_PhoneNumber::MergePartialFromCodedStream(
+    ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure
+  ::PROTOBUF_NAMESPACE_ID::uint32 tag;
+  // @@protoc_insertion_point(parse_start:tutorial.Person.PhoneNumber)
+  for (;;) {
+    ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u);
+    tag = p.first;
+    if (!p.second) goto handle_unusual;
+    switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+      // string number = 1;
+      case 1: {
+        if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) {
+          DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString(
+                input, this->mutable_number()));
+          DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+            this->number().data(), static_cast<int>(this->number().length()),
+            ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE,
+            "tutorial.Person.PhoneNumber.number"));
+        } else {
+          goto handle_unusual;
+        }
+        break;
+      }
+
+      // .tutorial.Person.PhoneType type = 2;
+      case 2: {
+        if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (16 & 0xFF)) {
+          int value = 0;
+          DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive<
+                   int, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_ENUM>(
+                 input, &value)));
+          set_type(static_cast< ::tutorial::Person_PhoneType >(value));
+        } else {
+          goto handle_unusual;
+        }
+        break;
+      }
+
+      default: {
+      handle_unusual:
+        if (tag == 0) {
+          goto success;
+        }
+        DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField(
+              input, tag, _internal_metadata_.mutable_unknown_fields()));
+        break;
+      }
+    }
+  }
+success:
+  // @@protoc_insertion_point(parse_success:tutorial.Person.PhoneNumber)
+  return true;
+failure:
+  // @@protoc_insertion_point(parse_failure:tutorial.Person.PhoneNumber)
+  return false;
+#undef DO_
+}
+#endif  // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER
+
+void Person_PhoneNumber::SerializeWithCachedSizes(
+    ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const {
+  // @@protoc_insertion_point(serialize_start:tutorial.Person.PhoneNumber)
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // string number = 1;
+  if (this->number().size() > 0) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->number().data(), static_cast<int>(this->number().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "tutorial.Person.PhoneNumber.number");
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased(
+      1, this->number(), output);
+  }
+
+  // .tutorial.Person.PhoneType type = 2;
+  if (this->type() != 0) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnum(
+      2, this->type(), output);
+  }
+
+  if (_internal_metadata_.have_unknown_fields()) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields(
+        _internal_metadata_.unknown_fields(), output);
+  }
+  // @@protoc_insertion_point(serialize_end:tutorial.Person.PhoneNumber)
+}
+
+::PROTOBUF_NAMESPACE_ID::uint8* Person_PhoneNumber::InternalSerializeWithCachedSizesToArray(
+    ::PROTOBUF_NAMESPACE_ID::uint8* target) const {
+  // @@protoc_insertion_point(serialize_to_array_start:tutorial.Person.PhoneNumber)
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // string number = 1;
+  if (this->number().size() > 0) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->number().data(), static_cast<int>(this->number().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "tutorial.Person.PhoneNumber.number");
+    target =
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray(
+        1, this->number(), target);
+  }
+
+  // .tutorial.Person.PhoneType type = 2;
+  if (this->type() != 0) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnumToArray(
+      2, this->type(), target);
+  }
+
+  if (_internal_metadata_.have_unknown_fields()) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields(), target);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:tutorial.Person.PhoneNumber)
+  return target;
+}
+
+size_t Person_PhoneNumber::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:tutorial.Person.PhoneNumber)
+  size_t total_size = 0;
+
+  if (_internal_metadata_.have_unknown_fields()) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize(
+        _internal_metadata_.unknown_fields());
+  }
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // string number = 1;
+  if (this->number().size() > 0) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->number());
+  }
+
+  // .tutorial.Person.PhoneType type = 2;
+  if (this->type() != 0) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::EnumSize(this->type());
+  }
+
+  int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);
+  SetCachedSize(cached_size);
+  return total_size;
+}
+
+void Person_PhoneNumber::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {
+// @@protoc_insertion_point(generalized_merge_from_start:tutorial.Person.PhoneNumber)
+  GOOGLE_DCHECK_NE(&from, this);
+  const Person_PhoneNumber* source =
+      ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated<Person_PhoneNumber>(
+          &from);
+  if (source == nullptr) {
+  // @@protoc_insertion_point(generalized_merge_from_cast_fail:tutorial.Person.PhoneNumber)
+    ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this);
+  } else {
+  // @@protoc_insertion_point(generalized_merge_from_cast_success:tutorial.Person.PhoneNumber)
+    MergeFrom(*source);
+  }
+}
+
+void Person_PhoneNumber::MergeFrom(const Person_PhoneNumber& from) {
+// @@protoc_insertion_point(class_specific_merge_from_start:tutorial.Person.PhoneNumber)
+  GOOGLE_DCHECK_NE(&from, this);
+  _internal_metadata_.MergeFrom(from._internal_metadata_);
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  if (from.number().size() > 0) {
+
+    number_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.number_);
+  }
+  if (from.type() != 0) {
+    set_type(from.type());
+  }
+}
+
+void Person_PhoneNumber::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {
+// @@protoc_insertion_point(generalized_copy_from_start:tutorial.Person.PhoneNumber)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+void Person_PhoneNumber::CopyFrom(const Person_PhoneNumber& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:tutorial.Person.PhoneNumber)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool Person_PhoneNumber::IsInitialized() const {
+  return true;
+}
+
+void Person_PhoneNumber::InternalSwap(Person_PhoneNumber* other) {
+  using std::swap;
+  _internal_metadata_.Swap(&other->_internal_metadata_);
+  number_.Swap(&other->number_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+    GetArenaNoVirtual());
+  swap(type_, other->type_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata Person_PhoneNumber::GetMetadata() const {
+  return GetMetadataStatic();
+}
+
+
+// ===================================================================
+
+void Person::InitAsDefaultInstance() {
+  ::tutorial::_Person_default_instance_._instance.get_mutable()->last_updated_ = const_cast< PROTOBUF_NAMESPACE_ID::Timestamp*>(
+      PROTOBUF_NAMESPACE_ID::Timestamp::internal_default_instance());
+}
+class Person::_Internal {
+ public:
+  static const PROTOBUF_NAMESPACE_ID::Timestamp& last_updated(const Person* msg);
+};
+
+const PROTOBUF_NAMESPACE_ID::Timestamp&
+Person::_Internal::last_updated(const Person* msg) {
+  return *msg->last_updated_;
+}
+void Person::clear_last_updated() {
+  if (GetArenaNoVirtual() == nullptr && last_updated_ != nullptr) {
+    delete last_updated_;
+  }
+  last_updated_ = nullptr;
+}
+Person::Person()
+  : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) {
+  SharedCtor();
+  // @@protoc_insertion_point(constructor:tutorial.Person)
+}
+Person::Person(const Person& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message(),
+      _internal_metadata_(nullptr),
+      phones_(from.phones_) {
+  _internal_metadata_.MergeFrom(from._internal_metadata_);
+  name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  if (!from.name().empty()) {
+    name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.name_);
+  }
+  email_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  if (!from.email().empty()) {
+    email_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.email_);
+  }
+  if (from.has_last_updated()) {
+    last_updated_ = new PROTOBUF_NAMESPACE_ID::Timestamp(*from.last_updated_);
+  } else {
+    last_updated_ = nullptr;
+  }
+  id_ = from.id_;
+  // @@protoc_insertion_point(copy_constructor:tutorial.Person)
+}
+
+void Person::SharedCtor() {
+  ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_Person_addressbook_2eproto.base);
+  name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  email_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  ::memset(&last_updated_, 0, static_cast<size_t>(
+      reinterpret_cast<char*>(&id_) -
+      reinterpret_cast<char*>(&last_updated_)) + sizeof(id_));
+}
+
+Person::~Person() {
+  // @@protoc_insertion_point(destructor:tutorial.Person)
+  SharedDtor();
+}
+
+void Person::SharedDtor() {
+  name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  email_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  if (this != internal_default_instance()) delete last_updated_;
+}
+
+void Person::SetCachedSize(int size) const {
+  _cached_size_.Set(size);
+}
+const Person& Person::default_instance() {
+  ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_Person_addressbook_2eproto.base);
+  return *internal_default_instance();
+}
+
+
+void Person::Clear() {
+// @@protoc_insertion_point(message_clear_start:tutorial.Person)
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  phones_.Clear();
+  name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  email_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+  if (GetArenaNoVirtual() == nullptr && last_updated_ != nullptr) {
+    delete last_updated_;
+  }
+  last_updated_ = nullptr;
+  id_ = 0;
+  _internal_metadata_.Clear();
+}
+
+#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER
+const char* Person::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    ::PROTOBUF_NAMESPACE_ID::uint32 tag;
+    ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);
+    CHK_(ptr);
+    switch (tag >> 3) {
+      // string name = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) {
+          ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_name(), ptr, ctx, "tutorial.Person.name");
+          CHK_(ptr);
+        } else goto handle_unusual;
+        continue;
+      // int32 id = 2;
+      case 2:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 16)) {
+          id_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+          CHK_(ptr);
+        } else goto handle_unusual;
+        continue;
+      // string email = 3;
+      case 3:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 26)) {
+          ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_email(), ptr, ctx, "tutorial.Person.email");
+          CHK_(ptr);
+        } else goto handle_unusual;
+        continue;
+      // repeated .tutorial.Person.PhoneNumber phones = 4;
+      case 4:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 34)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(add_phones(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 34);
+        } else goto handle_unusual;
+        continue;
+      // .google.protobuf.Timestamp last_updated = 5;
+      case 5:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 42)) {
+          ptr = ctx->ParseMessage(mutable_last_updated(), ptr);
+          CHK_(ptr);
+        } else goto handle_unusual;
+        continue;
+      default: {
+      handle_unusual:
+        if ((tag & 7) == 4 || tag == 0) {
+          ctx->SetLastTag(tag);
+          goto success;
+        }
+        ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx);
+        CHK_(ptr != nullptr);
+        continue;
+      }
+    }  // switch
+  }  // while
+success:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto success;
+#undef CHK_
+}
+#else  // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER
+bool Person::MergePartialFromCodedStream(
+    ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure
+  ::PROTOBUF_NAMESPACE_ID::uint32 tag;
+  // @@protoc_insertion_point(parse_start:tutorial.Person)
+  for (;;) {
+    ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u);
+    tag = p.first;
+    if (!p.second) goto handle_unusual;
+    switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+      // string name = 1;
+      case 1: {
+        if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) {
+          DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString(
+                input, this->mutable_name()));
+          DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+            this->name().data(), static_cast<int>(this->name().length()),
+            ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE,
+            "tutorial.Person.name"));
+        } else {
+          goto handle_unusual;
+        }
+        break;
+      }
+
+      // int32 id = 2;
+      case 2: {
+        if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (16 & 0xFF)) {
+
+          DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive<
+                   ::PROTOBUF_NAMESPACE_ID::int32, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT32>(
+                 input, &id_)));
+        } else {
+          goto handle_unusual;
+        }
+        break;
+      }
+
+      // string email = 3;
+      case 3: {
+        if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (26 & 0xFF)) {
+          DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString(
+                input, this->mutable_email()));
+          DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+            this->email().data(), static_cast<int>(this->email().length()),
+            ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE,
+            "tutorial.Person.email"));
+        } else {
+          goto handle_unusual;
+        }
+        break;
+      }
+
+      // repeated .tutorial.Person.PhoneNumber phones = 4;
+      case 4: {
+        if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (34 & 0xFF)) {
+          DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage(
+                input, add_phones()));
+        } else {
+          goto handle_unusual;
+        }
+        break;
+      }
+
+      // .google.protobuf.Timestamp last_updated = 5;
+      case 5: {
+        if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (42 & 0xFF)) {
+          DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage(
+               input, mutable_last_updated()));
+        } else {
+          goto handle_unusual;
+        }
+        break;
+      }
+
+      default: {
+      handle_unusual:
+        if (tag == 0) {
+          goto success;
+        }
+        DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField(
+              input, tag, _internal_metadata_.mutable_unknown_fields()));
+        break;
+      }
+    }
+  }
+success:
+  // @@protoc_insertion_point(parse_success:tutorial.Person)
+  return true;
+failure:
+  // @@protoc_insertion_point(parse_failure:tutorial.Person)
+  return false;
+#undef DO_
+}
+#endif  // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER
+
+void Person::SerializeWithCachedSizes(
+    ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const {
+  // @@protoc_insertion_point(serialize_start:tutorial.Person)
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // string name = 1;
+  if (this->name().size() > 0) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->name().data(), static_cast<int>(this->name().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "tutorial.Person.name");
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased(
+      1, this->name(), output);
+  }
+
+  // int32 id = 2;
+  if (this->id() != 0) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32(2, this->id(), output);
+  }
+
+  // string email = 3;
+  if (this->email().size() > 0) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->email().data(), static_cast<int>(this->email().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "tutorial.Person.email");
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased(
+      3, this->email(), output);
+  }
+
+  // repeated .tutorial.Person.PhoneNumber phones = 4;
+  for (unsigned int i = 0,
+      n = static_cast<unsigned int>(this->phones_size()); i < n; i++) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray(
+      4,
+      this->phones(static_cast<int>(i)),
+      output);
+  }
+
+  // .google.protobuf.Timestamp last_updated = 5;
+  if (this->has_last_updated()) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray(
+      5, _Internal::last_updated(this), output);
+  }
+
+  if (_internal_metadata_.have_unknown_fields()) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields(
+        _internal_metadata_.unknown_fields(), output);
+  }
+  // @@protoc_insertion_point(serialize_end:tutorial.Person)
+}
+
+::PROTOBUF_NAMESPACE_ID::uint8* Person::InternalSerializeWithCachedSizesToArray(
+    ::PROTOBUF_NAMESPACE_ID::uint8* target) const {
+  // @@protoc_insertion_point(serialize_to_array_start:tutorial.Person)
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // string name = 1;
+  if (this->name().size() > 0) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->name().data(), static_cast<int>(this->name().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "tutorial.Person.name");
+    target =
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray(
+        1, this->name(), target);
+  }
+
+  // int32 id = 2;
+  if (this->id() != 0) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(2, this->id(), target);
+  }
+
+  // string email = 3;
+  if (this->email().size() > 0) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+      this->email().data(), static_cast<int>(this->email().length()),
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
+      "tutorial.Person.email");
+    target =
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray(
+        3, this->email(), target);
+  }
+
+  // repeated .tutorial.Person.PhoneNumber phones = 4;
+  for (unsigned int i = 0,
+      n = static_cast<unsigned int>(this->phones_size()); i < n; i++) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+      InternalWriteMessageToArray(
+        4, this->phones(static_cast<int>(i)), target);
+  }
+
+  // .google.protobuf.Timestamp last_updated = 5;
+  if (this->has_last_updated()) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+      InternalWriteMessageToArray(
+        5, _Internal::last_updated(this), target);
+  }
+
+  if (_internal_metadata_.have_unknown_fields()) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields(), target);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:tutorial.Person)
+  return target;
+}
+
+size_t Person::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:tutorial.Person)
+  size_t total_size = 0;
+
+  if (_internal_metadata_.have_unknown_fields()) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize(
+        _internal_metadata_.unknown_fields());
+  }
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .tutorial.Person.PhoneNumber phones = 4;
+  {
+    unsigned int count = static_cast<unsigned int>(this->phones_size());
+    total_size += 1UL * count;
+    for (unsigned int i = 0; i < count; i++) {
+      total_size +=
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+          this->phones(static_cast<int>(i)));
+    }
+  }
+
+  // string name = 1;
+  if (this->name().size() > 0) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->name());
+  }
+
+  // string email = 3;
+  if (this->email().size() > 0) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+        this->email());
+  }
+
+  // .google.protobuf.Timestamp last_updated = 5;
+  if (this->has_last_updated()) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+        *last_updated_);
+  }
+
+  // int32 id = 2;
+  if (this->id() != 0) {
+    total_size += 1 +
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(
+        this->id());
+  }
+
+  int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);
+  SetCachedSize(cached_size);
+  return total_size;
+}
+
+void Person::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {
+// @@protoc_insertion_point(generalized_merge_from_start:tutorial.Person)
+  GOOGLE_DCHECK_NE(&from, this);
+  const Person* source =
+      ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated<Person>(
+          &from);
+  if (source == nullptr) {
+  // @@protoc_insertion_point(generalized_merge_from_cast_fail:tutorial.Person)
+    ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this);
+  } else {
+  // @@protoc_insertion_point(generalized_merge_from_cast_success:tutorial.Person)
+    MergeFrom(*source);
+  }
+}
+
+void Person::MergeFrom(const Person& from) {
+// @@protoc_insertion_point(class_specific_merge_from_start:tutorial.Person)
+  GOOGLE_DCHECK_NE(&from, this);
+  _internal_metadata_.MergeFrom(from._internal_metadata_);
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  phones_.MergeFrom(from.phones_);
+  if (from.name().size() > 0) {
+
+    name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.name_);
+  }
+  if (from.email().size() > 0) {
+
+    email_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.email_);
+  }
+  if (from.has_last_updated()) {
+    mutable_last_updated()->PROTOBUF_NAMESPACE_ID::Timestamp::MergeFrom(from.last_updated());
+  }
+  if (from.id() != 0) {
+    set_id(from.id());
+  }
+}
+
+void Person::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {
+// @@protoc_insertion_point(generalized_copy_from_start:tutorial.Person)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+void Person::CopyFrom(const Person& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:tutorial.Person)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool Person::IsInitialized() const {
+  return true;
+}
+
+void Person::InternalSwap(Person* other) {
+  using std::swap;
+  _internal_metadata_.Swap(&other->_internal_metadata_);
+  CastToBase(&phones_)->InternalSwap(CastToBase(&other->phones_));
+  name_.Swap(&other->name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+    GetArenaNoVirtual());
+  email_.Swap(&other->email_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+    GetArenaNoVirtual());
+  swap(last_updated_, other->last_updated_);
+  swap(id_, other->id_);
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata Person::GetMetadata() const {
+  return GetMetadataStatic();
+}
+
+
+// ===================================================================
+
+void AddressBook::InitAsDefaultInstance() {
+}
+class AddressBook::_Internal {
+ public:
+};
+
+AddressBook::AddressBook()
+  : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) {
+  SharedCtor();
+  // @@protoc_insertion_point(constructor:tutorial.AddressBook)
+}
+AddressBook::AddressBook(const AddressBook& from)
+  : ::PROTOBUF_NAMESPACE_ID::Message(),
+      _internal_metadata_(nullptr),
+      people_(from.people_) {
+  _internal_metadata_.MergeFrom(from._internal_metadata_);
+  // @@protoc_insertion_point(copy_constructor:tutorial.AddressBook)
+}
+
+void AddressBook::SharedCtor() {
+  ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_AddressBook_addressbook_2eproto.base);
+}
+
+AddressBook::~AddressBook() {
+  // @@protoc_insertion_point(destructor:tutorial.AddressBook)
+  SharedDtor();
+}
+
+void AddressBook::SharedDtor() {
+}
+
+void AddressBook::SetCachedSize(int size) const {
+  _cached_size_.Set(size);
+}
+const AddressBook& AddressBook::default_instance() {
+  ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_AddressBook_addressbook_2eproto.base);
+  return *internal_default_instance();
+}
+
+
+void AddressBook::Clear() {
+// @@protoc_insertion_point(message_clear_start:tutorial.AddressBook)
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  people_.Clear();
+  _internal_metadata_.Clear();
+}
+
+#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER
+const char* AddressBook::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+  while (!ctx->Done(&ptr)) {
+    ::PROTOBUF_NAMESPACE_ID::uint32 tag;
+    ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);
+    CHK_(ptr);
+    switch (tag >> 3) {
+      // repeated .tutorial.Person people = 1;
+      case 1:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) {
+          ptr -= 1;
+          do {
+            ptr += 1;
+            ptr = ctx->ParseMessage(add_people(), ptr);
+            CHK_(ptr);
+            if (!ctx->DataAvailable(ptr)) break;
+          } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 10);
+        } else goto handle_unusual;
+        continue;
+      default: {
+      handle_unusual:
+        if ((tag & 7) == 4 || tag == 0) {
+          ctx->SetLastTag(tag);
+          goto success;
+        }
+        ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx);
+        CHK_(ptr != nullptr);
+        continue;
+      }
+    }  // switch
+  }  // while
+success:
+  return ptr;
+failure:
+  ptr = nullptr;
+  goto success;
+#undef CHK_
+}
+#else  // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER
+bool AddressBook::MergePartialFromCodedStream(
+    ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure
+  ::PROTOBUF_NAMESPACE_ID::uint32 tag;
+  // @@protoc_insertion_point(parse_start:tutorial.AddressBook)
+  for (;;) {
+    ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u);
+    tag = p.first;
+    if (!p.second) goto handle_unusual;
+    switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+      // repeated .tutorial.Person people = 1;
+      case 1: {
+        if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) {
+          DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage(
+                input, add_people()));
+        } else {
+          goto handle_unusual;
+        }
+        break;
+      }
+
+      default: {
+      handle_unusual:
+        if (tag == 0) {
+          goto success;
+        }
+        DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField(
+              input, tag, _internal_metadata_.mutable_unknown_fields()));
+        break;
+      }
+    }
+  }
+success:
+  // @@protoc_insertion_point(parse_success:tutorial.AddressBook)
+  return true;
+failure:
+  // @@protoc_insertion_point(parse_failure:tutorial.AddressBook)
+  return false;
+#undef DO_
+}
+#endif  // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER
+
+void AddressBook::SerializeWithCachedSizes(
+    ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const {
+  // @@protoc_insertion_point(serialize_start:tutorial.AddressBook)
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // repeated .tutorial.Person people = 1;
+  for (unsigned int i = 0,
+      n = static_cast<unsigned int>(this->people_size()); i < n; i++) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray(
+      1,
+      this->people(static_cast<int>(i)),
+      output);
+  }
+
+  if (_internal_metadata_.have_unknown_fields()) {
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields(
+        _internal_metadata_.unknown_fields(), output);
+  }
+  // @@protoc_insertion_point(serialize_end:tutorial.AddressBook)
+}
+
+::PROTOBUF_NAMESPACE_ID::uint8* AddressBook::InternalSerializeWithCachedSizesToArray(
+    ::PROTOBUF_NAMESPACE_ID::uint8* target) const {
+  // @@protoc_insertion_point(serialize_to_array_start:tutorial.AddressBook)
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  // repeated .tutorial.Person people = 1;
+  for (unsigned int i = 0,
+      n = static_cast<unsigned int>(this->people_size()); i < n; i++) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+      InternalWriteMessageToArray(
+        1, this->people(static_cast<int>(i)), target);
+  }
+
+  if (_internal_metadata_.have_unknown_fields()) {
+    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray(
+        _internal_metadata_.unknown_fields(), target);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:tutorial.AddressBook)
+  return target;
+}
+
+size_t AddressBook::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:tutorial.AddressBook)
+  size_t total_size = 0;
+
+  if (_internal_metadata_.have_unknown_fields()) {
+    total_size +=
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize(
+        _internal_metadata_.unknown_fields());
+  }
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  // Prevent compiler warnings about cached_has_bits being unused
+  (void) cached_has_bits;
+
+  // repeated .tutorial.Person people = 1;
+  {
+    unsigned int count = static_cast<unsigned int>(this->people_size());
+    total_size += 1UL * count;
+    for (unsigned int i = 0; i < count; i++) {
+      total_size +=
+        ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+          this->people(static_cast<int>(i)));
+    }
+  }
+
+  int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);
+  SetCachedSize(cached_size);
+  return total_size;
+}
+
+void AddressBook::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {
+// @@protoc_insertion_point(generalized_merge_from_start:tutorial.AddressBook)
+  GOOGLE_DCHECK_NE(&from, this);
+  const AddressBook* source =
+      ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated<AddressBook>(
+          &from);
+  if (source == nullptr) {
+  // @@protoc_insertion_point(generalized_merge_from_cast_fail:tutorial.AddressBook)
+    ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this);
+  } else {
+  // @@protoc_insertion_point(generalized_merge_from_cast_success:tutorial.AddressBook)
+    MergeFrom(*source);
+  }
+}
+
+void AddressBook::MergeFrom(const AddressBook& from) {
+// @@protoc_insertion_point(class_specific_merge_from_start:tutorial.AddressBook)
+  GOOGLE_DCHECK_NE(&from, this);
+  _internal_metadata_.MergeFrom(from._internal_metadata_);
+  ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+  (void) cached_has_bits;
+
+  people_.MergeFrom(from.people_);
+}
+
+void AddressBook::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) {
+// @@protoc_insertion_point(generalized_copy_from_start:tutorial.AddressBook)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+void AddressBook::CopyFrom(const AddressBook& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:tutorial.AddressBook)
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool AddressBook::IsInitialized() const {
+  return true;
+}
+
+void AddressBook::InternalSwap(AddressBook* other) {
+  using std::swap;
+  _internal_metadata_.Swap(&other->_internal_metadata_);
+  CastToBase(&people_)->InternalSwap(CastToBase(&other->people_));
+}
+
+::PROTOBUF_NAMESPACE_ID::Metadata AddressBook::GetMetadata() const {
+  return GetMetadataStatic();
+}
+
+
+// @@protoc_insertion_point(namespace_scope)
+}  // namespace tutorial
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_NOINLINE ::tutorial::Person_PhoneNumber* Arena::CreateMaybeMessage< ::tutorial::Person_PhoneNumber >(Arena* arena) {
+  return Arena::CreateInternal< ::tutorial::Person_PhoneNumber >(arena);
+}
+template<> PROTOBUF_NOINLINE ::tutorial::Person* Arena::CreateMaybeMessage< ::tutorial::Person >(Arena* arena) {
+  return Arena::CreateInternal< ::tutorial::Person >(arena);
+}
+template<> PROTOBUF_NOINLINE ::tutorial::AddressBook* Arena::CreateMaybeMessage< ::tutorial::AddressBook >(Arena* arena) {
+  return Arena::CreateInternal< ::tutorial::AddressBook >(arena);
+}
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+#include <google/protobuf/port_undef.inc>
diff --git a/recipes/protobuf/all/test_package/addressbook.3.9.1.pb.h b/recipes/protobuf/all/test_package/addressbook.3.9.1.pb.h
new file mode 100644
--- /dev/null
+++ b/recipes/protobuf/all/test_package/addressbook.3.9.1.pb.h
@@ -0,0 +1,929 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: addressbook.proto
+
+#ifndef GOOGLE_PROTOBUF_INCLUDED_addressbook_2eproto
+#define GOOGLE_PROTOBUF_INCLUDED_addressbook_2eproto
+
+#include <limits>
+#include <string>
+
+#include <google/protobuf/port_def.inc>
+#if PROTOBUF_VERSION < 3009000
+#error This file was generated by a newer version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please update
+#error your headers.
+#endif
+#if 3009001 < PROTOBUF_MIN_PROTOC_VERSION
+#error This file was generated by an older version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please
+#error regenerate this file with a newer version of protoc.
+#endif
+
+#include <google/protobuf/port_undef.inc>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/generated_message_table_driven.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/inlined_string_field.h>
+#include <google/protobuf/metadata.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/repeated_field.h>  // IWYU pragma: export
+#include <google/protobuf/extension_set.h>  // IWYU pragma: export
+#include <google/protobuf/generated_enum_reflection.h>
+#include <google/protobuf/unknown_field_set.h>
+#include <google/protobuf/timestamp.pb.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+#define PROTOBUF_INTERNAL_EXPORT_addressbook_2eproto
+PROTOBUF_NAMESPACE_OPEN
+namespace internal {
+class AnyMetadata;
+}  // namespace internal
+PROTOBUF_NAMESPACE_CLOSE
+
+// Internal implementation detail -- do not use these members.
+struct TableStruct_addressbook_2eproto {
+  static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTableField entries[]
+    PROTOBUF_SECTION_VARIABLE(protodesc_cold);
+  static const ::PROTOBUF_NAMESPACE_ID::internal::AuxillaryParseTableField aux[]
+    PROTOBUF_SECTION_VARIABLE(protodesc_cold);
+  static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTable schema[3]
+    PROTOBUF_SECTION_VARIABLE(protodesc_cold);
+  static const ::PROTOBUF_NAMESPACE_ID::internal::FieldMetadata field_metadata[];
+  static const ::PROTOBUF_NAMESPACE_ID::internal::SerializationTable serialization_table[];
+  static const ::PROTOBUF_NAMESPACE_ID::uint32 offsets[];
+};
+extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_addressbook_2eproto;
+namespace tutorial {
+class AddressBook;
+class AddressBookDefaultTypeInternal;
+extern AddressBookDefaultTypeInternal _AddressBook_default_instance_;
+class Person;
+class PersonDefaultTypeInternal;
+extern PersonDefaultTypeInternal _Person_default_instance_;
+class Person_PhoneNumber;
+class Person_PhoneNumberDefaultTypeInternal;
+extern Person_PhoneNumberDefaultTypeInternal _Person_PhoneNumber_default_instance_;
+}  // namespace tutorial
+PROTOBUF_NAMESPACE_OPEN
+template<> ::tutorial::AddressBook* Arena::CreateMaybeMessage<::tutorial::AddressBook>(Arena*);
+template<> ::tutorial::Person* Arena::CreateMaybeMessage<::tutorial::Person>(Arena*);
+template<> ::tutorial::Person_PhoneNumber* Arena::CreateMaybeMessage<::tutorial::Person_PhoneNumber>(Arena*);
+PROTOBUF_NAMESPACE_CLOSE
+namespace tutorial {
+
+enum Person_PhoneType : int {
+  Person_PhoneType_MOBILE = 0,
+  Person_PhoneType_HOME = 1,
+  Person_PhoneType_WORK = 2,
+  Person_PhoneType_Person_PhoneType_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::min(),
+  Person_PhoneType_Person_PhoneType_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::max()
+};
+bool Person_PhoneType_IsValid(int value);
+constexpr Person_PhoneType Person_PhoneType_PhoneType_MIN = Person_PhoneType_MOBILE;
+constexpr Person_PhoneType Person_PhoneType_PhoneType_MAX = Person_PhoneType_WORK;
+constexpr int Person_PhoneType_PhoneType_ARRAYSIZE = Person_PhoneType_PhoneType_MAX + 1;
+
+const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* Person_PhoneType_descriptor();
+template<typename T>
+inline const std::string& Person_PhoneType_Name(T enum_t_value) {
+  static_assert(::std::is_same<T, Person_PhoneType>::value ||
+    ::std::is_integral<T>::value,
+    "Incorrect type passed to function Person_PhoneType_Name.");
+  return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum(
+    Person_PhoneType_descriptor(), enum_t_value);
+}
+inline bool Person_PhoneType_Parse(
+    const std::string& name, Person_PhoneType* value) {
+  return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum<Person_PhoneType>(
+    Person_PhoneType_descriptor(), name, value);
+}
+// ===================================================================
+
+class Person_PhoneNumber :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:tutorial.Person.PhoneNumber) */ {
+ public:
+  Person_PhoneNumber();
+  virtual ~Person_PhoneNumber();
+
+  Person_PhoneNumber(const Person_PhoneNumber& from);
+  Person_PhoneNumber(Person_PhoneNumber&& from) noexcept
+    : Person_PhoneNumber() {
+    *this = ::std::move(from);
+  }
+
+  inline Person_PhoneNumber& operator=(const Person_PhoneNumber& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline Person_PhoneNumber& operator=(Person_PhoneNumber&& from) noexcept {
+    if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) {
+      if (this != &from) InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return GetMetadataStatic().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return GetMetadataStatic().reflection;
+  }
+  static const Person_PhoneNumber& default_instance();
+
+  static void InitAsDefaultInstance();  // FOR INTERNAL USE ONLY
+  static inline const Person_PhoneNumber* internal_default_instance() {
+    return reinterpret_cast<const Person_PhoneNumber*>(
+               &_Person_PhoneNumber_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    0;
+
+  friend void swap(Person_PhoneNumber& a, Person_PhoneNumber& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(Person_PhoneNumber* other) {
+    if (other == this) return;
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  inline Person_PhoneNumber* New() const final {
+    return CreateMaybeMessage<Person_PhoneNumber>(nullptr);
+  }
+
+  Person_PhoneNumber* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {
+    return CreateMaybeMessage<Person_PhoneNumber>(arena);
+  }
+  void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;
+  void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;
+  void CopyFrom(const Person_PhoneNumber& from);
+  void MergeFrom(const Person_PhoneNumber& from);
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  #else
+  bool MergePartialFromCodedStream(
+      ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final;
+  #endif  // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER
+  void SerializeWithCachedSizes(
+      ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final;
+  ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray(
+      ::PROTOBUF_NAMESPACE_ID::uint8* target) const final;
+  int GetCachedSize() const final { return _cached_size_.Get(); }
+
+  private:
+  inline void SharedCtor();
+  inline void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(Person_PhoneNumber* other);
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "tutorial.Person.PhoneNumber";
+  }
+  private:
+  inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const {
+    return nullptr;
+  }
+  inline void* MaybeArenaPtr() const {
+    return nullptr;
+  }
+  public:
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+  private:
+  static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() {
+    ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_addressbook_2eproto);
+    return ::descriptor_table_addressbook_2eproto.file_level_metadata[kIndexInFileMessages];
+  }
+
+  public:
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kNumberFieldNumber = 1,
+    kTypeFieldNumber = 2,
+  };
+  // string number = 1;
+  void clear_number();
+  const std::string& number() const;
+  void set_number(const std::string& value);
+  void set_number(std::string&& value);
+  void set_number(const char* value);
+  void set_number(const char* value, size_t size);
+  std::string* mutable_number();
+  std::string* release_number();
+  void set_allocated_number(std::string* number);
+
+  // .tutorial.Person.PhoneType type = 2;
+  void clear_type();
+  ::tutorial::Person_PhoneType type() const;
+  void set_type(::tutorial::Person_PhoneType value);
+
+  // @@protoc_insertion_point(class_scope:tutorial.Person.PhoneNumber)
+ private:
+  class _Internal;
+
+  ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_;
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr number_;
+  int type_;
+  mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  friend struct ::TableStruct_addressbook_2eproto;
+};
+// -------------------------------------------------------------------
+
+class Person :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:tutorial.Person) */ {
+ public:
+  Person();
+  virtual ~Person();
+
+  Person(const Person& from);
+  Person(Person&& from) noexcept
+    : Person() {
+    *this = ::std::move(from);
+  }
+
+  inline Person& operator=(const Person& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline Person& operator=(Person&& from) noexcept {
+    if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) {
+      if (this != &from) InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return GetMetadataStatic().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return GetMetadataStatic().reflection;
+  }
+  static const Person& default_instance();
+
+  static void InitAsDefaultInstance();  // FOR INTERNAL USE ONLY
+  static inline const Person* internal_default_instance() {
+    return reinterpret_cast<const Person*>(
+               &_Person_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    1;
+
+  friend void swap(Person& a, Person& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(Person* other) {
+    if (other == this) return;
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  inline Person* New() const final {
+    return CreateMaybeMessage<Person>(nullptr);
+  }
+
+  Person* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {
+    return CreateMaybeMessage<Person>(arena);
+  }
+  void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;
+  void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;
+  void CopyFrom(const Person& from);
+  void MergeFrom(const Person& from);
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  #else
+  bool MergePartialFromCodedStream(
+      ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final;
+  #endif  // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER
+  void SerializeWithCachedSizes(
+      ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final;
+  ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray(
+      ::PROTOBUF_NAMESPACE_ID::uint8* target) const final;
+  int GetCachedSize() const final { return _cached_size_.Get(); }
+
+  private:
+  inline void SharedCtor();
+  inline void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(Person* other);
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "tutorial.Person";
+  }
+  private:
+  inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const {
+    return nullptr;
+  }
+  inline void* MaybeArenaPtr() const {
+    return nullptr;
+  }
+  public:
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+  private:
+  static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() {
+    ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_addressbook_2eproto);
+    return ::descriptor_table_addressbook_2eproto.file_level_metadata[kIndexInFileMessages];
+  }
+
+  public:
+
+  // nested types ----------------------------------------------------
+
+  typedef Person_PhoneNumber PhoneNumber;
+
+  typedef Person_PhoneType PhoneType;
+  static constexpr PhoneType MOBILE =
+    Person_PhoneType_MOBILE;
+  static constexpr PhoneType HOME =
+    Person_PhoneType_HOME;
+  static constexpr PhoneType WORK =
+    Person_PhoneType_WORK;
+  static inline bool PhoneType_IsValid(int value) {
+    return Person_PhoneType_IsValid(value);
+  }
+  static constexpr PhoneType PhoneType_MIN =
+    Person_PhoneType_PhoneType_MIN;
+  static constexpr PhoneType PhoneType_MAX =
+    Person_PhoneType_PhoneType_MAX;
+  static constexpr int PhoneType_ARRAYSIZE =
+    Person_PhoneType_PhoneType_ARRAYSIZE;
+  static inline const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor*
+  PhoneType_descriptor() {
+    return Person_PhoneType_descriptor();
+  }
+  template<typename T>
+  static inline const std::string& PhoneType_Name(T enum_t_value) {
+    static_assert(::std::is_same<T, PhoneType>::value ||
+      ::std::is_integral<T>::value,
+      "Incorrect type passed to function PhoneType_Name.");
+    return Person_PhoneType_Name(enum_t_value);
+  }
+  static inline bool PhoneType_Parse(const std::string& name,
+      PhoneType* value) {
+    return Person_PhoneType_Parse(name, value);
+  }
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kPhonesFieldNumber = 4,
+    kNameFieldNumber = 1,
+    kEmailFieldNumber = 3,
+    kLastUpdatedFieldNumber = 5,
+    kIdFieldNumber = 2,
+  };
+  // repeated .tutorial.Person.PhoneNumber phones = 4;
+  int phones_size() const;
+  void clear_phones();
+  ::tutorial::Person_PhoneNumber* mutable_phones(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person_PhoneNumber >*
+      mutable_phones();
+  const ::tutorial::Person_PhoneNumber& phones(int index) const;
+  ::tutorial::Person_PhoneNumber* add_phones();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person_PhoneNumber >&
+      phones() const;
+
+  // string name = 1;
+  void clear_name();
+  const std::string& name() const;
+  void set_name(const std::string& value);
+  void set_name(std::string&& value);
+  void set_name(const char* value);
+  void set_name(const char* value, size_t size);
+  std::string* mutable_name();
+  std::string* release_name();
+  void set_allocated_name(std::string* name);
+
+  // string email = 3;
+  void clear_email();
+  const std::string& email() const;
+  void set_email(const std::string& value);
+  void set_email(std::string&& value);
+  void set_email(const char* value);
+  void set_email(const char* value, size_t size);
+  std::string* mutable_email();
+  std::string* release_email();
+  void set_allocated_email(std::string* email);
+
+  // .google.protobuf.Timestamp last_updated = 5;
+  bool has_last_updated() const;
+  void clear_last_updated();
+  const PROTOBUF_NAMESPACE_ID::Timestamp& last_updated() const;
+  PROTOBUF_NAMESPACE_ID::Timestamp* release_last_updated();
+  PROTOBUF_NAMESPACE_ID::Timestamp* mutable_last_updated();
+  void set_allocated_last_updated(PROTOBUF_NAMESPACE_ID::Timestamp* last_updated);
+
+  // int32 id = 2;
+  void clear_id();
+  ::PROTOBUF_NAMESPACE_ID::int32 id() const;
+  void set_id(::PROTOBUF_NAMESPACE_ID::int32 value);
+
+  // @@protoc_insertion_point(class_scope:tutorial.Person)
+ private:
+  class _Internal;
+
+  ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_;
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person_PhoneNumber > phones_;
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_;
+  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr email_;
+  PROTOBUF_NAMESPACE_ID::Timestamp* last_updated_;
+  ::PROTOBUF_NAMESPACE_ID::int32 id_;
+  mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  friend struct ::TableStruct_addressbook_2eproto;
+};
+// -------------------------------------------------------------------
+
+class AddressBook :
+    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:tutorial.AddressBook) */ {
+ public:
+  AddressBook();
+  virtual ~AddressBook();
+
+  AddressBook(const AddressBook& from);
+  AddressBook(AddressBook&& from) noexcept
+    : AddressBook() {
+    *this = ::std::move(from);
+  }
+
+  inline AddressBook& operator=(const AddressBook& from) {
+    CopyFrom(from);
+    return *this;
+  }
+  inline AddressBook& operator=(AddressBook&& from) noexcept {
+    if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) {
+      if (this != &from) InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
+    return GetDescriptor();
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
+    return GetMetadataStatic().descriptor;
+  }
+  static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
+    return GetMetadataStatic().reflection;
+  }
+  static const AddressBook& default_instance();
+
+  static void InitAsDefaultInstance();  // FOR INTERNAL USE ONLY
+  static inline const AddressBook* internal_default_instance() {
+    return reinterpret_cast<const AddressBook*>(
+               &_AddressBook_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    2;
+
+  friend void swap(AddressBook& a, AddressBook& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(AddressBook* other) {
+    if (other == this) return;
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  inline AddressBook* New() const final {
+    return CreateMaybeMessage<AddressBook>(nullptr);
+  }
+
+  AddressBook* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {
+    return CreateMaybeMessage<AddressBook>(arena);
+  }
+  void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;
+  void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final;
+  void CopyFrom(const AddressBook& from);
+  void MergeFrom(const AddressBook& from);
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  #else
+  bool MergePartialFromCodedStream(
+      ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final;
+  #endif  // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER
+  void SerializeWithCachedSizes(
+      ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final;
+  ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray(
+      ::PROTOBUF_NAMESPACE_ID::uint8* target) const final;
+  int GetCachedSize() const final { return _cached_size_.Get(); }
+
+  private:
+  inline void SharedCtor();
+  inline void SharedDtor();
+  void SetCachedSize(int size) const final;
+  void InternalSwap(AddressBook* other);
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "tutorial.AddressBook";
+  }
+  private:
+  inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const {
+    return nullptr;
+  }
+  inline void* MaybeArenaPtr() const {
+    return nullptr;
+  }
+  public:
+
+  ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
+  private:
+  static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() {
+    ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_addressbook_2eproto);
+    return ::descriptor_table_addressbook_2eproto.file_level_metadata[kIndexInFileMessages];
+  }
+
+  public:
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kPeopleFieldNumber = 1,
+  };
+  // repeated .tutorial.Person people = 1;
+  int people_size() const;
+  void clear_people();
+  ::tutorial::Person* mutable_people(int index);
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person >*
+      mutable_people();
+  const ::tutorial::Person& people(int index) const;
+  ::tutorial::Person* add_people();
+  const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person >&
+      people() const;
+
+  // @@protoc_insertion_point(class_scope:tutorial.AddressBook)
+ private:
+  class _Internal;
+
+  ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_;
+  ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person > people_;
+  mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  friend struct ::TableStruct_addressbook_2eproto;
+};
+// ===================================================================
+
+
+// ===================================================================
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic push
+  #pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#endif  // __GNUC__
+// Person_PhoneNumber
+
+// string number = 1;
+inline void Person_PhoneNumber::clear_number() {
+  number_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline const std::string& Person_PhoneNumber::number() const {
+  // @@protoc_insertion_point(field_get:tutorial.Person.PhoneNumber.number)
+  return number_.GetNoArena();
+}
+inline void Person_PhoneNumber::set_number(const std::string& value) {
+  
+  number_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value);
+  // @@protoc_insertion_point(field_set:tutorial.Person.PhoneNumber.number)
+}
+inline void Person_PhoneNumber::set_number(std::string&& value) {
+  
+  number_.SetNoArena(
+    &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value));
+  // @@protoc_insertion_point(field_set_rvalue:tutorial.Person.PhoneNumber.number)
+}
+inline void Person_PhoneNumber::set_number(const char* value) {
+  GOOGLE_DCHECK(value != nullptr);
+  
+  number_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+  // @@protoc_insertion_point(field_set_char:tutorial.Person.PhoneNumber.number)
+}
+inline void Person_PhoneNumber::set_number(const char* value, size_t size) {
+  
+  number_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+      ::std::string(reinterpret_cast<const char*>(value), size));
+  // @@protoc_insertion_point(field_set_pointer:tutorial.Person.PhoneNumber.number)
+}
+inline std::string* Person_PhoneNumber::mutable_number() {
+  
+  // @@protoc_insertion_point(field_mutable:tutorial.Person.PhoneNumber.number)
+  return number_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline std::string* Person_PhoneNumber::release_number() {
+  // @@protoc_insertion_point(field_release:tutorial.Person.PhoneNumber.number)
+  
+  return number_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline void Person_PhoneNumber::set_allocated_number(std::string* number) {
+  if (number != nullptr) {
+    
+  } else {
+    
+  }
+  number_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), number);
+  // @@protoc_insertion_point(field_set_allocated:tutorial.Person.PhoneNumber.number)
+}
+
+// .tutorial.Person.PhoneType type = 2;
+inline void Person_PhoneNumber::clear_type() {
+  type_ = 0;
+}
+inline ::tutorial::Person_PhoneType Person_PhoneNumber::type() const {
+  // @@protoc_insertion_point(field_get:tutorial.Person.PhoneNumber.type)
+  return static_cast< ::tutorial::Person_PhoneType >(type_);
+}
+inline void Person_PhoneNumber::set_type(::tutorial::Person_PhoneType value) {
+  
+  type_ = value;
+  // @@protoc_insertion_point(field_set:tutorial.Person.PhoneNumber.type)
+}
+
+// -------------------------------------------------------------------
+
+// Person
+
+// string name = 1;
+inline void Person::clear_name() {
+  name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline const std::string& Person::name() const {
+  // @@protoc_insertion_point(field_get:tutorial.Person.name)
+  return name_.GetNoArena();
+}
+inline void Person::set_name(const std::string& value) {
+  
+  name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value);
+  // @@protoc_insertion_point(field_set:tutorial.Person.name)
+}
+inline void Person::set_name(std::string&& value) {
+  
+  name_.SetNoArena(
+    &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value));
+  // @@protoc_insertion_point(field_set_rvalue:tutorial.Person.name)
+}
+inline void Person::set_name(const char* value) {
+  GOOGLE_DCHECK(value != nullptr);
+  
+  name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+  // @@protoc_insertion_point(field_set_char:tutorial.Person.name)
+}
+inline void Person::set_name(const char* value, size_t size) {
+  
+  name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+      ::std::string(reinterpret_cast<const char*>(value), size));
+  // @@protoc_insertion_point(field_set_pointer:tutorial.Person.name)
+}
+inline std::string* Person::mutable_name() {
+  
+  // @@protoc_insertion_point(field_mutable:tutorial.Person.name)
+  return name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline std::string* Person::release_name() {
+  // @@protoc_insertion_point(field_release:tutorial.Person.name)
+  
+  return name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline void Person::set_allocated_name(std::string* name) {
+  if (name != nullptr) {
+    
+  } else {
+    
+  }
+  name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), name);
+  // @@protoc_insertion_point(field_set_allocated:tutorial.Person.name)
+}
+
+// int32 id = 2;
+inline void Person::clear_id() {
+  id_ = 0;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Person::id() const {
+  // @@protoc_insertion_point(field_get:tutorial.Person.id)
+  return id_;
+}
+inline void Person::set_id(::PROTOBUF_NAMESPACE_ID::int32 value) {
+  
+  id_ = value;
+  // @@protoc_insertion_point(field_set:tutorial.Person.id)
+}
+
+// string email = 3;
+inline void Person::clear_email() {
+  email_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline const std::string& Person::email() const {
+  // @@protoc_insertion_point(field_get:tutorial.Person.email)
+  return email_.GetNoArena();
+}
+inline void Person::set_email(const std::string& value) {
+  
+  email_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value);
+  // @@protoc_insertion_point(field_set:tutorial.Person.email)
+}
+inline void Person::set_email(std::string&& value) {
+  
+  email_.SetNoArena(
+    &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value));
+  // @@protoc_insertion_point(field_set_rvalue:tutorial.Person.email)
+}
+inline void Person::set_email(const char* value) {
+  GOOGLE_DCHECK(value != nullptr);
+  
+  email_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+  // @@protoc_insertion_point(field_set_char:tutorial.Person.email)
+}
+inline void Person::set_email(const char* value, size_t size) {
+  
+  email_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+      ::std::string(reinterpret_cast<const char*>(value), size));
+  // @@protoc_insertion_point(field_set_pointer:tutorial.Person.email)
+}
+inline std::string* Person::mutable_email() {
+  
+  // @@protoc_insertion_point(field_mutable:tutorial.Person.email)
+  return email_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline std::string* Person::release_email() {
+  // @@protoc_insertion_point(field_release:tutorial.Person.email)
+  
+  return email_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline void Person::set_allocated_email(std::string* email) {
+  if (email != nullptr) {
+    
+  } else {
+    
+  }
+  email_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), email);
+  // @@protoc_insertion_point(field_set_allocated:tutorial.Person.email)
+}
+
+// repeated .tutorial.Person.PhoneNumber phones = 4;
+inline int Person::phones_size() const {
+  return phones_.size();
+}
+inline void Person::clear_phones() {
+  phones_.Clear();
+}
+inline ::tutorial::Person_PhoneNumber* Person::mutable_phones(int index) {
+  // @@protoc_insertion_point(field_mutable:tutorial.Person.phones)
+  return phones_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person_PhoneNumber >*
+Person::mutable_phones() {
+  // @@protoc_insertion_point(field_mutable_list:tutorial.Person.phones)
+  return &phones_;
+}
+inline const ::tutorial::Person_PhoneNumber& Person::phones(int index) const {
+  // @@protoc_insertion_point(field_get:tutorial.Person.phones)
+  return phones_.Get(index);
+}
+inline ::tutorial::Person_PhoneNumber* Person::add_phones() {
+  // @@protoc_insertion_point(field_add:tutorial.Person.phones)
+  return phones_.Add();
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person_PhoneNumber >&
+Person::phones() const {
+  // @@protoc_insertion_point(field_list:tutorial.Person.phones)
+  return phones_;
+}
+
+// .google.protobuf.Timestamp last_updated = 5;
+inline bool Person::has_last_updated() const {
+  return this != internal_default_instance() && last_updated_ != nullptr;
+}
+inline const PROTOBUF_NAMESPACE_ID::Timestamp& Person::last_updated() const {
+  const PROTOBUF_NAMESPACE_ID::Timestamp* p = last_updated_;
+  // @@protoc_insertion_point(field_get:tutorial.Person.last_updated)
+  return p != nullptr ? *p : *reinterpret_cast<const PROTOBUF_NAMESPACE_ID::Timestamp*>(
+      &PROTOBUF_NAMESPACE_ID::_Timestamp_default_instance_);
+}
+inline PROTOBUF_NAMESPACE_ID::Timestamp* Person::release_last_updated() {
+  // @@protoc_insertion_point(field_release:tutorial.Person.last_updated)
+  
+  PROTOBUF_NAMESPACE_ID::Timestamp* temp = last_updated_;
+  last_updated_ = nullptr;
+  return temp;
+}
+inline PROTOBUF_NAMESPACE_ID::Timestamp* Person::mutable_last_updated() {
+  
+  if (last_updated_ == nullptr) {
+    auto* p = CreateMaybeMessage<PROTOBUF_NAMESPACE_ID::Timestamp>(GetArenaNoVirtual());
+    last_updated_ = p;
+  }
+  // @@protoc_insertion_point(field_mutable:tutorial.Person.last_updated)
+  return last_updated_;
+}
+inline void Person::set_allocated_last_updated(PROTOBUF_NAMESPACE_ID::Timestamp* last_updated) {
+  ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual();
+  if (message_arena == nullptr) {
+    delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(last_updated_);
+  }
+  if (last_updated) {
+    ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+      reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(last_updated)->GetArena();
+    if (message_arena != submessage_arena) {
+      last_updated = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+          message_arena, last_updated, submessage_arena);
+    }
+    
+  } else {
+    
+  }
+  last_updated_ = last_updated;
+  // @@protoc_insertion_point(field_set_allocated:tutorial.Person.last_updated)
+}
+
+// -------------------------------------------------------------------
+
+// AddressBook
+
+// repeated .tutorial.Person people = 1;
+inline int AddressBook::people_size() const {
+  return people_.size();
+}
+inline void AddressBook::clear_people() {
+  people_.Clear();
+}
+inline ::tutorial::Person* AddressBook::mutable_people(int index) {
+  // @@protoc_insertion_point(field_mutable:tutorial.AddressBook.people)
+  return people_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person >*
+AddressBook::mutable_people() {
+  // @@protoc_insertion_point(field_mutable_list:tutorial.AddressBook.people)
+  return &people_;
+}
+inline const ::tutorial::Person& AddressBook::people(int index) const {
+  // @@protoc_insertion_point(field_get:tutorial.AddressBook.people)
+  return people_.Get(index);
+}
+inline ::tutorial::Person* AddressBook::add_people() {
+  // @@protoc_insertion_point(field_add:tutorial.AddressBook.people)
+  return people_.Add();
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person >&
+AddressBook::people() const {
+  // @@protoc_insertion_point(field_list:tutorial.AddressBook.people)
+  return people_;
+}
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic pop
+#endif  // __GNUC__
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+
+// @@protoc_insertion_point(namespace_scope)
+
+}  // namespace tutorial
+
+PROTOBUF_NAMESPACE_OPEN
+
+template <> struct is_proto_enum< ::tutorial::Person_PhoneType> : ::std::true_type {};
+template <>
+inline const EnumDescriptor* GetEnumDescriptor< ::tutorial::Person_PhoneType>() {
+  return ::tutorial::Person_PhoneType_descriptor();
+}
+
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+
+#include <google/protobuf/port_undef.inc>
+#endif  // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_addressbook_2eproto
diff --git a/recipes/protobuf/all/test_package/conanfile.py b/recipes/protobuf/all/test_package/conanfile.py
--- a/recipes/protobuf/all/test_package/conanfile.py
+++ b/recipes/protobuf/all/test_package/conanfile.py
@@ -1,17 +1,51 @@
 import os
-from conans import ConanFile, CMake, tools
+from conans import ConanFile, CMake, RunEnvironment, tools
+import shutil
 
 
 class TestPackageConan(ConanFile):
     settings = "os", "compiler", "build_type", "arch"
-    generators = "cmake"
+    generators = "cmake", "cmake_find_package"
+
+    @property
+    def _protoc_available(self):
+        return not self.options["protobuf"].lite and not tools.cross_building(self.settings)
 
     def build(self):
+        # Build without protoc
+        os.mkdir("without_protoc")
+        shutil.copy(os.path.join(self.source_folder, "addressbook.{}.pb.h".format(self.deps_cpp_info["protobuf"].version)),
+                    os.path.join("without_protoc", "addressbook.pb.h"))
+        shutil.copy(os.path.join(self.source_folder, "addressbook.{}.pb.cc".format(self.deps_cpp_info["protobuf"].version)),
+                    os.path.join("without_protoc", "addressbook.pb.cc"))
         cmake = CMake(self)
-        cmake.configure()
+        cmake.definitions["protobuf_VERBOSE"] = True
+        cmake.definitions["protobuf_MODULE_COMPATIBLE"] = True
+        cmake.definitions["PROTOC_AVAILABLE"] = False
+        cmake.configure(build_folder="without_protoc")
         cmake.build()
 
+        with tools.environment_append(RunEnvironment(self).vars):
+            if self._protoc_available:
+                # Build with protoc
+                cmake = CMake(self)
+                cmake.definitions["protobuf_VERBOSE"] = True
+                cmake.definitions["protobuf_MODULE_COMPATIBLE"] = True
+                cmake.definitions["PROTOC_AVAILABLE"] = True
+                cmake.configure(build_folder="with_protoc")
+                cmake.build()
+
     def test(self):
         if not tools.cross_building(self.settings):
-            bin_path = os.path.abspath(os.path.join("bin", "test_package"))
+            self.run("protoc --version", run_environment=True)
+
+            # Test the build built without protoc
+            bin_path = os.path.join("without_protoc", "bin", "test_package")
             self.run(bin_path, run_environment=True)
+
+            if self._protoc_available:
+                # Test the build built with protoc
+                assert os.path.isfile(os.path.join("with_protoc", "addressbook.pb.cc"))
+                assert os.path.isfile(os.path.join("with_protoc", "addressbook.pb.h"))
+                bin_path = os.path.join("with_protoc", "bin", "test_package")
+                self.run(bin_path, run_environment=True)
diff --git a/recipes/protobuf/all/test_package/test_package.cpp b/recipes/protobuf/all/test_package/test_package.cpp
--- a/recipes/protobuf/all/test_package/test_package.cpp
+++ b/recipes/protobuf/all/test_package/test_package.cpp
@@ -9,8 +9,8 @@ int main()
 
 	tutorial::Person p;
 	p.set_id(21);
-	p.set_name("Bincrafters");
-	p.set_email("bincrafters@github.com");
+	p.set_name("conan-center-index");
+	p.set_email("info@conan.io");
 
 	std::cout << p.SerializeAsString() << "\n";
 	return EXIT_SUCCESS;