refactor(tvix/nix-compat): simplify fingerprint calculation

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>
This commit is contained in:
Florian Klink 2023-03-15 00:40:10 +01:00 committed by flokli
parent 4d465249ef
commit 09b30f895a

View file

@ -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());
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.
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);
// 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());
output_path_name
};
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(),
));
// 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();
// 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();
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);
}
};
output.path = abs_store_path.clone();
self.environment
.insert(output_name.to_string(), abs_store_path);
}
Ok(())
}