diff options
Diffstat (limited to 'tvix/glue')
-rw-r--r-- | tvix/glue/benches/eval.rs | 17 | ||||
-rw-r--r-- | tvix/glue/src/builtins/derivation.rs | 127 | ||||
-rw-r--r-- | tvix/glue/src/known_paths.rs | 131 | ||||
-rw-r--r-- | tvix/glue/src/tvix_io.rs | 19 |
4 files changed, 80 insertions, 214 deletions
diff --git a/tvix/glue/benches/eval.rs b/tvix/glue/benches/eval.rs index a466a0e0fcfa..f5c9813c9063 100644 --- a/tvix/glue/benches/eval.rs +++ b/tvix/glue/benches/eval.rs @@ -29,7 +29,7 @@ fn interpret(code: &str) { let mut eval = tvix_eval::Evaluation::new_impure(); let known_paths: Rc<RefCell<KnownPaths>> = Default::default(); - add_derivation_builtins(&mut eval, known_paths.clone()); + add_derivation_builtins(&mut eval, known_paths); configure_nix_path( &mut eval, // The benchmark requires TVIX_BENCH_NIX_PATH to be set, so barf out @@ -37,15 +37,12 @@ fn interpret(code: &str) { &Some(env::var("TVIX_BENCH_NIX_PATH").expect("TVIX_BENCH_NIX_PATH must be set")), ); - eval.io_handle = Box::new(tvix_glue::tvix_io::TvixIO::new( - known_paths.clone(), - TvixStoreIO::new( - BLOB_SERVICE.clone(), - DIRECTORY_SERVICE.clone(), - PATH_INFO_SERVICE.clone(), - TOKIO_RUNTIME.handle().clone(), - ), - )); + eval.io_handle = Box::new(tvix_glue::tvix_io::TvixIO::new(TvixStoreIO::new( + BLOB_SERVICE.clone(), + DIRECTORY_SERVICE.clone(), + PATH_INFO_SERVICE.clone(), + TOKIO_RUNTIME.handle().clone(), + ))); let result = eval.evaluate(code, None); diff --git a/tvix/glue/src/builtins/derivation.rs b/tvix/glue/src/builtins/derivation.rs index 95314f4b9dc9..517ea0032180 100644 --- a/tvix/glue/src/builtins/derivation.rs +++ b/tvix/glue/src/builtins/derivation.rs @@ -1,6 +1,6 @@ //! Implements `builtins.derivation`, the core of what makes Nix build packages. use crate::builtins::DerivationError; -use crate::known_paths::{KnownPaths, PathKind, PathName}; +use crate::known_paths::KnownPaths; use nix_compat::derivation::{Derivation, Output}; use nix_compat::nixhash; use std::cell::RefCell; @@ -9,7 +9,8 @@ use std::rc::Rc; use tvix_eval::builtin_macros::builtins; use tvix_eval::generators::{self, emit_warning_kind, GenCo}; use tvix_eval::{ - AddContext, CatchableErrorKind, CoercionKind, ErrorKind, NixAttrs, NixList, Value, WarningKind, + AddContext, CatchableErrorKind, CoercionKind, ErrorKind, NixAttrs, NixContext, + NixContextElement, NixList, Value, WarningKind, }; // Constants used for strangely named fields in derivation inputs. @@ -46,20 +47,15 @@ async fn populate_outputs( } /// Populate the inputs of a derivation from the build references -/// found when scanning the derivation's parameters. -fn populate_inputs<I: IntoIterator<Item = PathName>>( - drv: &mut Derivation, - known_paths: &KnownPaths, - references: I, -) { - for reference in references.into_iter() { - let reference = &known_paths[&reference]; - match &reference.kind { - PathKind::Plain => { - drv.input_sources.insert(reference.path.clone()); +/// found when scanning the derivation's parameters and extracting their contexts. +fn populate_inputs(drv: &mut Derivation, full_context: NixContext) { + for element in full_context.iter() { + match element { + NixContextElement::Plain(source) => { + drv.input_sources.insert(source.clone()); } - PathKind::Output { name, derivation } => { + NixContextElement::Single { name, derivation } => { match drv.input_derivations.entry(derivation.clone()) { btree_map::Entry::Vacant(entry) => { entry.insert(BTreeSet::from([name.clone()])); @@ -71,16 +67,12 @@ fn populate_inputs<I: IntoIterator<Item = PathName>>( } } - PathKind::Derivation { output_names } => { - match drv.input_derivations.entry(reference.path.clone()) { - btree_map::Entry::Vacant(entry) => { - entry.insert(output_names.clone()); - } - - btree_map::Entry::Occupied(mut entry) => { - entry.get_mut().extend(output_names.clone().into_iter()); - } - } + NixContextElement::Derivation(_drv_path) => { + // This is a hard one, it means that + // we are depending on a drvPath of ourselves + // *or* another derivation's drvPath. + // What to do here? + panic!("please do not depend on drvPath, I have 2 hours of sleep in blood"); } } } @@ -232,6 +224,7 @@ pub(crate) mod derivation_builtins { use super::*; use nix_compat::store_path::hash_placeholder; use tvix_eval::generators::Gen; + use tvix_eval::{NixContext, NixContextElement, NixString}; #[builtin("placeholder")] async fn builtin_placeholder(co: GenCo, input: Value) -> Result<Value, ErrorKind> { @@ -299,15 +292,31 @@ pub(crate) mod derivation_builtins { Ok(Ok(None)) } + let mut input_context = NixContext::new(); + for (name, value) in input.clone().into_iter_sorted() { let value = generators::request_force(&co, value).await; if ignore_nulls && matches!(value, Value::Null) { continue; } - match strong_importing_coerce_to_string(&co, value.clone()).await? { + match generators::request_string_coerce( + &co, + value.clone(), + CoercionKind { + strong: true, + import_paths: true, + }, + ) + .await + { Err(cek) => return Ok(Value::Catchable(cek)), Ok(val_str) => { + // Learn about this derivation references + // by looking at its context. + input_context.mimic(&val_str); + + let val_str = val_str.as_str().to_string(); // handle_derivation_parameters tells us whether the // argument should be added to the environment; continue // to the next one otherwise @@ -370,21 +379,6 @@ pub(crate) mod derivation_builtins { } } - // Scan references in relevant attributes to detect any build-references. - let references = { - let state = state.borrow(); - if state.is_empty() { - // skip reference scanning, create an empty result - Default::default() - } else { - let mut refscan = state.reference_scanner(); - drv.arguments.iter().for_each(|s| refscan.scan(s)); - drv.environment.values().for_each(|s| refscan.scan(s)); - refscan.scan(&drv.builder); - refscan.finalise() - } - }; - // Each output name needs to exist in the environment, at this // point initialised as an empty string because that is the // way of Golang ;) @@ -398,8 +392,8 @@ pub(crate) mod derivation_builtins { } } + populate_inputs(&mut drv, input_context); let mut known_paths = state.borrow_mut(); - populate_inputs(&mut drv, &known_paths, references); // At this point, derivation fields are fully populated from // eval data structures. @@ -428,25 +422,35 @@ pub(crate) mod derivation_builtins { &derivation_or_fod_hash_final, ); - // mark all the new paths as known - let output_names: Vec<String> = drv.outputs.keys().map(Clone::clone).collect(); - known_paths.drv(derivation_path.to_absolute_path(), &output_names); - - for (output_name, output) in &drv.outputs { - known_paths.output( - &output.path, - output_name, - derivation_path.to_absolute_path(), - ); - } - - let mut new_attrs: Vec<(String, String)> = drv + let mut new_attrs: Vec<(String, NixString)> = drv .outputs .into_iter() - .map(|(name, output)| (name, output.path)) + .map(|(name, output)| { + ( + name.clone(), + ( + output.path, + Some( + NixContextElement::Single { + name, + derivation: derivation_path.to_absolute_path(), + } + .into(), + ), + ) + .into(), + ) + }) .collect(); - new_attrs.push(("drvPath".to_string(), derivation_path.to_absolute_path())); + new_attrs.push(( + "drvPath".to_string(), + ( + derivation_path.to_absolute_path(), + Some(NixContextElement::Derivation(derivation_path.to_absolute_path()).into()), + ) + .into(), + )); Ok(Value::Attrs(Box::new(NixAttrs::from_iter( new_attrs.into_iter(), @@ -454,12 +458,7 @@ pub(crate) mod derivation_builtins { } #[builtin("toFile")] - async fn builtin_to_file( - state: Rc<RefCell<KnownPaths>>, - co: GenCo, - name: Value, - content: Value, - ) -> Result<Value, ErrorKind> { + async fn builtin_to_file(co: GenCo, name: Value, content: Value) -> Result<Value, ErrorKind> { let name = name .to_str() .context("evaluating the `name` parameter of builtins.toFile")?; @@ -482,11 +481,11 @@ pub(crate) mod derivation_builtins { .map_err(DerivationError::InvalidDerivation)? .to_absolute_path(); - state.borrow_mut().plain(&path); + let context: NixContext = NixContextElement::Plain(path.clone()).into(); // TODO: actually persist the file in the store at that path ... - Ok(Value::String(path.into())) + Ok(Value::String(NixString::new_context_from(context, &path))) } } diff --git a/tvix/glue/src/known_paths.rs b/tvix/glue/src/known_paths.rs index a01f8007ebd7..b847ccada397 100644 --- a/tvix/glue/src/known_paths.rs +++ b/tvix/glue/src/known_paths.rs @@ -11,12 +11,9 @@ //! Please see //tvix/eval/docs/build-references.md for more //! information. -use crate::refscan::{ReferenceScanner, STORE_PATH_LEN}; +use crate::refscan::STORE_PATH_LEN; use nix_compat::nixhash::NixHash; -use std::{ - collections::{hash_map, BTreeSet, HashMap}, - ops::Index, -}; +use std::collections::{BTreeSet, HashMap}; #[derive(Debug, PartialEq)] pub enum PathKind { @@ -36,12 +33,6 @@ pub struct KnownPath { pub kind: PathKind, } -impl KnownPath { - fn new(path: String, kind: PathKind) -> Self { - KnownPath { path, kind } - } -} - /// Internal struct to prevent accidental leaks of the truncated path /// names. #[repr(transparent)] @@ -54,6 +45,12 @@ impl From<&str> for PathName { } } +impl From<String> for PathName { + fn from(s: String) -> Self { + s.as_str().into() + } +} + /// This instance is required to pass PathName instances as needles to /// the reference scanner. impl AsRef<[u8]> for PathName { @@ -64,125 +61,13 @@ impl AsRef<[u8]> for PathName { #[derive(Debug, Default)] pub struct KnownPaths { - /// All known paths, keyed by a truncated version of their store - /// path used for reference scanning. - paths: HashMap<PathName, KnownPath>, - /// All known derivation or FOD hashes. /// /// Keys are derivation paths, values is the NixHash. derivation_or_fod_hashes: HashMap<String, NixHash>, } -impl Index<&PathName> for KnownPaths { - type Output = KnownPath; - - fn index(&self, index: &PathName) -> &Self::Output { - &self.paths[index] - } -} - impl KnownPaths { - fn insert_path(&mut self, path: String, path_kind: PathKind) { - match self.paths.entry(path.as_str().into()) { - hash_map::Entry::Vacant(entry) => { - entry.insert(KnownPath::new(path, path_kind)); - } - - hash_map::Entry::Occupied(mut entry) => { - match (path_kind, &mut entry.get_mut().kind) { - // These variant combinations require no "merging action". - (PathKind::Plain, PathKind::Plain) => (), - - #[allow(unused_variables)] - ( - PathKind::Output { - name: name1, - derivation: drv1, - }, - PathKind::Output { - name: ref name2, - derivation: ref drv2, - }, - ) => { - #[cfg(debug_assertions)] - { - if &name1 != name2 { - panic!( - "inserted path {} with two different names: {} and {}", - path, name1, name2 - ); - } - if &drv1 != drv2 { - panic!( - "inserted path {} with two different derivations: {} and {}", - path, drv1, drv2 - ); - } - } - } - - ( - PathKind::Derivation { output_names: new }, - PathKind::Derivation { - output_names: ref mut old, - }, - ) => { - old.extend(new); - } - - _ => panic!( - "path '{}' inserted twice with different types", - entry.key().0 - ), - }; - } - }; - } - - /// Mark a plain path as known. - pub fn plain<S: ToString>(&mut self, path: S) { - self.insert_path(path.to_string(), PathKind::Plain); - } - - /// Mark a derivation as known. - pub fn drv<P: ToString, O: ToString>(&mut self, path: P, outputs: &[O]) { - self.insert_path( - path.to_string(), - PathKind::Derivation { - output_names: outputs.iter().map(ToString::to_string).collect(), - }, - ); - } - - /// Mark a derivation output path as known. - pub fn output<P: ToString, N: ToString, D: ToString>( - &mut self, - output_path: P, - name: N, - drv_path: D, - ) { - self.insert_path( - output_path.to_string(), - PathKind::Output { - name: name.to_string(), - derivation: drv_path.to_string(), - }, - ); - } - - /// Checks whether there are any known paths. If not, a reference - /// scanner can not be created. - pub fn is_empty(&self) -> bool { - self.paths.is_empty() - } - - /// Create a reference scanner from the current set of known paths. - pub fn reference_scanner(&self) -> ReferenceScanner<PathName> { - let candidates = self.paths.keys().map(Clone::clone).collect(); - ReferenceScanner::new(candidates) - } - /// Fetch the opaque "hash derivation modulo" for a given derivation path. pub fn get_hash_derivation_modulo(&self, drv_path: &str) -> NixHash { // TODO: we rely on an invariant that things *should* have diff --git a/tvix/glue/src/tvix_io.rs b/tvix/glue/src/tvix_io.rs index 52bbd7bc9cda..77dcb9291032 100644 --- a/tvix/glue/src/tvix_io.rs +++ b/tvix/glue/src/tvix_io.rs @@ -8,30 +8,19 @@ //! otherwise fundamental features like nixpkgs bootstrapping and hash //! calculation will not work. -use std::cell::RefCell; use std::io; use std::path::{Path, PathBuf}; -use std::rc::Rc; use tvix_eval::{EvalIO, FileType}; -use crate::known_paths::KnownPaths; - // TODO: Merge this together with TvixStoreIO? pub struct TvixIO<T: EvalIO> { - /// Ingested paths must be reported to this known paths tracker - /// for accurate build reference scanning. - known_paths: Rc<RefCell<KnownPaths>>, - // Actual underlying [EvalIO] implementation. actual: T, } impl<T: EvalIO> TvixIO<T> { - pub fn new(known_paths: Rc<RefCell<KnownPaths>>, actual: T) -> Self { - Self { - known_paths, - actual, - } + pub fn new(actual: T) -> Self { + Self { actual } } } @@ -42,10 +31,6 @@ impl<T: EvalIO> EvalIO for TvixIO<T> { fn import_path(&self, path: &Path) -> io::Result<PathBuf> { let imported_path = self.actual.import_path(path)?; - self.known_paths - .borrow_mut() - .plain(imported_path.to_string_lossy()); - Ok(imported_path) } |