Add --no-dependency flag to build
This commit is contained in:
parent
41fd92222d
commit
a0990a518c
7 changed files with 109 additions and 47 deletions
|
@ -8,15 +8,18 @@ namespace Microsoft.DotNet.Tools.Build
|
|||
internal class BuilderCommandApp : CompilerCommandApp
|
||||
{
|
||||
public const string BuildProfileFlag = "--build-profile";
|
||||
public const string ForceUnsafeFlag = "--no-incremental";
|
||||
public const string NoIncrementalFlag = "--no-incremental";
|
||||
public const string NoDependenciesFlag = "--no-dependencies";
|
||||
|
||||
public bool BuildProfileValue => OptionHasValue(BuildProfileFlag);
|
||||
public bool ForceUnsafeValue => OptionHasValue(ForceUnsafeFlag);
|
||||
public bool ShouldPrintIncrementalPreconditions => OptionHasValue(BuildProfileFlag);
|
||||
public bool ShouldNotUseIncrementality => OptionHasValue(NoIncrementalFlag);
|
||||
public bool ShouldSkipDependencies => OptionHasValue(NoDependenciesFlag);
|
||||
|
||||
public BuilderCommandApp(string name, string fullName, string description) : base(name, fullName, description)
|
||||
{
|
||||
AddNoValueOption(BuildProfileFlag, "Set this flag to print the incremental safety checks that prevent incremental compilation");
|
||||
AddNoValueOption(ForceUnsafeFlag, "Set this flag to turn off incremental build");
|
||||
AddNoValueOption(NoIncrementalFlag, "Set this flag to turn off incremental build");
|
||||
AddNoValueOption(NoDependenciesFlag, "Set this flag to ignore project to project references and only build the root project");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,9 +23,9 @@ namespace Microsoft.DotNet.Tools.Build
|
|||
public static readonly string[] KnownCompilers = { "csc", "vbc", "fsc" };
|
||||
|
||||
private readonly ProjectContext _rootProject;
|
||||
private readonly ProjectDependenciesFacade _rootProjectDependencies;
|
||||
private readonly BuilderCommandApp _args;
|
||||
private readonly IncrementalPreconditions _preconditions;
|
||||
private readonly ProjectDependenciesFacade _dependencies;
|
||||
|
||||
public bool IsSafeForIncrementalCompilation => !_preconditions.PreconditionsDetected();
|
||||
|
||||
|
@ -36,12 +36,9 @@ namespace Microsoft.DotNet.Tools.Build
|
|||
// Cleaner to clone the args and mutate the clone than have separate CompileContext fields for mutated args
|
||||
// and then reasoning which ones to get from args and which ones from fields.
|
||||
_args = (BuilderCommandApp)args.ShallowCopy();
|
||||
|
||||
_args.OutputValue = _args.OutputValue;
|
||||
_args.BuildBasePathValue = _args.BuildBasePathValue;
|
||||
|
||||
// Set up dependencies
|
||||
_dependencies = new ProjectDependenciesFacade(_rootProject, _args.ConfigValue);
|
||||
_rootProjectDependencies = new ProjectDependenciesFacade(_rootProject, _args.ConfigValue);
|
||||
|
||||
// gather preconditions
|
||||
_preconditions = GatherIncrementalPreconditions();
|
||||
|
@ -51,17 +48,36 @@ namespace Microsoft.DotNet.Tools.Build
|
|||
{
|
||||
CreateOutputDirectories();
|
||||
|
||||
// compile dependencies
|
||||
foreach (var dependency in Sort(_dependencies.ProjectDependenciesWithSources))
|
||||
{
|
||||
if (incremental)
|
||||
{
|
||||
var dependencyProjectContext = ProjectContext.Create(dependency.Path, dependency.Framework, new[] { _rootProject.RuntimeIdentifier });
|
||||
return CompileDendencies(incremental) && CompileRootProject(incremental);
|
||||
}
|
||||
|
||||
if (!NeedsRebuilding(dependencyProjectContext, new ProjectDependenciesFacade(dependencyProjectContext, _args.ConfigValue)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
private bool CompileRootProject(bool incremental)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
var success = InvokeCompileOnRootProject();
|
||||
|
||||
PrintSummary(success);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
private bool CompileDendencies(bool incremental)
|
||||
{
|
||||
if (_args.ShouldSkipDependencies)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var dependency in Sort(_rootProjectDependencies.ProjectDependenciesWithSources))
|
||||
{
|
||||
if (incremental && !DependencyNeedsRebuilding(dependency))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!InvokeCompileOnDependency(dependency))
|
||||
|
@ -70,28 +86,18 @@ namespace Microsoft.DotNet.Tools.Build
|
|||
}
|
||||
}
|
||||
|
||||
if (incremental && !NeedsRebuilding(_rootProject, _dependencies))
|
||||
{
|
||||
// todo: what if the previous build had errors / warnings and nothing changed? Need to propagate them in case of incremental
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// compile project
|
||||
var success = InvokeCompileOnRootProject();
|
||||
|
||||
PrintSummary(success);
|
||||
|
||||
return success;
|
||||
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)
|
||||
{
|
||||
return NeedsRebuilding(project, dependencies, _args.BuildBasePathValue);
|
||||
}
|
||||
|
||||
private bool NeedsRebuilding(ProjectContext project, ProjectDependenciesFacade dependencies, string baseBuildPath)
|
||||
{
|
||||
var compilerIO = GetCompileIO(project, _args.ConfigValue, baseBuildPath, _args.OutputValue, dependencies, project == _rootProject);
|
||||
var compilerIO = GetCompileIO(project, _args.ConfigValue, _args.BuildBasePathValue, _args.OutputValue, dependencies, project == _rootProject);
|
||||
|
||||
// rebuild if empty inputs / outputs
|
||||
if (!(compilerIO.Outputs.Any() && compilerIO.Inputs.Any()))
|
||||
|
@ -195,9 +201,9 @@ namespace Microsoft.DotNet.Tools.Build
|
|||
|
||||
private IncrementalPreconditions GatherIncrementalPreconditions()
|
||||
{
|
||||
var preconditions = new IncrementalPreconditions(_args.BuildProfileValue);
|
||||
var preconditions = new IncrementalPreconditions(_args.ShouldPrintIncrementalPreconditions);
|
||||
|
||||
if (_args.ForceUnsafeValue)
|
||||
if (_args.ShouldNotUseIncrementality)
|
||||
{
|
||||
preconditions.AddForceUnsafePrecondition();
|
||||
}
|
||||
|
@ -217,11 +223,16 @@ namespace Microsoft.DotNet.Tools.Build
|
|||
// check the entire project tree that needs to be compiled, duplicated for each framework
|
||||
private List<ProjectContext> GetProjectsToCheck()
|
||||
{
|
||||
if (_args.ShouldSkipDependencies)
|
||||
{
|
||||
return new List<ProjectContext>(1) { _rootProject };
|
||||
}
|
||||
|
||||
// include initial root project
|
||||
var contextsToCheck = new List<ProjectContext>(1 + _dependencies.ProjectDependenciesWithSources.Count) { _rootProject };
|
||||
var contextsToCheck = new List<ProjectContext>(1 + _rootProjectDependencies.ProjectDependenciesWithSources.Count) { _rootProject };
|
||||
|
||||
// convert ProjectDescription to ProjectContext
|
||||
var dependencyContexts = _dependencies.ProjectDependenciesWithSources.Select
|
||||
var dependencyContexts = _rootProjectDependencies.ProjectDependenciesWithSources.Select
|
||||
(keyValuePair => ProjectContext.Create(keyValuePair.Value.Path, keyValuePair.Value.Framework));
|
||||
|
||||
contextsToCheck.AddRange(dependencyContexts);
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace Microsoft.DotNet.Tools.Build
|
|||
|
||||
public void AddForceUnsafePrecondition()
|
||||
{
|
||||
_preconditions.Add($"[Forced Unsafe] The build was marked as unsafe. Remove the {BuilderCommandApp.ForceUnsafeFlag} flag to enable incremental compilation");
|
||||
_preconditions.Add($"[Forced Unsafe] The build was marked as unsafe. Remove the {BuilderCommandApp.NoIncrementalFlag} flag to enable incremental compilation");
|
||||
}
|
||||
|
||||
public bool PreconditionsDetected()
|
||||
|
|
|
@ -33,3 +33,6 @@ Prints out the incremental safety checks that users need to address in order for
|
|||
|
||||
--no-incremental
|
||||
Marks the build as unsafe for incrementality. This turns off incremental compilation and forces a clean rebuild of the project dependency graph.
|
||||
|
||||
--no-dependencies
|
||||
Ignore project to project references and only build the root project specified to build.
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
|
|||
private string _cppCompilerFlags;
|
||||
private bool _buildProfile;
|
||||
private bool _noIncremental;
|
||||
private bool _noDependencies;
|
||||
|
||||
private string OutputOption
|
||||
{
|
||||
|
@ -166,6 +167,16 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
|
|||
}
|
||||
}
|
||||
|
||||
private string NoDependencies
|
||||
{
|
||||
get
|
||||
{
|
||||
return _noDependencies ?
|
||||
"--no-dependencies" :
|
||||
"";
|
||||
}
|
||||
}
|
||||
|
||||
public BuildCommand(
|
||||
string projectPath,
|
||||
string output="",
|
||||
|
@ -181,11 +192,11 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
|
|||
bool nativeCppMode=false,
|
||||
string cppCompilerFlags="",
|
||||
bool buildProfile=true,
|
||||
bool noIncremental=false
|
||||
bool noIncremental=false,
|
||||
bool noDependencies=false
|
||||
)
|
||||
: base("dotnet")
|
||||
{
|
||||
|
||||
_projectPath = projectPath;
|
||||
_project = ProjectReader.GetProject(projectPath);
|
||||
|
||||
|
@ -203,6 +214,7 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
|
|||
_cppCompilerFlags = cppCompilerFlags;
|
||||
_buildProfile = buildProfile;
|
||||
_noIncremental = noIncremental;
|
||||
_noDependencies = noDependencies;
|
||||
}
|
||||
|
||||
public override CommandResult Execute(string args = "")
|
||||
|
@ -226,7 +238,7 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
|
|||
|
||||
private string BuildArgs()
|
||||
{
|
||||
return $"{BuildProfile} {NoIncremental} \"{_projectPath}\" {OutputOption} {BuildBasePathOption} {ConfigurationOption} {FrameworkOption} {NoHostOption} {NativeOption} {ArchitectureOption} {IlcArgsOption} {IlcPathOption} {AppDepSDKPathOption} {NativeCppModeOption} {CppCompilerFlagsOption}";
|
||||
return $"{BuildProfile} {NoDependencies} {NoIncremental} \"{_projectPath}\" {OutputOption} {BuildBasePathOption} {ConfigurationOption} {FrameworkOption} {NoHostOption} {NativeOption} {ArchitectureOption} {IlcArgsOption} {IlcPathOption} {AppDepSDKPathOption} {NativeCppModeOption} {CppCompilerFlagsOption}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
using Microsoft.DotNet.Tools.Test.Utilities;
|
||||
using Xunit;
|
||||
|
@ -13,7 +14,7 @@ namespace Microsoft.DotNet.Tools.Builder.Tests
|
|||
{
|
||||
public class ProjectToProjectDependenciesIncrementalTest : IncrementalTestBase
|
||||
{
|
||||
private string[] _projects = new[] { "L0", "L11", "L12", "L21", "L22" };
|
||||
private readonly string[] _projects = new[] { "L0", "L11", "L12", "L21", "L22" };
|
||||
|
||||
public ProjectToProjectDependenciesIncrementalTest() : base(
|
||||
Path.Combine(AppContext.BaseDirectory, "TestAssets", "TestProjects", "TestProjectToProjectDependencies"),
|
||||
|
@ -48,6 +49,38 @@ namespace Microsoft.DotNet.Tools.Builder.Tests
|
|||
AssertRebuilt(result3, expectedRebuiltProjects);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestNoDependencyFlag()
|
||||
{
|
||||
var dependencies = new[] { "L11", "L12", "L21", "L22" };
|
||||
|
||||
// first clean build; all projects required compilation
|
||||
var result1 = BuildProject();
|
||||
AssertRebuilt(result1, _projects);
|
||||
|
||||
// modify the source code of a leaf dependency
|
||||
TouchSourcesOfProject("L22");
|
||||
|
||||
// second build with no dependencies and no incremental; only the root rebuilds
|
||||
var result2 = BuildProject(noDependencies: true, noIncremental: true);
|
||||
result2.Should().StdOutMatchPattern("Compiling.*L0.*");
|
||||
|
||||
AssertResultDoesNotContainStrings(result2, dependencies);
|
||||
|
||||
// third build with no dependencies but incremental; nothing rebuilds
|
||||
var result3 = BuildProject(noDependencies: true);
|
||||
result3.Should().HaveSkippedProjectCompilation("L0");
|
||||
AssertResultDoesNotContainStrings(result3, dependencies);
|
||||
}
|
||||
|
||||
private static void AssertResultDoesNotContainStrings(CommandResult commandResult, string[] strings)
|
||||
{
|
||||
foreach (var s in strings)
|
||||
{
|
||||
commandResult.StdOut.Should().NotContain(s);
|
||||
}
|
||||
}
|
||||
|
||||
// compute A - B
|
||||
private T[] SetDifference<T>(T[] A, T[] B)
|
||||
{
|
||||
|
|
|
@ -46,11 +46,11 @@ namespace Microsoft.DotNet.Tools.Builder.Tests
|
|||
File.SetLastWriteTimeUtc(file, DateTime.UtcNow);
|
||||
}
|
||||
|
||||
protected CommandResult BuildProject(bool noIncremental = false, bool expectBuildFailure = false)
|
||||
protected CommandResult BuildProject(bool noDependencies = false, bool noIncremental = false, bool expectBuildFailure = false)
|
||||
{
|
||||
var mainProjectFile = GetProjectFile(MainProject);
|
||||
|
||||
var buildCommand = new BuildCommand(mainProjectFile, output: GetBinRoot(), framework: "dnxcore50", noIncremental : noIncremental);
|
||||
var buildCommand = new BuildCommand(mainProjectFile, output: GetBinRoot(), framework: "dnxcore50", noIncremental : noIncremental, noDependencies : noDependencies);
|
||||
var result = buildCommand.ExecuteWithCapturedOutput();
|
||||
|
||||
if (!expectBuildFailure)
|
||||
|
|
Loading…
Reference in a new issue