1 #![doc(html_root_url = "https://docs.rs/prost-build/0.8.0")]
2 #![allow(clippy::option_as_ref_deref)]
3 
4 //! `prost-build` compiles `.proto` files into Rust.
5 //!
6 //! `prost-build` is designed to be used for build-time code generation as part of a Cargo
7 //! build-script.
8 //!
9 //! ## Example
10 //!
11 //! Let's create a small crate, `snazzy`, that defines a collection of
12 //! snazzy new items in a protobuf file.
13 //!
14 //! ```bash
15 //! $ cargo new snazzy && cd snazzy
16 //! ```
17 //!
18 //! First, add `prost-build`, `prost` and its public dependencies to `Cargo.toml`
19 //! (see [crates.io](https://crates.io/crates/prost) for the current versions):
20 //!
21 //! ```toml
22 //! [dependencies]
23 //! bytes = <bytes-version>
24 //! prost = <prost-version>
25 //!
26 //! [build-dependencies]
27 //! prost-build = { version = <prost-version> }
28 //! ```
29 //!
30 //! Next, add `src/items.proto` to the project:
31 //!
32 //! ```proto
33 //! syntax = "proto3";
34 //!
35 //! package snazzy.items;
36 //!
37 //! // A snazzy new shirt!
38 //! message Shirt {
39 //!     enum Size {
40 //!         SMALL = 0;
41 //!         MEDIUM = 1;
42 //!         LARGE = 2;
43 //!     }
44 //!
45 //!     string color = 1;
46 //!     Size size = 2;
47 //! }
48 //! ```
49 //!
50 //! To generate Rust code from `items.proto`, we use `prost-build` in the crate's
51 //! `build.rs` build-script:
52 //!
53 //! ```rust,no_run
54 //! use std::io::Result;
55 //! fn main() -> Result<()> {
56 //!     prost_build::compile_protos(&["src/items.proto"], &["src/"])?;
57 //!     Ok(())
58 //! }
59 //! ```
60 //!
61 //! And finally, in `lib.rs`, include the generated code:
62 //!
63 //! ```rust,ignore
64 //! // Include the `items` module, which is generated from items.proto.
65 //! pub mod items {
66 //!     include!(concat!(env!("OUT_DIR"), "/snazzy.items.rs"));
67 //! }
68 //!
69 //! pub fn create_large_shirt(color: String) -> items::Shirt {
70 //!     let mut shirt = items::Shirt::default();
71 //!     shirt.color = color;
72 //!     shirt.set_size(items::shirt::Size::Large);
73 //!     shirt
74 //! }
75 //! ```
76 //!
77 //! That's it! Run `cargo doc` to see documentation for the generated code. The full
78 //! example project can be found on [GitHub](https://github.com/danburkert/snazzy).
79 //!
80 //! ## Sourcing `protoc`
81 //!
82 //! `prost-build` depends on the Protocol Buffers compiler, `protoc`, to parse `.proto` files into
83 //! a representation that can be transformed into Rust. If set, `prost-build` uses the `PROTOC` and
84 //! `PROTOC_INCLUDE` environment variables for locating `protoc` and the Protobuf includes
85 //! directory. For example, on a macOS system where Protobuf is installed with Homebrew, set the
86 //! environment to:
87 //!
88 //! ```bash
89 //! PROTOC=/usr/local/bin/protoc
90 //! PROTOC_INCLUDE=/usr/local/include
91 //! ```
92 //!
93 //! and in a typical Linux installation:
94 //!
95 //! ```bash
96 //! PROTOC=/usr/bin/protoc
97 //! PROTOC_INCLUDE=/usr/include
98 //! ```
99 //!
100 //! If `PROTOC` is not found in the environment, then a pre-compiled `protoc` binary bundled in the
101 //! prost-build crate is used. Pre-compiled `protoc` binaries exist for Linux (non-musl), macOS,
102 //! and Windows systems. If no pre-compiled `protoc` is available for the host platform, then the
103 //! `protoc` or `protoc.exe` binary on the `PATH` is used. If `protoc` is not available in any of
104 //! these fallback locations, then the build fails.
105 //!
106 //! If `PROTOC_INCLUDE` is not found in the environment, then the Protobuf include directory
107 //! bundled in the prost-build crate is be used.
108 //!
109 //! To force `prost-build` to use the `protoc` on the `PATH`, add `PROTOC=protoc` to the
110 //! environment.
111 
112 mod ast;
113 mod code_generator;
114 mod extern_paths;
115 mod ident;
116 mod message_graph;
117 mod path;
118 
119 use std::collections::HashMap;
120 use std::default;
121 use std::env;
122 use std::ffi::{OsStr, OsString};
123 use std::fmt;
124 use std::fs;
125 use std::io::{Error, ErrorKind, Result};
126 use std::path::{Path, PathBuf};
127 use std::process::Command;
128 
129 use log::trace;
130 use prost::Message;
131 use prost_types::{FileDescriptorProto, FileDescriptorSet};
132 
133 pub use crate::ast::{Comments, Method, Service};
134 use crate::code_generator::CodeGenerator;
135 use crate::extern_paths::ExternPaths;
136 use crate::ident::to_snake;
137 use crate::message_graph::MessageGraph;
138 use crate::path::PathMap;
139 
140 type Module = Vec<String>;
141 
142 /// A service generator takes a service descriptor and generates Rust code.
143 ///
144 /// `ServiceGenerator` can be used to generate application-specific interfaces
145 /// or implementations for Protobuf service definitions.
146 ///
147 /// Service generators are registered with a code generator using the
148 /// `Config::service_generator` method.
149 ///
150 /// A viable scenario is that an RPC framework provides a service generator. It generates a trait
151 /// describing methods of the service and some glue code to call the methods of the trait, defining
152 /// details like how errors are handled or if it is asynchronous. Then the user provides an
153 /// implementation of the generated trait in the application code and plugs it into the framework.
154 ///
155 /// Such framework isn't part of Prost at present.
156 pub trait ServiceGenerator {
157     /// Generates a Rust interface or implementation for a service, writing the
158     /// result to `buf`.
generate(&mut self, service: Service, buf: &mut String)159     fn generate(&mut self, service: Service, buf: &mut String);
160 
161     /// Finalizes the generation process.
162     ///
163     /// In case there's something that needs to be output at the end of the generation process, it
164     /// goes here. Similar to [`generate`](#method.generate), the output should be appended to
165     /// `buf`.
166     ///
167     /// An example can be a module or other thing that needs to appear just once, not for each
168     /// service generated.
169     ///
170     /// This still can be called multiple times in a lifetime of the service generator, because it
171     /// is called once per `.proto` file.
172     ///
173     /// The default implementation is empty and does nothing.
finalize(&mut self, _buf: &mut String)174     fn finalize(&mut self, _buf: &mut String) {}
175 
176     /// Finalizes the generation process for an entire protobuf package.
177     ///
178     /// This differs from [`finalize`](#method.finalize) by where (and how often) it is called
179     /// during the service generator life cycle. This method is called once per protobuf package,
180     /// making it ideal for grouping services within a single package spread across multiple
181     /// `.proto` files.
182     ///
183     /// The default implementation is empty and does nothing.
finalize_package(&mut self, _package: &str, _buf: &mut String)184     fn finalize_package(&mut self, _package: &str, _buf: &mut String) {}
185 }
186 
187 /// The map collection type to output for Protobuf `map` fields.
188 #[non_exhaustive]
189 #[derive(Clone, Copy, Debug, PartialEq)]
190 enum MapType {
191     /// The [`std::collections::HashMap`] type.
192     HashMap,
193     /// The [`std::collections::BTreeMap`] type.
194     BTreeMap,
195 }
196 
197 impl Default for MapType {
default() -> MapType198     fn default() -> MapType {
199         MapType::HashMap
200     }
201 }
202 
203 /// The bytes collection type to output for Protobuf `bytes` fields.
204 #[non_exhaustive]
205 #[derive(Clone, Copy, Debug, PartialEq)]
206 enum BytesType {
207     /// The [`alloc::collections::Vec::<u8>`] type.
208     Vec,
209     /// The [`bytes::Bytes`] type.
210     Bytes,
211 }
212 
213 impl Default for BytesType {
default() -> BytesType214     fn default() -> BytesType {
215         BytesType::Vec
216     }
217 }
218 
219 /// Configuration options for Protobuf code generation.
220 ///
221 /// This configuration builder can be used to set non-default code generation options.
222 pub struct Config {
223     file_descriptor_set_path: Option<PathBuf>,
224     service_generator: Option<Box<dyn ServiceGenerator>>,
225     map_type: PathMap<MapType>,
226     bytes_type: PathMap<BytesType>,
227     type_attributes: PathMap<String>,
228     field_attributes: PathMap<String>,
229     prost_types: bool,
230     strip_enum_prefix: bool,
231     out_dir: Option<PathBuf>,
232     extern_paths: Vec<(String, String)>,
233     protoc_args: Vec<OsString>,
234     disable_comments: PathMap<()>,
235 }
236 
237 impl Config {
238     /// Creates a new code generator configuration with default options.
new() -> Config239     pub fn new() -> Config {
240         Config::default()
241     }
242 
243     /// Configure the code generator to generate Rust [`BTreeMap`][1] fields for Protobuf
244     /// [`map`][2] type fields.
245     ///
246     /// # Arguments
247     ///
248     /// **`paths`** - paths to specific fields, messages, or packages which should use a Rust
249     /// `BTreeMap` for Protobuf `map` fields. Paths are specified in terms of the Protobuf type
250     /// name (not the generated Rust type name). Paths with a leading `.` are treated as fully
251     /// qualified names. Paths without a leading `.` are treated as relative, and are suffix
252     /// matched on the fully qualified field name. If a Protobuf map field matches any of the
253     /// paths, a Rust `BTreeMap` field is generated instead of the default [`HashMap`][3].
254     ///
255     /// The matching is done on the Protobuf names, before converting to Rust-friendly casing
256     /// standards.
257     ///
258     /// # Examples
259     ///
260     /// ```rust
261     /// # let mut config = prost_build::Config::new();
262     /// // Match a specific field in a message type.
263     /// config.btree_map(&[".my_messages.MyMessageType.my_map_field"]);
264     ///
265     /// // Match all map fields in a message type.
266     /// config.btree_map(&[".my_messages.MyMessageType"]);
267     ///
268     /// // Match all map fields in a package.
269     /// config.btree_map(&[".my_messages"]);
270     ///
271     /// // Match all map fields. Expecially useful in `no_std` contexts.
272     /// config.btree_map(&["."]);
273     ///
274     /// // Match all map fields in a nested message.
275     /// config.btree_map(&[".my_messages.MyMessageType.MyNestedMessageType"]);
276     ///
277     /// // Match all fields named 'my_map_field'.
278     /// config.btree_map(&["my_map_field"]);
279     ///
280     /// // Match all fields named 'my_map_field' in messages named 'MyMessageType', regardless of
281     /// // package or nesting.
282     /// config.btree_map(&["MyMessageType.my_map_field"]);
283     ///
284     /// // Match all fields named 'my_map_field', and all fields in the 'foo.bar' package.
285     /// config.btree_map(&["my_map_field", ".foo.bar"]);
286     /// ```
287     ///
288     /// [1]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html
289     /// [2]: https://developers.google.com/protocol-buffers/docs/proto3#maps
290     /// [3]: https://doc.rust-lang.org/std/collections/struct.HashMap.html
btree_map<I, S>(&mut self, paths: I) -> &mut Self where I: IntoIterator<Item = S>, S: AsRef<str>,291     pub fn btree_map<I, S>(&mut self, paths: I) -> &mut Self
292     where
293         I: IntoIterator<Item = S>,
294         S: AsRef<str>,
295     {
296         self.map_type.clear();
297         for matcher in paths {
298             self.map_type
299                 .insert(matcher.as_ref().to_string(), MapType::BTreeMap);
300         }
301         self
302     }
303 
304     /// Configure the code generator to generate Rust [`bytes::Bytes`][1] fields for Protobuf
305     /// [`bytes`][2] type fields.
306     ///
307     /// # Arguments
308     ///
309     /// **`paths`** - paths to specific fields, messages, or packages which should use a Rust
310     /// `Bytes` for Protobuf `bytes` fields. Paths are specified in terms of the Protobuf type
311     /// name (not the generated Rust type name). Paths with a leading `.` are treated as fully
312     /// qualified names. Paths without a leading `.` are treated as relative, and are suffix
313     /// matched on the fully qualified field name. If a Protobuf map field matches any of the
314     /// paths, a Rust `Bytes` field is generated instead of the default [`Vec<u8>`][3].
315     ///
316     /// The matching is done on the Protobuf names, before converting to Rust-friendly casing
317     /// standards.
318     ///
319     /// # Examples
320     ///
321     /// ```rust
322     /// # let mut config = prost_build::Config::new();
323     /// // Match a specific field in a message type.
324     /// config.bytes(&[".my_messages.MyMessageType.my_bytes_field"]);
325     ///
326     /// // Match all bytes fields in a message type.
327     /// config.bytes(&[".my_messages.MyMessageType"]);
328     ///
329     /// // Match all bytes fields in a package.
330     /// config.bytes(&[".my_messages"]);
331     ///
332     /// // Match all bytes fields. Expecially useful in `no_std` contexts.
333     /// config.bytes(&["."]);
334     ///
335     /// // Match all bytes fields in a nested message.
336     /// config.bytes(&[".my_messages.MyMessageType.MyNestedMessageType"]);
337     ///
338     /// // Match all fields named 'my_bytes_field'.
339     /// config.bytes(&["my_bytes_field"]);
340     ///
341     /// // Match all fields named 'my_bytes_field' in messages named 'MyMessageType', regardless of
342     /// // package or nesting.
343     /// config.bytes(&["MyMessageType.my_bytes_field"]);
344     ///
345     /// // Match all fields named 'my_bytes_field', and all fields in the 'foo.bar' package.
346     /// config.bytes(&["my_bytes_field", ".foo.bar"]);
347     /// ```
348     ///
349     /// [1]: https://docs.rs/bytes/latest/bytes/struct.Bytes.html
350     /// [2]: https://developers.google.com/protocol-buffers/docs/proto3#scalar
351     /// [3]: https://doc.rust-lang.org/std/vec/struct.Vec.html
bytes<I, S>(&mut self, paths: I) -> &mut Self where I: IntoIterator<Item = S>, S: AsRef<str>,352     pub fn bytes<I, S>(&mut self, paths: I) -> &mut Self
353     where
354         I: IntoIterator<Item = S>,
355         S: AsRef<str>,
356     {
357         self.bytes_type.clear();
358         for matcher in paths {
359             self.bytes_type
360                 .insert(matcher.as_ref().to_string(), BytesType::Bytes);
361         }
362         self
363     }
364 
365     /// Add additional attribute to matched fields.
366     ///
367     /// # Arguments
368     ///
369     /// **`path`** - a patch matching any number of fields. These fields get the attribute.
370     /// For details about matching fields see [`btree_map`](#method.btree_map).
371     ///
372     /// **`attribute`** - an arbitrary string that'll be placed before each matched field. The
373     /// expected usage are additional attributes, usually in concert with whole-type
374     /// attributes set with [`type_attribute`](method.type_attribute), but it is not
375     /// checked and anything can be put there.
376     ///
377     /// Note that the calls to this method are cumulative ‒ if multiple paths from multiple calls
378     /// match the same field, the field gets all the corresponding attributes.
379     ///
380     /// # Examples
381     ///
382     /// ```rust
383     /// # let mut config = prost_build::Config::new();
384     /// // Prost renames fields named `in` to `in_`. But if serialized through serde,
385     /// // they should as `in`.
386     /// config.field_attribute("in", "#[serde(rename = \"in\")]");
387     /// ```
field_attribute<P, A>(&mut self, path: P, attribute: A) -> &mut Self where P: AsRef<str>, A: AsRef<str>,388     pub fn field_attribute<P, A>(&mut self, path: P, attribute: A) -> &mut Self
389     where
390         P: AsRef<str>,
391         A: AsRef<str>,
392     {
393         self.field_attributes
394             .insert(path.as_ref().to_string(), attribute.as_ref().to_string());
395         self
396     }
397 
398     /// Add additional attribute to matched messages, enums and one-ofs.
399     ///
400     /// # Arguments
401     ///
402     /// **`paths`** - a path matching any number of types. It works the same way as in
403     /// [`btree_map`](#method.btree_map), just with the field name omitted.
404     ///
405     /// **`attribute`** - an arbitrary string to be placed before each matched type. The
406     /// expected usage are additional attributes, but anything is allowed.
407     ///
408     /// The calls to this method are cumulative. They don't overwrite previous calls and if a
409     /// type is matched by multiple calls of the method, all relevant attributes are added to
410     /// it.
411     ///
412     /// For things like serde it might be needed to combine with [field
413     /// attributes](#method.field_attribute).
414     ///
415     /// # Examples
416     ///
417     /// ```rust
418     /// # let mut config = prost_build::Config::new();
419     /// // Nothing around uses floats, so we can derive real `Eq` in addition to `PartialEq`.
420     /// config.type_attribute(".", "#[derive(Eq)]");
421     /// // Some messages want to be serializable with serde as well.
422     /// config.type_attribute("my_messages.MyMessageType",
423     ///                       "#[derive(Serialize)] #[serde(rename-all = \"snake_case\")]");
424     /// config.type_attribute("my_messages.MyMessageType.MyNestedMessageType",
425     ///                       "#[derive(Serialize)] #[serde(rename-all = \"snake_case\")]");
426     /// ```
427     ///
428     /// # Oneof fields
429     ///
430     /// The `oneof` fields don't have a type name of their own inside Protobuf. Therefore, the
431     /// field name can be used both with `type_attribute` and `field_attribute` ‒ the first is
432     /// placed before the `enum` type definition, the other before the field inside corresponding
433     /// message `struct`.
434     ///
435     /// In other words, to place an attribute on the `enum` implementing the `oneof`, the match
436     /// would look like `my_messages.MyMessageType.oneofname`.
type_attribute<P, A>(&mut self, path: P, attribute: A) -> &mut Self where P: AsRef<str>, A: AsRef<str>,437     pub fn type_attribute<P, A>(&mut self, path: P, attribute: A) -> &mut Self
438     where
439         P: AsRef<str>,
440         A: AsRef<str>,
441     {
442         self.type_attributes
443             .insert(path.as_ref().to_string(), attribute.as_ref().to_string());
444         self
445     }
446 
447     /// Configures the code generator to use the provided service generator.
service_generator(&mut self, service_generator: Box<dyn ServiceGenerator>) -> &mut Self448     pub fn service_generator(&mut self, service_generator: Box<dyn ServiceGenerator>) -> &mut Self {
449         self.service_generator = Some(service_generator);
450         self
451     }
452 
453     /// Configures the code generator to not use the `prost_types` crate for Protobuf well-known
454     /// types, and instead generate Protobuf well-known types from their `.proto` definitions.
compile_well_known_types(&mut self) -> &mut Self455     pub fn compile_well_known_types(&mut self) -> &mut Self {
456         self.prost_types = false;
457         self
458     }
459 
460     /// Configures the code generator to omit documentation comments on generated Protobuf types.
461     ///
462     /// # Example
463     ///
464     /// Occasionally `.proto` files contain code blocks which are not valid Rust. To avoid doctest
465     /// failures, annotate the invalid code blocks with an [`ignore` or `no_run` attribute][1], or
466     /// disable doctests for the crate with a [Cargo.toml entry][2]. If neither of these options
467     /// are possible, then omit comments on generated code during doctest builds:
468     ///
469     /// ```rust,ignore
470     /// let mut config = prost_build::Config::new();
471     /// config.disable_comments(".");
472     /// config.compile_protos(&["src/frontend.proto", "src/backend.proto"], &["src"])?;
473     /// ```
474     ///
475     /// As with other options which take a set of paths, comments can be disabled on a per-package
476     /// or per-symbol basis.
477     ///
478     /// [1]: https://doc.rust-lang.org/rustdoc/documentation-tests.html#attributes
479     /// [2]: https://doc.rust-lang.org/cargo/reference/cargo-targets.html#configuring-a-target
disable_comments<I, S>(&mut self, paths: I) -> &mut Self where I: IntoIterator<Item = S>, S: AsRef<str>,480     pub fn disable_comments<I, S>(&mut self, paths: I) -> &mut Self
481     where
482         I: IntoIterator<Item = S>,
483         S: AsRef<str>,
484     {
485         self.disable_comments.clear();
486         for matcher in paths {
487             self.disable_comments
488                 .insert(matcher.as_ref().to_string(), ());
489         }
490         self
491     }
492 
493     /// Declare an externally provided Protobuf package or type.
494     ///
495     /// `extern_path` allows `prost` types in external crates to be referenced in generated code.
496     ///
497     /// When `prost` compiles a `.proto` which includes an import of another `.proto`, it will
498     /// automatically recursively compile the imported file as well. `extern_path` can be used
499     /// to instead substitute types from an external crate.
500     ///
501     /// # Example
502     ///
503     /// As an example, consider a crate, `uuid`, with a `prost`-generated `Uuid` type:
504     ///
505     /// ```proto
506     /// // uuid.proto
507     ///
508     /// syntax = "proto3";
509     /// package uuid;
510     ///
511     /// message Uuid {
512     ///     string uuid_str = 1;
513     /// }
514     /// ```
515     ///
516     /// The `uuid` crate implements some traits for `Uuid`, and publicly exports it:
517     ///
518     /// ```rust,ignore
519     /// // lib.rs in the uuid crate
520     ///
521     /// include!(concat!(env!("OUT_DIR"), "/uuid.rs"));
522     ///
523     /// pub trait DoSomething {
524     ///     fn do_it(&self);
525     /// }
526     ///
527     /// impl DoSomething for Uuid {
528     ///     fn do_it(&self) {
529     ///         println!("Done");
530     ///     }
531     /// }
532     /// ```
533     ///
534     /// A separate crate, `my_application`, uses `prost` to generate message types which reference
535     /// `Uuid`:
536     ///
537     /// ```proto
538     /// // my_application.proto
539     ///
540     /// syntax = "proto3";
541     /// package my_application;
542     ///
543     /// import "uuid.proto";
544     ///
545     /// message MyMessage {
546     ///     uuid.Uuid message_id = 1;
547     ///     string some_payload = 2;
548     /// }
549     /// ```
550     ///
551     /// Additionally, `my_application` depends on the trait impls provided by the `uuid` crate:
552     ///
553     /// ```rust,ignore
554     /// // `main.rs` of `my_application`
555     ///
556     /// use uuid::{DoSomething, Uuid};
557     ///
558     /// include!(concat!(env!("OUT_DIR"), "/my_application.rs"));
559     ///
560     /// pub fn process_message(msg: MyMessage) {
561     ///     if let Some(uuid) = msg.message_id {
562     ///         uuid.do_it();
563     ///     }
564     /// }
565     /// ```
566     ///
567     /// Without configuring `uuid` as an external path in `my_application`'s `build.rs`, `prost`
568     /// would compile a completely separate version of the `Uuid` type, and `process_message` would
569     /// fail to compile. However, if `my_application` configures `uuid` as an extern path with a
570     /// call to `.extern_path(".uuid", "::uuid")`, `prost` will use the external type instead of
571     /// compiling a new version of `Uuid`. Note that the configuration could also be specified as
572     /// `.extern_path(".uuid.Uuid", "::uuid::Uuid")` if only the `Uuid` type were externally
573     /// provided, and not the whole `uuid` package.
574     ///
575     /// # Usage
576     ///
577     /// `extern_path` takes a fully-qualified Protobuf path, and the corresponding Rust path that
578     /// it will be substituted with in generated code. The Protobuf path can refer to a package or
579     /// a type, and the Rust path should correspondingly refer to a Rust module or type.
580     ///
581     /// ```rust
582     /// # let mut config = prost_build::Config::new();
583     /// // Declare the `uuid` Protobuf package and all nested packages and types as externally
584     /// // provided by the `uuid` crate.
585     /// config.extern_path(".uuid", "::uuid");
586     ///
587     /// // Declare the `foo.bar.baz` Protobuf package and all nested packages and types as
588     /// // externally provided by the `foo_bar_baz` crate.
589     /// config.extern_path(".foo.bar.baz", "::foo_bar_baz");
590     ///
591     /// // Declare the `uuid.Uuid` Protobuf type (and all nested types) as externally provided
592     /// // by the `uuid` crate's `Uuid` type.
593     /// config.extern_path(".uuid.Uuid", "::uuid::Uuid");
594     /// ```
extern_path<P1, P2>(&mut self, proto_path: P1, rust_path: P2) -> &mut Self where P1: Into<String>, P2: Into<String>,595     pub fn extern_path<P1, P2>(&mut self, proto_path: P1, rust_path: P2) -> &mut Self
596     where
597         P1: Into<String>,
598         P2: Into<String>,
599     {
600         self.extern_paths
601             .push((proto_path.into(), rust_path.into()));
602         self
603     }
604 
605     /// When set, the `FileDescriptorSet` generated by `protoc` is written to the provided
606     /// filesystem path.
607     ///
608     /// This option can be used in conjunction with the [`include_bytes!`] macro and the types in
609     /// the `prost-types` crate for implementing reflection capabilities, among other things.
610     ///
611     /// ## Example
612     ///
613     /// In `build.rs`:
614     ///
615     /// ```rust
616     /// # use std::env;
617     /// # use std::path::PathBuf;
618     /// # let mut config = prost_build::Config::new();
619     /// config.file_descriptor_set_path(
620     ///     PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR environment variable not set"))
621     ///         .join("file_descriptor_set.bin"));
622     /// ```
623     ///
624     /// In `lib.rs`:
625     ///
626     /// ```rust,ignore
627     /// let file_descriptor_set_bytes = include_bytes!(concat!(env!("OUT_DIR"), "/file_descriptor_set.bin"));
628     /// let file_descriptor_set = prost_types::FileDescriptorSet::decode(&file_descriptor_set_bytes[..]).unwrap();
629     /// ```
file_descriptor_set_path<P>(&mut self, path: P) -> &mut Self where P: Into<PathBuf>,630     pub fn file_descriptor_set_path<P>(&mut self, path: P) -> &mut Self
631     where
632         P: Into<PathBuf>,
633     {
634         self.file_descriptor_set_path = Some(path.into());
635         self
636     }
637 
638     /// Configures the code generator to not strip the enum name from variant names.
639     ///
640     /// Protobuf enum definitions commonly include the enum name as a prefix of every variant name.
641     /// This style is non-idiomatic in Rust, so by default `prost` strips the enum name prefix from
642     /// variants which include it. Configuring this option prevents `prost` from stripping the
643     /// prefix.
retain_enum_prefix(&mut self) -> &mut Self644     pub fn retain_enum_prefix(&mut self) -> &mut Self {
645         self.strip_enum_prefix = false;
646         self
647     }
648 
649     /// Configures the output directory where generated Rust files will be written.
650     ///
651     /// If unset, defaults to the `OUT_DIR` environment variable. `OUT_DIR` is set by Cargo when
652     /// executing build scripts, so `out_dir` typically does not need to be configured.
out_dir<P>(&mut self, path: P) -> &mut Self where P: Into<PathBuf>,653     pub fn out_dir<P>(&mut self, path: P) -> &mut Self
654     where
655         P: Into<PathBuf>,
656     {
657         self.out_dir = Some(path.into());
658         self
659     }
660 
661     /// Add an argument to the `protoc` protobuf compilation invocation.
662     ///
663     /// # Example `build.rs`
664     ///
665     /// ```rust,no_run
666     /// # use std::io::Result;
667     /// fn main() -> Result<()> {
668     ///   let mut prost_build = prost_build::Config::new();
669     ///   // Enable a protoc experimental feature.
670     ///   prost_build.protoc_arg("--experimental_allow_proto3_optional");
671     ///   prost_build.compile_protos(&["src/frontend.proto", "src/backend.proto"], &["src"])?;
672     ///   Ok(())
673     /// }
674     /// ```
protoc_arg<S>(&mut self, arg: S) -> &mut Self where S: AsRef<OsStr>,675     pub fn protoc_arg<S>(&mut self, arg: S) -> &mut Self
676     where
677         S: AsRef<OsStr>,
678     {
679         self.protoc_args.push(arg.as_ref().to_owned());
680         self
681     }
682 
683     /// Compile `.proto` files into Rust files during a Cargo build with additional code generator
684     /// configuration options.
685     ///
686     /// This method is like the `prost_build::compile_protos` function, with the added ability to
687     /// specify non-default code generation options. See that function for more information about
688     /// the arguments and generated outputs.
689     ///
690     /// # Example `build.rs`
691     ///
692     /// ```rust,no_run
693     /// # use std::io::Result;
694     /// fn main() -> Result<()> {
695     ///   let mut prost_build = prost_build::Config::new();
696     ///   prost_build.btree_map(&["."]);
697     ///   prost_build.compile_protos(&["src/frontend.proto", "src/backend.proto"], &["src"])?;
698     ///   Ok(())
699     /// }
700     /// ```
compile_protos( &mut self, protos: &[impl AsRef<Path>], includes: &[impl AsRef<Path>], ) -> Result<()>701     pub fn compile_protos(
702         &mut self,
703         protos: &[impl AsRef<Path>],
704         includes: &[impl AsRef<Path>],
705     ) -> Result<()> {
706         let target: PathBuf = self.out_dir.clone().map(Ok).unwrap_or_else(|| {
707             env::var_os("OUT_DIR")
708                 .ok_or_else(|| {
709                     Error::new(ErrorKind::Other, "OUT_DIR environment variable is not set")
710                 })
711                 .map(Into::into)
712         })?;
713 
714         // TODO: This should probably emit 'rerun-if-changed=PATH' directives for cargo, however
715         // according to [1] if any are output then those paths replace the default crate root,
716         // which is undesirable. Figure out how to do it in an additive way; perhaps gcc-rs has
717         // this figured out.
718         // [1]: http://doc.crates.io/build-script.html#outputs-of-the-build-script
719 
720         let tmp;
721         let file_descriptor_set_path = match self.file_descriptor_set_path.clone() {
722             Some(file_descriptor_set_path) => file_descriptor_set_path,
723             None => {
724                 tmp = tempfile::Builder::new().prefix("prost-build").tempdir()?;
725                 tmp.path().join("prost-descriptor-set")
726             }
727         };
728 
729         let mut cmd = Command::new(protoc());
730         cmd.arg("--include_imports")
731             .arg("--include_source_info")
732             .arg("-o")
733             .arg(&file_descriptor_set_path);
734 
735         for include in includes {
736             cmd.arg("-I").arg(include.as_ref());
737         }
738 
739         // Set the protoc include after the user includes in case the user wants to
740         // override one of the built-in .protos.
741         cmd.arg("-I").arg(protoc_include());
742 
743         for arg in &self.protoc_args {
744             cmd.arg(arg);
745         }
746 
747         for proto in protos {
748             cmd.arg(proto.as_ref());
749         }
750 
751         let output = cmd.output().map_err(|error| {
752             Error::new(
753                 error.kind(),
754                 format!("failed to invoke protoc (hint: https://docs.rs/prost-build/#sourcing-protoc): {}", error),
755             )
756         })?;
757 
758         if !output.status.success() {
759             return Err(Error::new(
760                 ErrorKind::Other,
761                 format!("protoc failed: {}", String::from_utf8_lossy(&output.stderr)),
762             ));
763         }
764 
765         let buf = fs::read(file_descriptor_set_path)?;
766         let file_descriptor_set = FileDescriptorSet::decode(&*buf).map_err(|error| {
767             Error::new(
768                 ErrorKind::InvalidInput,
769                 format!("invalid FileDescriptorSet: {}", error.to_string()),
770             )
771         })?;
772 
773         let modules = self.generate(file_descriptor_set.file)?;
774         for (module, content) in modules {
775             let mut filename = module.join(".");
776             filename.push_str(".rs");
777 
778             let output_path = target.join(&filename);
779 
780             let previous_content = fs::read(&output_path);
781 
782             if previous_content
783                 .map(|previous_content| previous_content == content.as_bytes())
784                 .unwrap_or(false)
785             {
786                 trace!("unchanged: {:?}", filename);
787             } else {
788                 trace!("writing: {:?}", filename);
789                 fs::write(output_path, content)?;
790             }
791         }
792 
793         Ok(())
794     }
795 
generate(&mut self, files: Vec<FileDescriptorProto>) -> Result<HashMap<Module, String>>796     fn generate(&mut self, files: Vec<FileDescriptorProto>) -> Result<HashMap<Module, String>> {
797         let mut modules = HashMap::new();
798         let mut packages = HashMap::new();
799 
800         let message_graph = MessageGraph::new(&files)
801             .map_err(|error| Error::new(ErrorKind::InvalidInput, error))?;
802         let extern_paths = ExternPaths::new(&self.extern_paths, self.prost_types)
803             .map_err(|error| Error::new(ErrorKind::InvalidInput, error))?;
804 
805         for file in files {
806             let module = self.module(&file);
807 
808             // Only record packages that have services
809             if !file.service.is_empty() {
810                 packages.insert(module.clone(), file.package().to_string());
811             }
812 
813             let mut buf = modules.entry(module).or_insert_with(String::new);
814             CodeGenerator::generate(self, &message_graph, &extern_paths, file, &mut buf);
815         }
816 
817         if let Some(ref mut service_generator) = self.service_generator {
818             for (module, package) in packages {
819                 let buf = modules.get_mut(&module).unwrap();
820                 service_generator.finalize_package(&package, buf);
821             }
822         }
823 
824         Ok(modules)
825     }
826 
module(&self, file: &FileDescriptorProto) -> Module827     fn module(&self, file: &FileDescriptorProto) -> Module {
828         file.package()
829             .split('.')
830             .filter(|s| !s.is_empty())
831             .map(to_snake)
832             .collect()
833     }
834 }
835 
836 impl default::Default for Config {
default() -> Config837     fn default() -> Config {
838         Config {
839             file_descriptor_set_path: None,
840             service_generator: None,
841             map_type: PathMap::default(),
842             bytes_type: PathMap::default(),
843             type_attributes: PathMap::default(),
844             field_attributes: PathMap::default(),
845             prost_types: true,
846             strip_enum_prefix: true,
847             out_dir: None,
848             extern_paths: Vec::new(),
849             protoc_args: Vec::new(),
850             disable_comments: PathMap::default(),
851         }
852     }
853 }
854 
855 impl fmt::Debug for Config {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result856     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
857         fmt.debug_struct("Config")
858             .field("file_descriptor_set_path", &self.file_descriptor_set_path)
859             .field(
860                 "service_generator",
861                 &self.file_descriptor_set_path.is_some(),
862             )
863             .field("map_type", &self.map_type)
864             .field("bytes_type", &self.bytes_type)
865             .field("type_attributes", &self.type_attributes)
866             .field("field_attributes", &self.field_attributes)
867             .field("prost_types", &self.prost_types)
868             .field("strip_enum_prefix", &self.strip_enum_prefix)
869             .field("out_dir", &self.out_dir)
870             .field("extern_paths", &self.extern_paths)
871             .field("protoc_args", &self.protoc_args)
872             .field("disable_comments", &self.disable_comments)
873             .finish()
874     }
875 }
876 
877 /// Compile `.proto` files into Rust files during a Cargo build.
878 ///
879 /// The generated `.rs` files are written to the Cargo `OUT_DIR` directory, suitable for use with
880 /// the [include!][1] macro. See the [Cargo `build.rs` code generation][2] example for more info.
881 ///
882 /// This function should be called in a project's `build.rs`.
883 ///
884 /// # Arguments
885 ///
886 /// **`protos`** - Paths to `.proto` files to compile. Any transitively [imported][3] `.proto`
887 /// files are automatically be included.
888 ///
889 /// **`includes`** - Paths to directories in which to search for imports. Directories are searched
890 /// in order. The `.proto` files passed in **`protos`** must be found in one of the provided
891 /// include directories.
892 ///
893 /// # Errors
894 ///
895 /// This function can fail for a number of reasons:
896 ///
897 ///   - Failure to locate or download `protoc`.
898 ///   - Failure to parse the `.proto`s.
899 ///   - Failure to locate an imported `.proto`.
900 ///   - Failure to compile a `.proto` without a [package specifier][4].
901 ///
902 /// It's expected that this function call be `unwrap`ed in a `build.rs`; there is typically no
903 /// reason to gracefully recover from errors during a build.
904 ///
905 /// # Example `build.rs`
906 ///
907 /// ```rust,no_run
908 /// # use std::io::Result;
909 /// fn main() -> Result<()> {
910 ///   prost_build::compile_protos(&["src/frontend.proto", "src/backend.proto"], &["src"])?;
911 ///   Ok(())
912 /// }
913 /// ```
914 ///
915 /// [1]: https://doc.rust-lang.org/std/macro.include.html
916 /// [2]: http://doc.crates.io/build-script.html#case-study-code-generation
917 /// [3]: https://developers.google.com/protocol-buffers/docs/proto3#importing-definitions
918 /// [4]: https://developers.google.com/protocol-buffers/docs/proto#packages
compile_protos(protos: &[impl AsRef<Path>], includes: &[impl AsRef<Path>]) -> Result<()>919 pub fn compile_protos(protos: &[impl AsRef<Path>], includes: &[impl AsRef<Path>]) -> Result<()> {
920     Config::new().compile_protos(protos, includes)
921 }
922 
923 /// Returns the path to the `protoc` binary.
protoc() -> PathBuf924 pub fn protoc() -> PathBuf {
925     match env::var_os("PROTOC") {
926         Some(protoc) => PathBuf::from(protoc),
927         None => PathBuf::from(env!("PROTOC")),
928     }
929 }
930 
931 /// Returns the path to the Protobuf include directory.
protoc_include() -> PathBuf932 pub fn protoc_include() -> PathBuf {
933     match env::var_os("PROTOC_INCLUDE") {
934         Some(include) => PathBuf::from(include),
935         None => PathBuf::from(env!("PROTOC_INCLUDE")),
936     }
937 }
938 
939 #[cfg(test)]
940 mod tests {
941     use super::*;
942     use std::cell::RefCell;
943     use std::rc::Rc;
944 
945     /// An example service generator that generates a trait with methods corresponding to the
946     /// service methods.
947     struct ServiceTraitGenerator;
948     impl ServiceGenerator for ServiceTraitGenerator {
generate(&mut self, service: Service, buf: &mut String)949         fn generate(&mut self, service: Service, buf: &mut String) {
950             // Generate a trait for the service.
951             service.comments.append_with_indent(0, buf);
952             buf.push_str(&format!("trait {} {{\n", &service.name));
953 
954             // Generate the service methods.
955             for method in service.methods {
956                 method.comments.append_with_indent(1, buf);
957                 buf.push_str(&format!(
958                     "    fn {}({}) -> {};\n",
959                     method.name, method.input_type, method.output_type
960                 ));
961             }
962 
963             // Close out the trait.
964             buf.push_str("}\n");
965         }
finalize(&mut self, buf: &mut String)966         fn finalize(&mut self, buf: &mut String) {
967             // Needs to be present only once, no matter how many services there are
968             buf.push_str("pub mod utils { }\n");
969         }
970     }
971 
972     /// Implements `ServiceGenerator` and provides some state for assertions.
973     struct MockServiceGenerator {
974         state: Rc<RefCell<MockState>>,
975     }
976 
977     /// Holds state for `MockServiceGenerator`
978     #[derive(Default)]
979     struct MockState {
980         service_names: Vec<String>,
981         package_names: Vec<String>,
982         finalized: u32,
983     }
984 
985     impl MockServiceGenerator {
new(state: Rc<RefCell<MockState>>) -> Self986         fn new(state: Rc<RefCell<MockState>>) -> Self {
987             Self { state }
988         }
989     }
990 
991     impl ServiceGenerator for MockServiceGenerator {
generate(&mut self, service: Service, _buf: &mut String)992         fn generate(&mut self, service: Service, _buf: &mut String) {
993             let mut state = self.state.borrow_mut();
994             state.service_names.push(service.name);
995         }
996 
finalize(&mut self, _buf: &mut String)997         fn finalize(&mut self, _buf: &mut String) {
998             let mut state = self.state.borrow_mut();
999             state.finalized += 1;
1000         }
1001 
finalize_package(&mut self, package: &str, _buf: &mut String)1002         fn finalize_package(&mut self, package: &str, _buf: &mut String) {
1003             let mut state = self.state.borrow_mut();
1004             state.package_names.push(package.to_string());
1005         }
1006     }
1007 
1008     #[test]
smoke_test()1009     fn smoke_test() {
1010         let _ = env_logger::try_init();
1011         Config::new()
1012             .service_generator(Box::new(ServiceTraitGenerator))
1013             .compile_protos(&["src/smoke_test.proto"], &["src"])
1014             .unwrap();
1015     }
1016 
1017     #[test]
finalize_package()1018     fn finalize_package() {
1019         let _ = env_logger::try_init();
1020 
1021         let state = Rc::new(RefCell::new(MockState::default()));
1022         let gen = MockServiceGenerator::new(Rc::clone(&state));
1023 
1024         Config::new()
1025             .service_generator(Box::new(gen))
1026             .compile_protos(&["src/hello.proto", "src/goodbye.proto"], &["src"])
1027             .unwrap();
1028 
1029         let state = state.borrow();
1030         assert_eq!(&state.service_names, &["Greeting", "Farewell"]);
1031         assert_eq!(&state.package_names, &["helloworld"]);
1032         assert_eq!(state.finalized, 3);
1033     }
1034 }
1035