Skip to content

Commit 63b284e

Browse files
authored
Touch all output files (#120)
1 parent af763c3 commit 63b284e

File tree

10 files changed

+42
-25
lines changed

10 files changed

+42
-25
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ These settings are common across all plugins, although different implementations
7474
| `$(MSBuildCacheGetResultsForUnqueriedDependencies)` | `bool` | false | Whether to try and query the cache for dependencies if they have not previously been requested. This option can help in cases where the build isn't done in graph order, or if some projects are skipped. |
7575
| `$(MSBuildCacheTargetsToIgnore)` | `string[]` | `GetTargetFrameworks;GetNativeManifest;GetCopyToOutputDirectoryItems;GetTargetFrameworksWithPlatformForSingleTargetFramework` | The list of targets to ignore when determining if a build request matches a cache entry. This is intended for "information gathering" targets which do not have side-effect. eg. a build with `/t:Build` and `/t:Build;GetTargetFrameworks` should be considered to have equivalent results. Note: This only works "one-way" in that the build request is allowed to have missing targets, while the cache entry is not. This is to avoid a situation where a build request recieves a cache hit with missing target results, where a cache hit with extra target results is acceptable. |
7676
| `$(MSBuildCacheSkipUnchangedOutputFiles)` | `bool` | false | Whether to avoid writing output files on cache hit if the file is unchanged, which can improve performance for incremental builds. A file is considered unchanged if it exists, the previously placed file and file to be placed have the same hash, and the the previously placed file and current file on disk have the same timestamp and file size. |
77+
| `$(MSBuildCacheTouchOutputFiles)` | `bool` | false | Whether to update the last write time for output files on cache hit. All files for a given cache entry will have the same timestamp. Note that outputs which skip materialization via `MSBuildCacheSkipUnchangedOutputFiles` are still touched. |
7778
| `$(MSBuildCacheIgnoreDotNetSdkPatchVersion)` | `bool` | false | Whether to ignore the patch version when doing cache lookups. This trades off some correctness for the sake of getting cache hits when the SDK version isn't exactly the same. The default behavior is to consider the exact SDK version, eg. "8.0.404". With this setting set to true, it will instead use something like "8.0.4XX". Note that the major version, minor version, and feature bands are still considered. |
7879

7980
When configuring settings which are list types, you should always append to the existing value to avoid overriding the defaults:

src/AzureBlobStorage/MSBuildCacheAzureBlobStoragePlugin.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ protected override async Task<ICacheClient> CreateCacheClientAsync(PluginLoggerB
112112
Settings.MaxConcurrentCacheContentOperations,
113113
Settings.AsyncCachePublishing,
114114
Settings.AsyncCacheMaterialization,
115-
Settings.SkipUnchangedOutputFiles);
115+
Settings.SkipUnchangedOutputFiles,
116+
Settings.TouchOutputFiles);
116117
}
117118

118119
private async Task<ICache> CreateRemoteCacheAsync(

src/AzurePipelines/MSBuildCacheAzurePipelinesPlugin.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ protected override async Task<ICacheClient> CreateCacheClientAsync(PluginLoggerB
6262
Settings.RemoteCacheIsReadOnly,
6363
Settings.AsyncCachePublishing,
6464
Settings.AsyncCacheMaterialization,
65-
Settings.SkipUnchangedOutputFiles);
65+
Settings.SkipUnchangedOutputFiles,
66+
Settings.TouchOutputFiles);
6667
}
6768

6869
private static async Task<ICacheSession> StartCacheSessionAsync(Context context, LocalCache cache, string name)

src/AzurePipelines/PipelineCachingCacheClient.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,9 @@ public PipelineCachingCacheClient(
106106
bool remoteCacheIsReadOnly,
107107
bool enableAsyncPublishing,
108108
bool enableAsyncMaterialization,
109-
bool skipUnchangedOutputFiles)
110-
: base(rootContext, fingerprintFactory, hasher, repoRoot, nugetPackageRoot, getFileRealizationMode, localCache, localCAS, maxConcurrentCacheContentOperations, enableAsyncPublishing, enableAsyncMaterialization, skipUnchangedOutputFiles)
109+
bool skipUnchangedOutputFiles,
110+
bool touchOutputFiles)
111+
: base(rootContext, fingerprintFactory, hasher, repoRoot, nugetPackageRoot, getFileRealizationMode, localCache, localCAS, maxConcurrentCacheContentOperations, enableAsyncPublishing, enableAsyncMaterialization, skipUnchangedOutputFiles, touchOutputFiles)
111112
{
112113
_remoteCacheIsReadOnly = remoteCacheIsReadOnly;
113114
_universe = $"pccc-{(int)hasher.Info.HashType}-{InternalSeed}-" + (string.IsNullOrEmpty(universe) ? "DEFAULT" : universe);

src/Common/Caching/CacheClient.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public abstract class CacheClient : ICacheClient
4040
private readonly IContentHasher _hasher;
4141
private readonly IFingerprintFactory _fingerprintFactory;
4242
private readonly bool _enableAsyncMaterialization;
43+
private readonly bool _touchOutputFiles;
4344
private readonly ICache _localCache;
4445
private readonly string _nugetPackageRoot;
4546
private readonly bool _canCloneInNugetCachePath;
@@ -57,7 +58,8 @@ protected CacheClient(
5758
int maxConcurrentCacheContentOperations,
5859
bool enableAsyncPublishing,
5960
bool enableAsyncMaterialization,
60-
bool skipUnchangedOutputFiles)
61+
bool skipUnchangedOutputFiles,
62+
bool touchOutputFiles)
6163
{
6264
RootContext = rootContext;
6365
_fingerprintFactory = fingerprintFactory;
@@ -69,6 +71,7 @@ protected CacheClient(
6971
LocalCacheSession = localCas;
7072
EnableAsyncPublishing = enableAsyncPublishing;
7173
_enableAsyncMaterialization = enableAsyncMaterialization;
74+
_touchOutputFiles = touchOutputFiles;
7275
GetFileRealizationMode = getFileRealizationMode;
7376

7477
PutOrPlaceFileGate = new SemaphoreSlim(maxConcurrentCacheContentOperations);
@@ -494,6 +497,26 @@ async Task PlaceFilesAsync(CancellationToken ct)
494497
tasks.Add(placeFilesTask);
495498
await Task.WhenAll(tasks);
496499

500+
if (_touchOutputFiles)
501+
{
502+
// When touching files, use the same timestamp for every file to ensure we don't end up with some outputs with slightly different timestamps, which may lead to missed incrementality.
503+
DateTime fileTimestamp = DateTime.Now;
504+
foreach (KeyValuePair<string, ContentHash> kvp in nodeBuildResult.Outputs)
505+
{
506+
string relativeFilePath = kvp.Key;
507+
508+
// Avoid touching the reference assembly as incremental build functionality heavily depends on this file not being updated when it does not change.
509+
if (string.Equals(relativeFilePath, nodeContext.ReferenceAssemblyRelativePath, StringComparison.OrdinalIgnoreCase))
510+
{
511+
continue;
512+
}
513+
514+
// Touch the file to ensure incremental builds understand that the file is up to date
515+
string absoluteFilePath = Path.Combine(RepoRoot, relativeFilePath);
516+
File.SetLastWriteTime(absoluteFilePath, fileTimestamp);
517+
}
518+
}
519+
497520
if (_localCacheStateManager is not null)
498521
{
499522
await _localCacheStateManager.WriteStateFileAsync(nodeContext, nodeBuildResult);

src/Common/Caching/CasCacheClient.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,9 @@ public CasCacheClient(
5252
int maxConcurrentCacheContentOperations,
5353
bool enableAsyncPublishing,
5454
bool enableAsyncMaterialization,
55-
bool skipUnchangedOutputFiles)
56-
: base(rootContext, fingerprintFactory, hasher, repoRoot, nugetPackageRoot, getFileRealizationMode, localCache, localCacheSession, maxConcurrentCacheContentOperations, enableAsyncPublishing, enableAsyncMaterialization, skipUnchangedOutputFiles)
55+
bool skipUnchangedOutputFiles,
56+
bool touchOutputFiles)
57+
: base(rootContext, fingerprintFactory, hasher, repoRoot, nugetPackageRoot, getFileRealizationMode, localCache, localCacheSession, maxConcurrentCacheContentOperations, enableAsyncPublishing, enableAsyncMaterialization, skipUnchangedOutputFiles, touchOutputFiles)
5758
{
5859
ICacheSession cacheSession;
5960
if (remoteCache == null)

src/Common/Caching/LocalCacheStateManager.cs

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -86,27 +86,11 @@ internal async Task<List<KeyValuePair<string, ContentHash>>> GetOutOfDateFilesAs
8686
}
8787

8888
List<KeyValuePair<string, ContentHash>> outOfDateFiles = new(nodeBuildResult.Outputs.Count);
89-
90-
// When touching files, use the same timestamp for every file to ensure we don't end up with some outputs with slightly different timestamps, which may lead to missed incrementality.
91-
DateTime fileTimestamp = DateTime.Now;
92-
9389
foreach (KeyValuePair<string, ContentHash> kvp in nodeBuildResult.Outputs)
9490
{
9591
string relativeFilePath = kvp.Key;
9692
ContentHash contentHash = kvp.Value;
97-
if (IsFileUpToDate(context, depFile, relativeFilePath, contentHash))
98-
{
99-
// Avoid touching the reference assembly as incremental build functionality heavily depends on this file not being updated when it does not change.
100-
if (string.Equals(relativeFilePath, nodeContext.ReferenceAssemblyRelativePath, StringComparison.OrdinalIgnoreCase))
101-
{
102-
continue;
103-
}
104-
105-
// Touch the file to ensure incremental builds understand that the file is up to date
106-
string absoluteFilePath = Path.Combine(_repoRoot, relativeFilePath);
107-
File.SetLastWriteTime(absoluteFilePath, fileTimestamp);
108-
}
109-
else
93+
if (!IsFileUpToDate(context, depFile, relativeFilePath, contentHash))
11094
{
11195
outOfDateFiles.Add(kvp);
11296
}

src/Common/PluginSettings.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ public string LocalCacheRootPath
109109

110110
public bool SkipUnchangedOutputFiles { get; init; }
111111

112+
public bool TouchOutputFiles { get; init; }
113+
112114
public static T Create<T>(
113115
IReadOnlyDictionary<string, string> settings,
114116
PluginLoggerBase logger,

src/Common/build/Microsoft.MSBuildCache.Common.targets

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
<MSBuildCacheGlobalPropertiesToIgnore>$(MSBuildCacheGlobalPropertiesToIgnore);MSBuildCacheTargetsToIgnore</MSBuildCacheGlobalPropertiesToIgnore>
2525
<MSBuildCacheGlobalPropertiesToIgnore>$(MSBuildCacheGlobalPropertiesToIgnore);MSBuildCacheIgnoreDotNetSdkPatchVersion</MSBuildCacheGlobalPropertiesToIgnore>
2626
<MSBuildCacheGlobalPropertiesToIgnore>$(MSBuildCacheGlobalPropertiesToIgnore);MSBuildCacheSkipUnchangedOutputFiles</MSBuildCacheGlobalPropertiesToIgnore>
27+
<MSBuildCacheGlobalPropertiesToIgnore>$(MSBuildCacheGlobalPropertiesToIgnore);MSBuildCacheTouchOutputFiles</MSBuildCacheGlobalPropertiesToIgnore>
2728
</PropertyGroup>
2829

2930
<ItemGroup Condition="'$(MSBuildCacheEnabled)' != 'false'">
@@ -47,6 +48,7 @@
4748
<TargetsToIgnore>$(MSBuildCacheTargetsToIgnore)</TargetsToIgnore>
4849
<IgnoreDotNetSdkPatchVersion>$(MSBuildCacheIgnoreDotNetSdkPatchVersion)</IgnoreDotNetSdkPatchVersion>
4950
<SkipUnchangedOutputFiles>$(MSBuildCacheSkipUnchangedOutputFiles)</SkipUnchangedOutputFiles>
51+
<TouchOutputFiles>$(MSBuildCacheTouchOutputFiles)</TouchOutputFiles>
5052
</ProjectCachePlugin>
5153
</ItemGroup>
5254

src/Local/MSBuildCacheLocalPlugin.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ protected override async Task<ICacheClient> CreateCacheClientAsync(PluginLoggerB
6363
Settings.MaxConcurrentCacheContentOperations,
6464
Settings.AsyncCachePublishing,
6565
Settings.AsyncCacheMaterialization,
66-
Settings.SkipUnchangedOutputFiles);
66+
Settings.SkipUnchangedOutputFiles,
67+
Settings.TouchOutputFiles);
6768
}
6869
}

0 commit comments

Comments
 (0)