Skip to content

Commit 9100970

Browse files
committed
Use a wrapper project to build the source project.
1 parent 4641790 commit 9100970

File tree

7 files changed

+62
-87
lines changed

7 files changed

+62
-87
lines changed

src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ private static CoreRuntime GetPlatformSpecific(CoreRuntime fallback, Assembly? a
232232
? new CoreRuntime(fallback.RuntimeMoniker, $"{fallback.MsBuildMoniker}-{platform}", fallback.Name)
233233
: fallback;
234234

235-
internal static bool TryGetTargetPlatform(Assembly? assembly, [NotNullWhen(true)] out string? platform)
235+
private static bool TryGetTargetPlatform(Assembly? assembly, [NotNullWhen(true)] out string? platform)
236236
{
237237
platform = null;
238238

@@ -252,11 +252,8 @@ internal static bool TryGetTargetPlatform(Assembly? assembly, [NotNullWhen(true)
252252
if (platformNameProperty is null)
253253
return false;
254254

255-
if (platformNameProperty.GetValue(attributeInstance) is not string platformName)
256-
return false;
257-
258-
platform = platformName;
259-
return true;
255+
platform = platformNameProperty.GetValue(attributeInstance) as string;
256+
return platform.IsNotBlank();
260257
}
261258
}
262259
}

src/BenchmarkDotNet/Helpers/FrameworkVersionHelper.cs

Lines changed: 0 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
using System;
2-
using System.Diagnostics.CodeAnalysis;
32
using System.IO;
43
using System.Linq;
54
using System.Reflection;
65
using System.Runtime.Versioning;
7-
using BenchmarkDotNet.Environments;
86
using Microsoft.Win32;
97

108
namespace BenchmarkDotNet.Helpers
@@ -124,44 +122,5 @@ private static bool IsDeveloperPackInstalled(string version) => Directory.Exists
124122
Environment.Is64BitOperatingSystem
125123
? Environment.SpecialFolder.ProgramFilesX86
126124
: Environment.SpecialFolder.ProgramFiles);
127-
128-
internal static string? GetTfm(Assembly assembly)
129-
{
130-
// We don't support exotic frameworks like Silverlight, WindowsPhone, Xamarin.Mac, etc.
131-
const string CorePrefix = ".NETCoreApp,Version=v";
132-
const string FrameworkPrefix = ".NETFramework,Version=v";
133-
const string StandardPrefix = ".NETStandard,Version=v";
134-
135-
// Look for a TargetFrameworkAttribute with a supported Framework version.
136-
string? framework = assembly.GetCustomAttribute<TargetFrameworkAttribute>()?.FrameworkName;
137-
if (TryParseVersion(CorePrefix, out var version))
138-
{
139-
return version.Major < 5
140-
? $"netcoreapp{version.Major}.{version.Minor}"
141-
: CoreRuntime.TryGetTargetPlatform(assembly, out var platform)
142-
? $"net{version.Major}.{version.Minor}-{platform}"
143-
: $"net{version.Major}.{version.Minor}";
144-
}
145-
if (TryParseVersion(FrameworkPrefix, out version))
146-
{
147-
return version.Build > 0
148-
? $"net{version.Major}{version.Minor}{version.Build}"
149-
: $"net{version.Major}{version.Minor}";
150-
}
151-
if (!TryParseVersion(StandardPrefix, out version))
152-
{
153-
return $"netstandard{version.Major}.{version.Minor}";
154-
}
155-
156-
// TargetFrameworkAttribute not found, or the assembly targeted a framework we don't support.
157-
return null;
158-
159-
bool TryParseVersion(string prefix, [NotNullWhen(true)] out Version? version)
160-
{
161-
version = null;
162-
return framework?.StartsWith(prefix) == true
163-
&& Version.TryParse(framework[prefix.Length..], out version);
164-
}
165-
}
166125
}
167126
}

src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs

Lines changed: 48 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ protected override void GenerateBuildScript(BuildPartition buildPartition, Artif
7171

7272
var content = new StringBuilder(300)
7373
.AppendLine($"call {CliPath ?? "dotnet"} {DotNetCliCommand.GetRestoreCommand(artifactsPaths, buildPartition, projectFilePath)}")
74-
.AppendLine($"call {CliPath ?? "dotnet"} {DotNetCliCommand.GetPublishCommand(artifactsPaths, buildPartition, projectFilePath, TargetFrameworkMoniker)}")
74+
.AppendLine($"call {CliPath ?? "dotnet"} {DotNetCliCommand.GetBuildCommand(artifactsPaths, buildPartition, projectFilePath, TargetFrameworkMoniker)}")
7575
.AppendLine($"call {CliPath ?? "dotnet"} {DotNetCliCommand.GetRestoreCommand(artifactsPaths, buildPartition, artifactsPaths.ProjectFilePath)}")
76-
.AppendLine($"call {CliPath ?? "dotnet"} {DotNetCliCommand.GetPublishCommand(artifactsPaths, buildPartition, artifactsPaths.ProjectFilePath, TargetFrameworkMoniker)}")
76+
.AppendLine($"call {CliPath ?? "dotnet"} {DotNetCliCommand.GetBuildCommand(artifactsPaths, buildPartition, artifactsPaths.ProjectFilePath, TargetFrameworkMoniker)}")
7777
.ToString();
7878

7979
File.WriteAllText(artifactsPaths.BuildScriptFilePath, content);
@@ -105,37 +105,63 @@ protected override void GenerateProject(BuildPartition buildPartition, Artifacts
105105
// Integration tests are built without dependencies, so we skip gathering dlls.
106106
if (!buildPartition.ForcedNoDependenciesForIntegrationTests)
107107
{
108-
GatherReferences(projectFile.FullName, buildPartition, artifactsPaths, logger);
108+
GatherReferences(buildPartition, artifactsPaths, logger);
109109
}
110110
}
111111

112-
protected void GatherReferences(string projectFilePath, BuildPartition buildPartition, ArtifactsPaths artifactsPaths, ILogger logger)
112+
private static string GetDllGathererPath(string filePath)
113+
=> Path.Combine(Path.GetDirectoryName(filePath), $"DllGatherer{Path.GetExtension(filePath)}");
114+
115+
protected void GatherReferences(BuildPartition buildPartition, ArtifactsPaths artifactsPaths, ILogger logger)
113116
{
114-
// Build the original project then reference all of the built dlls.
115-
BuildResult buildResult = BuildProject(TargetFrameworkMoniker);
117+
// Copy csproj template without the generated C# file to build the original project for all necessary runtime dlls.
118+
// We can't just build the original project directly because it could be a library project, so we need an exe project to reference it.
119+
var xmlDoc = new XmlDocument();
120+
xmlDoc.Load(artifactsPaths.ProjectFilePath);
121+
var projectElement = xmlDoc.DocumentElement;
122+
var compileNode = projectElement.SelectSingleNode("ItemGroup/Compile");
123+
string emptyMainFile = GetDllGathererPath(artifactsPaths.ProgramCodePath);
124+
compileNode.Attributes["Include"].Value = emptyMainFile;
125+
string gathererProject = GetDllGathererPath(artifactsPaths.ProjectFilePath);
126+
xmlDoc.Save(gathererProject);
127+
128+
// Generate a C# file with an empty Main method to satisfy the exe build.
129+
File.WriteAllText(emptyMainFile, """
130+
namespace BenchmarkDotNet.Autogenerated
131+
{
132+
public class UniqueProgramName
133+
{
134+
public static int Main(string[] args)
135+
{
136+
return 0;
137+
}
138+
}
139+
}
140+
""");
116141

117-
// The build could fail because the project doesn't have a tfm that matches the runtime, e.g. netstandard2.0 vs net10.0,
118-
// So we try to get the actual tfm of the assembly and build again.
119-
if (!buildResult.IsBuildSuccess
120-
&& FrameworkVersionHelper.GetTfm(buildPartition.RepresentativeBenchmarkCase.Descriptor.Type.Assembly) is { } actualTfm
121-
&& actualTfm != TargetFrameworkMoniker)
122-
{
123-
buildResult = BuildProject(actualTfm);
124-
}
142+
// Build the original project then reference all of the built dlls.
143+
BuildResult buildResult = new DotNetCliCommand(
144+
CliPath,
145+
gathererProject,
146+
TargetFrameworkMoniker,
147+
null,
148+
GenerateResult.Success(artifactsPaths, []),
149+
logger,
150+
buildPartition,
151+
[],
152+
buildPartition.Timeout
153+
).RestoreThenBuild();
125154

126155
if (!buildResult.IsBuildSuccess)
127156
{
128-
if (!buildResult.TryToExplainFailureReason(out string reason))
129-
{
130-
reason = buildResult.ErrorMessage;
131-
}
132-
logger.WriteLineWarning($"Failed to build source project to obtain dll references. Moving forward without it. Reason: {reason}");
133-
return;
157+
throw buildResult.TryToExplainFailureReason(out string reason)
158+
? new Exception(reason)
159+
: new Exception(buildResult.ErrorMessage);
134160
}
135161

136-
var xmlDoc = new XmlDocument();
162+
xmlDoc = new XmlDocument();
137163
xmlDoc.Load(artifactsPaths.ProjectFilePath);
138-
XmlElement projectElement = xmlDoc.DocumentElement;
164+
projectElement = xmlDoc.DocumentElement;
139165
var itemGroup = xmlDoc.CreateElement("ItemGroup");
140166
projectElement.AppendChild(itemGroup);
141167
foreach (var assemblyFile in Directory.GetFiles(artifactsPaths.BinariesDirectoryPath, "*.dll"))
@@ -151,20 +177,6 @@ protected void GatherReferences(string projectFilePath, BuildPartition buildPart
151177
}
152178

153179
xmlDoc.Save(artifactsPaths.ProjectFilePath);
154-
155-
BuildResult BuildProject(string tfm)
156-
=> new DotNetCliCommand(
157-
CliPath,
158-
projectFilePath,
159-
tfm,
160-
null,
161-
GenerateResult.Success(artifactsPaths, []),
162-
logger,
163-
buildPartition,
164-
[],
165-
buildPartition.Timeout
166-
)
167-
.RestoreThenBuild();
168180
}
169181

170182
/// <summary>

src/BenchmarkDotNet/Toolchains/MonoAotLLVM/MonoAotLLVMGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ protected override void GenerateProject(BuildPartition buildPartition, Artifacts
5252

5353
File.WriteAllText(artifactsPaths.ProjectFilePath, content);
5454

55-
GatherReferences(projectFile.FullName, buildPartition, artifactsPaths, logger);
55+
GatherReferences(buildPartition, artifactsPaths, logger);
5656
}
5757

5858
protected override string GetPublishDirectoryPath(string buildArtifactsDirectoryPath, string configuration)

src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ protected void GenerateProjectFile(BuildPartition buildPartition, ArtifactsPaths
6363

6464
File.WriteAllText(artifactsPaths.ProjectFilePath, content);
6565

66-
GatherReferences(projectFile.FullName, buildPartition, artifactsPaths, logger);
66+
GatherReferences(buildPartition, artifactsPaths, logger);
6767
}
6868

6969
protected void GenerateLinkerDescriptionFile(ArtifactsPaths artifactsPaths)

src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ protected override void GenerateProject(BuildPartition buildPartition, Artifacts
119119

120120
File.WriteAllText(artifactsPaths.ProjectFilePath, GenerateProjectForNuGetBuild(projectFile, buildPartition, artifactsPaths, logger));
121121

122-
GatherReferences(projectFile, buildPartition, artifactsPaths, logger);
122+
GatherReferences(buildPartition, artifactsPaths, logger);
123123
GenerateReflectionFile(artifactsPaths);
124124
}
125125

tests/BenchmarkDotNet.IntegrationTests/DisassemblyDiagnoserTests.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,14 @@ private IConfig CreateConfig(Jit jit, Platform platform, IToolchain toolchain, I
173173
.AddJob(Job.Dry.WithJit(jit)
174174
.WithPlatform(platform)
175175
.WithToolchain(toolchain)
176-
.WithStrategy(runStrategy))
176+
.WithStrategy(runStrategy)
177+
// Ensure the build goes through the full process and doesn't build without dependencies like most of the integration tests do.
178+
#if RELEASE
179+
.WithCustomBuildConfiguration("Release")
180+
#else
181+
.WithCustomBuildConfiguration("Debug")
182+
#endif
183+
)
177184
.AddLogger(DefaultConfig.Instance.GetLoggers().ToArray())
178185
.AddColumnProvider(DefaultColumnProviders.Instance)
179186
.AddDiagnoser(disassemblyDiagnoser)

0 commit comments

Comments
 (0)