Corniel Nobel
Corniel Nobel

Reputation: 429

How to compile a .NET solution with the Roslyn SDK

For multiple (quite advanced) analysis I would like to the Roslyn SDK: 1. Apply diagnostics only to 'new' code (leave obsolete code out) 2. Detect dead code (for public methods, defining the entry points of application) 3. Bulk code fixes

In other words: just building an applying a DiagnosticAnalyzer within visual studio, is not enough. I successfully built those, and wrote unit tests for them (inspired by the Sonar). However, that was only analysing one (or max a small couple of) file(s) at one go.

So here is my problem: I've tried several ways to load a full .NET solution and compile it (myself) but it all failed.

public static async Task Main(string[] args)
{
    var analyzers = (new DiagnosticAnalyzer[] { new MyCustomAnalyzer }).ToImmutableArray();

    var file = new FileInfo(@"c:\path\my_solution.sln");

    using (var workspace = MSBuildWorkspace.Create())
    {
        workspace.WorkspaceFailed += OnWorkspaceFailed;

        var solution = await workspace.OpenSolutionAsync(solutionFile.FullName);
        var dependencyGraph = solution.GetProjectDependencyGraph();
        var projectIds = dependencyGraph.GetTopologicallySortedProjects();

        foreach (var projectId in projectIds)
        {
            using (var cancel = new CancellationTokenSource())
            {
                var project = solution.GetProject(projectId);

                var compilation = (await project.GetCompilationAsync(cancel.Token))
                    .WithOptions(project.CompilationOptions)
                    .WithAnalyzers(array, cancellationToken: cancel.Token);

                foreach (var diagnostic in await compilation.GetAllDiagnosticsAsync())
                {
                    (diagnostic.Severity >= DiagnosticSeverity.Error
                        ? Console.Error
                        : Console.Out).WriteLine(diagnostic);
                    // Do magic
                }
            }
        }
    }
}

This is the list of packages I included:

<PackageReference Include="Microsoft.Build" Version="15.8.166" />
<PackageReference Include="Microsoft.Build.Framework" Version="15.8.166" />
<PackageReference Include="Microsoft.Build.Tasks.Core" Version="15.8.166" />
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="15.8.166" />
<PackageReference Include="Microsoft.CodeAnalysis" Version="2.9.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="2.9.0" />

I tried it both running it with .NET core 2.1 and .NET 4.6.1 (as Microsoft.CodeAnalysis.Workspaces.MSBuild is trageted as .NET 4.6.1)

The errors I get (based on different settings):

.NET 4.6.1

Msbuild failed when processing the file 'cusotom project.vbproj.or.csproj' with message: C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\Microsoft.Common.CurrentVersion.targets: (1657, 5): The "GetReferenceNearestTargetFrameworkTask" task could not be instantiated from the assembly "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.Build.Tasks.dll". Please verify the task assembly has been built using the same version of the Microsoft.Build.Framework assembly as the one installed on your computer and that your host application is not missing a binding redirect for Microsoft.Build.Framework. Unable to cast object of type 'NuGet.Build.Tasks.GetReferenceNearestTargetFrameworkTask' to type 'Microsoft.Build.Framework.ITask'.

.NET 4.6.1 with MSBUILD_EXE_PATH

Msbuild failed when processing the file 'cusotom project.vbproj.or.csproj' with message: C:\Program Files (x86)\MSBuild\14.0\bin\Microsoft.Common.CurrentVersion.targets: (1407, 5): The "AssignProjectConfiguration" task could not be instantiated from the assembly "Microsoft.Build.Tasks.Core, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a". Please verify the task assembly has been built using the same version of the Microsoft.Build.Framework assembly as the one installed on your computer and that your host application is not missing a binding redirect for Microsoft.Build.Framework. Unable to cast object of type 'Microsoft.Build.Tasks.AssignProjectConfiguration' to type 'Microsoft.Build.Framework.ITask'.

.NET Core 2.1

Msbuild failed when processing the file 'cusotom project.vbproj.or.csproj' with message: The imported project "\bin\Debug\netcoreapp2.1\Microsoft..targets" was not found. Confirm that the path in the declaration is correct, and that the file exists on disk. *

.NET Core 2.1 (with MSBUILD_EXE_PATH)

Msbuild failed when processing the file 'cusotom project.vbproj.or.csproj' with message: Program Files\dotnet\sdk..\Microsoft.Common.CurrentVersion.targets: (1544, 5): The "Microsoft.Build.Tasks.ResolveNonMSBuildProjectOutput" task could not be loaded from the assembly Microsoft.Build.Tasks.Core, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a. Confirm that the declaration is correct, that the assembly and all its dependencies are available, and that the task contains a public class that implements Microsoft.Build.Framework.ITask.

The way I set the path:

Environment.SetEnvironmentVariable("MSBUILD_EXE_PATH", @"Program Files\dotnet\sdk\..\MSBuild.dll");

I also tried true as that was suggested somewhere, but that didn't change anything.

Upvotes: 3

Views: 2057

Answers (1)

Stephanvs
Stephanvs

Reputation: 731

Ok, so there appear to be several things in play here.

  1. Before you can invoke the MSBuildWorkspace.Create() method, it should be configured to use a specific msbuild configuration from your local machine. That is required to resolve all the required dependencies using MEF. Take a look at the description Dustin Campbell wrote here.

    Do not include any Microsoft.Build.* assemblies in your application output except for Microsoft.Build.Locator. If your application includes Microsoft.Build.dll, Microsoft.Build.Framework.dll, Microsoft.Build.Tasks.Core or Microsoft.Build.Utiltiies.Core, they can interfere with the assembly resolution handler that MSBuildLocator has installed.

  2. To actually localize an msbuild instance on your machine you should use a helper class MSBuildLocator which can be found in the Microsoft.MSBuild.Locator nuget package (here).

  3. Currently this entire process is not really supported to run on dotnet core. They updated the specified nuget package above to add support for dotnet core (see this github issue) and is available through the preview myget feed.

Upvotes: 3

Related Questions