Drop support for configuring plugins via xml file and refactor PluginLoader.Create* APIs
This removes support for configuring the plugin via XML. It's better to do most of the plugin configuration programmatically. Some settings, such as assemblies to share, have to be expressed via code and can't be expressed in XML. There weren't enough use cases for changing the plugin config via file post-publish, so I'm simplifying the library by removing this.
This commit is contained in:
parent
808b134f5c
commit
0bd41c4b3a
@ -33,8 +33,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReferencedLibv2", "test\Tes
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XunitSample", "test\TestProjects\XunitSample\XunitSample.csproj", "{3CEF9F32-5887-4977-AB8A-1BBC48875A21}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "McMaster.NETCore.Plugins.Sdk", "src\Plugins.Sdk\McMaster.NETCore.Plugins.Sdk.csproj", "{652D3662-DEB4-4FF6-8904-E581A84C040D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Banana", "test\TestProjects\Banana\Banana.csproj", "{8797AFCA-1EE8-4270-8FC4-F93A85F9C825}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Strawberry", "test\TestProjects\Strawberry\Strawberry.csproj", "{78D2B20D-B173-4CF2-A763-F2AA5347947E}"
|
||||
@ -202,18 +200,6 @@ Global
|
||||
{3CEF9F32-5887-4977-AB8A-1BBC48875A21}.Release|x64.Build.0 = Release|Any CPU
|
||||
{3CEF9F32-5887-4977-AB8A-1BBC48875A21}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{3CEF9F32-5887-4977-AB8A-1BBC48875A21}.Release|x86.Build.0 = Release|Any CPU
|
||||
{652D3662-DEB4-4FF6-8904-E581A84C040D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{652D3662-DEB4-4FF6-8904-E581A84C040D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{652D3662-DEB4-4FF6-8904-E581A84C040D}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{652D3662-DEB4-4FF6-8904-E581A84C040D}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{652D3662-DEB4-4FF6-8904-E581A84C040D}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{652D3662-DEB4-4FF6-8904-E581A84C040D}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{652D3662-DEB4-4FF6-8904-E581A84C040D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{652D3662-DEB4-4FF6-8904-E581A84C040D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{652D3662-DEB4-4FF6-8904-E581A84C040D}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{652D3662-DEB4-4FF6-8904-E581A84C040D}.Release|x64.Build.0 = Release|Any CPU
|
||||
{652D3662-DEB4-4FF6-8904-E581A84C040D}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{652D3662-DEB4-4FF6-8904-E581A84C040D}.Release|x86.Build.0 = Release|Any CPU
|
||||
{8797AFCA-1EE8-4270-8FC4-F93A85F9C825}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8797AFCA-1EE8-4270-8FC4-F93A85F9C825}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8797AFCA-1EE8-4270-8FC4-F93A85F9C825}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
@ -326,7 +312,6 @@ Global
|
||||
{CC37B369-BA9E-4B82-A14D-68B58924885D} = {98E964A2-55DA-4740-9F2E-B64FDF6715DB}
|
||||
{50F86C8F-E052-448C-84CE-08763945FCC7} = {98E964A2-55DA-4740-9F2E-B64FDF6715DB}
|
||||
{3CEF9F32-5887-4977-AB8A-1BBC48875A21} = {98E964A2-55DA-4740-9F2E-B64FDF6715DB}
|
||||
{652D3662-DEB4-4FF6-8904-E581A84C040D} = {471CA3C0-0BC9-4C0C-A013-D9356DBD0F38}
|
||||
{8797AFCA-1EE8-4270-8FC4-F93A85F9C825} = {98E964A2-55DA-4740-9F2E-B64FDF6715DB}
|
||||
{78D2B20D-B173-4CF2-A763-F2AA5347947E} = {98E964A2-55DA-4740-9F2E-B64FDF6715DB}
|
||||
{C0BA460C-C1AC-4C54-9C88-60E4B8681E94} = {98E964A2-55DA-4740-9F2E-B64FDF6715DB}
|
||||
|
||||
25
README.md
25
README.md
@ -138,28 +138,3 @@ public class Program
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Plugin config file
|
||||
|
||||
This also supports using a [config file](./docs/plugin-config.md) to control the settings of the loader per-plugin. This plugin config file can be hand-crafted, or generated using `McMaster.NETCore.Plugins.Sdk`.
|
||||
|
||||
```xml
|
||||
<!-- A project that produces the plugin. -->
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<IsPlugin>true</IsPlugin>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="McMaster.NETCore.Plugins.Sdk" Version="*" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
```
|
||||
|
||||
You can then use `PluginLoader.CreateFromConfigFile` to load the plugin from the configuration file.
|
||||
|
||||
```csharp
|
||||
PluginLoader.CreateFromConfigFile(
|
||||
filePath: "./plugins/MyPlugin/plugin.config",
|
||||
sharedTypes: new [] { typeof(IPlugin), typeof(IServiceCollection), typeof(ILogger) })
|
||||
```
|
||||
|
||||
@ -11,7 +11,7 @@ variables:
|
||||
- name: DOTNET_SKIP_FIRST_TIME_EXPERIENCE
|
||||
value: 1
|
||||
- name: DotNetCoreVersion
|
||||
value: 3.0.100-preview3-010431
|
||||
value: 3.0.100-preview5-011555
|
||||
- name: BuildNumber
|
||||
value: $[counter('buildnumber')]
|
||||
|
||||
|
||||
@ -56,10 +56,12 @@ public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
foreach (var pluginFile in Glob("plugins/*/plugin.config"))
|
||||
foreach (var pluginDirectory in Directory.GetDirectories("plugins/"))
|
||||
{
|
||||
var loader = PluginLoader.CreateFromConfigFile(
|
||||
configFile: pluginFile,
|
||||
var pluginDirName = Path.GetFileName(pluginDir);
|
||||
var assemblyFile = Path.Combine(pluginDir, pluginDirName + ".dll");
|
||||
var loader = PluginLoader.CreateFromAssemblyFile(
|
||||
assemblyFile: pluginFile,
|
||||
sharedTypes: new [] { typeof(IFruit) });
|
||||
|
||||
var plugin = loader.LoadDefaultAssembly();
|
||||
@ -79,9 +81,7 @@ A plugin author could implement the shared abstraction and distribute a plugin w
|
||||
|
||||
```xml
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Sdk Name="McMaster.NETCore.Plugins.Sdk" />
|
||||
<PropertyGroup>
|
||||
<IsPlugin>true</IsPlugin>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@ -1,31 +0,0 @@
|
||||
Plugin Config File
|
||||
==================
|
||||
|
||||
Plugins can be configured with an xml file, conventionally named 'plugin.config'. This file is optional,
|
||||
and allows plugins to control some settings about how they load.
|
||||
|
||||
```xml
|
||||
<PluginConfig MainAssembly="Banana, Version=1.0.0.0, Culture=neutral, PublicKeyToken=abc12341873">
|
||||
<PrivateDependency Identity="MyPrivateDependency, Version=2.0.0.0" />
|
||||
</PluginConfig>
|
||||
```
|
||||
|
||||
## Elements
|
||||
|
||||
The following elements are supported.
|
||||
|
||||
### `<PluginConfig>`
|
||||
|
||||
The root element of the config file is `PluginConfig`.
|
||||
|
||||
The following attributes are supported:
|
||||
|
||||
* `MainAssembly` - **required**. This value defines the default assembly for the plugin. Its value should be a valid assembly name.
|
||||
|
||||
### `<PrivateDependency>`
|
||||
|
||||
This element defines assemblies which the plugin will prefer to load as private versions instead of attempting
|
||||
to unify with the version with the host application.
|
||||
|
||||
The following attributes are supported:
|
||||
* `Identity` - **required**. The assembly identity of the private dependency.
|
||||
5
global.json
Normal file
5
global.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "3.0.100-preview5-011555"
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,11 @@
|
||||
Changes:
|
||||
* .NET Core 3.0 support
|
||||
* Support unloading plugins from memory (only .NET Core >3.0)
|
||||
|
||||
Breaking changes:
|
||||
* Support for loading plugin config from an XML file has been dropped. The APIs for PluginLoader.CreateFromConfigFile were removed
|
||||
in favor of PluginLoader.CreateFromAssemblyFile
|
||||
* Merged PluginLoaderOptions with the PluginConfig class
|
||||
</PackageReleaseNotes>
|
||||
<PackageReleaseNotes Condition="'$(VersionPrefix)' == '0.2.4'">
|
||||
Bug fix:
|
||||
|
||||
3
samples/Directory.Build.targets
Normal file
3
samples/Directory.Build.targets
Normal file
@ -0,0 +1,3 @@
|
||||
<!-- Intentionally empty to prevent samples from inheriting from the other repo targests -->
|
||||
<Project />
|
||||
|
||||
@ -23,7 +23,9 @@ namespace MvcWebApp
|
||||
|
||||
var plugin = PluginLoader.CreateFromAssemblyFile(
|
||||
Path.Combine(dir, pluginName + ".dll"), // create a plugin from for the .dll file
|
||||
sharedTypes: new [] { typeof(Controller) }); // this ensures that the version of MVC is shared between this app and the plugin
|
||||
config =>
|
||||
// this ensures that the version of MVC is shared between this app and the plugin
|
||||
config.PreferSharedTypes = true);
|
||||
|
||||
var pluginAssembly = plugin.LoadDefaultAssembly();
|
||||
Console.WriteLine($"Loading application parts from plugin {pluginName}");
|
||||
|
||||
@ -18,9 +18,11 @@ namespace MainWebApp
|
||||
|
||||
public Startup()
|
||||
{
|
||||
foreach (var pluginFile in Directory.GetFiles(AppContext.BaseDirectory, "plugin.config", SearchOption.AllDirectories))
|
||||
foreach (var pluginDir in Directory.GetDirectories(Path.Combine(AppContext.BaseDirectory, "plugins")))
|
||||
{
|
||||
var loader = PluginLoader.CreateFromConfigFile(pluginFile,
|
||||
var dirName = Path.GetFileName(pluginDir);
|
||||
var pluginFile = Path.Combine(pluginDir, dirName + ".dll");
|
||||
var loader = PluginLoader.CreateFromAssemblyFile(pluginFile,
|
||||
// this ensures that the plugin resolves to the same version of DependencyInjection
|
||||
// and ASP.NET Core that the current app uses
|
||||
sharedTypes: new[]
|
||||
|
||||
@ -23,10 +23,11 @@ This plugin uses AutoMapper, Version=7.0.1.0, Culture=neutral, PublicKeyToken=be
|
||||
|
||||
There are some important types, however, which must share the same identity between the plugins and the host.
|
||||
To ensure type exchange works between the host and the plugins, the MainWebApp project uses the `sharedTypes`
|
||||
parameter on `PluginLoader.CreateFromConfigFile`.
|
||||
parameter on `PluginLoader.CreateFromAssemblyFile`.
|
||||
|
||||
```csharp
|
||||
var loader = PluginLoader.CreateFromConfigFile(pluginFile,
|
||||
var loader = PluginLoader.CreateFromAssemblyFile(
|
||||
pluginAssembly,
|
||||
sharedTypes: new[]
|
||||
{
|
||||
typeof(IApplicationBuilder),
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
<Project>
|
||||
|
||||
<!--
|
||||
Required for the samples in this repo only.
|
||||
Normally, you should replace this with a PackageReference to McMaster.NETCore.Plugins.Sdk.
|
||||
-->
|
||||
<Import Project="$(RepoRoot)src\Plugins.Sdk\sdk\Sdk.targets" />
|
||||
|
||||
</Project>
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<IsPlugin>true</IsPlugin>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
<Project>
|
||||
|
||||
<!--
|
||||
Required for the samples in this repo only.
|
||||
Normally, you should replace this with a PackageReference to McMaster.NETCore.Plugins.Sdk.
|
||||
-->
|
||||
<Import Project="$(RepoRoot)src\Plugins.Sdk\sdk\Sdk.targets" />
|
||||
|
||||
</Project>
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<IsPlugin>true</IsPlugin>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -1,24 +0,0 @@
|
||||
<Project>
|
||||
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<PackageDescription>Generates the plugin configuration files required to load .dll files.</PackageDescription>
|
||||
<PackageTags>.NET Core;plugins;Sdk</PackageTags>
|
||||
<IncludeBuildOutput>false</IncludeBuildOutput>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<IncludeSymbols>false</IncludeSymbols>
|
||||
<IsPackable>true</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="sdk/*" PackagePath="sdk/" />
|
||||
<Content Include="build/*" PackagePath="build/" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
|
||||
|
||||
<Target Name="Compile" />
|
||||
<Target Name="CopyFilesToOutputDirectory" />
|
||||
</Project>
|
||||
@ -1,3 +0,0 @@
|
||||
<Project>
|
||||
<Import Project="..\sdk\Sdk.props" />
|
||||
</Project>
|
||||
@ -1,3 +0,0 @@
|
||||
<Project>
|
||||
<Import Project="..\sdk\Sdk.targets" />
|
||||
</Project>
|
||||
@ -1,7 +0,0 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
||||
|
||||
<PluginConfigFileName>plugin.config</PluginConfigFileName>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@ -1,42 +0,0 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
||||
|
||||
<IsPlugin Condition=" '$(IsPlugin)' == '' ">true</IsPlugin>
|
||||
<IntermediatePluginConfigFile>$(IntermediateOutputPath)$(MSBuildProjectName).$(PluginConfigFileName)</IntermediatePluginConfigFile>
|
||||
<GeneratePluginConfigFile Condition="'$(IsPlugin)' == 'true' AND '$(TargetFramework)' != ''">true</GeneratePluginConfigFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="$(IntermediatePluginConfigFile)" Condition="'$(GeneratePluginConfigFile)' == 'true'">
|
||||
<Visible>false</Visible>
|
||||
<Link>$(PluginConfigFileName)</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="GeneratePluginConfig"
|
||||
BeforeTargets="CoreCompile"
|
||||
Inputs="$(MSBuildAllProjects);$(TargetPath)"
|
||||
Outputs="$(IntermediatePluginConfigFile)"
|
||||
Condition="'$(GeneratePluginConfigFile)' == 'true'">
|
||||
<PropertyGroup>
|
||||
<_AssemblyIdentity>$(AssemblyName)</_AssemblyIdentity>
|
||||
<_AssemblyIdentity Condition="'$(AssemblyVersion)' != ''">$(_AssemblyIdentity), Version=$(AssemblyVersion)</_AssemblyIdentity>
|
||||
<PluginConfigContent>
|
||||
<![CDATA[
|
||||
<PluginConfig MainAssembly="$(_AssemblyIdentity)">
|
||||
@(PrivateDependency->'<PrivateDependency Identity="%(Identity)" />', '%0A ')
|
||||
</PluginConfig>
|
||||
]]>
|
||||
</PluginConfigContent>
|
||||
</PropertyGroup>
|
||||
|
||||
<WriteLinesToFile Lines="$(PluginConfigContent)" Overwrite="true" File="$(IntermediatePluginConfigFile)" />
|
||||
|
||||
<ItemGroup>
|
||||
<FileWrites Include="$(IntermediatePluginConfigFile)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
@ -23,7 +23,7 @@ namespace McMaster.NETCore.Plugins.Loader
|
||||
private readonly Dictionary<string, NativeLibrary> _nativeLibraries = new Dictionary<string, NativeLibrary>(StringComparer.Ordinal);
|
||||
private readonly HashSet<string> _privateAssemblies = new HashSet<string>(StringComparer.Ordinal);
|
||||
private readonly HashSet<string> _defaultAssemblies = new HashSet<string>(StringComparer.Ordinal);
|
||||
private string _basePath;
|
||||
private string _mainAssemblyPath;
|
||||
private bool _preferDefaultLoadContext;
|
||||
|
||||
#if FEATURE_UNLOAD
|
||||
@ -46,7 +46,7 @@ namespace McMaster.NETCore.Plugins.Loader
|
||||
}
|
||||
|
||||
return new ManagedLoadContext(
|
||||
_basePath,
|
||||
_mainAssemblyPath,
|
||||
_managedLibraries,
|
||||
_nativeLibraries,
|
||||
_privateAssemblies,
|
||||
@ -62,12 +62,12 @@ namespace McMaster.NETCore.Plugins.Loader
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the base directory for the context. This is used as the starting point for loading
|
||||
/// assemblies. Also known as the 'app local' directory.
|
||||
/// Set the file path to the main assembly for the context. This is used as the starting point for loading
|
||||
/// other assemblies. The directory that contains it is also known as the 'app local' directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The file path. Must not be null or empty. Must be an absolute path.</param>
|
||||
/// <returns>The builder.</returns>
|
||||
public AssemblyLoadContextBuilder SetBaseDirectory(string path)
|
||||
public AssemblyLoadContextBuilder SetMainAssemblyPath(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
@ -79,7 +79,7 @@ namespace McMaster.NETCore.Plugins.Loader
|
||||
throw new ArgumentException("Argument must be a full path.", nameof(path));
|
||||
}
|
||||
|
||||
_basePath = path;
|
||||
_mainAssemblyPath = path;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@ -52,7 +52,6 @@ namespace McMaster.NETCore.Plugins.Loader
|
||||
using (var file = File.OpenRead(depsFilePath))
|
||||
{
|
||||
var deps = reader.Read(file);
|
||||
builder.SetBaseDirectory(Path.GetDirectoryName(depsFilePath));
|
||||
builder.AddDependencyContext(deps);
|
||||
}
|
||||
|
||||
|
||||
@ -18,6 +18,7 @@ namespace McMaster.NETCore.Plugins.Loader
|
||||
internal class ManagedLoadContext : AssemblyLoadContext
|
||||
{
|
||||
private readonly string _basePath;
|
||||
private readonly string _mainAssemblyPath;
|
||||
private readonly IReadOnlyDictionary<string, ManagedLibrary> _managedAssemblies;
|
||||
private readonly IReadOnlyDictionary<string, NativeLibrary> _nativeLibraries;
|
||||
private readonly IReadOnlyCollection<string> _privateAssemblies;
|
||||
@ -26,7 +27,7 @@ namespace McMaster.NETCore.Plugins.Loader
|
||||
private readonly bool _preferDefaultLoadContext;
|
||||
private readonly string[] _resourceRoots;
|
||||
|
||||
public ManagedLoadContext(string baseDirectory,
|
||||
public ManagedLoadContext(string mainAssemblyPath,
|
||||
IReadOnlyDictionary<string, ManagedLibrary> managedAssemblies,
|
||||
IReadOnlyDictionary<string, NativeLibrary> nativeLibraries,
|
||||
IReadOnlyCollection<string> privateAssemblies,
|
||||
@ -44,7 +45,8 @@ namespace McMaster.NETCore.Plugins.Loader
|
||||
throw new ArgumentNullException(nameof(resourceProbingPaths));
|
||||
}
|
||||
|
||||
_basePath = baseDirectory ?? throw new ArgumentNullException(nameof(baseDirectory));
|
||||
_mainAssemblyPath = mainAssemblyPath ?? throw new ArgumentNullException(nameof(mainAssemblyPath));
|
||||
_basePath = Path.GetDirectoryName(mainAssemblyPath);
|
||||
_managedAssemblies = managedAssemblies ?? throw new ArgumentNullException(nameof(managedAssemblies));
|
||||
_privateAssemblies = privateAssemblies ?? throw new ArgumentNullException(nameof(privateAssemblies));
|
||||
_defaultAssemblies = defaultAssemblies ?? throw new ArgumentNullException(nameof(defaultAssemblies));
|
||||
|
||||
@ -5,8 +5,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace McMaster.NETCore.Plugins
|
||||
{
|
||||
@ -16,78 +14,52 @@ namespace McMaster.NETCore.Plugins
|
||||
public class PluginConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Initialize an instance of <see cref="PluginConfig" />.
|
||||
/// Initializes a new instance of <see cref="PluginConfig" />
|
||||
/// </summary>
|
||||
/// <param name="mainAssembly">The name of the main assembly.</param>
|
||||
/// <param name="privateAssembly">A list of assemblies to treat as private, if possible.</param>
|
||||
protected PluginConfig(AssemblyName mainAssembly, IReadOnlyCollection<AssemblyName> privateAssembly)
|
||||
/// <param name="mainAssemblyPath">The full file path to the main assembly for the plugin.</param>
|
||||
public PluginConfig(string mainAssemblyPath)
|
||||
{
|
||||
MainAssembly = mainAssembly ?? throw new ArgumentNullException(nameof(mainAssembly));
|
||||
PrivateAssemblies = privateAssembly ?? throw new ArgumentNullException(nameof(privateAssembly));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of <see cref="PluginConfig" /> from a file.
|
||||
/// </summary>
|
||||
/// <param name="filePath">The path the config file.</param>
|
||||
/// <returns></returns>
|
||||
public static PluginConfig CreateFromFile(string filePath)
|
||||
{
|
||||
using (var reader = File.OpenText(filePath))
|
||||
if (string.IsNullOrEmpty(mainAssemblyPath))
|
||||
{
|
||||
return PluginConfig.CreateFromReader(reader);
|
||||
throw new ArgumentException("Value must be null or not empty", nameof(mainAssemblyPath));
|
||||
}
|
||||
|
||||
if (!Path.IsPathRooted(mainAssemblyPath))
|
||||
{
|
||||
throw new ArgumentException("Value must be an absolute file path", nameof(mainAssemblyPath));
|
||||
}
|
||||
|
||||
MainAssemblyPath = mainAssemblyPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of <see cref="PluginConfig" /> from a file.
|
||||
/// The file path to the main assembly.
|
||||
/// </summary>
|
||||
/// <param name="reader">The reader containing the config file.</param>
|
||||
/// <returns></returns>
|
||||
|
||||
public static PluginConfig CreateFromReader(TextReader reader)
|
||||
{
|
||||
var privateDeps = new HashSet<AssemblyName>();
|
||||
var doc = XDocument.Load(reader, LoadOptions.SetLineInfo);
|
||||
|
||||
if (doc.Root.Name != "PluginConfig")
|
||||
{
|
||||
throw new InvalidDataException("Root element should be 'PluginConfig'");
|
||||
}
|
||||
|
||||
var mainAssemblyAttr = doc.Root.Attribute("MainAssembly");
|
||||
if (mainAssemblyAttr == null || string.IsNullOrEmpty(mainAssemblyAttr.Value))
|
||||
{
|
||||
IXmlLineInfo line = doc.Root;
|
||||
throw new InvalidDataException($"Missing required attribute 'MainAssembly' for PluginConfig on line {line.LineNumber}");
|
||||
}
|
||||
|
||||
var mainAssembly = new AssemblyName(mainAssemblyAttr.Value);
|
||||
|
||||
foreach (var dep in doc.Root.Descendants("PrivateDependency"))
|
||||
{
|
||||
var identity = dep.Attribute("Identity");
|
||||
if (identity == null || string.IsNullOrEmpty(identity.Value))
|
||||
{
|
||||
IXmlLineInfo line = dep;
|
||||
throw new InvalidDataException($"Missing required attribute 'Identity' for PrivateDependency on line {line.LineNumber}");
|
||||
}
|
||||
|
||||
privateDeps.Add(new AssemblyName(identity.Value));
|
||||
}
|
||||
|
||||
return new PluginConfig(mainAssembly, privateDeps);
|
||||
}
|
||||
public string MainAssemblyPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of assemblies which should be treated as private.
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<AssemblyName> PrivateAssemblies { get; protected set; }
|
||||
public ICollection<AssemblyName> PrivateAssemblies { get; protected set; } = new List<AssemblyName>();
|
||||
|
||||
/// <summary>
|
||||
/// The name of the main assembly.
|
||||
/// A list of assemblies which should be unified between the host and the plugin.
|
||||
/// </summary>
|
||||
public AssemblyName MainAssembly { get; protected set; }
|
||||
public ICollection<AssemblyName> SharedAssemblies { get; protected set; } = new List<AssemblyName>();
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to unify all types from a plugin with the host.
|
||||
/// <para>
|
||||
/// This does not guarantee types will unify.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public bool PreferSharedTypes { get; set; }
|
||||
|
||||
#if FEATURE_UNLOAD
|
||||
/// <summary>
|
||||
/// The plugin can be unloaded from memory.
|
||||
/// </summary>
|
||||
public bool IsUnloadable { get; set; }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,134 +21,107 @@ namespace McMaster.NETCore.Plugins
|
||||
/// </summary>
|
||||
public class PluginLoader : IDisposable
|
||||
{
|
||||
// we have to duplicate a large block of xml code because C# doesn't allow conditional XML elements
|
||||
#if FEATURE_UNLOAD
|
||||
/// <summary>
|
||||
/// Create a plugin loader using the settings from a plugin config file.
|
||||
/// <seealso cref="PluginConfig" /> for defaults on the plugin configuration.
|
||||
/// </summary>
|
||||
/// <param name="filePath">The file path to the plugin config.</param>
|
||||
/// <param name="sharedTypes">A list of types which should be shared between the host and the plugin.</param>
|
||||
/// <param name="isUnloadable">Enable unloading the plugin from memory.</param>
|
||||
/// <returns>A loader.</returns>
|
||||
public static PluginLoader CreateFromConfigFile(string filePath, Type[] sharedTypes = null, bool isUnloadable = false)
|
||||
{
|
||||
var loaderOptions = isUnloadable
|
||||
? PluginLoaderOptions.IsUnloadable
|
||||
: PluginLoaderOptions.None;
|
||||
#else
|
||||
/// <summary>
|
||||
/// Create a plugin loader using the settings from a plugin config file.
|
||||
/// <seealso cref="PluginConfig" /> for defaults on the plugin configuration.
|
||||
/// </summary>
|
||||
/// <param name="filePath">The file path to the plugin config.</param>
|
||||
/// <param name="sharedTypes">A list of types which should be shared between the host and the plugin.</param>
|
||||
/// <returns>A loader.</returns>
|
||||
public static PluginLoader CreateFromConfigFile(string filePath, Type[] sharedTypes = null)
|
||||
{
|
||||
var loaderOptions = PluginLoaderOptions.None;
|
||||
#endif
|
||||
var config = PluginConfig.CreateFromFile(filePath);
|
||||
var baseDir = Path.GetDirectoryName(filePath);
|
||||
return new PluginLoader(config,
|
||||
baseDir,
|
||||
sharedTypes,
|
||||
loaderOptions);
|
||||
}
|
||||
|
||||
#if FEATURE_UNLOAD
|
||||
/// <summary>
|
||||
/// Create a plugin loader using an existing <see cref="PluginConfig"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="config">The <see cref="PluginConfig"/> instance.</param>
|
||||
/// <param name="baseDir">The base directory from which to load / search for dependencies on disk.</param>
|
||||
/// <param name="sharedTypes">A list of types which should be shared between the host and the plugin.</param>
|
||||
/// <param name="isUnloadable">Enable unloading the plugin from memory.</param>
|
||||
/// <returns>A loader.</returns>
|
||||
public static PluginLoader CreateFromConfigFile(PluginConfig config, string baseDir, Type[] sharedTypes = null, bool isUnloadable = false)
|
||||
{
|
||||
var loaderOptions = isUnloadable
|
||||
? PluginLoaderOptions.IsUnloadable
|
||||
: PluginLoaderOptions.None;
|
||||
#else
|
||||
/// <summary>
|
||||
/// Create a plugin loader using an existing <see cref="PluginConfig"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="config">The <see cref="PluginConfig"/> instance.</param>
|
||||
/// <param name="baseDir">The base directory from which to load / search for dependencies on disk.</param>
|
||||
/// <param name="sharedTypes">A list of types which should be shared between the host and the plugin.</param>
|
||||
/// <returns>A loader.</returns>
|
||||
public static PluginLoader CreateFromConfigFile(PluginConfig config, string baseDir, Type[] sharedTypes = null)
|
||||
{
|
||||
var loaderOptions = PluginLoaderOptions.None;
|
||||
#endif
|
||||
return new PluginLoader(config,
|
||||
baseDir,
|
||||
sharedTypes,
|
||||
loaderOptions);
|
||||
}
|
||||
|
||||
#if FEATURE_UNLOAD
|
||||
/// <summary>
|
||||
/// Create a plugin loader for an assembly file.
|
||||
/// </summary>
|
||||
/// <param name="assemblyFile">The file path to the plugin config.</param>
|
||||
/// <param name="sharedTypes">A list of types which should be shared between the host and the plugin.</param>
|
||||
/// <param name="assemblyFile">The file path to the main assembly for the plugin.</param>
|
||||
/// <param name="isUnloadable">Enable unloading the plugin from memory.</param>
|
||||
/// <param name="sharedTypes">A list of types which should be shared between the host and the plugin.</param>
|
||||
/// <returns>A loader.</returns>
|
||||
public static PluginLoader CreateFromAssemblyFile(string assemblyFile, Type[] sharedTypes = null, bool isUnloadable = false)
|
||||
{
|
||||
var loaderOptions = isUnloadable
|
||||
? PluginLoaderOptions.IsUnloadable
|
||||
: PluginLoaderOptions.None;
|
||||
#else
|
||||
public static PluginLoader CreateFromAssemblyFile(string assemblyFile, bool isUnloadable, Type[] sharedTypes)
|
||||
=> CreateFromAssemblyFile(assemblyFile,isUnloadable, sharedTypes, _ => { });
|
||||
|
||||
/// <summary>
|
||||
/// Create a plugin loader for an assembly file.
|
||||
/// </summary>
|
||||
/// <param name="assemblyFile">The file path to the plugin config.</param>
|
||||
/// <param name="assemblyFile">The file path to the main assembly for the plugin.</param>
|
||||
/// <param name="isUnloadable">Enable unloading the plugin from memory.</param>
|
||||
/// <param name="sharedTypes">A list of types which should be shared between the host and the plugin.</param>
|
||||
/// <param name="configure">A function which can be used to configure advanced options for the plugin loader.</param>
|
||||
/// <returns>A loader.</returns>
|
||||
public static PluginLoader CreateFromAssemblyFile(string assemblyFile, Type[] sharedTypes = null)
|
||||
public static PluginLoader CreateFromAssemblyFile(string assemblyFile, bool isUnloadable, Type[] sharedTypes, Action<PluginConfig> configure)
|
||||
{
|
||||
var loaderOptions = PluginLoaderOptions.None;
|
||||
#endif
|
||||
return CreateFromAssemblyFile(assemblyFile,
|
||||
sharedTypes,
|
||||
loaderOptions);
|
||||
config =>
|
||||
{
|
||||
config.IsUnloadable = isUnloadable;
|
||||
configure(config);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Create a plugin loader for an assembly file.
|
||||
/// </summary>
|
||||
/// <param name="assemblyFile">The file path to the main assembly for the plugin.</param>
|
||||
/// <param name="sharedTypes">A list of types which should be shared between the host and the plugin.</param>
|
||||
/// <returns>A loader.</returns>
|
||||
public static PluginLoader CreateFromAssemblyFile(string assemblyFile, Type[] sharedTypes)
|
||||
=> CreateFromAssemblyFile(assemblyFile, sharedTypes, _ => { });
|
||||
|
||||
/// <summary>
|
||||
/// Create a plugin loader for an assembly file.
|
||||
/// </summary>
|
||||
/// <param name="assemblyFile">The file path to the main assembly for the plugin.</param>
|
||||
/// <param name="sharedTypes">A list of types which should be shared between the host and the plugin.</param>
|
||||
/// <param name="configure">A function which can be used to configure advanced options for the plugin loader.</param>
|
||||
/// <returns>A loader.</returns>
|
||||
public static PluginLoader CreateFromAssemblyFile(string assemblyFile, Type[] sharedTypes, Action<PluginConfig> configure)
|
||||
{
|
||||
return CreateFromAssemblyFile(assemblyFile,
|
||||
config =>
|
||||
{
|
||||
if (sharedTypes != null)
|
||||
{
|
||||
foreach (var type in sharedTypes)
|
||||
{
|
||||
config.SharedAssemblies.Add(type.Assembly.GetName());
|
||||
}
|
||||
}
|
||||
configure(config);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a plugin loader for an assembly file.
|
||||
/// </summary>
|
||||
/// <param name="assemblyFile">The file path to the plugin config.</param>
|
||||
/// <param name="sharedTypes">A list of types which should be shared between the host and the plugin.</param>
|
||||
/// <param name="loaderOptions">Options for the loader</param>
|
||||
/// <param name="assemblyFile">The file path to the main assembly for the plugin.</param>
|
||||
/// <returns>A loader.</returns>
|
||||
public static PluginLoader CreateFromAssemblyFile(string assemblyFile, Type[] sharedTypes, PluginLoaderOptions loaderOptions)
|
||||
public static PluginLoader CreateFromAssemblyFile(string assemblyFile)
|
||||
=> CreateFromAssemblyFile(assemblyFile, _ => { });
|
||||
|
||||
/// <summary>
|
||||
/// Create a plugin loader for an assembly file.
|
||||
/// </summary>
|
||||
/// <param name="assemblyFile">The file path to the main assembly for the plugin.</param>
|
||||
/// <param name="configure">A function which can be used to configure advanced options for the plugin loader.</param>
|
||||
/// <returns>A loader.</returns>
|
||||
public static PluginLoader CreateFromAssemblyFile(string assemblyFile, Action<PluginConfig> configure)
|
||||
{
|
||||
var config = new FileOnlyPluginConfig(assemblyFile);
|
||||
var baseDir = Path.GetDirectoryName(assemblyFile);
|
||||
return new PluginLoader(config, baseDir, sharedTypes, loaderOptions);
|
||||
if (configure == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configure));
|
||||
}
|
||||
|
||||
var config = new PluginConfig(assemblyFile);
|
||||
configure(config);
|
||||
return new PluginLoader(config);
|
||||
}
|
||||
|
||||
private class FileOnlyPluginConfig : PluginConfig
|
||||
{
|
||||
public FileOnlyPluginConfig(string filePath)
|
||||
: base(new AssemblyName(Path.GetFileNameWithoutExtension(filePath)), Array.Empty<AssemblyName>())
|
||||
{ }
|
||||
}
|
||||
|
||||
private readonly string _mainAssembly;
|
||||
private readonly PluginConfig _config;
|
||||
private readonly AssemblyLoadContext _context;
|
||||
private volatile bool _disposed;
|
||||
|
||||
internal PluginLoader(PluginConfig config,
|
||||
string baseDir,
|
||||
Type[] sharedTypes,
|
||||
PluginLoaderOptions loaderOptions)
|
||||
/// <summary>
|
||||
/// Initialize an instance of <see cref="PluginLoader" />
|
||||
/// </summary>
|
||||
/// <param name="config">The configuration for the plugin.</param>
|
||||
public PluginLoader(PluginConfig config)
|
||||
{
|
||||
_mainAssembly = Path.Combine(baseDir, config.MainAssembly.Name + ".dll");
|
||||
_context = CreateLoadContext(baseDir, config, sharedTypes, loaderOptions);
|
||||
_config = config ?? throw new ArgumentNullException(nameof(config));
|
||||
_context = CreateLoadContext(config);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -174,7 +147,7 @@ namespace McMaster.NETCore.Plugins
|
||||
public Assembly LoadDefaultAssembly()
|
||||
{
|
||||
EnsureNotDisposed();
|
||||
return _context.LoadFromAssemblyPath(_mainAssembly);
|
||||
return _context.LoadFromAssemblyPath(_config.MainAssemblyPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -229,49 +202,44 @@ namespace McMaster.NETCore.Plugins
|
||||
}
|
||||
}
|
||||
|
||||
private static AssemblyLoadContext CreateLoadContext(
|
||||
string baseDir,
|
||||
PluginConfig config,
|
||||
Type[] sharedTypes,
|
||||
PluginLoaderOptions loaderOptions)
|
||||
private static AssemblyLoadContext CreateLoadContext(PluginConfig config)
|
||||
{
|
||||
var depsJsonFile = Path.Combine(baseDir, config.MainAssembly.Name + ".deps.json");
|
||||
|
||||
var builder = new AssemblyLoadContextBuilder();
|
||||
|
||||
if (File.Exists(depsJsonFile))
|
||||
{
|
||||
builder.AddDependencyContext(depsJsonFile);
|
||||
}
|
||||
|
||||
builder.SetBaseDirectory(baseDir);
|
||||
builder.SetMainAssemblyPath(config.MainAssemblyPath);
|
||||
|
||||
foreach (var ext in config.PrivateAssemblies)
|
||||
{
|
||||
builder.PreferLoadContextAssembly(ext);
|
||||
}
|
||||
|
||||
if (loaderOptions.HasFlag(PluginLoaderOptions.PreferSharedTypes))
|
||||
if (config.PreferSharedTypes)
|
||||
{
|
||||
builder.PreferDefaultLoadContext(true);
|
||||
}
|
||||
|
||||
#if FEATURE_UNLOAD
|
||||
if (loaderOptions.HasFlag(PluginLoaderOptions.IsUnloadable))
|
||||
if (config.IsUnloadable)
|
||||
{
|
||||
builder.EnableUnloading();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (sharedTypes != null)
|
||||
foreach (var assemblyName in config.SharedAssemblies)
|
||||
{
|
||||
foreach (var type in sharedTypes)
|
||||
{
|
||||
builder.PreferDefaultLoadContextAssembly(type.Assembly.GetName());
|
||||
}
|
||||
builder.PreferDefaultLoadContextAssembly(assemblyName);
|
||||
}
|
||||
|
||||
var pluginRuntimeConfigFile = Path.Combine(baseDir, config.MainAssembly.Name + ".runtimeconfig.json");
|
||||
var baseDir = Path.GetDirectoryName(config.MainAssemblyPath);
|
||||
var assemblyFileName = Path.GetFileNameWithoutExtension(config.MainAssemblyPath);
|
||||
|
||||
var depsJsonFile = Path.Combine(baseDir, assemblyFileName + ".deps.json");
|
||||
if (File.Exists(depsJsonFile))
|
||||
{
|
||||
builder.AddDependencyContext(depsJsonFile);
|
||||
}
|
||||
|
||||
var pluginRuntimeConfigFile = Path.Combine(baseDir, assemblyFileName + ".runtimeconfig.json");
|
||||
|
||||
builder.TryAddAdditionalProbingPathFromRuntimeConfig(pluginRuntimeConfigFile, includeDevConfig: true, out _);
|
||||
|
||||
|
||||
@ -1,37 +0,0 @@
|
||||
// Copyright (c) Nate McMaster.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace McMaster.NETCore.Plugins
|
||||
{
|
||||
/// <summary>
|
||||
/// Options for how <see cref="PluginLoader"/> behaves.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum PluginLoaderOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Use the default behavior.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to unify all types from a plugin with the host.
|
||||
/// <para>
|
||||
/// This does not guarantee types will unify.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
PreferSharedTypes = 1 << 0,
|
||||
|
||||
#if FEATURE_UNLOAD
|
||||
/// <summary>
|
||||
/// If the platform supports it, allow unloading the plugin. This requires .NET Core 3.0 or higher.
|
||||
/// <para>
|
||||
/// Setting this option does not guarantee that the plugin can be unloaded.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
IsUnloadable = 1 << 1,
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -32,7 +32,7 @@ namespace McMaster.NETCore.Plugins.Tests
|
||||
[MethodImpl(MethodImplOptions.NoInlining)] // ensure no local vars are create
|
||||
private void ExecuteAndUnload(string path, out WeakReference weakRef)
|
||||
{
|
||||
var loader = PluginLoader.CreateFromConfigFile(path, isUnloadable: true);
|
||||
var loader = PluginLoader.CreateFromAssemblyFile(path, c => { c.IsUnloadable = true; });
|
||||
var assembly = loader.LoadDefaultAssembly();
|
||||
|
||||
var method = assembly
|
||||
@ -57,7 +57,7 @@ namespace McMaster.NETCore.Plugins.Tests
|
||||
public void LoadsNetCoreProjectWithNativeDeps()
|
||||
{
|
||||
var path = TestResources.GetTestProjectAssembly("PowerShellPlugin");
|
||||
var loader = PluginLoader.CreateFromConfigFile(path);
|
||||
var loader = PluginLoader.CreateFromAssemblyFile(path);
|
||||
var assembly = loader.LoadDefaultAssembly();
|
||||
|
||||
var method = assembly
|
||||
@ -74,7 +74,7 @@ namespace McMaster.NETCore.Plugins.Tests
|
||||
// SqlClient has P/invoke that calls "sni.dll" on Windows. This test checks
|
||||
// that native libraries can still be resolved in this case.
|
||||
var path = TestResources.GetTestProjectAssembly("SqlClientApp");
|
||||
var loader = PluginLoader.CreateFromConfigFile(path);
|
||||
var loader = PluginLoader.CreateFromAssemblyFile(path);
|
||||
var assembly = loader.LoadDefaultAssembly();
|
||||
|
||||
var method = assembly
|
||||
@ -88,7 +88,7 @@ namespace McMaster.NETCore.Plugins.Tests
|
||||
public void LoadsNetCoreApp20Project()
|
||||
{
|
||||
var path = TestResources.GetTestProjectAssembly("NetCoreApp20App");
|
||||
var loader = PluginLoader.CreateFromConfigFile(path);
|
||||
var loader = PluginLoader.CreateFromAssemblyFile(path);
|
||||
var assembly = loader.LoadDefaultAssembly();
|
||||
|
||||
var method = assembly
|
||||
@ -102,7 +102,7 @@ namespace McMaster.NETCore.Plugins.Tests
|
||||
public void LoadsNetStandard20Project()
|
||||
{
|
||||
var path = TestResources.GetTestProjectAssembly("NetStandardClassLib");
|
||||
var loader = PluginLoader.CreateFromConfigFile(path);
|
||||
var loader = PluginLoader.CreateFromAssemblyFile(path);
|
||||
var assembly = loader.LoadDefaultAssembly();
|
||||
|
||||
var type = assembly.GetType("NetStandardClassLib.Class1", throwOnError: true);
|
||||
@ -119,7 +119,7 @@ namespace McMaster.NETCore.Plugins.Tests
|
||||
// In this case, the host will pick the rid-specific version
|
||||
|
||||
var path = TestResources.GetTestProjectAssembly("DrawingApp");
|
||||
var loader = PluginLoader.CreateFromConfigFile(path);
|
||||
var loader = PluginLoader.CreateFromAssemblyFile(path);
|
||||
var assembly = loader.LoadDefaultAssembly();
|
||||
|
||||
var type = assembly.GetType("Finder", throwOnError: true);
|
||||
@ -151,7 +151,7 @@ namespace McMaster.NETCore.Plugins.Tests
|
||||
private IFruit GetPlátano()
|
||||
{
|
||||
var path = TestResources.GetTestProjectAssembly("Plátano");
|
||||
var loader = PluginLoader.CreateFromConfigFile(path,
|
||||
var loader = PluginLoader.CreateFromAssemblyFile(path,
|
||||
#if NETCOREAPP3_0
|
||||
isUnloadable: true,
|
||||
#endif
|
||||
|
||||
@ -14,6 +14,7 @@ namespace McMaster.NETCore.Plugins.Tests
|
||||
{
|
||||
var samplePath = TestResources.GetTestProjectAssembly("XunitSample");
|
||||
var context = new AssemblyLoadContextBuilder()
|
||||
.SetMainAssemblyPath(samplePath)
|
||||
.AddProbingPath(samplePath)
|
||||
.AddDependencyContext(Path.Combine(Path.GetDirectoryName(samplePath), "XunitSample.deps.json"))
|
||||
.PreferDefaultLoadContext(true)
|
||||
@ -28,6 +29,7 @@ namespace McMaster.NETCore.Plugins.Tests
|
||||
var samplePath = TestResources.GetTestProjectAssembly("XunitSample");
|
||||
|
||||
var context = new AssemblyLoadContextBuilder()
|
||||
.SetMainAssemblyPath(samplePath)
|
||||
.AddProbingPath(samplePath)
|
||||
.AddDependencyContext(Path.Combine(Path.GetDirectoryName(samplePath), "XunitSample.deps.json"))
|
||||
.Build();
|
||||
@ -41,12 +43,14 @@ namespace McMaster.NETCore.Plugins.Tests
|
||||
var samplePath = TestResources.GetTestProjectAssembly("NetCoreApp20App");
|
||||
|
||||
var defaultLoader = new AssemblyLoadContextBuilder()
|
||||
.SetMainAssemblyPath(samplePath)
|
||||
.AddProbingPath(samplePath)
|
||||
.PreferDefaultLoadContext(false)
|
||||
.AddDependencyContext(Path.Combine(Path.GetDirectoryName(samplePath), "NetCoreApp20App.deps.json"))
|
||||
.Build();
|
||||
|
||||
var unifedLoader = new AssemblyLoadContextBuilder()
|
||||
.SetMainAssemblyPath(samplePath)
|
||||
.AddProbingPath(samplePath)
|
||||
.PreferDefaultLoadContext(true)
|
||||
.AddDependencyContext(Path.Combine(Path.GetDirectoryName(samplePath), "NetCoreApp20App.deps.json"))
|
||||
|
||||
@ -10,9 +10,9 @@ namespace McMaster.NETCore.Plugins.Tests
|
||||
[Fact]
|
||||
public void EachContextHasPrivateVersions()
|
||||
{
|
||||
var json9context = PluginLoader.CreateFromConfigFile(TestResources.GetTestProjectAssembly("JsonNet9"));
|
||||
var json10context = PluginLoader.CreateFromConfigFile(TestResources.GetTestProjectAssembly("JsonNet10"));
|
||||
var json11context = PluginLoader.CreateFromConfigFile(TestResources.GetTestProjectAssembly("JsonNet11"));
|
||||
var json9context = PluginLoader.CreateFromAssemblyFile(TestResources.GetTestProjectAssembly("JsonNet9"));
|
||||
var json10context = PluginLoader.CreateFromAssemblyFile(TestResources.GetTestProjectAssembly("JsonNet10"));
|
||||
var json11context = PluginLoader.CreateFromAssemblyFile(TestResources.GetTestProjectAssembly("JsonNet11"));
|
||||
|
||||
// Load newest first to prove we can load older assemblies later into the same process
|
||||
var json11 = GetJson(json11context);
|
||||
|
||||
@ -17,7 +17,7 @@ namespace McMaster.NETCore.Plugins.Tests
|
||||
var loaders = new List<PluginLoader>();
|
||||
foreach (var name in pluginsNames)
|
||||
{
|
||||
var loader = PluginLoader.CreateFromConfigFile(
|
||||
var loader = PluginLoader.CreateFromAssemblyFile(
|
||||
TestResources.GetTestProjectAssembly(name),
|
||||
sharedTypes: new[] { typeof(IFruit) });
|
||||
loaders.Add(loader);
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
<ItemGroup>
|
||||
<AssemblyAttribute Include="McMaster.NETCore.Plugins.Tests.TestProjectReferenceAttribute">
|
||||
<_Parameter1>%(_ResolvedTestProjectReference.FileName)</_Parameter1>
|
||||
<_Parameter2>%(_ResolvedTestProjectReference.RootDir)%(_ResolvedTestProjectReference.Directory)plugin.config</_Parameter2>
|
||||
<_Parameter2>%(_ResolvedTestProjectReference.RootDir)%(_ResolvedTestProjectReference.Directory)%(_ResolvedTestProjectReference.FileName).dll</_Parameter2>
|
||||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
<ItemGroup>
|
||||
<AssemblyAttribute Include="McMaster.NETCore.Plugins.Tests.TestProjectReferenceAttribute">
|
||||
<_Parameter1>%(PublishedTestProject.FileName)</_Parameter1>
|
||||
<_Parameter2>$(TargetDir)%(PublishedTestProject.FileName)/plugin.config</_Parameter2>
|
||||
<_Parameter2>$(TargetDir)%(PublishedTestProject.FileName)/%(PublishedTestProject.FileName).dll</_Parameter2>
|
||||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<IsPlugin>true</IsPlugin>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<Project>
|
||||
<Import Project="..\..\Directory.Build.props" />
|
||||
<Import Project="..\..\src\Plugins.Sdk\sdk\Sdk.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
<Project>
|
||||
<Import Project="..\..\src\Plugins.Sdk\sdk\Sdk.targets" />
|
||||
<Import Project="..\..\Directory.Build.targets" />
|
||||
</Project>
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<IsPlugin>true</IsPlugin>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<IsPlugin>true</IsPlugin>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<IsPlugin>true</IsPlugin>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<IsPlugin>true</IsPlugin>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<IsPlugin>true</IsPlugin>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<IsPlugin>true</IsPlugin>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<IsPlugin>true</IsPlugin>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<IsPlugin>true</IsPlugin>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user