use core::panic;
use std::path::PathBuf;
use std::process::{Command, Stdio};
use cargo_metadata::{Message, MetadataCommand};
#[derive(Clone, Debug)]
pub struct MetaData {
pub pkg_name: String,
pub lib_name: String,
pub pkg_path: PathBuf,
pub workspace_dir: PathBuf,
}
impl MetaData {
pub fn strip_workspace_path(&self, path: &PathBuf) -> PathBuf {
path.strip_prefix(&self.workspace_dir)
.expect("pkg path is not a prefix of workspace root path")
.to_path_buf()
}
}
pub struct Cargo;
impl Cargo {
pub fn precheck() {
}
pub fn get_metadata() -> MetaData {
let cmd = MetadataCommand::new();
let metadata = cmd.exec().expect("failed to execute `cargo metadata`");
let root_pkg = metadata
.root_package()
.expect("failed to retrieve root package name");
let dylib_target = root_pkg.targets.iter().find(|target| {
if let Some(kind) = target.kind.first() {
if kind.as_str() == "cdylib" {
if let Some(crate_type) = target.crate_types.first() {
if crate_type.as_str() == "cdylib" {
return true;
}
}
}
}
false
});
let pkg_name;
let lib_name;
if let Some(dylib_target) = dylib_target {
pkg_name = root_pkg.name.clone();
lib_name = dylib_target.name.clone();
} else {
panic!(
"no `cdylib` library target found in package `{}`",
root_pkg.name
);
}
let mut pkg_path = PathBuf::from(root_pkg.manifest_path.clone());
pkg_path.pop();
MetaData {
pkg_name,
lib_name,
pkg_path,
workspace_dir: PathBuf::from(metadata.workspace_root.clone()),
}
}
pub fn expand(pkg_name: &str) -> String {
let mut cmd = Command::new("cargo");
cmd.arg("rustc")
.arg("--package")
.arg(pkg_name)
.arg("--lib")
.arg("--")
.env("RUSTC_BOOTSTRAP", "1")
.stderr(Stdio::inherit());
const UNSTABLE_FLAGS: &[&str] = &["unstable-options", "unpretty=expanded", "no-codegen"];
for unstable_flag in UNSTABLE_FLAGS {
cmd.arg(format!("-Z{}", unstable_flag));
}
let output = cmd.output().expect("failed to start `cargo rustc` process");
let content =
String::from_utf8(output.stdout).expect("failed to parse `rustc` expansion output");
content
}
pub fn build(pkg_name: &str, release: bool, mut cfgs: Vec<&str>) -> PathBuf {
let mut cmd = Command::new("cargo");
cmd
.arg("build")
.arg("--package")
.arg(pkg_name)
.arg("--lib")
.arg("--message-format=json")
.stderr(Stdio::inherit())
.stdout(Stdio::piped());
cfgs.push("deno_bindgen");
let cfgs = cfgs
.iter()
.map(|cfg| format!("--cfg {} ", cfg))
.collect::<String>();
cmd.env("RUSTFLAGS", cfgs);
if release {
cmd.arg("--release");
}
let output = cmd.output().expect("failed to start `cargo build` process");
let dylib_path;
if !output.status.success() {
panic!(
"failed to execute `cargo`: process exited with {}\nfull command: {:?}",
output.status, cmd
)
} else {
let cargo_out = std::io::BufReader::new(output.stdout.as_slice());
let mut artifact_paths = Vec::new();
for msg in Message::parse_stream(cargo_out) {
if let Err(err) = msg {
panic!("failed to parse cargo output: {:?}", err);
}
match msg.unwrap() {
Message::CompilerArtifact(artifact) => {
if artifact.target.kind.contains(&"cdylib".to_string()) {
let path = artifact.filenames[0].to_string();
artifact_paths.push(PathBuf::from(path));
}
},
_ => (),
}
}
if let Some(path) = artifact_paths.pop() {
#[cfg(target_os = "windows")]
let dylib_path = path
.strip_prefix(&cwd)
.expect("path is not a prefix of cwd");
dylib_path = path;
} else {
panic!(
"failed to retrieve cargo build artifacts\nis your library set to the `cdylib` type?"
)
}
}
dylib_path
}
}
#[cfg(test)]
mod tests {
use super::*;
fn test_get_metadata() {
let metadata = Cargo::get_metadata();
dbg!(&metadata);
}
fn test_expand() {
let metadata = Cargo::get_metadata();
let content = Cargo::expand(metadata.pkg_name.as_str());
println!("{content}");
}
fn test_build() {
let metadata = Cargo::get_metadata();
let dylib_path = Cargo::build(&metadata.pkg_name, false, vec![]);
dbg!(&dylib_path);
}
}