From e5e88bbefba2ef99fe94023114edc3c46cedde62 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Mon, 23 Jul 2018 07:58:38 -0700 Subject: [PATCH] Add sample showing how to use plugins in an ASP.NET Core application --- Directory.Build.props | 1 + README.md | 5 ++ .../Abstractions/Abstractions.csproj | 11 +++ samples/aspnetcore/Abstractions/IPlugin.cs | 12 +++ .../aspnetcore/Abstractions/IPluginLink.cs | 7 ++ .../aspnetcore/MainWebApp/MainWebApp.csproj | 31 ++++++++ .../aspnetcore/MainWebApp/Pages/Index.cshtml | 11 +++ .../MainWebApp/Pages/Index.cshtml.cs | 24 ++++++ samples/aspnetcore/MainWebApp/Program.cs | 24 ++++++ .../MainWebApp/Properties/launchSettings.json | 27 +++++++ samples/aspnetcore/MainWebApp/Startup.cs | 68 +++++++++++++++++ samples/aspnetcore/README.md | 41 ++++++++++ .../WebAppPlugin1/Directory.Build.targets | 9 +++ .../WebAppPlugin1/WebAppPlugin1.csproj | 13 ++++ .../aspnetcore/WebAppPlugin1/WebPlugin1.cs | 30 ++++++++ .../WebAppPlugin2/Directory.Build.targets | 9 +++ .../WebAppPlugin2/WebAppPlugin2.csproj | 13 ++++ .../aspnetcore/WebAppPlugin2/WebPlugin2.cs | 30 ++++++++ samples/aspnetcore/aspnetcore.sln | 76 +++++++++++++++++++ src/Plugins.Sdk/sdk/Sdk.targets | 14 +++- 20 files changed, 455 insertions(+), 1 deletion(-) create mode 100644 samples/aspnetcore/Abstractions/Abstractions.csproj create mode 100644 samples/aspnetcore/Abstractions/IPlugin.cs create mode 100644 samples/aspnetcore/Abstractions/IPluginLink.cs create mode 100644 samples/aspnetcore/MainWebApp/MainWebApp.csproj create mode 100644 samples/aspnetcore/MainWebApp/Pages/Index.cshtml create mode 100644 samples/aspnetcore/MainWebApp/Pages/Index.cshtml.cs create mode 100644 samples/aspnetcore/MainWebApp/Program.cs create mode 100644 samples/aspnetcore/MainWebApp/Properties/launchSettings.json create mode 100644 samples/aspnetcore/MainWebApp/Startup.cs create mode 100644 samples/aspnetcore/README.md create mode 100644 samples/aspnetcore/WebAppPlugin1/Directory.Build.targets create mode 100644 samples/aspnetcore/WebAppPlugin1/WebAppPlugin1.csproj create mode 100644 samples/aspnetcore/WebAppPlugin1/WebPlugin1.cs create mode 100644 samples/aspnetcore/WebAppPlugin2/Directory.Build.targets create mode 100644 samples/aspnetcore/WebAppPlugin2/WebAppPlugin2.csproj create mode 100644 samples/aspnetcore/WebAppPlugin2/WebPlugin2.cs create mode 100644 samples/aspnetcore/aspnetcore.sln diff --git a/Directory.Build.props b/Directory.Build.props index aa7a424..68bae61 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -8,6 +8,7 @@ Copyright © Nate McMaster en-US false + $(MSBuildThisFileDirectory) https://www.apache.org/licenses/LICENSE-2.0 https://github.com/natemcmaster/DotNetCorePlugins https://github.com/natemcmaster/DotNetCorePlugins.git diff --git a/README.md b/README.md index 8df4eb5..1178f43 100644 --- a/README.md +++ b/README.md @@ -7,3 +7,8 @@ simplify the usage of those lower-level APIs. ## Getting started This project requires [.NET Core 2.0](https://aka.ms/dotnet-download) or higher. + + +## Usage + +See example projects in [samples/](./samples/) for more detailed, example usage. diff --git a/samples/aspnetcore/Abstractions/Abstractions.csproj b/samples/aspnetcore/Abstractions/Abstractions.csproj new file mode 100644 index 0000000..ea8d303 --- /dev/null +++ b/samples/aspnetcore/Abstractions/Abstractions.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/samples/aspnetcore/Abstractions/IPlugin.cs b/samples/aspnetcore/Abstractions/IPlugin.cs new file mode 100644 index 0000000..fc8d7d7 --- /dev/null +++ b/samples/aspnetcore/Abstractions/IPlugin.cs @@ -0,0 +1,12 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.AspNetCore.Builder; + +namespace Plugin.Abstractions +{ + public interface IWebPlugin + { + void Configure(IApplicationBuilder appBuilder); + void ConfigureServices(IServiceCollection services); + } +} diff --git a/samples/aspnetcore/Abstractions/IPluginLink.cs b/samples/aspnetcore/Abstractions/IPluginLink.cs new file mode 100644 index 0000000..048b490 --- /dev/null +++ b/samples/aspnetcore/Abstractions/IPluginLink.cs @@ -0,0 +1,7 @@ +namespace Plugin.Abstractions +{ + public interface IPluginLink + { + string GetHref(); + } +} diff --git a/samples/aspnetcore/MainWebApp/MainWebApp.csproj b/samples/aspnetcore/MainWebApp/MainWebApp.csproj new file mode 100644 index 0000000..6fd04a0 --- /dev/null +++ b/samples/aspnetcore/MainWebApp/MainWebApp.csproj @@ -0,0 +1,31 @@ + + + + netcoreapp2.1 + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/aspnetcore/MainWebApp/Pages/Index.cshtml b/samples/aspnetcore/MainWebApp/Pages/Index.cshtml new file mode 100644 index 0000000..d0eaa7b --- /dev/null +++ b/samples/aspnetcore/MainWebApp/Pages/Index.cshtml @@ -0,0 +1,11 @@ +@page +@using MainWebApp +@model IndexModel + diff --git a/samples/aspnetcore/MainWebApp/Pages/Index.cshtml.cs b/samples/aspnetcore/MainWebApp/Pages/Index.cshtml.cs new file mode 100644 index 0000000..6122919 --- /dev/null +++ b/samples/aspnetcore/MainWebApp/Pages/Index.cshtml.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Plugin.Abstractions; + +namespace MainWebApp +{ + public class IndexModel : PageModel + { + public IndexModel(IEnumerable pluginLinks) + { + Links = pluginLinks.Select(p => p.GetHref()).ToArray(); + } + + public string[] Links { get; } + + public void OnGet() + { + } + } +} diff --git a/samples/aspnetcore/MainWebApp/Program.cs b/samples/aspnetcore/MainWebApp/Program.cs new file mode 100644 index 0000000..24741d4 --- /dev/null +++ b/samples/aspnetcore/MainWebApp/Program.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +namespace MainWebApp +{ + public class Program + { + public static void Main(string[] args) + { + CreateWebHostBuilder(args).Build().Run(); + } + + public static IWebHostBuilder CreateWebHostBuilder(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseStartup(); + } +} diff --git a/samples/aspnetcore/MainWebApp/Properties/launchSettings.json b/samples/aspnetcore/MainWebApp/Properties/launchSettings.json new file mode 100644 index 0000000..1b32514 --- /dev/null +++ b/samples/aspnetcore/MainWebApp/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:35566", + "sslPort": 44362 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "WebAppWithPlugins": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/samples/aspnetcore/MainWebApp/Startup.cs b/samples/aspnetcore/MainWebApp/Startup.cs new file mode 100644 index 0000000..32191f1 --- /dev/null +++ b/samples/aspnetcore/MainWebApp/Startup.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using System.Threading.Tasks; +using McMaster.Extensions.Plugins; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Plugin.Abstractions; + +namespace MainWebApp +{ + public class Startup + { + private List _plugins = new List(); + + public Startup() + { + foreach (var pluginFile in Directory.GetFiles(AppContext.BaseDirectory, "plugin.config", SearchOption.AllDirectories)) + { + var loader = PluginLoader.CreateFromConfigFile(pluginFile, + // this ensures that the plugin resolves to the same version of DependencyInjection + // and ASP.NET Core that the current app uses + sharedTypes: new[] + { + typeof(IApplicationBuilder), + typeof(IWebPlugin), + typeof(IServiceCollection), + }); + foreach (var type in loader.LoadDefaultAssembly() + .GetTypes() + .Where(t => typeof(IWebPlugin).IsAssignableFrom(t) && !t.IsAbstract)) + { + Console.WriteLine("Found plugin " + type.Name); + var plugin = (IWebPlugin)Activator.CreateInstance(type); + _plugins.Add(plugin); + } + } + } + + public void ConfigureServices(IServiceCollection services) + { + services.AddMvc(); + + foreach (var plugin in _plugins) + { + plugin.ConfigureServices(services); + } + } + + public void Configure(IApplicationBuilder app, IHostingEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + foreach (var plugin in _plugins) + { + plugin.Configure(app); + } + + app.UseMvcWithDefaultRoute(); + } + } +} diff --git a/samples/aspnetcore/README.md b/samples/aspnetcore/README.md new file mode 100644 index 0000000..b9d73e7 --- /dev/null +++ b/samples/aspnetcore/README.md @@ -0,0 +1,41 @@ +ASP.NET Core Sample +=================== + +This sample contains 4 projects which demonstrate a simple plugin scenario. + +1. 'Abstractions' defines common interfaces shared by the web application (host) and plugins +2. 'MainWebApp' is an ASP.NET Core application which scans for a 'plugins' folder in its base directory and attempts to load any plugins it finds +3. 'WebAppPlugin1' references 'Abstractions' and implements `IWebPlugin`. This plugin has a dependency on [AutoMapper](https://www.nuget.org/packages/AutoMapper/) version 6. +4. 'WebAppPlugin2' is the same as plugin1, but it uses AutoMapper version 7. + +Normally, in .NET Core applications you cannot reference two different versions of the same assembly. +However, as this sample demonstrates, using .NET Core plugins you can load and use two different versions. + +* http://localhost:5000/plugin/v1 responds with +``` +This plugin uses AutoMapper, Version=6.2.2.0, Culture=neutral, PublicKeyToken=be96cd2c38ef1005 +``` + +* http://localhost:5000/plugin/v2 responds with +``` +This plugin uses AutoMapper, Version=7.0.1.0, Culture=neutral, PublicKeyToken=be96cd2c38ef1005 +``` + +There are some important types, however, which much share the same identity between the plugins and the host. +To ensure type exchnage works between the host and the plugins, the MainWebApp project uses the `sharedTypes` +parameter on `PluginLoader.CreateFromConfigFile`. + +```csharp + var loader = PluginLoader.CreateFromConfigFile(pluginFile, + sharedTypes: new[] + { + typeof(IApplicationBuilder), + typeof(IWebPlugin), + typeof(IServiceCollection), + }); +``` + +This is important because the plugins in this sample are compiled for ASP.NET Core 2.0 interfaces, +but the MainWebApp uses ASP.NET Core 2.1. If not for this parameter, the plugins would also attempt to use +a private copy of the ASP.NET Core implementations and type exchange between the plugin and the web app +would fail to resolve `IApplicationBuilder` and `IServiceCollection` as the same type. diff --git a/samples/aspnetcore/WebAppPlugin1/Directory.Build.targets b/samples/aspnetcore/WebAppPlugin1/Directory.Build.targets new file mode 100644 index 0000000..23032f7 --- /dev/null +++ b/samples/aspnetcore/WebAppPlugin1/Directory.Build.targets @@ -0,0 +1,9 @@ + + + + + + diff --git a/samples/aspnetcore/WebAppPlugin1/WebAppPlugin1.csproj b/samples/aspnetcore/WebAppPlugin1/WebAppPlugin1.csproj new file mode 100644 index 0000000..89e0482 --- /dev/null +++ b/samples/aspnetcore/WebAppPlugin1/WebAppPlugin1.csproj @@ -0,0 +1,13 @@ + + + + netstandard2.0 + true + + + + + + + + diff --git a/samples/aspnetcore/WebAppPlugin1/WebPlugin1.cs b/samples/aspnetcore/WebAppPlugin1/WebPlugin1.cs new file mode 100644 index 0000000..db3b54f --- /dev/null +++ b/samples/aspnetcore/WebAppPlugin1/WebPlugin1.cs @@ -0,0 +1,30 @@ +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Plugin.Abstractions; + +namespace Plugin1 +{ + internal class WebPlugin1 : IWebPlugin, IPluginLink + { + public string GetHref() => "/plugin/v1"; + + public void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); + } + + public void Configure(IApplicationBuilder appBuilder) + { + appBuilder.Map("/plugin/v1", c => + { + var autoMapperType = typeof(AutoMapper.IMapper).Assembly; + c.Run(async (ctx) => + { + await ctx.Response.WriteAsync("This plugin uses " + autoMapperType.GetName().ToString()); + }); + }); + } + } +} diff --git a/samples/aspnetcore/WebAppPlugin2/Directory.Build.targets b/samples/aspnetcore/WebAppPlugin2/Directory.Build.targets new file mode 100644 index 0000000..23032f7 --- /dev/null +++ b/samples/aspnetcore/WebAppPlugin2/Directory.Build.targets @@ -0,0 +1,9 @@ + + + + + + diff --git a/samples/aspnetcore/WebAppPlugin2/WebAppPlugin2.csproj b/samples/aspnetcore/WebAppPlugin2/WebAppPlugin2.csproj new file mode 100644 index 0000000..c1e6abf --- /dev/null +++ b/samples/aspnetcore/WebAppPlugin2/WebAppPlugin2.csproj @@ -0,0 +1,13 @@ + + + + netstandard2.0 + true + + + + + + + + diff --git a/samples/aspnetcore/WebAppPlugin2/WebPlugin2.cs b/samples/aspnetcore/WebAppPlugin2/WebPlugin2.cs new file mode 100644 index 0000000..2cf6886 --- /dev/null +++ b/samples/aspnetcore/WebAppPlugin2/WebPlugin2.cs @@ -0,0 +1,30 @@ +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Plugin.Abstractions; + +namespace Plugin2 +{ + internal class WebPlugin2 : IWebPlugin, IPluginLink + { + public string GetHref() => "/plugin/v2"; + + public void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); + } + + public void Configure(IApplicationBuilder appBuilder) + { + appBuilder.Map("/plugin/v2", c => + { + var autoMapperType = typeof(AutoMapper.IMapper).Assembly; + c.Run(async (ctx) => + { + await ctx.Response.WriteAsync("This plugin uses " + autoMapperType.GetName().ToString()); + }); + }); + } + } +} diff --git a/samples/aspnetcore/aspnetcore.sln b/samples/aspnetcore/aspnetcore.sln new file mode 100644 index 0000000..4207936 --- /dev/null +++ b/samples/aspnetcore/aspnetcore.sln @@ -0,0 +1,76 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Abstractions", "Abstractions\Abstractions.csproj", "{7CDF7B07-F103-4C22-9BB3-26B7C11716FF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MainWebApp", "MainWebApp\MainWebApp.csproj", "{781A86B1-C278-44CD-997B-1AA26D6BFB38}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebAppPlugin1", "WebAppPlugin1\WebAppPlugin1.csproj", "{B17BE4CB-C382-4C7D-8B11-FD21E0E1A7CB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebAppPlugin2", "WebAppPlugin2\WebAppPlugin2.csproj", "{ECAB37EC-1E82-4B1F-8C51-983E46A557A4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7CDF7B07-F103-4C22-9BB3-26B7C11716FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7CDF7B07-F103-4C22-9BB3-26B7C11716FF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7CDF7B07-F103-4C22-9BB3-26B7C11716FF}.Debug|x64.ActiveCfg = Debug|Any CPU + {7CDF7B07-F103-4C22-9BB3-26B7C11716FF}.Debug|x64.Build.0 = Debug|Any CPU + {7CDF7B07-F103-4C22-9BB3-26B7C11716FF}.Debug|x86.ActiveCfg = Debug|Any CPU + {7CDF7B07-F103-4C22-9BB3-26B7C11716FF}.Debug|x86.Build.0 = Debug|Any CPU + {7CDF7B07-F103-4C22-9BB3-26B7C11716FF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7CDF7B07-F103-4C22-9BB3-26B7C11716FF}.Release|Any CPU.Build.0 = Release|Any CPU + {7CDF7B07-F103-4C22-9BB3-26B7C11716FF}.Release|x64.ActiveCfg = Release|Any CPU + {7CDF7B07-F103-4C22-9BB3-26B7C11716FF}.Release|x64.Build.0 = Release|Any CPU + {7CDF7B07-F103-4C22-9BB3-26B7C11716FF}.Release|x86.ActiveCfg = Release|Any CPU + {7CDF7B07-F103-4C22-9BB3-26B7C11716FF}.Release|x86.Build.0 = Release|Any CPU + {781A86B1-C278-44CD-997B-1AA26D6BFB38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {781A86B1-C278-44CD-997B-1AA26D6BFB38}.Debug|Any CPU.Build.0 = Debug|Any CPU + {781A86B1-C278-44CD-997B-1AA26D6BFB38}.Debug|x64.ActiveCfg = Debug|Any CPU + {781A86B1-C278-44CD-997B-1AA26D6BFB38}.Debug|x64.Build.0 = Debug|Any CPU + {781A86B1-C278-44CD-997B-1AA26D6BFB38}.Debug|x86.ActiveCfg = Debug|Any CPU + {781A86B1-C278-44CD-997B-1AA26D6BFB38}.Debug|x86.Build.0 = Debug|Any CPU + {781A86B1-C278-44CD-997B-1AA26D6BFB38}.Release|Any CPU.ActiveCfg = Release|Any CPU + {781A86B1-C278-44CD-997B-1AA26D6BFB38}.Release|Any CPU.Build.0 = Release|Any CPU + {781A86B1-C278-44CD-997B-1AA26D6BFB38}.Release|x64.ActiveCfg = Release|Any CPU + {781A86B1-C278-44CD-997B-1AA26D6BFB38}.Release|x64.Build.0 = Release|Any CPU + {781A86B1-C278-44CD-997B-1AA26D6BFB38}.Release|x86.ActiveCfg = Release|Any CPU + {781A86B1-C278-44CD-997B-1AA26D6BFB38}.Release|x86.Build.0 = Release|Any CPU + {B17BE4CB-C382-4C7D-8B11-FD21E0E1A7CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B17BE4CB-C382-4C7D-8B11-FD21E0E1A7CB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B17BE4CB-C382-4C7D-8B11-FD21E0E1A7CB}.Debug|x64.ActiveCfg = Debug|Any CPU + {B17BE4CB-C382-4C7D-8B11-FD21E0E1A7CB}.Debug|x64.Build.0 = Debug|Any CPU + {B17BE4CB-C382-4C7D-8B11-FD21E0E1A7CB}.Debug|x86.ActiveCfg = Debug|Any CPU + {B17BE4CB-C382-4C7D-8B11-FD21E0E1A7CB}.Debug|x86.Build.0 = Debug|Any CPU + {B17BE4CB-C382-4C7D-8B11-FD21E0E1A7CB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B17BE4CB-C382-4C7D-8B11-FD21E0E1A7CB}.Release|Any CPU.Build.0 = Release|Any CPU + {B17BE4CB-C382-4C7D-8B11-FD21E0E1A7CB}.Release|x64.ActiveCfg = Release|Any CPU + {B17BE4CB-C382-4C7D-8B11-FD21E0E1A7CB}.Release|x64.Build.0 = Release|Any CPU + {B17BE4CB-C382-4C7D-8B11-FD21E0E1A7CB}.Release|x86.ActiveCfg = Release|Any CPU + {B17BE4CB-C382-4C7D-8B11-FD21E0E1A7CB}.Release|x86.Build.0 = Release|Any CPU + {ECAB37EC-1E82-4B1F-8C51-983E46A557A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ECAB37EC-1E82-4B1F-8C51-983E46A557A4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ECAB37EC-1E82-4B1F-8C51-983E46A557A4}.Debug|x64.ActiveCfg = Debug|Any CPU + {ECAB37EC-1E82-4B1F-8C51-983E46A557A4}.Debug|x64.Build.0 = Debug|Any CPU + {ECAB37EC-1E82-4B1F-8C51-983E46A557A4}.Debug|x86.ActiveCfg = Debug|Any CPU + {ECAB37EC-1E82-4B1F-8C51-983E46A557A4}.Debug|x86.Build.0 = Debug|Any CPU + {ECAB37EC-1E82-4B1F-8C51-983E46A557A4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ECAB37EC-1E82-4B1F-8C51-983E46A557A4}.Release|Any CPU.Build.0 = Release|Any CPU + {ECAB37EC-1E82-4B1F-8C51-983E46A557A4}.Release|x64.ActiveCfg = Release|Any CPU + {ECAB37EC-1E82-4B1F-8C51-983E46A557A4}.Release|x64.Build.0 = Release|Any CPU + {ECAB37EC-1E82-4B1F-8C51-983E46A557A4}.Release|x86.ActiveCfg = Release|Any CPU + {ECAB37EC-1E82-4B1F-8C51-983E46A557A4}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/src/Plugins.Sdk/sdk/Sdk.targets b/src/Plugins.Sdk/sdk/Sdk.targets index 8072598..866f1bd 100644 --- a/src/Plugins.Sdk/sdk/Sdk.targets +++ b/src/Plugins.Sdk/sdk/Sdk.targets @@ -1,9 +1,17 @@ - $(OutputPath)plugin.config + $(TargetDir)plugin.config $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + false + PreserveNewest + PreserveNewest + + + + + + +