Skip to content
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
9a139fb
Support laxer isolation mode via CLI
DmitriyShepelev Oct 24, 2022
66054b9
Limit message description to <= 80 characters
DmitriyShepelev Oct 26, 2022
972f87c
Move `IsolateProjects` into more appropriate directory
DmitriyShepelev Nov 2, 2022
cf948cf
Skipped results should cause cache misses only if `IsolateProjects.True`
DmitriyShepelev Nov 9, 2022
b3455d9
Prevent isolation-exempted targets from being stored in dependency pr…
DmitriyShepelev Nov 16, 2022
db8eb77
Combine static graph and non-static graph isolation constraint skippi…
DmitriyShepelev Nov 17, 2022
73f4cc0
Revert "Combine static graph and non-static graph isolation constrain…
DmitriyShepelev Nov 18, 2022
cbc04e2
Place all isolation-violating build results into the override cache
DmitriyShepelev Nov 19, 2022
679c708
Revert "Prevent isolation-exempted targets from being stored in depen…
DmitriyShepelev Nov 21, 2022
86e6026
Error incompatible projects with isolation-violating target referenci…
DmitriyShepelev Nov 22, 2022
d5fb00b
Eliminate any dependency project cache results from current cache
DmitriyShepelev Nov 28, 2022
cdbb0fb
Test MSB4047
DmitriyShepelev Nov 28, 2022
6759017
Test cache serialization
DmitriyShepelev Nov 30, 2022
b66f1d0
Fix `TaskExecutionHost_Tests`
DmitriyShepelev Dec 5, 2022
f87659a
Add test for not placing build results into current cache if their co…
DmitriyShepelev Dec 15, 2022
ea52a3d
Fix documentation
DmitriyShepelev Dec 15, 2022
01203a2
Improve test name
DmitriyShepelev Dec 15, 2022
003d864
Address nits
DmitriyShepelev Dec 19, 2022
af09626
Move isolation-violating cache result bug fix to separate PR
DmitriyShepelev Dec 20, 2022
893d5d1
Improve naming & preserve IsolateProjects API functionality
DmitriyShepelev Dec 20, 2022
3b18e55
Only serialize specified targets in `MessageUponIsolationViolation` mode
DmitriyShepelev Jan 10, 2023
64df918
Clean up documentation
DmitriyShepelev Jan 10, 2023
cd8d23d
Lowercase the 'i' in '-isolateProjects'
DmitriyShepelev Jan 10, 2023
dec0cf4
Fix nit
DmitriyShepelev Jan 12, 2023
08dba8b
Keep results for default targets if no targets were explicitly specif…
DmitriyShepelev Jan 12, 2023
c6cb413
Remove old `BuildParameters` setters
DmitriyShepelev Jan 13, 2023
de48e6c
Revert MockHost BuildParameters setter
DmitriyShepelev Jan 17, 2023
a97d044
Obtain targets to serialize from `BuildRequestConfiguration.TargetNames`
DmitriyShepelev Jan 19, 2023
7fb580d
Add comments and test
DmitriyShepelev Jan 24, 2023
ae763da
Improve `MessageUponIsolationViolation` documentation
DmitriyShepelev Jan 24, 2023
8fb016d
Merge branch 'main' into isolate-with-messaging
DmitriyShepelev Jan 26, 2023
4a3571c
Add file header
DmitriyShepelev Jan 26, 2023
6788c16
Merge branch 'main' into isolate-with-messaging
DmitriyShepelev Jan 26, 2023
7d85073
Merge branch 'main' into isolate-with-messaging
DmitriyShepelev Jan 26, 2023
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
93 changes: 82 additions & 11 deletions src/Build.UnitTests/BackEnd/CacheSerialization_Tests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Build.BackEnd;
Expand All @@ -22,31 +23,63 @@ public static IEnumerable<object[]> CacheData
{
var configCache = new ConfigCache();
var brq1 = new BuildRequestConfiguration(
1,
new BuildRequestData("path1", new Dictionary<string, string> { ["a1"] = "b1" }, Constants.defaultToolsVersion, new[] { "target1" }, null),
Constants.defaultToolsVersion);
1,
new BuildRequestData("path1", new Dictionary<string, string> { ["a1"] = "b1" }, Constants.defaultToolsVersion, new[] { "target1", "target2" }, null),
Constants.defaultToolsVersion);

var brq2 = new BuildRequestConfiguration(
2,
new BuildRequestData("path2", new Dictionary<string, string> { ["a2"] = "b2" }, Constants.defaultToolsVersion, new[] { "target2" }, null),
Constants.defaultToolsVersion);
var brq3 = new BuildRequestConfiguration(
3,
new BuildRequestData("path3", new Dictionary<string, string> { ["a3"] = "b3" }, Constants.defaultToolsVersion, new[] { "target3" }, null),
Constants.defaultToolsVersion);
3,
new BuildRequestData("path3", new Dictionary<string, string> { ["a3"] = "b3" }, Constants.defaultToolsVersion, new[] { "target3" }, null),
Constants.defaultToolsVersion);

configCache.AddConfiguration(brq1);
configCache.AddConfiguration(brq2);
configCache.AddConfiguration(brq3);

var resultsCache = new ResultsCache();
var request1 = new BuildRequest(1, 0, 1, new string[] { "target1" }, null, BuildEventContext.Invalid, null);
var request1 = new BuildRequest(1, 0, 1, new string[] { "target1", "target2", "target3" }, null, BuildEventContext.Invalid, null);
var request2 = new BuildRequest(2, 0, 2, new string[] { "target2" }, null, BuildEventContext.Invalid, null);
var request3 = new BuildRequest(3, 0, 3, new string[] { "target3" }, null, BuildEventContext.Invalid, null);

resultsCache.AddResult(new BuildResult(request1));
resultsCache.AddResult(new BuildResult(request2));
resultsCache.AddResult(new BuildResult(request3));
var buildResult1 = new BuildResult(request1);
var buildResult2 = new BuildResult(request2);
var buildResult3 = new BuildResult(request3);

buildResult1.AddResultsForTarget(
"target1",
new TargetResult(
Array.Empty<ProjectItemInstance.TaskItem>(),
new WorkUnitResult(WorkUnitResultCode.Success, WorkUnitActionCode.Continue, null)));
buildResult1.AddResultsForTarget(
"target2",
new TargetResult(
Array.Empty<ProjectItemInstance.TaskItem>(),
new WorkUnitResult(WorkUnitResultCode.Success, WorkUnitActionCode.Continue, null)));
buildResult1.AddResultsForTarget(
"target3",
new TargetResult(
Array.Empty<ProjectItemInstance.TaskItem>(),
new WorkUnitResult(WorkUnitResultCode.Success, WorkUnitActionCode.Continue, null)));

buildResult2.AddResultsForTarget(
"target2",
new TargetResult(
Array.Empty<ProjectItemInstance.TaskItem>(),
new WorkUnitResult(WorkUnitResultCode.Success, WorkUnitActionCode.Continue, null)));

buildResult3.AddResultsForTarget(
"target3",
new TargetResult(
Array.Empty<ProjectItemInstance.TaskItem>(),
new WorkUnitResult(WorkUnitResultCode.Success, WorkUnitActionCode.Continue, null)));

resultsCache.AddResult(buildResult1);
resultsCache.AddResult(buildResult2);
resultsCache.AddResult(buildResult3);

return new List<object[]>
{
Expand All @@ -63,7 +96,11 @@ public void OnlySerializeCacheEntryWithSmallestConfigId(object configCache, obje
try
{
cacheFile = FileUtilities.GetTemporaryFile("MSBuildResultsCache");
Assert.Null(CacheSerialization.SerializeCaches((ConfigCache)configCache, (ResultsCache)resultsCache, cacheFile));
Assert.Null(CacheSerialization.SerializeCaches(
(ConfigCache)configCache,
(ResultsCache)resultsCache,
cacheFile,
ProjectIsolationMode.True));

var result = CacheSerialization.DeserializeCaches(cacheFile);
Assert.True(result.ConfigCache.HasConfiguration(1));
Expand All @@ -75,5 +112,39 @@ public void OnlySerializeCacheEntryWithSmallestConfigId(object configCache, obje
File.Delete(cacheFile);
}
}

[Theory]
[MemberData(nameof(CacheData))]
public void OnlySerializeResultsForSpecifiedTargets(object configCache, object resultsCache)
{
// Setup:
// 1. Create a config with id 1 whose project is built with top-level targets target1
// and target2.
// 2. Send a build request and collect the BuildResults for targets target1, target2,
// and target3.
// 3. Ensure the BuildResult for target3 is excluded from output cache serialization
// since it's not a top-level target.
string cacheFile = null;
try
{
cacheFile = FileUtilities.GetTemporaryFile("MSBuildResultsCache");
Assert.Null(CacheSerialization.SerializeCaches(
(ConfigCache)configCache,
(ResultsCache)resultsCache,
cacheFile,
ProjectIsolationMode.MessageUponIsolationViolation));

var result = CacheSerialization.DeserializeCaches(cacheFile);
Assert.True(result.ConfigCache.HasConfiguration(1));
BuildResult buildResult = result.ResultsCache.GetResultsForConfiguration(1);
Assert.True(buildResult.HasResultsForTarget("target1"));
Assert.True(buildResult.HasResultsForTarget("target2"));
Assert.False(buildResult.HasResultsForTarget("target3"));
}
finally
{
File.Delete(cacheFile);
}
}
}
}
44 changes: 33 additions & 11 deletions src/Build.UnitTests/BackEnd/MockHost.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using Microsoft.Build.BackEnd;
using Microsoft.Build.BackEnd.Logging;
using System;
using Microsoft.Build.BackEnd.SdkResolution;
using Microsoft.Build.Engine.UnitTests.BackEnd;
using Microsoft.Build.Evaluation;
Expand Down Expand Up @@ -66,24 +66,47 @@ internal class MockHost : MockLoggingService, IBuildComponentHost, IBuildCompone
#endregion;

/// <summary>
/// Constructor
/// Initializes a new instance of the <see cref="MockHost"/> class.
/// </summary>
public MockHost()
: this(new BuildParameters())
/// <param name="overrideConfigCache">The override config cache.</param>
/// <param name="overrideResultsCache">The override results cache.</param>
public MockHost(ConfigCache overrideConfigCache = null, ResultsCache overrideResultsCache = null)
: this(new BuildParameters(), overrideConfigCache, overrideResultsCache)
{
}

/// <summary>
/// Constructor
/// Initializes a new instance of the <see cref="MockHost"/> class.
/// </summary>
public MockHost(BuildParameters buildParameters)
/// <param name="buildParameters">The mock host's build parameters.</param>
/// <param name="overrideConfigCache">The override config cache.</param>
/// <param name="overrideResultsCache">The override results cache.</param>
public MockHost(BuildParameters buildParameters, ConfigCache overrideConfigCache = null, ResultsCache overrideResultsCache = null)
{
_buildParameters = buildParameters;

_buildParameters.ProjectRootElementCache = new ProjectRootElementCache(false);

_configCache = new ConfigCache();
((IBuildComponent)_configCache).InitializeComponent(this);
if (overrideConfigCache != null && overrideResultsCache != null)
{
_configCache = new ConfigCacheWithOverride(overrideConfigCache);
_resultsCache = new ResultsCacheWithOverride(overrideResultsCache);
}
else if (overrideConfigCache == null && overrideResultsCache == null)
{
_configCache = new ConfigCache();
_resultsCache = new ResultsCache();
}
else if (overrideConfigCache == null)
{
throw new ArgumentNullException($"Attempted to create an override cache with a null {nameof(overrideConfigCache)}.");
}
else
{
throw new ArgumentNullException($"Attempted to create an override cache with a null {nameof(overrideResultsCache)}.");
}

_configCache.InitializeComponent(this);

// We are a logging service
_loggingService = this;
Expand All @@ -93,10 +116,9 @@ public MockHost(BuildParameters buildParameters)
_requestEngine = new BuildRequestEngine();
((IBuildComponent)_requestEngine).InitializeComponent(this);

_resultsCache = new ResultsCache();
((IBuildComponent)_resultsCache).InitializeComponent(this);
_resultsCache.InitializeComponent(this);

_requestBuilder = new Microsoft.Build.UnitTests.BackEnd.BuildRequestEngine_Tests.MockRequestBuilder();
_requestBuilder = new BuildRequestEngine_Tests.MockRequestBuilder();
((IBuildComponent)_requestBuilder).InitializeComponent(this);

_targetBuilder = new TestTargetBuilder();
Expand Down
1 change: 0 additions & 1 deletion src/Build.UnitTests/BackEnd/NodeEndpointInProc_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using Microsoft.Build.BackEnd.Logging;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
using LegacyThreadingData = Microsoft.Build.Execution.LegacyThreadingData;
using Xunit;

Expand Down
62 changes: 50 additions & 12 deletions src/Build.UnitTests/BackEnd/Scheduler_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,22 @@ public void VerifyNoOverCreationOfNodesWithBuildLoop()
Assert.Equal(4, nextNodeId); // 3 nodes
}

[Fact]
public void BuildResultNotPlacedInCurrentCacheIfConfigExistsInOverrideCache()
{
ConfigCache overrideConfigCache = new();
ResultsCache overrideResultsCache = new();
CreateConfiguration(1, "test.csproj", overrideConfigCache);
BuildRequest br1 = CreateBuildRequest(1, 1, new string[] { "A" });
CacheBuildResult(br1, "A", BuildResultUtilities.GetSuccessResult(), overrideResultsCache);
_host = new MockHost(overrideConfigCache, overrideResultsCache);
_scheduler = new Scheduler();
_scheduler.InitializeComponent(_host);
BuildRequest br2 = CreateBuildRequest(1, 1, new string[] { "B" });
_scheduler.RecordResultToCurrentCacheIfConfigNotInOverrideCache(CreateBuildResult(br2, "B", BuildResultUtilities.GetSuccessResult()));
Assert.Null(((ResultsCacheWithOverride)_host.GetComponent(BuildComponentType.ResultsCache)).CurrentCache.GetResultsForConfiguration(1));
}

/// <summary>
/// Verify that if we get two requests but one of them is a failure, we only get the failure result back.
/// </summary>
Expand Down Expand Up @@ -706,26 +722,48 @@ public void TestDetailedSummary()
}

/// <summary>
/// Creates a configuration and stores it in the cache.
/// Creates a configuration to store in the <see cref="ConfigCache"/>.
/// </summary>
private void CreateConfiguration(int configId, string file)
/// <param name="configId">The configuration id.</param>
/// <param name="projectFullPath">The project's full path.</param>
/// <param name="configCache">The config cache in which to place the configuration. If
/// <see cref="langword"="null" />, use the host's config cache.</param>
private void CreateConfiguration(int configId, string projectFullPath, ConfigCache configCache = null)
{
BuildRequestData data = new BuildRequestData(file, new Dictionary<string, string>(), "4.0", Array.Empty<string>(), null);
BuildRequestConfiguration config = new BuildRequestConfiguration(configId, data, "4.0");
config.ProjectInitialTargets = new List<string>();
config.ProjectDefaultTargets = new List<string>();

(_host.GetComponent(BuildComponentType.ConfigCache) as IConfigCache).AddConfiguration(config);
BuildRequestData data = new(projectFullPath, new Dictionary<string, string>(), "4.0", Array.Empty<string>(), null);
BuildRequestConfiguration config = new(configId, data, "4.0") { ProjectInitialTargets = new List<string>(), ProjectDefaultTargets = new List<string>() };
if (configCache == null)
{
(_host.GetComponent(BuildComponentType.ConfigCache) as IConfigCache).AddConfiguration(config);
}
else
{
configCache.AddConfiguration(config);
}
}

/// <summary>
/// Creates and caches a built result.
/// Creates and caches a <see cref="BuildResult"/> in the <see cref="ResultsCache"/>.
/// </summary>
private BuildResult CacheBuildResult(BuildRequest request, string target, WorkUnitResult workUnitResult)
/// <param name="request">The build request corresponding to the <see cref="BuildResult"/> to be
/// created and cached.</param>
/// <param name="target">The target for which there will be a result.</param>
/// <param name="workUnitResult">The result of executing the specified target.</param>
/// <param name="resultsCache">The results cache to contain the <see cref="BuildResult"/>.
/// If <see cref="langword"="null"/>, use the host's results cache.</param>
/// <returns>The build result.</returns>
private BuildResult CacheBuildResult(BuildRequest request, string target, WorkUnitResult workUnitResult, ResultsCache resultsCache = null)
{
BuildResult result = CreateBuildResult(request, target, workUnitResult);
IResultsCache resultsCache = _host.GetComponent(BuildComponentType.ResultsCache) as IResultsCache;
resultsCache.AddResult(result);
if (resultsCache == null)
{
(_host.GetComponent(BuildComponentType.ResultsCache) as IResultsCache).AddResult(result);
}
else
{
resultsCache.AddResult(result);
}

return result;
}

Expand Down
Loading