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