forked from dotnet/roslyn
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathExportProviderBuilder.cs
More file actions
108 lines (90 loc) · 5.67 KB
/
ExportProviderBuilder.cs
File metadata and controls
108 lines (90 loc) · 5.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Immutable;
using System.Reflection;
using Microsoft.CodeAnalysis.LanguageServer.Logging;
using Microsoft.CodeAnalysis.LanguageServer.Services;
using Microsoft.CodeAnalysis.Shared.Collections;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.Composition;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.LanguageServer;
internal sealed class ExportProviderBuilder
{
public static async Task<ExportProvider> CreateExportProviderAsync(ExtensionAssemblyManager extensionAssemblyManager, string? devKitDependencyPath, ILoggerFactory loggerFactory)
{
var logger = loggerFactory.CreateLogger<ExportProviderBuilder>();
var baseDirectory = AppContext.BaseDirectory;
// Load any Roslyn assemblies from the extension directory
var assemblyPaths = Directory.EnumerateFiles(baseDirectory, "Microsoft.CodeAnalysis*.dll");
assemblyPaths = assemblyPaths.Concat(Directory.EnumerateFiles(baseDirectory, "Microsoft.ServiceHub*.dll"));
// Temporarily explicitly load the dlls we want to add to the MEF composition. This is due to a runtime bug
// in the 7.0.4 runtime where the APIs MEF uses to load assemblies break with R2R assemblies.
// See https://github.com/dotnet/runtime/issues/83526
//
// Once a newer version of the runtime is widely available, we can remove this.
foreach (var path in assemblyPaths)
{
Assembly.LoadFrom(path);
}
// DevKit assemblies are not shipped in the main language server folder
// and not included in ExtensionAssemblyPaths (they get loaded into the default ALC).
// So manually add them to the MEF catalog here.
if (devKitDependencyPath != null)
{
assemblyPaths = assemblyPaths.Concat(devKitDependencyPath);
}
// Add the extension assemblies to the MEF catalog.
assemblyPaths = assemblyPaths.Concat(extensionAssemblyManager.ExtensionAssemblyPaths);
logger.LogTrace($"Composing MEF catalog using:{Environment.NewLine}{string.Join($" {Environment.NewLine}", assemblyPaths)}.");
// Create a MEF resolver that can resolve assemblies in the extension contexts.
var resolver = new Resolver(new CustomExportAssemblyLoader(extensionAssemblyManager, loggerFactory));
var discovery = PartDiscovery.Combine(
resolver,
new AttributedPartDiscovery(resolver, isNonPublicSupported: true), // "NuGet MEF" attributes (Microsoft.Composition)
new AttributedPartDiscoveryV1(resolver));
// TODO - we should likely cache the catalog so we don't have to rebuild it every time.
var catalog = ComposableCatalog.Create(resolver)
.AddParts(await discovery.CreatePartsAsync(assemblyPaths))
.WithCompositionService(); // Makes an ICompositionService export available to MEF parts to import
// Assemble the parts into a valid graph.
var config = CompositionConfiguration.Create(catalog);
// Verify we only have expected errors.
ThrowOnUnexpectedErrors(config, logger);
// Prepare an ExportProvider factory based on this graph.
var exportProviderFactory = config.CreateExportProviderFactory();
// Create an export provider, which represents a unique container of values.
// You can create as many of these as you want, but typically an app needs just one.
var exportProvider = exportProviderFactory.CreateExportProvider();
// Immediately set the logger factory, so that way it'll be available for the rest of the composition
exportProvider.GetExportedValue<ServerLoggerFactory>().SetFactory(loggerFactory);
return exportProvider;
}
private static void ThrowOnUnexpectedErrors(CompositionConfiguration configuration, ILogger logger)
{
// Verify that we have exactly the MEF errors that we expect. If we have less or more this needs to be updated to assert the expected behavior.
// Currently we are expecting the following:
// "----- CompositionError level 1 ------
// Microsoft.CodeAnalysis.ExternalAccess.Pythia.PythiaSignatureHelpProvider.ctor(implementation): expected exactly 1 export matching constraints:
// Contract name: Microsoft.CodeAnalysis.ExternalAccess.Pythia.Api.IPythiaSignatureHelpProviderImplementation
// TypeIdentityName: Microsoft.CodeAnalysis.ExternalAccess.Pythia.Api.IPythiaSignatureHelpProviderImplementation
// but found 0.
// part definition Microsoft.CodeAnalysis.ExternalAccess.Pythia.PythiaSignatureHelpProvider
var erroredParts = configuration.CompositionErrors.FirstOrDefault()?.SelectMany(error => error.Parts).Select(part => part.Definition.Type.Name) ?? Enumerable.Empty<string>();
var expectedErroredParts = new string[] { "PythiaSignatureHelpProvider" };
if (erroredParts.Count() != expectedErroredParts.Length || !erroredParts.All(part => expectedErroredParts.Contains(part)))
{
try
{
configuration.ThrowOnErrors();
}
catch (CompositionFailedException ex)
{
// The ToString for the composition failed exception doesn't output a nice set of errors by default, so log it separately here.
logger.LogError($"Encountered errors in the MEF composition:{Environment.NewLine}{ex.ErrorsAsString}");
throw;
}
}
}
}