From c6811f0cea0b9aad5bc188fd29d6425e145847e8 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Mon, 23 Jan 2023 01:00:26 +0300 Subject: feat(tvix/cli): add helper for populating derivation outputs Adds a small helper function which uses a Nix value supplied to `builtins.derivation{Strict}` to populate the `outputs` field of the `Derivation` struct. Change-Id: Iccc7a4f293b3d913140aed576a573a8992241e46 Reviewed-on: https://cl.tvl.fyi/c/depot/+/7898 Reviewed-by: flokli Tested-by: BuildkiteCI --- tvix/cli/src/derivation.rs | 88 ++++++++++++++++++++++++++++++++++++++++++++++ tvix/cli/src/main.rs | 1 + 2 files changed, 89 insertions(+) create mode 100644 tvix/cli/src/derivation.rs diff --git a/tvix/cli/src/derivation.rs b/tvix/cli/src/derivation.rs new file mode 100644 index 000000000000..ad8fec226bfd --- /dev/null +++ b/tvix/cli/src/derivation.rs @@ -0,0 +1,88 @@ +//! Implements `builtins.derivation`, the core of what makes Nix build packages. + +use tvix_derivation::Derivation; +use tvix_eval::{AddContext, ErrorKind, NixList, VM}; + +use crate::errors::Error; + +/// Helper function for populating the `drv.outputs` field from a +/// manually specified set of outputs, instead of the default +/// `outputs`. +fn populate_outputs(vm: &mut VM, drv: &mut Derivation, outputs: NixList) -> Result<(), ErrorKind> { + // Remove the original default `out` output. + drv.outputs.clear(); + + for output in outputs { + let output_name = output + .force(vm)? + .to_str() + .context("determining output name")?; + + if drv + .outputs + .insert(output_name.as_str().into(), Default::default()) + .is_some() + { + return Err(Error::DuplicateOutput(output_name.as_str().into()).into()); + } + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use tvix_eval::observer::NoOpObserver; + use tvix_eval::Value; + + static mut OBSERVER: NoOpObserver = NoOpObserver {}; + + // Creates a fake VM for tests, which can *not* actually be + // used to force (most) values but can satisfy the type + // parameter. + fn fake_vm() -> VM<'static> { + // safe because accessing the observer doesn't actually do anything + unsafe { + VM::new( + Default::default(), + Box::new(tvix_eval::DummyIO), + &mut OBSERVER, + Default::default(), + ) + } + } + + #[test] + fn populate_outputs_ok() { + let mut vm = fake_vm(); + let mut drv = Derivation::default(); + drv.outputs.insert("out".to_string(), Default::default()); + + let outputs = NixList::construct( + 2, + vec![Value::String("foo".into()), Value::String("bar".into())], + ); + + populate_outputs(&mut vm, &mut drv, outputs).expect("populate_outputs should succeed"); + + assert_eq!(drv.outputs.len(), 2); + assert!(drv.outputs.contains_key("bar")); + assert!(drv.outputs.contains_key("foo")); + } + + #[test] + fn populate_outputs_duplicate() { + let mut vm = fake_vm(); + let mut drv = Derivation::default(); + drv.outputs.insert("out".to_string(), Default::default()); + + let outputs = NixList::construct( + 2, + vec![Value::String("foo".into()), Value::String("foo".into())], + ); + + populate_outputs(&mut vm, &mut drv, outputs) + .expect_err("supplying duplicate outputs should fail"); + } +} diff --git a/tvix/cli/src/main.rs b/tvix/cli/src/main.rs index 6757044750fe..0150d83bc6b4 100644 --- a/tvix/cli/src/main.rs +++ b/tvix/cli/src/main.rs @@ -1,3 +1,4 @@ +mod derivation; mod errors; mod known_paths; mod nix_compat; -- cgit 1.4.1