xref: /wasmtime-44.0.1/crates/cli-flags/src/lib.rs (revision 677e92a2)
1 //! Contains the common Wasmtime command line interface (CLI) flags.
2 
3 use anyhow::Result;
4 use clap::Parser;
5 use std::time::Duration;
6 use wasmtime::Config;
7 
8 pub mod opt;
9 
10 #[cfg(feature = "logging")]
11 fn init_file_per_thread_logger(prefix: &'static str) {
12     file_per_thread_logger::initialize(prefix);
13     file_per_thread_logger::allow_uninitialized();
14 
15     // Extending behavior of default spawner:
16     // https://docs.rs/rayon/1.1.0/rayon/struct.ThreadPoolBuilder.html#method.spawn_handler
17     // Source code says DefaultSpawner is implementation detail and
18     // shouldn't be used directly.
19     #[cfg(feature = "parallel-compilation")]
20     rayon::ThreadPoolBuilder::new()
21         .spawn_handler(move |thread| {
22             let mut b = std::thread::Builder::new();
23             if let Some(name) = thread.name() {
24                 b = b.name(name.to_owned());
25             }
26             if let Some(stack_size) = thread.stack_size() {
27                 b = b.stack_size(stack_size);
28             }
29             b.spawn(move || {
30                 file_per_thread_logger::initialize(prefix);
31                 thread.run()
32             })?;
33             Ok(())
34         })
35         .build_global()
36         .unwrap();
37 }
38 
39 wasmtime_option_group! {
40     #[derive(PartialEq, Clone)]
41     pub struct OptimizeOptions {
42         /// Optimization level of generated code (0-2, s; default: 2)
43         pub opt_level: Option<wasmtime::OptLevel>,
44 
45         /// Byte size of the guard region after dynamic memories are allocated
46         pub dynamic_memory_guard_size: Option<u64>,
47 
48         /// Force using a "static" style for all wasm memories
49         pub static_memory_forced: Option<bool>,
50 
51         /// Maximum size in bytes of wasm memory before it becomes dynamically
52         /// relocatable instead of up-front-reserved.
53         pub static_memory_maximum_size: Option<u64>,
54 
55         /// Byte size of the guard region after static memories are allocated
56         pub static_memory_guard_size: Option<u64>,
57 
58         /// Bytes to reserve at the end of linear memory for growth for dynamic
59         /// memories.
60         pub dynamic_memory_reserved_for_growth: Option<u64>,
61 
62         /// Indicates whether an unmapped region of memory is placed before all
63         /// linear memories.
64         pub guard_before_linear_memory: Option<bool>,
65 
66         /// Enable the pooling allocator, in place of the on-demand allocator.
67         pub pooling_allocator: Option<bool>,
68 
69         /// How many bytes to keep resident between instantiations for the
70         /// pooling allocator in linear memories.
71         pub pooling_memory_keep_resident: Option<usize>,
72 
73         /// How many bytes to keep resident between instantiations for the
74         /// pooling allocator in tables.
75         pub pooling_table_keep_resident: Option<usize>,
76 
77         /// Enable memory protection keys for the pooling allocator; this can
78         /// optimize the size of memory slots.
79         pub memory_protection_keys: Option<bool>,
80 
81         /// Configure attempting to initialize linear memory via a
82         /// copy-on-write mapping (default: yes)
83         pub memory_init_cow: Option<bool>,
84 
85         /// The maximum number of WebAssembly instances which can be created
86         /// with the pooling allocator.
87         pub pooling_total_core_instances: Option<u32>,
88 
89         /// The maximum number of WebAssembly components which can be created
90         /// with the pooling allocator.
91         pub pooling_total_component_instances: Option<u32>,
92 
93         /// The maximum number of WebAssembly memories which can be created with
94         /// the pooling allocator.
95         pub pooling_total_memories: Option<u32>,
96 
97         /// The maximum number of WebAssembly tables which can be created with
98         /// the pooling allocator.
99         pub pooling_total_tables: Option<u32>,
100 
101         /// The maximum number of WebAssembly stacks which can be created with
102         /// the pooling allocator.
103         pub pooling_total_stacks: Option<u32>,
104     }
105 
106     enum Optimize {
107         ...
108     }
109 }
110 
111 wasmtime_option_group! {
112     #[derive(PartialEq, Clone)]
113     pub struct CodegenOptions {
114         /// Either `cranelift` or `winch`.
115         ///
116         /// Currently only `cranelift` and `winch` are supported, but not all
117         /// builds of Wasmtime have both built in.
118         pub compiler: Option<wasmtime::Strategy>,
119         /// Enable Cranelift's internal debug verifier (expensive)
120         pub cranelift_debug_verifier: Option<bool>,
121         /// Whether or not to enable caching of compiled modules.
122         pub cache: Option<bool>,
123         /// Configuration for compiled module caching.
124         pub cache_config: Option<String>,
125         /// Whether or not to enable parallel compilation of modules.
126         pub parallel_compilation: Option<bool>,
127         /// Whether to enable proof-carrying code (PCC)-based validation.
128         pub pcc: Option<bool>,
129 
130         #[prefixed = "cranelift"]
131         /// Set a cranelift-specific option. Use `wasmtime settings` to see
132         /// all.
133         pub cranelift: Vec<(String, Option<String>)>,
134     }
135 
136     enum Codegen {
137         ...
138     }
139 }
140 
141 wasmtime_option_group! {
142     #[derive(PartialEq, Clone)]
143     pub struct DebugOptions {
144         /// Enable generation of DWARF debug information in compiled code.
145         pub debug_info: Option<bool>,
146         /// Configure whether compiled code can map native addresses to wasm.
147         pub address_map: Option<bool>,
148         /// Configure whether logging is enabled.
149         pub logging: Option<bool>,
150         /// Configure whether logs are emitted to files
151         pub log_to_files: Option<bool>,
152         /// Enable coredump generation to this file after a WebAssembly trap.
153         pub coredump: Option<String>,
154     }
155 
156     enum Debug {
157         ...
158     }
159 }
160 
161 wasmtime_option_group! {
162     #[derive(PartialEq, Clone)]
163     pub struct WasmOptions {
164         /// Enable canonicalization of all NaN values.
165         pub nan_canonicalization: Option<bool>,
166         /// Enable execution fuel with N units fuel, trapping after running out
167         /// of fuel.
168         ///
169         /// Most WebAssembly instructions consume 1 unit of fuel. Some
170         /// instructions, such as `nop`, `drop`, `block`, and `loop`, consume 0
171         /// units, as any execution cost associated with them involves other
172         /// instructions which do consume fuel.
173         pub fuel: Option<u64>,
174         /// Yield when a global epoch counter changes, allowing for async
175         /// operation without blocking the executor.
176         pub epoch_interruption: Option<bool>,
177         /// Maximum stack size, in bytes, that wasm is allowed to consume before a
178         /// stack overflow is reported.
179         pub max_wasm_stack: Option<usize>,
180         /// Allow unknown exports when running commands.
181         pub unknown_exports_allow: Option<bool>,
182         /// Allow the main module to import unknown functions, using an
183         /// implementation that immediately traps, when running commands.
184         pub unknown_imports_trap: Option<bool>,
185         /// Allow the main module to import unknown functions, using an
186         /// implementation that returns default values, when running commands.
187         pub unknown_imports_default: Option<bool>,
188         /// Enables memory error checking. (see wmemcheck.md for more info)
189         pub wmemcheck: Option<bool>,
190         /// Maximum size, in bytes, that a linear memory is allowed to reach.
191         ///
192         /// Growth beyond this limit will cause `memory.grow` instructions in
193         /// WebAssembly modules to return -1 and fail.
194         pub max_memory_size: Option<usize>,
195         /// Maximum size, in table elements, that a table is allowed to reach.
196         pub max_table_elements: Option<u32>,
197         /// Maximum number of WebAssembly instances allowed to be created.
198         pub max_instances: Option<usize>,
199         /// Maximum number of WebAssembly tables allowed to be created.
200         pub max_tables: Option<usize>,
201         /// Maximum number of WebAssembly linear memories allowed to be created.
202         pub max_memories: Option<usize>,
203         /// Force a trap to be raised on `memory.grow` and `table.grow` failure
204         /// instead of returning -1 from these instructions.
205         ///
206         /// This is not necessarily a spec-compliant option to enable but can be
207         /// useful for tracking down a backtrace of what is requesting so much
208         /// memory, for example.
209         pub trap_on_grow_failure: Option<bool>,
210         /// Maximum execution time of wasm code before timing out (1, 2s, 100ms, etc)
211         pub timeout: Option<Duration>,
212         /// Configures support for all WebAssembly proposals implemented.
213         pub all_proposals: Option<bool>,
214         /// Configure support for the bulk memory proposal.
215         pub bulk_memory: Option<bool>,
216         /// Configure support for the multi-memory proposal.
217         pub multi_memory: Option<bool>,
218         /// Configure support for the multi-value proposal.
219         pub multi_value: Option<bool>,
220         /// Configure support for the reference-types proposal.
221         pub reference_types: Option<bool>,
222         /// Configure support for the simd proposal.
223         pub simd: Option<bool>,
224         /// Configure support for the relaxed-simd proposal.
225         pub relaxed_simd: Option<bool>,
226         /// Configure forcing deterministic and host-independent behavior of
227         /// the relaxed-simd instructions.
228         ///
229         /// By default these instructions may have architecture-specific behavior as
230         /// allowed by the specification, but this can be used to force the behavior
231         /// of these instructions to match the deterministic behavior classified in
232         /// the specification. Note that enabling this option may come at a
233         /// performance cost.
234         pub relaxed_simd_deterministic: Option<bool>,
235         /// Configure support for the tail-call proposal.
236         pub tail_call: Option<bool>,
237         /// Configure support for the threads proposal.
238         pub threads: Option<bool>,
239         /// Configure support for the memory64 proposal.
240         pub memory64: Option<bool>,
241         /// Configure support for the component-model proposal.
242         pub component_model: Option<bool>,
243         /// Configure support for the function-references proposal.
244         pub function_references: Option<bool>,
245         /// Configure support for the GC proposal.
246         pub gc: Option<bool>,
247     }
248 
249     enum Wasm {
250         ...
251     }
252 }
253 
254 wasmtime_option_group! {
255     #[derive(PartialEq, Clone)]
256     pub struct WasiOptions {
257         /// Enable support for WASI CLI APIs, including filesystems, sockets, clocks, and random.
258         pub cli: Option<bool>,
259         /// Deprecated alias for `cli`
260         pub common: Option<bool>,
261         /// Enable suport for WASI neural network API (experimental)
262         pub nn: Option<bool>,
263         /// Enable suport for WASI threading API (experimental)
264         pub threads: Option<bool>,
265         /// Enable suport for WASI HTTP API (experimental)
266         pub http: Option<bool>,
267         /// Inherit environment variables and file descriptors following the
268         /// systemd listen fd specification (UNIX only)
269         pub listenfd: Option<bool>,
270         /// Grant access to the given TCP listen socket
271         pub tcplisten: Vec<String>,
272         /// Implement WASI CLI APIs with preview2 primitives (experimental).
273         ///
274         /// Indicates that the implementation of WASI preview1 should be backed by
275         /// the preview2 implementation for components.
276         ///
277         /// This will become the default in the future and this option will be
278         /// removed. For now this is primarily here for testing.
279         pub preview2: Option<bool>,
280         /// Pre-load machine learning graphs (i.e., models) for use by wasi-nn.
281         ///
282         /// Each use of the flag will preload a ML model from the host directory
283         /// using the given model encoding. The model will be mapped to the
284         /// directory name: e.g., `--wasi-nn-graph openvino:/foo/bar` will preload
285         /// an OpenVINO model named `bar`. Note that which model encodings are
286         /// available is dependent on the backends implemented in the
287         /// `wasmtime_wasi_nn` crate.
288         pub nn_graph: Vec<WasiNnGraph>,
289         /// Flag for WASI preview2 to inherit the host's network within the
290         /// guest so it has full access to all addresses/ports/etc.
291         pub inherit_network: Option<bool>,
292         /// Indicates whether `wasi:sockets/ip-name-lookup` is enabled or not.
293         pub allow_ip_name_lookup: Option<bool>,
294         /// Indicates whether `wasi:sockets` TCP support is enabled or not.
295         pub tcp: Option<bool>,
296         /// Indicates whether `wasi:sockets` UDP support is enabled or not.
297         pub udp: Option<bool>,
298         /// Allows imports from the `wasi_unstable` core wasm module.
299         pub preview0: Option<bool>,
300         /// Inherit all environment variables from the parent process.
301         ///
302         /// This option can be further overwritten with `--env` flags.
303         pub inherit_env: Option<bool>,
304     }
305 
306     enum Wasi {
307         ...
308     }
309 }
310 
311 #[derive(Debug, Clone, PartialEq)]
312 pub struct WasiNnGraph {
313     pub format: String,
314     pub dir: String,
315 }
316 
317 /// Common options for commands that translate WebAssembly modules
318 #[derive(Parser, Clone)]
319 pub struct CommonOptions {
320     // These options groups are used to parse `-O` and such options but aren't
321     // the raw form consumed by the CLI. Instead they're pushed into the `pub`
322     // fields below as part of the `configure` method.
323     //
324     // Ideally clap would support `pub opts: OptimizeOptions` and parse directly
325     // into that but it does not appear to do so for multiple `-O` flags for
326     // now.
327     /// Optimization and tuning related options for wasm performance, `-O help` to
328     /// see all.
329     #[arg(short = 'O', long = "optimize", value_name = "KEY[=VAL[,..]]")]
330     opts_raw: Vec<opt::CommaSeparated<Optimize>>,
331 
332     /// Codegen-related configuration options, `-C help` to see all.
333     #[arg(short = 'C', long = "codegen", value_name = "KEY[=VAL[,..]]")]
334     codegen_raw: Vec<opt::CommaSeparated<Codegen>>,
335 
336     /// Debug-related configuration options, `-D help` to see all.
337     #[arg(short = 'D', long = "debug", value_name = "KEY[=VAL[,..]]")]
338     debug_raw: Vec<opt::CommaSeparated<Debug>>,
339 
340     /// Options for configuring semantic execution of WebAssembly, `-W help` to see
341     /// all.
342     #[arg(short = 'W', long = "wasm", value_name = "KEY[=VAL[,..]]")]
343     wasm_raw: Vec<opt::CommaSeparated<Wasm>>,
344 
345     /// Options for configuring WASI and its proposals, `-S help` to see all.
346     #[arg(short = 'S', long = "wasi", value_name = "KEY[=VAL[,..]]")]
347     wasi_raw: Vec<opt::CommaSeparated<Wasi>>,
348 
349     // These fields are filled in by the `configure` method below via the
350     // options parsed from the CLI above. This is what the CLI should use.
351     #[arg(skip)]
352     configured: bool,
353     #[arg(skip)]
354     pub opts: OptimizeOptions,
355     #[arg(skip)]
356     pub codegen: CodegenOptions,
357     #[arg(skip)]
358     pub debug: DebugOptions,
359     #[arg(skip)]
360     pub wasm: WasmOptions,
361     #[arg(skip)]
362     pub wasi: WasiOptions,
363 }
364 
365 macro_rules! match_feature {
366     (
367         [$feat:tt : $config:expr]
368         $val:ident => $e:expr,
369         $p:pat => err,
370     ) => {
371         #[cfg(feature = $feat)]
372         {
373             if let Some($val) = $config {
374                 $e;
375             }
376         }
377         #[cfg(not(feature = $feat))]
378         {
379             if let Some($p) = $config {
380                 anyhow::bail!(concat!("support for ", $feat, " disabled at compile time"));
381             }
382         }
383     };
384 }
385 
386 impl CommonOptions {
387     fn configure(&mut self) {
388         if self.configured {
389             return;
390         }
391         self.configured = true;
392         self.opts.configure_with(&self.opts_raw);
393         self.codegen.configure_with(&self.codegen_raw);
394         self.debug.configure_with(&self.debug_raw);
395         self.wasm.configure_with(&self.wasm_raw);
396         self.wasi.configure_with(&self.wasi_raw);
397     }
398 
399     pub fn init_logging(&mut self) -> Result<()> {
400         self.configure();
401         if self.debug.logging == Some(false) {
402             return Ok(());
403         }
404         #[cfg(feature = "logging")]
405         if self.debug.log_to_files == Some(true) {
406             let prefix = "wasmtime.dbg.";
407             init_file_per_thread_logger(prefix);
408         } else {
409             use std::io::IsTerminal;
410             use tracing_subscriber::{EnvFilter, FmtSubscriber};
411             let b = FmtSubscriber::builder()
412                 .with_writer(std::io::stderr)
413                 .with_env_filter(EnvFilter::from_env("WASMTIME_LOG"))
414                 .with_ansi(std::io::stderr().is_terminal());
415             b.init();
416         }
417         #[cfg(not(feature = "logging"))]
418         if self.debug.log_to_files == Some(true) || self.debug.logging == Some(true) {
419             anyhow::bail!("support for logging disabled at compile time");
420         }
421         Ok(())
422     }
423 
424     pub fn config(&mut self, target: Option<&str>) -> Result<Config> {
425         self.configure();
426         let mut config = Config::new();
427 
428         match_feature! {
429             ["cranelift" : self.codegen.compiler]
430             strategy => config.strategy(strategy),
431             _ => err,
432         }
433         match_feature! {
434             ["cranelift" : target]
435             target => config.target(target)?,
436             _ => err,
437         }
438         match_feature! {
439             ["cranelift" : self.codegen.cranelift_debug_verifier]
440             enable => config.cranelift_debug_verifier(enable),
441             true => err,
442         }
443         if let Some(enable) = self.debug.debug_info {
444             config.debug_info(enable);
445         }
446         if self.debug.coredump.is_some() {
447             #[cfg(feature = "coredump")]
448             config.coredump_on_trap(true);
449             #[cfg(not(feature = "coredump"))]
450             anyhow::bail!("support for coredumps disabled at compile time");
451         }
452         match_feature! {
453             ["cranelift" : self.opts.opt_level]
454             level => config.cranelift_opt_level(level),
455             _ => err,
456         }
457         match_feature! {
458             ["cranelift" : self.wasm.nan_canonicalization]
459             enable => config.cranelift_nan_canonicalization(enable),
460             true => err,
461         }
462         match_feature! {
463             ["cranelift" : self.codegen.pcc]
464             enable => config.cranelift_pcc(enable),
465             true => err,
466         }
467 
468         self.enable_wasm_features(&mut config)?;
469 
470         #[cfg(feature = "cranelift")]
471         for (name, value) in self.codegen.cranelift.iter() {
472             let name = name.replace('-', "_");
473             unsafe {
474                 match value {
475                     Some(val) => {
476                         config.cranelift_flag_set(&name, val);
477                     }
478                     None => {
479                         config.cranelift_flag_enable(&name);
480                     }
481                 }
482             }
483         }
484         #[cfg(not(feature = "cranelift"))]
485         if !self.codegen.cranelift.is_empty() {
486             anyhow::bail!("support for cranelift disabled at compile time");
487         }
488 
489         #[cfg(feature = "cache")]
490         if self.codegen.cache != Some(false) {
491             match &self.codegen.cache_config {
492                 Some(path) => {
493                     config.cache_config_load(path)?;
494                 }
495                 None => {
496                     config.cache_config_load_default()?;
497                 }
498             }
499         }
500         #[cfg(not(feature = "cache"))]
501         if self.codegen.cache == Some(true) {
502             anyhow::bail!("support for caching disabled at compile time");
503         }
504 
505         match_feature! {
506             ["parallel-compilation" : self.codegen.parallel_compilation]
507             enable => config.parallel_compilation(enable),
508             true => err,
509         }
510 
511         if let Some(max) = self.opts.static_memory_maximum_size {
512             config.static_memory_maximum_size(max);
513         }
514 
515         if let Some(enable) = self.opts.static_memory_forced {
516             config.static_memory_forced(enable);
517         }
518 
519         if let Some(size) = self.opts.static_memory_guard_size {
520             config.static_memory_guard_size(size);
521         }
522 
523         if let Some(size) = self.opts.dynamic_memory_guard_size {
524             config.dynamic_memory_guard_size(size);
525         }
526         if let Some(size) = self.opts.dynamic_memory_reserved_for_growth {
527             config.dynamic_memory_reserved_for_growth(size);
528         }
529         if let Some(enable) = self.opts.guard_before_linear_memory {
530             config.guard_before_linear_memory(enable);
531         }
532 
533         // If fuel has been configured, set the `consume fuel` flag on the config.
534         if self.wasm.fuel.is_some() {
535             config.consume_fuel(true);
536         }
537 
538         if let Some(enable) = self.wasm.epoch_interruption {
539             config.epoch_interruption(enable);
540         }
541         if let Some(enable) = self.debug.address_map {
542             config.generate_address_map(enable);
543         }
544         if let Some(enable) = self.opts.memory_init_cow {
545             config.memory_init_cow(enable);
546         }
547 
548         match_feature! {
549             ["pooling-allocator" : self.opts.pooling_allocator]
550             enable => {
551                 if enable {
552                     let mut cfg = wasmtime::PoolingAllocationConfig::default();
553                     if let Some(size) = self.opts.pooling_memory_keep_resident {
554                         cfg.linear_memory_keep_resident(size);
555                     }
556                     if let Some(size) = self.opts.pooling_table_keep_resident {
557                         cfg.table_keep_resident(size);
558                     }
559                     if let Some(limit) = self.opts.pooling_total_core_instances {
560                         cfg.total_core_instances(limit);
561                     }
562                     if let Some(limit) = self.opts.pooling_total_component_instances {
563                         cfg.total_component_instances(limit);
564                     }
565                     if let Some(limit) = self.opts.pooling_total_memories {
566                         cfg.total_memories(limit);
567                     }
568                     if let Some(limit) = self.opts.pooling_total_tables {
569                         cfg.total_tables(limit);
570                     }
571                     if let Some(limit) = self.opts.pooling_total_stacks {
572                         cfg.total_stacks(limit);
573                     }
574                     if let Some(enable) = self.opts.memory_protection_keys {
575                         if enable {
576                             cfg.memory_protection_keys(wasmtime::MpkEnabled::Enable);
577                         }
578                     }
579                     config.allocation_strategy(wasmtime::InstanceAllocationStrategy::Pooling(cfg));
580                 }
581             },
582             true => err,
583         }
584 
585         if self.opts.memory_protection_keys.unwrap_or(false)
586             && !self.opts.pooling_allocator.unwrap_or(false)
587         {
588             anyhow::bail!("memory protection keys require the pooling allocator");
589         }
590 
591         if let Some(max) = self.wasm.max_wasm_stack {
592             config.max_wasm_stack(max);
593         }
594 
595         if let Some(enable) = self.wasm.relaxed_simd_deterministic {
596             config.relaxed_simd_deterministic(enable);
597         }
598         match_feature! {
599             ["cranelift" : self.wasm.wmemcheck]
600             enable => config.wmemcheck(enable),
601             true => err,
602         }
603 
604         Ok(config)
605     }
606 
607     pub fn enable_wasm_features(&self, config: &mut Config) -> Result<()> {
608         let all = self.wasm.all_proposals;
609 
610         if let Some(enable) = self.wasm.simd.or(all) {
611             config.wasm_simd(enable);
612         }
613         if let Some(enable) = self.wasm.relaxed_simd.or(all) {
614             config.wasm_relaxed_simd(enable);
615         }
616         if let Some(enable) = self.wasm.bulk_memory.or(all) {
617             config.wasm_bulk_memory(enable);
618         }
619         if let Some(enable) = self.wasm.multi_value.or(all) {
620             config.wasm_multi_value(enable);
621         }
622         if let Some(enable) = self.wasm.tail_call.or(all) {
623             config.wasm_tail_call(enable);
624         }
625         if let Some(enable) = self.wasm.multi_memory.or(all) {
626             config.wasm_multi_memory(enable);
627         }
628         if let Some(enable) = self.wasm.memory64.or(all) {
629             config.wasm_memory64(enable);
630         }
631 
632         macro_rules! handle_conditionally_compiled {
633             ($(($feature:tt, $field:tt, $method:tt))*) => ($(
634                 if let Some(enable) = self.wasm.$field.or(all) {
635                     #[cfg(feature = $feature)]
636                     config.$method(enable);
637                     #[cfg(not(feature = $feature))]
638                     if enable && all.is_none() {
639                         anyhow::bail!("support for {} was disabled at compile-time", $feature);
640                     }
641                 }
642             )*)
643         }
644 
645         handle_conditionally_compiled! {
646             ("component-model", component_model, wasm_component_model)
647             ("threads", threads, wasm_threads)
648             ("gc", gc, wasm_gc)
649             ("gc", reference_types, wasm_reference_types)
650             ("gc", function_references, wasm_function_references)
651         }
652         Ok(())
653     }
654 }
655 
656 impl PartialEq for CommonOptions {
657     fn eq(&self, other: &CommonOptions) -> bool {
658         let mut me = self.clone();
659         me.configure();
660         let mut other = other.clone();
661         other.configure();
662         let CommonOptions {
663             opts_raw: _,
664             codegen_raw: _,
665             debug_raw: _,
666             wasm_raw: _,
667             wasi_raw: _,
668             configured: _,
669 
670             opts,
671             codegen,
672             debug,
673             wasm,
674             wasi,
675         } = me;
676         opts == other.opts
677             && codegen == other.codegen
678             && debug == other.debug
679             && wasm == other.wasm
680             && wasi == other.wasi
681     }
682 }
683