using Codelyzer.Analysis.Common; using Microsoft.CodeAnalysis; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Codelyzer.Analysis.Build { public class WorkspaceBuilder { private readonly ILogger Logger; private readonly AnalyzerConfiguration _analyzerConfiguration; private readonly string _workspacePath; private List ProjectResults { get; } public WorkspaceBuilder(ILogger logger, string workspacePath, AnalyzerConfiguration analyzerConfiguration = null) { this.ProjectResults = new List(); this._workspacePath = workspacePath; this.Logger = logger; _analyzerConfiguration = analyzerConfiguration; } public async IAsyncEnumerable BuildProject() { using (var builder = new WorkspaceBuilderHelper(Logger, _workspacePath, _analyzerConfiguration)) { try { if (!_analyzerConfiguration.BuildSettings.SyntaxOnly) { var projectResultEnumerator = builder.BuildProjectIncremental().GetAsyncEnumerator(); while (await projectResultEnumerator.MoveNextAsync().ConfigureAwait(false)) { var result = projectResultEnumerator.Current; if (result?.AnalyzerResult != null) { using (ProjectBuildHandler projectBuildHandler = new ProjectBuildHandler(Logger, result.Project, _analyzerConfiguration)) { projectBuildHandler.AnalyzerResult = result.AnalyzerResult; projectBuildHandler.ProjectAnalyzer = result.ProjectAnalyzer; var projectBuildResult = await projectBuildHandler.Build(); yield return projectBuildResult; } } else { if (_analyzerConfiguration.AnalyzeFailedProjects) { using (ProjectBuildHandler projectBuildHandler = new ProjectBuildHandler(Logger, _analyzerConfiguration)) { projectBuildHandler.ProjectAnalyzer = result.ProjectAnalyzer; var projectBuildResult = projectBuildHandler.SyntaxOnlyBuild(); yield return projectBuildResult; } } } } await projectResultEnumerator.DisposeAsync(); } else { var projectReferencesMap = FileUtils.GetProjectsWithReferences(_workspacePath); builder.GenerateNoBuildAnalysis(); var projectsInOrder = CreateDependencyQueue(projectReferencesMap); Dictionary references = new Dictionary(); foreach (string projectPath in projectsInOrder) { var project = builder.Projects.Find(p => p.ProjectAnalyzer.ProjectFile.Path.Equals(projectPath, StringComparison.InvariantCultureIgnoreCase)); var projectReferencePaths = projectReferencesMap[projectPath]?.Distinct().ToHashSet(); using (ProjectBuildHandler projectBuildHandler = new ProjectBuildHandler(Logger, project.Project, _analyzerConfiguration)) { projectBuildHandler.AnalyzerResult = project.AnalyzerResult; projectBuildHandler.ProjectAnalyzer = project.ProjectAnalyzer; var projectReferences = references.Where(r => projectReferencePaths.Contains(r.Key)).ToDictionary(p => p.Key, p => p.Value); var result = projectBuildHandler.SyntaxOnlyBuild(projectReferences); if (result != null) { references.Add(projectPath, result.Compilation.ToMetadataReference()); yield return result; } } } } } finally { } } } public async Task> Build() { using (var builder = new WorkspaceBuilderHelper(Logger, _workspacePath, _analyzerConfiguration)) { if (!_analyzerConfiguration.BuildSettings.SyntaxOnly) { builder.Build(); foreach (var projectResult in builder.Projects) { using (ProjectBuildHandler projectBuildHandler = new ProjectBuildHandler(Logger, projectResult.Project, _analyzerConfiguration)) { projectBuildHandler.AnalyzerResult = projectResult.AnalyzerResult; projectBuildHandler.ProjectAnalyzer = projectResult.ProjectAnalyzer; var result = await projectBuildHandler.Build(); ProjectResults.Add(result); } } if (_analyzerConfiguration.AnalyzeFailedProjects) { foreach (var projectResult in builder.FailedProjects) { using (ProjectBuildHandler projectBuildHandler = new ProjectBuildHandler(Logger, _analyzerConfiguration)) { projectBuildHandler.ProjectAnalyzer = projectResult.ProjectAnalyzer; var result = projectBuildHandler.SyntaxOnlyBuild(); ProjectResults.Add(result); } } } } else { var projectReferencesMap = FileUtils.GetProjectsWithReferences(_workspacePath); builder.GenerateNoBuildAnalysis(); var projectsInOrder = CreateDependencyQueue(projectReferencesMap); Dictionary references = new Dictionary(); while (projectsInOrder.Count > 0) { var projectPath = projectsInOrder.Dequeue(); var project = builder.Projects.Find(p => p.ProjectAnalyzer.ProjectFile.Path.Equals(projectPath, StringComparison.InvariantCultureIgnoreCase)); var projectReferencePaths = projectReferencesMap[projectPath]?.Distinct().ToHashSet(); using (ProjectBuildHandler projectBuildHandler = new ProjectBuildHandler(Logger, project.Project, _analyzerConfiguration)) { projectBuildHandler.AnalyzerResult = project.AnalyzerResult; projectBuildHandler.ProjectAnalyzer = project.ProjectAnalyzer; var projectReferences = references.Where(r => projectReferencePaths.Contains(r.Key)).ToDictionary(p=>p.Key, p=> p.Value); var result = projectBuildHandler.SyntaxOnlyBuild(projectReferences); if (result != null) { references.Add(projectPath, result.Compilation.ToMetadataReference()); ProjectResults.Add(result); } } } } } return ProjectResults; } private Queue CreateDependencyQueue(Dictionary> projectReferencesMap) { var projectsInOrder = new Queue(); var builtProjects = new HashSet(); foreach (var project in projectReferencesMap.Keys) { try { if (!builtProjects.Contains(project)) { CreateDependencyQueueHelper(project, builtProjects, projectReferencesMap, projectsInOrder); } } catch (Exception ex) { Logger.LogError(ex, $"Error while getting dependencies for project {project}"); } } return projectsInOrder; } private void CreateDependencyQueueHelper(string projectPath, HashSet builtProjects, Dictionary> projectReferencesMap, Queue buildOrder) { builtProjects.Add(projectPath); foreach (var dependency in projectReferencesMap[projectPath]) { if (!builtProjects.Contains(dependency)) CreateDependencyQueueHelper(dependency, builtProjects, projectReferencesMap, buildOrder); } buildOrder.Enqueue(projectPath); } public List GenerateNoBuildAnalysis(Dictionary> oldReferences, Dictionary> references) { using (var builder = new WorkspaceBuilderHelper(Logger, _workspacePath, _analyzerConfiguration)) { builder.GenerateNoBuildAnalysis(); foreach (var projectResult in builder.Projects) { var projectPath = projectResult.ProjectAnalyzer.ProjectFile.Path; var oldRefs = oldReferences?.ContainsKey(projectPath) == true ? oldReferences[projectPath] : null; var refs = references?.ContainsKey(projectPath) == true ? references[projectPath] : null; using (ProjectBuildHandler projectBuildHandler = new ProjectBuildHandler(Logger, projectPath, oldRefs, refs, _analyzerConfiguration)) { projectBuildHandler.ProjectAnalyzer = projectResult.ProjectAnalyzer; var result = projectBuildHandler.ReferenceOnlyBuild(); ProjectResults.Add(result); } } } return ProjectResults; } } }