use std::{fs, path::PathBuf, process::Command};
use craby_common::{
config::CompleteConfig,
constants::{crate_target_dir, cxx_bridge_dir, cxx_bridge_include_dir, lib_base_name},
utils::{fs::collect_files, string::SanitizedString},
};
use log::debug;
use crate::constants::toolchain::Target;
#[derive(Clone)]
pub struct Artifacts {
pub identifier: String,
pub srcs: Vec<PathBuf>,
pub headers: Vec<PathBuf>,
pub libs: Vec<PathBuf>,
}
#[derive(PartialEq)]
pub enum ArtifactType {
Src,
Header,
Lib,
}
const CXX_SRC_EXTS: &[&str] = &["c", "cc"];
const CXX_HEADER_EXTS: &[&str] = &["h", "hh"];
impl Artifacts {
pub fn try_get_target_dir() -> Result<PathBuf, anyhow::Error> {
let res = Command::new("cargo")
.args(["metadata", "--no-deps", "--format-version", "1"])
.output()?;
if !res.status.success() {
anyhow::bail!(
"Failed to get target path: {}",
String::from_utf8_lossy(&res.stderr)
);
}
let metadata = serde_json::from_slice::<serde_json::Value>(&res.stdout)?;
let target_dir = metadata["target_directory"].as_str();
match target_dir {
Some(target_dir) => Ok(PathBuf::from(target_dir)),
None => anyhow::bail!("target_directory field not found in cargo metadata"),
}
}
pub fn get_artifacts(
config: &CompleteConfig,
target: &Target,
) -> Result<Artifacts, anyhow::Error> {
let cxx_bridge_dir = cxx_bridge_dir(&config.project_root, target.to_str());
let cxx_bridge_include_dir = cxx_bridge_include_dir(&config.project_root);
let cxx_src_filter = |path: &PathBuf| {
let ext = path.extension().unwrap_or_default();
let is_target = CXX_SRC_EXTS.contains(&ext.to_str().unwrap_or_default());
is_target
};
let cxx_header_filter = |path: &PathBuf| {
let ext = path.extension().unwrap_or_default();
let is_target = CXX_HEADER_EXTS.contains(&ext.to_str().unwrap_or_default());
is_target
};
let cxx_srcs = collect_files(&cxx_bridge_dir, &cxx_src_filter)?;
let cxx_headers = collect_files(&cxx_bridge_dir, &cxx_header_filter)?;
let cxx_bridge_headers = collect_files(&cxx_bridge_include_dir, &cxx_header_filter)?;
let target_dir = Self::try_get_target_dir()?;
let lib_name = SanitizedString::from(&config.project.name);
let lib = crate_target_dir(&target_dir, target.to_str())
.join(format!("lib{}.a", lib_base_name(&lib_name)));
debug!("cxx_srcs: {:?}", cxx_srcs);
debug!("cxx_headers: {:?}", cxx_headers);
debug!("cxx_bridge_headers: {:?}", cxx_bridge_headers);
debug!("lib: {:?}", lib);
Ok(Artifacts {
identifier: target.to_str().to_string(),
srcs: cxx_srcs,
headers: [cxx_headers, cxx_bridge_headers].concat(),
libs: vec![lib],
})
}
pub fn path_of(&self, artifact_type: ArtifactType) -> &[PathBuf] {
match artifact_type {
ArtifactType::Src => &self.srcs,
ArtifactType::Header => &self.headers,
ArtifactType::Lib => &self.libs,
}
}
pub fn copy_to(
&self,
artifact_type: ArtifactType,
dest: &PathBuf,
) -> Result<(), anyhow::Error> {
let target_artifacts = match artifact_type {
ArtifactType::Src => &self.srcs,
ArtifactType::Header => &self.headers,
ArtifactType::Lib => &self.libs,
};
if !dest.try_exists()? {
debug!("Creating destination directory: {:?}", dest);
fs::create_dir_all(dest)?;
}
for src in target_artifacts {
let file_name = src.file_name().unwrap();
let ext = src.extension().unwrap().to_string_lossy().to_string();
let dest = if artifact_type == ArtifactType::Lib {
let lib_name = file_name
.to_string_lossy()
.to_string()
.replace(&format!(".{ext}"), &format!("-prebuilt.{ext}"));
dest.join(lib_name)
} else {
dest.join(file_name)
};
debug!("Copying artifact: {:?} to {:?}", src, dest);
fs::copy(src, dest)?;
}
Ok(())
}
}