use anyhow::{Result, anyhow};
use clap::Parser;
use serde::{Serialize, ser::SerializeMap};
use std::collections::BTreeMap;
use std::str::FromStr;
use wasmtime_environ::{CompilerBuilder, FlagValue, Setting, SettingKind, Tunables};
#[derive(Parser, PartialEq)]
pub struct SettingsCommand {
#[arg(long, value_name = "TARGET")]
target: Option<String>,
#[arg(long)]
json: bool,
}
struct SettingData(Setting);
impl Serialize for SettingData {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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()
}
}
#[derive(serde_derive::Serialize)]
struct Settings {
triple: String,
enums: Vec<SettingData>,
nums: Vec<SettingData>,
bools: Vec<SettingData>,
presets: Vec<SettingData>,
inferred: Option<Vec<&'static str>>,
}
impl Settings {
fn from_builder(builder: &Box<dyn CompilerBuilder>) -> 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<dyn CompilerBuilder>) -> Result<()> {
let compiler = builder.build()?;
let values = compiler.isa_flags().into_iter().collect::<BTreeMap<_, _>>();
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<I>(&mut self, iterable: I)
where
I: IntoIterator<Item = Setting>,
{
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 {
pub fn execute(self) -> Result<()> {
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| anyhow!(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);
if self.target.is_none() {
settings.infer(&builder)?;
}
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
);
}
}
}