about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFlorian Klink <flokli@flokli.de>2023-03-14T23·40+0100
committerflokli <flokli@flokli.de>2023-03-15T14·02+0000
commit09b30f895a4901557194179182840320c829b471 (patch)
treeb364eef101c5723dc3607f08a9ac91420dc81320
parent4d465249eff7488e712586bf693668d513211632 (diff)
refactor(tvix/nix-compat): simplify fingerprint calculation r/6011
Instead of having two very similar match branches for the FOD and non-
FOD case, detect the FOD case while looping over all outputs.

In the case of anything other than recursive sha256 FODs, the
fingerprint and output path calculation is exactly the same.

Change-Id: Ieb6995653d008766e595cf29d7cd4fb1334e33dd
Reviewed-on: https://cl.tvl.fyi/c/depot/+/8310
Tested-by: BuildkiteCI
Reviewed-by: tazjin <tazjin@tvl.su>
Autosubmit: flokli <flokli@flokli.de>
-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 06b7ba02f4..d5857c1a69 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(())
     }