Buildalyzer
Buildalyzer copied to clipboard
Cache dotnet info results per global.json file
I ran into this problem with stryker.net (which uses Buildalyzer), getting exceptions like this
Unhandled exception. System.AggregateException: One or more errors occurred. (Could not find build environment) (Could not find build environment) (Could not find build environment) (Could not find build environment) (Could not find build environment) (Could not find build environment) (Could not find build environment) (Could not find build environment)
---> System.InvalidOperationException: Could not find build environment
at Buildalyzer.Environment.EnvironmentFactory.GetBuildEnvironment(String targetFramework, EnvironmentOptions options)
at Buildalyzer.Environment.EnvironmentFactory.GetBuildEnvironment(String targetFramework)
at Buildalyzer.ProjectAnalyzer.Build(String targetFramework)
at Buildalyzer.ProjectAnalyzer.Build()
at Stryker.Core.Initialisation.ProjectOrchestrator.<>c__DisplayClass7_0.<AnalyzeSolution>b__0(IProjectAnalyzer project) in /_/src/Stryker.Core/Stryker.Core/Initialisation/ProjectOrchestrator.cs:line 124
I was able to reproduce it directly against Buildalyzer (main branch) using this type of test ( no sln included here, but I think any large sln file should do, mine has 278 projects, and was executed on a 5950x CPU with 16c/32t).
AnalyzerManager manager = new AnalyzerManager(@"C:\path\to\Big.sln", new AnalyzerManagerOptions());
Parallel.ForEach(manager.Projects.Values, project =>
{
Console.Error.WriteLine("Analysing {0}", project.ProjectFile.Path);
IAnalyzerResults? buildResult = project.Build();
IAnalyzerResult? projectAnalyzerResult = buildResult.Results.FirstOrDefault();
if (projectAnalyzerResult is { })
{
Console.Error.WriteLine("Analysis of project {0} succeeded", project.ProjectFile.Path);
}
else
{
Console.Error.WriteLine("Analysis of project {0} failed", project.ProjectFile.Path);
}
});
As far as I can tell the problem is that the time-out in DotnetPathResolver.cs
for running dotnet --info is "only" 4000ms, and when that time-out is exceeded it fails somewhat silently. In my case I just bumped it to 60000ms.
I wondering if perhaps an optimization is in order to avoid invoking dotnet --info so many times? AFAIK the info will only change if a global.json file is placed in a subfolder above the project file, so it should only be necessary to call dotnet --info once per located global.json (+1 if no global.json exists).
I just bumped the timeout to 10 seconds in version 6.0.2 (see #228) and there's now a DOTNET_INFO_WAIT_TIME
environment variable that can be used to specify an alternate amount of time to wait as well.
I do really like the idea of caching the dotnet --info
call per global.json
so I'll leave this open as a general enhancement and try to get something done in that regard.
@Corniel, I'm going to implement this feature, I thought of a static class to store this cache during execution or use the InMemory cache, do you have any considerations? If you've already started something, I can get another feature too.
I thought about having it in place why logging/running/tracing the build. I think the scope of one run would be sufficient, but may be a static cache will work better.
PR with the feature.
Caching has been implemented, @petertiedemann
I will close this issue, let us know if there is any problem, thank you.