Merge pull request #1272 from cdmihai/cdmihai/noDependencies

Add --no-dependency flag to build
This commit is contained in:
Piotr Puszkiewicz 2016-02-12 10:59:54 -08:00
commit 82bbd22d46
7 changed files with 109 additions and 47 deletions

View file

@ -8,15 +8,18 @@ namespace Microsoft.DotNet.Tools.Build
internal class BuilderCommandApp : CompilerCommandApp internal class BuilderCommandApp : CompilerCommandApp
{ {
public const string BuildProfileFlag = "--build-profile"; 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 ShouldPrintIncrementalPreconditions => OptionHasValue(BuildProfileFlag);
public bool ForceUnsafeValue => OptionHasValue(ForceUnsafeFlag); public bool ShouldNotUseIncrementality => OptionHasValue(NoIncrementalFlag);
public bool ShouldSkipDependencies => OptionHasValue(NoDependenciesFlag);
public BuilderCommandApp(string name, string fullName, string description) : base(name, fullName, description) 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(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");
} }
} }
} }

View file

@ -23,9 +23,9 @@ namespace Microsoft.DotNet.Tools.Build
public static readonly string[] KnownCompilers = { "csc", "vbc", "fsc" }; public static readonly string[] KnownCompilers = { "csc", "vbc", "fsc" };
private readonly ProjectContext _rootProject; private readonly ProjectContext _rootProject;
private readonly ProjectDependenciesFacade _rootProjectDependencies;
private readonly BuilderCommandApp _args; private readonly BuilderCommandApp _args;
private readonly IncrementalPreconditions _preconditions; private readonly IncrementalPreconditions _preconditions;
private readonly ProjectDependenciesFacade _dependencies;
public bool IsSafeForIncrementalCompilation => !_preconditions.PreconditionsDetected(); public bool IsSafeForIncrementalCompilation => !_preconditions.PreconditionsDetected();
@ -37,11 +37,8 @@ namespace Microsoft.DotNet.Tools.Build
// and then reasoning which ones to get from args and which ones from fields. // and then reasoning which ones to get from args and which ones from fields.
_args = (BuilderCommandApp)args.ShallowCopy(); _args = (BuilderCommandApp)args.ShallowCopy();
_args.OutputValue = _args.OutputValue;
_args.BuildBasePathValue = _args.BuildBasePathValue;
// Set up dependencies // Set up dependencies
_dependencies = new ProjectDependenciesFacade(_rootProject, _args.ConfigValue); _rootProjectDependencies = new ProjectDependenciesFacade(_rootProject, _args.ConfigValue);
// gather preconditions // gather preconditions
_preconditions = GatherIncrementalPreconditions(); _preconditions = GatherIncrementalPreconditions();
@ -51,17 +48,36 @@ namespace Microsoft.DotNet.Tools.Build
{ {
CreateOutputDirectories(); CreateOutputDirectories();
// compile dependencies return CompileDendencies(incremental) && CompileRootProject(incremental);
foreach (var dependency in Sort(_dependencies.ProjectDependenciesWithSources)) }
{
if (incremental)
{
var dependencyProjectContext = ProjectContext.Create(dependency.Path, dependency.Framework, new[] { _rootProject.RuntimeIdentifier });
if (!NeedsRebuilding(dependencyProjectContext, new ProjectDependenciesFacade(dependencyProjectContext, _args.ConfigValue))) private bool CompileRootProject(bool incremental)
{ {
continue; 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)) if (!InvokeCompileOnDependency(dependency))
@ -70,28 +86,18 @@ namespace Microsoft.DotNet.Tools.Build
} }
} }
if (incremental && !NeedsRebuilding(_rootProject, _dependencies)) return true;
{ }
// todo: what if the previous build had errors / warnings and nothing changed? Need to propagate them in case of incremental
return true;
}
// compile project private bool DependencyNeedsRebuilding(ProjectDescription dependency)
var success = InvokeCompileOnRootProject(); {
var dependencyProjectContext = ProjectContext.Create(dependency.Path, dependency.Framework, new[] { _rootProject.RuntimeIdentifier });
PrintSummary(success); return NeedsRebuilding(dependencyProjectContext, new ProjectDependenciesFacade(dependencyProjectContext, _args.ConfigValue));
return success;
} }
private bool NeedsRebuilding(ProjectContext project, ProjectDependenciesFacade dependencies) private bool NeedsRebuilding(ProjectContext project, ProjectDependenciesFacade dependencies)
{ {
return NeedsRebuilding(project, dependencies, _args.BuildBasePathValue); var compilerIO = GetCompileIO(project, _args.ConfigValue, _args.BuildBasePathValue, _args.OutputValue, dependencies, project == _rootProject);
}
private bool NeedsRebuilding(ProjectContext project, ProjectDependenciesFacade dependencies, string baseBuildPath)
{
var compilerIO = GetCompileIO(project, _args.ConfigValue, baseBuildPath, _args.OutputValue, dependencies, project == _rootProject);
// rebuild if empty inputs / outputs // rebuild if empty inputs / outputs
if (!(compilerIO.Outputs.Any() && compilerIO.Inputs.Any())) if (!(compilerIO.Outputs.Any() && compilerIO.Inputs.Any()))
@ -195,9 +201,9 @@ namespace Microsoft.DotNet.Tools.Build
private IncrementalPreconditions GatherIncrementalPreconditions() private IncrementalPreconditions GatherIncrementalPreconditions()
{ {
var preconditions = new IncrementalPreconditions(_args.BuildProfileValue); var preconditions = new IncrementalPreconditions(_args.ShouldPrintIncrementalPreconditions);
if (_args.ForceUnsafeValue) if (_args.ShouldNotUseIncrementality)
{ {
preconditions.AddForceUnsafePrecondition(); 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 // check the entire project tree that needs to be compiled, duplicated for each framework
private List<ProjectContext> GetProjectsToCheck() private List<ProjectContext> GetProjectsToCheck()
{ {
if (_args.ShouldSkipDependencies)
{
return new List<ProjectContext>(1) { _rootProject };
}
// include initial root project // 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 // convert ProjectDescription to ProjectContext
var dependencyContexts = _dependencies.ProjectDependenciesWithSources.Select var dependencyContexts = _rootProjectDependencies.ProjectDependenciesWithSources.Select
(keyValuePair => ProjectContext.Create(keyValuePair.Value.Path, keyValuePair.Value.Framework)); (keyValuePair => ProjectContext.Create(keyValuePair.Value.Path, keyValuePair.Value.Framework));
contextsToCheck.AddRange(dependencyContexts); contextsToCheck.AddRange(dependencyContexts);

View file

@ -36,7 +36,7 @@ namespace Microsoft.DotNet.Tools.Build
public void AddForceUnsafePrecondition() 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() public bool PreconditionsDetected()

View file

@ -33,3 +33,6 @@ Prints out the incremental safety checks that users need to address in order for
--no-incremental --no-incremental
Marks the build as unsafe for incrementality. This turns off incremental compilation and forces a clean rebuild of the project dependency graph. 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.

View file

@ -26,6 +26,7 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
private string _cppCompilerFlags; private string _cppCompilerFlags;
private bool _buildProfile; private bool _buildProfile;
private bool _noIncremental; private bool _noIncremental;
private bool _noDependencies;
private string OutputOption private string OutputOption
{ {
@ -166,6 +167,16 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
} }
} }
private string NoDependencies
{
get
{
return _noDependencies ?
"--no-dependencies" :
"";
}
}
public BuildCommand( public BuildCommand(
string projectPath, string projectPath,
string output="", string output="",
@ -181,11 +192,11 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
bool nativeCppMode=false, bool nativeCppMode=false,
string cppCompilerFlags="", string cppCompilerFlags="",
bool buildProfile=true, bool buildProfile=true,
bool noIncremental=false bool noIncremental=false,
bool noDependencies=false
) )
: base("dotnet") : base("dotnet")
{ {
_projectPath = projectPath; _projectPath = projectPath;
_project = ProjectReader.GetProject(projectPath); _project = ProjectReader.GetProject(projectPath);
@ -203,6 +214,7 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
_cppCompilerFlags = cppCompilerFlags; _cppCompilerFlags = cppCompilerFlags;
_buildProfile = buildProfile; _buildProfile = buildProfile;
_noIncremental = noIncremental; _noIncremental = noIncremental;
_noDependencies = noDependencies;
} }
public override CommandResult Execute(string args = "") public override CommandResult Execute(string args = "")
@ -226,7 +238,7 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
private string BuildArgs() 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}";
} }
} }
} }

View file

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
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;
@ -13,7 +14,7 @@ namespace Microsoft.DotNet.Tools.Builder.Tests
{ {
public class ProjectToProjectDependenciesIncrementalTest : IncrementalTestBase 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( public ProjectToProjectDependenciesIncrementalTest() : base(
Path.Combine(AppContext.BaseDirectory, "TestAssets", "TestProjects", "TestProjectToProjectDependencies"), Path.Combine(AppContext.BaseDirectory, "TestAssets", "TestProjects", "TestProjectToProjectDependencies"),
@ -48,6 +49,38 @@ namespace Microsoft.DotNet.Tools.Builder.Tests
AssertRebuilt(result3, expectedRebuiltProjects); 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 // compute A - B
private T[] SetDifference<T>(T[] A, T[] B) private T[] SetDifference<T>(T[] A, T[] B)
{ {

View file

@ -46,11 +46,11 @@ namespace Microsoft.DotNet.Tools.Builder.Tests
File.SetLastWriteTimeUtc(file, DateTime.UtcNow); 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 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(); var result = buildCommand.ExecuteWithCapturedOutput();
if (!expectBuildFailure) if (!expectBuildFailure)