xref: /wasmtime-44.0.1/src/commands/settings.rs (revision 94740588)
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