use std::env;
use std::path::{Path, PathBuf};
use anyhow::anyhow;
use mun_compiler::{Config, DisplayColor, Target};
use mun_project::MANIFEST_FILENAME;
use crate::ExitStatus;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)]
pub enum UseColor {
Disable,
Enable,
Auto,
}
#[derive(clap::Args)]
pub struct Args {
#[clap(long)]
manifest_path: Option<PathBuf>,
#[clap(long, short = 'O', default_value_t = 2)]
opt_level: u8,
#[clap(long, value_enum)]
color: Option<UseColor>,
#[clap(long)]
emit_ir: bool,
#[clap(long)]
watch: bool,
#[clap(long, value_parser=parse_target_triple)]
target: Option<Target>,
}
fn parse_target_triple(target_triple: &str) -> Result<Target, String> {
Target::search(target_triple)
.ok_or_else(|| format!("could not find target for '{}'", target_triple))
}
pub fn build(args: Args) -> Result<ExitStatus, anyhow::Error> {
log::trace!("starting build");
let optimization_lvl = match args.opt_level {
0 => mun_compiler::OptimizationLevel::None,
1 => mun_compiler::OptimizationLevel::Less,
2 => mun_compiler::OptimizationLevel::Default,
3 => mun_compiler::OptimizationLevel::Aggressive,
_ => return Err(anyhow!("Only optimization levels 0-3 are supported")),
};
let display_colors = args
.color
.map(|clr| match clr {
UseColor::Disable => DisplayColor::Disable,
UseColor::Enable => DisplayColor::Enable,
UseColor::Auto => DisplayColor::Auto,
})
.or_else(|| {
env::var("MUN_TERMINAL_COLOR")
.map(|value| match value.as_str() {
"disable" => DisplayColor::Disable,
"enable" => DisplayColor::Enable,
_ => DisplayColor::Auto,
})
.ok()
})
.unwrap_or(DisplayColor::Auto);
let manifest_path = match &args.manifest_path {
None => {
let current_dir =
std::env::current_dir().expect("could not determine current working directory");
find_manifest(¤t_dir).ok_or_else(|| {
anyhow::anyhow!(
"could not find {} in '{}' or a parent directory",
MANIFEST_FILENAME,
current_dir.display()
)
})?
}
Some(path) => std::fs::canonicalize(Path::new(&path)).map_err(|_| {
anyhow::anyhow!(
"'{}' does not refer to a valid manifest path",
path.display()
)
})?,
};
log::info!("located build manifest at: {}", manifest_path.display());
let compiler_options = Config {
target: args
.target
.unwrap_or_else(|| Target::host_target().expect("unable to determine host target")),
optimization_lvl,
out_dir: None,
emit_ir: args.emit_ir,
};
if args.watch {
mun_compiler_daemon::compile_and_watch_manifest(
&manifest_path,
compiler_options,
display_colors,
)
} else {
mun_compiler::compile_manifest(&manifest_path, compiler_options, display_colors)
}
.map(Into::into)
}
fn find_manifest(directory: &Path) -> Option<PathBuf> {
let mut current_dir = Some(directory);
while let Some(dir) = current_dir {
let manifest_path = dir.join(MANIFEST_FILENAME);
if manifest_path.exists() {
return Some(manifest_path);
}
current_dir = dir.parent();
}
None
}
#[cfg(test)]
mod test {
use super::find_manifest;
use mun_project::MANIFEST_FILENAME;
#[test]
fn test_find_manifest() {
let dir = tempfile::Builder::new()
.prefix("test_find_manifest")
.tempdir()
.unwrap();
let path = dir.path();
let manifest_path = path.join(MANIFEST_FILENAME);
assert_eq!(find_manifest(path), None);
std::fs::write(&manifest_path, "").unwrap();
assert_eq!(find_manifest(path).as_ref(), Some(&manifest_path));
let subdir_path = path.join("some/random/subdir");
std::fs::create_dir_all(&subdir_path).unwrap();
assert_eq!(find_manifest(&subdir_path).as_ref(), Some(&manifest_path));
}
}