diff --git a/src/Analysis/Codelyzer.Analysis.Build/Models/WorkspaceConfiguration.cs b/src/Analysis/Codelyzer.Analysis.Build/Models/WorkspaceConfiguration.cs new file mode 100644 index 00000000..e93e30fc --- /dev/null +++ b/src/Analysis/Codelyzer.Analysis.Build/Models/WorkspaceConfiguration.cs @@ -0,0 +1,40 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using System; +using System.Collections.Generic; + +namespace Codelyzer.Analysis.Build.Models +{ + public class WorkspaceConfiguration + { + public string workspace { get; set; } + public SolutionConfig solution { get; set; } + } + + public class SolutionConfig + { + public List projects { get; set; } + } + + public class ProjectConfig + { + public string projectId { get; set; } + public string assemblyName { get; set; } + public string language { get; set; } + public string filePath { get; set; } + public string outputFilePath { get; set; } + public List documents { get; set; } + public List projectReferences { get; set; } + public List metadataReferencesFilePath { get; set; } + public List analyzerReferences { get; set; } + public ParseOptions parseOptions { get; set; } + public CompilationOptions compilationOptions { get; set; } + } + + public class DocumentConfig + { + public string documentId { get; set; } + public string assemblyName { get; set; } + public string filePath { get; set; } + } +} diff --git a/src/Analysis/Codelyzer.Analysis/Analyzer/CodeAnalyzerByLanguage.cs b/src/Analysis/Codelyzer.Analysis/Analyzer/CodeAnalyzerByLanguage.cs index d644be1d..9e158315 100644 --- a/src/Analysis/Codelyzer.Analysis/Analyzer/CodeAnalyzerByLanguage.cs +++ b/src/Analysis/Codelyzer.Analysis/Analyzer/CodeAnalyzerByLanguage.cs @@ -5,9 +5,14 @@ using System.Text; using System.Threading.Tasks; using Codelyzer.Analysis.Build; +using Codelyzer.Analysis.Build.Models; using Codelyzer.Analysis.Common; using Codelyzer.Analysis.Model; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Text; using Microsoft.Extensions.Logging; +using Newtonsoft.Json; namespace Codelyzer.Analysis.Analyzer { @@ -15,6 +20,7 @@ public class CodeAnalyzerByLanguage { protected readonly AnalyzerConfiguration AnalyzerConfiguration; protected readonly ILogger Logger; + private readonly string projectBuildType = "KnownToBeMSBuildFormat"; public CodeAnalyzerByLanguage(AnalyzerConfiguration configuration, ILogger logger) { @@ -106,35 +112,9 @@ public async Task> Analyze(string path) { throw new FileNotFoundException(path); } - - List workspaceResults = new List(); - var analyzerResults = new List(); - WorkspaceBuilder builder = new WorkspaceBuilder(Logger, path, AnalyzerConfiguration); - var projectBuildResults = await builder.Build(); - - foreach (var projectBuildResult in projectBuildResults) - { - var workspaceResult = await Task.Run(() => AnalyzeProject(projectBuildResult)); - workspaceResult.ProjectGuid = projectBuildResult.ProjectGuid; - workspaceResult.ProjectType = projectBuildResult.ProjectType; - workspaceResults.Add(workspaceResult); - - //Generate Output result - if (AnalyzerConfiguration.MetaDataSettings.LoadBuildData) - { - analyzerResults.Add(new AnalyzerResult() { ProjectResult = workspaceResult, ProjectBuildResult = projectBuildResult }); - } - else - { - analyzerResults.Add(new AnalyzerResult() { ProjectResult = workspaceResult }); - } - } - - await GenerateOptionalOutput(analyzerResults); - - return analyzerResults; + return await AnalyzeBuildResults(projectBuildResults); } private async Task GenerateOptionalOutput(List analyzerResults) @@ -275,13 +255,151 @@ private async Task> AnalyzeWithReferences(string path, Dict throw new FileNotFoundException(path); } - List workspaceResults = new List(); - var analyzerResults = new List(); - WorkspaceBuilder builder = new WorkspaceBuilder(Logger, path, AnalyzerConfiguration); - var projectBuildResults = builder.GenerateNoBuildAnalysis(oldReferences, references); + return await AnalyzeBuildResults(projectBuildResults); + } + + public async Task AnalyzeProject(string projectPath, List oldReferences, List references) + { + var analyzerResult = await AnalyzeWithReferences(projectPath, oldReferences?.ToDictionary(r => projectPath, r => oldReferences), references?.ToDictionary(r => projectPath, r => references)); + return analyzerResult.FirstOrDefault(); + } + + public async Task> AnalyzeSolutionUsingVSWorkspace(string solutionPath, string workspaceConfig = null) + { + if (workspaceConfig == null) + { + return await Analyze(solutionPath); + } + return await AnalyzeUsingVSWorkspace(workspaceConfig); + } + + private async Task> AnalyzeUsingVSWorkspace(string workspaceConfig) + { + var adhocWorkspace = ConstructWorkspaceObject(workspaceConfig); + var projectBuildResults = BuildUsingAdHocWorkspace(adhocWorkspace); + return await AnalyzeBuildResults(projectBuildResults); + } + public Workspace ConstructWorkspaceObject(string workspace) + { + try + { + WorkspaceConfiguration workspaceConfig = JsonConvert.DeserializeObject(workspace); + AdhocWorkspace adhocWorkspace = new AdhocWorkspace(); + + foreach (var curProject in workspaceConfig.solution.projects) + { + List docInfoLst = new List(); + var projectId = ProjectId.CreateFromSerialized(new Guid(curProject.projectId)); + foreach (var doc in curProject.documents) + { + try + { + DocumentInfo docInfo = DocumentInfo.Create( + DocumentId.CreateFromSerialized(projectId, new Guid(doc.documentId)), + curProject.assemblyName, + loader: TextLoader.From( + TextAndVersion.Create( + SourceText.From(File.ReadAllText(doc.filePath), Encoding.Unicode), VersionStamp.Create())), + filePath: doc.filePath); + + docInfoLst.Add(docInfo); + } + catch (Exception e) + { + Console.WriteLine(e.ToString()); + } + } + + List metadataReferencesLst = new List(); + foreach (string filePath in curProject.metadataReferencesFilePath) + { + metadataReferencesLst.Add(MetadataReference.CreateFromFile(filePath)); + } + + + var projectInfo = ProjectInfo.Create( + projectId, + VersionStamp.Create(), + curProject.assemblyName, + curProject.assemblyName, + curProject.language, + filePath: curProject.filePath, + outputFilePath: curProject.outputFilePath, + documents: docInfoLst, + projectReferences: null,//curProject.projectReferences, + metadataReferences: metadataReferencesLst, + analyzerReferences: null,//curProject.analyzerReferences, + parseOptions: curProject.parseOptions, + compilationOptions: curProject.compilationOptions); + + var solution = adhocWorkspace.CurrentSolution.AddProject(projectInfo); + + if (!adhocWorkspace.TryApplyChanges(solution)) + { + return null; + } + } + + return adhocWorkspace; + } + catch (Exception ex) + { + return null; + } + } + + public List BuildUsingAdHocWorkspace(Workspace workspace) + { + List projectBuildResults = new List(); + + foreach (Project project in workspace.CurrentSolution.Projects) + { + ProjectBuildResult projectBuildResult = new ProjectBuildResult(); + projectBuildResult.Project = project; + projectBuildResult.ProjectPath = project.FilePath; + projectBuildResult.ProjectRootPath = Path.GetDirectoryName(project.FilePath); + projectBuildResult.ProjectGuid = project.Id.Id.ToString(); + projectBuildResult.BuildErrors = new List(); + projectBuildResult.Compilation = CSharpCompilation.Create(null).AddReferences(project.MetadataReferences); + projectBuildResult.ExternalReferences = GetExternalReferences(projectBuildResult.Compilation, project, project.MetadataReferences); + projectBuildResult.ProjectType = projectBuildType; + + foreach (var document in project.Documents) + { + SourceFileBuildResult sourceFileBuildResult = new SourceFileBuildResult(); + sourceFileBuildResult.SourceFileFullPath = document.FilePath; + sourceFileBuildResult.SourceFilePath = document.Name; + sourceFileBuildResult.SyntaxTree = document.GetSyntaxTreeAsync().Result; + sourceFileBuildResult.SemanticModel = document.GetSemanticModelAsync().Result; + projectBuildResult.SourceFileBuildResults.Add(sourceFileBuildResult); + } + + projectBuildResults.Add(projectBuildResult); + } + + return projectBuildResults; + } + + private ExternalReferences GetExternalReferences(Compilation compilation, Project project, IEnumerable externalReferencesMetaData) + { + IReadOnlyDictionary references = new Dictionary(); + ExternalReferenceLoader externalReferenceLoader = new ExternalReferenceLoader( + Directory.GetParent(project.FilePath).FullName, + compilation, + project, + new Dictionary>(), + Logger); + + return externalReferenceLoader.Load(); + } + + private async Task> AnalyzeBuildResults(List projectBuildResults) + { + var analyzerResults = new List(); + List workspaceResults = new List(); foreach (var projectBuildResult in projectBuildResults) { var workspaceResult = await Task.Run(() => AnalyzeProject(projectBuildResult)); @@ -304,12 +422,5 @@ private async Task> AnalyzeWithReferences(string path, Dict return analyzerResults; } - - public async Task AnalyzeProject(string projectPath, List oldReferences, List references) - { - var analyzerResult = await AnalyzeWithReferences(projectPath, oldReferences?.ToDictionary(r => projectPath, r => oldReferences), references?.ToDictionary(r => projectPath, r => references)); - return analyzerResult.FirstOrDefault(); - } - } }