Repository: tokio-rs/prost (language: rust)

Issue:
Custom output directory?
I am hoping to have prost write out the generated source files to a custom location (i.e. 'src' in my project), rather than a temp location (rust protoc has this option: https://github.com/stepancheg/rust-protobuf/blob/master/protoc-rust/src/lib.rs#L21)

Is this currently possible with prost? If not, it would be a very useful setting on the prost build config.

Thanks!


Discussion / hints:
you can set the OUT_DIR env variable dynamically in the build.rs before calling compile_protos():

```rust 
use std::env;
extern crate prost_build;

fn main() {
     env::set_var("OUT_DIR", "src");
     prost_build::compile_protos(&["./src/something.proto"], &["src/"]).unwrap();
}
```
Alright, but it is not too uncommon that the build script might also have to work with the `OUT_DIR`, and it might be a little bit weird to record the environment variable, set it, compile the protos and reset it again.
Maybe `compile_protos` would gain from becoming a builder-like API.

```rust
prost_build::compiler(&["./src/something.proto"])
    .output("./somewhere")
    .include(&["src/"])
    .compile();
```

Fix patch:
diff --git a/.appveyor.yml b/.appveyor.yml
index af22bfed5..426577ca0 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -22,9 +22,8 @@ build: false
 
 test_script:
   - SET RUST_BACKTRACE=1
-    # The 'protobuf' crate can't be compiled on Windows, which means none of the
-    # integration tests or benchmarks can be run.
-  - cargo test --target %TARGET% -p prost -p prost-build -p prost-types
+    # The Protobuf conformance test runner can't be compiled on Windows.
+  - cargo test --target %TARGET% --all --exclude conformance
 
 cache:
   - C:\Users\appveyor\.cargo\registry
diff --git a/.travis.yml b/.travis.yml
index bb6a14093..8416e8036 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -22,5 +22,5 @@ script:
   - cargo build --verbose --all --exclude benchmarks
   - cargo test --verbose --all --exclude benchmarks
   - if [[ $TRAVIS_RUST_VERSION = nightly* ]]; then
-      cargo bench -p benchmarks --verbose;
+      cargo bench --verbose;
     fi
diff --git a/Cargo.toml b/Cargo.toml
index 05a579b5c..cf6d54d77 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,7 +17,6 @@ appveyor = { repository = "danburkert/prost" }
 
 [workspace]
 members = [
-  "benchmarks",
   "conformance",
   "prost-build",
   "prost-derive",
@@ -36,7 +35,17 @@ byteorder = "1"
 bytes = "0.4.7"
 
 [dev-dependencies]
+criterion = "0.2"
 env_logger = { version = "0.5", default-features = false }
+failure = "0.1"
 log = "0.4"
 prost-derive = { version = "0.4.0", path = "prost-derive" }
+protobuf = { path = "protobuf" }
 quickcheck = "0.6"
+
+[[bench]]
+name = "varint"
+
+[[bench]]
+name = "benchmark"
+harness = false
diff --git a/benches/benchmark.rs b/benches/benchmark.rs
new file mode 100644
index 000000000..793820795
--- /dev/null
+++ b/benches/benchmark.rs
@@ -0,0 +1,106 @@
+extern crate bytes;
+extern crate criterion;
+extern crate prost;
+extern crate protobuf;
+
+#[macro_use]
+extern crate failure;
+
+use std::fs::File;
+use std::io::Read;
+use std::result;
+
+use criterion::{
+    Benchmark,
+    Criterion,
+    Throughput,
+};
+use prost::Message;
+use protobuf::benchmarks::{
+    BenchmarkDataset,
+    proto2,
+    proto3,
+};
+
+type Result = result::Result<(), failure::Error>;
+
+fn benchmark_dataset<M>(criterion: &mut Criterion, dataset: BenchmarkDataset) -> Result
+where M: prost::Message + Default + 'static {
+
+    let payload_len = dataset.payload.iter().map(Vec::len).sum::<usize>();
+
+    let messages = dataset.payload.iter().map(|buf| M::decode(buf)).collect::<result::Result<Vec<_>, _>>()?;
+    let encoded_len = messages.iter().map(|message| message.encoded_len()).sum::<usize>();
+
+    let mut buf = Vec::with_capacity(encoded_len);
+    let encode = Benchmark::new("encode", move |b| {
+        b.iter(|| {
+            buf.clear();
+            for message in &messages {
+                message.encode(&mut buf).unwrap();
+            }
+            criterion::black_box(&buf);
+        })
+    }).throughput(Throughput::Bytes(encoded_len as u32));
+
+    let payload = dataset.payload.clone();
+    let decode = Benchmark::new("decode", move |b| {
+        b.iter(|| {
+            for buf in &payload {
+                criterion::black_box(M::decode(buf).unwrap());
+            }
+        })
+    }).throughput(Throughput::Bytes(payload_len as u32));
+
+    let payload = dataset.payload.clone();
+    let merge = Benchmark::new("merge", move |b| {
+        let mut message = M::default();
+        b.iter(|| {
+            for buf in &payload {
+                message.clear();
+                message.merge(buf).unwrap();
+                criterion::black_box(&message);
+            }
+        })
+    }).throughput(Throughput::Bytes(payload_len as u32));
+
+    criterion
+        .bench(&dataset.name, encode)
+        .bench(&dataset.name, decode)
+        .bench(&dataset.name, merge);
+
+    Ok(())
+}
+
+fn main() -> Result {
+    let mut criterion = Criterion::default().configure_from_args();
+
+    for dataset in protobuf::benchmarks::datasets() {
+        let dataset = {
+            let mut f = File::open(dataset)?;
+            let mut buf = Vec::new();
+            f.read_to_end(&mut buf)?;
+            protobuf::benchmarks::BenchmarkDataset::decode(buf)?
+        };
+
+        match dataset.message_name.as_str() {
+            "benchmarks.proto2.GoogleMessage1" => benchmark_dataset::<proto2::GoogleMessage1>(&mut criterion, dataset)?,
+            "benchmarks.proto3.GoogleMessage1" => benchmark_dataset::<proto3::GoogleMessage1>(&mut criterion, dataset)?,
+
+            /*
+             TODO: groups are not yet supported
+            "benchmarks.proto2.GoogleMessage2" => benchmark_dataset::<proto2::GoogleMessage2>(&mut criterion, dataset)?,
+            "benchmarks.google_message3.GoogleMessage3" => benchmark_dataset::<GoogleMessage3>(&mut criterion, dataset)?,
+            "benchmarks.google_message4.GoogleMessage4" => benchmark_dataset::<GoogleMessage4>(&mut criterion, dataset)?,
+            */
+            "benchmarks.proto2.GoogleMessage2" => (),
+            "benchmarks.google_message3.GoogleMessage3" => (),
+            "benchmarks.google_message4.GoogleMessage4" => (),
+
+            _ => bail!("unknown dataset message type: {}", dataset.message_name),
+        }
+    }
+
+    criterion.final_summary();
+    Ok(())
+}
diff --git a/benchmarks/src/varint.rs b/benches/varint.rs
similarity index 97%
rename from benchmarks/src/varint.rs
rename to benches/varint.rs
index 836f72521..21882f533 100644
--- a/benchmarks/src/varint.rs
+++ b/benches/varint.rs
@@ -1,7 +1,10 @@
-use test;
+#![feature(test)]
 
-use bytes::IntoBuf;
+extern crate bytes;
+extern crate prost;
+extern crate test;
 
+use bytes::IntoBuf;
 use prost::encoding::{
     decode_varint,
     encode_varint,
diff --git a/benchmarks/Cargo.toml b/benchmarks/Cargo.toml
deleted file mode 100644
index dfc811831..000000000
--- a/benchmarks/Cargo.toml
+++ /dev/null
@@ -1,18 +0,0 @@
-[package]
-name = "benchmarks"
-version = "0.0.0"
-authors = ["Dan Burkert <dan@danburkert.com>"]
-publish = false
-
-[lib]
-doctest = false
-test = false
-
-[dependencies]
-bytes = "0.4.7"
-prost = { path = ".." }
-prost-derive = { path = "../prost-derive" }
-
-[build-dependencies]
-prost-build = { path = "../prost-build" }
-protobuf = { path = "../protobuf" }
diff --git a/benchmarks/build.rs b/benchmarks/build.rs
deleted file mode 100644
index 52ebebb76..000000000
--- a/benchmarks/build.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-extern crate prost_build;
-extern crate protobuf;
-
-fn main() {
-    let benchmarks = protobuf::include().join("benchmarks");
-    prost_build::compile_protos(&[benchmarks.join("benchmark_messages_proto2.proto")],
-                                &[benchmarks.clone()]).unwrap();
-    prost_build::compile_protos(&[benchmarks.join("benchmark_messages_proto3.proto")],
-                                &[benchmarks]).unwrap();
-
-    println!("cargo:rustc-env=GOOGLE_MESSAGE1={}",
-             protobuf::share().join("google_message1.dat").display());
-    println!("cargo:rustc-env=GOOGLE_MESSAGE2={}",
-             protobuf::share().join("google_message2.dat").display());
-}
diff --git a/benchmarks/src/lib.rs b/benchmarks/src/lib.rs
deleted file mode 100644
index f395ed7e3..000000000
--- a/benchmarks/src/lib.rs
+++ /dev/null
@@ -1,100 +0,0 @@
-#![feature(test)]
-
-extern crate bytes;
-extern crate prost;
-extern crate test;
-
-#[macro_use]
-extern crate prost_derive;
-
-mod varint;
-
-use bytes::IntoBuf;
-use prost::Message;
-
-pub fn bench_message_encode<M>(b: &mut test::Bencher, message: &M) where M: Message  {
-    let encoded_len = message.encoded_len();
-    let mut buf = Vec::with_capacity(encoded_len);
-    b.iter(|| {
-        buf.clear();
-        message.encode(&mut buf).unwrap();
-        test::black_box(&buf[..]);
-    });
-    b.bytes = encoded_len as u64;
-}
-
-pub fn bench_message_encode_raw<M>(b: &mut test::Bencher, message: &M) where M: Message  {
-    let encoded_len = message.encoded_len();
-    let mut buf = Vec::with_capacity(encoded_len);
-    b.iter(|| {
-        buf.clear();
-        message.encode_raw(&mut buf);
-        test::black_box(&buf[..]);
-    });
-    b.bytes = encoded_len as u64;
-}
-
-pub fn bench_message_decode<M>(b: &mut test::Bencher, bytes: &[u8]) where M: Message + Default {
-    b.iter(|| {
-        test::black_box(M::decode(bytes.into_buf()).unwrap());
-    });
-    b.bytes = bytes.len() as u64;
-}
-
-// Encoded GoogleMessage1 extracted from the protobuf cpp benchmarks.
-// https://github.com/google/protobuf/tree/3.3.x/benchmarks
-pub const GOOGLE_MESSAGE1: &'static [u8] = include_bytes!(env!("GOOGLE_MESSAGE1"));
-
-pub mod proto2 {
-    use bytes::IntoBuf;
-    use prost::Message;
-    use test;
-
-    use super::*;
-
-    include!(concat!(env!("OUT_DIR"), "/benchmarks.proto2.rs"));
-
-    #[bench]
-    fn google_message1_encode(b: &mut test::Bencher) {
-        let message = GoogleMessage1::decode(GOOGLE_MESSAGE1.into_buf()).unwrap();
-        bench_message_encode(b, &message);
-    }
-
-    #[bench]
-    fn google_message1_encode_raw(b: &mut test::Bencher) {
-        let message = GoogleMessage1::decode(GOOGLE_MESSAGE1.into_buf()).unwrap();
-        bench_message_encode_raw(b, &message);
-    }
-
-    #[bench]
-    fn google_message1_decode(b: &mut test::Bencher) {
-        bench_message_decode::<GoogleMessage1>(b, GOOGLE_MESSAGE1);
-    }
-}
-
-pub mod proto3 {
-    use bytes::IntoBuf;
-    use prost::Message;
-    use test;
-
-    use super::*;
-
-    include!(concat!(env!("OUT_DIR"), "/benchmarks.proto3.rs"));
-
-    #[bench]
-    fn google_message1_encode(b: &mut test::Bencher) {
-        let message = GoogleMessage1::decode(GOOGLE_MESSAGE1.into_buf()).unwrap();
-        bench_message_encode(b, &message);
-    }
-
-    #[bench]
-    fn google_message1_encode_raw(b: &mut test::Bencher) {
-        let message = GoogleMessage1::decode(GOOGLE_MESSAGE1.into_buf()).unwrap();
-        bench_message_encode_raw(b, &message);
-    }
-
-    #[bench]
-    fn google_message1_decode(b: &mut test::Bencher) {
-        bench_message_decode::<GoogleMessage1>(b, GOOGLE_MESSAGE1);
-    }
-}
diff --git a/conformance/Cargo.toml b/conformance/Cargo.toml
index 0b622b85b..b8a19a343 100644
--- a/conformance/Cargo.toml
+++ b/conformance/Cargo.toml
@@ -9,9 +9,5 @@ bytes = "0.4.7"
 env_logger = { version = "0.5", default-features = false }
 log = "0.3"
 prost = { path = ".." }
-prost-derive = { path = "../prost-derive" }
-tests = { path = "../tests" }
-
-[build-dependencies]
 protobuf = { path = "../protobuf" }
-prost-build = { path = "../prost-build" }
+tests = { path = "../tests" }
diff --git a/conformance/build.rs b/conformance/build.rs
deleted file mode 100644
index 493e80c83..000000000
--- a/conformance/build.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-extern crate prost_build;
-extern crate protobuf;
-
-fn main() {
-    // Emit an environment variable with the path to the conformance test runner so that it can be
-    // used in the conformance tests.
-    println!("cargo:rustc-env=CONFORMANCE_TEST_RUNNER={}",
-             protobuf::bin().join("conformance-test-runner").display());
-
-    let conformance = protobuf::include().join("conformance");
-    prost_build::compile_protos(&[conformance.join("conformance.proto")],
-                                &[conformance]).unwrap();
-}
diff --git a/conformance/src/main.rs b/conformance/src/main.rs
index 8ae48c5c7..fac368ba1 100644
--- a/conformance/src/main.rs
+++ b/conformance/src/main.rs
@@ -1,13 +1,9 @@
 extern crate bytes;
 extern crate env_logger;
 extern crate prost;
+extern crate protobuf;
 extern crate tests;
 
-#[macro_use]
-extern crate prost_derive;
-
-include!(concat!(env!("OUT_DIR"), "/conformance.rs"));
-
 use std::io::{
     Read,
     Write,
@@ -20,8 +16,15 @@ use bytes::{
 };
 use prost::Message;
 
-use tests::protobuf_test_messages::proto2::TestAllTypesProto2;
-use tests::protobuf_test_messages::proto3::TestAllTypesProto3;
+use protobuf::test_messages::proto2::TestAllTypesProto2;
+use protobuf::test_messages::proto3::TestAllTypesProto3;
+use protobuf::conformance::{
+    ConformanceRequest,
+    ConformanceResponse,
+    WireFormat,
+    conformance_request,
+    conformance_response,
+};
 use tests::{
     RoundtripResult,
     roundtrip,
diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml
index 1e921df5c..bcec96f9f 100644
--- a/fuzz/Cargo.toml
+++ b/fuzz/Cargo.toml
@@ -9,6 +9,7 @@ cargo-fuzz = true
 
 [dependencies]
 libfuzzer-sys = { git = "https://github.com/rust-fuzz/libfuzzer-sys.git" }
+protobuf = { path = "../protobuf" }
 tests = { path = "../tests" }
 
 [[bin]]
diff --git a/fuzz/fuzzers/proto3.rs b/fuzz/fuzzers/proto3.rs
index d1d2d03b6..da806d0f2 100644
--- a/fuzz/fuzzers/proto3.rs
+++ b/fuzz/fuzzers/proto3.rs
@@ -1,9 +1,10 @@
 #![no_main]
 
 #[macro_use] extern crate libfuzzer_sys;
+extern crate protobuf;
 extern crate tests;
 
-use tests::protobuf_test_messages::proto3::TestAllTypesProto3;
+use protobuf::test_messages::proto3::TestAllTypesProto3;
 use tests::roundtrip;
 
 fuzz_target!(|data: &[u8]| {
diff --git a/prost-build/src/lib.rs b/prost-build/src/lib.rs
index fc8272ae9..f44e9b191 100644
--- a/prost-build/src/lib.rs
+++ b/prost-build/src/lib.rs
@@ -205,6 +205,7 @@ pub struct Config {
     field_attributes: Vec<(String, String)>,
     prost_types: bool,
     strip_enum_prefix: bool,
+    out_dir: Option<PathBuf>,
 }
 
 impl Config {
@@ -369,6 +370,15 @@ impl Config {
         self
     }
 
+    /// Configures the output directory where generated Rust files will be written.
+    ///
+    /// If unset, defaults to the `OUT_DIR` environment variable. `OUT_DIR` is set by Cargo when
+    /// executing build scripts, so `out_dir` typically does not need to be configured.
+    pub fn out_dir<P>(&mut self, path: P) -> &mut Self where P: Into<PathBuf> {
+        self.out_dir = Some(path.into());
+        self
+    }
+
     /// Compile `.proto` files into Rust files during a Cargo build with additional code generator
     /// configuration options.
     ///
@@ -389,10 +399,12 @@ impl Config {
     /// }
     /// ```
     pub fn compile_protos<P>(&mut self, protos: &[P], includes: &[P]) -> Result<()> where P: AsRef<Path> {
-        let target: PathBuf = env::var_os("OUT_DIR")
-            .ok_or_else(|| Error::new(ErrorKind::Other,
-                                      "OUT_DIR environment variable is not set"))?
-            .into();
+        let target: PathBuf = self.out_dir.clone().map(Ok).unwrap_or_else(|| {
+            env::var_os("OUT_DIR")
+                .ok_or_else(|| Error::new(ErrorKind::Other,
+                                          "OUT_DIR environment variable is not set"))
+                .map(Into::into)
+        })?;
 
         // TODO: This should probably emit 'rerun-if-changed=PATH' directives for cargo, however
         // according to [1] if any are output then those paths replace the default crate root,
@@ -467,6 +479,7 @@ impl default::Default for Config {
             field_attributes: Vec::new(),
             prost_types: true,
             strip_enum_prefix: true,
+            out_dir: None,
         }
     }
 }
diff --git a/prost-build/third-party/protobuf/README.md b/prost-build/third-party/protobuf/README.md
index 7b1df5645..7cc361e36 100755
--- a/prost-build/third-party/protobuf/README.md
+++ b/prost-build/third-party/protobuf/README.md
@@ -1,11 +1,11 @@
-`LICENSE` was taken from https://github.com/google/protobuf/archive/v3.5.1.zip.
+`LICENSE` was taken from https://github.com/google/protobuf/archive/v3.6.0.zip.
 
 The `protoc-*` and `protoc-*.exe` binaries here were taken from:
 
-* https://github.com/google/protobuf/releases/download/v3.5.1/protoc-3.5.1-linux-aarch_64.zip
-* https://github.com/google/protobuf/releases/download/v3.5.1/protoc-3.5.1-linux-x86_32.zip
-* https://github.com/google/protobuf/releases/download/v3.5.1/protoc-3.5.1-linux-x86_64.zip
-* https://github.com/google/protobuf/releases/download/v3.5.1/protoc-3.5.1-osx-x86_64.zip
-* https://github.com/google/protobuf/releases/download/v3.5.1/protoc-3.5.1-win32.zip
+* https://github.com/google/protobuf/releases/download/v3.6.0/protoc-3.6.0-linux-aarch_64.zip
+* https://github.com/google/protobuf/releases/download/v3.6.0/protoc-3.6.0-linux-x86_32.zip
+* https://github.com/google/protobuf/releases/download/v3.6.0/protoc-3.6.0-linux-x86_64.zip
+* https://github.com/google/protobuf/releases/download/v3.6.0/protoc-3.6.0-osx-x86_64.zip
+* https://github.com/google/protobuf/releases/download/v3.6.0/protoc-3.6.0-win32.zip
 
 The `include` directory's contents were taken from the linux-x86_64 package.
diff --git a/prost-build/third-party/protobuf/include/google/protobuf/any.proto b/prost-build/third-party/protobuf/include/google/protobuf/any.proto
index c74866762..493294255 100644
--- a/prost-build/third-party/protobuf/include/google/protobuf/any.proto
+++ b/prost-build/third-party/protobuf/include/google/protobuf/any.proto
@@ -120,17 +120,18 @@ option objc_class_prefix = "GPB";
 //     }
 //
 message Any {
-  // A URL/resource name whose content describes the type of the
-  // serialized protocol buffer message.
+  // A URL/resource name that uniquely identifies the type of the serialized
+  // protocol buffer message. The last segment of the URL's path must represent
+  // the fully qualified name of the type (as in
+  // `path/google.protobuf.Duration`). The name should be in a canonical form
+  // (e.g., leading "." is not accepted).
   //
-  // For URLs which use the scheme `http`, `https`, or no scheme, the
-  // following restrictions and interpretations apply:
+  // In practice, teams usually precompile into the binary all types that they
+  // expect it to use in the context of Any. However, for URLs which use the
+  // scheme `http`, `https`, or no scheme, one can optionally set up a type
+  // server that maps type URLs to message definitions as follows:
   //
   // * If no scheme is provided, `https` is assumed.
-  // * The last segment of the URL's path must represent the fully
-  //   qualified name of the type (as in `path/google.protobuf.Duration`).
-  //   The name should be in a canonical form (e.g., leading "." is
-  //   not accepted).
   // * An HTTP GET on the URL must yield a [google.protobuf.Type][]
   //   value in binary format, or produce an error.
   // * Applications are allowed to cache lookup results based on the
@@ -139,6 +140,10 @@ message Any {
   //   on changes to types. (Use versioned type names to manage
   //   breaking changes.)
   //
+  // Note: this functionality is not currently available in the official
+  // protobuf release, and it is not used for type URLs beginning with
+  // type.googleapis.com.
+  //
   // Schemes other than `http`, `https` (or the empty scheme) might be
   // used with implementation specific semantics.
   //
diff --git a/prost-build/third-party/protobuf/include/google/protobuf/descriptor.proto b/prost-build/third-party/protobuf/include/google/protobuf/descriptor.proto
index 8697a50de..ed08fcbc5 100644
--- a/prost-build/third-party/protobuf/include/google/protobuf/descriptor.proto
+++ b/prost-build/third-party/protobuf/include/google/protobuf/descriptor.proto
@@ -417,6 +417,17 @@ message FileOptions {
   // determining the namespace.
   optional string php_namespace = 41;
 
+
+  // Use this option to change the namespace of php generated metadata classes.
+  // Default is empty. When this option is empty, the proto file name will be used
+  // for determining the namespace.
+  optional string php_metadata_namespace = 44;
+
+  // Use this option to change the package of ruby generated classes. Default
+  // is empty. When this option is not set, the package name will be used for
+  // determining the ruby package.
+  optional string ruby_package = 45;
+
   // The parser stores options it doesn't recognize here.
   // See the documentation for the "Options" section above.
   repeated UninterpretedOption uninterpreted_option = 999;
diff --git a/prost-build/third-party/protobuf/include/google/protobuf/field_mask.proto b/prost-build/third-party/protobuf/include/google/protobuf/field_mask.proto
index eb96ba00a..76e09f391 100644
--- a/prost-build/third-party/protobuf/include/google/protobuf/field_mask.proto
+++ b/prost-build/third-party/protobuf/include/google/protobuf/field_mask.proto
@@ -243,8 +243,8 @@ option go_package = "google.golang.org/genproto/protobuf/field_mask;field_mask";
 //
 // ## Field Mask Verification
 //
-// The implementation of the all the API methods, which have any FieldMask type
-// field in the request, should verify the included field paths, and return
+// The implementation of any API method which has a FieldMask type field in the
+// request should verify the included field paths, and return an
 // `INVALID_ARGUMENT` error if any path is duplicated or unmappable.
 message FieldMask {
   // The set of field mask paths.
diff --git a/prost-build/third-party/protobuf/include/google/protobuf/timestamp.proto b/prost-build/third-party/protobuf/include/google/protobuf/timestamp.proto
index 06750ab1f..eafb3fa03 100644
--- a/prost-build/third-party/protobuf/include/google/protobuf/timestamp.proto
+++ b/prost-build/third-party/protobuf/include/google/protobuf/timestamp.proto
@@ -103,7 +103,9 @@ option objc_class_prefix = "GPB";
 // {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional
 // seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
 // are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone
-// is required, though only UTC (as indicated by "Z") is presently supported.
+// is required. A proto3 JSON serializer should always use UTC (as indicated by
+// "Z") when printing the Timestamp type and a proto3 JSON parser should be
+// able to accept both UTC and other timezones (as indicated by an offset).
 //
 // For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past
 // 01:30 UTC on January 15, 2017.
@@ -114,8 +116,8 @@ option objc_class_prefix = "GPB";
 // to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime)
 // with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one
 // can use the Joda Time's [`ISODateTimeFormat.dateTime()`](
-// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime--)
-// to obtain a formatter capable of generating timestamps in this format.
+// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime--
+// ) to obtain a formatter capable of generating timestamps in this format.
 //
 //
 message Timestamp {
diff --git a/prost-build/third-party/protobuf/protoc-linux-aarch_64 b/prost-build/third-party/protobuf/protoc-linux-aarch_64
index be19f78dc..1e156d4bd 100755
Binary files a/prost-build/third-party/protobuf/protoc-linux-aarch_64 and b/prost-build/third-party/protobuf/protoc-linux-aarch_64 differ
diff --git a/prost-build/third-party/protobuf/protoc-linux-x86_32 b/prost-build/third-party/protobuf/protoc-linux-x86_32
index 96516b531..44e5ac442 100755
Binary files a/prost-build/third-party/protobuf/protoc-linux-x86_32 and b/prost-build/third-party/protobuf/protoc-linux-x86_32 differ
diff --git a/prost-build/third-party/protobuf/protoc-linux-x86_64 b/prost-build/third-party/protobuf/protoc-linux-x86_64
index 07689c0d9..1cb992fed 100755
Binary files a/prost-build/third-party/protobuf/protoc-linux-x86_64 and b/prost-build/third-party/protobuf/protoc-linux-x86_64 differ
diff --git a/prost-build/third-party/protobuf/protoc-osx-x86_64 b/prost-build/third-party/protobuf/protoc-osx-x86_64
index 3996f4da3..178595c17 100755
Binary files a/prost-build/third-party/protobuf/protoc-osx-x86_64 and b/prost-build/third-party/protobuf/protoc-osx-x86_64 differ
diff --git a/prost-build/third-party/protobuf/protoc-win32.exe b/prost-build/third-party/protobuf/protoc-win32.exe
index 16bc9c310..73a388dde 100755
Binary files a/prost-build/third-party/protobuf/protoc-win32.exe and b/prost-build/third-party/protobuf/protoc-win32.exe differ
diff --git a/prost-types/src/protobuf.rs b/prost-types/src/protobuf.rs
index 60182a688..af491cf74 100644
--- a/prost-types/src/protobuf.rs
+++ b/prost-types/src/protobuf.rs
@@ -398,6 +398,16 @@ pub struct FileOptions {
     /// determining the namespace.
     #[prost(string, optional, tag="41")]
     pub php_namespace: ::std::option::Option<String>,
+    /// Use this option to change the namespace of php generated metadata classes.
+    /// Default is empty. When this option is empty, the proto file name will be used
+    /// for determining the namespace.
+    #[prost(string, optional, tag="44")]
+    pub php_metadata_namespace: ::std::option::Option<String>,
+    /// Use this option to change the package of ruby generated classes. Default
+    /// is empty. When this option is not set, the package name will be used for
+    /// determining the ruby package.
+    #[prost(string, optional, tag="45")]
+    pub ruby_package: ::std::option::Option<String>,
     /// The parser stores options it doesn't recognize here.
     /// See the documentation for the "Options" section above.
     #[prost(message, repeated, tag="999")]
@@ -944,17 +954,18 @@ pub mod generated_code_info {
 ///
 #[derive(Clone, PartialEq, Message)]
 pub struct Any {
-    /// A URL/resource name whose content describes the type of the
-    /// serialized protocol buffer message.
+    /// A URL/resource name that uniquely identifies the type of the serialized
+    /// protocol buffer message. The last segment of the URL's path must represent
+    /// the fully qualified name of the type (as in
+    /// `path/google.protobuf.Duration`). The name should be in a canonical form
+    /// (e.g., leading "." is not accepted).
     ///
-    /// For URLs which use the scheme `http`, `https`, or no scheme, the
-    /// following restrictions and interpretations apply:
+    /// In practice, teams usually precompile into the binary all types that they
+    /// expect it to use in the context of Any. However, for URLs which use the
+    /// scheme `http`, `https`, or no scheme, one can optionally set up a type
+    /// server that maps type URLs to message definitions as follows:
     ///
     /// * If no scheme is provided, `https` is assumed.
-    /// * The last segment of the URL's path must represent the fully
-    ///   qualified name of the type (as in `path/google.protobuf.Duration`).
-    ///   The name should be in a canonical form (e.g., leading "." is
-    ///   not accepted).
     /// * An HTTP GET on the URL must yield a [google.protobuf.Type][]
     ///   value in binary format, or produce an error.
     /// * Applications are allowed to cache lookup results based on the
@@ -963,6 +974,10 @@ pub struct Any {
     ///   on changes to types. (Use versioned type names to manage
     ///   breaking changes.)
     ///
+    /// Note: this functionality is not currently available in the official
+    /// protobuf release, and it is not used for type URLs beginning with
+    /// type.googleapis.com.
+    ///
     /// Schemes other than `http`, `https` (or the empty scheme) might be
     /// used with implementation specific semantics.
     ///
@@ -1600,8 +1615,8 @@ pub struct Duration {
 ///
 /// ## Field Mask Verification
 ///
-/// The implementation of the all the API methods, which have any FieldMask type
-/// field in the request, should verify the included field paths, and return
+/// The implementation of any API method which has a FieldMask type field in the
+/// request should verify the included field paths, and return an
 /// `INVALID_ARGUMENT` error if any path is duplicated or unmappable.
 #[derive(Clone, PartialEq, Message)]
 pub struct FieldMask {
@@ -1740,7 +1755,9 @@ pub enum NullValue {
 /// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional
 /// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
 /// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone
-/// is required, though only UTC (as indicated by "Z") is presently supported.
+/// is required. A proto3 JSON serializer should always use UTC (as indicated by
+/// "Z") when printing the Timestamp type and a proto3 JSON parser should be
+/// able to accept both UTC and other timezones (as indicated by an offset).
 ///
 /// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past
 /// 01:30 UTC on January 15, 2017.
@@ -1751,8 +1768,8 @@ pub enum NullValue {
 /// to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime)
 /// with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one
 /// can use the Joda Time's [`ISODateTimeFormat.dateTime()`](
-/// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime--)
-/// to obtain a formatter capable of generating timestamps in this format.
+/// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime--
+/// ) to obtain a formatter capable of generating timestamps in this format.
 ///
 ///
 #[derive(Clone, PartialEq, Message)]
diff --git a/protobuf/Cargo.toml b/protobuf/Cargo.toml
index 441813c05..58674ebbe 100644
--- a/protobuf/Cargo.toml
+++ b/protobuf/Cargo.toml
@@ -4,9 +4,16 @@ version = "0.0.0"
 authors = ["Dan Burkert <dan@danburkert.com>"]
 publish = false
 
+[dependencies]
+bytes = "0.4.7"
+prost = { path = ".." }
+prost-derive = { path = "../prost-derive" }
+prost-types = { path = "../prost-types" }
+
 [build-dependencies]
 cfg-if = "0.1"
 curl = "0.4"
 flate2 = "1.0"
+prost-build = { path = "../prost-build" }
 tar = "0.4"
 tempdir = "0.3"
diff --git a/protobuf/build.rs b/protobuf/build.rs
index d4aba4be6..366a3723d 100644
--- a/protobuf/build.rs
+++ b/protobuf/build.rs
@@ -1,5 +1,6 @@
 extern crate curl;
 extern crate flate2;
+extern crate prost_build;
 extern crate tar;
 extern crate tempdir;
 
@@ -14,22 +15,114 @@ use flate2::bufread::GzDecoder;
 use tar::Archive;
 use tempdir::TempDir;
 
-const VERSION: &'static str = "3.5.1.1";
+const VERSION: &'static str = "3.6.0.1";
+
+static TEST_PROTOS: &[&str] = &[
+    "test_messages_proto2.proto",
+    "test_messages_proto3.proto",
+    "unittest.proto",
+    "unittest_import.proto",
+    "unittest_import_public.proto"
+];
+
+static DATASET_PROTOS: &[&str] = &[
+    "google_message1/proto2/benchmark_message1_proto2.proto",
+    "google_message1/proto3/benchmark_message1_proto3.proto",
+    "google_message2/benchmark_message2.proto",
+    "google_message3/benchmark_message3.proto",
+    "google_message3/benchmark_message3_1.proto",
+    "google_message3/benchmark_message3_2.proto",
+    "google_message3/benchmark_message3_3.proto",
+    "google_message3/benchmark_message3_4.proto",
+    "google_message3/benchmark_message3_5.proto",
+    "google_message3/benchmark_message3_6.proto",
+    "google_message3/benchmark_message3_7.proto",
+    "google_message3/benchmark_message3_8.proto",
+    "google_message4/benchmark_message4.proto",
+    "google_message4/benchmark_message4_1.proto",
+    "google_message4/benchmark_message4_2.proto",
+    "google_message4/benchmark_message4_3.proto",
+];
 
 fn main() {
-    let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR environment variable not set"));
-    let protobuf_dir = out_dir.join(format!("protobuf-{}", VERSION));
+    let out_dir = &PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR environment variable not set"));
+    let protobuf_dir = &out_dir.join(format!("protobuf-{}", VERSION));
 
     if !protobuf_dir.exists() {
-        let tempdir = TempDir::new_in(&out_dir, "protobuf")
+        let tempdir = TempDir::new_in(out_dir, "protobuf")
                               .expect("failed to create temporary directory");
 
-        // Download the protobuf source tarball.
-        download_protobuf(tempdir.path());
-        let src_dir = tempdir.path().join(format!("protobuf-{}", VERSION));
-        let prefix_dir = tempdir.path().join("prefix");
-        fs::create_dir(&prefix_dir).expect("failed to create temporary build directory");
+        let src_dir = &download_protobuf(tempdir.path());
+        let prefix_dir = &src_dir.join("prefix");
+        fs::create_dir(prefix_dir).expect("failed to create prefix directory");
+        install_conformance_test_runner(src_dir, prefix_dir);
+        install_protos(src_dir, prefix_dir);
+        install_datasets(src_dir, prefix_dir);
+        fs::rename(prefix_dir, protobuf_dir).expect("failed to move protobuf dir");
+    }
+
+    let include_dir = &protobuf_dir.join("include");
+    let benchmarks_include_dir = &include_dir.join("benchmarks");
+    let datasets_include_dir = &benchmarks_include_dir.join("datasets");
+    let mut benchmark_protos = vec![benchmarks_include_dir.join("benchmarks.proto")];
+    benchmark_protos.extend(DATASET_PROTOS.iter().map(|proto| datasets_include_dir.join(proto)));
+    prost_build::compile_protos(&benchmark_protos, &[benchmarks_include_dir.to_path_buf()]).unwrap();
+
+    let conformance_include_dir = include_dir.join("conformance");
+    prost_build::compile_protos(&[conformance_include_dir.join("conformance.proto")],
+                                &[conformance_include_dir]).unwrap();
+
+    let test_includes = &include_dir.join("google").join("protobuf");
+
+    // Generate BTreeMap fields for all messages. This forces encoded output to be consistent, so
+    // that encode/decode roundtrips can use encoded output for comparison. Otherwise trying to
+    // compare based on the Rust PartialEq implementations is difficult, due to presence of NaN
+    // values.
+    let mut config = prost_build::Config::new();
+    config.btree_map(&["."]);
+
+    prost_build::Config::new()
+        .btree_map(&["."])
+        .compile_protos(&[
+            test_includes.join("test_messages_proto2.proto"),
+            test_includes.join("test_messages_proto3.proto"),
+            test_includes.join("unittest.proto"),
+        ], &[include_dir.to_path_buf()]).unwrap();
+
+    // Emit an environment variable with the path to the build so that it can be located in the
+    // main crate.
+    println!("cargo:rustc-env=PROTOBUF={}", protobuf_dir.display());
+}
+
+fn download_tarball(url: &str, out_dir: &Path) {
+    let mut data = Vec::new();
+    let mut handle = Easy::new();
 
+    handle.url(url).expect("failed to configure tarball URL");
+    handle.follow_location(true).expect("failed to configure follow location");
+    {
+        let mut transfer = handle.transfer();
+        transfer.write_function(|new_data| {
+            data.extend_from_slice(new_data);
+            Ok(new_data.len())
+        }).expect("failed to write download data");
+        transfer.perform().expect("failed to download tarball");
+    }
+
+    Archive::new(GzDecoder::new(Cursor::new(data)))
+            .unpack(out_dir).expect("failed to unpack tarball");
+}
+
+/// Downloads and unpacks a Protobuf release tarball to the provided directory.
+fn download_protobuf(out_dir: &Path) -> PathBuf {
+    download_tarball(&format!("https://github.com/google/protobuf/archive/v{}.tar.gz", VERSION),
+                     out_dir);
+    out_dir.join(format!("protobuf-{}", VERSION))
+}
+
+fn install_conformance_test_runner(src_dir: &Path, prefix_dir: &Path) {
+    #[cfg(not(windows))]
+    {
         // Build and install protoc, the protobuf libraries, and the conformance test runner.
         let rc = Command::new("./autogen.sh")
                         .current_dir(&src_dir)
@@ -61,64 +154,62 @@ fn main() {
                         .current_dir(src_dir.join("conformance"))
                         .status()
                         .expect("failed to execute make conformance");
-        assert!(rc.success(), "failed to make protobuf");
-
-        // Copy .protos and data into the install directory.
-        fs::create_dir(prefix_dir.join("share")).expect("failed to create share directory");
-        fs::create_dir(prefix_dir.join("include").join("benchmarks"))
-           .expect("failed to create benchmarks include directory");
-        fs::create_dir(prefix_dir.join("include").join("conformance"))
-           .expect("failed to create conformance include directory");
-
-        for proto in &[
-            "benchmarks/benchmark_messages_proto2.proto",
-            "benchmarks/benchmark_messages_proto3.proto",
-            "conformance/conformance.proto"
-        ] {
-            fs::rename(src_dir.join(proto), prefix_dir.join("include").join(proto))
-               .expect(&format!("failed to move {}", proto));
-        }
-        for proto in &[
-            "google/protobuf/test_messages_proto2.proto",
-            "google/protobuf/test_messages_proto3.proto",
-            "google/protobuf/unittest.proto",
-            "google/protobuf/unittest_import.proto",
-            "google/protobuf/unittest_import_public.proto"
-        ] {
-            fs::rename(src_dir.join("src").join(proto), prefix_dir.join("include").join(proto))
-               .expect(&format!("failed to move {}", proto));
-        }
-
-        for dat in &["google_message1.dat", "google_message2.dat"] {
-            fs::rename(src_dir.join("benchmarks").join(dat), prefix_dir.join("share").join(dat))
-            .expect(&format!("failed to move {}", dat));
-        }
-
-        fs::rename(&prefix_dir, &protobuf_dir).expect("unable to move temporary directory");
+        assert!(rc.success(), "failed to make conformance");
     }
-
-    // Emit an environment variable with the path to the conformance test runner
-    // so that it can be used in the conformance tests.
-    println!("cargo:rustc-env=PROTOBUF={}", protobuf_dir.display());
 }
 
-fn download_protobuf(out_dir: &Path) {
-    let mut data = Vec::new();
-    let mut handle = Easy::new();
+fn install_protos(src_dir: &Path, prefix_dir: &Path) {
+    let include_dir = prefix_dir.join("include");
 
-    handle.url(&format!("https://github.com/google/protobuf/archive/v{}.tar.gz", VERSION))
-          .expect("failed to configure protobuf tarball URL");
-    handle.follow_location(true)
-          .expect("failed to configure follow location");
-    {
-        let mut transfer = handle.transfer();
-        transfer.write_function(|new_data| {
-            data.extend_from_slice(new_data);
-            Ok(new_data.len())
-        }).expect("failed to write download data");
-        transfer.perform().expect("failed to download protobuf");
+    // Move test protos to the prefix directory.
+    let test_include_dir = &include_dir.join("google").join("protobuf");
+    fs::create_dir_all(test_include_dir).expect("failed to create test include directory");
+    for proto in TEST_PROTOS {
+        fs::rename(src_dir.join("src").join("google").join("protobuf").join(proto),
+                   test_include_dir.join(proto))
+            .expect(&format!("failed to move {}", proto));
     }
 
-    Archive::new(GzDecoder::new(Cursor::new(data)))
-            .unpack(out_dir).expect("failed to unpack protobuf tarball");
+    // Move conformance.proto to the install directory.
+    let conformance_include_dir = &include_dir.join("conformance");
+    fs::create_dir(conformance_include_dir)
+        .expect("failed to create conformance include directory");
+    fs::rename(src_dir.join("conformance").join("conformance.proto"),
+               conformance_include_dir.join("conformance.proto"))
+        .expect("failed to move conformance.proto");
+
+    // Move the benchmark datasets to the install directory.
+    let benchmarks_src_dir = &src_dir.join("benchmarks");
+    let benchmarks_include_dir = &include_dir.join("benchmarks");
+    let datasets_src_dir = &benchmarks_src_dir.join("datasets");
+    let datasets_include_dir = &benchmarks_include_dir.join("datasets");
+    fs::create_dir(benchmarks_include_dir)
+        .expect("failed to create benchmarks include directory");
+    fs::rename(benchmarks_src_dir.join("benchmarks.proto"),
+               benchmarks_include_dir.join("benchmarks.proto"))
+        .expect("failed to move benchmarks.proto");
+    for proto in DATASET_PROTOS.iter().map(Path::new) {
+        let dir = &datasets_include_dir.join(proto.parent().unwrap());
+        fs::create_dir_all(dir).expect(&format!("unable to create directory {}", dir.display()));
+        fs::rename(datasets_src_dir.join(proto), datasets_include_dir.join(proto))
+            .expect(&format!("failed to move {}", proto.display()));
+    }
+}
+
+fn install_datasets(src_dir: &Path, prefix_dir: &Path) {
+    let share_dir = &prefix_dir.join("share");
+    fs::create_dir(share_dir)
+        .expect("failed to create share directory");
+    for dataset in &[
+        Path::new("google_message1").join("proto2").join("dataset.google_message1_proto2.pb"),
+        Path::new("google_message1").join("proto3").join("dataset.google_message1_proto3.pb"),
+        Path::new("google_message2").join("dataset.google_message2.pb"),
+    ] {
+        fs::rename(src_dir.join("benchmarks").join("datasets").join(dataset),
+                   share_dir.join(dataset.file_name().unwrap()))
+            .expect(&format!("failed to move {}", dataset.display()));
+    };
+
+    download_tarball("https://storage.googleapis.com/protobuf_opensource_benchmark_data/datasets.tar.gz",
+                     share_dir);
 }
diff --git a/protobuf/src/lib.rs b/protobuf/src/lib.rs
index 031b1068d..7e1cc7510 100644
--- a/protobuf/src/lib.rs
+++ b/protobuf/src/lib.rs
@@ -1,19 +1,63 @@
-use std::path::{Path, PathBuf};
+extern crate bytes;
+extern crate prost;
+extern crate prost_types;
 
-const PROTOBUF: &'static str = env!("PROTOBUF");
+#[macro_use] extern crate prost_derive;
 
-/// Returns the path to the installed Protobuf bin directory.
-pub fn bin() -> PathBuf {
-    Path::new(PROTOBUF).join("bin")
+pub mod benchmarks {
+    include!(concat!(env!("OUT_DIR"), "/benchmarks.rs"));
+
+    use std::path::Path;
+
+    pub fn datasets() -> Vec<&'static Path> {
+        vec![
+            Path::new(concat!(env!("PROTOBUF"), "/share/dataset.google_message1_proto2.pb")),
+            Path::new(concat!(env!("PROTOBUF"), "/share/dataset.google_message1_proto3.pb")),
+            Path::new(concat!(env!("PROTOBUF"), "/share/dataset.google_message2.pb")),
+            Path::new(concat!(env!("PROTOBUF"), "/share/dataset.google_message3_1.pb")),
+            Path::new(concat!(env!("PROTOBUF"), "/share/dataset.google_message3_2.pb")),
+            Path::new(concat!(env!("PROTOBUF"), "/share/dataset.google_message3_3.pb")),
+            Path::new(concat!(env!("PROTOBUF"), "/share/dataset.google_message3_4.pb")),
+            Path::new(concat!(env!("PROTOBUF"), "/share/dataset.google_message3_5.pb")),
+            Path::new(concat!(env!("PROTOBUF"), "/share/dataset.google_message4.pb")),
+        ]
+    }
+
+    pub mod google_message3 {
+        include!(concat!(env!("OUT_DIR"), "/benchmarks.google_message3.rs"));
+    }
+    pub mod google_message4 {
+        include!(concat!(env!("OUT_DIR"), "/benchmarks.google_message4.rs"));
+    }
+    pub mod proto2 {
+        include!(concat!(env!("OUT_DIR"), "/benchmarks.proto2.rs"));
+    }
+    pub mod proto3 {
+        include!(concat!(env!("OUT_DIR"), "/benchmarks.proto3.rs"));
+    }
 }
 
-/// Returns the path to the installed Protobuf include directory.
-pub fn include() -> PathBuf {
-    Path::new(PROTOBUF).join("include")
+pub mod conformance {
+    use std::path::Path;
+
+    pub fn test_runner() -> &'static Path {
+        Path::new(concat!(env!("PROTOBUF"), "/bin/conformance-test-runner"))
+    }
+
+    include!(concat!(env!("OUT_DIR"), "/conformance.rs"));
 }
 
-/// Returns the path to the installed Protobuf share directory, which includes benchmarking
-/// datasets.
-pub fn share() -> PathBuf {
-    Path::new(PROTOBUF).join("share")
+pub mod test_messages {
+    pub mod proto2 {
+        include!(concat!(env!("OUT_DIR"), "/protobuf_test_messages.proto2.rs"));
+    }
+    pub mod proto3 {
+        include!(concat!(env!("OUT_DIR"), "/protobuf_test_messages.proto3.rs"));
+    }
+    pub mod protobuf_unittest {
+        include!(concat!(env!("OUT_DIR"), "/protobuf_unittest.rs"));
+    }
+    pub mod protobuf_unittest_import {
+        include!(concat!(env!("OUT_DIR"), "/protobuf_unittest_import.rs"));
+    }
 }


Test patch:
diff --git a/conformance/tests/conformance.rs b/conformance/tests/conformance.rs
index 5aa4c72fe..127e10f87 100644
--- a/conformance/tests/conformance.rs
+++ b/conformance/tests/conformance.rs
@@ -1,6 +1,10 @@
+extern crate protobuf;
+
 use std::env;
 use std::process::Command;
 
+use protobuf::conformance;
+
 /// Runs the protobuf conformance test. This must be done in an integration test
 /// so that Cargo will build the proto-conformance binary.
 #[test]
@@ -15,7 +19,7 @@ fn test_conformance() {
         path.join("conformance")
     }).unwrap();
 
-    let status = Command::new(env!("CONFORMANCE_TEST_RUNNER"))
+    let status = Command::new(conformance::test_runner())
                          .arg("--enforce_recommended")
                          .arg("--failure_list").arg("failing_tests.txt")
                          .arg(proto_conformance)
diff --git a/tests/Cargo.toml b/tests/Cargo.toml
index 174877985..7ac64cce5 100644
--- a/tests/Cargo.toml
+++ b/tests/Cargo.toml
@@ -18,6 +18,7 @@ bytes = "0.4.7"
 prost = { path = ".." }
 prost-derive = { path = "../prost-derive" }
 prost-types = { path = "../prost-types" }
+protobuf = { path = "../protobuf" }
 
 [dev-dependencies]
 diff = "0.1"
diff --git a/tests/src/bootstrap.rs b/tests/src/bootstrap.rs
index 2ef0f779a..4a7174130 100644
--- a/tests/src/bootstrap.rs
+++ b/tests/src/bootstrap.rs
@@ -1,4 +1,3 @@
-use std::env;
 use std::fs;
 use std::io::Read;
 use std::io::Write;
@@ -14,11 +13,11 @@ fn bootstrap() {
     let protobuf = Path::new(prost_build::protoc_include()).join("google").join("protobuf");
 
     let tempdir = tempdir::TempDir::new("prost-types-bootstrap").unwrap();
-    env::set_var("OUT_DIR", tempdir.path());
 
     let mut config = prost_build::Config::new();
     config.compile_well_known_types();
     config.btree_map(&["."]);
+    config.out_dir(tempdir.path());
     config.compile_protos(&[
                             // Protobuf Plugins.
                             protobuf.join("descriptor.proto"),
diff --git a/tests/src/build.rs b/tests/src/build.rs
index 66eaf8bc6..1ff90cd41 100644
--- a/tests/src/build.rs
+++ b/tests/src/build.rs
@@ -5,8 +5,6 @@ extern crate protobuf;
 fn main() {
     let _ = env_logger::init();
 
-    let proto_includes = protobuf::include().join("google").join("protobuf");
-
     // Generate BTreeMap fields for all messages. This forces encoded output to be consistent, so
     // that encode/decode roundtrips can use encoded output for comparison. Otherwise trying to
     // compare based on the Rust PartialEq implementations is difficult, due to presence of NaN
@@ -27,15 +25,6 @@ fn main() {
     prost_build.field_attribute("Foo.Custom.Attrs.Msg.field.a", "/// Oneof A docs");
     prost_build.field_attribute("Foo.Custom.Attrs.Msg.field.b", "/// Oneof B docs");
 
-    prost_build.compile_protos(&[proto_includes.join("test_messages_proto2.proto")],
-                               &[protobuf::include()]).unwrap();
-
-    prost_build.compile_protos(&[proto_includes.join("test_messages_proto3.proto")],
-                               &[protobuf::include()]).unwrap();
-
-    prost_build.compile_protos(&[proto_includes.join("unittest.proto")],
-                               &[protobuf::include()]).unwrap();
-
     prost_build.compile_protos(&["src/packages/widget_factory.proto"],
                                &["src/packages"]).unwrap();
 
diff --git a/tests/src/lib.rs b/tests/src/lib.rs
index 8bc60921f..45154f794 100644
--- a/tests/src/lib.rs
+++ b/tests/src/lib.rs
@@ -1,6 +1,7 @@
 extern crate bytes;
 extern crate prost;
 extern crate prost_types;
+extern crate protobuf;
 
 #[macro_use] extern crate prost_derive;
 
@@ -15,21 +16,6 @@ pub mod unittest;
 #[cfg(test)] mod message_encoding;
 #[cfg(test)] mod no_unused_results;
 
-pub mod protobuf_test_messages {
-    pub mod proto2 {
-        include!(concat!(env!("OUT_DIR"), "/protobuf_test_messages.proto2.rs"));
-    }
-    pub mod proto3 {
-        include!(concat!(env!("OUT_DIR"), "/protobuf_test_messages.proto3.rs"));
-    }
-}
-
-pub mod google {
-    pub mod protobuf {
-        include!(concat!(env!("OUT_DIR"), "/google.protobuf.rs"));
-    }
-}
-
 pub mod foo {
     pub mod bar_baz {
         include!(concat!(env!("OUT_DIR"), "/foo.bar_baz.rs"));
@@ -182,7 +168,7 @@ mod tests {
 
     use std::collections::{BTreeMap, BTreeSet};
 
-    use protobuf_test_messages::proto3::TestAllTypesProto3;
+    use protobuf::test_messages::proto3::TestAllTypesProto3;
     use super::*;
 
     #[test]
diff --git a/tests/src/unittest.rs b/tests/src/unittest.rs
index 285edabac..8d117f0e0 100644
--- a/tests/src/unittest.rs
+++ b/tests/src/unittest.rs
@@ -1,14 +1,8 @@
-pub mod protobuf_unittest {
-    include!(concat!(env!("OUT_DIR"), "/protobuf_unittest.rs"));
-}
-
-pub mod protobuf_unittest_import {
-    include!(concat!(env!("OUT_DIR"), "/protobuf_unittest_import.rs"));
-}
-
 #[test]
 fn extreme_default_values() {
     use std::{f32, f64};
+    use protobuf::test_messages::protobuf_unittest;
+
     let pb = protobuf_unittest::TestExtremeDefaultValues::default();
 
     assert_eq!(b"\0\x01\x07\x08\x0C\n\r\t\x0B\\\'\"\xFE", pb.escaped_bytes());