Skip to content
This repository was archived by the owner on Jan 12, 2024. It is now read-only.

Commit 281b6e2

Browse files
committed
Added support for opening binlog on when test build runs
A new `[OpenBuildLog]` can be used to annotate `[Fact]/[Theory]` tests so that right after a build happens, the automatically generated .binlog is opened using Process.Start. This will only work if the viewer is installed from http://msbuildlog.com. The log is not generated unless the attribute is present, so there's no overhead unless it's used. Also, even if you forget the attribute and commit/push, on release builds, the attribute is ignored and the log is not launched.
1 parent c1a64fa commit 281b6e2

4 files changed

Lines changed: 79 additions & 10 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ log.txt
2626
test.proj
2727
*.log
2828
*.g
29+
*.binlog

src/Build/NuGet.Build.Packaging.Tests/Builder.NuGetizer.cs

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Diagnostics;
34
using System.IO;
45
using System.Linq;
56
using System.Reflection;
67
using System.Threading;
78
using Microsoft.Build.Execution;
89
using Microsoft.Build.Framework;
10+
using Microsoft.Build.Logging.StructuredLogger;
911
using NuGet.Build.Packaging;
1012
using Xunit;
1113
using Xunit.Abstractions;
@@ -16,7 +18,13 @@
1618
/// </summary>
1719
static partial class Builder
1820
{
19-
public static TargetResult BuildScenario(string scenarioName, object properties = null, string projectName = null, string target = "GetPackageContents", ITestOutputHelper output = null, LoggerVerbosity? verbosity = null)
21+
public static TargetResult BuildScenario(
22+
string scenarioName,
23+
object properties = null,
24+
string projectName = null,
25+
string target = "GetPackageContents",
26+
ITestOutputHelper output = null,
27+
LoggerVerbosity? verbosity = null)
2028
{
2129
var scenarioDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Scenarios", scenarioName);
2230

@@ -55,16 +63,22 @@ public static TargetResult BuildScenario(string scenarioName, object properties
5563
logger = new TestOutputLogger(null);
5664
}
5765

66+
var loggers = OpenBuildLogAttribute.IsActive ?
67+
new ILogger[] { logger, new StructuredLogger { Verbosity = verbosity.GetValueOrDefault(), Parameters = scenarioName + ".binlog" } } :
68+
new ILogger[] { logger };
69+
5870
var buildProps = properties?.GetType().GetProperties()
5971
.ToDictionary(prop => prop.Name, prop => prop.GetValue(properties).ToString())
6072
?? new Dictionary<string, string>();
6173

6274
buildProps[nameof(ThisAssembly.Project.Properties.NuGetRestoreTargets)] = ThisAssembly.Project.Properties.NuGetRestoreTargets;
6375
buildProps[nameof(ThisAssembly.Project.Properties.NuGetTargets)] = ThisAssembly.Project.Properties.NuGetTargets;
6476

65-
return new TargetResult(projectOrSolution, Build(projectOrSolution, target,
66-
properties: buildProps,
67-
logger: logger), target, logger);
77+
var result = Build(projectOrSolution, target, buildProps, loggers);
78+
if (OpenBuildLogAttribute.IsActive)
79+
Process.Start(scenarioName + ".binlog");
80+
81+
return new TargetResult(projectOrSolution, result, target, logger);
6882
}
6983

7084
public class TargetResult : ITargetResult
@@ -114,6 +128,58 @@ public override string ToString()
114128
}
115129
}
116130

131+
/// <summary>
132+
/// Declaratively specifies that the .binlog for the build
133+
/// should be opened automatically after building a project.
134+
/// </summary>
135+
public class OpenBuildLogAttribute : BeforeAfterTestAttribute
136+
{
137+
/// <summary>
138+
/// Whether the attribute is active for the current test.
139+
/// </summary>
140+
public static bool IsActive
141+
{
142+
get
143+
{
144+
#if DEBUG
145+
var data = Thread.GetNamedDataSlot(nameof(OpenBuildLogAttribute));
146+
if (data == null)
147+
return false;
148+
149+
return Thread.GetData(data) != null;
150+
#else
151+
return false;
152+
#endif
153+
}
154+
}
155+
156+
public override void Before(MethodInfo methodUnderTest)
157+
{
158+
// Don't ever set this flag on release builds just in case
159+
// we forget the attribute in a commit ;)
160+
#if DEBUG
161+
var data = Thread.GetNamedDataSlot(nameof(OpenBuildLogAttribute));
162+
if (data == null)
163+
data = Thread.AllocateNamedDataSlot(nameof(OpenBuildLogAttribute));
164+
165+
Thread.SetData(data, new object());
166+
#endif
167+
168+
base.Before(methodUnderTest);
169+
}
170+
171+
public override void After(MethodInfo methodUnderTest)
172+
{
173+
#if DEBUG
174+
var data = Thread.GetNamedDataSlot(nameof(OpenBuildLogAttribute));
175+
if (data != null)
176+
Thread.SetData(data, null);
177+
#endif
178+
179+
base.After(methodUnderTest);
180+
}
181+
}
182+
117183
/// <summary>
118184
/// Allows declaratively increasing the MSBuild logger verbosity. In
119185
/// order to output anything at all, the BuildScenario must be called

src/Build/NuGet.Build.Packaging.Tests/Builder.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public static partial class Builder
2020
const string ToolsVersion = "14.0";
2121
#endif
2222

23-
public static BuildResult Build(ProjectInstance project, string targets, Dictionary<string, string> properties = null, ILogger logger = null)
23+
public static BuildResult Build(ProjectInstance project, string targets, Dictionary<string, string> properties = null, ILogger[] loggers = null)
2424
{
2525
properties = properties ?? new Dictionary<string, string>();
2626

@@ -42,16 +42,16 @@ public static BuildResult Build(ProjectInstance project, string targets, Diction
4242
LogTaskInputs = true,
4343
};
4444

45-
if (logger != null)
46-
parameters.Loggers = new[] { logger };
45+
if (loggers != null)
46+
parameters.Loggers = loggers;
4747

4848
var result = manager.Build(parameters, request);
4949
//manager.ShutdownAllNodes();
5050
return result;
5151
}
5252
}
5353

54-
public static BuildResult Build(string projectOrSolution, string targets, Dictionary<string, string> properties = null, ILogger logger = null)
54+
public static BuildResult Build(string projectOrSolution, string targets, Dictionary<string, string> properties = null, ILogger[] loggers = null)
5555
{
5656
if (!Path.IsPathRooted(projectOrSolution))
5757
projectOrSolution = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, projectOrSolution);
@@ -67,7 +67,7 @@ public static BuildResult Build(string projectOrSolution, string targets, Dictio
6767

6868
var projectInstance = new ProjectInstance(projectOrSolution, properties, ToolsVersion);
6969

70-
return Build(projectInstance, targets, properties, logger);
70+
return Build(projectInstance, targets, properties, loggers);
7171
}
7272

7373
static void AddSolutionConfiguration(string projectFile, Dictionary<string, string> properties)

src/Build/NuGet.Build.Packaging.Tests/NuGet.Build.Packaging.Tests.csproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@
1515
<ItemGroup>
1616
<PackageReference Include="GitInfo" Version="1.1.61" />
1717
<PackageReference Include="Microsoft.Build" Version="14.3.0" />
18+
<PackageReference Include="Microsoft.Build.Logging.StructuredLogger" Version="1.1.168" />
1819
<PackageReference Include="Microsoft.Build.Tasks.Core" Version="14.3.0" />
19-
<PackageReference Include="MSBuilder.Dump" Version="*" />
20+
<PackageReference Include="MSBuilder.CodeTaskAssembly" Version="*" />
2021
<PackageReference Include="MSBuilder.DumpItems" Version="*" />
2122
<PackageReference Include="MSBuilder.Introspect" Version="*" />
2223
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
@@ -33,6 +34,7 @@
3334
<ItemGroup>
3435
<None Remove="Scenarios\**\obj\**\*.*;Scenarios\**\bin\**\*.*" />
3536
<None Remove="App.config" />
37+
<None Include="Scenarios\**\*.*proj;Scenarios\**\*.sln" />
3638
</ItemGroup>
3739
<ItemGroup>
3840
<Content Include="App.config">

0 commit comments

Comments
 (0)