diff --git a/core/build/src/lib.rs b/core/build/src/lib.rs index 929443a5..f3b62dfd 100644 --- a/core/build/src/lib.rs +++ b/core/build/src/lib.rs @@ -7,7 +7,7 @@ use bindgen::{ callbacks::{IntKind, ParseCallbacks}, Builder, EnumVariation, }; -use std::collections::HashSet; +use std::collections::BTreeSet; use std::{env, path::PathBuf}; static DEFAULT_SGX_SDK_PATH: &str = "/opt/intel/sgxsdk"; @@ -206,43 +206,47 @@ impl SgxParseCallbacks { .extend(serialize_types.into_iter().map(ToString::to_string)); self } -} - -impl ParseCallbacks for SgxParseCallbacks { - fn item_name(&self, name: &str) -> Option { - normalize_item_name(name) - } - fn add_derives(&self, info: &DeriveInfo<'_>) -> Vec { - let mut attributes = HashSet::new(); - let name = info.name; + /// Get the derives that should be applied to the provided named type. + fn derives(&self, type_name: &str) -> Vec { + let mut attributes = BTreeSet::new(); - if self.default_types.iter().any(|n| *n == name) { + if self.default_types.iter().any(|n| *n == type_name) { attributes.insert("Default"); } // The [enum_types] method adds enums to the [copyable_types] - if self.copyable_types.iter().any(|n| *n == name) { + if self.copyable_types.iter().any(|n| *n == type_name) { attributes.insert("Copy"); } - if !self.dynamically_sized_types.iter().any(|n| *n == name) { + if !self.dynamically_sized_types.iter().any(|n| *n == type_name) { // For dynamically sized types we don't even derive Debug, because // they are often times packed and packed types can't derive Debug // without deriving Copy, however by the dynamic nature one can't // derive Copy attributes.insert("Debug"); - if !self.enum_types.iter().any(|n| *n == name) { + if !self.enum_types.iter().any(|n| *n == type_name) { attributes.extend(["Eq", "PartialEq", "Hash", "Clone"]); } }; - if self.serialize_types.iter().any(|n| *n == name) { + if self.serialize_types.iter().any(|n| *n == type_name) { attributes.extend(["Serialize", "Deserialize"]); } attributes.into_iter().map(String::from).collect::>() } +} + +impl ParseCallbacks for SgxParseCallbacks { + fn item_name(&self, name: &str) -> Option { + normalize_item_name(name) + } + + fn add_derives(&self, info: &DeriveInfo<'_>) -> Vec { + self.derives(info.name) + } fn int_macro(&self, name: &str, _value: i64) -> Option { const USIZE_SUFFIXES: &[&str] = &["_SIZE", "_BYTES", "_IDX", "_COUNT"]; @@ -328,3 +332,36 @@ pub fn sgx_library_suffix() -> &'static str { _ => "", } } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn verify_consistency_of_derive_attributes() { + // This test prevents a regression where `HashSet` was used to prevent + // duplicates in the derives. Using `HashSet` resulted in the derives + // coming back in varying order based on seed of the default hasher. + // The seed of the default hasher is randomized per process run. + // This inconsistent ordering of the derives prevented reproducible + // builds as the derived implementations would move around in the + // resultant binary. + let mut callbacks = SgxParseCallbacks::default(); + callbacks = callbacks.serialize_types(["foo"]); + let derives = callbacks.derives("foo"); + + let expected = [ + "Clone", + "Debug", + "Deserialize", + "Eq", + "Hash", + "PartialEq", + "Serialize", + ] + .into_iter() + .map(String::from) + .collect::>(); + assert_eq!(derives, expected); + } +}