mod printing;
use alloc::{
boxed::Box,
fmt,
str::FromStr,
string::{String, ToString},
sync::Arc,
vec,
vec::Vec,
};
use miden_debug_types::SourceManager;
use miden_project::TargetType;
pub use self::printing::IrFilter;
use crate::{
ColorChoice, CompileFlags, InputFile, LinkLibrary, OutputFile, OutputTypes, PathBuf,
diagnostics::{DiagnosticsConfig, Emitter, Report},
};
#[derive(Debug, Clone)]
pub struct Options {
pub manifest_path: Option<PathBuf>,
pub name: Option<String>,
pub entrypoint: Option<String>,
pub profile: String,
pub workspace: bool,
pub packages: Vec<String>,
pub target: Option<String>,
pub target_type: Option<TargetType>,
pub optimize: OptLevel,
pub debug: DebugInfo,
pub output_types: OutputTypes,
pub search_paths: Vec<PathBuf>,
pub link_libraries: Vec<LinkLibrary>,
pub link_modules: Vec<(miden_assembly_syntax::PathBuf, String)>,
pub sysroot: Option<PathBuf>,
pub midenup_home: Option<PathBuf>,
pub toolchain: Option<String>,
pub color: ColorChoice,
pub diagnostics: DiagnosticsConfig,
pub current_dir: PathBuf,
pub target_dir: PathBuf,
pub output_dir: Option<PathBuf>,
pub output_file: Option<OutputFile>,
pub remap_path_prefixes: Vec<RemapPathPrefix>,
pub print_hir_source_locations: bool,
pub parse_only: bool,
pub analyze_only: bool,
pub link_only: bool,
pub no_link: bool,
pub lint: bool,
pub print_cfg_after_all: bool,
pub print_cfg_after_pass: Vec<String>,
pub print_ir_before_stage: Vec<String>,
pub print_ir_after_all: bool,
pub print_ir_after_pass: Vec<String>,
pub print_ir_after_modified: bool,
pub print_ir_filters: Vec<IrFilter>,
pub save_temps: bool,
pub rustflags: Option<String>,
pub cargo_frontmatter: bool,
pub flags: CompileFlags,
}
impl Default for Options {
fn default() -> Self {
let current_dir = current_dir();
let target_dir = current_dir.join("target");
Self::new(None, None, current_dir, target_dir, None, None)
}
}
impl Options {
pub fn new(
name: Option<String>,
target: Option<TargetType>,
current_dir: PathBuf,
target_dir: PathBuf,
output_dir: Option<PathBuf>,
sysroot: Option<PathBuf>,
) -> Self {
let search_paths = if let Some(sysroot) = sysroot.as_deref() {
let lib_dir = sysroot.join("lib");
if lib_dir.try_exists().is_ok_and(|exists| exists) {
vec![lib_dir]
} else {
vec![]
}
} else {
vec![]
};
Self {
manifest_path: None,
name,
profile: "dev".to_string(),
workspace: false,
packages: vec![],
target: None,
target_type: target,
entrypoint: None,
optimize: OptLevel::None,
debug: DebugInfo::None,
output_types: Default::default(),
search_paths,
link_libraries: vec![],
link_modules: vec![],
sysroot,
midenup_home: None,
toolchain: None,
color: Default::default(),
diagnostics: Default::default(),
current_dir,
target_dir,
output_dir,
output_file: None,
print_hir_source_locations: false,
parse_only: false,
analyze_only: false,
link_only: false,
no_link: false,
save_temps: false,
lint: false,
cargo_frontmatter: false,
print_cfg_after_all: false,
print_cfg_after_pass: vec![],
print_ir_before_stage: vec![],
print_ir_after_all: false,
print_ir_after_pass: vec![],
print_ir_after_modified: false,
print_ir_filters: vec![],
rustflags: None,
remap_path_prefixes: vec![],
flags: CompileFlags::default(),
}
}
#[inline(always)]
pub fn with_color(mut self: Box<Self>, color: ColorChoice) -> Box<Self> {
self.color = color;
self
}
#[inline(always)]
pub fn with_verbosity(mut self: Box<Self>, verbosity: Verbosity) -> Box<Self> {
self.diagnostics.verbosity = verbosity;
self
}
#[inline(always)]
pub fn with_debug_info(mut self: Box<Self>, debug: DebugInfo) -> Box<Self> {
self.debug = debug;
self
}
#[inline(always)]
pub fn with_optimization(mut self: Box<Self>, level: OptLevel) -> Box<Self> {
self.optimize = level;
self
}
pub fn with_warnings(mut self: Box<Self>, warnings: Warnings) -> Box<Self> {
self.diagnostics.warnings = warnings;
self
}
pub fn with_output_types(
mut self: Box<Self>,
mut output_types: OutputTypes,
output_file: Option<OutputFile>,
) -> Box<Self> {
use crate::OutputType;
let has_final_output = output_types.keys().any(|ty| matches!(ty, OutputType::Masp));
if !has_final_output {
output_types.insert(OutputType::Masp, output_file);
} else if output_file.is_some() && output_types.get(&OutputType::Masp).is_some() {
output_types.insert(OutputType::Masp, output_file);
}
self.output_types = output_types;
self
}
#[doc(hidden)]
pub fn with_extra_flags(mut self: Box<Self>, flags: CompileFlags) -> Box<Self> {
self.flags = flags;
self
}
#[doc(hidden)]
pub fn set_extra_flags(&mut self, flags: CompileFlags) {
self.flags = flags;
}
pub fn into_session(
self: Box<Self>,
input: InputFile,
emitter: Option<Arc<dyn Emitter>>,
source_manager: Option<Arc<dyn SourceManager + Send + Sync>>,
) -> Result<crate::Session, Report> {
use crate::diagnostics::DefaultSourceManager;
let source_manager =
source_manager.unwrap_or_else(|| Arc::new(DefaultSourceManager::default()));
crate::Session::new(input, self, emitter, source_manager)
}
pub fn default_emitter(&self) -> Arc<dyn Emitter> {
use crate::diagnostics::{DefaultEmitter, NullEmitter};
match self.diagnostics.verbosity {
Verbosity::Silent => Arc::new(NullEmitter::new(self.color)),
_ => Arc::new(DefaultEmitter::new(self.color)),
}
}
#[inline(always)]
pub fn emit_source_locations(&self) -> bool {
matches!(self.debug, DebugInfo::Line | DebugInfo::Full)
}
#[inline(always)]
pub fn emit_debug_decorators(&self) -> bool {
matches!(self.debug, DebugInfo::Line | DebugInfo::Full)
}
#[inline(always)]
pub fn emit_debug_assertions(&self) -> bool {
self.debug != DebugInfo::None && matches!(self.optimize, OptLevel::None | OptLevel::Basic)
}
pub fn target_requires_protocol(&self) -> bool {
use miden_project::TargetType;
!matches!(
self.target_type,
Some(TargetType::Kernel | TargetType::Executable | TargetType::Library) | None
)
}
}
#[derive(Debug, Copy, Clone, Default)]
#[cfg_attr(feature = "std", derive(clap::ValueEnum))]
pub enum OptLevel {
None,
Basic,
#[default]
Balanced,
Max,
Size,
SizeMin,
}
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(clap::ValueEnum))]
pub enum DebugInfo {
None,
#[default]
Line,
Full,
}
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(clap::ValueEnum))]
pub enum Warnings {
None,
#[default]
All,
Error,
}
impl Warnings {
#[inline]
pub fn should_be_pedantic(&self) -> bool {
matches!(self, Self::All)
}
#[inline]
pub fn warnings_as_errors(&self) -> bool {
matches!(self, Self::Error)
}
}
impl fmt::Display for Warnings {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::None => f.write_str("none"),
Self::All => f.write_str("auto"),
Self::Error => f.write_str("error"),
}
}
}
impl FromStr for Warnings {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"none" => Ok(Self::None),
"all" => Ok(Self::All),
"error" => Ok(Self::Error),
_ => Err(()),
}
}
}
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "std", derive(clap::ValueEnum))]
pub enum Verbosity {
Debug,
#[default]
Info,
Warning,
Error,
Silent,
}
#[derive(Debug, Clone)]
pub struct RemapPathPrefix {
pub from: Box<crate::Path>,
pub to: Option<Box<crate::Path>>,
}
impl RemapPathPrefix {
pub fn source_prefix(&self) -> &crate::Path {
&self.from
}
pub fn target_prefix(&self) -> &crate::Path {
self.to.as_deref().unwrap_or(crate::Path::new(""))
}
}
#[doc(hidden)]
#[derive(Clone)]
#[cfg(feature = "std")]
pub struct RemapPathPrefixParser;
#[cfg(feature = "std")]
impl clap::builder::TypedValueParser for RemapPathPrefixParser {
type Value = RemapPathPrefix;
fn parse_ref(
&self,
_cmd: &clap::Command,
_arg: Option<&clap::Arg>,
value: &std::ffi::OsStr,
) -> Result<Self::Value, clap::error::Error> {
use clap::error::{Error, ErrorKind};
let input = value.to_str().ok_or_else(|| Error::new(ErrorKind::InvalidUtf8))?;
Ok(match input.split_once('=') {
Some((from, to)) => RemapPathPrefix {
from: PathBuf::from(from.trim()).into_boxed_path(),
to: Some(PathBuf::from(to.trim()).into_boxed_path()),
},
None => RemapPathPrefix {
from: PathBuf::from(input.trim()).into_boxed_path(),
to: None,
},
})
}
}
#[cfg(feature = "std")]
fn current_dir() -> PathBuf {
std::env::current_dir().expect("could not get working directory")
}
#[cfg(not(feature = "std"))]
fn current_dir() -> PathBuf {
PathBuf::from(".")
}