pretend_build_script.rs
1 // Note: This will print to stderr what look like errors but these are only from autocfg doing the 2 // intended probing (as it runs its own rustc commands that expectedly might have compiler 3 // errors), and this will also print the build-script instructions to stdout, and these prints 4 // will be intermixed (and their order is randomized, due to the current internal iteration of a 5 // HashMap). It can be helpful to redirect these, e.g.: 6 // cargo test --test pretend_build_script 2> /dev/null 7 8 #![allow(unknown_lints, deprecated, bare_trait_objects)] 9 10 extern crate cfg_rust_features; 11 extern crate create_temp_subdir; 12 13 use std::collections::{BTreeSet, HashSet}; 14 use std::env; 15 use std::error::Error; 16 use std::hash::Hash; 17 use std::iter::FromIterator; 18 19 use cfg_rust_features::{emit_rerun_if_changed_file, CfgRustFeatures, FeatureCategory}; 20 use create_temp_subdir::TempSubDir; 21 22 type ResultDynErr<T> = Result<T, Box<Error>>; 23 24 type FeatureName = &'static str; 25 type EnabledFeatures = cfg_rust_features::EnabledFeatures<FeatureName>; 26 27 28 /// Like a `main` function of a build script (modulo the `Ok` type). 29 fn pretend_build_script() -> ResultDynErr<EnabledFeatures> 30 { 31 emit_rerun_if_changed_file(file!()); 32 33 Ok(try!(try!(CfgRustFeatures::new()).emit_multiple(vec![ 34 "arbitrary_self_types", 35 // "cfg_version", // Omitted to exercise not giving a supported one. 36 "inner_deref", 37 "destructuring_assignment", 38 "error_in_core", 39 "iter_zip", 40 "never_type", 41 "question_mark", 42 "rust1", 43 "step_trait", 44 "unstable_features", 45 "unwrap_infallible", 46 ]))) 47 } 48 49 50 fn main() 51 { 52 // Setup to pretend that this program is a build script. 53 let out_dir = TempSubDir::new("intgtest-pretend_build_script").unwrap(); 54 env::set_var("OUT_DIR", &out_dir); 55 56 assert_enabled_features(&pretend_build_script().unwrap()); 57 } 58 59 60 /// Check the `EnabledFeatures` `HashMap` value, returned by the call to 61 /// `CfgRustFeatures::emit_multiple`, which indicates whether each of the chosen features was 62 /// found to be enabled and its categories if so. 63 /// 64 /// Must correspond to what [`pretend_build_script`] emits. 65 fn assert_enabled_features(enabled: &EnabledFeatures) 66 { 67 /// Element of `HashSet`s. Similar shape as a Set iterator yields. `BTreeSet` needed because 68 /// it `impl`s `Hash`. 69 type Feature = (FeatureName, BTreeSet<FeatureCategory>); 70 71 macro_rules! set { 72 [$t:ty: $($e:expr),*] => { 73 <$t>::from_iter(vec![$($e),*]) 74 } 75 } 76 macro_rules! hset { 77 [$($rest:tt)*] => { 78 set![HashSet<_>: $($rest)*] 79 } 80 } 81 macro_rules! bset { 82 [$($rest:tt)*] => { 83 set![BTreeSet<_>: $($rest)*] 84 } 85 } 86 87 fn bset_from_hset<T: Clone + Hash + Ord>(hset: &HashSet<T>) -> BTreeSet<T> 88 { 89 hset.iter().cloned().collect() 90 } 91 92 fn from_enabled_features(enabled_features: &EnabledFeatures) -> HashSet<Feature> 93 { 94 enabled_features 95 .iter() 96 .filter_map(|(&k, v)| v.as_ref().map(|c| (k, bset_from_hset(c)))) 97 .collect() 98 } 99 100 fn assert_enabled_fits_required_and_allowed<T: Hash + Eq>( 101 enabled: &HashSet<T>, 102 required: &HashSet<T>, 103 allowed: &HashSet<T>, 104 ) 105 { 106 assert!(enabled.is_superset(required)); 107 assert!(enabled.is_subset(allowed)); 108 } 109 110 111 let required = hset![("rust1", bset!["comp", "lang", "lib"])]; 112 let optional = hset![ 113 ("unstable_features", bset!["comp"]), 114 ("arbitrary_self_types", bset!["lang"]), 115 ("destructuring_assignment", bset!["lang"]), 116 ("never_type", bset!["lang"]), 117 ("question_mark", bset!["lang"]), 118 ("error_in_core", bset!["lib"]), 119 ("inner_deref", bset!["lib"]), 120 ("iter_zip", bset!["lib"]), 121 ("step_trait", bset!["lib"]), 122 ("unwrap_infallible", bset!["lib"]) 123 ]; 124 let allowed = &required | &optional; 125 126 let enabled = from_enabled_features(enabled); 127 assert_enabled_fits_required_and_allowed(&enabled, &required, &allowed); 128 }