Skip to content

Commit

Permalink
Make prost a no_std compatible library, and prost-build able to gen…
Browse files Browse the repository at this point in the history
…erate `no_std` code

The alternative is to get collections types from `core` and `alloc`.
In the `no_std` mode in `prost_build`, we force it to always use BTreeMap
since HashMap was not stabilized in `alloc::collections` library.

The functionality is identical and the only incompatibilities in the interface
are that we cannot use `std::error::Error` or `std::io::Error` in `no_std` mode
because these types have not been moved to `alloc` and there is no alternative.

This also uprevs `bytes` dependency to 0.5, and fixes up breakage after IntoBuf api
is removed in `bytes` crate.
  • Loading branch information
cbeck88 committed Sep 9, 2019
1 parent 9551f28 commit 35869fc
Show file tree
Hide file tree
Showing 16 changed files with 351 additions and 219 deletions.
8 changes: 5 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ os:
- linux
- osx

# After 1.36.0 alloc crate is stable and we can avoid special handling below
rust:
- 1.32.0
- 1.36.0
- nightly

script:
- cargo build --verbose --all --exclude benchmarks
- cargo test --verbose --all --exclude benchmarks
- cargo build --verbose --all --exclude benchmarks --exclude test-alloc
- cargo test --verbose --all --exclude benchmarks --exclude test-alloc
- if [[ $TRAVIS_RUST_VERSION = nightly* ]]; then
cargo test --verbose test-alloc
cargo bench --verbose --no-run;
fi
9 changes: 7 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ members = [
"protobuf",
"tests",
"tests-2015",
"tests-alloc",
]
exclude = [
# The fuzz crate can't be compiled or tested without the 'cargo fuzz' command,
Expand All @@ -33,11 +34,15 @@ exclude = [
]

[features]
default = ["prost-derive"]
default = ["prost-derive", "std"]
no-recursion-limit = []
std = []
# When std is disabled, we attempt to provide no_std support in prost
# Config::use_alloc_collections() should be set when using prost_build in your build.rs
# so that generated files will not have std:: either, and will use alloc crate instead

[dependencies]
bytes = "0.4.7"
bytes = { git = "https://github.com/tokio-rs/bytes", rev = "c17e40115f5bb2a2db71ed90dceae6ec643dc024", default-features = false }
prost-derive = { version = "0.5.0", path = "prost-derive", optional = true }

[dev-dependencies]
Expand Down
20 changes: 12 additions & 8 deletions prost-build/src/code_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use crate::ast::{Comments, Method, Service};
use crate::extern_paths::ExternPaths;
use crate::ident::{match_ident, to_snake, to_upper_camel};
use crate::message_graph::MessageGraph;
use crate::CollectionsLib;
use crate::Config;

#[derive(PartialEq)]
Expand Down Expand Up @@ -366,12 +367,14 @@ impl<'a> CodeGenerator<'a> {
self.buf.push_str(&to_snake(field.name()));
self.buf.push_str(": ");
if repeated {
self.buf.push_str("::std::vec::Vec<");
self.buf.push_str(self.config.collections_lib.to_str());
self.buf.push_str("::vec::Vec<");
} else if optional {
self.buf.push_str("::std::option::Option<");
self.buf.push_str("::core::option::Option<");
}
if boxed {
self.buf.push_str("::std::boxed::Box<");
self.buf.push_str(self.config.collections_lib.to_str());
self.buf.push_str("::boxed::Box<");
}
self.buf.push_str(&ty);
if boxed {
Expand Down Expand Up @@ -403,7 +406,7 @@ impl<'a> CodeGenerator<'a> {
self.append_doc();
self.push_indent();

let btree_map = self
let btree_map = (self.config.collections_lib != CollectionsLib::Std) || self
.config
.btree_map
.iter()
Expand All @@ -426,8 +429,9 @@ impl<'a> CodeGenerator<'a> {
self.append_field_attributes(msg_name, field.name());
self.push_indent();
self.buf.push_str(&format!(
"pub {}: ::std::collections::{}<{}, {}>,\n",
"pub {}: {}::collections::{}<{}, {}>,\n",
to_snake(field.name()),
self.config.collections_lib.to_str(),
rust_ty,
key_ty,
value_ty
Expand Down Expand Up @@ -459,7 +463,7 @@ impl<'a> CodeGenerator<'a> {
self.append_field_attributes(fq_message_name, oneof.name());
self.push_indent();
self.buf.push_str(&format!(
"pub {}: ::std::option::Option<{}>,\n",
"pub {}: ::core::option::Option<{}>,\n",
to_snake(oneof.name()),
name
));
Expand Down Expand Up @@ -713,8 +717,8 @@ impl<'a> CodeGenerator<'a> {
Type::Int32 | Type::Sfixed32 | Type::Sint32 | Type::Enum => String::from("i32"),
Type::Int64 | Type::Sfixed64 | Type::Sint64 => String::from("i64"),
Type::Bool => String::from("bool"),
Type::String => String::from("std::string::String"),
Type::Bytes => String::from("std::vec::Vec<u8>"),
Type::String => format!("{}::string::string", self.config.collections_lib.to_str()),
Type::Bytes => format!("{}::vec::Vec<u8>", self.config.collections_lib.to_str()),
Type::Group | Type::Message => self.resolve_ident(field.type_name()),
}
}
Expand Down
31 changes: 31 additions & 0 deletions prost-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,32 @@ pub trait ServiceGenerator {
fn finalize(&mut self, _buf: &mut String) {}
}

/// Configuration enum for whether to use `std` prefixes or `alloc` prefixes in generated code
///
/// This option also forces Btree everywhere, overriding the BtreeMap options,
/// since HashMap is not in alloc::collections, only std (it requires randomness)
///
#[derive(PartialEq)]
pub enum CollectionsLib {
Std,
Alloc,
}

impl CollectionsLib {
pub fn to_str(&self) -> &'static str {
match self {
CollectionsLib::Std => { "::std" },
CollectionsLib::Alloc => { "::alloc" },
}
}
}

/// Configuration options for Protobuf code generation.
///
/// This configuration builder can be used to set non-default code generation options.
pub struct Config {
service_generator: Option<Box<dyn ServiceGenerator>>,
collections_lib: CollectionsLib,
btree_map: Vec<String>,
type_attributes: Vec<(String, String)>,
field_attributes: Vec<(String, String)>,
Expand Down Expand Up @@ -460,6 +481,15 @@ impl Config {
self
}

/// Configure the code generator to use the `::alloc` namespace rather than `::std`, and Btree everywhere
/// rather than `std`.
///
/// This allows generated code to be used in a `#![no_std]` crate
pub fn use_alloc_collections_lib(&mut self) -> &mut Self {
self.collections_lib = CollectionsLib::Alloc;
self
}

/// Configures the output directory where generated Rust files will be written.
///
/// If unset, defaults to the `OUT_DIR` environment variable. `OUT_DIR` is set by Cargo when
Expand Down Expand Up @@ -581,6 +611,7 @@ impl default::Default for Config {
fn default() -> Config {
Config {
service_generator: None,
collections_lib: CollectionsLib::Std,
btree_map: Vec::new(),
type_attributes: Vec::new(),
field_attributes: Vec::new(),
Expand Down
6 changes: 5 additions & 1 deletion prost-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ edition = "2018"
doctest = false
test = false

[features]
default = ["std"]
std = []

[dependencies]
bytes = "0.4.7"
prost = { version = "0.5.0", path = ".." }
prost = { version = "0.5.0", path = "..", default-features = false }
22 changes: 11 additions & 11 deletions prost-types/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Version {
#[prost(int32, optional, tag="1")]
pub major: ::std::option::Option<i32>,
pub major: ::core::option::Option<i32>,
#[prost(int32, optional, tag="2")]
pub minor: ::std::option::Option<i32>,
pub minor: ::core::option::Option<i32>,
#[prost(int32, optional, tag="3")]
pub patch: ::std::option::Option<i32>,
pub patch: ::core::option::Option<i32>,
/// A suffix for alpha, beta or rc release, e.g., "alpha-1", "rc2". It should
/// be empty for mainline stable releases.
#[prost(string, optional, tag="4")]
pub suffix: ::std::option::Option<std::string::String>,
pub suffix: ::core::option::Option<alloc::string::String>,
}
/// An encoded CodeGeneratorRequest is written to the plugin's stdin.
#[derive(Clone, PartialEq, ::prost::Message)]
Expand All @@ -19,10 +19,10 @@ pub struct CodeGeneratorRequest {
/// code generator should generate code only for these files. Each file's
/// descriptor will be included in proto_file, below.
#[prost(string, repeated, tag="1")]
pub file_to_generate: ::std::vec::Vec<std::string::String>,
pub file_to_generate: ::std::vec::Vec<alloc::string::String>,
/// The generator parameter passed on the command-line.
#[prost(string, optional, tag="2")]
pub parameter: ::std::option::Option<std::string::String>,
pub parameter: ::core::option::Option<alloc::string::String>,
/// FileDescriptorProtos for all files in files_to_generate and everything
/// they import. The files will appear in topological order, so each file
/// appears before any file that imports it.
Expand All @@ -41,7 +41,7 @@ pub struct CodeGeneratorRequest {
pub proto_file: ::std::vec::Vec<super::FileDescriptorProto>,
/// The version number of protocol compiler.
#[prost(message, optional, tag="3")]
pub compiler_version: ::std::option::Option<Version>,
pub compiler_version: ::core::option::Option<Version>,
}
/// The plugin writes an encoded CodeGeneratorResponse to stdout.
#[derive(Clone, PartialEq, ::prost::Message)]
Expand All @@ -55,7 +55,7 @@ pub struct CodeGeneratorResponse {
/// unparseable -- should be reported by writing a message to stderr and
/// exiting with a non-zero status code.
#[prost(string, optional, tag="1")]
pub error: ::std::option::Option<std::string::String>,
pub error: ::core::option::Option<alloc::string::String>,
#[prost(message, repeated, tag="15")]
pub file: ::std::vec::Vec<code_generator_response::File>,
}
Expand All @@ -75,7 +75,7 @@ pub mod code_generator_response {
/// this writing protoc does not optimize for this -- it will read the entire
/// CodeGeneratorResponse before writing files to disk.
#[prost(string, optional, tag="1")]
pub name: ::std::option::Option<std::string::String>,
pub name: ::core::option::Option<alloc::string::String>,
/// If non-empty, indicates that the named file should already exist, and the
/// content here is to be inserted into that file at a defined insertion
/// point. This feature allows a code generator to extend the output
Expand Down Expand Up @@ -114,9 +114,9 @@ pub mod code_generator_response {
///
/// If |insertion_point| is present, |name| must also be present.
#[prost(string, optional, tag="2")]
pub insertion_point: ::std::option::Option<std::string::String>,
pub insertion_point: ::core::option::Option<alloc::string::String>,
/// The file contents.
#[prost(string, optional, tag="15")]
pub content: ::std::option::Option<std::string::String>,
pub content: ::core::option::Option<alloc::string::String>,
}
}
15 changes: 13 additions & 2 deletions prost-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,15 @@
//!
//! [1]: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf

use std::i32;
use std::i64;
#![no_std]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;

use core::i32;
use core::i64;

#[cfg(feature = "std")]
use std::time;

include!("protobuf.rs");
Expand Down Expand Up @@ -51,6 +58,7 @@ impl Duration {
}

/// Converts a `std::time::Duration` to a `Duration`.
#[cfg(feature = "std")]
impl From<time::Duration> for Duration {
fn from(duration: time::Duration) -> Duration {
let seconds = duration.as_secs();
Expand All @@ -74,6 +82,7 @@ impl From<time::Duration> for Duration {
/// Converts a `Duration` to a result containing a positive (`Ok`) or negative (`Err`)
/// `std::time::Duration`.
// TODO: convert this to TryFrom when it's stable.
#[cfg(feature = "std")]
impl From<Duration> for Result<time::Duration, time::Duration> {
fn from(mut duration: Duration) -> Result<time::Duration, time::Duration> {
duration.normalize();
Expand Down Expand Up @@ -116,6 +125,7 @@ impl Timestamp {
}

/// Converts a `std::time::SystemTime` to a `Timestamp`.
#[cfg(feature = "std")]
impl From<time::SystemTime> for Timestamp {
fn from(time: time::SystemTime) -> Timestamp {
let duration = Duration::from(time.duration_since(time::UNIX_EPOCH).unwrap());
Expand All @@ -129,6 +139,7 @@ impl From<time::SystemTime> for Timestamp {
/// Converts a `Timestamp` to a `SystemTime`, or if the timestamp falls before the Unix epoch, a
/// duration containing the difference.
// TODO: convert this to TryFrom when it's stable.
#[cfg(feature = "std")]
impl From<Timestamp> for Result<time::SystemTime, time::Duration> {
fn from(mut timestamp: Timestamp) -> Result<time::SystemTime, time::Duration> {
timestamp.normalize();
Expand Down
Loading

0 comments on commit 35869fc

Please sign in to comment.