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 parse_only: bool,
84 pub analyze_only: bool,
86 pub link_only: bool,
88 pub no_link: bool,
90 pub lint: bool,
95 pub print_cfg_after_all: bool,
97 pub print_cfg_after_pass: Vec<String>,
99 pub print_ir_before_stage: Vec<String>,
101 pub print_ir_after_all: bool,
103 pub print_ir_after_pass: Vec<String>,
105 pub print_ir_after_modified: bool,
107 pub print_ir_filters: Vec<IrFilter>,
109 pub save_temps: bool,
111 pub rustflags: Option<String>,
113 pub cargo_frontmatter: bool,
115 pub flags: CompileFlags,
118}
119
120impl Default for Options {
121 fn default() -> Self {
122 let current_dir = current_dir();
123 let target_dir = current_dir.join("target");
124 Self::new(None, None, current_dir, target_dir, None, None)
125 }
126}
127
128impl Options {
129 pub fn new(
130 name: Option<String>,
131 target: Option<TargetType>,
132 current_dir: PathBuf,
133 target_dir: PathBuf,
134 output_dir: Option<PathBuf>,
135 sysroot: Option<PathBuf>,
136 ) -> Self {
137 let search_paths = if let Some(sysroot) = sysroot.as_deref() {
138 let lib_dir = sysroot.join("lib");
139 if lib_dir.try_exists().is_ok_and(|exists| exists) {
140 vec![lib_dir]
141 } else {
142 vec![]
143 }
144 } else {
145 vec![]
146 };
147
148 Self {
149 manifest_path: None,
150 name,
151 profile: "dev".to_string(),
152 workspace: false,
153 packages: vec![],
154 target: None,
155 target_type: target,
156 entrypoint: None,
157 optimize: OptLevel::None,
158 debug: DebugInfo::None,
159 output_types: Default::default(),
160 search_paths,
161 link_libraries: vec![],
162 link_modules: vec![],
163 sysroot,
164 midenup_home: None,
165 toolchain: None,
166 color: Default::default(),
167 diagnostics: Default::default(),
168 current_dir,
169 target_dir,
170 output_dir,
171 output_file: None,
172 print_hir_source_locations: false,
173 parse_only: false,
174 analyze_only: false,
175 link_only: false,
176 no_link: false,
177 save_temps: false,
178 lint: false,
179 cargo_frontmatter: false,
180 print_cfg_after_all: false,
181 print_cfg_after_pass: vec![],
182 print_ir_before_stage: vec![],
183 print_ir_after_all: false,
184 print_ir_after_pass: vec![],
185 print_ir_after_modified: false,
186 print_ir_filters: vec![],
187 rustflags: None,
188 remap_path_prefixes: vec![],
189 flags: CompileFlags::default(),
190 }
191 }
192
193 #[inline(always)]
194 pub fn with_color(mut self: Box<Self>, color: ColorChoice) -> Box<Self> {
195 self.color = color;
196 self
197 }
198
199 #[inline(always)]
200 pub fn with_verbosity(mut self: Box<Self>, verbosity: Verbosity) -> Box<Self> {
201 self.diagnostics.verbosity = verbosity;
202 self
203 }
204
205 #[inline(always)]
206 pub fn with_debug_info(mut self: Box<Self>, debug: DebugInfo) -> Box<Self> {
207 self.debug = debug;
208 self
209 }
210
211 #[inline(always)]
212 pub fn with_optimization(mut self: Box<Self>, level: OptLevel) -> Box<Self> {
213 self.optimize = level;
214 self
215 }
216
217 pub fn with_warnings(mut self: Box<Self>, warnings: Warnings) -> Box<Self> {
218 self.diagnostics.warnings = warnings;
219 self
220 }
221
222 pub fn with_output_types(
223 mut self: Box<Self>,
224 mut output_types: OutputTypes,
225 output_file: Option<OutputFile>,
226 ) -> Box<Self> {
227 use crate::OutputType;
228 let has_final_output = output_types.keys().any(|ty| matches!(ty, OutputType::Masp));
229 if !has_final_output {
230 output_types.insert(OutputType::Masp, output_file);
232 } else if output_file.is_some() && output_types.get(&OutputType::Masp).is_some() {
233 output_types.insert(OutputType::Masp, output_file);
235 }
236 self.output_types = output_types;
237 self
238 }
239
240 #[doc(hidden)]
241 pub fn with_extra_flags(mut self: Box<Self>, flags: CompileFlags) -> Box<Self> {
242 self.flags = flags;
243 self
244 }
245
246 #[doc(hidden)]
247 pub fn set_extra_flags(&mut self, flags: CompileFlags) {
248 self.flags = flags;
249 }
250
251 pub fn into_session(
253 self: Box<Self>,
254 input: InputFile,
255 emitter: Option<Arc<dyn Emitter>>,
256 source_manager: Option<Arc<dyn SourceManager + Send + Sync>>,
257 ) -> Result<crate::Session, Report> {
258 use crate::diagnostics::DefaultSourceManager;
259
260 let source_manager =
261 source_manager.unwrap_or_else(|| Arc::new(DefaultSourceManager::default()));
262 crate::Session::new(input, self, emitter, source_manager)
263 }
264
265 pub fn default_emitter(&self) -> Arc<dyn Emitter> {
267 use crate::diagnostics::{DefaultEmitter, NullEmitter};
268
269 match self.diagnostics.verbosity {
270 Verbosity::Silent => Arc::new(NullEmitter::new(self.color)),
271 _ => Arc::new(DefaultEmitter::new(self.color)),
272 }
273 }
274
275 #[inline(always)]
277 pub fn emit_source_locations(&self) -> bool {
278 matches!(self.debug, DebugInfo::Line | DebugInfo::Full)
279 }
280
281 #[inline(always)]
284 pub fn emit_debug_decorators(&self) -> bool {
285 matches!(self.debug, DebugInfo::Line | DebugInfo::Full)
286 }
287
288 #[inline(always)]
290 pub fn emit_debug_assertions(&self) -> bool {
291 self.debug != DebugInfo::None && matches!(self.optimize, OptLevel::None | OptLevel::Basic)
292 }
293
294 pub fn target_requires_protocol(&self) -> bool {
296 use miden_project::TargetType;
297 !matches!(
298 self.target_type,
299 Some(TargetType::Kernel | TargetType::Executable | TargetType::Library) | None
300 )
301 }
302}
303
304#[derive(Debug, Copy, Clone, Default)]
306#[cfg_attr(feature = "std", derive(clap::ValueEnum))]
307pub enum OptLevel {
308 None,
310 Basic,
312 #[default]
314 Balanced,
315 Max,
317 Size,
319 SizeMin,
321}
322
323#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
325#[cfg_attr(feature = "std", derive(clap::ValueEnum))]
326pub enum DebugInfo {
327 None,
329 #[default]
331 Line,
332 Full,
334}
335
336#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
338#[cfg_attr(feature = "std", derive(clap::ValueEnum))]
339pub enum Warnings {
340 None,
342 #[default]
344 All,
345 Error,
347}
348impl Warnings {
349 #[inline]
350 pub fn should_be_pedantic(&self) -> bool {
351 matches!(self, Self::All)
352 }
353
354 #[inline]
355 pub fn warnings_as_errors(&self) -> bool {
356 matches!(self, Self::Error)
357 }
358}
359impl fmt::Display for Warnings {
360 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
361 match self {
362 Self::None => f.write_str("none"),
363 Self::All => f.write_str("auto"),
364 Self::Error => f.write_str("error"),
365 }
366 }
367}
368impl FromStr for Warnings {
369 type Err = ();
370
371 fn from_str(s: &str) -> Result<Self, Self::Err> {
372 match s {
373 "none" => Ok(Self::None),
374 "all" => Ok(Self::All),
375 "error" => Ok(Self::Error),
376 _ => Err(()),
377 }
378 }
379}
380
381#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
383#[cfg_attr(feature = "std", derive(clap::ValueEnum))]
384pub enum Verbosity {
385 Debug,
387 #[default]
389 Info,
390 Warning,
392 Error,
394 Silent,
396}
397
398#[derive(Debug, Clone)]
401pub struct RemapPathPrefix {
402 pub from: Box<crate::Path>,
404 pub to: Option<Box<crate::Path>>,
408}
409
410impl RemapPathPrefix {
411 pub fn source_prefix(&self) -> &crate::Path {
412 &self.from
413 }
414
415 pub fn target_prefix(&self) -> &crate::Path {
416 self.to.as_deref().unwrap_or(crate::Path::new(""))
417 }
418}
419
420#[doc(hidden)]
422#[derive(Clone)]
423#[cfg(feature = "std")]
424pub struct RemapPathPrefixParser;
425
426#[cfg(feature = "std")]
427impl clap::builder::TypedValueParser for RemapPathPrefixParser {
428 type Value = RemapPathPrefix;
429
430 fn parse_ref(
431 &self,
432 _cmd: &clap::Command,
433 _arg: Option<&clap::Arg>,
434 value: &std::ffi::OsStr,
435 ) -> Result<Self::Value, clap::error::Error> {
436 use clap::error::{Error, ErrorKind};
437
438 let input = value.to_str().ok_or_else(|| Error::new(ErrorKind::InvalidUtf8))?;
439
440 Ok(match input.split_once('=') {
441 Some((from, to)) => RemapPathPrefix {
442 from: PathBuf::from(from.trim()).into_boxed_path(),
443 to: Some(PathBuf::from(to.trim()).into_boxed_path()),
444 },
445 None => RemapPathPrefix {
446 from: PathBuf::from(input.trim()).into_boxed_path(),
447 to: None,
448 },
449 })
450 }
451}
452
453#[cfg(feature = "std")]
454fn current_dir() -> PathBuf {
455 std::env::current_dir().expect("could not get working directory")
456}
457
458#[cfg(not(feature = "std"))]
459fn current_dir() -> PathBuf {
460 PathBuf::from(".")
461}