diff --git a/.github/workflows/continuous-integration-workflow.yaml b/.github/workflows/continuous-integration-workflow.yaml index 4f0088c85..8339345e3 100644 --- a/.github/workflows/continuous-integration-workflow.yaml +++ b/.github/workflows/continuous-integration-workflow.yaml @@ -37,10 +37,6 @@ jobs: default: true profile: minimal components: clippy - - name: Install Protoc - uses: arduino/setup-protoc@v1 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - name: install ninja uses: seanmiddleditch/gha-setup-ninja@v3 - name: clippy @@ -63,10 +59,6 @@ jobs: - uses: actions/checkout@v3 with: submodules: recursive - - name: Install Protoc - uses: arduino/setup-protoc@v1 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - name: install ninja uses: seanmiddleditch/gha-setup-ninja@v3 - name: cargo update -Zminimal-versions @@ -104,10 +96,6 @@ jobs: toolchain: ${{ matrix.toolchain }} default: true profile: minimal - - name: Install Protoc - uses: arduino/setup-protoc@v1 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - name: install ninja uses: seanmiddleditch/gha-setup-ninja@v3 - uses: Swatinem/rust-cache@v1 @@ -135,10 +123,6 @@ jobs: toolchain: nightly default: true profile: minimal - - name: Install Protoc - uses: arduino/setup-protoc@v1 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - uses: Swatinem/rust-cache@v1 - name: install cargo-no-std-check uses: actions-rs/cargo@v1 @@ -165,3 +149,21 @@ jobs: with: command: check args: --manifest-path prost-build/Cargo.toml + + vendored: + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v2 + with: + submodules: recursive + - name: install toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + default: true + profile: minimal + - uses: Swatinem/rust-cache@v1 + - name: cargo check + run: cd test-vendored && cargo check + diff --git a/.gitignore b/.gitignore index 493aa4f04..a9d37c560 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,2 @@ target Cargo.lock - -.DS_Store \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 6b4057620..760378b2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ exclude = [ "fuzz", # Same counts for the afl fuzz targets "afl", + "test-vendored" ] [lib] diff --git a/fuzz/afl/.gitignore b/afl/.gitignore similarity index 100% rename from fuzz/afl/.gitignore rename to afl/.gitignore diff --git a/fuzz/afl/proto3/Cargo.toml b/afl/proto3/Cargo.toml similarity index 100% rename from fuzz/afl/proto3/Cargo.toml rename to afl/proto3/Cargo.toml diff --git a/fuzz/afl/proto3/README.md b/afl/proto3/README.md similarity index 100% rename from fuzz/afl/proto3/README.md rename to afl/proto3/README.md diff --git a/fuzz/afl/proto3/in/empty b/afl/proto3/in/empty similarity index 100% rename from fuzz/afl/proto3/in/empty rename to afl/proto3/in/empty diff --git a/fuzz/afl/proto3/in/testmessage b/afl/proto3/in/testmessage similarity index 100% rename from fuzz/afl/proto3/in/testmessage rename to afl/proto3/in/testmessage diff --git a/fuzz/afl/proto3/src/main.rs b/afl/proto3/src/main.rs similarity index 100% rename from fuzz/afl/proto3/src/main.rs rename to afl/proto3/src/main.rs diff --git a/fuzz/afl/proto3/src/reproduce.rs b/afl/proto3/src/reproduce.rs similarity index 100% rename from fuzz/afl/proto3/src/reproduce.rs rename to afl/proto3/src/reproduce.rs diff --git a/bootstrap/src/lib.rs b/bootstrap/src/lib.rs index 0bcfae160..f50e583de 100644 --- a/bootstrap/src/lib.rs +++ b/bootstrap/src/lib.rs @@ -6,19 +6,14 @@ use std::fs; use std::io::Read; use std::io::Write; use std::path::Path; -use std::path::PathBuf; /// Test which bootstraps protobuf.rs and compiler.rs from the .proto definitions in the Protobuf /// repo. Ensures that the checked-in compiled versions are up-to-date. #[test] fn bootstrap() { - let include = PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .parent() - .unwrap() - .join("tests") - .join("src") - .join("include"); - let protobuf = include.join("google").join("protobuf"); + let protobuf = prost_build::protoc_include() + .join("google") + .join("protobuf"); let tempdir = tempfile::Builder::new() .prefix("prost-types-bootstrap") @@ -45,7 +40,7 @@ fn bootstrap() { protobuf.join("timestamp.proto"), protobuf.join("type.proto"), ], - &[include], + &[""], ) .unwrap(); diff --git a/prost-build/Cargo.toml b/prost-build/Cargo.toml index eb9c314b4..4add32677 100644 --- a/prost-build/Cargo.toml +++ b/prost-build/Cargo.toml @@ -16,6 +16,7 @@ rust-version = "1.56" [features] default = [] +vendored = [] # When MSRV moves to 1.60, these can change to dep: cleanup-markdown = ["pulldown-cmark", "pulldown-cmark-to-cmark"] @@ -31,12 +32,15 @@ prost-types = { version = "0.11.0", path = "../prost-types", default-features = tempfile = "3" lazy_static = "1.4.0" regex = { version = "1.5.5", default-features = false, features = ["std", "unicode-bool"] } -which = "4" - -# These two must be kept in sync, used for `cleanup-markdown` feature. +# These two must be kept in sync pulldown-cmark = { version = "0.9.1", optional = true, default-features = false } pulldown-cmark-to-cmark = { version = "10.0.1", optional = true } +[build-dependencies] +which = { version = "4", default-features = false } +cfg-if = "1" +cmake = "0.1" + [dev-dependencies] env_logger = { version = "0.8", default-features = false } diff --git a/prost-build/build.rs b/prost-build/build.rs new file mode 100644 index 000000000..14ebe5746 --- /dev/null +++ b/prost-build/build.rs @@ -0,0 +1,120 @@ +//! Finds the appropriate `protoc` binary and Protobuf include directory for this host, and outputs +//! build directives so that the main `prost-build` crate can use them. +//! +//! This build script attempts to find `protoc` in a few ways: +//! +//! 1. If `PROTOC_NO_VENDOR` is enabled, it will check the `PROTOC` environment variable +//! then check the `PATH` for a `protoc` or `protoc.exe`. +//! 2. If the `vendored` feature flag is enabled or `protoc` can't be found via the environment +//! variable or in the `PATH` then `prost-build` will attempt to build `protoc` from the +//! bundled source code. +//! 3. Otherwise, it will attempt to execute from the `PATH` and fail if it does not exist. +//! +//! The following locations are checked for the Protobuf include directory in decreasing priority: +//! +//! 1. The `PROTOC_INCLUDE` environment variable. +//! 2. The bundled Protobuf include directory. +//! + +use cfg_if::cfg_if; +use std::env; +use std::path::PathBuf; +use which::which; + +/// Returns the path to the location of the bundled Protobuf artifacts. +fn bundle_path() -> PathBuf { + env::current_dir().unwrap().join("third-party") +} + +/// Returns the path to the Protobuf include directory pointed to by the `PROTOC_INCLUDE` +/// environment variable, if it is set. +fn env_protoc_include() -> Option { + let protoc_include = match env::var_os("PROTOC_INCLUDE") { + Some(path) => PathBuf::from(path), + None => return None, + }; + + if !protoc_include.exists() { + panic!( + "PROTOC_INCLUDE environment variable points to non-existent directory ({:?})", + protoc_include + ); + } + if !protoc_include.is_dir() { + panic!( + "PROTOC_INCLUDE environment variable points to a non-directory file ({:?})", + protoc_include + ); + } + + Some(protoc_include) +} + +/// Returns the path to the bundled Protobuf include directory. +fn bundled_protoc_include() -> PathBuf { + bundle_path().join("include") +} + +/// Check for `protoc` via the `PROTOC` env var or in the `PATH`. +fn path_protoc() -> Option { + env::var_os("PROTOC") + .map(PathBuf::from) + .or_else(|| which("protoc").ok()) +} + +/// Returns true if the vendored flag is enabled. +fn vendored() -> bool { + cfg_if! { + if #[cfg(feature = "vendored")] { + true + } else { + false + } + } +} + +/// Compile `protoc` via `cmake`. +fn compile() -> Option { + let protobuf_src = bundle_path().join("protobuf").join("cmake"); + + println!("cargo:rerun-if-changed={}", protobuf_src.display()); + + let dst = cmake::Config::new(protobuf_src) + .define("protobuf_BUILD_TESTS", "OFF") + .build(); + + Some(dst.join("bin").join("protoc")) +} + +/// Try to find a `protoc` through a few methods. +/// +/// Check module docs for more info. +fn protoc() -> Option { + if env::var_os("PROTOC_NO_VENDOR").is_some() { + path_protoc() + } else if vendored() { + compile() + } else { + path_protoc().or_else(compile) + } +} + +fn main() { + let protoc = protoc().expect( + "Failed to find or build the protoc binary. The PROTOC environment \ + is not set, `protoc` is not in PATH or you are missing the requirements to compile protobuf \ + from source. \n \ + Check out the `prost-build` README for instructions on the requirements: \ + https://github.com/tokio-rs/prost#generated-code", + ); + + let protoc_include = env_protoc_include().unwrap_or_else(bundled_protoc_include); + + println!("cargo:rustc-env=PROTOC={}", protoc.display()); + println!( + "cargo:rustc-env=PROTOC_INCLUDE={}", + protoc_include.display() + ); + println!("cargo:rerun-if-env-changed=PROTOC"); + println!("cargo:rerun-if-env-changed=PROTOC_INCLUDE"); +} diff --git a/prost-build/src/lib.rs b/prost-build/src/lib.rs index f428c4da9..9133cfe35 100644 --- a/prost-build/src/lib.rs +++ b/prost-build/src/lib.rs @@ -92,35 +92,45 @@ //! ## Sourcing `protoc` //! //! `prost-build` depends on the Protocol Buffers compiler, `protoc`, to parse `.proto` files into -//! a representation that can be transformed into Rust. If set, `prost-build` uses the `PROTOC` -//! for locating `protoc`. For example, on a macOS system where Protobuf is installed -//! with Homebrew, set the environment variables to: +//! a representation that can be transformed into Rust. If set, `prost-build` uses the `PROTOC` and +//! `PROTOC_INCLUDE` environment variables for locating `protoc` and the Protobuf includes +//! directory. For example, on a macOS system where Protobuf is installed with Homebrew, set the +//! environment to: //! //! ```bash //! PROTOC=/usr/local/bin/protoc +//! PROTOC_INCLUDE=/usr/local/include //! ``` //! //! and in a typical Linux installation: //! //! ```bash //! PROTOC=/usr/bin/protoc +//! PROTOC_INCLUDE=/usr/include //! ``` //! //! If no `PROTOC` environment variable is set then `prost-build` will search the -//! current path for `protoc` or `protoc.exe`. If `prost-build` can not find `protoc` -//! via these methods the `compile_protos` method will fail. +//! current path for `protoc` or `protoc.exe`. If `protoc` is not found via these +//! two methods then `prost-build` will attempt to compile `protoc` from the bundled +//! source. //! -//! ### Compiling `protoc` from source +//! If you would not like `prost-build` to not compile `protoc` from source ever then +//! ensure you have set `PROTOC_NO_VENDOR` environment variable as this will disable +//! compiling from source even if the `vendored` feature flag is enabled. //! -//! To compile `protoc` from source you can use the `protobuf-src` crate and -//! set the correct environment variables. -//! ```no_run,ignore, rust -//! std::env::set_var("PROTOC", protobuf_src::protoc()); +//! If you would like to always compile from source then setting the `vendored` feature +//! flag will force `prost-build` to always build `protoc` from source. //! -//! // Now compile your proto files via prost-build -//! ``` +//! If `PROTOC_INCLUDE` is not found in the environment, then the Protobuf include directory +//! bundled in the prost-build crate is be used. +//! +//! ### Compiling `protoc` from source +//! +//! Compiling `protoc` from source requires a few external dependencies. Currently, +//! `prost-build` uses `cmake` to build `protoc`. For more information check out the +//! [protobuf build instructions][protobuf-build]. //! -//! [`protobuf-src`]: https://docs.rs/protobuf-src +//! [protobuf-build]: https://github.com/protocolbuffers/protobuf/blob/master/src/README.md mod ast; mod code_generator; @@ -650,7 +660,7 @@ impl Config { /// /// In `build.rs`: /// - /// ```rust, no_run + /// ```rust /// # use std::env; /// # use std::path::PathBuf; /// # let mut config = prost_build::Config::new(); @@ -837,30 +847,19 @@ impl Config { }; if !self.skip_protoc_run { - let protoc = protoc_from_env(); - - let mut cmd = Command::new(protoc.clone()); + let mut cmd = Command::new(protoc()); cmd.arg("--include_imports") .arg("--include_source_info") .arg("-o") .arg(&file_descriptor_set_path); for include in includes { - if include.as_ref().exists() { - cmd.arg("-I").arg(include.as_ref()); - } else { - println!( - "ignoring {} since it does not exist.", - include.as_ref().display() - ) - } + cmd.arg("-I").arg(include.as_ref()); } // Set the protoc include after the user includes in case the user wants to // override one of the built-in .protos. - if let Some(protoc_include) = protoc_include_from_env() { - cmd.arg("-I").arg(protoc_include); - } + cmd.arg("-I").arg(protoc_include()); for arg in &self.protoc_args { cmd.arg(arg); @@ -870,12 +869,10 @@ impl Config { cmd.arg(proto.as_ref()); } - println!("Running: {:?}", cmd); - let output = cmd.output().map_err(|error| { Error::new( error.kind(), - format!("failed to invoke protoc (hint: https://docs.rs/prost-build/#sourcing-protoc): (path: {:?}): {}", &protoc, error), + format!("failed to invoke protoc (hint: https://docs.rs/prost-build/#sourcing-protoc): {}", error), ) })?; @@ -887,15 +884,7 @@ impl Config { } } - let buf = fs::read(&file_descriptor_set_path).map_err(|e| { - Error::new( - e.kind(), - format!( - "unable to open file_descriptor_set_path: {:?}, OS: {}", - &file_descriptor_set_path, e - ), - ) - })?; + let buf = fs::read(file_descriptor_set_path)?; let file_descriptor_set = FileDescriptorSet::decode(&*buf).map_err(|error| { Error::new( ErrorKind::InvalidInput, @@ -1235,40 +1224,19 @@ pub fn compile_protos(protos: &[impl AsRef], includes: &[impl AsRef] } /// Returns the path to the `protoc` binary. -pub fn protoc_from_env() -> PathBuf { - let msg = " -Could not find `protoc` installation and this build crate cannot proceed without -this knowledge. If `protoc` is installed and this crate had trouble finding -it, you can set the `PROTOC` environment variable with the specific path to your -installed `protoc` binary. - -For more information: https://docs.rs/prost-build/#sourcing-protoc -"; - - env::var_os("PROTOC") - .map(PathBuf::from) - .or_else(|| which::which("protoc").ok()) - .expect(msg) +pub fn protoc() -> PathBuf { + match env::var_os("PROTOC") { + Some(protoc) => PathBuf::from(protoc), + None => PathBuf::from(env!("PROTOC")), + } } /// Returns the path to the Protobuf include directory. -pub fn protoc_include_from_env() -> Option { - let protoc_include: PathBuf = env::var_os("PROTOC_INCLUDE")?.into(); - - if !protoc_include.exists() { - panic!( - "PROTOC_INCLUDE environment variable points to non-existent directory ({:?})", - protoc_include - ); +pub fn protoc_include() -> PathBuf { + match env::var_os("PROTOC_INCLUDE") { + Some(include) => PathBuf::from(include), + None => PathBuf::from(env!("PROTOC_INCLUDE")), } - if !protoc_include.is_dir() { - panic!( - "PROTOC_INCLUDE environment variable points to a non-directory file ({:?})", - protoc_include - ); - } - - Some(protoc_include) } #[cfg(test)] @@ -1345,7 +1313,6 @@ mod tests { let _ = env_logger::try_init(); Config::new() .service_generator(Box::new(ServiceTraitGenerator)) - .out_dir(std::env::temp_dir()) .compile_protos(&["src/smoke_test.proto"], &["src"]) .unwrap(); } @@ -1360,7 +1327,6 @@ mod tests { Config::new() .service_generator(Box::new(gen)) .include_file("_protos.rs") - .out_dir(std::env::temp_dir()) .compile_protos(&["src/hello.proto", "src/goodbye.proto"], &["src"]) .unwrap(); diff --git a/tests/src/include/google/protobuf/any.proto b/prost-build/third-party/include/google/protobuf/any.proto similarity index 100% rename from tests/src/include/google/protobuf/any.proto rename to prost-build/third-party/include/google/protobuf/any.proto diff --git a/tests/src/include/google/protobuf/api.proto b/prost-build/third-party/include/google/protobuf/api.proto similarity index 100% rename from tests/src/include/google/protobuf/api.proto rename to prost-build/third-party/include/google/protobuf/api.proto diff --git a/tests/src/include/google/protobuf/compiler/plugin.proto b/prost-build/third-party/include/google/protobuf/compiler/plugin.proto similarity index 100% rename from tests/src/include/google/protobuf/compiler/plugin.proto rename to prost-build/third-party/include/google/protobuf/compiler/plugin.proto diff --git a/tests/src/include/google/protobuf/descriptor.proto b/prost-build/third-party/include/google/protobuf/descriptor.proto similarity index 100% rename from tests/src/include/google/protobuf/descriptor.proto rename to prost-build/third-party/include/google/protobuf/descriptor.proto diff --git a/tests/src/include/google/protobuf/duration.proto b/prost-build/third-party/include/google/protobuf/duration.proto similarity index 100% rename from tests/src/include/google/protobuf/duration.proto rename to prost-build/third-party/include/google/protobuf/duration.proto diff --git a/tests/src/include/google/protobuf/empty.proto b/prost-build/third-party/include/google/protobuf/empty.proto similarity index 100% rename from tests/src/include/google/protobuf/empty.proto rename to prost-build/third-party/include/google/protobuf/empty.proto diff --git a/tests/src/include/google/protobuf/field_mask.proto b/prost-build/third-party/include/google/protobuf/field_mask.proto similarity index 100% rename from tests/src/include/google/protobuf/field_mask.proto rename to prost-build/third-party/include/google/protobuf/field_mask.proto diff --git a/tests/src/include/google/protobuf/source_context.proto b/prost-build/third-party/include/google/protobuf/source_context.proto similarity index 100% rename from tests/src/include/google/protobuf/source_context.proto rename to prost-build/third-party/include/google/protobuf/source_context.proto diff --git a/tests/src/include/google/protobuf/struct.proto b/prost-build/third-party/include/google/protobuf/struct.proto similarity index 100% rename from tests/src/include/google/protobuf/struct.proto rename to prost-build/third-party/include/google/protobuf/struct.proto diff --git a/tests/src/include/google/protobuf/timestamp.proto b/prost-build/third-party/include/google/protobuf/timestamp.proto similarity index 100% rename from tests/src/include/google/protobuf/timestamp.proto rename to prost-build/third-party/include/google/protobuf/timestamp.proto diff --git a/tests/src/include/google/protobuf/type.proto b/prost-build/third-party/include/google/protobuf/type.proto similarity index 100% rename from tests/src/include/google/protobuf/type.proto rename to prost-build/third-party/include/google/protobuf/type.proto diff --git a/tests/src/include/google/protobuf/wrappers.proto b/prost-build/third-party/include/google/protobuf/wrappers.proto similarity index 100% rename from tests/src/include/google/protobuf/wrappers.proto rename to prost-build/third-party/include/google/protobuf/wrappers.proto diff --git a/prost-build/third-party/protobuf b/prost-build/third-party/protobuf new file mode 160000 index 000000000..22d0e265d --- /dev/null +++ b/prost-build/third-party/protobuf @@ -0,0 +1 @@ +Subproject commit 22d0e265de7d2b3d2e9a00d071313502e7d4cccf diff --git a/prost-build/third-party/update-vendored-protobuf.sh b/prost-build/third-party/update-vendored-protobuf.sh new file mode 100755 index 000000000..d8d741ad5 --- /dev/null +++ b/prost-build/third-party/update-vendored-protobuf.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +set -ex + +if [ "$#" -ne 1 ] +then + echo "Usage: $0 " + exit 1 +fi + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +VERSION="$1" +TEMPDIR=$(mktemp -d "protobuf-$VERSION-XXX") +ARCH="linux-x86_64" + +mkdir "$TEMPDIR/$ARCH" +curl --proto '=https' --tlsv1.2 -sSfL \ + "https://github.com/protocolbuffers/protobuf/releases/download/v$VERSION/protoc-$VERSION-$ARCH.zip" \ + -o "$TEMPDIR/$ARCH/protoc.zip" + +unzip "$TEMPDIR/$ARCH/protoc.zip" -d "$TEMPDIR/$ARCH" + +# Update the include directory +rm -rf "$DIR/include" +mv "$TEMPDIR/linux-x86_64/include" "$DIR/include/" + + +rm -rf $TEMPDIR +cd "$DIR/protobuf" +git checkout "v$VERSION" +cd $DIR + +echo "third-party protobuf items updated to v$VERSION" \ No newline at end of file diff --git a/test-vendored/Cargo.toml b/test-vendored/Cargo.toml new file mode 100644 index 000000000..0613874f4 --- /dev/null +++ b/test-vendored/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "test-vendored" +version = "0.1.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[build-dependencies] +prost-build = { path = "../prost-build", features = ["vendored"] } diff --git a/test-vendored/build.rs b/test-vendored/build.rs new file mode 100644 index 000000000..ada1dc4b2 --- /dev/null +++ b/test-vendored/build.rs @@ -0,0 +1,3 @@ +fn main() { + prost_build::compile_protos(&["proto/foo.proto"], &["proto"]).unwrap() +} diff --git a/test-vendored/proto/foo.proto b/test-vendored/proto/foo.proto new file mode 100644 index 000000000..40160936b --- /dev/null +++ b/test-vendored/proto/foo.proto @@ -0,0 +1,6 @@ +syntax = "proto3"; +package foo; + +message Foo { + string bar = 1; +} \ No newline at end of file diff --git a/test-vendored/src/lib.rs b/test-vendored/src/lib.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/test-vendored/src/lib.rs @@ -0,0 +1 @@ + diff --git a/tests/src/well_known_types.proto b/tests/src/well_known_types.proto index 650e023a1..be0d8b7c2 100644 --- a/tests/src/well_known_types.proto +++ b/tests/src/well_known_types.proto @@ -1,17 +1,8 @@ syntax = "proto3"; +import "google/protobuf/struct.proto"; import "google/protobuf/timestamp.proto"; -import "google/protobuf/type.proto"; -import "google/protobuf/any.proto"; -import "google/protobuf/field_mask.proto"; -import "google/protobuf/api.proto"; -import "google/protobuf/descriptor.proto"; import "google/protobuf/wrappers.proto"; -import "google/protobuf/source_context.proto"; -import "google/protobuf/empty.proto"; -import "google/protobuf/struct.proto"; -import "google/protobuf/compiler/plugin.proto"; -import "google/protobuf/duration.proto"; package well_known_types; @@ -44,32 +35,4 @@ message Foo { message Test { bytes bytes = 1; map bytes_dict = 2; -} - - -message Bar { - google.protobuf.Empty ee = 1; - google.protobuf.Timestamp ts = 2; - google.protobuf.Option p = 3; - google.protobuf.Any a = 4; - google.protobuf.Api b = 5; - google.protobuf.BoolValue c = 6; - google.protobuf.BytesValue d = 7; - google.protobuf.DoubleValue e = 8; - google.protobuf.Duration f = 9; - google.protobuf.Empty g = 10; - google.protobuf.Enum h = 11; - google.protobuf.EnumValue i = 12; - google.protobuf.Field j = 13; - google.protobuf.Int32Value k = 14; - google.protobuf.ListValue l = 15; - google.protobuf.Method m = 16; - google.protobuf.Mixin n = 17; - google.protobuf.NullValue pp = 18; - google.protobuf.Option o = 19; - google.protobuf.Struct q = 20; - google.protobuf.Syntax r = 21; - google.protobuf.Timestamp t = 22; - google.protobuf.Type s = 23; - google.protobuf.Value v = 24; -} +} \ No newline at end of file