use serde::Deserialize; use std::process::Command; // We don't use cargo-metadata because it has a very unstable API! #[derive(Deserialize, Debug)] struct Metadata { workspace_members: Vec, resolve: MResolve, packages: Vec, } #[derive(Deserialize, Debug)] struct MResolve { nodes: Vec, } #[derive(Deserialize, Debug)] struct MNode { id: PackageId, dependencies: Vec, } #[derive(Deserialize, Debug)] struct MPackage { id: PackageId, version: String, name: String, } #[derive(Deserialize, Debug, Eq, PartialEq)] #[serde(transparent)] struct PackageId(String); impl Metadata { fn package(&self, id: &PackageId) -> &MPackage { self.packages.iter() .find(|p| &p.id == id).expect("missing") } } struct Context<'c> { hippo: &'c MNode, metadata: &'c Metadata, } impl Context<'_> { fn emit_for_package(&self, package: &str, versions: &[&str]) { let pkg = self.hippo.dependencies.iter() .map(|i| self.metadata.package(i)) .find(|i| i.name == package).expect("no nix in hippotat's deps"); let pkg = &pkg.version; let pkg: semver::Version = pkg.parse().expect(pkg); for test in versions { let cfg = test.replace('.', "_").replace(">=", "ge_"); let test: semver::VersionReq = test.parse().unwrap(); let cfg = format!("{package}_{cfg}"); println!(r#"cargo:rustc-check-cfg=cfg({cfg})"#); if test.matches(&pkg) { println!("cargo:rustc-cfg={cfg}"); } } } } fn main(){ let x = Command::new("cargo") .args(["metadata", "--format-version=1"]) .stderr(std::process::Stdio::inherit()) .output().unwrap(); if !x.status.success() { panic!() } let output = std::str::from_utf8(&x.stdout).unwrap(); // eprintln!("{}", output); let metadata: Metadata = serde_json::from_str(&output).unwrap(); let hippo = metadata.workspace_members.iter() .map(|i| metadata.package(i)) .find(|p| p.name == "hippotat").expect("no hippotat in workspace?"); let hippo = metadata.resolve.nodes.iter() .find(|n| n.id == hippo.id).expect("no hippotat in nodes!"); let ctx = Context { hippo, metadata: &metadata }; ctx.emit_for_package("nix", &[">=0.27", ">=0.28"]); }