//! The module that implements the `wasmtime settings` command. use clap::Parser; use serde::{Serialize, ser::SerializeMap}; use std::collections::BTreeMap; use std::str::FromStr; use wasmtime::{Result, format_err}; use wasmtime_environ::{CompilerBuilder, FlagValue, Setting, SettingKind, Tunables}; /// Displays available Cranelift settings for a target. #[derive(Parser, PartialEq)] pub struct SettingsCommand { /// The target triple to get the settings for; defaults to the host triple. #[arg(long, value_name = "TARGET")] target: Option, /// Switch output format to JSON #[arg(long)] json: bool, } struct SettingData(Setting); impl Serialize for SettingData { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { let mut map = serializer.serialize_map(None)?; map.serialize_entry("name", self.0.name)?; map.serialize_entry("description", self.0.description)?; map.serialize_entry("values", &self.0.values)?; map.end() } } // Gather together all of the setting data to displays #[derive(serde_derive::Serialize)] struct Settings { triple: String, enums: Vec, nums: Vec, bools: Vec, presets: Vec, inferred: Option>, } impl Settings { fn from_builder(builder: &Box) -> Settings { let mut settings = Settings { triple: builder.triple().to_string(), enums: Vec::new(), nums: Vec::new(), bools: Vec::new(), presets: Vec::new(), inferred: None, }; settings.add_settings(builder.settings()); settings } fn infer(&mut self, builder: &Box) -> Result<()> { let compiler = builder.build()?; let values = compiler.isa_flags().into_iter().collect::>(); let mut result = Vec::new(); for (name, value) in values { if let FlagValue::Bool(true) = value { result.push(name); } } self.inferred = Some(result); Ok(()) } fn add_setting(&mut self, setting: Setting) { let collection = match setting.kind { SettingKind::Enum => &mut self.enums, SettingKind::Num => &mut self.nums, SettingKind::Bool => &mut self.bools, SettingKind::Preset => &mut self.presets, }; collection.push(SettingData(setting)); } fn add_settings(&mut self, iterable: I) where I: IntoIterator, { for item in iterable.into_iter() { self.add_setting(item); } } fn is_empty(&self) -> bool { self.enums.is_empty() && self.nums.is_empty() && self.bools.is_empty() && self.presets.is_empty() } } impl SettingsCommand { /// Executes the command. pub fn execute(self) -> Result<()> { // Gather settings from the cranelift compiler builder let mut builder = wasmtime_cranelift::builder(None)?; let tunables = if let Some(target) = &self.target { let target = target_lexicon::Triple::from_str(target).map_err(|e| format_err!(e))?; let tunables = Tunables::default_for_target(&target)?; builder.target(target)?; tunables } else { Tunables::default_host() }; builder.set_tunables(tunables)?; let mut settings = Settings::from_builder(&builder); // Add inferred settings if no target specified if self.target.is_none() { settings.infer(&builder)?; } // Print settings if self.json { self.print_json(settings) } else { self.print_human_readable(settings) } } fn print_json(self, settings: Settings) -> Result<()> { println!("{}", serde_json::to_string_pretty(&settings)?); Ok(()) } fn print_human_readable(self, settings: Settings) -> Result<()> { if settings.is_empty() { println!("Target '{}' has no settings.", settings.triple); return Ok(()); } println!("Cranelift settings for target '{}':", settings.triple); Self::print_settings_human_readable("Boolean settings:", &settings.bools); Self::print_settings_human_readable("Enum settings:", &settings.enums); Self::print_settings_human_readable("Numerical settings:", &settings.nums); Self::print_settings_human_readable("Presets:", &settings.presets); if let Some(inferred) = settings.inferred { println!(); println!("Settings inferred for the current host:"); for name in inferred { println!(" {name}"); } } Ok(()) } fn print_settings_human_readable(header: &str, settings: &[SettingData]) { if settings.is_empty() { return; } println!(); println!("{header}"); let width = settings.iter().map(|s| s.0.name.len()).max().unwrap_or(0); for setting in settings { println!( " {:width$} {}{}", setting.0.name, setting.0.description, setting .0 .values .map(|v| format!(" Supported values: {}.", v.join(", "))) .unwrap_or("".to_string()), width = width + 2 ); } } }