--json option for wasmtime settings command (#5411)
* - Added `--json` flag to settings command - Refactored gathering of data into a `Settings` struct which can be used in both human/machine readable paths - Split out human readable output to another function, it produces exactly the same result as before * Outputting JSON by hand formatting it. This approach has the advantage of not needing any extra dependencies (i.e.serde), but is obviously a bit ugly. * Rewritten JSON serialization to use serde * Commenting and formatting * Applied rustfmt * Reduced version of serde and serde_json to fix cargo vet errors * Updated cargo.lock to fix cargo vet errors
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -3475,6 +3475,8 @@ dependencies = [
|
||||
"once_cell",
|
||||
"rayon",
|
||||
"rustix",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"target-lexicon",
|
||||
"tempfile",
|
||||
"test-programs",
|
||||
|
||||
@@ -38,6 +38,8 @@ humantime = "2.0.0"
|
||||
once_cell = { workspace = true }
|
||||
listenfd = "1.0.0"
|
||||
wat = { workspace = true }
|
||||
serde = "1.0.94"
|
||||
serde_json = "1.0.26"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
rustix = { workspace = true, features = ["mm", "param"] }
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use clap::Parser;
|
||||
use serde::{ser::SerializeMap, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
use std::str::FromStr;
|
||||
use wasmtime_environ::{FlagValue, Setting, SettingKind};
|
||||
use wasmtime_environ::{CompilerBuilder, FlagValue, Setting, SettingKind};
|
||||
|
||||
/// Displays available Cranelift settings for a target.
|
||||
#[derive(Parser)]
|
||||
@@ -13,79 +14,167 @@ pub struct SettingsCommand {
|
||||
/// The target triple to get the settings for; defaults to the host triple.
|
||||
#[clap(long, value_name = "TARGET")]
|
||||
target: Option<String>,
|
||||
|
||||
/// Switch output format to JSON
|
||||
#[clap(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()
|
||||
}
|
||||
}
|
||||
|
||||
// Gather together all of the setting data to displays
|
||||
#[derive(Serialize)]
|
||||
struct Settings {
|
||||
triple: String,
|
||||
|
||||
enums: Vec<SettingData>,
|
||||
nums: Vec<SettingData>,
|
||||
bools: Vec<SettingData>,
|
||||
presets: Vec<SettingData>,
|
||||
|
||||
inferred: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
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 {
|
||||
/// Executes the command.
|
||||
pub fn execute(self) -> Result<()> {
|
||||
// Gather settings from the cranelift compiler builder
|
||||
let mut builder = wasmtime_cranelift::builder();
|
||||
if let Some(target) = &self.target {
|
||||
let target = target_lexicon::Triple::from_str(target).map_err(|e| anyhow!(e))?;
|
||||
builder.target(target)?;
|
||||
}
|
||||
let mut settings = Settings::from_builder(&builder);
|
||||
|
||||
let mut enums = (Vec::new(), 0, "Enum settings:");
|
||||
let mut nums = (Vec::new(), 0, "Numerical settings:");
|
||||
let mut bools = (Vec::new(), 0, "Boolean settings:");
|
||||
let mut presets = (Vec::new(), 0, "Presets:");
|
||||
|
||||
for setting in builder.settings() {
|
||||
let (collection, max, _) = match setting.kind {
|
||||
SettingKind::Enum => &mut enums,
|
||||
SettingKind::Num => &mut nums,
|
||||
SettingKind::Bool => &mut bools,
|
||||
SettingKind::Preset => &mut presets,
|
||||
};
|
||||
|
||||
if setting.name.len() > *max {
|
||||
*max = setting.name.len();
|
||||
}
|
||||
|
||||
collection.push(setting);
|
||||
// Add inferred settings if no target specified
|
||||
if self.target.is_none() {
|
||||
settings.infer(&builder)?;
|
||||
}
|
||||
|
||||
if enums.0.is_empty() && nums.0.is_empty() && bools.0.is_empty() && presets.0.is_empty() {
|
||||
println!("Target '{}' has no settings.", builder.triple());
|
||||
// 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 '{}':", builder.triple());
|
||||
println!("Cranelift settings for target '{}':", settings.triple);
|
||||
|
||||
for (collection, max, header) in &mut [enums, nums, bools, presets] {
|
||||
if collection.is_empty() {
|
||||
continue;
|
||||
}
|
||||
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);
|
||||
|
||||
collection.sort_by_key(|k| k.name);
|
||||
println!();
|
||||
Self::print_settings(header, collection, *max);
|
||||
}
|
||||
|
||||
if self.target.is_none() {
|
||||
let compiler = builder.build()?;
|
||||
if let Some(inferred) = settings.inferred {
|
||||
println!();
|
||||
println!("Settings inferred for the current host:");
|
||||
|
||||
let values = compiler.isa_flags().into_iter().collect::<BTreeMap<_, _>>();
|
||||
|
||||
for (name, value) in values {
|
||||
if let FlagValue::Bool(true) = value {
|
||||
println!(" {}", name);
|
||||
}
|
||||
for name in inferred {
|
||||
println!(" {}", name);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_settings(header: &str, settings: &[Setting], width: usize) {
|
||||
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.name,
|
||||
setting.description,
|
||||
setting.0.name,
|
||||
setting.0.description,
|
||||
setting
|
||||
.0
|
||||
.values
|
||||
.map(|v| format!(" Supported values: {}.", v.join(", ")))
|
||||
.unwrap_or("".to_string()),
|
||||
|
||||
Reference in New Issue
Block a user