// This is where the magic happens - returns one result per result target framework private IEnumerable <AnalyzerResult> BuildTargets(BuildEnvironment buildEnvironment, string targetFramework, string[] targetsToBuild, bool analyze) { using (AnonymousPipeLoggerServer pipeLogger = new AnonymousPipeLoggerServer()) { using (EventProcessor eventProcessor = new EventProcessor(ProjectFile.Path, Loggers, pipeLogger, analyze)) { // Get the filename string fileName = buildEnvironment.MsBuildExePath; string initialArguments = string.Empty; if (Path.GetExtension(buildEnvironment.MsBuildExePath).Equals(".dll", StringComparison.OrdinalIgnoreCase)) { // .NET Core MSBuild .dll needs to be run with dotnet fileName = "dotnet"; initialArguments = $"\"{buildEnvironment.MsBuildExePath}\""; } // Get the arguments to use string loggerPath = typeof(PipeLogger).Assembly.Location; string loggerArgument = $"/l:{nameof(PipeLogger)},{FormatArgument(loggerPath)};{pipeLogger.GetClientHandle()}"; Dictionary <string, string> effectiveGlobalProperties = GetEffectiveGlobalProperties(buildEnvironment); if (!string.IsNullOrEmpty(targetFramework)) { // Setting the TargetFramework MSBuild property tells MSBuild which target framework to use for the outer build effectiveGlobalProperties[MsBuildProperties.TargetFramework] = targetFramework; } string propertyArgument = effectiveGlobalProperties.Count == 0 ? string.Empty : $"/property:{(string.Join(";", effectiveGlobalProperties.Select(x => $"{x.Key}={FormatArgument(x.Value)}")))}"; string targetArgument = targetsToBuild == null || targetsToBuild.Length == 0 ? string.Empty : $"/target:{string.Join(";", targetsToBuild)}"; string arguments = $"{initialArguments} /noconsolelogger /nodeReuse:False {targetArgument} {propertyArgument} {loggerArgument} {FormatArgument(ProjectFile.Path)}"; int exitCode; using (ProcessRunner processRunner = new ProcessRunner(fileName, arguments, Path.GetDirectoryName(ProjectFile.Path), GetEffectiveEnvironmentVariables(buildEnvironment), Manager.ProjectLogger)) { // Start MSBuild processRunner.Start(); // Read the pipe InterlockedBool exit = new InterlockedBool(false); AutoResetEvent exited = new AutoResetEvent(false); Thread thread = new Thread(() => { while (pipeLogger.Read() && !exit) { } exited.Set(); }) { IsBackground = true }; thread.Start(); // Wait for MSBuild to finish and then wait for pipe closure exitCode = processRunner.WaitForExit(); exit.Set(); exited.WaitOne(); } return(exitCode != 0 ? Array.Empty <AnalyzerResult>() : eventProcessor.GetResults(this)); } } }