about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2023-01-22T22·00+0300
committertazjin <tazjin@tvl.su>2023-01-27T12·21+0000
commitc6811f0cea0b9aad5bc188fd29d6425e145847e8 (patch)
tree83a8d78b506ccd28a4ed68882c0c5b6c2aa7a7e7
parent1028aff105dfda292698d023594f2ac8498c0851 (diff)
feat(tvix/cli): add helper for populating derivation outputs r/5764
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 <flokli@flokli.de>
Tested-by: BuildkiteCI
-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;