Cannot open solution with code analyzer class - c#

I'm trying to build a simple program to static code analysis with provided package from Microsoft. So I wan to open solution then open projects via code then get all the document and analyze with my methods.
This is my method, I'm calling somewhere else, and I double checked solution path is correct. You can see my namespaces that I used.
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.MSBuild;
public class Fixer
{
public Fixer(string solutionPath)
{
using (var workspace = MSBuildWorkspace.Create())
{
_solution = workspace.OpenSolutionAsync(_solutionPath).Result;
}
}
}
After that I want to
public IEnumerable<Document> GetDocuments(Solution solution)
{
foreach (var projectId in solution.ProjectIds)
{
var project = solution.GetProject(projectId);
foreach (Document document in project.Documents)
{
if (document.SupportsSyntaxTree)
yield return document;
}
}
}
public IEnumerable<MethodDeclarationSyntax> GetMethods(IEnumerable<Document> documents)
{
return documents.SelectMany(p => p.GetSyntaxRootAsync().Result.DescendantNodes().OfType<MethodDeclarationSyntax>());
}
Main problem is its not open solution at all. When I checked workspace object. There is diagnostic property and says
{[Failure] Cannot open project '.\RunningDiagnostics.csproj' because the language 'C#' is not supported.}
For all project in that solution.
I tried to update nuget packages that I used. Nothing happened. What is the main problem ?
And guidance will be appreciated.
Thank you.

Related

How to properly use ControlFlowGraph from roslyn code analysis in C#

I cannot understand why I am getting an error (using VS2017) for the code in below related to not finding the class ControlFlowGraph which is supposed to be part of the package Microsoft.CodeAnalysis.FlowAnalysis:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Build.Locator;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.CodeAnalysis.FlowAnalysis;
namespace CodeAnalysisApp3
{
class Program
{
static async Task Main(string[] args)
{
// Attempt to set the version of MSBuild.
var visualStudioInstances = MSBuildLocator.QueryVisualStudioInstances().ToArray();
var instance = visualStudioInstances[0];
Console.WriteLine($"Using MSBuild at '{instance.MSBuildPath}' to load projects.");
// NOTE: Be sure to register an instance with the MSBuildLocator
// before calling MSBuildWorkspace.Create()
// otherwise, MSBuildWorkspace won't MEF compose.
MSBuildLocator.RegisterInstance(instance);
using (var workspace = MSBuildWorkspace.Create())
{
// Print message for WorkspaceFailed event to help diagnosing project load failures.
workspace.WorkspaceFailed += (o, e) => Console.WriteLine(e.Diagnostic.Message);
var solutionPath = args[0];
Console.WriteLine($"Loading solution '{solutionPath}'");
// Attach progress reporter so we print projects as they are loaded.
var solution = await workspace.OpenSolutionAsync(solutionPath, new ConsoleProgressReporter());
Console.WriteLine($"Finished loading solution '{solutionPath}'");
// TODO: Do analysis on the projects in the loaded solution
CSharpParseOptions options = CSharpParseOptions.Default
.WithFeatures(new[] { new KeyValuePair<string, string>("flow-analysis", "") });
var projIds = solution.ProjectIds;
var project = solution.GetProject(projIds[0]);
Compilation compilation = await project.GetCompilationAsync();
if (compilation != null && !string.IsNullOrEmpty(compilation.AssemblyName))
{
var mySyntaxTree = compilation.SyntaxTrees.First();
// get syntax nodes for methods
var methodNodes = from methodDeclaration in mySyntaxTree.GetRoot().DescendantNodes()
.Where(x => x is MethodDeclarationSyntax)
select methodDeclaration;
foreach (MethodDeclarationSyntax node in methodNodes)
{
var model = compilation.GetSemanticModel(node.SyntaxTree);
node.Identifier.ToString();
if (node.SyntaxTree.Options.Features.Any())
{
var graph = ControlFlowGraph.Create(node, model); // CFG is here
}
}
}
}
}
private class ConsoleProgressReporter : IProgress<ProjectLoadProgress>
{
public void Report(ProjectLoadProgress loadProgress)
{
var projectDisplay = Path.GetFileName(loadProgress.FilePath);
if (loadProgress.TargetFramework != null)
{
projectDisplay += $" ({loadProgress.TargetFramework})";
}
Console.WriteLine($"{loadProgress.Operation,-15} {loadProgress.ElapsedTime,-15:m\\:ss\\.fffffff} {projectDisplay}");
}
}
}
}
However, when I compile the above code I am getting the following error message with VS2017:
1>Program.cs(67,41,67,57): error CS0103: The name 'ControlFlowGraph' does not exist in the current context
1>Done building project "CodeAnalysisApp3.csproj" -- FAILED.
========== Rebuild All: 0 succeeded, 1 failed, 0 skipped ==========
Version used:
Microsoft (R) Visual C# Compiler version 4.8.3761.0
for C# 5
Based on my test, I find I can use class ControlFlowGraph.
I installed the following nugetpackage.
Microsoft.CodeAnalysis
Microsoft.Build.Locator
Then, you will see the following result.
Besides, I used .net framwork 4.6.1.
I was able to solve the problem when I used roslyn CodeAnalysis packages with the proper versions:
CodeAnalysis.CSharp.Workspaces (3.4.0)
CodeAnalysis.FlowAnalysis.Utilities (2.9.6)
CodeAnalysis.Workspaces.MSBuild (3.4.0)
The target framework is .NETFramework 4.7.2
A link to a closed issue created for this question on roslyn Github repo is here

How to resolve the Method not found in C#?

I'm trying to read an xlsx file, and I got a basic overview from This Codeproject Link
Now, I'm getting the following exception message:
The code segment related to this exception is given below:
public static sst SharedStrings;
/// <summary>
/// All worksheets in the Excel workbook deserialized
/// </summary>
/// <param name="ExcelFileName">Full path and filename of the Excel xlsx-file</param>
/// <returns></returns>
public static IEnumerable<worksheet> Worksheets(string ExcelFileName)
{
worksheet ws;
using (ZipArchive zipArchive = ZipFile.Open(ExcelFileName, ZipArchiveMode.Read))
{
SharedStrings = DeserializedZipEntry<sst>(GetZipArchiveEntry(zipArchive, #"xl/sharedStrings.xml"));
foreach (var worksheetEntry in (WorkSheetFileNames(zipArchive)).OrderBy(x => x.FullName))
{
ws = DeserializedZipEntry<worksheet>(worksheetEntry);
ws.NumberOfColumns = worksheet.MaxColumnIndex + 1;
ws.ExpandRows();
yield return ws;
}
}
}
After some searching, I figured out that I needed to target .NET 4.5 or above version, (I'm targetting 4.6.1, but I also have tried 4.5, still same results). Also, as far as the dependencies are concerned, my references look like this:
After doing all this, I'm still getting the exception mentioned above, and have no idea why this is happening.
EDIT 1: I've been through This StackOverflow Link, where I figured that I need the latest DLLs. I went to the Nuget Package Manager, and there were "No Packages Found" which were available for the updates.
I have tried .NET 4.6.1 and 4.6.2. Both work OK for me.
My Visual Studio is Community Edition 2017
The project references:
Note that System.IO.Compression.ZipFile is not referenced. It was not possible to reference it in my environment.
Use Workbook.Worksheets() or declare the calling class as sub-class of Excel.Workbook.
Excel is the namespace of the CodeProject article source which provides the necessary classes.
using Excel;
using System;
namespace akExcelAsZipDemo
{
class Program : Workbook
{
// from
// https://www.codeproject.com/tips/801032/csharp-how-to-read-xlsx-excel-file-with-lines-ofthe
//
// inspired:
// https://github.com/ahmadalli/ExcelReader
static void Main(string[] args)
{
const string fileName = #"E:\AK\export.xlsx";
var worksheets = Worksheets(fileName);
foreach (worksheet ws in worksheets)
{
Console.WriteLine($"cols={ws.NumberOfColumns} rows={ws.Rows.Length}");
}
Console.WriteLine();
}
}
}

Programmatically build solution with filter

After looking all over the Google I found a good way to build a solution. However the solution I want to build also contains unit test projects, which I don't want to include in the build, or if I can't prevent that at least put those binaries in a separate folder. The code is as follows:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
using Microsoft.Build.Logging;
public class BuildSolution
{
private readonly string _solutionPath;
private readonly string _outputPath = "C:\\Temp\\TestBuild\\";
public BuildSolution(string solutionPath, string outputPath = null)
{
if (!string.IsNullOrEmpty(outputPath))
_outputPath = outputPath;
_solutionPath = solutionPath;
Directory.EnumerateFiles(_outputPath, "*", SearchOption.AllDirectories)
.Select(x => new FileInfo(x))
.ToList()
.ForEach(x => x.Delete());
}
public void Build()
{
var pc = new ProjectCollection();
var globalProps = new Dictionary<string, string>()
{
{ ProjectPropertyNames.Configuration, "Debug" },
{ ProjectPropertyNames.OutputPath, _outputPath },
{ ProjectPropertyNames.EnableNuGetPackageRestore, "true"},
};
var targetsToBuild = new[] { "Build" };
var buildRequest = new BuildRequestData(_solutionPath, globalProps, null, targetsToBuild, null);
var buildParams = new BuildParameters(pc);
buildParams.Loggers = new List<ILogger>() { new ConsoleLogger(LoggerVerbosity.Minimal) };
var buildManager = BuildManager.DefaultBuildManager;
buildManager.BeginBuild(buildParams);
var buildSubmission = buildManager.PendBuildRequest(buildRequest);
buildSubmission.ExecuteAsync(BuildCompleted, null);
while (!done)
{
Thread.Sleep(10);
}
buildManager.EndBuild();
Console.WriteLine("OverallResult:{0}", buildSubmission.BuildResult.OverallResult);
}
bool done = false;
private void BuildCompleted(BuildSubmission submission)
{
done = submission.IsCompleted;
}
/// <summary>
/// Unused, but I tried it and it gives me back the correct projects but the build fails because of dependant nuget packages
/// </summary>
/// <param name="path">path of solution</param>
/// <returns></returns>
private IEnumerable<FileInfo> GetFirstLevelProjects(string path)
{
foreach (var dir in Directory.EnumerateDirectories(path))
{
foreach (var file in Directory.EnumerateFiles(dir, "*.csproj"))
{
if (!file.Contains("Test"))
yield return new FileInfo(file);
}
}
}
}
nothing fancy about it. (I'm playing with the idea of making the build async so I can update status...we'll see about that, I might switch it back to sync). One thing I tried was that instead of putting the solution in the build request, I would build the project collection using the first level projects (I use git with sub-modules, so I don't want to build all the non-relevant sub-modules). The problem with that route was that the build would fail because of nuget packages (not sure why or how to get around that). When I build the solution it builds successfully, but my outputPath also includes the test binaries. My end game is that the output can get copied to a specific folder of mine. I wouldn't mind having the test binaries if I knew I could filter ALL the binaries that are in the test projects... So how? What options do I have?
Why Not MsBuild?
The easiest way that I can think of to do this is to use MsBuild to do it.
msbuild C:\myFolder\mySolution.sln /p:Configuration=Release
As for not building the tests, this could now be easily changed from within visual studio
Right Click on Solution > Properties > Configuration Properties (on left side)
From there you could switch to release mode and uncheck the box next to your test projects. This will tell visual studio that when it does a release build it can skip these projects.
Without MsBuild
However, if you wanted to keep building them in the manner that you showed, I would change the assembly name (the dll name) so that you could identify them easily in your favorite scripting language.
Right Click on Project > Application Tab (on left side) > Assembly Name Box
I would call them something like SolutionNameSpace.Tests.ProjectUnderTest.dll.
Then in your build process you can filter out SolutionNameSpace.Tests.*.dll. Just be carefull, if you reference testing libraries they could get copied to your output folder also.

How do I compile a C# solution with Roslyn?

I have a piece of software that generates code for a C# project based on user actions. I would like to create a GUI to automatically compile the solution so I don't have to load up Visual Studio just to trigger a recompile.
I've been looking for a chance to play with Roslyn a bit and decided to try and use Roslyn instead of msbuild to do this. Unfortunately, I can't seem to find any good resources on using Roslyn in this fashion.
Can anyone point me in the right direction?
You can load the solution by using Roslyn.Services.Workspace.LoadSolution. Once you have done so, you need to go through each of the projects in dependency order, get the Compilation for the project and call Emit on it.
You can get the compilations in dependency order with code like below. (Yes, I know that having to cast to IHaveWorkspaceServices sucks. It'll be better in the next public release, I promise).
using Roslyn.Services;
using Roslyn.Services.Host;
using System;
using System.Collections.Generic;
using System.IO;
class Program
{
static void Main(string[] args)
{
var solution = Solution.Create(SolutionId.CreateNewId()).AddCSharpProject("Foo", "Foo").Solution;
var workspaceServices = (IHaveWorkspaceServices)solution;
var projectDependencyService = workspaceServices.WorkspaceServices.GetService<IProjectDependencyService>();
var assemblies = new List<Stream>();
foreach (var projectId in projectDependencyService.GetDependencyGraph(solution).GetTopologicallySortedProjects())
{
using (var stream = new MemoryStream())
{
solution.GetProject(projectId).GetCompilation().Emit(stream);
assemblies.Add(stream);
}
}
}
}
Note1: LoadSolution still does use msbuild under the covers to parse the .csproj files and determine the files/references/compiler options.
Note2: As Roslyn is not yet language complete, there will likely be projects that don't compile successfully when you attempt this.
I also wanted to compile a full solution on the fly. Building from Kevin Pilch-Bisson's answer and Josh E's comment, I wrote code to compile itself and write it to files.
Software Used
Visual Studio Community 2015 Update 1
Microsoft.CodeAnalysis v1.1.0.0 (Installed using Package Manager Console with command Install-Package Microsoft.CodeAnalysis).
Code
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.MSBuild;
namespace Roslyn.TryItOut
{
class Program
{
static void Main(string[] args)
{
string solutionUrl = "C:\\Dev\\Roslyn.TryItOut\\Roslyn.TryItOut.sln";
string outputDir = "C:\\Dev\\Roslyn.TryItOut\\output";
if (!Directory.Exists(outputDir))
{
Directory.CreateDirectory(outputDir);
}
bool success = CompileSolution(solutionUrl, outputDir);
if (success)
{
Console.WriteLine("Compilation completed successfully.");
Console.WriteLine("Output directory:");
Console.WriteLine(outputDir);
}
else
{
Console.WriteLine("Compilation failed.");
}
Console.WriteLine("Press the any key to exit.");
Console.ReadKey();
}
private static bool CompileSolution(string solutionUrl, string outputDir)
{
bool success = true;
MSBuildWorkspace workspace = MSBuildWorkspace.Create();
Solution solution = workspace.OpenSolutionAsync(solutionUrl).Result;
ProjectDependencyGraph projectGraph = solution.GetProjectDependencyGraph();
Dictionary<string, Stream> assemblies = new Dictionary<string, Stream>();
foreach (ProjectId projectId in projectGraph.GetTopologicallySortedProjects())
{
Compilation projectCompilation = solution.GetProject(projectId).GetCompilationAsync().Result;
if (null != projectCompilation && !string.IsNullOrEmpty(projectCompilation.AssemblyName))
{
using (var stream = new MemoryStream())
{
EmitResult result = projectCompilation.Emit(stream);
if (result.Success)
{
string fileName = string.Format("{0}.dll", projectCompilation.AssemblyName);
using (FileStream file = File.Create(outputDir + '\\' + fileName))
{
stream.Seek(0, SeekOrigin.Begin);
stream.CopyTo(file);
}
}
else
{
success = false;
}
}
}
else
{
success = false;
}
}
return success;
}
}
}

Roslyn CTP - How to Write Changes To File System

I created an empty Visual Studio Solution called Solution.sln which I load into the workspace int the first line. Then I add a project to the solution, and update the workspace to the latest solution which should now contain a project. How do I write out the files for the new stuff I added to the empty solution?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Roslyn.Compilers;
using Roslyn.Compilers.CSharp;
using Roslyn.Services;
namespace RoslynMainApp
{
class Program
{
static void Main(string[] args)
{
IWorkspace workspace = Workspace.LoadSolution(#"C:\RoslynSolutions\Solution.sln");
ProjectId projectId;
ISolution solution = Solution.Create(SolutionId.CreateNewId("Solution"));
solution.AddCSharpProject("Project1.dll", "Project1", out projectId);
var success = workspace.ApplyChanges(workspace.CurrentSolution, solution);
if(success)
{
//How do I write out all the stuff I just added to Solution.sln to the directory RoslynSolutions?
}
}
}
}
Thanks in advance,
Bob
The act of calling ApplyChanges should write the changes to disk. However, note that in CTP1, only a small set of the changes you can apply to solutions are actually implemented.

Categories