1 //! The module that implements the `wasmtime settings` command. 2 3 use clap::Parser; 4 use serde::{Serialize, ser::SerializeMap}; 5 use std::collections::BTreeMap; 6 use std::str::FromStr; 7 use wasmtime::{Result, format_err}; 8 use wasmtime_environ::{CompilerBuilder, FlagValue, Setting, SettingKind, Tunables}; 9 10 /// Displays available Cranelift settings for a target. 11 #[derive(Parser, PartialEq)] 12 pub struct SettingsCommand { 13 /// The target triple to get the settings for; defaults to the host triple. 14 #[arg(long, value_name = "TARGET")] 15 target: Option<String>, 16 17 /// Switch output format to JSON 18 #[arg(long)] 19 json: bool, 20 } 21 22 struct SettingData(Setting); 23 24 impl Serialize for SettingData { serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer,25 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 26 where 27 S: serde::Serializer, 28 { 29 let mut map = serializer.serialize_map(None)?; 30 map.serialize_entry("name", self.0.name)?; 31 map.serialize_entry("description", self.0.description)?; 32 map.serialize_entry("values", &self.0.values)?; 33 map.end() 34 } 35 } 36 37 // Gather together all of the setting data to displays 38 #[derive(serde_derive::Serialize)] 39 struct Settings { 40 triple: String, 41 42 enums: Vec<SettingData>, 43 nums: Vec<SettingData>, 44 bools: Vec<SettingData>, 45 presets: Vec<SettingData>, 46 47 inferred: Option<Vec<&'static str>>, 48 } 49 50 impl Settings { from_builder(builder: &Box<dyn CompilerBuilder>) -> Settings51 fn from_builder(builder: &Box<dyn CompilerBuilder>) -> Settings { 52 let mut settings = Settings { 53 triple: builder.triple().to_string(), 54 enums: Vec::new(), 55 nums: Vec::new(), 56 bools: Vec::new(), 57 presets: Vec::new(), 58 inferred: None, 59 }; 60 settings.add_settings(builder.settings()); 61 settings 62 } 63 infer(&mut self, builder: &Box<dyn CompilerBuilder>) -> Result<()>64 fn infer(&mut self, builder: &Box<dyn CompilerBuilder>) -> Result<()> { 65 let compiler = builder.build()?; 66 let values = compiler.isa_flags().into_iter().collect::<BTreeMap<_, _>>(); 67 let mut result = Vec::new(); 68 for (name, value) in values { 69 if let FlagValue::Bool(true) = value { 70 result.push(name); 71 } 72 } 73 74 self.inferred = Some(result); 75 76 Ok(()) 77 } 78 add_setting(&mut self, setting: Setting)79 fn add_setting(&mut self, setting: Setting) { 80 let collection = match setting.kind { 81 SettingKind::Enum => &mut self.enums, 82 SettingKind::Num => &mut self.nums, 83 SettingKind::Bool => &mut self.bools, 84 SettingKind::Preset => &mut self.presets, 85 }; 86 collection.push(SettingData(setting)); 87 } 88 add_settings<I>(&mut self, iterable: I) where I: IntoIterator<Item = Setting>,89 fn add_settings<I>(&mut self, iterable: I) 90 where 91 I: IntoIterator<Item = Setting>, 92 { 93 for item in iterable.into_iter() { 94 self.add_setting(item); 95 } 96 } 97 is_empty(&self) -> bool98 fn is_empty(&self) -> bool { 99 self.enums.is_empty() 100 && self.nums.is_empty() 101 && self.bools.is_empty() 102 && self.presets.is_empty() 103 } 104 } 105 106 impl SettingsCommand { 107 /// Executes the command. execute(self) -> Result<()>108 pub fn execute(self) -> Result<()> { 109 // Gather settings from the cranelift compiler builder 110 let mut builder = wasmtime_cranelift::builder(None)?; 111 let tunables = if let Some(target) = &self.target { 112 let target = target_lexicon::Triple::from_str(target).map_err(|e| format_err!(e))?; 113 let tunables = Tunables::default_for_target(&target)?; 114 builder.target(target)?; 115 tunables 116 } else { 117 Tunables::default_host() 118 }; 119 120 builder.set_tunables(tunables)?; 121 let mut settings = Settings::from_builder(&builder); 122 123 // Add inferred settings if no target specified 124 if self.target.is_none() { 125 settings.infer(&builder)?; 126 } 127 128 // Print settings 129 if self.json { 130 self.print_json(settings) 131 } else { 132 self.print_human_readable(settings) 133 } 134 } 135 print_json(self, settings: Settings) -> Result<()>136 fn print_json(self, settings: Settings) -> Result<()> { 137 println!("{}", serde_json::to_string_pretty(&settings)?); 138 Ok(()) 139 } 140 print_human_readable(self, settings: Settings) -> Result<()>141 fn print_human_readable(self, settings: Settings) -> Result<()> { 142 if settings.is_empty() { 143 println!("Target '{}' has no settings.", settings.triple); 144 return Ok(()); 145 } 146 147 println!("Cranelift settings for target '{}':", settings.triple); 148 149 Self::print_settings_human_readable("Boolean settings:", &settings.bools); 150 Self::print_settings_human_readable("Enum settings:", &settings.enums); 151 Self::print_settings_human_readable("Numerical settings:", &settings.nums); 152 Self::print_settings_human_readable("Presets:", &settings.presets); 153 154 if let Some(inferred) = settings.inferred { 155 println!(); 156 println!("Settings inferred for the current host:"); 157 158 for name in inferred { 159 println!(" {name}"); 160 } 161 } 162 163 Ok(()) 164 } 165 print_settings_human_readable(header: &str, settings: &[SettingData])166 fn print_settings_human_readable(header: &str, settings: &[SettingData]) { 167 if settings.is_empty() { 168 return; 169 } 170 171 println!(); 172 println!("{header}"); 173 174 let width = settings.iter().map(|s| s.0.name.len()).max().unwrap_or(0); 175 176 for setting in settings { 177 println!( 178 " {:width$} {}{}", 179 setting.0.name, 180 setting.0.description, 181 setting 182 .0 183 .values 184 .map(|v| format!(" Supported values: {}.", v.join(", "))) 185 .unwrap_or("".to_string()), 186 width = width + 2 187 ); 188 } 189 } 190 } 191