Skip to content

Commit aa46da8

Browse files
smerapgunasekara
authored andcommitted
Merged PR 700546: Consider rewrites for creation date check
Rewrites were excluded from the just-produced outputs we pass to the fingerprint computation under OIP. But we need to know about them because otherwise they get excluded by the creation timestamp check
1 parent 13a481a commit aa46da8

File tree

4 files changed

+74
-18
lines changed

4 files changed

+74
-18
lines changed

Public/Src/Engine/Scheduler/Fingerprints/ObservedInputProcessingState.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ internal class ObservedInputProcessingState : IDisposable
2626
public readonly Dictionary<AbsolutePath, (DirectoryMembershipFilter, DirectoryEnumerationMode)> EnumeratedDirectories = new Dictionary<AbsolutePath, (DirectoryMembershipFilter, DirectoryEnumerationMode)>();
2727
public readonly HashSet<HierarchicalNameId> AllDependencyPathIds = new HashSet<HierarchicalNameId>();
2828
public readonly HashSet<AbsolutePath> SearchPaths = new HashSet<AbsolutePath>();
29-
public readonly HashSet<AbsolutePath> SharedOpaqueOutputs = new HashSet<AbsolutePath>();
29+
/// <summary>
30+
/// Shared opaque outputs paths produced by the pip to whether they are allowed undeclared rewrites
31+
/// </summary>
32+
public readonly Dictionary<AbsolutePath, bool> SharedOpaqueOutputs = new Dictionary<AbsolutePath, bool>();
3033

3134
/// <summary>
3235
/// Gets a pooled instance instance of <see cref="ObservedInputProcessingState"/>.
@@ -48,12 +51,12 @@ private void Clear()
4851
AllDependencyPathIds.Clear();
4952
SearchPaths.Clear();
5053
SharedOpaqueOutputs.Clear();
51-
}
54+
}
5255

53-
/// <summary>
54-
/// Returns the instance to the pool
55-
/// </summary>
56-
public void Dispose()
56+
/// <summary>
57+
/// Returns the instance to the pool
58+
/// </summary>
59+
public void Dispose()
5760
{
5861
s_pool.PutInstance(this);
5962
}

Public/Src/Engine/Scheduler/Fingerprints/ObservedInputProcessor.cs

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -182,15 +182,16 @@ internal static async Task<ObservedInputProcessingResult> ProcessInternalAsync<T
182182

183183
// Compute the set of all shared dynamic outputs. This is only done if there is a chance we end up using MinimalGraphWithAlienFiles
184184
// for this pip, otherwise we keep the set empty to avoid unnecessary computations
185-
HashSet<AbsolutePath> sharedOpaqueOutputs = processingState.SharedOpaqueOutputs;
185+
Dictionary<AbsolutePath, bool> sharedOpaqueOutputs = processingState.SharedOpaqueOutputs;
186+
186187
if (unPopulatedSharedOpaqueOutputs != null && envAdapter.MayDetermineMinimalGraphWithAlienFiles(allowUndeclaredSourceReads))
187188
{
188-
// We filter out artifacts that are allowed file rewrites since that information is not available
189-
// when processing a prior path set. The final result will be that allowed rewrites, even though outputs,
190-
// will be part of the directory fingerprint when using minimal with alien files. This is the desired outcome
191-
// since those files existed before the build begun.
189+
// Let's distinguish rewrites vs not. We want rewritten files to be part of the fingerprint (those files
190+
// existed before the build begun) so we could exclude them from the just-produced outputs we pass to the
191+
// fingerprint enumeration computation, but on the other hand we need to know about them for the creation time
192+
// check
192193
sharedOpaqueOutputs.AddRange(unPopulatedSharedOpaqueOutputs.Values.SelectMany(fileArtifacts =>
193-
fileArtifacts.Where(fa => !fa.IsUndeclaredFileRewrite).Select(fa => fa.Path)));
194+
fileArtifacts.Select(fa => new KeyValuePair<AbsolutePath, bool>(fa.Path, fa.IsUndeclaredFileRewrite))));
194195
}
195196

196197
using (operationContext.StartOperation(PipExecutorCounter.ObservedInputProcessorPreProcessDuration))
@@ -1264,7 +1265,7 @@ internal interface IObservedInputProcessingEnvironment
12641265
DirectoryMembershipFilter filter,
12651266
bool isReadOnlyDirectory,
12661267
DirectoryMembershipHashedEventData eventData,
1267-
IReadOnlyCollection<AbsolutePath> sharedOpaqueOutputs,
1268+
IReadOnlyDictionary<AbsolutePath, bool> sharedOpaqueOutputs,
12681269
IReadOnlyCollection<AbsolutePath> createdDirectories,
12691270
ConcurrentBigMap<AbsolutePath, IReadOnlyList<DirectoryMemberEntry>> alienFileEnumerationCache,
12701271
out DirectoryEnumerationMode enumerationMode,
@@ -1938,7 +1939,7 @@ internal DirectoryEnumerationMode DetermineEnumerationModeAndRule(AbsolutePath d
19381939
DirectoryMembershipFilter filter,
19391940
bool isReadOnlyDirectory,
19401941
DirectoryMembershipHashedEventData eventData,
1941-
IReadOnlyCollection<AbsolutePath> sharedOpaqueOutputs,
1942+
IReadOnlyDictionary<AbsolutePath, bool> sharedOpaqueOutputs,
19421943
IReadOnlyCollection<AbsolutePath> createdDirectories,
19431944
ConcurrentBigMap<AbsolutePath, IReadOnlyList<DirectoryMemberEntry>> alienFileEnumerationCache,
19441945
out DirectoryEnumerationMode enumerationMode,
@@ -2106,7 +2107,7 @@ private Action<AbsolutePath, string> FilteredHandledEntry(AbsolutePath directory
21062107
}
21072108

21082109
private Func<EnumerationRequest, PathExistence?> TryEnumerateDirectoryWithMinimalGraphWithAlienFiles(
2109-
IReadOnlyCollection<AbsolutePath> sharedDynamicOutputs,
2110+
IReadOnlyDictionary<AbsolutePath, bool> sharedDynamicOutputs,
21102111
IReadOnlyCollection<AbsolutePath> createdDirectories,
21112112
ConcurrentBigMap<AbsolutePath, IReadOnlyList<DirectoryMemberEntry>> alienFileEnumerationCache)
21122113
{
@@ -2216,7 +2217,9 @@ private Action<AbsolutePath, string> FilteredHandledEntry(AbsolutePath directory
22162217

22172218
// Rule out all shared opaques produced by this pip. These files may not be part yet of the output file system since those get registered
22182219
// in a post-execution step which is not reached yet on cache miss, so the output file system may have not captured them yet
2219-
if (sharedDynamicOutputs.Contains(realFileEntryPath))
2220+
// Only exclude the ones that are not a file rewrite, in consonance with the check we did above against the output file system
2221+
var isOutputProducedByThisPip = sharedDynamicOutputs.TryGetValue(realFileEntryPath, out var isFileRewrite);
2222+
if (isOutputProducedByThisPip && !isFileRewrite)
22202223
{
22212224
continue;
22222225
}
@@ -2241,7 +2244,10 @@ private Action<AbsolutePath, string> FilteredHandledEntry(AbsolutePath directory
22412244
// Some considerations here: the correctness of this check relies on making sure outputs get to the output
22422245
// file system before they are either hardlinked from the cache or (on Windows) made shared opaque by virtue of timestamp flagging.
22432246
// Otherwise, we run the risk of altering the file creation original timestamp.
2244-
if (m_env.State.FileTimestampTracker.IsFileCreationTrackingSupported && m_env.State.FileTimestampTracker.PathCreatedAfterEngineStarted(realFileEntryPath))
2247+
// We could have here an output produced by this pip that is a file rewrite (and whose creation will therefore had occurred after the engine
2248+
// started. We do want those as part of the fingerprint, so don't rule it out here.
2249+
if (m_env.State.FileTimestampTracker.IsFileCreationTrackingSupported && !isOutputProducedByThisPip &&
2250+
m_env.State.FileTimestampTracker.PathCreatedAfterEngineStarted(realFileEntryPath))
22452251
{
22462252
continue;
22472253
}

Public/Src/Engine/UnitTests/Scheduler.IntegrationTest/MinimalGraphWithAlienFileTests.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,53 @@ public void ExistingDirectoriesArePartOfTheFingerprint(bool createDirectoryInBui
261261
RunScheduler(fileTimestampTracker: tracker).AssertCacheMiss(pip.Process.PipId);
262262
}
263263

264+
[Fact]
265+
public void FileRewritesArePartOfTheFingerprint()
266+
{
267+
// Make sure the source file we create before the build runs is considered a true source file by making
268+
// it older than the engine start time
269+
var timestampTracker = GetFileTimestampTrackerFromTomorrow();
270+
271+
AbsolutePath dirPath = AbsolutePath.Create(Context.PathTable, Path.Combine(SourceRoot, "dir"));
272+
DirectoryArtifact dirToEnumerate = DirectoryArtifact.CreateWithZeroPartialSealId(dirPath);
273+
274+
var sourceFile = CreateSourceFile(root: dirPath);
275+
string sourceFileString = sourceFile.Path.ToString(Context.PathTable);
276+
277+
// Create a source file under the directory we will enumerate
278+
File.WriteAllText(sourceFileString, "some text");
279+
280+
var builder = CreatePipBuilder(new Operation[]
281+
{
282+
// Rewrite the source file
283+
Operation.WriteFile(sourceFile, doNotInfer: true),
284+
// And enumerate it
285+
Operation.EnumerateDir(dirToEnumerate, doNotInfer: true),
286+
// Dummy output
287+
Operation.WriteFile(CreateOutputFileArtifact())
288+
});
289+
290+
// This makes sure we use the right file system, which is aware of alien files
291+
builder.Options |= global::BuildXL.Pips.Operations.Process.Options.AllowUndeclaredSourceReads;
292+
// Let source rewrites happen
293+
builder.RewritePolicy = RewritePolicy.SafeSourceRewritesAreAllowed;
294+
295+
builder.AddOutputDirectory(dirPath, SealDirectoryKind.SharedOpaque);
296+
297+
var pip = SchedulePipBuilder(builder);
298+
299+
// Run once
300+
RunScheduler(fileTimestampTracker: timestampTracker).AssertSuccess();
301+
302+
// Make sure fingerprints are stable
303+
RunScheduler(fileTimestampTracker: timestampTracker).AssertSuccess().AssertCacheHit(pip.Process.PipId);
304+
305+
// Delete the source file and make sure we get a cache miss since the enumeration changed
306+
File.Delete(sourceFileString);
307+
308+
RunScheduler(fileTimestampTracker: timestampTracker).AssertCacheMiss(pip.Process.PipId);
309+
}
310+
264311
/// <summary>
265312
/// Requiring Windows for this test is not actually a limitation of the enumeration mode (which should work fine in non-windows OSs) but
266313
/// a limitation of <see cref="IScheduleConfiguration.TreatAbsentDirectoryAsExistentUnderOpaque"/>, where directory probes cannot be simulated

Public/Src/Engine/UnitTests/Scheduler/ObservedInputProcessorTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -771,7 +771,7 @@ public Environment(Harness harness, LoggingContext loggingContext)
771771
DirectoryMembershipFilter filter,
772772
bool isReadOnlyDirectory,
773773
DirectoryMembershipHashedEventData eventData,
774-
IReadOnlyCollection<AbsolutePath> sharedOpaqueOutputs,
774+
IReadOnlyDictionary<AbsolutePath, bool> sharedOpaqueOutputs,
775775
IReadOnlyCollection<AbsolutePath> createdDirectories,
776776
ConcurrentBigMap<AbsolutePath, IReadOnlyList<(AbsolutePath, string)>> alienFileEnumerationCache,
777777
out DirectoryEnumerationMode mode,

0 commit comments

Comments
 (0)