pub mod common_options;
pub mod flash;
pub mod logging;
pub mod rtt;
use cargo_toml::Manifest;
use serde::Deserialize;
use thiserror::Error;
use cargo_metadata::Message;
use std::{
path::{Path, PathBuf},
process::{Command, Stdio},
};
pub use clap;
pub use indicatif;
pub use log;
#[derive(Debug, Error)]
pub enum ArtifactError {
#[error("Failed to canonicalize path '{work_dir}'.")]
Canonicalize {
#[source]
source: std::io::Error,
work_dir: String,
},
#[error("An IO error occurred during the execution of 'cargo build'.")]
Io(#[source] std::io::Error),
#[error("Unable to read the Cargo.toml at '{path}'.")]
CargoToml {
#[source]
source: std::io::Error,
path: String,
},
#[error("Failed to run cargo build: exit code = {0:?}.")]
CargoBuild(Option<i32>),
#[error("Multiple binary artifacts were found.")]
MultipleArtifacts,
#[error("No binary artifacts were found.")]
NoArtifacts,
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
pub struct Meta {
pub chip: Option<String>,
}
pub fn read_metadata(work_dir: &Path) -> Result<Meta, ArtifactError> {
let cargo_toml = work_dir.join("Cargo.toml");
let cargo_toml_content = std::fs::read(&cargo_toml).map_err(|e| ArtifactError::CargoToml {
source: e,
path: format!("{}", cargo_toml.display()),
})?;
let meta = match Manifest::<Meta>::from_slice_with_metadata(&cargo_toml_content) {
Ok(m) => m.package.and_then(|p| p.metadata),
Err(_e) => None,
};
Ok(Meta {
chip: meta.and_then(|m| m.chip),
})
}
pub struct Artifact {
path: PathBuf,
fresh: bool,
}
impl Artifact {
pub fn path(&self) -> &Path {
&self.path
}
pub fn fresh(&self) -> bool {
self.fresh
}
}
pub fn build_artifact(work_dir: &Path, args: &[String]) -> Result<Artifact, ArtifactError> {
let work_dir = dunce::canonicalize(work_dir).map_err(|e| ArtifactError::Canonicalize {
source: e,
work_dir: format!("{}", work_dir.display()),
})?;
let cargo_executable = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_owned());
log::debug!(
"Running '{}' in directory {}",
cargo_executable,
work_dir.display()
);
let cargo_command = Command::new(cargo_executable)
.current_dir(work_dir)
.arg("build")
.args(args)
.args(["--message-format", "json-diagnostic-rendered-ansi"])
.stdout(Stdio::piped())
.spawn()
.map_err(ArtifactError::Io)?;
let output = cargo_command
.wait_with_output()
.map_err(ArtifactError::Io)?;
let messages = Message::parse_stream(&output.stdout[..]);
let mut target_artifact = None;
for message in messages {
match message.map_err(ArtifactError::Io)? {
Message::CompilerArtifact(artifact) => {
if artifact.executable.is_some() {
if target_artifact.is_some() {
return Err(ArtifactError::MultipleArtifacts);
} else {
target_artifact = Some(artifact);
}
}
}
Message::CompilerMessage(message) => {
if let Some(rendered) = message.message.rendered {
print!("{rendered}");
}
}
_ => (),
}
}
if !output.status.success() {
return Err(ArtifactError::CargoBuild(output.status.code()));
}
if let Some(artifact) = target_artifact {
Ok(Artifact {
path: PathBuf::from(artifact.executable.unwrap().as_path()),
fresh: artifact.fresh,
})
} else {
Err(ArtifactError::NoArtifacts)
}
}