Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for capturing MSBuild properties, items and target results #4329

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using Cake.Common.Tools.DotNet.MSBuild;

namespace Cake.Common.Tests.Fixtures.Tools.DotNet.MSBuild
{
internal sealed class DotNetMSBuildBuilderFixture : DotNetFixture<DotNetMSBuildSettings>
{
public string Project { get; set; }
public Action<IEnumerable<string>> StandardOutputAction { get; set; }

protected override void RunTool()
{
var tool = new DotNetMSBuildBuilder(FileSystem, Environment, ProcessRunner, Tools);
tool.Build(Project, Settings);
tool.Build(Project, Settings, StandardOutputAction);
}
}
}
4 changes: 3 additions & 1 deletion src/Cake.Common.Tests/Fixtures/Tools/MSBuildRunnerFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using Cake.Common.Tools.MSBuild;
using Cake.Core;
Expand All @@ -15,6 +16,7 @@ internal sealed class MSBuildRunnerFixture : ToolFixture<MSBuildSettings>
{
public HashSet<FilePath> KnownMSBuildPaths { get; }
public FilePath Solution { get; set; }
public Action<IEnumerable<string>> StandardOutputAction { get; set; }

public MSBuildRunnerFixture(bool is64BitOperativeSystem, PlatformFamily platformFamily)
: base("MSBuild.exe")
Expand Down Expand Up @@ -83,7 +85,7 @@ public void GivenMSBuildIsNotInstalled()
protected override void RunTool()
{
var runner = new MSBuildRunner(FileSystem, Environment, ProcessRunner, Tools);
runner.Run(Solution, Settings);
runner.Run(Solution, Settings, StandardOutputAction);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using Cake.Common.Tests.Fixtures.Tools;
using Cake.Common.Tests.Fixtures.Tools.DotNet.MSBuild;
using Cake.Common.Tools.DotNet;
Expand Down Expand Up @@ -205,6 +206,113 @@ public void Should_Throw_If_Property_Has_No_Value(string[] propertyValues)
AssertEx.IsArgumentException(result, "Properties", "A property must have at least one non-empty value");
}

[Fact]
public void Should_Append_GetProperty_To_Process_Arguments_And_Collects_Output()
{
IEnumerable<string> msbuildOutput = null;

// Given
var fixture = new DotNetMSBuildBuilderFixture();
fixture.Settings.WithGetProperty("A");
fixture.Settings.WithGetProperty("B");
fixture.StandardOutputAction = lines => msbuildOutput = lines;
var standardOutput = new string[]
{
"{",
" \"Properties\": {",
" \"A\": \"A value\",",
" \"B\": \"B value\"",
" }",
"}",
};
fixture.ProcessRunner.Process.SetStandardOutput(standardOutput);

// When
var result = fixture.Run();

// Then
Assert.Equal("msbuild /getProperty:A /getProperty:B",
result.Args);

Assert.Equal(standardOutput, msbuildOutput);
}

[Fact]
public void Should_Append_GetItem_To_Process_Arguments_And_Collects_Output()
{
IEnumerable<string> msbuildOutput = null;

// Given
var fixture = new DotNetMSBuildBuilderFixture();
fixture.Settings.WithGetItem("A");
fixture.Settings.WithGetItem("B");
fixture.StandardOutputAction = lines => msbuildOutput = lines;
var standardOutput = new string[]
{
"{",
" \"Items\": {",
" \"A\": [",
" {",
" \"Identity\": \"Identity value\"",
" }",
" ],",
" \"B\": [",
" {",
" \"Identity\": \"Identity value\"",
" }",
" ],",
" }",
"}",
};
fixture.ProcessRunner.Process.SetStandardOutput(standardOutput);

// When
var result = fixture.Run();

// Then
Assert.Equal("msbuild /getItem:A /getItem:B",
result.Args);

Assert.Equal(standardOutput, msbuildOutput);
}

[Fact]
public void Should_Append_GetTargetResult_To_Process_Arguments_And_Collects_Output()
{
IEnumerable<string> msbuildOutput = null;

// Given
var fixture = new DotNetMSBuildBuilderFixture();
fixture.Settings.WithGetTargetResult("A");
fixture.Settings.WithGetTargetResult("B");
fixture.StandardOutputAction = lines => msbuildOutput = lines;
var standardOutput = new string[]
{
"{",
" \"TargetResults\": {",
" \"A\": {",
" \"Result\": \"Success\"",
" \"Items\": []",
" },",
" \"B\": {",
" \"Result\": \"Success\"",
" \"Items\": []",
" }",
" }",
"}",
};
fixture.ProcessRunner.Process.SetStandardOutput(standardOutput);

// When
var result = fixture.Run();

// Then
Assert.Equal("msbuild /getTargetResult:A /getTargetResult:B",
result.Args);

Assert.Equal(standardOutput, msbuildOutput);
}

[Theory]
[InlineData(0)]
[InlineData(-10)]
Expand Down
108 changes: 108 additions & 0 deletions src/Cake.Common.Tests/Unit/Tools/MSBuild/MSBuildRunnerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using Cake.Common.Tests.Fixtures.Tools;
using Cake.Common.Tools.MSBuild;
using Cake.Core;
Expand Down Expand Up @@ -1014,6 +1015,113 @@ public void Should_Not_Escape_Semicolons_For_Specified_Property_Arguments_When_A
"\"C:/Working/src/Solution.sln\"", result.Args);
}

[Fact]
public void Should_Append_GetProperty_To_Process_Arguments_And_Collects_Output()
{
IEnumerable<string> msbuildOutput = null;

// Given
var fixture = new MSBuildRunnerFixture(false, PlatformFamily.Windows);
fixture.Settings.WithGetProperty("A");
fixture.Settings.WithGetProperty("B");
fixture.StandardOutputAction = lines => msbuildOutput = lines;
var standardOutput = new string[]
{
"{",
" \"Properties\": {",
" \"A\": \"A value\",",
" \"B\": \"B value\"",
" }",
"}",
};
fixture.ProcessRunner.Process.SetStandardOutput(standardOutput);

// When
var result = fixture.Run();

// Then
Assert.Equal("/v:normal /target:Build /getProperty:A /getProperty:B " +
"\"C:/Working/src/Solution.sln\"", result.Args);

Assert.Equal(standardOutput, msbuildOutput);
}

[Fact]
public void Should_Append_GetItem_To_Process_Arguments_And_Collects_Output()
{
IEnumerable<string> msbuildOutput = null;

// Given
var fixture = new MSBuildRunnerFixture(false, PlatformFamily.Windows);
fixture.Settings.WithGetItem("A");
fixture.Settings.WithGetItem("B");
fixture.StandardOutputAction = lines => msbuildOutput = lines;
var standardOutput = new string[]
{
"{",
" \"Items\": {",
" \"A\": [",
" {",
" \"Identity\": \"Identity value\"",
" }",
" ],",
" \"B\": [",
" {",
" \"Identity\": \"Identity value\"",
" }",
" ],",
" }",
"}",
};
fixture.ProcessRunner.Process.SetStandardOutput(standardOutput);

// When
var result = fixture.Run();

// Then
Assert.Equal("/v:normal /target:Build /getItem:A /getItem:B " +
"\"C:/Working/src/Solution.sln\"", result.Args);

Assert.Equal(standardOutput, msbuildOutput);
}

[Fact]
public void Should_Append_GetTargetResult_To_Process_Arguments_And_Collects_Output()
{
IEnumerable<string> msbuildOutput = null;

// Given
var fixture = new MSBuildRunnerFixture(false, PlatformFamily.Windows);
fixture.Settings.WithGetTargetResult("A");
fixture.Settings.WithGetTargetResult("B");
fixture.StandardOutputAction = lines => msbuildOutput = lines;
var standardOutput = new string[]
{
"{",
" \"TargetResults\": {",
" \"A\": {",
" \"Result\": \"Success\"",
" \"Items\": []",
" },",
" \"B\": {",
" \"Result\": \"Success\"",
" \"Items\": []",
" }",
" }",
"}",
};
fixture.ProcessRunner.Process.SetStandardOutput(standardOutput);

// When
var result = fixture.Run();

// Then
Assert.Equal("/v:normal /target:Build /getTargetResult:A /getTargetResult:B " +
"\"C:/Working/src/Solution.sln\"", result.Args);

Assert.Equal(standardOutput, msbuildOutput);
}

[Theory]
[InlineData("Release", "/v:normal /p:Configuration=\"Release\" /target:Build \"C:/Working/src/Solution.sln\"")]
[InlineData("Custom Spaced", "/v:normal /p:Configuration=\"Custom Spaced\" /target:Build \"C:/Working/src/Solution.sln\"")]
Expand Down
72 changes: 70 additions & 2 deletions src/Cake.Common/Tools/DotNet/DotNetAliases.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1210,7 +1210,7 @@ public static void DotNetRun(this ICakeContext context, string project, DotNetRu
[CakeNamespaceImport("Cake.Common.Tools.DotNet.MSBuild")]
public static void DotNetMSBuild(this ICakeContext context)
{
context.DotNetMSBuild(null, null);
context.DotNetMSBuild((string)null, (DotNetMSBuildSettings)null);
}

/// <summary>
Expand Down Expand Up @@ -1263,6 +1263,32 @@ public static void DotNetMSBuild(this ICakeContext context, DotNetMSBuildSetting
context.DotNetMSBuild(null, settings);
}

/// <summary>
/// Builds the specified targets in a project file found in the current working directory.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="settings">The settings.</param>
/// <param name="standardOutputAction">The action to invoke with the standard output.</param>
/// <example>
/// <code>
/// var settings = new DotNetMSBuildSettings
/// {
/// NoLogo = true,
/// MaxCpuCount = -1
/// };
///
/// DotNetMSBuild(settings,
/// output => foreach(var line in output) outputBuilder.AppendLine(line));
/// </code>
/// </example>
[CakeMethodAlias]
[CakeAliasCategory("MSBuild")]
[CakeNamespaceImport("Cake.Common.Tools.DotNet.MSBuild")]
public static void DotNetMSBuild(this ICakeContext context, DotNetMSBuildSettings settings, Action<IEnumerable<string>> standardOutputAction)
{
context.DotNetMSBuild(null, settings, standardOutputAction);
}

/// <summary>
/// Builds the specified targets in the project file.
/// </summary>
Expand Down Expand Up @@ -1300,7 +1326,49 @@ public static void DotNetMSBuild(this ICakeContext context, string projectOrDire
}

var builder = new DotNetMSBuildBuilder(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools);
builder.Build(projectOrDirectory, settings);
builder.Build(projectOrDirectory, settings, null);
}

/// <summary>
/// Builds the specified targets in the project file.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="projectOrDirectory">Project file or directory to search for project file.</param>
/// <param name="settings">The settings.</param>
/// <param name="standardOutputAction">The action to invoke with the standard output.</param>
/// <example>
/// <code>
/// var settings = new DotNetMSBuildSettings
/// {
/// NoLogo = true,
/// MaxCpuCount = -1
/// };
///
/// DotNetMSBuild("foobar.proj", settings,
/// output => foreach(var line in output) outputBuilder.AppendLine(line));
/// </code>
/// </example>
/// <remarks>
/// If a project file is not specified, MSBuild searches the current working directory for a file that has a file
/// extension that ends in "proj" and uses that file. If a directory is specified, MSBuild searches that directory for a project file.
/// </remarks>
[CakeMethodAlias]
[CakeAliasCategory("MSBuild")]
[CakeNamespaceImport("Cake.Common.Tools.DotNet.MSBuild")]
public static void DotNetMSBuild(this ICakeContext context, string projectOrDirectory, DotNetMSBuildSettings settings, Action<IEnumerable<string>> standardOutputAction)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}

if (settings is null)
{
settings = new DotNetMSBuildSettings();
}

var builder = new DotNetMSBuildBuilder(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools);
builder.Build(projectOrDirectory, settings, standardOutputAction);
}

/// <summary>
Expand Down
Loading
Loading