about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--tvix/nix-compat/src/derivation/mod.rs130
1 files changed, 54 insertions, 76 deletions
diff --git a/tvix/nix-compat/src/derivation/mod.rs b/tvix/nix-compat/src/derivation/mod.rs
index 06b7ba02f4d8..d5857c1a6954 100644
--- a/tvix/nix-compat/src/derivation/mod.rs
+++ b/tvix/nix-compat/src/derivation/mod.rs
@@ -236,86 +236,64 @@ impl Derivation {
         name: &str,
         derivation_or_fod_hash: &NixHash,
     ) -> Result<(), DerivationError> {
-        // Check if the Derivation is fixed output, because they cause
-        // different fingerprints to be hashed.
-        match self.get_fixed_output() {
-            None => {
-                // The fingerprint and hash differs per output
-                for (output_name, output) in self.outputs.iter_mut() {
-                    // Assert that outputs are not yet populated, to avoid using this function wrongly.
-                    // We don't also go over self.environment, but it's a sufficient
-                    // footgun prevention mechanism.
-                    assert!(output.path.is_empty());
-
-                    // calculate the output_name_path, which is the part of the NixPath after the digest.
-                    let mut output_path_name = name.to_string();
-                    if output_name != "out" {
-                        output_path_name.push('-');
-                        output_path_name.push_str(output_name);
-                    }
-
-                    let fp = &format!(
-                        "output:{}:{}:{}:{}",
-                        output_name,
-                        derivation_or_fod_hash.to_nix_hash_string(),
-                        store_path::STORE_DIR,
-                        output_path_name,
-                    );
-
-                    let abs_store_path =
-                        utils::build_store_path(false, fp, &output_path_name)?.to_absolute_path();
-
-                    output.path = abs_store_path.clone();
-                    self.environment
-                        .insert(output_name.to_string(), abs_store_path);
+        let num_outputs = self.outputs.len();
+        // The fingerprint and hash differs per output
+        for (output_name, output) in self.outputs.iter_mut() {
+            // Assert that outputs are not yet populated, to avoid using this function wrongly.
+            // We don't also go over self.environment, but it's a sufficient
+            // footgun prevention mechanism.
+            assert!(output.path.is_empty());
+
+            // calculate the output_name_path, which is the part of the NixPath after the digest.
+            // It's the name, and (if it's the non-out output), the output name after a `-`.
+            let output_path_name = {
+                let mut output_path_name = name.to_string();
+                if output_name != "out" {
+                    output_path_name.push('-');
+                    output_path_name.push_str(output_name);
                 }
-            }
-            Some((fixed_output_path, fixed_output_hash)) => {
-                // Assert that outputs are not yet populated, to avoid using this function wrongly.
-                // We don't also go over self.environment, but it's a sufficient
-                // footgun prevention mechanism.
-                assert!(fixed_output_path.is_empty());
-
-                let fp = {
-                    let mut fp = String::new();
-                    // Fixed-output derivation.
-                    // There's two different hashing strategies in place,
-                    // depending on whether the hash is recursive AND sha256 or anything else.
-                    // This code is _weird_ but it is what Nix is doing. See:
-                    // https://github.com/NixOS/nix/blob/1385b2007804c8a0370f2a6555045a00e34b07c7/src/libstore/store-api.cc#L178-L196
-                    if let NixHashWithMode::Recursive(recursive_hash) = fixed_output_hash {
-                        if recursive_hash.algo == HashAlgo::Sha256 {
-                            fp.push_str(&format!("source:{}", recursive_hash.to_nix_hash_string()));
-                        } else {
-                            // This is similar to the FOD case, with an empty fixed_output_path.
-                            fp.push_str(&format!(
-                                "output:out:{}",
-                                derivation_or_fod_hash.to_nix_hash_string(),
-                            ));
-                        }
-                    } else {
-                        // This is similar to the FOD case, with an empty fixed_output_path.
-                        fp.push_str(&format!(
-                            "output:out:{}",
-                            derivation_or_fod_hash.to_nix_hash_string(),
-                        ));
+                output_path_name
+            };
+
+            // In the case this is a fixed-output derivation AND the
+            // hash mode is recursive AND the hash algo is sha256, a
+            // custom fingerprint is used to calculate the output path.
+            //
+            // In all other cases, the fingerprint is derived from the
+            // derivation_or_fod_hash.
+            let custom_fp: Option<String> =
+                if num_outputs == 1 && output_name == "out" && output.hash_with_mode.is_some() {
+                    match &output.hash_with_mode {
+                        Some(NixHashWithMode::Recursive(NixHash {
+                            digest,
+                            algo: HashAlgo::Sha256,
+                        })) => Some(format!(
+                            "source:sha256:{}:{}:{}",
+                            data_encoding::HEXLOWER.encode(digest),
+                            store_path::STORE_DIR,
+                            output_path_name
+                        )),
+                        _ => None,
                     }
-                    fp.push_str(&format!(":{}:{}", store_path::STORE_DIR, name));
-                    fp
+                } else {
+                    None
                 };
 
-                let abs_store_path = utils::build_store_path(false, &fp, name)?.to_absolute_path();
-
-                self.outputs.insert(
-                    "out".to_string(),
-                    Output {
-                        path: abs_store_path.clone(),
-                        hash_with_mode: Some(fixed_output_hash.clone()),
-                    },
-                );
-                self.environment.insert("out".to_string(), abs_store_path);
-            }
-        };
+            // If no custom_fp has been determined, use the default one.
+            let fp = custom_fp.unwrap_or(format!(
+                "output:{}:{}:{}:{}",
+                output_name,
+                derivation_or_fod_hash.to_nix_hash_string(),
+                store_path::STORE_DIR,
+                output_path_name,
+            ));
+            let abs_store_path =
+                utils::build_store_path(false, &fp, &output_path_name)?.to_absolute_path();
+
+            output.path = abs_store_path.clone();
+            self.environment
+                .insert(output_name.to_string(), abs_store_path);
+        }
 
         Ok(())
     }