Trigger rebuild when the CLI changed

- Stamp each project with the CLI version it was last compiled with
- Rebuild those projects with a local version file that does not match the one of the current CLI that is building it
This commit is contained in:
Mihai Codoban 2016-03-03 22:57:43 -08:00
parent 7a82a98e4c
commit f14b4cbd3d
6 changed files with 142 additions and 23 deletions

View file

@ -0,0 +1,16 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.IO;
using System.Reflection;
namespace Microsoft.DotNet.Cli.Utils
{
public static class DotnetFiles
{
/// <summary>
/// The CLI ships with a .version file that stores the commit information and CLI version
/// </summary>
public static string VersionFile => Path.GetFullPath(Path.Combine(typeof(DotnetFiles).GetTypeInfo().Assembly.Location, "..", ".version"));
}
}

View file

@ -31,6 +31,11 @@ namespace Microsoft.DotNet.Cli.Compiler.Common
return baseOption; return baseOption;
} }
public static string GetSDKVersionFile(this ProjectContext context, string configuration, string buildBasePath, string outputPath)
{
var intermediatePath = context.GetOutputPaths(configuration, buildBasePath, outputPath).IntermediateOutputDirectoryPath;
return Path.Combine(intermediatePath, ".SDKVersion");
}
// used in incremental compilation for the key file // used in incremental compilation for the key file
public static CommonCompilerOptions ResolveCompilationOptions(this ProjectContext context, string configuration) public static CommonCompilerOptions ResolveCompilationOptions(this ProjectContext context, string configuration)

View file

@ -159,8 +159,7 @@ namespace Microsoft.DotNet.Cli
private static string GetCommitSha() private static string GetCommitSha()
{ {
// The CLI ships with a .version file that stores the commit information var versionFile = DotnetFiles.VersionFile;
var versionFile = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, ".version"));
if (File.Exists(versionFile)) if (File.Exists(versionFile))
{ {

View file

@ -52,10 +52,11 @@ namespace Microsoft.DotNet.Tools.Build
} }
private bool CompileRootProject(bool incremental) private bool CompileRootProject(bool incremental)
{
try
{ {
if (incremental && !NeedsRebuilding(_rootProject, _rootProjectDependencies)) if (incremental && !NeedsRebuilding(_rootProject, _rootProjectDependencies))
{ {
// todo: what if the previous build had errors / warnings and nothing changed? Need to propagate them in case of incremental
return true; return true;
} }
@ -65,6 +66,11 @@ namespace Microsoft.DotNet.Tools.Build
return success; return success;
} }
finally
{
StampProjectWithSDKVersion(_rootProject);
}
}
private bool CompileDependencies(bool incremental) private bool CompileDependencies(bool incremental)
{ {
@ -75,7 +81,11 @@ namespace Microsoft.DotNet.Tools.Build
foreach (var dependency in Sort(_rootProjectDependencies.ProjectDependenciesWithSources)) foreach (var dependency in Sort(_rootProjectDependencies.ProjectDependenciesWithSources))
{ {
if (incremental && !DependencyNeedsRebuilding(dependency)) var dependencyProjectContext = ProjectContext.Create(dependency.Path, dependency.Framework, new[] { _rootProject.RuntimeIdentifier });
try
{
if (incremental && !NeedsRebuilding(dependencyProjectContext, new ProjectDependenciesFacade(dependencyProjectContext, _args.ConfigValue)))
{ {
continue; continue;
} }
@ -85,18 +95,23 @@ namespace Microsoft.DotNet.Tools.Build
return false; return false;
} }
} }
finally
{
StampProjectWithSDKVersion(dependencyProjectContext);
}
}
return true; return true;
} }
private bool DependencyNeedsRebuilding(ProjectDescription dependency)
{
var dependencyProjectContext = ProjectContext.Create(dependency.Path, dependency.Framework, new[] { _rootProject.RuntimeIdentifier });
return NeedsRebuilding(dependencyProjectContext, new ProjectDependenciesFacade(dependencyProjectContext, _args.ConfigValue));
}
private bool NeedsRebuilding(ProjectContext project, ProjectDependenciesFacade dependencies) private bool NeedsRebuilding(ProjectContext project, ProjectDependenciesFacade dependencies)
{ {
if (CLIChangedSinceLastCompilation(project))
{
Reporter.Output.WriteLine($"Project {project.GetDisplayName()} will be compiled because the CLI changed");
return true;
}
var compilerIO = GetCompileIO(project, dependencies); var compilerIO = GetCompileIO(project, dependencies);
// rebuild if empty inputs / outputs // rebuild if empty inputs / outputs
@ -175,6 +190,48 @@ namespace Microsoft.DotNet.Tools.Build
return true; return true;
} }
private bool CLIChangedSinceLastCompilation(ProjectContext project)
{
var currentVersionFile = DotnetFiles.VersionFile;
var versionFileFromLastCompile = project.GetSDKVersionFile(_args.ConfigValue, _args.BuildBasePathValue, _args.OutputValue);
if (!File.Exists(currentVersionFile))
{
// this CLI does not have a version file; cannot tell if CLI changed
return false;
}
if (!File.Exists(versionFileFromLastCompile))
{
// this is the first compilation; cannot tell if CLI changed
return false;
}
var versionsAreEqual = string.Equals(File.ReadAllText(currentVersionFile), File.ReadAllText(versionFileFromLastCompile), StringComparison.OrdinalIgnoreCase);
return !versionsAreEqual;
}
private void StampProjectWithSDKVersion(ProjectContext project)
{
if (File.Exists(DotnetFiles.VersionFile))
{
var projectVersionFile = project.GetSDKVersionFile(_args.ConfigValue, _args.BuildBasePathValue,_args.OutputValue);
var parentDirectory = Path.GetDirectoryName(projectVersionFile);
if (!Directory.Exists(parentDirectory))
{
Directory.CreateDirectory(parentDirectory);
}
File.Copy(DotnetFiles.VersionFile, projectVersionFile, true);
}
else
{
Reporter.Verbose.WriteLine($"Project {project.GetDisplayName()} was not stamped with a CLI version because the version file does not exist: {DotnetFiles.VersionFile}");
}
}
private void PrintSummary(bool success) private void PrintSummary(bool success)
{ {
// todo: Ideally it's the builder's responsibility for adding the time elapsed. That way we avoid cross cutting display concerns between compile and build for printing time elapsed // todo: Ideally it's the builder's responsibility for adding the time elapsed. That way we avoid cross cutting display concerns between compile and build for printing time elapsed

View file

@ -124,5 +124,12 @@ namespace Microsoft.DotNet.Tools.Builder.Tests
return executablePath; return executablePath;
} }
protected string GetIntermediaryOutputPath()
{
var executablePath = Path.Combine(TestProjectRoot, "obj", "Debug", "netstandardapp1.5");
return executablePath;
}
} }
} }

View file

@ -4,6 +4,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using FluentAssertions;
using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Tools.Test.Utilities; using Microsoft.DotNet.Tools.Test.Utilities;
using Xunit; using Xunit;
@ -79,6 +80,40 @@ namespace Microsoft.DotNet.Tools.Builder.Tests
Assert.Contains("does not have a lock file", buildResult.StdErr); Assert.Contains("does not have a lock file", buildResult.StdErr);
} }
[Fact]
public void TestModifiedVersionFile()
{
CreateTestInstance();
BuildProject().Should().HaveCompiledProject(MainProject);
//change version file
var versionFile = Path.Combine(GetIntermediaryOutputPath(), ".SDKVersion");
File.Exists(versionFile).Should().BeTrue();
File.AppendAllText(versionFile, "text");
//assert rebuilt
BuildProject().Should().HaveCompiledProject(MainProject);
}
[Fact]
public void TestNoVersionFile()
{
CreateTestInstance();
BuildProject().Should().HaveCompiledProject(MainProject);
//delete version file
var versionFile = Path.Combine(GetIntermediaryOutputPath(), ".SDKVersion");
File.Exists(versionFile).Should().BeTrue();
File.Delete(versionFile);
File.Exists(versionFile).Should().BeFalse();
//assert build skipped due to no version file
BuildProject().Should().HaveSkippedProjectCompilation(MainProject);
//the version file should have been regenerated during the build, even if compilation got skipped
File.Exists(versionFile).Should().BeTrue();
}
[Fact] [Fact]
public void TestRebuildChangedLockFile() public void TestRebuildChangedLockFile()
{ {