1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
//! Context registration methods for [`DepGraph`].
//!
//! Carved out of `mod.rs` to keep each file under the 1k-LOC guard.
use std::path::Path;
use std::time::Instant;
use crate::core::NormalizedPath;
use super::super::context::{compute_context_key_with, CompileContext, ContextKey};
use super::{rebase_project_path, ContextEntry, ContextRegistration, ContextState, DepGraph};
impl DepGraph {
/// Register a compilation context. Returns the context key.
/// If the context already exists, returns the existing key.
pub fn register(&self, ctx: CompileContext) -> ContextKey {
self.register_with_root(ctx, None)
}
/// Register a compilation context with an optional key root used to
/// normalize project-local paths across workspace renames.
/// Variant of [`Self::register_with_root`] that folds an optional
/// `worktree_salt` into the context key (issue #474). Used by the
/// multi-file compile path when `keys::requires_worktree_in_key` is
/// true for the unit. Returns only the resulting [`ContextKey`].
pub fn register_with_root_and_salt(
&self,
ctx: CompileContext,
key_root: Option<NormalizedPath>,
worktree_salt: Option<&Path>,
) -> ContextKey {
self.register_with_root_and_salt_result(ctx, key_root, worktree_salt)
.key
}
pub fn register_with_root(
&self,
ctx: CompileContext,
key_root: Option<NormalizedPath>,
) -> ContextKey {
self.register_with_root_result(ctx, key_root).key
}
pub fn register_with_root_result(
&self,
ctx: CompileContext,
key_root: Option<NormalizedPath>,
) -> ContextRegistration {
self.register_with_root_and_salt_result(ctx, key_root, None)
}
/// Issue #474: variant of [`Self::register_with_root_result`] that folds an
/// optional `worktree_salt` into the context key. Used by the C/C++
/// compile pipeline when `keys::requires_worktree_in_key` returns true
/// (PCH builds + MSVC), so the resulting cache entry is scoped to one
/// worktree and can't be served to a sibling clone whose embedded
/// paths would diverge from the artifact's.
pub fn register_with_root_and_salt_result(
&self,
ctx: CompileContext,
key_root: Option<NormalizedPath>,
worktree_salt: Option<&Path>,
) -> ContextRegistration {
let key =
compute_context_key_with(&ctx, key_root.as_deref(), worktree_salt, |path, root| {
self.cached_normalize_key_path(path, root)
});
self.register_with_key_and_root_result(key, ctx, key_root)
}
/// Register a compilation context with a precomputed key.
///
/// Used for Rustc compilations where the context key is computed from
/// `RustcCompileContext` (different domain tag) but the dep_graph stores
/// a `CompileContext` with the source file path for freshness checks.
pub fn register_with_key(&self, key: ContextKey, ctx: CompileContext) -> ContextKey {
self.register_with_key_and_root(key, ctx, None)
}
pub fn register_with_key_and_root(
&self,
key: ContextKey,
ctx: CompileContext,
key_root: Option<NormalizedPath>,
) -> ContextKey {
self.register_with_key_and_root_result(key, ctx, key_root)
.key
}
pub fn register_with_key_and_root_result(
&self,
key: ContextKey,
ctx: CompileContext,
key_root: Option<NormalizedPath>,
) -> ContextRegistration {
let registration = self.register_context_entry(key, ctx, key_root);
self.rustc_externs.remove(®istration.key);
registration
}
/// Register a rustc context with its current `--extern` file inputs.
///
/// Rustc context keys already reduce extern path prefixes to filename
/// identity. The dependency graph keeps the actual extern paths here only
/// for hashing/freshness; artifact keys incorporate them by crate name.
pub fn register_rustc_with_key_and_root_result(
&self,
key: ContextKey,
ctx: CompileContext,
key_root: Option<NormalizedPath>,
externs: Vec<(String, NormalizedPath)>,
) -> ContextRegistration {
let registration = self.register_context_entry(key, ctx, key_root);
self.rustc_externs.insert(registration.key, externs);
registration
}
pub(super) fn register_context_entry(
&self,
key: ContextKey,
ctx: CompileContext,
key_root: Option<NormalizedPath>,
) -> ContextRegistration {
let mut rebased_from_equivalent_root = false;
self.contexts
.entry(key)
.and_modify(|entry| {
if entry.context.source_file != ctx.source_file || entry.key_root != key_root {
let old_root = entry.key_root.clone();
rebased_from_equivalent_root =
old_root.is_some() && key_root.is_some() && old_root != key_root;
entry.resolved_includes = entry
.resolved_includes
.iter()
.map(|path| rebase_project_path(path, old_root.as_ref(), key_root.as_ref()))
.collect();
entry.last_file_hashes = entry
.last_file_hashes
.iter()
.map(|(path, hash)| {
(
rebase_project_path(path, old_root.as_ref(), key_root.as_ref()),
*hash,
)
})
.collect();
entry.context = ctx.clone();
entry.key_root = key_root.clone();
}
entry.last_accessed = Instant::now();
})
.or_insert_with(|| ContextEntry {
context: ctx,
key_root,
resolved_includes: Vec::new(),
unresolved_includes: Vec::new(),
has_computed_includes: false,
artifact_key: None,
last_file_hashes: Vec::new(),
last_accessed: Instant::now(),
state: ContextState::Cold,
});
ContextRegistration {
key,
rebased_from_equivalent_root,
}
}
/// Returns `true` if the context has never been updated (no artifact key).
/// Used by the server to skip pre-compile hashing on cold contexts where
/// `check_diagnostic` would return `Cold` without examining any hashes.
#[must_use]
pub fn is_cold(&self, key: &ContextKey) -> bool {
match self.contexts.get(key) {
Some(entry) => entry.state == ContextState::Cold,
None => true,
}
}
}