Skip to content

Commit 03fd097

Browse files
committed
Make prost a no_std compatible library, and prost-build able to generate 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. Note that for now, we put a patch to `bytes` to pin to a specific commit in master, since bytes `0.5` is not released yet.
1 parent 9551f28 commit 03fd097

File tree

26 files changed

+383
-248
lines changed

26 files changed

+383
-248
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ os:
1515
- osx
1616

1717
rust:
18-
- 1.32.0
18+
- 1.36.0
1919
- nightly
2020

2121
script:

Cargo.toml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ members = [
2525
"protobuf",
2626
"tests",
2727
"tests-2015",
28+
"tests-alloc",
2829
]
2930
exclude = [
3031
# The fuzz crate can't be compiled or tested without the 'cargo fuzz' command,
@@ -33,11 +34,15 @@ exclude = [
3334
]
3435

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

3944
[dependencies]
40-
bytes = "0.4.7"
45+
bytes = { version = "0.5", default-features = false }
4146
prost-derive = { version = "0.5.0", path = "prost-derive", optional = true }
4247

4348
[dev-dependencies]
@@ -54,3 +59,6 @@ name = "varint"
5459
[[bench]]
5560
name = "benchmark"
5661
harness = false
62+
63+
[patch.crates-io]
64+
bytes = { git = "https://github.com/tokio-rs/bytes", rev = "c17e40115f5bb2a2db71ed90dceae6ec643dc024" }

conformance/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ publish = false
66
edition = "2018"
77

88
[dependencies]
9-
bytes = "0.4.7"
9+
bytes = "0.5"
1010
env_logger = { version = "0.6", default-features = false }
1111
log = "0.3"
1212
prost = { path = ".." }

prost-build/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ description = "A Protocol Buffers implementation for the Rust Language."
1111
edition = "2018"
1212

1313
[dependencies]
14-
bytes = "0.4.7"
14+
bytes = "0.5"
1515
heck = "0.3"
1616
itertools = "0.8"
1717
log = "0.4"

prost-build/src/code_generator.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use crate::ast::{Comments, Method, Service};
1717
use crate::extern_paths::ExternPaths;
1818
use crate::ident::{match_ident, to_snake, to_upper_camel};
1919
use crate::message_graph::MessageGraph;
20+
use crate::CollectionsLib;
2021
use crate::Config;
2122

2223
#[derive(PartialEq)]
@@ -366,12 +367,14 @@ impl<'a> CodeGenerator<'a> {
366367
self.buf.push_str(&to_snake(field.name()));
367368
self.buf.push_str(": ");
368369
if repeated {
369-
self.buf.push_str("::std::vec::Vec<");
370+
self.buf.push_str(self.config.collections_lib.to_str());
371+
self.buf.push_str("::vec::Vec<");
370372
} else if optional {
371-
self.buf.push_str("::std::option::Option<");
373+
self.buf.push_str("::core::option::Option<");
372374
}
373375
if boxed {
374-
self.buf.push_str("::std::boxed::Box<");
376+
self.buf.push_str(self.config.collections_lib.to_str());
377+
self.buf.push_str("::boxed::Box<");
375378
}
376379
self.buf.push_str(&ty);
377380
if boxed {
@@ -403,7 +406,7 @@ impl<'a> CodeGenerator<'a> {
403406
self.append_doc();
404407
self.push_indent();
405408

406-
let btree_map = self
409+
let btree_map = (self.config.collections_lib != CollectionsLib::Std) || self
407410
.config
408411
.btree_map
409412
.iter()
@@ -426,8 +429,9 @@ impl<'a> CodeGenerator<'a> {
426429
self.append_field_attributes(msg_name, field.name());
427430
self.push_indent();
428431
self.buf.push_str(&format!(
429-
"pub {}: ::std::collections::{}<{}, {}>,\n",
432+
"pub {}: {}::collections::{}<{}, {}>,\n",
430433
to_snake(field.name()),
434+
self.config.collections_lib.to_str(),
431435
rust_ty,
432436
key_ty,
433437
value_ty
@@ -459,7 +463,7 @@ impl<'a> CodeGenerator<'a> {
459463
self.append_field_attributes(fq_message_name, oneof.name());
460464
self.push_indent();
461465
self.buf.push_str(&format!(
462-
"pub {}: ::std::option::Option<{}>,\n",
466+
"pub {}: ::core::option::Option<{}>,\n",
463467
to_snake(oneof.name()),
464468
name
465469
));
@@ -713,8 +717,8 @@ impl<'a> CodeGenerator<'a> {
713717
Type::Int32 | Type::Sfixed32 | Type::Sint32 | Type::Enum => String::from("i32"),
714718
Type::Int64 | Type::Sfixed64 | Type::Sint64 => String::from("i64"),
715719
Type::Bool => String::from("bool"),
716-
Type::String => String::from("std::string::String"),
717-
Type::Bytes => String::from("std::vec::Vec<u8>"),
720+
Type::String => format!("{}::string::String", self.config.collections_lib.to_str()),
721+
Type::Bytes => format!("{}::vec::Vec<u8>", self.config.collections_lib.to_str()),
718722
Type::Group | Type::Message => self.resolve_ident(field.type_name()),
719723
}
720724
}

prost-build/src/lib.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,32 @@ pub trait ServiceGenerator {
165165
fn finalize(&mut self, _buf: &mut String) {}
166166
}
167167

168+
/// Configuration enum for whether to use `std` prefixes or `alloc` prefixes in generated code
169+
///
170+
/// This option also forces Btree everywhere, overriding the BtreeMap options,
171+
/// since HashMap is not in alloc::collections, only std (it requires randomness)
172+
///
173+
#[derive(PartialEq)]
174+
pub enum CollectionsLib {
175+
Std,
176+
Alloc,
177+
}
178+
179+
impl CollectionsLib {
180+
pub fn to_str(&self) -> &'static str {
181+
match self {
182+
CollectionsLib::Std => { "::std" },
183+
CollectionsLib::Alloc => { "::alloc" },
184+
}
185+
}
186+
}
187+
168188
/// Configuration options for Protobuf code generation.
169189
///
170190
/// This configuration builder can be used to set non-default code generation options.
171191
pub struct Config {
172192
service_generator: Option<Box<dyn ServiceGenerator>>,
193+
collections_lib: CollectionsLib,
173194
btree_map: Vec<String>,
174195
type_attributes: Vec<(String, String)>,
175196
field_attributes: Vec<(String, String)>,
@@ -460,6 +481,15 @@ impl Config {
460481
self
461482
}
462483

484+
/// Configure the code generator to use the `::alloc` namespace rather than `::std`, and Btree everywhere
485+
/// rather than `std`.
486+
///
487+
/// This allows generated code to be used in a `#![no_std]` crate
488+
pub fn use_alloc_collections_lib(&mut self) -> &mut Self {
489+
self.collections_lib = CollectionsLib::Alloc;
490+
self
491+
}
492+
463493
/// Configures the output directory where generated Rust files will be written.
464494
///
465495
/// If unset, defaults to the `OUT_DIR` environment variable. `OUT_DIR` is set by Cargo when
@@ -538,7 +568,7 @@ impl Config {
538568

539569
let mut buf = Vec::new();
540570
fs::File::open(descriptor_set)?.read_to_end(&mut buf)?;
541-
let descriptor_set = FileDescriptorSet::decode(&buf)?;
571+
let descriptor_set = FileDescriptorSet::decode(&buf[..])?;
542572

543573
let modules = self.generate(descriptor_set.file)?;
544574
for (module, content) in modules {
@@ -581,6 +611,7 @@ impl default::Default for Config {
581611
fn default() -> Config {
582612
Config {
583613
service_generator: None,
614+
collections_lib: CollectionsLib::Std,
584615
btree_map: Vec::new(),
585616
type_attributes: Vec::new(),
586617
field_attributes: Vec::new(),

prost-derive/src/field/group.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ impl Field {
120120

121121
pub fn clear(&self, ident: TokenStream) -> TokenStream {
122122
match self.label {
123-
Label::Optional => quote!(#ident = ::std::option::Option::None),
123+
Label::Optional => quote!(#ident = ::core::option::Option::None),
124124
Label::Required => quote!(#ident.clear()),
125125
Label::Repeated => quote!(#ident.clear()),
126126
}

prost-derive/src/field/map.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -223,10 +223,10 @@ impl Field {
223223
};
224224

225225
Some(quote! {
226-
pub fn #get(&self, key: #key_ref_ty) -> ::std::option::Option<#ty> {
226+
pub fn #get(&self, key: #key_ref_ty) -> ::core::option::Option<#ty> {
227227
self.#ident.get(#take_ref key).cloned().and_then(#ty::from_i32)
228228
}
229-
pub fn #insert(&mut self, key: #key_ty, value: #ty) -> ::std::option::Option<#ty> {
229+
pub fn #insert(&mut self, key: #key_ty, value: #ty) -> ::core::option::Option<#ty> {
230230
self.#ident.insert(key, value as i32).and_then(#ty::from_i32)
231231
}
232232
})
@@ -249,7 +249,7 @@ impl Field {
249249
let key = self.key_ty.rust_type();
250250
let value_wrapper = self.value_ty.debug();
251251
let fmt = quote! {
252-
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
252+
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
253253
#key_wrapper
254254
#value_wrapper
255255
let mut builder = f.debug_map();
@@ -263,17 +263,17 @@ impl Field {
263263
ValueTy::Scalar(ref ty) => {
264264
let value = ty.rust_type();
265265
quote! {
266-
struct #wrapper_name<'a>(&'a ::std::collections::#type_name<#key, #value>);
267-
impl<'a> ::std::fmt::Debug for #wrapper_name<'a> {
266+
struct #wrapper_name<'a>(&'a ::alloc::collections::#type_name<#key, #value>);
267+
impl<'a> ::core::fmt::Debug for #wrapper_name<'a> {
268268
#fmt
269269
}
270270
}
271271
}
272272
ValueTy::Message => quote! {
273-
struct #wrapper_name<'a, V: 'a>(&'a ::std::collections::#type_name<#key, V>);
274-
impl<'a, V> ::std::fmt::Debug for #wrapper_name<'a, V>
273+
struct #wrapper_name<'a, V: 'a>(&'a ::alloc::collections::#type_name<#key, V>);
274+
impl<'a, V> ::core::fmt::Debug for #wrapper_name<'a, V>
275275
where
276-
V: ::std::fmt::Debug + 'a,
276+
V: ::core::fmt::Debug + 'a,
277277
{
278278
#fmt
279279
}

prost-derive/src/field/message.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ impl Field {
123123

124124
pub fn clear(&self, ident: TokenStream) -> TokenStream {
125125
match self.label {
126-
Label::Optional => quote!(#ident = ::std::option::Option::None),
126+
Label::Optional => quote!(#ident = ::core::option::Option::None),
127127
Label::Required => quote!(#ident.clear()),
128128
Label::Repeated => quote!(#ident.clear()),
129129
}

prost-derive/src/lib.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ fn try_message(input: TokenStream) -> Result<TokenStream, Error> {
191191
wire_type: _prost::encoding::WireType,
192192
buf: &mut B,
193193
ctx: _prost::encoding::DecodeContext,
194-
) -> ::std::result::Result<(), _prost::DecodeError>
194+
) -> ::core::result::Result<(), _prost::DecodeError>
195195
where B: _bytes::Buf {
196196
#struct_name
197197
match tag {
@@ -218,8 +218,8 @@ fn try_message(input: TokenStream) -> Result<TokenStream, Error> {
218218
}
219219
}
220220

221-
impl ::std::fmt::Debug for #ident {
222-
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
221+
impl ::core::fmt::Debug for #ident {
222+
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
223223
let mut builder = #debug_builder;
224224
#(#debugs;)*
225225
builder.finish()
@@ -286,7 +286,7 @@ fn try_enumeration(input: TokenStream) -> Result<TokenStream, Error> {
286286
.iter()
287287
.map(|&(_, ref value)| quote!(#value => true));
288288
let from = variants.iter().map(
289-
|&(ref variant, ref value)| quote!(#value => ::std::option::Option::Some(#ident::#variant)),
289+
|&(ref variant, ref value)| quote!(#value => ::core::option::Option::Some(#ident::#variant)),
290290
);
291291

292292
let is_valid_doc = format!("Returns `true` if `value` is a variant of `{}`.", ident);
@@ -309,21 +309,21 @@ fn try_enumeration(input: TokenStream) -> Result<TokenStream, Error> {
309309
}
310310

311311
#[doc=#from_i32_doc]
312-
pub fn from_i32(value: i32) -> ::std::option::Option<#ident> {
312+
pub fn from_i32(value: i32) -> ::core::option::Option<#ident> {
313313
match value {
314314
#(#from,)*
315-
_ => ::std::option::Option::None,
315+
_ => ::core::option::Option::None,
316316
}
317317
}
318318
}
319319

320-
impl ::std::default::Default for #ident {
320+
impl ::core::default::Default for #ident {
321321
fn default() -> #ident {
322322
#ident::#default
323323
}
324324
}
325325

326-
impl ::std::convert::From<#ident> for i32 {
326+
impl ::core::convert::From<#ident> for i32 {
327327
fn from(value: #ident) -> i32 {
328328
value as i32
329329
}
@@ -411,8 +411,8 @@ fn try_oneof(input: TokenStream) -> Result<TokenStream, Error> {
411411
let merge = field.merge(quote!(value));
412412
quote! {
413413
#tag => {
414-
let mut value = ::std::default::Default::default();
415-
#merge.map(|_| *field = ::std::option::Option::Some(#ident::#variant_ident(value)))
414+
let mut value = ::core::default::Default::default();
415+
#merge.map(|_| *field = ::core::option::Option::Some(#ident::#variant_ident(value)))
416416
}
417417
}
418418
});
@@ -445,12 +445,12 @@ fn try_oneof(input: TokenStream) -> Result<TokenStream, Error> {
445445
}
446446
}
447447

448-
pub fn merge<B>(field: &mut ::std::option::Option<#ident>,
448+
pub fn merge<B>(field: &mut ::core::option::Option<#ident>,
449449
tag: u32,
450450
wire_type: _prost::encoding::WireType,
451451
buf: &mut B,
452452
ctx: _prost::encoding::DecodeContext,
453-
) -> ::std::result::Result<(), _prost::DecodeError>
453+
) -> ::core::result::Result<(), _prost::DecodeError>
454454
where B: _bytes::Buf {
455455
match tag {
456456
#(#merge,)*
@@ -466,8 +466,8 @@ fn try_oneof(input: TokenStream) -> Result<TokenStream, Error> {
466466
}
467467
}
468468

469-
impl ::std::fmt::Debug for #ident {
470-
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
469+
impl ::core::fmt::Debug for #ident {
470+
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
471471
match *self {
472472
#(#debug,)*
473473
}

0 commit comments

Comments
 (0)