git.delta.rocks / fleet / refs/commits / 12bc0d4f3eb3

difftreelog

source

crates/nix-eval/src/drv.rs4.5 KiBsourcehistory
1use std::collections::{HashMap, HashSet, VecDeque};23use anyhow::{Result, bail};4use camino::{Utf8Component, Utf8Path, Utf8PathBuf};5use serde::Deserialize;67use crate::nix_raw::{derivation_free, derivation_to_json, store_drv_from_store_path};8use crate::{Store, copy_nix_str, with_default_context};910pub struct Derivation(*mut crate::nix_raw::derivation);11unsafe impl Send for Derivation {}1213impl Derivation {14	pub fn from_path(store: &Store, drv_path: &Utf8Path) -> Result<Self> {15		let store_path = store.parse_path(drv_path)?;16		let drv = with_default_context(|c, _| unsafe {17			store_drv_from_store_path(c, store.as_ptr(), store_path.as_ptr())18		});19		let drv = drv?;20		if drv.is_null() {21			bail!("failed to read derivation from {drv_path}");22		}23		Ok(Self(drv))24	}2526	pub fn to_json_string(&self) -> Result<String> {27		let mut out = String::new();28		with_default_context(|c, _| unsafe {29			derivation_to_json(c, self.0, Some(copy_nix_str), (&raw mut out).cast())30		})?;31		Ok(out)32	}3334	pub fn parsed(&self) -> Result<DrvParsed> {35		let s = self.to_json_string()?;36		Ok(serde_json::from_str(&s)?)37	}38}3940impl Drop for Derivation {41	fn drop(&mut self) {42		unsafe { derivation_free(self.0) };43	}44}4546#[derive(Debug, Deserialize)]47pub struct DrvParsed {48	pub inputs: DrvInputs,49	pub outputs: HashMap<String, DrvParsedOutput>,50}5152#[derive(Debug, Deserialize)]53pub struct DrvParsedOutput {54	#[serde(default)]55	pub path: Option<String>,56}5758#[derive(Debug, Deserialize)]59pub struct DrvInputs {60	#[serde(default)]61	pub srcs: Vec<Utf8PathBuf>,62	#[serde(default)]63	pub drvs: HashMap<Utf8PathBuf, DrvInputEntry>,64}6566#[derive(Debug, Deserialize)]67pub struct DrvInputEntry {68	pub outputs: Vec<String>,69}7071#[derive(Debug, Clone)]72pub struct DrvGraph {73	pub root: Utf8PathBuf,74	pub nodes: HashMap<Utf8PathBuf, DrvNode>,75}7677#[derive(Debug, Clone)]78pub struct DrvNode {79	pub name: String,80	pub input_drvs: HashMap<Utf8PathBuf, Vec<String>>,81	pub input_srcs: Vec<Utf8PathBuf>,82	// TODO: CA outputs without a known paths are skipped83	pub outputs: HashMap<String, Utf8PathBuf>,84}8586impl DrvGraph {87	pub fn resolve(store: &Store, drv_path: &Utf8Path) -> Result<Self> {88		let sd = store.store_dir()?;89		let root = sd.join(drv_path);9091		let mut nodes = HashMap::new();92		let mut queue = VecDeque::new();93		let mut visited = HashSet::new();94		queue.push_back(root.clone());95		visited.insert(root.clone());9697		while let Some(path) = queue.pop_front() {98			let drv = Derivation::from_path(store, &path)?;99			let parsed = drv.parsed()?;100101			let input_drvs: HashMap<Utf8PathBuf, Vec<String>> = parsed102				.inputs103				.drvs104				.into_iter()105				.map(|(k, v)| (sd.join(&k), v.outputs))106				.collect();107			let input_srcs: Vec<Utf8PathBuf> = parsed108				.inputs109				.srcs110				.into_iter()111				.map(|k| sd.join(&k))112				.collect();113114			for dep_path in input_drvs.keys() {115				if visited.insert(dep_path.clone()) {116					queue.push_back(dep_path.clone());117				}118			}119120			let outputs: HashMap<String, Utf8PathBuf> = parsed121				.outputs122				.into_iter()123				.filter_map(|(name, out)| out.path.map(|p| (name, sd.join(&p))))124				.collect();125126			nodes.insert(127				path.clone(),128				DrvNode {129					name: extract_drv_name(&path),130					input_drvs,131					input_srcs,132					outputs,133				},134			);135		}136137		Ok(Self { root, nodes })138	}139140	pub fn wanted_outputs(&self, root_outputs: &[String]) -> HashMap<Utf8PathBuf, Vec<String>> {141		let mut wanted: HashMap<Utf8PathBuf, HashSet<String>> = HashMap::new();142		wanted.insert(self.root.clone(), root_outputs.iter().cloned().collect());143144		let mut queue: VecDeque<Utf8PathBuf> = VecDeque::new();145		queue.push_back(self.root.clone());146		while let Some(path) = queue.pop_front() {147			let Some(node) = self.nodes.get(&path) else {148				continue;149			};150			for (dep_path, dep_outputs) in &node.input_drvs {151				let entry = wanted.entry(dep_path.clone()).or_default();152				let mut changed = false;153				for o in dep_outputs {154					if entry.insert(o.clone()) {155						changed = true;156					}157				}158				if changed {159					queue.push_back(dep_path.clone());160				}161			}162		}163164		wanted165			.into_iter()166			.map(|(k, v)| {167				let mut v: Vec<_> = v.into_iter().collect();168				v.sort();169				(k, v)170			})171			.collect()172	}173}174175pub fn extract_drv_name(drv_path: &Utf8Path) -> String {176	let comp = drv_path177		.components()178		.rev()179		.next()180		.expect("drv path is at least one component");181	let Utf8Component::Normal(n) = comp else {182		panic!("drv path is normal");183	};184185	let n = n.strip_suffix(".drv").unwrap_or(n);186187	let n = n.split_once(' ').map(|(_, n)| n).unwrap_or(n);188189	n.to_owned()190}