diff --git a/.github/workflows/continuous-integration-workflow.yaml b/.github/workflows/continuous-integration-workflow.yaml index 5efebde60..1db931bc9 100644 --- a/.github/workflows/continuous-integration-workflow.yaml +++ b/.github/workflows/continuous-integration-workflow.yaml @@ -138,3 +138,11 @@ jobs: - name: cargo check run: cd test-vendored && cargo check + deny: + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v2 + with: + submodules: recursive + - uses: EmbarkStudios/cargo-deny-action@v1 diff --git a/.gitmodules b/.gitmodules index 6251abeb8..ebee18b86 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "prost-build/third-party/protobuf"] path = prost-build/third-party/protobuf - url = git@github.com:protocolbuffers/protobuf + url = https://github.com/protocolbuffers/protobuf diff --git a/Cargo.toml b/Cargo.toml index e8d41fc2d..1ede18270 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "prost" -version = "0.10.1" +version = "0.10.3" authors = [ "Dan Burkert ", "Tokio Contributors ", diff --git a/deny.toml b/deny.toml new file mode 100644 index 000000000..978f2c9de --- /dev/null +++ b/deny.toml @@ -0,0 +1,17 @@ +[advisories] +ignore = [ + # serde_cbor is unmaintained, but brought in by criterion + "RUSTSEC-2021-0127", +] + +[licenses] +allow = ["Apache-2.0", "MIT", "BSD-3-Clause"] + +[bans] +deny = [{ name = "cmake" }] +skip = [ + # old csv via old criterion + { name = "itoa", version = "=0.4.8" }, + # Proptest includes 2 version :( + { name = "quick-error", version = "=1.2.3" }, +] diff --git a/prost-build/Cargo.toml b/prost-build/Cargo.toml index 751cac6f7..ed52bd66e 100644 --- a/prost-build/Cargo.toml +++ b/prost-build/Cargo.toml @@ -30,9 +30,9 @@ lazy_static = "1.4.0" regex = { version = "1.5.5", default-features = false, features = ["std", "unicode-bool"] } [build-dependencies] -which = { version = "4", default-features = false } +cc = { version = "1.0", features = ["parallel"] } cfg-if = "1" -cmake = "0.1" +which = { version = "4", default-features = false } [dev-dependencies] env_logger = { version = "0.8", default-features = false } diff --git a/prost-build/build.rs b/prost-build/build.rs index 89abf0176..c93020b6a 100644 --- a/prost-build/build.rs +++ b/prost-build/build.rs @@ -16,7 +16,6 @@ //! 2. The bundled Protobuf include directory. //! -use cfg_if::cfg_if; use std::env; use std::path::PathBuf; use which::which; @@ -64,24 +63,108 @@ fn path_protoc() -> Option { /// Returns true if the vendored flag is enabled. fn vendored() -> bool { - cfg_if! { - if #[cfg(feature = "vendored")] { - true - } else { - false - } - } + env::var("CARGO_FEATURE_VENDORED").is_ok() } -/// Compile `protoc` via `cmake`. -fn compile() -> Option { - let protobuf_src = bundle_path().join("protobuf").join("cmake"); +/// Compile `protoc`. If stub is true, this won't do anything other than emit +/// the single function we need to link, this is needed if the `vendored` +/// feature is being used, but the user has also set the `PROTOC_NO_VENDOR` flag +/// so protoc will be used at runtime +fn compile(stub: bool) -> Option { + let protobuf_src = bundle_path().join("protobuf/src/google/protobuf"); println!("cargo:rerun-if-changed={}", protobuf_src.display()); - let dst = cmake::Config::new(protobuf_src).build(); + // compile our protoc wrapper lib + { + let mut build = cc::Build::new(); + build + .cpp(true) + // We _always_ want to build optmized, protoc code is far too slow otherwise + .opt_level_str("2"); + + // Disable all the compiler warnings for the protoc code we have no + // intention of changing + if !build.get_compiler().is_like_msvc() { + build + .flag("-std=c++11") + .flag("-Wno-unused-parameter") + .flag("-Wno-redundant-move") + .flag("-Wno-sign-compare") + .flag("-Wno-stringop-overflow"); + } - Some(dst.join("bin").join("protoc")) + build.includes(&[bundle_path().join("protobuf/src")]); + + if !stub { + build.files( + [ + "any.cc", + "any_lite.cc", + "arena.cc", + "arenastring.cc", + "descriptor.cc", + "descriptor.pb.cc", + "descriptor_database.cc", + "dynamic_message.cc", + "extension_set.cc", + "extension_set_heavy.cc", + "implicit_weak_message.cc", + "map.cc", + "map_field.cc", + "message.cc", + "message_lite.cc", + "generated_message_reflection.cc", + "generated_message_util.cc", + "parse_context.cc", + "reflection_ops.cc", + "repeated_field.cc", + "repeated_ptr_field.cc", + "text_format.cc", + "unknown_field_set.cc", + "wire_format.cc", + "wire_format_lite.cc", + "compiler/importer.cc", + "compiler/parser.cc", + "io/coded_stream.cc", + "io/strtod.cc", + "io/tokenizer.cc", + "io/zero_copy_stream.cc", + "io/zero_copy_stream_impl.cc", + "io/zero_copy_stream_impl_lite.cc", + "stubs/common.cc", + "stubs/stringpiece.cc", + "stubs/stringprintf.cc", + "stubs/structurally_valid.cc", + "stubs/strutil.cc", + "stubs/substitute.cc", + ] + .iter() + .map(|fname| protobuf_src.join(fname)), + ); + + if env::var("CARGO_CFG_TARGET_FAMILY").as_deref() == Ok("windows") { + build.files( + ["io/io_win32.cc", "stubs/int128.cc", "stubs/status.cc"] + .iter() + .map(|fname| protobuf_src.join(fname)), + ); + } + } + + if stub { + build.define("STUB_ONLY", "1"); + } + + // This is our little wrapper that only does the 1 thing prost-build + // actually needs from the the bloated protoc binary + build.file("src/libprotoc.cpp"); + build.compile("protoc"); + + println!("cargo:rerun-if-changed=src/libprotoc.cpp"); + } + + Some(PathBuf::from("linked")) } /// Try to find a `protoc` through a few methods. @@ -89,11 +172,17 @@ fn compile() -> Option { /// Check module docs for more info. fn protoc() -> Option { if env::var_os("PROTOC_NO_VENDOR").is_some() { + compile(true); path_protoc() } else if vendored() { - compile() + compile(false) } else { - path_protoc().or_else(compile) + if let Some(path) = path_protoc() { + compile(true); + Some(path) + } else { + compile(false) + } } } @@ -115,4 +204,5 @@ fn main() { ); println!("cargo:rerun-if-env-changed=PROTOC"); println!("cargo:rerun-if-env-changed=PROTOC_INCLUDE"); + println!("cargo:rerun-if-env-changed=PROTOC_NO_VENDOR"); } diff --git a/prost-build/src/lib.rs b/prost-build/src/lib.rs index e8defe4fb..dcd97d3b7 100644 --- a/prost-build/src/lib.rs +++ b/prost-build/src/lib.rs @@ -797,59 +797,8 @@ impl Config { // this figured out. // [1]: http://doc.crates.io/build-script.html#outputs-of-the-build-script - let tmp; - let file_descriptor_set_path = if let Some(path) = &self.file_descriptor_set_path { - path.clone() - } else { - if self.skip_protoc_run { - return Err(Error::new( - ErrorKind::Other, - "file_descriptor_set_path is required with skip_protoc_run", - )); - } - tmp = tempfile::Builder::new().prefix("prost-build").tempdir()?; - tmp.path().join("prost-descriptor-set") - }; - - if !self.skip_protoc_run { - let mut cmd = Command::new(protoc()); - cmd.arg("--include_imports") - .arg("--include_source_info") - .arg("-o") - .arg(&file_descriptor_set_path); - - for include in includes { - cmd.arg("-I").arg(include.as_ref()); - } - - // Set the protoc include after the user includes in case the user wants to - // override one of the built-in .protos. - cmd.arg("-I").arg(protoc_include()); - - for arg in &self.protoc_args { - cmd.arg(arg); - } - - for proto in protos { - cmd.arg(proto.as_ref()); - } - - let output = cmd.output().map_err(|error| { - Error::new( - error.kind(), - format!("failed to invoke protoc (hint: https://docs.rs/prost-build/#sourcing-protoc): {}", error), - ) - })?; - - if !output.status.success() { - return Err(Error::new( - ErrorKind::Other, - format!("protoc failed: {}", String::from_utf8_lossy(&output.stderr)), - )); - } - } + let buf = self.load_file_descriptor_set(protos, includes)?; - let buf = fs::read(file_descriptor_set_path)?; let file_descriptor_set = FileDescriptorSet::decode(&*buf).map_err(|error| { Error::new( ErrorKind::InvalidInput, @@ -913,6 +862,222 @@ impl Config { Ok(()) } + fn load_file_descriptor_set( + &self, + protos: &[impl AsRef], + includes: &[impl AsRef], + ) -> Result> { + if self.skip_protoc_run { + if let Some(path) = &self.file_descriptor_set_path { + return Ok(fs::read(path)?); + } else { + return Err(Error::new( + ErrorKind::Other, + "file_descriptor_set_path is required with skip_protoc_run", + )); + } + } + + if protoc() == Path::new("linked") { + let file_descriptor_set = Self::serialize_file_descriptor_set(protos, includes)?; + + // If the user has set the file descriptor path manually then also save it + // to disk in case they are counting on that behavior + if let Some(path) = &self.file_descriptor_set_path { + std::fs::write(path, &file_descriptor_set)?; + } + + Ok(file_descriptor_set) + } else { + let tmp; + let file_descriptor_set_path = if let Some(path) = &self.file_descriptor_set_path { + path.clone() + } else { + if self.skip_protoc_run { + return Err(Error::new( + ErrorKind::Other, + "file_descriptor_set_path is required with skip_protoc_run", + )); + } + tmp = tempfile::Builder::new().prefix("prost-build").tempdir()?; + tmp.path().join("prost-descriptor-set") + }; + + if !self.skip_protoc_run { + let mut cmd = Command::new(protoc()); + cmd.arg("--include_imports") + .arg("--include_source_info") + .arg("-o") + .arg(&file_descriptor_set_path); + + for include in includes { + cmd.arg("-I").arg(include.as_ref()); + } + + // Set the protoc include after the user includes in case the user wants to + // override one of the built-in .protos. + cmd.arg("-I").arg(protoc_include()); + + for arg in &self.protoc_args { + cmd.arg(arg); + } + + for proto in protos { + cmd.arg(proto.as_ref()); + } + + let output = cmd.output().map_err(|error| { + Error::new( + error.kind(), + format!("failed to invoke protoc (hint: https://docs.rs/prost-build/#sourcing-protoc): {}", error), + ) + })?; + + if !output.status.success() { + return Err(Error::new( + ErrorKind::Other, + format!("protoc failed: {}", String::from_utf8_lossy(&output.stderr)), + )); + } + } + + fs::read(file_descriptor_set_path) + } + } + + fn serialize_file_descriptor_set( + protos: &[impl AsRef], + includes: &[impl AsRef], + ) -> Result> { + use std::ffi::c_void; + + #[no_mangle] + unsafe extern "C" fn resize_callback(ctx: *mut c_void, size: usize) -> *mut c_void { + let vec: &mut Vec = &mut (*ctx.cast()); + vec.resize(size, 0); + vec.as_mut_ptr().cast() + } + + #[no_mangle] + unsafe extern "C" fn cap_callback(ctx: *mut c_void) -> usize { + let vec: &Vec = &(*ctx.cast()); + vec.capacity() + } + + #[repr(C)] + struct CPath { + path: *const i8, + len: usize, + } + + #[repr(C)] + struct Buffer { + resize_buffer: unsafe extern "C" fn(ctx: *mut c_void, size: usize) -> *mut c_void, + buffer_capacity: unsafe extern "C" fn(ctx: *mut c_void) -> usize, + context: *mut c_void, + } + + extern "C" { + fn write_descriptor_set( + input_files: *const CPath, + num_inputs: usize, + includes_paths: *const CPath, + num_includes: usize, + output: *mut Buffer, + ) -> i32; + } + + struct PathBufs<'s> { + #[allow(dead_code)] + storage: Vec>, + paths: Vec, + } + + #[inline] + fn path_to_vec<'s>(path: &'s impl AsRef) -> std::borrow::Cow<'s, [u8]> { + #[cfg(unix)] + { + use std::os::unix::ffi::OsStrExt; + path.as_ref().as_os_str().as_bytes().into() + } + + #[cfg(windows)] + { + // https://internals.rust-lang.org/t/pathbuf-to-cstring/12560/19 + // protobuf is internally (incorrectly) using std::string to store + // paths, and I don't know if it's expected to work with actual + // non-ascii/utf-8 paths, so for now just cheat until someone + // complains about it not working + match path.as_ref().to_string_lossy() { + std::borrow::Cow::Owned(s) => { + log::warn!( + "non utf-8 path '{}' detected, this may fail in protoc", + path.as_ref().display() + ); + s.into_bytes().into() + } + std::borrow::Cow::Borrowed(s) => s.as_bytes().into(), + } + } + } + + #[inline] + fn path_bufs<'s>(paths: &'s [impl AsRef]) -> PathBufs<'s> { + let storage: Vec<_> = paths.iter().map(path_to_vec).collect(); + + let paths = storage + .iter() + .map(|sp| CPath { + path: sp.as_ref().as_ptr().cast(), + len: sp.len(), + }) + .collect(); + + PathBufs { storage, paths } + } + + let protos = path_bufs(protos); + let mut includes = path_bufs(includes); + + // Explicitly add the default protoc includes, these are normally added + // by the CLI, which we don't use + let protoc_include = protoc_include(); + { + let pi = path_to_vec(&protoc_include); + includes.paths.push(CPath { + path: pi.as_ref().as_ptr().cast(), + len: pi.len(), + }); + includes.storage.push(pi); + } + + let mut output_vec = Vec::new(); + + let mut buffer = Buffer { + resize_buffer: resize_callback, + buffer_capacity: cap_callback, + context: (&mut output_vec as *mut Vec).cast(), + }; + + if unsafe { + write_descriptor_set( + protos.paths.as_ptr(), + protos.paths.len(), + includes.paths.as_ptr(), + includes.paths.len(), + &mut buffer, + ) + } == 0 + { + Ok(output_vec) + } else { + Err(Error::new( + ErrorKind::Other, + "failed to serialize file descriptor set, see stderr", + )) + } + } + fn write_includes( &self, mut entries: Vec<&Module>, diff --git a/prost-build/src/libprotoc.cpp b/prost-build/src/libprotoc.cpp new file mode 100644 index 000000000..1ac9f467b --- /dev/null +++ b/prost-build/src/libprotoc.cpp @@ -0,0 +1,366 @@ +#include + +#ifndef STUB_ONLY +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#endif // STUB_ONLY + +extern "C" { + // Small and simple and FFI safe, unlike `StringPiece` + struct Path { + const char* path; + size_t len; + }; + + typedef void* (*resize)(void*, size_t); + typedef size_t (*capacity)(void*); + + // Simple buffer wrapper so that we can write directly to memory allocated + // in Rust + struct Buffer { + resize resize_buffer; + capacity buffer_capacity; + void* context; + }; + + #ifdef STUB_ONLY + // Expose a function that always fails in case the user has configured + // the `vendored` feature, but has also set the `PROTOC_NO_VENDOR` + // env var, meaning we still need to link the lib, but it won't actually be used + int write_descriptor_set( + const Path* input_files, + size_t num_inputs, + const Path* includes_paths, + size_t num_includes, + Buffer* output + ) { + return 1; + } + #endif // STUB_ONLY +} + +#ifndef STUB_ONLY +using google::protobuf::FileDescriptor; +using google::protobuf::FileDescriptorSet; +using google::protobuf::DescriptorPool; +using google::protobuf::MergedDescriptorDatabase; +using google::protobuf::Message; +using google::protobuf::FileDescriptorProto; +using google::protobuf::RepeatedPtrField; + +using google::protobuf::compiler::DiskSourceTree; +using google::protobuf::compiler::SourceTreeDescriptorDatabase; + +// Port of CommandLineInterface::ParseInputFiles +bool parse_input_files( + const std::vector& input_files, + DescriptorPool* descriptor_pool, + std::vector* parsed_files +) { + for (const auto& input : input_files) { + // Import the file. + const FileDescriptor* parsed_file = descriptor_pool->FindFileByName(input); + if (parsed_file == nullptr) { + return false; + } + parsed_files->push_back(parsed_file); + } + + return true; +} + +bool make_inputs_relative(std::vector* inputs, DiskSourceTree* source_tree) { + for (auto& input_file : *inputs) { + std::string virtual_file, shadowing_disk_file; + + auto mapping = source_tree->DiskFileToVirtualFile( + input_file, + &virtual_file, + &shadowing_disk_file); + + switch (mapping) { + case DiskSourceTree::SUCCESS: { + input_file = virtual_file; + break; + } + case DiskSourceTree::SHADOWED: { + fprintf(stderr, "%s: Input is shadowed by an include in \"%s\"." + "Either use the latter file as your input or reorder the" + "includes so that the former file's location comes first.\n", + input_file.c_str(), shadowing_disk_file.c_str() + ); + return false; + } + case DiskSourceTree::CANNOT_OPEN: { + auto error_str = source_tree->GetLastErrorMessage().empty() + ? strerror(errno) + : source_tree->GetLastErrorMessage(); + fprintf(stderr, "Could not map to virtual file: %s: %s\n", input_file.c_str(), error_str.c_str()); + return false; + } + case DiskSourceTree::NO_MAPPING: { + // Try to interpret the path as a virtual path. + std::string disk_file; + if (source_tree->VirtualFileToDiskFile(input_file, &disk_file)) { + return true; + } else { + // The input file path can't be mapped to any --proto_path and it also + // can't be interpreted as a virtual path. + fprintf(stderr, "%s: File does not reside within any include path.\n", input_file.c_str()); + return false; + } + } + } + } + + return true; +} + +void get_transitive_deps( + const FileDescriptor* file, + bool include_json_name, + bool include_source_code_info, + std::set* already_seen, + RepeatedPtrField* output +) { + if (!already_seen->insert(file).second) { + return; + } + + for (int i = 0; i < file->dependency_count(); i++) { + get_transitive_deps( + file->dependency(i), + include_json_name, + include_source_code_info, + already_seen, + output + ); + } + + FileDescriptorProto* new_descriptor = output->Add(); + file->CopyTo(new_descriptor); + if (include_json_name) { + file->CopyJsonNameTo(new_descriptor); + } + if (include_source_code_info) { + file->CopySourceCodeInfoTo(new_descriptor); + } +} + +class BufferOutput : public google::protobuf::io::ZeroCopyOutputStream { +public: + Buffer* impl; + void* buffer_base = nullptr; + size_t len = 0; + + BufferOutput(Buffer* impl) : impl(impl) {} + + bool Next(void** data, int* size) final { + size_t old_size = this->len; + size_t cur_cap = this->impl->buffer_capacity(this->impl->context); + + size_t new_size; + if (old_size < cur_cap) { + new_size = cur_cap; + } else { + new_size = old_size * 2; + } + + // Avoid integer overflow in returned '*size'. + new_size = std::min(new_size, old_size + std::numeric_limits::max()); + new_size = std::max(new_size, size_t(1024)); + this->buffer_base = this->impl->resize_buffer(this->impl->context, new_size); + + *data = ((uint8_t*)buffer_base + old_size); + *size = new_size - old_size; + this->len = new_size; + + return true; + } + + void BackUp(int count) final { + this->buffer_base = this->impl->resize_buffer(this->impl->context, this->len - count); + } + + int64_t ByteCount() const final { + return this->len; + } +}; + +bool write_descriptor_set(const std::vector& parsed_files, Buffer* output) { + FileDescriptorSet file_set; + + std::set already_seen; + + for (const auto& parsed : parsed_files) { + get_transitive_deps( + parsed, + true, // Include json_name + true, // Include source info, prost requires this + &already_seen, + file_set.mutable_file() + ); + } + + { + BufferOutput zero_copy_stream(output); + google::protobuf::io::CodedOutputStream coded_out(&zero_copy_stream); + + // Determinism is useful here because build outputs are sometimes checked + // into version control. + coded_out.SetSerializationDeterministic(true); + if (!file_set.SerializeToCodedStream(&coded_out)) { + return false; + } + } + + return true; +} + +// A MultiFileErrorCollector that prints errors to stderr. +class ErrorPrinter + : public google::protobuf::compiler::MultiFileErrorCollector + , public google::protobuf::io::ErrorCollector + , public DescriptorPool::ErrorCollector { +public: + ErrorPrinter() + : found_errors_(false), + found_warnings_(false) {} + ~ErrorPrinter() {} + + // implements MultiFileErrorCollector ------------------------------ + void AddError( const std::string& filename, int line, int column, + const std::string& message) override { + found_errors_ = true; + AddErrorOrWarning(filename, line, column, message, "error", std::cerr); + } + + void AddWarning(const std::string& filename, int line, int column, + const std::string& message) override { + found_warnings_ = true; + AddErrorOrWarning(filename, line, column, message, "warning", std::clog); + } + + // implements io::ErrorCollector ----------------------------------- + void AddError(int line, int column, const std::string& message) final { + AddError("input", line, column, message); + } + + void AddWarning(int line, int column, const std::string& message) final { + AddErrorOrWarning("input", line, column, message, "warning", std::clog); + } + + // implements DescriptorPool::ErrorCollector------------------------- + void AddError( const std::string& filename, const std::string& element_name, + const Message* descriptor, ErrorLocation location, + const std::string& message) override { + AddErrorOrWarning(filename, -1, -1, message, "error", std::cerr); + } + + void AddWarning(const std::string& filename, const std::string& element_name, + const Message* descriptor, ErrorLocation location, + const std::string& message) final { + AddErrorOrWarning(filename, -1, -1, message, "warning", std::clog); + } + + bool FoundErrors() const { return found_errors_; } + + bool FoundWarnings() const { return found_warnings_; } + +private: + void AddErrorOrWarning( const std::string& filename, int line, int column, + const std::string& message, const std::string& type, + std::ostream& out) { + out << filename; + + // Users typically expect 1-based line/column numbers, so we add 1 + // to each here. + if (line != -1) { + out << ":" << (line + 1) << ":" << (column + 1); + } + + if (type == "warning") { + out << ": warning: " << message << std::endl; + } else { + out << ": " << message << std::endl; + } + } + + bool found_errors_; + bool found_warnings_; +}; + +extern "C" { + int write_descriptor_set( + const Path* input_files, + size_t num_inputs, + const Path* includes_paths, + size_t num_includes, + Buffer* output + ) { + // We're forced to reallocate these because some of the following APIs + // only take std::string :p + std::vector inputs; + inputs.reserve(num_inputs); + // I'm sure there's some fancier way to initialize this these days but I + // prefer to keep the C++ as dumb as possible + for (size_t i = 0; i < num_inputs; ++i) { + inputs.push_back(std::string(input_files[i].path, input_files[i].len)); + } + + std::vector includes; + includes.reserve(num_includes); + for (size_t i = 0; i < num_includes; ++i) { + includes.push_back(std::string(includes_paths[i].path, includes_paths[i].len)); + } + + // Port of CommandLineInterface::InitializeDiskSourceTree + std::unique_ptr source_tree(new DiskSourceTree()); + std::unique_ptr descriptor_set_in_database; + + // Set up the source tree. Note that the way prost uses protoc, virtual + // paths would only be possible if the user is explicitly sending their + // own -I args with the ':/;' separated virtual path. I seriously doubt + // this happens in practice. (famous last words) + for (const auto& include : includes) { + source_tree->MapPath("", include); + } + + if (!make_inputs_relative(&inputs, source_tree.get())) { + return 1; + } + + std::unique_ptr error_collector(new ErrorPrinter()); + std::unique_ptr source_tree_database( + new SourceTreeDescriptorDatabase(source_tree.get(), descriptor_set_in_database.get())); + source_tree_database->RecordErrorsTo(error_collector.get()); + std::unique_ptr descriptor_pool(new DescriptorPool( + source_tree_database.get(), + source_tree_database->GetValidationErrorCollector())); + descriptor_pool->EnforceWeakDependencies(true); + + // Try to actually parse all of our inputs, if this + std::vector parsed; + parsed.reserve(inputs.size()); + if (!parse_input_files(inputs, descriptor_pool.get(), &parsed)) { + return 1; + } + + if (!write_descriptor_set(parsed, output)) { + return 1; + } + + return 0; + } +} +#endif // STUB_ONLY diff --git a/src/encoding.rs b/src/encoding.rs index 4953d6603..70ef62b2b 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -2,7 +2,11 @@ //! //! Meant to be used only from `Message` implementations. -#![allow(clippy::implicit_hasher, clippy::ptr_arg)] +#![allow( + clippy::implicit_hasher, + clippy::ptr_arg, + clippy::manual_range_contains +)] use alloc::collections::BTreeMap; use alloc::format; diff --git a/tests/single-include/Cargo.toml b/tests/single-include/Cargo.toml index 1d8eebebd..d95c9852c 100644 --- a/tests/single-include/Cargo.toml +++ b/tests/single-include/Cargo.toml @@ -7,7 +7,7 @@ publish = false license = "MIT" [dependencies] -prost = { path = "../../../prost" } +prost = { path = "../.." } [build-dependencies] prost-build = { path = "../../prost-build" }