prost_msg_build/lib.rs
1#![doc(html_root_url = "https://docs.rs/prost-build/0.11.8")]
2#![allow(clippy::option_as_ref_deref, clippy::format_push_string)]
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//! // It is important to maintain the same structure as in the proto.
66//! pub mod snazzy {
67//! pub mod items {
68//! include!(concat!(env!("OUT_DIR"), "/snazzy.items.rs"));
69//! }
70//! }
71//!
72//! use snazzy::items;
73//!
74//! pub fn create_large_shirt(color: String) -> items::Shirt {
75//! let mut shirt = items::Shirt::default();
76//! shirt.color = color;
77//! shirt.set_size(items::shirt::Size::Large);
78//! shirt
79//! }
80//! ```
81//!
82//! That's it! Run `cargo doc` to see documentation for the generated code. The full
83//! example project can be found on [GitHub](https://github.com/danburkert/snazzy).
84//!
85//! ### Cleaning up Markdown in code docs
86//!
87//! If you are using protobuf files from third parties, where the author of the protobuf
88//! is not treating comments as Markdown, or is, but has codeblocks in their docs,
89//! then you may need to clean up the documentation in order that `cargo test --doc`
90//! will not fail spuriously, and that `cargo doc` doesn't attempt to render the
91//! codeblocks as Rust code.
92//!
93//! To do this, in your `Cargo.toml`, add `features = ["cleanup-markdown"]` to the inclusion
94//! of the `prost-build` crate and when your code is generated, the code docs will automatically
95//! be cleaned up a bit.
96//!
97//! ## Sourcing `protoc`
98//!
99//! `prost-build` depends on the Protocol Buffers compiler, `protoc`, to parse `.proto` files into
100//! a representation that can be transformed into Rust. If set, `prost-build` uses the `PROTOC`
101//! for locating `protoc`. For example, on a macOS system where Protobuf is installed
102//! with Homebrew, set the environment variables to:
103//!
104//! ```bash
105//! PROTOC=/usr/local/bin/protoc
106//! ```
107//!
108//! and in a typical Linux installation:
109//!
110//! ```bash
111//! PROTOC=/usr/bin/protoc
112//! ```
113//!
114//! If no `PROTOC` environment variable is set then `prost-build` will search the
115//! current path for `protoc` or `protoc.exe`. If `prost-build` can not find `protoc`
116//! via these methods the `compile_protos` method will fail.
117//!
118//! ### Compiling `protoc` from source
119//!
120//! To compile `protoc` from source you can use the `protobuf-src` crate and
121//! set the correct environment variables.
122//! ```no_run,ignore, rust
123//! std::env::set_var("PROTOC", protobuf_src::protoc());
124//!
125//! // Now compile your proto files via prost-build
126//! ```
127//!
128//! [`protobuf-src`]: https://docs.rs/protobuf-src
129
130use std::collections::HashMap;
131use std::default;
132use std::env;
133use std::ffi::{OsStr, OsString};
134use std::fmt;
135use std::fs;
136use std::io::{Error, ErrorKind, Result, Write};
137use std::ops::RangeToInclusive;
138use std::path::{Path, PathBuf};
139use std::process::Command;
140
141use log::debug;
142use log::trace;
143
144use prost::Message;
145use prost_types::{FileDescriptorProto, FileDescriptorSet};
146
147pub use crate::ast::{Comments, Method, Service};
148use crate::code_generator::CodeGenerator;
149use crate::extern_paths::ExternPaths;
150use crate::ident::to_snake;
151use crate::message_graph::MessageGraph;
152use crate::path::PathMap;
153
154mod ast;
155mod code_generator;
156mod extern_paths;
157mod ident;
158mod message_graph;
159mod path;
160
161/// A service generator takes a service descriptor and generates Rust code.
162///
163/// `ServiceGenerator` can be used to generate application-specific interfaces
164/// or implementations for Protobuf service definitions.
165///
166/// Service generators are registered with a code generator using the
167/// `Config::service_generator` method.
168///
169/// A viable scenario is that an RPC framework provides a service generator. It generates a trait
170/// describing methods of the service and some glue code to call the methods of the trait, defining
171/// details like how errors are handled or if it is asynchronous. Then the user provides an
172/// implementation of the generated trait in the application code and plugs it into the framework.
173///
174/// Such framework isn't part of Prost at present.
175pub trait ServiceGenerator {
176 /// Generates a Rust interface or implementation for a service, writing the
177 /// result to `buf`.
178 fn generate(&mut self, service: Service, buf: &mut String);
179
180 /// Finalizes the generation process.
181 ///
182 /// In case there's something that needs to be output at the end of the generation process, it
183 /// goes here. Similar to [`generate`](#method.generate), the output should be appended to
184 /// `buf`.
185 ///
186 /// An example can be a module or other thing that needs to appear just once, not for each
187 /// service generated.
188 ///
189 /// This still can be called multiple times in a lifetime of the service generator, because it
190 /// is called once per `.proto` file.
191 ///
192 /// The default implementation is empty and does nothing.
193 fn finalize(&mut self, _buf: &mut String) {}
194
195 /// Finalizes the generation process for an entire protobuf package.
196 ///
197 /// This differs from [`finalize`](#method.finalize) by where (and how often) it is called
198 /// during the service generator life cycle. This method is called once per protobuf package,
199 /// making it ideal for grouping services within a single package spread across multiple
200 /// `.proto` files.
201 ///
202 /// The default implementation is empty and does nothing.
203 fn finalize_package(&mut self, _package: &str, _buf: &mut String) {}
204}
205
206/// The map collection type to output for Protobuf `map` fields.
207#[non_exhaustive]
208#[derive(Clone, Copy, Debug, PartialEq, Default)]
209enum MapType {
210 /// The [`std::collections::HashMap`] type.
211 #[default]
212 HashMap,
213 /// The [`std::collections::BTreeMap`] type.
214 BTreeMap,
215}
216
217/// The bytes collection type to output for Protobuf `bytes` fields.
218#[non_exhaustive]
219#[derive(Clone, Copy, Debug, PartialEq, Default)]
220enum BytesType {
221 /// The [`alloc::collections::Vec::<u8>`] type.
222 #[default]
223 Vec,
224 /// The [`bytes::Bytes`] type.
225 Bytes,
226}
227
228/// Configuration options for Protobuf code generation.
229///
230/// This configuration builder can be used to set non-default code generation options.
231pub struct Config {
232 file_descriptor_set_path: Option<PathBuf>,
233 service_generator: Option<Box<dyn ServiceGenerator>>,
234 map_type: PathMap<MapType>,
235 bytes_type: PathMap<BytesType>,
236 type_attributes: PathMap<String>,
237 message_attributes: PathMap<String>,
238 enum_attributes: PathMap<String>,
239 field_attributes: PathMap<String>,
240 prost_types: bool,
241 strip_enum_prefix: bool,
242 out_dir: Option<PathBuf>,
243 extern_paths: Vec<(String, String)>,
244 default_package_filename: String,
245 protoc_args: Vec<OsString>,
246 disable_comments: PathMap<()>,
247 skip_protoc_run: bool,
248 include_file: Option<PathBuf>,
249 prost_path: Option<String>,
250 fmt: bool,
251}
252
253impl Config {
254 /// Creates a new code generator configuration with default options.
255 pub fn new() -> Config {
256 Config::default()
257 }
258
259 /// Configure the code generator to generate Rust [`BTreeMap`][1] fields for Protobuf
260 /// [`map`][2] type fields.
261 ///
262 /// # Arguments
263 ///
264 /// **`paths`** - paths to specific fields, messages, or packages which should use a Rust
265 /// `BTreeMap` for Protobuf `map` fields. Paths are specified in terms of the Protobuf type
266 /// name (not the generated Rust type name). Paths with a leading `.` are treated as fully
267 /// qualified names. Paths without a leading `.` are treated as relative, and are suffix
268 /// matched on the fully qualified field name. If a Protobuf map field matches any of the
269 /// paths, a Rust `BTreeMap` field is generated instead of the default [`HashMap`][3].
270 ///
271 /// The matching is done on the Protobuf names, before converting to Rust-friendly casing
272 /// standards.
273 ///
274 /// # Examples
275 ///
276 /// ```rust
277 /// # let mut config = prost_build::Config::new();
278 /// // Match a specific field in a message type.
279 /// config.btree_map(&[".my_messages.MyMessageType.my_map_field"]);
280 ///
281 /// // Match all map fields in a message type.
282 /// config.btree_map(&[".my_messages.MyMessageType"]);
283 ///
284 /// // Match all map fields in a package.
285 /// config.btree_map(&[".my_messages"]);
286 ///
287 /// // Match all map fields. Specially useful in `no_std` contexts.
288 /// config.btree_map(&["."]);
289 ///
290 /// // Match all map fields in a nested message.
291 /// config.btree_map(&[".my_messages.MyMessageType.MyNestedMessageType"]);
292 ///
293 /// // Match all fields named 'my_map_field'.
294 /// config.btree_map(&["my_map_field"]);
295 ///
296 /// // Match all fields named 'my_map_field' in messages named 'MyMessageType', regardless of
297 /// // package or nesting.
298 /// config.btree_map(&["MyMessageType.my_map_field"]);
299 ///
300 /// // Match all fields named 'my_map_field', and all fields in the 'foo.bar' package.
301 /// config.btree_map(&["my_map_field", ".foo.bar"]);
302 /// ```
303 ///
304 /// [1]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html
305 /// [2]: https://developers.google.com/protocol-buffers/docs/proto3#maps
306 /// [3]: https://doc.rust-lang.org/std/collections/struct.HashMap.html
307 pub fn btree_map<I, S>(&mut self, paths: I) -> &mut Self
308 where
309 I: IntoIterator<Item = S>,
310 S: AsRef<str>,
311 {
312 self.map_type.clear();
313 for matcher in paths {
314 self.map_type
315 .insert(matcher.as_ref().to_string(), MapType::BTreeMap);
316 }
317 self
318 }
319
320 /// Configure the code generator to generate Rust [`bytes::Bytes`][1] fields for Protobuf
321 /// [`bytes`][2] type fields.
322 ///
323 /// # Arguments
324 ///
325 /// **`paths`** - paths to specific fields, messages, or packages which should use a Rust
326 /// `Bytes` for Protobuf `bytes` fields. Paths are specified in terms of the Protobuf type
327 /// name (not the generated Rust type name). Paths with a leading `.` are treated as fully
328 /// qualified names. Paths without a leading `.` are treated as relative, and are suffix
329 /// matched on the fully qualified field name. If a Protobuf map field matches any of the
330 /// paths, a Rust `Bytes` field is generated instead of the default [`Vec<u8>`][3].
331 ///
332 /// The matching is done on the Protobuf names, before converting to Rust-friendly casing
333 /// standards.
334 ///
335 /// # Examples
336 ///
337 /// ```rust
338 /// # let mut config = prost_build::Config::new();
339 /// // Match a specific field in a message type.
340 /// config.bytes(&[".my_messages.MyMessageType.my_bytes_field"]);
341 ///
342 /// // Match all bytes fields in a message type.
343 /// config.bytes(&[".my_messages.MyMessageType"]);
344 ///
345 /// // Match all bytes fields in a package.
346 /// config.bytes(&[".my_messages"]);
347 ///
348 /// // Match all bytes fields. Specially useful in `no_std` contexts.
349 /// config.bytes(&["."]);
350 ///
351 /// // Match all bytes fields in a nested message.
352 /// config.bytes(&[".my_messages.MyMessageType.MyNestedMessageType"]);
353 ///
354 /// // Match all fields named 'my_bytes_field'.
355 /// config.bytes(&["my_bytes_field"]);
356 ///
357 /// // Match all fields named 'my_bytes_field' in messages named 'MyMessageType', regardless of
358 /// // package or nesting.
359 /// config.bytes(&["MyMessageType.my_bytes_field"]);
360 ///
361 /// // Match all fields named 'my_bytes_field', and all fields in the 'foo.bar' package.
362 /// config.bytes(&["my_bytes_field", ".foo.bar"]);
363 /// ```
364 ///
365 /// [1]: https://docs.rs/bytes/latest/bytes/struct.Bytes.html
366 /// [2]: https://developers.google.com/protocol-buffers/docs/proto3#scalar
367 /// [3]: https://doc.rust-lang.org/std/vec/struct.Vec.html
368 pub fn bytes<I, S>(&mut self, paths: I) -> &mut Self
369 where
370 I: IntoIterator<Item = S>,
371 S: AsRef<str>,
372 {
373 self.bytes_type.clear();
374 for matcher in paths {
375 self.bytes_type
376 .insert(matcher.as_ref().to_string(), BytesType::Bytes);
377 }
378 self
379 }
380
381 /// Add additional attribute to matched fields.
382 ///
383 /// # Arguments
384 ///
385 /// **`path`** - a path matching any number of fields. These fields get the attribute.
386 /// For details about matching fields see [`btree_map`](#method.btree_map).
387 ///
388 /// **`attribute`** - an arbitrary string that'll be placed before each matched field. The
389 /// expected usage are additional attributes, usually in concert with whole-type
390 /// attributes set with [`type_attribute`](method.type_attribute), but it is not
391 /// checked and anything can be put there.
392 ///
393 /// Note that the calls to this method are cumulative ‒ if multiple paths from multiple calls
394 /// match the same field, the field gets all the corresponding attributes.
395 ///
396 /// # Examples
397 ///
398 /// ```rust
399 /// # let mut config = prost_build::Config::new();
400 /// // Prost renames fields named `in` to `in_`. But if serialized through serde,
401 /// // they should as `in`.
402 /// config.field_attribute("in", "#[serde(rename = \"in\")]");
403 /// ```
404 pub fn field_attribute<P, A>(&mut self, path: P, attribute: A) -> &mut Self
405 where
406 P: AsRef<str>,
407 A: AsRef<str>,
408 {
409 self.field_attributes
410 .insert(path.as_ref().to_string(), attribute.as_ref().to_string());
411 self
412 }
413
414 /// Add additional attribute to matched messages, enums and one-ofs.
415 ///
416 /// # Arguments
417 ///
418 /// **`paths`** - a path matching any number of types. It works the same way as in
419 /// [`btree_map`](#method.btree_map), just with the field name omitted.
420 ///
421 /// **`attribute`** - an arbitrary string to be placed before each matched type. The
422 /// expected usage are additional attributes, but anything is allowed.
423 ///
424 /// The calls to this method are cumulative. They don't overwrite previous calls and if a
425 /// type is matched by multiple calls of the method, all relevant attributes are added to
426 /// it.
427 ///
428 /// For things like serde it might be needed to combine with [field
429 /// attributes](#method.field_attribute).
430 ///
431 /// # Examples
432 ///
433 /// ```rust
434 /// # let mut config = prost_build::Config::new();
435 /// // Nothing around uses floats, so we can derive real `Eq` in addition to `PartialEq`.
436 /// config.type_attribute(".", "#[derive(Eq)]");
437 /// // Some messages want to be serializable with serde as well.
438 /// config.type_attribute("my_messages.MyMessageType",
439 /// "#[derive(Serialize)] #[serde(rename_all = \"snake_case\")]");
440 /// config.type_attribute("my_messages.MyMessageType.MyNestedMessageType",
441 /// "#[derive(Serialize)] #[serde(rename_all = \"snake_case\")]");
442 /// ```
443 ///
444 /// # Oneof fields
445 ///
446 /// The `oneof` fields don't have a type name of their own inside Protobuf. Therefore, the
447 /// field name can be used both with `type_attribute` and `field_attribute` ‒ the first is
448 /// placed before the `enum` type definition, the other before the field inside corresponding
449 /// message `struct`.
450 ///
451 /// In other words, to place an attribute on the `enum` implementing the `oneof`, the match
452 /// would look like `my_messages.MyMessageType.oneofname`.
453 pub fn type_attribute<P, A>(&mut self, path: P, attribute: A) -> &mut Self
454 where
455 P: AsRef<str>,
456 A: AsRef<str>,
457 {
458 self.type_attributes
459 .insert(path.as_ref().to_string(), attribute.as_ref().to_string());
460 self
461 }
462
463 /// Add additional attribute to matched messages.
464 ///
465 /// # Arguments
466 ///
467 /// **`paths`** - a path matching any number of types. It works the same way as in
468 /// [`btree_map`](#method.btree_map), just with the field name omitted.
469 ///
470 /// **`attribute`** - an arbitrary string to be placed before each matched type. The
471 /// expected usage are additional attributes, but anything is allowed.
472 ///
473 /// The calls to this method are cumulative. They don't overwrite previous calls and if a
474 /// type is matched by multiple calls of the method, all relevant attributes are added to
475 /// it.
476 ///
477 /// For things like serde it might be needed to combine with [field
478 /// attributes](#method.field_attribute).
479 ///
480 /// # Examples
481 ///
482 /// ```rust
483 /// # let mut config = prost_build::Config::new();
484 /// // Nothing around uses floats, so we can derive real `Eq` in addition to `PartialEq`.
485 /// config.message_attribute(".", "#[derive(Eq)]");
486 /// // Some messages want to be serializable with serde as well.
487 /// config.message_attribute("my_messages.MyMessageType",
488 /// "#[derive(Serialize)] #[serde(rename_all = \"snake_case\")]");
489 /// config.message_attribute("my_messages.MyMessageType.MyNestedMessageType",
490 /// "#[derive(Serialize)] #[serde(rename_all = \"snake_case\")]");
491 /// ```
492 pub fn message_attribute<P, A>(&mut self, path: P, attribute: A) -> &mut Self
493 where
494 P: AsRef<str>,
495 A: AsRef<str>,
496 {
497 self.message_attributes
498 .insert(path.as_ref().to_string(), attribute.as_ref().to_string());
499 self
500 }
501
502 /// Add additional attribute to matched enums and one-ofs.
503 ///
504 /// # Arguments
505 ///
506 /// **`paths`** - a path matching any number of types. It works the same way as in
507 /// [`btree_map`](#method.btree_map), just with the field name omitted.
508 ///
509 /// **`attribute`** - an arbitrary string to be placed before each matched type. The
510 /// expected usage are additional attributes, but anything is allowed.
511 ///
512 /// The calls to this method are cumulative. They don't overwrite previous calls and if a
513 /// type is matched by multiple calls of the method, all relevant attributes are added to
514 /// it.
515 ///
516 /// For things like serde it might be needed to combine with [field
517 /// attributes](#method.field_attribute).
518 ///
519 /// # Examples
520 ///
521 /// ```rust
522 /// # let mut config = prost_build::Config::new();
523 /// // Nothing around uses floats, so we can derive real `Eq` in addition to `PartialEq`.
524 /// config.enum_attribute(".", "#[derive(Eq)]");
525 /// // Some messages want to be serializable with serde as well.
526 /// config.enum_attribute("my_messages.MyEnumType",
527 /// "#[derive(Serialize)] #[serde(rename_all = \"snake_case\")]");
528 /// config.enum_attribute("my_messages.MyMessageType.MyNestedEnumType",
529 /// "#[derive(Serialize)] #[serde(rename_all = \"snake_case\")]");
530 /// ```
531 ///
532 /// # Oneof fields
533 ///
534 /// The `oneof` fields don't have a type name of their own inside Protobuf. Therefore, the
535 /// field name can be used both with `enum_attribute` and `field_attribute` ‒ the first is
536 /// placed before the `enum` type definition, the other before the field inside corresponding
537 /// message `struct`.
538 ///
539 /// In other words, to place an attribute on the `enum` implementing the `oneof`, the match
540 /// would look like `my_messages.MyNestedMessageType.oneofname`.
541 pub fn enum_attribute<P, A>(&mut self, path: P, attribute: A) -> &mut Self
542 where
543 P: AsRef<str>,
544 A: AsRef<str>,
545 {
546 self.enum_attributes
547 .insert(path.as_ref().to_string(), attribute.as_ref().to_string());
548 self
549 }
550
551 /// Configures the code generator to use the provided service generator.
552 pub fn service_generator(&mut self, service_generator: Box<dyn ServiceGenerator>) -> &mut Self {
553 self.service_generator = Some(service_generator);
554 self
555 }
556
557 /// Configures the code generator to not use the `prost_types` crate for Protobuf well-known
558 /// types, and instead generate Protobuf well-known types from their `.proto` definitions.
559 pub fn compile_well_known_types(&mut self) -> &mut Self {
560 self.prost_types = false;
561 self
562 }
563
564 /// Configures the code generator to omit documentation comments on generated Protobuf types.
565 ///
566 /// # Example
567 ///
568 /// Occasionally `.proto` files contain code blocks which are not valid Rust. To avoid doctest
569 /// failures, annotate the invalid code blocks with an [`ignore` or `no_run` attribute][1], or
570 /// disable doctests for the crate with a [Cargo.toml entry][2]. If neither of these options
571 /// are possible, then omit comments on generated code during doctest builds:
572 ///
573 /// ```rust,no_run
574 /// # fn main() -> std::io::Result<()> {
575 /// let mut config = prost_build::Config::new();
576 /// config.disable_comments(&["."]);
577 /// config.compile_protos(&["src/frontend.proto", "src/backend.proto"], &["src"])?;
578 /// # Ok(())
579 /// # }
580 /// ```
581 ///
582 /// As with other options which take a set of paths, comments can be disabled on a per-package
583 /// or per-symbol basis.
584 ///
585 /// [1]: https://doc.rust-lang.org/rustdoc/documentation-tests.html#attributes
586 /// [2]: https://doc.rust-lang.org/cargo/reference/cargo-targets.html#configuring-a-target
587 pub fn disable_comments<I, S>(&mut self, paths: I) -> &mut Self
588 where
589 I: IntoIterator<Item = S>,
590 S: AsRef<str>,
591 {
592 self.disable_comments.clear();
593 for matcher in paths {
594 self.disable_comments
595 .insert(matcher.as_ref().to_string(), ());
596 }
597 self
598 }
599
600 /// Declare an externally provided Protobuf package or type.
601 ///
602 /// `extern_path` allows `prost` types in external crates to be referenced in generated code.
603 ///
604 /// When `prost` compiles a `.proto` which includes an import of another `.proto`, it will
605 /// automatically recursively compile the imported file as well. `extern_path` can be used
606 /// to instead substitute types from an external crate.
607 ///
608 /// # Example
609 ///
610 /// As an example, consider a crate, `uuid`, with a `prost`-generated `Uuid` type:
611 ///
612 /// ```proto
613 /// // uuid.proto
614 ///
615 /// syntax = "proto3";
616 /// package uuid;
617 ///
618 /// message Uuid {
619 /// string uuid_str = 1;
620 /// }
621 /// ```
622 ///
623 /// The `uuid` crate implements some traits for `Uuid`, and publicly exports it:
624 ///
625 /// ```rust,ignore
626 /// // lib.rs in the uuid crate
627 ///
628 /// include!(concat!(env!("OUT_DIR"), "/uuid.rs"));
629 ///
630 /// pub trait DoSomething {
631 /// fn do_it(&self);
632 /// }
633 ///
634 /// impl DoSomething for Uuid {
635 /// fn do_it(&self) {
636 /// println!("Done");
637 /// }
638 /// }
639 /// ```
640 ///
641 /// A separate crate, `my_application`, uses `prost` to generate message types which reference
642 /// `Uuid`:
643 ///
644 /// ```proto
645 /// // my_application.proto
646 ///
647 /// syntax = "proto3";
648 /// package my_application;
649 ///
650 /// import "uuid.proto";
651 ///
652 /// message MyMessage {
653 /// uuid.Uuid message_id = 1;
654 /// string some_payload = 2;
655 /// }
656 /// ```
657 ///
658 /// Additionally, `my_application` depends on the trait impls provided by the `uuid` crate:
659 ///
660 /// ```rust,ignore
661 /// // `main.rs` of `my_application`
662 ///
663 /// use uuid::{DoSomething, Uuid};
664 ///
665 /// include!(concat!(env!("OUT_DIR"), "/my_application.rs"));
666 ///
667 /// pub fn process_message(msg: MyMessage) {
668 /// if let Some(uuid) = msg.message_id {
669 /// uuid.do_it();
670 /// }
671 /// }
672 /// ```
673 ///
674 /// Without configuring `uuid` as an external path in `my_application`'s `build.rs`, `prost`
675 /// would compile a completely separate version of the `Uuid` type, and `process_message` would
676 /// fail to compile. However, if `my_application` configures `uuid` as an extern path with a
677 /// call to `.extern_path(".uuid", "::uuid")`, `prost` will use the external type instead of
678 /// compiling a new version of `Uuid`. Note that the configuration could also be specified as
679 /// `.extern_path(".uuid.Uuid", "::uuid::Uuid")` if only the `Uuid` type were externally
680 /// provided, and not the whole `uuid` package.
681 ///
682 /// # Usage
683 ///
684 /// `extern_path` takes a fully-qualified Protobuf path, and the corresponding Rust path that
685 /// it will be substituted with in generated code. The Protobuf path can refer to a package or
686 /// a type, and the Rust path should correspondingly refer to a Rust module or type.
687 ///
688 /// ```rust
689 /// # let mut config = prost_build::Config::new();
690 /// // Declare the `uuid` Protobuf package and all nested packages and types as externally
691 /// // provided by the `uuid` crate.
692 /// config.extern_path(".uuid", "::uuid");
693 ///
694 /// // Declare the `foo.bar.baz` Protobuf package and all nested packages and types as
695 /// // externally provided by the `foo_bar_baz` crate.
696 /// config.extern_path(".foo.bar.baz", "::foo_bar_baz");
697 ///
698 /// // Declare the `uuid.Uuid` Protobuf type (and all nested types) as externally provided
699 /// // by the `uuid` crate's `Uuid` type.
700 /// config.extern_path(".uuid.Uuid", "::uuid::Uuid");
701 /// ```
702 pub fn extern_path<P1, P2>(&mut self, proto_path: P1, rust_path: P2) -> &mut Self
703 where
704 P1: Into<String>,
705 P2: Into<String>,
706 {
707 self.extern_paths
708 .push((proto_path.into(), rust_path.into()));
709 self
710 }
711
712 /// When set, the `FileDescriptorSet` generated by `protoc` is written to the provided
713 /// filesystem path.
714 ///
715 /// This option can be used in conjunction with the [`include_bytes!`] macro and the types in
716 /// the `prost-types` crate for implementing reflection capabilities, among other things.
717 ///
718 /// ## Example
719 ///
720 /// In `build.rs`:
721 ///
722 /// ```rust, no_run
723 /// # use std::env;
724 /// # use std::path::PathBuf;
725 /// # let mut config = prost_build::Config::new();
726 /// config.file_descriptor_set_path(
727 /// PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR environment variable not set"))
728 /// .join("file_descriptor_set.bin"));
729 /// ```
730 ///
731 /// In `lib.rs`:
732 ///
733 /// ```rust,ignore
734 /// let file_descriptor_set_bytes = include_bytes!(concat!(env!("OUT_DIR"), "/file_descriptor_set.bin"));
735 /// let file_descriptor_set = prost_types::FileDescriptorSet::decode(&file_descriptor_set_bytes[..]).unwrap();
736 /// ```
737 pub fn file_descriptor_set_path<P>(&mut self, path: P) -> &mut Self
738 where
739 P: Into<PathBuf>,
740 {
741 self.file_descriptor_set_path = Some(path.into());
742 self
743 }
744
745 /// In combination with with `file_descriptor_set_path`, this can be used to provide a file
746 /// descriptor set as an input file, rather than having prost-build generate the file by calling
747 /// protoc.
748 ///
749 /// In `build.rs`:
750 ///
751 /// ```rust
752 /// # let mut config = prost_build::Config::new();
753 /// config.file_descriptor_set_path("path/from/build/system")
754 /// .skip_protoc_run()
755 /// .compile_protos(&["src/items.proto"], &["src/"]);
756 /// ```
757 ///
758 pub fn skip_protoc_run(&mut self) -> &mut Self {
759 self.skip_protoc_run = true;
760 self
761 }
762
763 /// Configures the code generator to not strip the enum name from variant names.
764 ///
765 /// Protobuf enum definitions commonly include the enum name as a prefix of every variant name.
766 /// This style is non-idiomatic in Rust, so by default `prost` strips the enum name prefix from
767 /// variants which include it. Configuring this option prevents `prost` from stripping the
768 /// prefix.
769 pub fn retain_enum_prefix(&mut self) -> &mut Self {
770 self.strip_enum_prefix = false;
771 self
772 }
773
774 /// Configures the output directory where generated Rust files will be written.
775 ///
776 /// If unset, defaults to the `OUT_DIR` environment variable. `OUT_DIR` is set by Cargo when
777 /// executing build scripts, so `out_dir` typically does not need to be configured.
778 pub fn out_dir<P>(&mut self, path: P) -> &mut Self
779 where
780 P: Into<PathBuf>,
781 {
782 self.out_dir = Some(path.into());
783 self
784 }
785
786 /// Configures what filename protobufs with no package definition are written to.
787 /// The filename will be appended with the `.rs` extension.
788 pub fn default_package_filename<S>(&mut self, filename: S) -> &mut Self
789 where
790 S: Into<String>,
791 {
792 self.default_package_filename = filename.into();
793 self
794 }
795
796 /// Configures the path that's used for deriving `Message` for generated messages.
797 /// This is mainly useful for generating crates that wish to re-export prost.
798 /// Defaults to `::prost::Message` if not specified.
799 pub fn prost_path<S>(&mut self, path: S) -> &mut Self
800 where
801 S: Into<String>,
802 {
803 self.prost_path = Some(path.into());
804 self
805 }
806
807 /// Add an argument to the `protoc` protobuf compilation invocation.
808 ///
809 /// # Example `build.rs`
810 ///
811 /// ```rust,no_run
812 /// # use std::io::Result;
813 /// fn main() -> Result<()> {
814 /// let mut prost_build = prost_build::Config::new();
815 /// // Enable a protoc experimental feature.
816 /// prost_build.protoc_arg("--experimental_allow_proto3_optional");
817 /// prost_build.compile_protos(&["src/frontend.proto", "src/backend.proto"], &["src"])?;
818 /// Ok(())
819 /// }
820 /// ```
821 pub fn protoc_arg<S>(&mut self, arg: S) -> &mut Self
822 where
823 S: AsRef<OsStr>,
824 {
825 self.protoc_args.push(arg.as_ref().to_owned());
826 self
827 }
828
829 /// Configures the optional module filename for easy inclusion of all generated Rust files
830 ///
831 /// If set, generates a file (inside the `OUT_DIR` or `out_dir()` as appropriate) which contains
832 /// a set of `pub mod XXX` statements combining to load all Rust files generated. This can allow
833 /// for a shortcut where multiple related proto files have been compiled together resulting in
834 /// a semi-complex set of includes.
835 ///
836 /// Turning a need for:
837 ///
838 /// ```rust,no_run,ignore
839 /// pub mod Foo {
840 /// pub mod Bar {
841 /// include!(concat!(env!("OUT_DIR"), "/foo.bar.rs"));
842 /// }
843 /// pub mod Baz {
844 /// include!(concat!(env!("OUT_DIR"), "/foo.baz.rs"));
845 /// }
846 /// }
847 /// ```
848 ///
849 /// Into the simpler:
850 ///
851 /// ```rust,no_run,ignore
852 /// include!(concat!(env!("OUT_DIR"), "/_includes.rs"));
853 /// ```
854 pub fn include_file<P>(&mut self, path: P) -> &mut Self
855 where
856 P: Into<PathBuf>,
857 {
858 self.include_file = Some(path.into());
859 self
860 }
861
862 /// Configures the code generator to format the output code via `prettyplease`.
863 ///
864 /// By default, this is enabled but if the `format` feature is not enabled this does
865 /// nothing.
866 pub fn format(&mut self, enabled: bool) -> &mut Self {
867 self.fmt = enabled;
868 self
869 }
870
871 /// Compile a [`FileDescriptorSet`] into Rust files during a Cargo build with
872 /// additional code generator configuration options.
873 ///
874 /// This method is like `compile_protos` function except it does not invoke `protoc`
875 /// and instead requires the user to supply a [`FileDescriptorSet`].
876 ///
877 /// # Example `build.rs`
878 ///
879 /// ```rust,no_run
880 /// # fn fds() -> FileDescriptorSet { todo!() }
881 /// fn main() -> std::io::Result<()> {
882 /// let file_descriptor_set = fds();
883 ///
884 /// prost_build::Config::new()
885 /// .compile_fds(file_descriptor_set)?;
886 /// }
887 /// ```
888 pub fn compile_fds(&mut self, fds: FileDescriptorSet) -> Result<()> {
889 let mut target_is_env = false;
890 let target: PathBuf = self.out_dir.clone().map(Ok).unwrap_or_else(|| {
891 env::var_os("OUT_DIR")
892 .ok_or_else(|| {
893 Error::new(ErrorKind::Other, "OUT_DIR environment variable is not set")
894 })
895 .map(|val| {
896 target_is_env = true;
897 Into::into(val)
898 })
899 })?;
900
901 let requests = fds
902 .file
903 .into_iter()
904 .map(|descriptor| {
905 (
906 Module::from_protobuf_package_name(descriptor.package()),
907 descriptor,
908 )
909 })
910 .collect::<Vec<_>>();
911
912 let file_names = requests
913 .iter()
914 .map(|req| {
915 (
916 req.0.clone(),
917 req.0.to_file_name_or(&self.default_package_filename),
918 )
919 })
920 .collect::<HashMap<Module, String>>();
921
922 let modules = self.generate(requests)?;
923 for (module, content) in &modules {
924 let file_name = file_names
925 .get(module)
926 .expect("every module should have a filename");
927
928 let file_name = if file_name.ends_with(".rs") {
929 let check = &file_name[..file_name.len() - 3];
930 if check.contains('.') {
931 format!("{}.rs", check.replace('.', "_"))
932 } else {
933 file_name.to_string()
934 }
935 } else {
936 file_name.to_string()
937 };
938
939 let output_path = target.join(&file_name);
940
941 let previous_content = fs::read(&output_path);
942
943 if previous_content
944 .map(|previous_content| previous_content == content.as_bytes())
945 .unwrap_or(false)
946 {
947 trace!("unchanged: {:?}", file_name);
948 } else {
949 trace!("writing: {:?}", file_name);
950 fs::write(output_path, content)?;
951 }
952 }
953
954 if let Some(ref include_file) = self.include_file {
955 trace!("Writing include file: {:?}", target.join(include_file));
956 let mut file = fs::File::create(target.join(include_file))?;
957 self.write_includes(
958 modules.keys().collect(),
959 &mut file,
960 0,
961 if target_is_env { None } else { Some(&target) },
962 )?;
963 file.flush()?;
964 }
965
966 Ok(())
967 }
968
969 /// Compile `.proto` files into Rust files during a Cargo build with additional code generator
970 /// configuration options.
971 ///
972 /// This method is like the `prost_build::compile_protos` function, with the added ability to
973 /// specify non-default code generation options. See that function for more information about
974 /// the arguments and generated outputs.
975 ///
976 /// The `protos` and `includes` arguments are ignored if `skip_protoc_run` is specified.
977 ///
978 /// # Example `build.rs`
979 ///
980 /// ```rust,no_run
981 /// # use std::io::Result;
982 /// fn main() -> Result<()> {
983 /// let mut prost_build = prost_build::Config::new();
984 /// prost_build.btree_map(&["."]);
985 /// prost_build.compile_protos(&["src/frontend.proto", "src/backend.proto"], &["src"])?;
986 /// Ok(())
987 /// }
988 /// ```
989 pub fn compile_protos(
990 &mut self,
991 protos: &[impl AsRef<Path>],
992 includes: &[impl AsRef<Path>],
993 ) -> Result<()> {
994 // TODO: This should probably emit 'rerun-if-changed=PATH' directives for cargo, however
995 // according to [1] if any are output then those paths replace the default crate root,
996 // which is undesirable. Figure out how to do it in an additive way; perhaps gcc-rs has
997 // this figured out.
998 // [1]: http://doc.crates.io/build-script.html#outputs-of-the-build-script
999
1000 let tmp;
1001 let file_descriptor_set_path = if let Some(path) = &self.file_descriptor_set_path {
1002 path.clone()
1003 } else {
1004 if self.skip_protoc_run {
1005 return Err(Error::new(
1006 ErrorKind::Other,
1007 "file_descriptor_set_path is required with skip_protoc_run",
1008 ));
1009 }
1010 tmp = tempfile::Builder::new().prefix("prost-build").tempdir()?;
1011 tmp.path().join("prost-descriptor-set")
1012 };
1013
1014 if !self.skip_protoc_run {
1015 let protoc = protoc_from_env();
1016
1017 let mut cmd = Command::new(protoc.clone());
1018 cmd.arg("--include_imports")
1019 .arg("--include_source_info")
1020 .arg("-o")
1021 .arg(&file_descriptor_set_path);
1022
1023 for include in includes {
1024 if include.as_ref().exists() {
1025 cmd.arg("-I").arg(include.as_ref());
1026 } else {
1027 debug!(
1028 "ignoring {} since it does not exist.",
1029 include.as_ref().display()
1030 )
1031 }
1032 }
1033
1034 // Set the protoc include after the user includes in case the user wants to
1035 // override one of the built-in .protos.
1036 if let Some(protoc_include) = protoc_include_from_env() {
1037 cmd.arg("-I").arg(protoc_include);
1038 }
1039
1040 for arg in &self.protoc_args {
1041 cmd.arg(arg);
1042 }
1043
1044 for proto in protos {
1045 cmd.arg(proto.as_ref());
1046 }
1047
1048 debug!("Running: {:?}", cmd);
1049
1050 let output = cmd.output().map_err(|error| {
1051 Error::new(
1052 error.kind(),
1053 format!("failed to invoke protoc (hint: https://docs.rs/prost-build/#sourcing-protoc): (path: {:?}): {}", &protoc, error),
1054 )
1055 })?;
1056
1057 if !output.status.success() {
1058 return Err(Error::new(
1059 ErrorKind::Other,
1060 format!("protoc failed: {}", String::from_utf8_lossy(&output.stderr)),
1061 ));
1062 }
1063 }
1064
1065 let buf = fs::read(&file_descriptor_set_path).map_err(|e| {
1066 Error::new(
1067 e.kind(),
1068 format!(
1069 "unable to open file_descriptor_set_path: {:?}, OS: {}",
1070 &file_descriptor_set_path, e
1071 ),
1072 )
1073 })?;
1074 let file_descriptor_set = FileDescriptorSet::decode(&*buf).map_err(|error| {
1075 Error::new(
1076 ErrorKind::InvalidInput,
1077 format!("invalid FileDescriptorSet: {}", error),
1078 )
1079 })?;
1080
1081 self.compile_fds(file_descriptor_set)
1082 }
1083
1084 fn write_includes(
1085 &self,
1086 mut entries: Vec<&Module>,
1087 outfile: &mut fs::File,
1088 depth: usize,
1089 basepath: Option<&PathBuf>,
1090 ) -> Result<usize> {
1091 let mut written = 0;
1092 entries.sort();
1093
1094 while !entries.is_empty() {
1095 let modident = entries[0].part(depth);
1096 let matching: Vec<&Module> = entries
1097 .iter()
1098 .filter(|&v| v.part(depth) == modident)
1099 .copied()
1100 .collect();
1101 {
1102 // Will NLL sort this mess out?
1103 let _temp = entries
1104 .drain(..)
1105 .filter(|&v| v.part(depth) != modident)
1106 .collect();
1107 entries = _temp;
1108 }
1109 self.write_line(outfile, depth, &format!("pub mod {} {{", modident))?;
1110 let subwritten = self.write_includes(
1111 matching
1112 .iter()
1113 .filter(|v| v.len() > depth + 1)
1114 .copied()
1115 .collect(),
1116 outfile,
1117 depth + 1,
1118 basepath,
1119 )?;
1120 written += subwritten;
1121 if subwritten != matching.len() {
1122 let modname = matching[0].to_partial_file_name(..=depth);
1123 if basepath.is_some() {
1124 self.write_line(
1125 outfile,
1126 depth + 1,
1127 &format!("include!(\"{}.rs\");", modname),
1128 )?;
1129 } else {
1130 self.write_line(
1131 outfile,
1132 depth + 1,
1133 &format!("include!(concat!(env!(\"OUT_DIR\"), \"/{}.rs\"));", modname),
1134 )?;
1135 }
1136 written += 1;
1137 }
1138
1139 self.write_line(outfile, depth, "}")?;
1140 }
1141 Ok(written)
1142 }
1143
1144 fn write_line(&self, outfile: &mut fs::File, depth: usize, line: &str) -> Result<()> {
1145 outfile.write_all(format!("{}{}\n", (" ").to_owned().repeat(depth), line).as_bytes())
1146 }
1147
1148 /// Processes a set of modules and file descriptors, returning a map of modules to generated
1149 /// code contents.
1150 ///
1151 /// This is generally used when control over the output should not be managed by Prost,
1152 /// such as in a flow for a `protoc` code generating plugin. When compiling as part of a
1153 /// `build.rs` file, instead use [`compile_protos()`].
1154 pub fn generate(
1155 &mut self,
1156 requests: Vec<(Module, FileDescriptorProto)>,
1157 ) -> Result<HashMap<Module, String>> {
1158 let mut modules = HashMap::new();
1159 let mut packages = HashMap::new();
1160
1161 let message_graph = MessageGraph::new(requests.iter().map(|x| &x.1))
1162 .map_err(|error| Error::new(ErrorKind::InvalidInput, error))?;
1163 let extern_paths = ExternPaths::new(&self.extern_paths, self.prost_types)
1164 .map_err(|error| Error::new(ErrorKind::InvalidInput, error))?;
1165
1166 for (request_module, request_fd) in requests {
1167 // Only record packages that have services
1168 if !request_fd.service.is_empty() {
1169 packages.insert(request_module.clone(), request_fd.package().to_string());
1170 }
1171 let buf = modules
1172 .entry(request_module.clone())
1173 .or_insert_with(String::new);
1174 CodeGenerator::generate(self, &message_graph, &extern_paths, request_fd, buf);
1175 if buf.is_empty() {
1176 // Did not generate any code, remove from list to avoid inclusion in include file or output file list
1177 modules.remove(&request_module);
1178 }
1179 }
1180
1181 if let Some(ref mut service_generator) = self.service_generator {
1182 for (module, package) in packages {
1183 let buf = modules.get_mut(&module).unwrap();
1184 service_generator.finalize_package(&package, buf);
1185 }
1186 }
1187
1188 if self.fmt {
1189 self.fmt_modules(&mut modules);
1190 }
1191
1192 Ok(modules)
1193 }
1194
1195 #[cfg(feature = "format")]
1196 fn fmt_modules(&mut self, modules: &mut HashMap<Module, String>) {
1197 for buf in modules.values_mut() {
1198 let file = syn::parse_file(buf).unwrap();
1199 let formatted = prettyplease::unparse(&file);
1200 *buf = formatted;
1201 }
1202 }
1203
1204 #[cfg(not(feature = "format"))]
1205 fn fmt_modules(&mut self, _: &mut HashMap<Module, String>) {}
1206}
1207
1208impl default::Default for Config {
1209 fn default() -> Config {
1210 Config {
1211 file_descriptor_set_path: None,
1212 service_generator: None,
1213 map_type: PathMap::default(),
1214 bytes_type: PathMap::default(),
1215 type_attributes: PathMap::default(),
1216 message_attributes: PathMap::default(),
1217 enum_attributes: PathMap::default(),
1218 field_attributes: PathMap::default(),
1219 prost_types: true,
1220 strip_enum_prefix: true,
1221 out_dir: None,
1222 extern_paths: Vec::new(),
1223 default_package_filename: "_".to_string(),
1224 protoc_args: Vec::new(),
1225 disable_comments: PathMap::default(),
1226 skip_protoc_run: false,
1227 include_file: None,
1228 prost_path: None,
1229 fmt: true,
1230 }
1231 }
1232}
1233
1234impl fmt::Debug for Config {
1235 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
1236 fmt.debug_struct("Config")
1237 .field("file_descriptor_set_path", &self.file_descriptor_set_path)
1238 .field("service_generator", &self.service_generator.is_some())
1239 .field("map_type", &self.map_type)
1240 .field("bytes_type", &self.bytes_type)
1241 .field("type_attributes", &self.type_attributes)
1242 .field("field_attributes", &self.field_attributes)
1243 .field("prost_types", &self.prost_types)
1244 .field("strip_enum_prefix", &self.strip_enum_prefix)
1245 .field("out_dir", &self.out_dir)
1246 .field("extern_paths", &self.extern_paths)
1247 .field("default_package_filename", &self.default_package_filename)
1248 .field("protoc_args", &self.protoc_args)
1249 .field("disable_comments", &self.disable_comments)
1250 .field("prost_path", &self.prost_path)
1251 .finish()
1252 }
1253}
1254
1255/// A Rust module path for a Protobuf package.
1256#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
1257pub struct Module {
1258 components: Vec<String>,
1259}
1260
1261impl Module {
1262 /// Construct a module path from an iterator of parts.
1263 pub fn from_parts<I>(parts: I) -> Self
1264 where
1265 I: IntoIterator,
1266 I::Item: Into<String>,
1267 {
1268 Self {
1269 components: parts.into_iter().map(|s| s.into()).collect(),
1270 }
1271 }
1272
1273 /// Construct a module path from a Protobuf package name.
1274 ///
1275 /// Constituent parts are automatically converted to snake case in order to follow
1276 /// Rust module naming conventions.
1277 pub fn from_protobuf_package_name(name: &str) -> Self {
1278 Self {
1279 components: name
1280 .split('.')
1281 .filter(|s| !s.is_empty())
1282 .map(to_snake)
1283 .collect(),
1284 }
1285 }
1286
1287 /// An iterator over the parts of the path.
1288 pub fn parts(&self) -> impl Iterator<Item = &str> {
1289 self.components.iter().map(|s| s.as_str())
1290 }
1291
1292 /// Format the module path into a filename for generated Rust code.
1293 ///
1294 /// If the module path is empty, `default` is used to provide the root of the filename.
1295 pub fn to_file_name_or(&self, default: &str) -> String {
1296 let mut root = if self.components.is_empty() {
1297 default.to_owned()
1298 } else {
1299 self.components.join(".")
1300 };
1301
1302 root.push_str(".rs");
1303
1304 root
1305 }
1306
1307 /// The number of parts in the module's path.
1308 pub fn len(&self) -> usize {
1309 self.components.len()
1310 }
1311
1312 /// Whether the module's path contains any components.
1313 pub fn is_empty(&self) -> bool {
1314 self.components.is_empty()
1315 }
1316
1317 fn to_partial_file_name(&self, range: RangeToInclusive<usize>) -> String {
1318 self.components[range].join(".")
1319 }
1320
1321 fn part(&self, idx: usize) -> &str {
1322 self.components[idx].as_str()
1323 }
1324}
1325
1326impl fmt::Display for Module {
1327 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1328 let mut parts = self.parts();
1329 if let Some(first) = parts.next() {
1330 f.write_str(first)?;
1331 }
1332 for part in parts {
1333 f.write_str("::")?;
1334 f.write_str(part)?;
1335 }
1336 Ok(())
1337 }
1338}
1339
1340/// Compile `.proto` files into Rust files during a Cargo build.
1341///
1342/// The generated `.rs` files are written to the Cargo `OUT_DIR` directory, suitable for use with
1343/// the [include!][1] macro. See the [Cargo `build.rs` code generation][2] example for more info.
1344///
1345/// This function should be called in a project's `build.rs`.
1346///
1347/// # Arguments
1348///
1349/// **`protos`** - Paths to `.proto` files to compile. Any transitively [imported][3] `.proto`
1350/// files are automatically be included.
1351///
1352/// **`includes`** - Paths to directories in which to search for imports. Directories are searched
1353/// in order. The `.proto` files passed in **`protos`** must be found in one of the provided
1354/// include directories.
1355///
1356/// # Errors
1357///
1358/// This function can fail for a number of reasons:
1359///
1360/// - Failure to locate or download `protoc`.
1361/// - Failure to parse the `.proto`s.
1362/// - Failure to locate an imported `.proto`.
1363/// - Failure to compile a `.proto` without a [package specifier][4].
1364///
1365/// It's expected that this function call be `unwrap`ed in a `build.rs`; there is typically no
1366/// reason to gracefully recover from errors during a build.
1367///
1368/// # Example `build.rs`
1369///
1370/// ```rust,no_run
1371/// # use std::io::Result;
1372/// fn main() -> Result<()> {
1373/// prost_build::compile_protos(&["src/frontend.proto", "src/backend.proto"], &["src"])?;
1374/// Ok(())
1375/// }
1376/// ```
1377///
1378/// [1]: https://doc.rust-lang.org/std/macro.include.html
1379/// [2]: http://doc.crates.io/build-script.html#case-study-code-generation
1380/// [3]: https://developers.google.com/protocol-buffers/docs/proto3#importing-definitions
1381/// [4]: https://developers.google.com/protocol-buffers/docs/proto#packages
1382pub fn compile_protos(protos: &[impl AsRef<Path>], includes: &[impl AsRef<Path>]) -> Result<()> {
1383 Config::new().compile_protos(protos, includes)
1384}
1385
1386/// Compile a [`FileDescriptorSet`] into Rust files during a Cargo build.
1387///
1388/// The generated `.rs` files are written to the Cargo `OUT_DIR` directory, suitable for use with
1389/// the [include!][1] macro. See the [Cargo `build.rs` code generation][2] example for more info.
1390///
1391/// This function should be called in a project's `build.rs`.
1392///
1393/// This function can be combined with a crate like [`protox`] which outputs a
1394/// [`FileDescriptorSet`] and is a pure Rust implementation of `protoc`.
1395///
1396/// [`protox`]: https://github.com/andrewhickman/protox
1397///
1398/// # Example
1399/// ```rust,no_run
1400/// # fn fds() -> FileDescriptorSet { todo!() }
1401/// fn main() -> std::io::Result<()> {
1402/// let file_descriptor_set = fds();
1403///
1404/// prost_build::compile_fds(file_descriptor_set)?;
1405/// }
1406/// ```
1407pub fn compile_fds(fds: FileDescriptorSet) -> Result<()> {
1408 Config::new().compile_fds(fds)
1409}
1410
1411/// Returns the path to the `protoc` binary.
1412pub fn protoc_from_env() -> PathBuf {
1413 let os_specific_hint = if cfg!(target_os = "macos") {
1414 "You could try running `brew install protobuf` or downloading it from https://github.com/protocolbuffers/protobuf/releases"
1415 } else if cfg!(target_os = "linux") {
1416 "If you're on debian, try `apt-get install protobuf-compiler` or download it from https://github.com/protocolbuffers/protobuf/releases"
1417 } else {
1418 "You can download it from https://github.com/protocolbuffers/protobuf/releases or from your package manager."
1419 };
1420 let error_msg =
1421 "Could not find `protoc` installation and this build crate cannot proceed without
1422 this knowledge. If `protoc` is installed and this crate had trouble finding
1423 it, you can set the `PROTOC` environment variable with the specific path to your
1424 installed `protoc` binary.";
1425 let msg = format!(
1426 "{}{}
1427
1428For more information: https://docs.rs/prost-build/#sourcing-protoc
1429",
1430 error_msg, os_specific_hint
1431 );
1432
1433 env::var_os("PROTOC")
1434 .map(PathBuf::from)
1435 .or_else(|| which::which("protoc").ok())
1436 .expect(&msg)
1437}
1438
1439/// Returns the path to the Protobuf include directory.
1440pub fn protoc_include_from_env() -> Option<PathBuf> {
1441 let protoc_include: PathBuf = env::var_os("PROTOC_INCLUDE")?.into();
1442
1443 if !protoc_include.exists() {
1444 panic!(
1445 "PROTOC_INCLUDE environment variable points to non-existent directory ({:?})",
1446 protoc_include
1447 );
1448 }
1449 if !protoc_include.is_dir() {
1450 panic!(
1451 "PROTOC_INCLUDE environment variable points to a non-directory file ({:?})",
1452 protoc_include
1453 );
1454 }
1455
1456 Some(protoc_include)
1457}
1458
1459#[cfg(test)]
1460mod tests {
1461 use std::cell::RefCell;
1462 use std::fs::File;
1463 use std::io::Read;
1464 use std::path::Path;
1465 use std::rc::Rc;
1466
1467 use super::*;
1468
1469 /// An example service generator that generates a trait with methods corresponding to the
1470 /// service methods.
1471 struct ServiceTraitGenerator;
1472
1473 impl ServiceGenerator for ServiceTraitGenerator {
1474 fn generate(&mut self, service: Service, buf: &mut String) {
1475 // Generate a trait for the service.
1476 service.comments.append_with_indent(0, buf);
1477 buf.push_str(&format!("trait {} {{\n", &service.name));
1478
1479 // Generate the service methods.
1480 for method in service.methods {
1481 method.comments.append_with_indent(1, buf);
1482 buf.push_str(&format!(
1483 " fn {}(_: {}) -> {};\n",
1484 method.name, method.input_type, method.output_type
1485 ));
1486 }
1487
1488 // Close out the trait.
1489 buf.push_str("}\n");
1490 }
1491 fn finalize(&mut self, buf: &mut String) {
1492 // Needs to be present only once, no matter how many services there are
1493 buf.push_str("pub mod utils { }\n");
1494 }
1495 }
1496
1497 /// Implements `ServiceGenerator` and provides some state for assertions.
1498 struct MockServiceGenerator {
1499 state: Rc<RefCell<MockState>>,
1500 }
1501
1502 /// Holds state for `MockServiceGenerator`
1503 #[derive(Default)]
1504 struct MockState {
1505 service_names: Vec<String>,
1506 package_names: Vec<String>,
1507 finalized: u32,
1508 }
1509
1510 impl MockServiceGenerator {
1511 fn new(state: Rc<RefCell<MockState>>) -> Self {
1512 Self { state }
1513 }
1514 }
1515
1516 impl ServiceGenerator for MockServiceGenerator {
1517 fn generate(&mut self, service: Service, _buf: &mut String) {
1518 let mut state = self.state.borrow_mut();
1519 state.service_names.push(service.name);
1520 }
1521
1522 fn finalize(&mut self, _buf: &mut String) {
1523 let mut state = self.state.borrow_mut();
1524 state.finalized += 1;
1525 }
1526
1527 fn finalize_package(&mut self, package: &str, _buf: &mut String) {
1528 let mut state = self.state.borrow_mut();
1529 state.package_names.push(package.to_string());
1530 }
1531 }
1532
1533 #[test]
1534 fn smoke_test() {
1535 let _ = env_logger::try_init();
1536 Config::new()
1537 .service_generator(Box::new(ServiceTraitGenerator))
1538 .out_dir(std::env::temp_dir())
1539 .compile_protos(&["src/fixtures/smoke_test/smoke_test.proto"], &["src"])
1540 .unwrap();
1541 }
1542
1543 #[test]
1544 fn finalize_package() {
1545 let _ = env_logger::try_init();
1546
1547 let state = Rc::new(RefCell::new(MockState::default()));
1548 let gen = MockServiceGenerator::new(Rc::clone(&state));
1549
1550 Config::new()
1551 .service_generator(Box::new(gen))
1552 .include_file("_protos.rs")
1553 .out_dir(std::env::temp_dir())
1554 .compile_protos(
1555 &[
1556 "src/fixtures/helloworld/hello.proto",
1557 "src/fixtures/helloworld/goodbye.proto",
1558 ],
1559 &["src/fixtures/helloworld"],
1560 )
1561 .unwrap();
1562
1563 let state = state.borrow();
1564 assert_eq!(&state.service_names, &["Greeting", "Farewell"]);
1565 assert_eq!(&state.package_names, &["helloworld"]);
1566 assert_eq!(state.finalized, 3);
1567 }
1568
1569 #[test]
1570 fn test_generate_message_attributes() {
1571 let _ = env_logger::try_init();
1572
1573 let out_dir = std::env::temp_dir();
1574
1575 Config::new()
1576 .out_dir(out_dir.clone())
1577 .message_attribute(".", "#[derive(derive_builder::Builder)]")
1578 .enum_attribute(".", "#[some_enum_attr(u8)]")
1579 .compile_protos(
1580 &["src/fixtures/helloworld/hello.proto"],
1581 &["src/fixtures/helloworld"],
1582 )
1583 .unwrap();
1584
1585 let out_file = out_dir
1586 .join("helloworld.rs")
1587 .as_path()
1588 .display()
1589 .to_string();
1590 let expected_content = read_all_content("src/fixtures/helloworld/_expected_helloworld.rs")
1591 .replace("\r\n", "\n");
1592 let content = read_all_content(&out_file).replace("\r\n", "\n");
1593 assert_eq!(
1594 expected_content, content,
1595 "Unexpected content: \n{}",
1596 content
1597 );
1598 }
1599
1600 #[test]
1601 fn test_generate_no_empty_outputs() {
1602 let _ = env_logger::try_init();
1603 let state = Rc::new(RefCell::new(MockState::default()));
1604 let gen = MockServiceGenerator::new(Rc::clone(&state));
1605 let include_file = "_include.rs";
1606 let out_dir = std::env::temp_dir()
1607 .as_path()
1608 .join("test_generate_no_empty_outputs");
1609 let previously_empty_proto_path = out_dir.as_path().join(Path::new("google.protobuf.rs"));
1610 // For reproducibility, ensure we start with the out directory created and empty
1611 let _ = fs::remove_dir_all(&out_dir);
1612 let _ = fs::create_dir(&out_dir);
1613
1614 Config::new()
1615 .service_generator(Box::new(gen))
1616 .include_file(include_file)
1617 .out_dir(&out_dir)
1618 .compile_protos(
1619 &["src/fixtures/imports_empty/imports_empty.proto"],
1620 &["src/fixtures/imports_empty"],
1621 )
1622 .unwrap();
1623
1624 // Prior to PR introducing this test, the generated include file would have the file
1625 // google.protobuf.rs which was an empty file. Now that file should only exist if it has content
1626 if let Ok(mut f) = File::open(&previously_empty_proto_path) {
1627 // Since this file was generated, it should not be empty.
1628 let mut contents = String::new();
1629 f.read_to_string(&mut contents).unwrap();
1630 assert!(!contents.is_empty());
1631 } else {
1632 // The file wasn't generated so the result include file should not reference it
1633 let expected = read_all_content("src/fixtures/imports_empty/_expected_include.rs");
1634 let actual = read_all_content(
1635 out_dir
1636 .as_path()
1637 .join(Path::new(include_file))
1638 .display()
1639 .to_string()
1640 .as_str(),
1641 );
1642 // Normalizes windows and Linux-style EOL
1643 let expected = expected.replace("\r\n", "\n");
1644 let actual = actual.replace("\r\n", "\n");
1645 assert_eq!(expected, actual);
1646 }
1647 }
1648
1649 #[test]
1650 fn deterministic_include_file() {
1651 let _ = env_logger::try_init();
1652
1653 for _ in 1..10 {
1654 let state = Rc::new(RefCell::new(MockState::default()));
1655 let gen = MockServiceGenerator::new(Rc::clone(&state));
1656 let include_file = "_include.rs";
1657 let tmp_dir = std::env::temp_dir();
1658
1659 Config::new()
1660 .service_generator(Box::new(gen))
1661 .include_file(include_file)
1662 .out_dir(std::env::temp_dir())
1663 .compile_protos(
1664 &[
1665 "src/fixtures/alphabet/a.proto",
1666 "src/fixtures/alphabet/b.proto",
1667 "src/fixtures/alphabet/c.proto",
1668 "src/fixtures/alphabet/d.proto",
1669 "src/fixtures/alphabet/e.proto",
1670 "src/fixtures/alphabet/f.proto",
1671 ],
1672 &["src/fixtures/alphabet"],
1673 )
1674 .unwrap();
1675
1676 let expected = read_all_content("src/fixtures/alphabet/_expected_include.rs");
1677 let actual = read_all_content(
1678 tmp_dir
1679 .as_path()
1680 .join(Path::new(include_file))
1681 .display()
1682 .to_string()
1683 .as_str(),
1684 );
1685 // Normalizes windows and Linux-style EOL
1686 let expected = expected.replace("\r\n", "\n");
1687 let actual = actual.replace("\r\n", "\n");
1688
1689 assert_eq!(expected, actual);
1690 }
1691 }
1692
1693 fn read_all_content(filepath: &str) -> String {
1694 let mut f = File::open(filepath).unwrap();
1695 let mut content = String::new();
1696 f.read_to_string(&mut content).unwrap();
1697 content
1698 }
1699}