1mod printing;
2
3use alloc::{
4 boxed::Box,
5 fmt,
6 str::FromStr,
7 string::{String, ToString},
8 sync::Arc,
9 vec,
10 vec::Vec,
11};
12
13use miden_debug_types::SourceManager;
14use miden_project::TargetType;
15
16pub use self::printing::IrFilter;
17use crate::{
18 ColorChoice, CompileFlags, InputFile, LinkLibrary, OutputFile, OutputTypes, PathBuf,
19 diagnostics::{DiagnosticsConfig, Emitter, Report},
20};
21
22#[derive(Debug, Clone)]
24pub struct Options {
25 pub manifest_path: Option<PathBuf>,
27 pub name: Option<String>,
29 pub entrypoint: Option<String>,
31 pub profile: String,
33 pub workspace: bool,
35 pub packages: Vec<String>,
37 pub target: Option<String>,
39 pub target_type: Option<TargetType>,
41 pub optimize: OptLevel,
43 pub debug: DebugInfo,
45 pub output_types: OutputTypes,
47 pub search_paths: Vec<PathBuf>,
49 pub link_libraries: Vec<LinkLibrary>,
51 pub link_modules: Vec<(miden_assembly_syntax::PathBuf, String)>,
53 pub sysroot: Option<PathBuf>,
58 pub midenup_home: Option<PathBuf>,
62 pub toolchain: Option<String>,
66 pub color: ColorChoice,
68 pub diagnostics: DiagnosticsConfig,
70 pub current_dir: PathBuf,
72 pub target_dir: PathBuf,
74 pub output_dir: Option<PathBuf>,
76 pub output_file: Option<OutputFile>,
78 pub remap_path_prefixes: Vec<RemapPathPrefix>,
80 pub print_hir_source_locations: bool,
82 pub stop_after: Option<String>,
93 pub parse_only: bool,
95 pub analyze_only: bool,
97 pub link_only: bool,
99 pub no_link: bool,
101 pub lint: bool,
106 pub print_cfg_after_all: bool,
108 pub print_cfg_after_pass: Vec<String>,
110 pub print_ir_before_stage: Vec<String>,
112 pub print_ir_after_all: bool,
114 pub print_ir_after_pass: Vec<String>,
116 pub print_ir_after_modified: bool,
118 pub print_ir_filters: Vec<IrFilter>,
120 pub save_temps: bool,
122 pub rustflags: Option<String>,
124 pub cargo_frontmatter: bool,
126 pub flags: CompileFlags,
129}
130
131impl Default for Options {
132 fn default() -> Self {
133 let current_dir = current_dir();
134 let target_dir = current_dir.join("target");
135 Self::new(None, None, current_dir, target_dir, None, None)
136 }
137}
138
139impl Options {
140 pub fn new(
141 name: Option<String>,
142 target: Option<TargetType>,
143 current_dir: PathBuf,
144 target_dir: PathBuf,
145 output_dir: Option<PathBuf>,
146 sysroot: Option<PathBuf>,
147 ) -> Self {
148 let search_paths = if let Some(sysroot) = sysroot.as_deref() {
149 let lib_dir = sysroot.join("lib");
150 if lib_dir.try_exists().is_ok_and(|exists| exists) {
151 vec![lib_dir]
152 } else {
153 vec![]
154 }
155 } else {
156 vec![]
157 };
158
159 Self {
160 manifest_path: None,
161 name,
162 profile: "dev".to_string(),
163 workspace: false,
164 packages: vec![],
165 target: None,
166 target_type: target,
167 entrypoint: None,
168 optimize: OptLevel::None,
169 debug: DebugInfo::None,
170 output_types: Default::default(),
171 search_paths,
172 link_libraries: vec![],
173 link_modules: vec![],
174 sysroot,
175 midenup_home: None,
176 toolchain: None,
177 color: Default::default(),
178 diagnostics: Default::default(),
179 current_dir,
180 target_dir,
181 output_dir,
182 output_file: None,
183 print_hir_source_locations: false,
184 stop_after: None,
185 parse_only: false,
186 analyze_only: false,
187 link_only: false,
188 no_link: false,
189 save_temps: false,
190 lint: false,
191 cargo_frontmatter: false,
192 print_cfg_after_all: false,
193 print_cfg_after_pass: vec![],
194 print_ir_before_stage: vec![],
195 print_ir_after_all: false,
196 print_ir_after_pass: vec![],
197 print_ir_after_modified: false,
198 print_ir_filters: vec![],
199 rustflags: None,
200 remap_path_prefixes: vec![],
201 flags: CompileFlags::default(),
202 }
203 }
204
205 #[inline(always)]
206 pub fn with_color(mut self: Box<Self>, color: ColorChoice) -> Box<Self> {
207 self.color = color;
208 self
209 }
210
211 #[inline(always)]
212 pub fn with_verbosity(mut self: Box<Self>, verbosity: Verbosity) -> Box<Self> {
213 self.diagnostics.verbosity = verbosity;
214 self
215 }
216
217 #[inline(always)]
218 pub fn with_debug_info(mut self: Box<Self>, debug: DebugInfo) -> Box<Self> {
219 self.debug = debug;
220 self
221 }
222
223 #[inline(always)]
224 pub fn with_optimization(mut self: Box<Self>, level: OptLevel) -> Box<Self> {
225 self.optimize = level;
226 self
227 }
228
229 pub fn with_warnings(mut self: Box<Self>, warnings: Warnings) -> Box<Self> {
230 self.diagnostics.warnings = warnings;
231 self
232 }
233
234 pub fn with_output_types(
235 mut self: Box<Self>,
236 mut output_types: OutputTypes,
237 output_file: Option<OutputFile>,
238 ) -> Box<Self> {
239 use crate::OutputType;
240 let has_final_output = output_types.keys().any(|ty| matches!(ty, OutputType::Masp));
241 if !has_final_output {
242 output_types.insert(OutputType::Masp, output_file);
244 } else if output_file.is_some() && output_types.get(&OutputType::Masp).is_some() {
245 output_types.insert(OutputType::Masp, output_file);
247 }
248 self.output_types = output_types;
249 self
250 }
251
252 #[doc(hidden)]
253 pub fn with_extra_flags(mut self: Box<Self>, flags: CompileFlags) -> Box<Self> {
254 self.flags = flags;
255 self
256 }
257
258 #[doc(hidden)]
259 pub fn set_extra_flags(&mut self, flags: CompileFlags) {
260 self.flags = flags;
261 }
262
263 pub fn into_session(
265 self: Box<Self>,
266 input: InputFile,
267 emitter: Option<Arc<dyn Emitter>>,
268 source_manager: Option<Arc<dyn SourceManager + Send + Sync>>,
269 ) -> Result<crate::Session, Report> {
270 use crate::diagnostics::DefaultSourceManager;
271
272 let source_manager =
273 source_manager.unwrap_or_else(|| Arc::new(DefaultSourceManager::default()));
274 crate::Session::new(input, self, emitter, source_manager)
275 }
276
277 pub fn default_emitter(&self) -> Arc<dyn Emitter> {
279 use crate::diagnostics::{DefaultEmitter, NullEmitter};
280
281 match self.diagnostics.verbosity {
282 Verbosity::Silent => Arc::new(NullEmitter::new(self.color)),
283 _ => Arc::new(DefaultEmitter::new(self.color)),
284 }
285 }
286
287 #[inline(always)]
289 pub fn emit_source_locations(&self) -> bool {
290 matches!(self.debug, DebugInfo::Line | DebugInfo::Full)
291 }
292
293 #[inline(always)]
296 pub fn emit_debug_decorators(&self) -> bool {
297 matches!(self.debug, DebugInfo::Line | DebugInfo::Full)
298 }
299
300 #[inline(always)]
302 pub fn emit_debug_assertions(&self) -> bool {
303 self.debug != DebugInfo::None && matches!(self.optimize, OptLevel::None | OptLevel::Basic)
304 }
305
306 pub fn target_requires_protocol(&self) -> bool {
308 use miden_project::TargetType;
309 !matches!(
310 self.target_type,
311 Some(TargetType::Kernel | TargetType::Executable | TargetType::Library) | None
312 )
313 }
314
315 pub fn quiet(&self) -> bool {
317 matches!(self.diagnostics.verbosity, Verbosity::Silent)
318 }
319}
320
321#[derive(Debug, Copy, Clone, Default)]
323#[cfg_attr(feature = "std", derive(clap::ValueEnum))]
324pub enum OptLevel {
325 None,
327 Basic,
329 #[default]
331 Balanced,
332 Max,
334 Size,
336 SizeMin,
338}
339
340#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
342#[cfg_attr(feature = "std", derive(clap::ValueEnum))]
343pub enum DebugInfo {
344 None,
346 #[default]
348 Line,
349 Full,
351}
352
353#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
355#[cfg_attr(feature = "std", derive(clap::ValueEnum))]
356pub enum Warnings {
357 None,
359 #[default]
361 All,
362 Error,
364}
365impl Warnings {
366 #[inline]
367 pub fn should_be_pedantic(&self) -> bool {
368 matches!(self, Self::All)
369 }
370
371 #[inline]
372 pub fn warnings_as_errors(&self) -> bool {
373 matches!(self, Self::Error)
374 }
375}
376impl fmt::Display for Warnings {
377 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
378 match self {
379 Self::None => f.write_str("none"),
380 Self::All => f.write_str("auto"),
381 Self::Error => f.write_str("error"),
382 }
383 }
384}
385impl FromStr for Warnings {
386 type Err = ();
387
388 fn from_str(s: &str) -> Result<Self, Self::Err> {
389 match s {
390 "none" => Ok(Self::None),
391 "all" => Ok(Self::All),
392 "error" => Ok(Self::Error),
393 _ => Err(()),
394 }
395 }
396}
397
398#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
400#[cfg_attr(feature = "std", derive(clap::ValueEnum))]
401pub enum Verbosity {
402 Debug,
404 #[default]
406 Info,
407 Warning,
409 Error,
411 Silent,
413}
414
415#[derive(Debug, Clone)]
418pub struct RemapPathPrefix {
419 pub from: Box<crate::Path>,
421 pub to: Option<Box<crate::Path>>,
425}
426
427impl RemapPathPrefix {
428 pub fn source_prefix(&self) -> &crate::Path {
429 &self.from
430 }
431
432 pub fn target_prefix(&self) -> &crate::Path {
433 self.to.as_deref().unwrap_or(crate::Path::new(""))
434 }
435}
436
437#[doc(hidden)]
439#[derive(Clone)]
440#[cfg(feature = "std")]
441pub struct RemapPathPrefixParser;
442
443#[cfg(feature = "std")]
444impl clap::builder::TypedValueParser for RemapPathPrefixParser {
445 type Value = RemapPathPrefix;
446
447 fn parse_ref(
448 &self,
449 _cmd: &clap::Command,
450 _arg: Option<&clap::Arg>,
451 value: &std::ffi::OsStr,
452 ) -> Result<Self::Value, clap::error::Error> {
453 use clap::error::{Error, ErrorKind};
454
455 let input = value.to_str().ok_or_else(|| Error::new(ErrorKind::InvalidUtf8))?;
456
457 Ok(match input.split_once('=') {
458 Some((from, to)) => RemapPathPrefix {
459 from: PathBuf::from(from.trim()).into_boxed_path(),
460 to: Some(PathBuf::from(to.trim()).into_boxed_path()),
461 },
462 None => RemapPathPrefix {
463 from: PathBuf::from(input.trim()).into_boxed_path(),
464 to: None,
465 },
466 })
467 }
468}
469
470#[cfg(feature = "std")]
471fn current_dir() -> PathBuf {
472 std::env::current_dir().expect("could not get working directory")
473}
474
475#[cfg(not(feature = "std"))]
476fn current_dir() -> PathBuf {
477 PathBuf::from(".")
478}