pub mod algorithm_binary;
pub mod commands;
pub mod fetch;
pub mod flash_device;
pub mod generate;
pub mod parser;
use anyhow::{ensure, Context, Result};
use clap::Parser;
use probe_rs::config::ChipFamily;
use std::{
env::current_dir,
fs::create_dir,
path::{Path, PathBuf},
};
use tracing_subscriber::EnvFilter;
use crate::commands::{
elf::{cmd_elf, serialize_to_yaml_file},
test::cmd_test,
};
use core::num::ParseIntError;
#[derive(clap::Parser)]
enum TargetGen {
Pack {
#[clap(
value_name = "INPUT",
value_parser,
help = "A Pack file or the unziped Pack directory."
)]
input: PathBuf,
#[clap(
value_name = "OUTPUT",
value_parser,
help = "An output directory where all the generated .yaml files are put in."
)]
output_dir: PathBuf,
},
Arm {
#[clap(
long = "list",
short = 'l',
help = "Optionally, list the names of all pack files available in <https://www.keil.com/pack/Keil.pidx>"
)]
list: bool,
#[clap(
long = "filter",
short = 'f',
help = "Optionally, filter the pack files that start with the specified name,\ne.g. `STM32H7xx` or `LPC55S69_DFP`.\nSee `target-gen arm --list` for a list of available Pack files"
)]
pack_filter: Option<String>,
#[clap(
name = "OUTPUT",
value_parser,
help = "An output directory where all the generated .yaml files are put in."
)]
output_dir: Option<PathBuf>,
},
Elf {
#[clap(value_parser)]
elf: PathBuf,
#[clap(long)]
fixed_load_address: bool,
#[clap(long = "name", short = 'n')]
name: Option<String>,
#[clap(long = "update", short = 'u', requires = "output")]
update: bool,
#[clap(value_parser)]
output: Option<PathBuf>,
},
Test {
template_path: PathBuf,
definition_export_path: PathBuf,
target_artifact: PathBuf,
#[clap(long = "test-address", value_parser = parse_u64)]
test_start_sector_address: Option<u64>,
},
}
pub fn parse_u64(input: &str) -> Result<u64, ParseIntError> {
parse_int::parse(input)
}
fn main() -> Result<()> {
tracing_subscriber::fmt()
.compact()
.with_env_filter(EnvFilter::from_default_env())
.init();
let options = TargetGen::parse();
let t = std::time::Instant::now();
match options {
TargetGen::Pack { input, output_dir } => cmd_pack(&input, &output_dir)?,
TargetGen::Elf {
elf,
output,
update,
name,
fixed_load_address,
} => cmd_elf(
elf.as_path(),
fixed_load_address,
output.as_deref(),
update,
name,
)?,
TargetGen::Arm {
output_dir,
pack_filter: chip_family,
list,
} => cmd_arm(output_dir, chip_family, list)?,
TargetGen::Test {
target_artifact,
template_path,
definition_export_path,
test_start_sector_address,
} => cmd_test(
target_artifact.as_path(),
template_path.as_path(),
definition_export_path.as_path(),
test_start_sector_address,
)?,
}
println!("Finished in {:?}", t.elapsed());
Ok(())
}
fn cmd_pack(input: &Path, out_dir: &Path) -> Result<()> {
ensure!(
input.exists(),
"No such file or directory: {}",
input.display()
);
if !out_dir.exists() {
create_dir(out_dir).context(format!(
"Failed to create output directory '{}'.",
out_dir.display()
))?;
}
let mut families = Vec::<ChipFamily>::new();
if input.is_file() {
generate::visit_file(input, &mut families)
.context(format!("Failed to process file {}.", input.display()))?;
} else {
generate::visit_dirs(input, &mut families)
.context("Failed to generate target configuration.")?;
ensure!(
!families.is_empty(),
"Unable to find any .pdsc files in the provided input directory."
);
}
let mut generated_files = Vec::with_capacity(families.len());
for family in &families {
let path = out_dir.join(family.name.clone().replace(' ', "_") + ".yaml");
let file = std::fs::File::create(&path)
.context(format!("Failed to create file '{}'.", path.display()))?;
serialize_to_yaml_file(family, &file)?;
generated_files.push(path);
}
println!("Generated {} target definition(s):", generated_files.len());
for file in generated_files {
println!("\t{}", file.display());
}
Ok(())
}
fn cmd_arm(out_dir: Option<PathBuf>, chip_family: Option<String>, list: bool) -> Result<()> {
if list {
let mut packs = crate::fetch::get_vidx()?;
println!("Available ARM CMSIS Pack files:");
packs.pdsc_index.sort_by(|a, b| a.name.cmp(&b.name));
for pack in packs.pdsc_index.iter() {
println!("\t{}", pack.name);
}
return Ok(());
}
let out_dir = if let Some(target_dir) = out_dir {
target_dir.as_path().to_owned()
} else {
log::info!("No output directory specified. Using current directory.");
current_dir()?
};
if !out_dir.exists() {
create_dir(&out_dir).context(format!(
"Failed to create output directory '{}'.",
out_dir.display()
))?;
}
let mut families = Vec::<ChipFamily>::new();
generate::visit_arm_files(&mut families, chip_family)?;
let mut generated_files = Vec::with_capacity(families.len());
for family in &families {
let path = out_dir.join(family.name.clone().replace(' ', "_") + ".yaml");
let file = std::fs::File::create(&path)
.context(format!("Failed to create file '{}'.", path.display()))?;
serialize_to_yaml_file(family, &file)?;
generated_files.push(path);
}
println!("Generated {} target definition(s):", generated_files.len());
for file in generated_files {
println!("\t{}", file.display());
}
Ok(())
}