This project is read-only.

How to set default FileResolver for OpenProjectAsync

Topics: APIs, General
Apr 8, 2014 at 5:10 AM
If I'm loading an existing project and it has assembly references that don't exist, how can I intercept the project load to provide the missing assemblies?

It looks like CompilationOptions.FileResolver is what I want, but I can't see how to set it in a way OpenProjectAsync will use it.

I can get everything running fine if the assemblies are present (MetadataReferences is correct). If assemblies are missing on disk, they're ignored in MetadataReferences).
Apr 8, 2014 at 6:15 PM
Once you load a project you can add new references to it using the MetatdataFileRefrence class. If you use the workspace apis it would look something like this.
using System;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.MSBuild;

class Program
{
    static void Main(string[] args)
    {
        TestWorkspace().GetAwaiter().GetResult();
    }


    private static async Task TestWorkspace()
    {
        var workspace = MSBuildWorkspace.Create();
        var project = await workspace.OpenProjectAsync(@"Path\To\Project.csproj");

        var mscorlib = new MetadataFileReference(typeof(object).Assembly.Location);
        var otherAssemblyReference = new MetadataFileReference(@"Path\To\Other\Assembly.dll");


        project = project.AddMetadataReference(mscorlib);
        project = project.AddMetadataReference(otherAssemblyReference);


        var compilation = await project.GetCompilationAsync();

        foreach (var diagnostic in compilation.GetDiagnostics())
        {
            Console.WriteLine(diagnostic.ToString());
        }
    }
}
Alternatively, if you need to build a compilation without a project you could do the following.
using System;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;

class Program
{
    static void Main(string[] args)
    {
        TestCompilation();
    }


    private static void TestCompilation()
    {
        var syntaxTree = CSharpSyntaxTree.ParseFile(@"File\To\Parse.cs");

        var mscorlib = new MetadataFileReference(typeof(object).Assembly.Location);
        var otherAssemblyReference = new MetadataFileReference(@"Path\To\Other\Assembly.dll");

        var compilation = CSharpCompilation.Create(
            "AssemblyName", 
            new[] { syntaxTree }, 
            new[] { mscorlib, otherAssemblyReference });

        foreach (var diagnostic in compilation.GetDiagnostics())
        {
            Console.WriteLine(diagnostic.ToString());
        }
    }
}
Apr 8, 2014 at 10:07 PM
Thanks! I guess my follow-up question is how can I find out which assembly references to add?...

What I'm hoping for is some kind of FileResolver that gets called during project load, that gives me the opportunity to lazily provide a missing assembly. Possible?
Apr 8, 2014 at 11:25 PM
You can accomplish something close to that with the MetadataFileReferenceResolver class. This hooks into how the compiler resolves references during compilation.
using System;
using System.Collections.Immutable;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.MSBuild;

class Program
{
    static void Main(string[] args)
    {
        TestWorkspace().GetAwaiter().GetResult();
    }


    private static async Task TestWorkspace()
    {
        var workspace = MSBuildWorkspace.Create();
        var project = await workspace.OpenProjectAsync(@"Path\To\Project.csproj");

        var mscorlib = new MetadataFileReference(typeof(object).Assembly.Location);

        var pathsUsedInAssembleResolution = new[]
        {
            @"Path\To\Other\Assembly.dll",
        }.ToImmutableArray();

        var referenceResolver = new MetadataFileReferenceResolver(
            pathsUsedInAssembleResolution, 
            @"Root\Assembly\Resolution\Path");

        var options = new CSharpCompilationOptions(
            OutputKind.ConsoleApplication, 
            metadataReferenceResolver: referenceResolver);

        var compilation = await project.GetCompilationAsync();

        compilation = compilation.WithOptions(options);

        foreach (var diagnostic in compilation.GetDiagnostics())
        {
            Console.WriteLine(diagnostic.ToString());
        }
    }
}
Apr 9, 2014 at 3:23 AM
Edited Apr 9, 2014 at 2:50 PM
Thanks again! I'd been using the version of Roslyn on nuget (Microsoft.CodeAnalysis -Pre), but it doesn't appear to have MetadataReferenceResolver in it. I was able to compile the main 4 projects from source (Workspaces, CSharpWorkspace, CodeAnalysis, CSharpCodeAnalysis... I get errors for most of the test projects etc) but after dropping those binaries in, I'm getting "Unrecognized project type" when opening the project (I assume because it's using MEF, and I've messed something up?).

Should the nuget version have this in already, and / or is there a quick & easy way I can get this compiling? (VS2013 Premium; I disabled the Roslyn add-ons as they broke a few things in VS... will send some feedback on that).