blob: fb80072c8f24c917cdac97d5b0a556a2d0fc6a53 [file] [log] [blame]
use std::collections::BTreeMap;
use std::env;
use std::ffi::OsString;
use std::path::PathBuf;
#[derive(Default)]
pub struct Crate {
pub include_prefix: Option<PathBuf>,
pub links: Option<OsString>,
pub header_dirs: Vec<HeaderDir>,
}
pub struct HeaderDir {
pub exported: bool,
pub path: PathBuf,
}
impl Crate {
pub fn print_to_cargo(&self) {
if let Some(include_prefix) = &self.include_prefix {
println!(
"cargo:CXXBRIDGE_PREFIX={}",
include_prefix.to_string_lossy(),
);
}
if let Some(links) = &self.links {
println!("cargo:CXXBRIDGE_LINKS={}", links.to_string_lossy());
}
for (i, header_dir) in self.header_dirs.iter().enumerate() {
if header_dir.exported {
println!(
"cargo:CXXBRIDGE_DIR{}={}",
i,
header_dir.path.to_string_lossy(),
);
}
}
}
}
pub fn direct_dependencies() -> Vec<Crate> {
let mut crates: BTreeMap<String, Crate> = BTreeMap::new();
let mut exported_header_dirs: BTreeMap<String, Vec<(usize, PathBuf)>> = BTreeMap::new();
// Only variables set from a build script of direct dependencies are
// observable. That's exactly what we want! Your crate needs to declare a
// direct dependency on the other crate in order to be able to #include its
// headers.
//
// Also, they're only observable if the dependency's manifest contains a
// `links` key. This is important because Cargo imposes no ordering on the
// execution of build scripts without a `links` key. When exposing a
// generated header for the current crate to #include, we need to be sure
// the dependency's build script has already executed and emitted that
// generated header.
//
// References:
// - https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key
// - https://doc.rust-lang.org/cargo/reference/build-script-examples.html#using-another-sys-crate
for (k, v) in env::vars_os() {
let mut k = k.to_string_lossy().into_owned();
if !k.starts_with("DEP_") {
continue;
}
if k.ends_with("_CXXBRIDGE_PREFIX") {
k.truncate(k.len() - "_CXXBRIDGE_PREFIX".len());
crates.entry(k).or_default().include_prefix = Some(PathBuf::from(v));
continue;
}
if k.ends_with("_CXXBRIDGE_LINKS") {
k.truncate(k.len() - "_CXXBRIDGE_LINKS".len());
crates.entry(k).or_default().links = Some(v);
continue;
}
let without_counter = k.trim_end_matches(|ch: char| ch.is_ascii_digit());
let counter_len = k.len() - without_counter.len();
if counter_len == 0 || !without_counter.ends_with("_CXXBRIDGE_DIR") {
continue;
}
let sort_key = k[k.len() - counter_len..]
.parse::<usize>()
.unwrap_or(usize::MAX);
k.truncate(k.len() - counter_len - "_CXXBRIDGE_DIR".len());
exported_header_dirs
.entry(k)
.or_default()
.push((sort_key, PathBuf::from(v)));
}
for (k, mut dirs) in exported_header_dirs {
dirs.sort_by_key(|(sort_key, _dir)| *sort_key);
crates
.entry(k)
.or_default()
.header_dirs
.extend(dirs.into_iter().map(|(_sort_key, dir)| HeaderDir {
exported: true,
path: dir,
}));
}
crates.into_iter().map(|entry| entry.1).collect()
}