Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 54 additions & 23 deletions src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ pub struct HashOptions {
struct TargetDigest {
overall: DigestBytes,
direct: DigestBytes,
deps: Vec<String>,
// The traversal cache and the output share these labels until all parents have been hashed.
deps: Option<Rc<Vec<String>>>,
}

struct TargetDigestBuilder {
Expand Down Expand Up @@ -97,7 +98,7 @@ impl TargetDigestBuilder {
TargetDigest {
overall: self.overall.finalize().into(),
direct,
deps: self.deps,
deps: (!self.deps.is_empty()).then(|| Rc::new(self.deps)),
}
}
}
Expand Down Expand Up @@ -376,11 +377,7 @@ fn hash_targets_with_environment(
generating_rule,
} => {
let mut digest = context.rule_digest(&generating_rule, &mut Vec::new())?;
digest.deps = if options.track_deps {
vec![generating_rule]
} else {
Vec::new()
};
digest.deps = options.track_deps.then(|| Rc::new(vec![generating_rule]));
(name, "GeneratedFile", digest)
}
HashTarget::Source(label) => {
Expand All @@ -399,13 +396,14 @@ fn hash_targets_with_environment(
TargetDigest {
overall: digest,
direct: digest,
deps: Vec::new(),
deps: None,
},
)
}
};
digests.push((key, kind, digest));
}
drop(context);
let (hashes, dep_edges) = aggregate_by_label(digests, options.track_deps);
Ok(HashFileData {
hashes,
Expand Down Expand Up @@ -475,7 +473,7 @@ fn aggregate_by_label(
}

fn label_hash(
mut instances: Vec<(String, &'static str, TargetDigest)>,
instances: Vec<(String, &'static str, TargetDigest)>,
) -> (String, TargetHash, Vec<String>) {
let label = target_label(&instances[0].0).to_owned();
let kind = instances[0].1;
Expand All @@ -490,17 +488,22 @@ fn label_hash(
}
(overall.finalize().into(), direct.finalize().into())
};
let mut seen = HashSet::new();
let deps = instances
.iter_mut()
.flat_map(|(_, _, digest)| digest.deps.drain(..))
.map(|mut dependency| {
let length = target_label(&dependency).len();
dependency.truncate(length);
dependency
})
.filter(|dependency| seen.insert(dependency.clone()))
.collect();
let mut edge_lists = instances
.into_iter()
.filter_map(|(_, _, digest)| digest.deps)
.map(Rc::unwrap_or_clone);
let mut deps = edge_lists.next().unwrap_or_default();
for mut edges in edge_lists {
deps.append(&mut edges);
}
for dependency in &mut deps {
dependency.truncate(target_label(dependency).len());
}
// Plain-query rule inputs are normally already unique and in label order.
if !deps.is_sorted_by(|left, right| left < right) {
let mut seen = HashSet::new();
deps.retain(|dependency| seen.insert(dependency.clone()));
}
(
label,
TargetHash {
Expand Down Expand Up @@ -586,7 +589,7 @@ impl HashContext<'_> {
return Ok(TargetDigest {
overall: placeholder,
direct: placeholder,
deps: Vec::new(),
deps: None,
});
}
path.push(canonical_key.clone());
Expand Down Expand Up @@ -2227,13 +2230,13 @@ esac
tracked.direct(b"direct");
tracked.transitive("//dep:a", b"dependency");
let tracked = tracked.finish();
assert_eq!(tracked.deps, ["//dep:a"]);
assert_eq!(tracked.deps.unwrap().as_slice(), ["//dep:a"]);
assert_ne!(tracked.direct, tracked.overall);

let mut untracked = TargetDigestBuilder::new(false);
untracked.direct(b"direct");
untracked.transitive("//dep:a", b"dependency");
assert!(untracked.finish().deps.is_empty());
assert!(untracked.finish().deps.is_none());
assert_eq!(crate::model::Digest::from([0; 32]).to_string().len(), 64);
}

Expand Down Expand Up @@ -2338,6 +2341,34 @@ esac
assert_ne!(included.finalize().to_vec(), ignored.finalize().to_vec());
}

#[test]
fn generated_alias_keeps_the_shared_rules_dependencies() {
let workspace = tempfile::tempdir().unwrap();
let rule = |name: &str, inputs: &[&str]| {
rule_target(Rule {
name: name.into(),
rule_class: "genrule".into(),
rule_input: inputs.iter().map(|input| (*input).to_owned()).collect(),
..Default::default()
})
};
// Raw targets can list a rule under a generated file's label. Both rule outputs
// then read the generating rule's cached digest, including its edge list.
let data = hash_targets_for_test(
&hash_options(workspace.path()),
vec![
rule("//:a", &["//:dep"]),
rule("//:dep", &[]),
rule("//:b", &[]),
generated_target("//:b", "//:a"),
],
workspace.path(),
)
.unwrap();
assert_eq!(data.dep_edges["//:a"], ["//:dep"]);
assert_eq!(data.dep_edges["//:b"], ["//:dep", "//:a"]);
}

#[cfg(unix)]
#[test]
fn hashes_rules_sources_generated_files_and_visibility_groups() {
Expand Down
Loading