use std::path::PathBuf;
use std::process::ExitCode;
use anyhow::Context as _;
use taktwerk_raw::fmu::build::{BuildOptions, Target, build, plan};
use crate::summary;
#[derive(Debug, clap::Args)]
pub struct Args {
pub package: PathBuf,
#[arg(short, long)]
pub output: Option<PathBuf>,
#[arg(long = "target", value_name = "ARCH")]
pub targets: Vec<String>,
#[arg(long = "bundle", value_name = "LIB")]
pub bundle: Vec<PathBuf>,
#[arg(long)]
pub write: bool,
}
pub fn run(args: &Args) -> anyhow::Result<ExitCode> {
let mut targets = args
.targets
.iter()
.map(|t| Target::parse(t))
.collect::<Result<Vec<_>, _>>()?;
if targets.is_empty() {
targets.push(Target::host()?);
}
let probe = BuildOptions {
targets: targets.clone(),
bundle: args.bundle.clone(),
output: PathBuf::new(),
};
let (descriptor, wrapper) = plan(&args.package, &probe)?;
let output = args
.output
.clone()
.unwrap_or_else(|| PathBuf::from(format!("{}.fmu", wrapper.model_identifier)));
println!(
"model {} -> {} (modelIdentifier {}, token {})",
descriptor.interface.name,
output.display(),
wrapper.model_identifier,
wrapper.token
);
println!(
"co-simulation: variable step {}, reset {}, single instance {}",
yes_no(wrapper.variable_step),
yes_no(wrapper.resettable),
yes_no(descriptor.interface.instances == taktwerk_core::model::Instances::Single)
);
if !wrapper.structural.is_empty() {
println!("\nstructural parameters");
let mut rows = vec![
["name", "vr", "start", "min", "max"]
.map(str::to_owned)
.to_vec(),
];
for s in &wrapper.structural {
rows.push(vec![
s.name.clone(),
s.vr.to_string(),
s.start.to_string(),
s.min.map_or("-".to_owned(), |v| v.to_string()),
s.max.map_or("-".to_owned(), |v| v.to_string()),
]);
}
print!("{}", summary::table(&rows));
}
println!("\nvariables");
let mut rows = vec![
["name", "vr", "type", "causality", "shape"]
.map(str::to_owned)
.to_vec(),
];
for v in &wrapper.variables {
rows.push(vec![
v.name.clone(),
v.vr.to_string(),
v.fmi_type.clone(),
v.causality.clone(),
v.shape.clone(),
]);
}
print!("{}", summary::table(&rows));
println!(
"\ntargets: {}",
targets
.iter()
.map(|t| t.platform().to_owned())
.collect::<Vec<_>>()
.join(", ")
);
if !args.bundle.is_empty() {
println!(
"bundled: {}",
args.bundle
.iter()
.map(|b| b.display().to_string())
.collect::<Vec<_>>()
.join(", ")
);
}
if !args.write {
println!("\ndry run: pass --write to build {}", output.display());
return Ok(ExitCode::SUCCESS);
}
let report = build(
&args.package,
&BuildOptions {
targets,
bundle: args.bundle.clone(),
output: output.clone(),
},
)
.with_context(|| format!("building {}", output.display()))?;
for (target, command) in &report.compiled {
eprintln!("{}: {command}", target.platform());
}
eprintln!("wrote {}", report.fmu.display());
Ok(ExitCode::SUCCESS)
}
const fn yes_no(b: bool) -> &'static str {
if b { "yes" } else { "no" }
}