about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--tvix/cli/src/derivation.rs88
-rw-r--r--tvix/cli/src/main.rs1
2 files changed, 89 insertions, 0 deletions
diff --git a/tvix/cli/src/derivation.rs b/tvix/cli/src/derivation.rs
new file mode 100644
index 0000000000..ad8fec226b
--- /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 6757044750..0150d83bc6 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;