Add version suffix to build cache to ensure incremental builds are reset when it changes (#3246)

* add test for #2687
* fix #2687 by writing version suffix to build cache
This commit is contained in:
Andrew Stanton-Nurse 2016-05-27 10:49:50 -07:00
parent e9d3a0903d
commit d5b1ee138f
5 changed files with 86 additions and 10 deletions

View file

@ -11,7 +11,7 @@ namespace Microsoft.DotNet.TestFramework
{
public class TestInstance: TestDirectory
{
// made tolower because the rest of the class works with normalized tolower strings
// made tolower because the rest of the class works with normalized tolower strings
private static readonly IEnumerable<string> BuildArtifactBlackList = new List<string>() {".IncrementalCache", ".SDKVersion"}.Select(s => s.ToLower()).ToArray();
private string _testAssetRoot;
@ -78,9 +78,9 @@ namespace Microsoft.DotNet.TestFramework
.Where(dir =>
{
dir = dir.ToLower();
return dir.EndsWith($"{System.IO.Path.DirectorySeparatorChar}bin")
return dir.EndsWith($"{System.IO.Path.DirectorySeparatorChar}bin")
|| dir.Contains($"{System.IO.Path.DirectorySeparatorChar}bin{System.IO.Path.DirectorySeparatorChar}")
|| dir.EndsWith($"{System.IO.Path.DirectorySeparatorChar}obj")
|| dir.EndsWith($"{System.IO.Path.DirectorySeparatorChar}obj")
|| dir.Contains($"{System.IO.Path.DirectorySeparatorChar}obj{System.IO.Path.DirectorySeparatorChar}");
});
@ -94,10 +94,10 @@ namespace Microsoft.DotNet.TestFramework
{
file = file.ToLower();
var isArtifact = file.Contains($"{System.IO.Path.DirectorySeparatorChar}bin{System.IO.Path.DirectorySeparatorChar}")
var isArtifact = file.Contains($"{System.IO.Path.DirectorySeparatorChar}bin{System.IO.Path.DirectorySeparatorChar}")
|| file.Contains($"{System.IO.Path.DirectorySeparatorChar}obj{System.IO.Path.DirectorySeparatorChar}");
var isBlackListed = BuildArtifactBlackList.Any(b => file.Contains(b));
var isBlackListed = BuildArtifactBlackList.Any(b => file.Contains(b));
return isArtifact && !isBlackListed;
});

View file

@ -2,6 +2,7 @@
// 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.DotNet.Cli;
using Microsoft.DotNet.Cli.Compiler.Common;
@ -44,7 +45,8 @@ namespace Microsoft.DotNet.Tools.Build
_args.ShouldSkipDependencies,
_args.ConfigValue,
_args.BuildBasePathValue,
_args.OutputValue
_args.OutputValue,
BuildIncrementalArgumentList(_args)
);
_scriptRunner = new ScriptRunner();
@ -52,6 +54,11 @@ namespace Microsoft.DotNet.Tools.Build
_commandFactory = new DotNetCommandFactory();
}
private static IDictionary<string, string> BuildIncrementalArgumentList(BuildCommandApp args) => new Dictionary<string, string>()
{
["version-suffix"] = args.VersionSuffixValue
};
private void StampProjectWithSDKVersion(ProjectContext project)
{
if (File.Exists(DotnetFiles.VersionFile))

View file

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@ -11,14 +12,21 @@ namespace Microsoft.DotNet.Tools.Build
{
internal class IncrementalCache
{
private const string BuildArgumentsKeyName = "buildArguments";
private const string InputsKeyName = "inputs";
private const string OutputsKeyNane = "outputs";
public CompilerIO CompilerIO { get; }
public IncrementalCache(CompilerIO compilerIO)
/// <summary>
/// Captures parameters that affect compilation outputs.
/// </summary>
public IDictionary<string, string> BuildArguments { get; }
public IncrementalCache(CompilerIO compilerIO, IEnumerable<KeyValuePair<string, string>> parameters)
{
CompilerIO = compilerIO;
BuildArguments = parameters.ToDictionary(p => p.Key, p => p.Value);
}
public void WriteToFile(string cacheFile)
@ -32,6 +40,7 @@ namespace Microsoft.DotNet.Tools.Build
var rootObject = new JObject();
rootObject[InputsKeyName] = new JArray(CompilerIO.Inputs);
rootObject[OutputsKeyNane] = new JArray(CompilerIO.Outputs);
rootObject[BuildArgumentsKeyName] = JObject.FromObject(BuildArguments);
JsonSerializer.Create().Serialize(streamWriter, rootObject);
}
@ -67,8 +76,9 @@ namespace Microsoft.DotNet.Tools.Build
var inputs = ReadArray<string>(jObject, InputsKeyName);
var outputs = ReadArray<string>(jObject, OutputsKeyNane);
var parameters = ReadDictionary(jObject, BuildArgumentsKeyName);
return new IncrementalCache(new CompilerIO(inputs, outputs));
return new IncrementalCache(new CompilerIO(inputs, outputs), parameters);
}
}
catch (Exception e)
@ -77,6 +87,18 @@ namespace Microsoft.DotNet.Tools.Build
}
}
private static IEnumerable<KeyValuePair<string, string>> ReadDictionary(JObject jObject, string keyName)
{
var obj = jObject[keyName] as JObject;
if(obj == null)
{
return Enumerable.Empty<KeyValuePair<string, string>>();
}
return obj.Properties().ToDictionary(p => p.Name, p => p.Value.ToString());
}
private static IEnumerable<T> ReadArray<T>(JObject jObject, string keyName)
{
var array = jObject.Value<JToken>(keyName)?.Values<T>();

View file

@ -20,6 +20,7 @@ namespace Microsoft.DotNet.Tools.Build
private readonly string _configuration;
private readonly string _buildBasePath;
private readonly string _outputPath;
private readonly IDictionary<string, string> _incrementalAffectingArguments;
public IncrementalManager(
ProjectBuilder projectBuilder,
@ -28,7 +29,8 @@ namespace Microsoft.DotNet.Tools.Build
bool shouldSkipDependencies,
string configuration,
string buildBasePath,
string outputPath)
string outputPath,
IDictionary<string, string> incrementalAffectingArguments)
{
_projectBuilder = projectBuilder;
_compilerIoManager = compilerIOManager;
@ -37,6 +39,7 @@ namespace Microsoft.DotNet.Tools.Build
_configuration = configuration;
_buildBasePath = buildBasePath;
_outputPath = outputPath;
_incrementalAffectingArguments = incrementalAffectingArguments;
}
public IncrementalResult NeedsRebuilding(ProjectGraphNode graphNode)
@ -156,6 +159,21 @@ namespace Microsoft.DotNet.Tools.Build
return new IncrementalResult("Input items added from last build", diffResult.Additions);
}
var keys = incrementalCache.BuildArguments.Keys.Union(_incrementalAffectingArguments.Keys);
var mismatchedKeys = keys.Where(k =>
{
string cachedVal;
string currentVal;
return !incrementalCache.BuildArguments.TryGetValue(k, out cachedVal) ||
!_incrementalAffectingArguments.TryGetValue(k, out currentVal) ||
!string.Equals(cachedVal ?? string.Empty, currentVal ?? string.Empty, StringComparison.Ordinal);
});
if (mismatchedKeys.Any())
{
return new IncrementalResult("Build arguments changed since last build", mismatchedKeys);
}
return IncrementalResult.DoesNotNeedRebuild;
}
@ -195,7 +213,7 @@ namespace Microsoft.DotNet.Tools.Build
{
var incrementalCacheFile = graphNode.ProjectContext.IncrementalCacheFile(_configuration, _buildBasePath, _outputPath);
var incrementalCache = new IncrementalCache(_compilerIoManager.GetCompileIO(graphNode));
var incrementalCache = new IncrementalCache(_compilerIoManager.GetCompileIO(graphNode), _incrementalAffectingArguments);
incrementalCache.WriteToFile(incrementalCacheFile);
}
}

View file

@ -0,0 +1,29 @@
using Microsoft.DotNet.Tools.Test.Utilities;
using Xunit;
namespace Microsoft.DotNet.Tools.Builder.Tests
{
public class IncrementalTestsVersionSuffix : IncrementalTestBase
{
[Fact]
public void TestRebuildWhenVersionSuffixChanged()
{
var testInstance = TestAssetsManager.CreateTestInstance("TestSimpleIncrementalApp")
.WithLockFiles();
// Build with Version Suffix 1
var command = new BuildCommand(testInstance.TestRoot, versionSuffix: "1");
var result = command.ExecuteWithCapturedOutput();
// Verify the result
result.Should().HaveCompiledProject("TestSimpleIncrementalApp", ".NETCoreApp,Version=v1.0");
// Build with Version Suffix 2
command = new BuildCommand(testInstance.TestRoot, versionSuffix: "2");
result = command.ExecuteWithCapturedOutput();
// Verify the result
result.Should().HaveCompiledProject("TestSimpleIncrementalApp", ".NETCoreApp,Version=v1.0");
}
}
}