From dfc50c9ef53155a1e2b40d4bb203e43aa06dc428 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Mon, 23 Jan 2023 01:28:27 +0300 Subject: feat(tvix/cli): add helper for populating drv output configuration This threads through the fields that control whether a derivation is a fixed-output derivation or not. Change-Id: I49739de178fed9f258291174ca1a2c15a7cf5c2a Reviewed-on: https://cl.tvl.fyi/c/depot/+/7900 Tested-by: BuildkiteCI Reviewed-by: flokli --- tvix/cli/src/derivation.rs | 121 ++++++++++++++++++++++++++++++++++++++++++++- tvix/cli/src/errors.rs | 6 +++ 2 files changed, 125 insertions(+), 2 deletions(-) diff --git a/tvix/cli/src/derivation.rs b/tvix/cli/src/derivation.rs index 2d951f7ae6..5b696ec18e 100644 --- a/tvix/cli/src/derivation.rs +++ b/tvix/cli/src/derivation.rs @@ -1,8 +1,8 @@ //! Implements `builtins.derivation`, the core of what makes Nix build packages. use std::collections::{btree_map, BTreeSet}; -use tvix_derivation::Derivation; -use tvix_eval::{AddContext, ErrorKind, NixList, VM}; +use tvix_derivation::{Derivation, Hash}; +use tvix_eval::{AddContext, CoercionKind, ErrorKind, NixList, Value, VM}; use crate::errors::Error; use crate::known_paths::{KnownPaths, PathType}; @@ -72,6 +72,65 @@ fn populate_inputs>( } } +/// Populate the output configuration of a derivation based on the +/// parameters passed to the call, flipping the required +/// parameters for a fixed-output derivation if necessary. +/// +/// This function handles all possible combinations of the +/// parameters, including invalid ones. +fn populate_output_configuration( + drv: &mut Derivation, + vm: &mut VM, + hash: Option<&Value>, // in nix: outputHash + hash_algo: Option<&Value>, // in nix: outputHashAlgo + hash_mode: Option<&Value>, // in nix: outputHashmode +) -> Result<(), ErrorKind> { + match (hash, hash_algo, hash_mode) { + (Some(hash), Some(algo), hash_mode) => match drv.outputs.get_mut("out") { + None => return Err(Error::ConflictingOutputTypes.into()), + Some(out) => { + let algo = algo + .force(vm)? + .coerce_to_string(CoercionKind::Strong, vm)? + .as_str() + .to_string(); + + let hash_mode = match hash_mode { + None => None, + Some(mode) => Some( + mode.force(vm)? + .coerce_to_string(CoercionKind::Strong, vm)? + .as_str() + .to_string(), + ), + }; + + let algo = match hash_mode.as_deref() { + None | Some("flat") => algo, + Some("recursive") => format!("r:{}", algo), + Some(other) => { + return Err(Error::InvalidOutputHashMode(other.to_string()).into()) + } + }; + + out.hash = Some(Hash { + algo, + + digest: hash + .force(vm)? + .coerce_to_string(CoercionKind::Strong, vm)? + .as_str() + .to_string(), + }); + } + }, + + _ => {} + } + + Ok(()) +} + #[cfg(test)] mod tests { use super::*; @@ -174,4 +233,62 @@ mod tests { .input_derivations .contains_key("/nix/store/aqffiyqx602lbam7n1zsaz3yrh6v08pc-bar.drv")); } + + #[test] + fn populate_output_config_std() { + let mut vm = fake_vm(); + let mut drv = Derivation::default(); + + populate_output_configuration(&mut drv, &mut vm, None, None, None) + .expect("populate_output_configuration() should succeed"); + + assert_eq!(drv, Derivation::default(), "derivation should be unchanged"); + } + + #[test] + fn populate_output_config_fod() { + let mut vm = fake_vm(); + let mut drv = Derivation::default(); + drv.outputs.insert("out".to_string(), Default::default()); + + let hash = Value::String( + "0000000000000000000000000000000000000000000000000000000000000000".into(), + ); + + let algo = Value::String("sha256".into()); + + populate_output_configuration(&mut drv, &mut vm, Some(&hash), Some(&algo), None) + .expect("populate_output_configuration() should succeed"); + + let expected = Hash { + algo: "sha256".into(), + digest: "0000000000000000000000000000000000000000000000000000000000000000".into(), + }; + + assert_eq!(drv.outputs["out"].hash, Some(expected)); + } + + #[test] + fn populate_output_config_fod_recursive() { + let mut vm = fake_vm(); + let mut drv = Derivation::default(); + drv.outputs.insert("out".to_string(), Default::default()); + + let hash = Value::String( + "0000000000000000000000000000000000000000000000000000000000000000".into(), + ); + + let algo = Value::String("sha256".into()); + let mode = Value::String("recursive".into()); + + populate_output_configuration(&mut drv, &mut vm, Some(&hash), Some(&algo), Some(&mode)) + .expect("populate_output_configuration() should succeed"); + + let expected = Hash { + algo: "r:sha256".into(), + digest: "0000000000000000000000000000000000000000000000000000000000000000".into(), + }; + + assert_eq!(drv.outputs["out"].hash, Some(expected)); + } } diff --git a/tvix/cli/src/errors.rs b/tvix/cli/src/errors.rs index 8eaaef1579..cbf8ed9457 100644 --- a/tvix/cli/src/errors.rs +++ b/tvix/cli/src/errors.rs @@ -9,6 +9,7 @@ pub enum Error { DuplicateEnvVar(String), ShadowedOutput(String), InvalidDerivation(DerivationError), + InvalidOutputHashMode(String), } impl Display for Error { @@ -32,6 +33,11 @@ impl Display for Error { "the environment variable '{name}' shadows the name of an output" ), Error::InvalidDerivation(error) => write!(f, "invalid derivation parameters: {error}"), + + Error::InvalidOutputHashMode(mode) => write!( + f, + "invalid output hash mode: '{mode}', only 'recursive' and 'flat` are supported" + ), } } } -- cgit 1.4.1