git.delta.rocks / fleet / refs/commits / 7bc4be6bcef6

difftreelog

source

crates/nix-eval/src/drv.rs4.7 KiBsourcehistory
1use std::collections::{HashMap, HashSet, VecDeque};2use std::ffi::CString;34use anyhow::{Result, bail};5use serde::Deserialize;67use crate::nix_raw::{derivation_free, derivation_to_json, store_drv_from_store_path};8use crate::{copy_nix_str, with_store_context};910fn store_dir() -> Result<String> {11	let mut out = String::new();12	with_store_context(|c, store, _| unsafe {13		crate::nix_raw::store_get_storedir(c, store, Some(copy_nix_str), (&raw mut out).cast())14	})?;15	Ok(out)16}1718fn to_absolute_store_path(store_dir: &str, path: &str) -> String {19	if path.starts_with('/') {20		path.to_owned()21	} else {22		format!("{store_dir}/{path}")23	}24}2526pub struct Derivation(*mut crate::nix_raw::derivation);27unsafe impl Send for Derivation {}2829impl Derivation {30	pub fn from_path(drv_path: &str) -> Result<Self> {31		let path_c = CString::new(drv_path)?;32		let store_path = with_store_context(|c, store, _| unsafe {33			crate::nix_raw::store_parse_path(c, store, path_c.as_ptr())34		})?;35		let drv = with_store_context(|c, store, _| unsafe {36			store_drv_from_store_path(c, store, store_path)37		});38		unsafe { crate::nix_raw::store_path_free(store_path) };39		let drv = drv?;40		if drv.is_null() {41			bail!("failed to read derivation from {drv_path}");42		}43		Ok(Self(drv))44	}4546	pub fn to_json_string(&self) -> Result<String> {47		let mut out = String::new();48		with_store_context(|c, _, _| unsafe {49			derivation_to_json(c, self.0, Some(copy_nix_str), (&raw mut out).cast())50		})?;51		Ok(out)52	}5354	pub fn parsed(&self) -> Result<DrvParsed> {55		let s = self.to_json_string()?;56		Ok(serde_json::from_str(&s)?)57	}58}5960impl Drop for Derivation {61	fn drop(&mut self) {62		unsafe { derivation_free(self.0) };63	}64}6566#[derive(Debug, Deserialize)]67pub struct DrvParsed {68	pub inputs: DrvInputs,69	pub outputs: HashMap<String, DrvParsedOutput>,70}7172#[derive(Debug, Deserialize)]73pub struct DrvParsedOutput {74	#[serde(default)]75	pub path: Option<String>,76}7778#[derive(Debug, Deserialize)]79pub struct DrvInputs {80	#[serde(default)]81	pub srcs: Vec<String>,82	#[serde(default)]83	pub drvs: HashMap<String, DrvInputEntry>,84}8586#[derive(Debug, Deserialize)]87pub struct DrvInputEntry {88	pub outputs: Vec<String>,89}9091#[derive(Debug, Clone)]92pub struct DrvGraph {93	pub root: String,94	pub nodes: HashMap<String, DrvNode>,95}9697#[derive(Debug, Clone)]98pub struct DrvNode {99	pub name: String,100	pub input_drvs: HashMap<String, Vec<String>>,101	pub input_srcs: Vec<String>,102	// TODO: CA outputs without a known paths are skipped103	pub outputs: HashMap<String, String>,104}105106impl DrvGraph {107	pub fn resolve(drv_path: &str) -> Result<Self> {108		let sd = store_dir()?;109		let root = to_absolute_store_path(&sd, drv_path);110111		let mut nodes = HashMap::new();112		let mut queue = VecDeque::new();113		let mut visited = HashSet::new();114		queue.push_back(root.clone());115		visited.insert(root.clone());116117		while let Some(path) = queue.pop_front() {118			let drv = Derivation::from_path(&path)?;119			let parsed = drv.parsed()?;120121			let input_drvs: HashMap<String, Vec<String>> = parsed122				.inputs123				.drvs124				.into_iter()125				.map(|(k, v)| (to_absolute_store_path(&sd, &k), v.outputs))126				.collect();127128			for dep_path in input_drvs.keys() {129				if visited.insert(dep_path.clone()) {130					queue.push_back(dep_path.clone());131				}132			}133134			let outputs: HashMap<String, String> = parsed135				.outputs136				.into_iter()137				.filter_map(|(name, out)| out.path.map(|p| (name, to_absolute_store_path(&sd, &p))))138				.collect();139140			nodes.insert(141				path.clone(),142				DrvNode {143					name: extract_drv_name(&path),144					input_drvs,145					input_srcs: parsed.inputs.srcs,146					outputs,147				},148			);149		}150151		Ok(Self { root, nodes })152	}153154	pub fn wanted_outputs(&self, root_outputs: &[String]) -> HashMap<String, Vec<String>> {155		let mut wanted: HashMap<String, HashSet<String>> = HashMap::new();156		wanted.insert(self.root.clone(), root_outputs.iter().cloned().collect());157158		let mut queue: VecDeque<String> = VecDeque::new();159		queue.push_back(self.root.clone());160		while let Some(path) = queue.pop_front() {161			let Some(node) = self.nodes.get(&path) else {162				continue;163			};164			for (dep_path, dep_outputs) in &node.input_drvs {165				let entry = wanted.entry(dep_path.clone()).or_default();166				let mut changed = false;167				for o in dep_outputs {168					if entry.insert(o.clone()) {169						changed = true;170					}171				}172				if changed {173					queue.push_back(dep_path.clone());174				}175			}176		}177178		wanted179			.into_iter()180			.map(|(k, v)| {181				let mut v: Vec<_> = v.into_iter().collect();182				v.sort();183				(k, v)184			})185			.collect()186	}187}188189fn extract_drv_name(drv_path: &str) -> String {190	drv_path191		.rsplit('/')192		.next()193		.and_then(|f| f.strip_suffix(".drv"))194		.and_then(|f| f.split_once('-').map(|(_, name)| name))195		.unwrap_or(drv_path)196		.to_owned()197}