From 5d856ac2e9b24f7b5a72346d8206cc366e1663ae Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Mon, 23 Jan 2023 15:43:47 +0300 Subject: feat(tvix/derivation): add `path_with_references` This allows the calculation of a store path for a plain string that potentially contains references. These paths are used for `builtins.toFile` (and potentially other features of C++ Nix). Change-Id: Ic507c7f264f362b5e6e628255869e5a4fbe4d788 Reviewed-on: https://cl.tvl.fyi/c/depot/+/7906 Tested-by: BuildkiteCI Reviewed-by: flokli --- tvix/derivation/src/derivation.rs | 26 ++++++++++++++++++++++++++ tvix/derivation/src/lib.rs | 2 +- tvix/derivation/src/tests/mod.rs | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) (limited to 'tvix') diff --git a/tvix/derivation/src/derivation.rs b/tvix/derivation/src/derivation.rs index 74e68ce1d7c5..1f9ed6146bbd 100644 --- a/tvix/derivation/src/derivation.rs +++ b/tvix/derivation/src/derivation.rs @@ -75,6 +75,32 @@ fn build_store_path( // invalid, so map errors to a [DerivationError::InvalidOutputName]. } +/// Build a store path for a literal text file in the store that may +/// contain references. +pub fn path_with_references<'a, I: IntoIterator, C: AsRef<[u8]>>( + name: &str, + content: C, + references: I, +) -> Result { + let mut hasher = Sha256::new(); + hasher.update(content); + let content_hash = hasher.finalize_reset(); + + hasher.update("text"); + for reference in references { + hasher.update(":"); + hasher.update(reference); + } + + hasher.update(&format!(":sha256:{:x}:", content_hash)); + hasher.update(STORE_DIR); + hasher.update(&format!(":{}", name)); + + let digest = hasher.finalize(); + + build_store_path(false, &digest, name) +} + impl Derivation { pub fn serialize(&self, writer: &mut impl Write) -> Result<(), fmt::Error> { writer.write_str(write::DERIVATION_PREFIX)?; diff --git a/tvix/derivation/src/lib.rs b/tvix/derivation/src/lib.rs index 4f17c3906e4c..1b82251bf672 100644 --- a/tvix/derivation/src/lib.rs +++ b/tvix/derivation/src/lib.rs @@ -10,6 +10,6 @@ mod tests; // Public API of the crate. -pub use derivation::Derivation; +pub use derivation::{path_with_references, Derivation}; pub use errors::{DerivationError, OutputError}; pub use output::{Hash, Output}; diff --git a/tvix/derivation/src/tests/mod.rs b/tvix/derivation/src/tests/mod.rs index 5dd60284f05c..ce39afdd2854 100644 --- a/tvix/derivation/src/tests/mod.rs +++ b/tvix/derivation/src/tests/mod.rs @@ -305,3 +305,39 @@ fn output_path_construction() { .expect("must succeed") ); } + +#[test] +fn path_with_zero_references() { + // This hash should match `builtins.toFile`, e.g.: + // + // nix-repl> builtins.toFile "foo" "bar" + // "/nix/store/vxjiwkjkn7x4079qvh1jkl5pn05j2aw0-foo" + + let store_path = crate::path_with_references("foo", "bar", vec![]) + .expect("path_with_references() should succeed"); + + assert_eq!( + store_path.to_absolute_path().as_str(), + "/nix/store/vxjiwkjkn7x4079qvh1jkl5pn05j2aw0-foo" + ); +} + +#[test] +fn path_with_non_zero_references() { + // This hash should match: + // + // nix-repl> builtins.toFile "baz" "${builtins.toFile "foo" "bar"}" + // "/nix/store/5xd714cbfnkz02h2vbsj4fm03x3f15nf-baz" + + let inner = crate::path_with_references("foo", "bar", vec![]) + .expect("path_with_references() should succeed"); + let inner_path = inner.to_absolute_path(); + + let outer = crate::path_with_references("baz", &inner_path, vec![inner_path.as_str()]) + .expect("path_with_references() should succeed"); + + assert_eq!( + outer.to_absolute_path().as_str(), + "/nix/store/5xd714cbfnkz02h2vbsj4fm03x3f15nf-baz" + ); +} -- cgit 1.4.1