From d9d86476d8fcf06f9e685cf1e2d1536848a0e76f Mon Sep 17 00:00:00 2001 From: freddyrios Date: Tue, 5 Oct 2021 13:04:00 +0200 Subject: [PATCH 1/3] breaking change: plugins run from the "plugins" subfolder reason: this allows to run without publishing the app as a single file, which at the time of writing allows to workaround https://github.com/dotnet/runtime/issues/59961 --- CA_DataUploaderLib/PluginsLoader.cs | 55 ++++++++++++++--------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/CA_DataUploaderLib/PluginsLoader.cs b/CA_DataUploaderLib/PluginsLoader.cs index 7d8e617a..da05e04d 100644 --- a/CA_DataUploaderLib/PluginsLoader.cs +++ b/CA_DataUploaderLib/PluginsLoader.cs @@ -30,13 +30,14 @@ public PluginsLoader(CommandHandler handler, Func<(string pluginName, string tar public void LoadPlugins(bool automaticallyLoadPluginChanges = true) { + Directory.CreateDirectory("plugins"); // load all - foreach (var assembly in Directory.GetFiles(".", "*.dll")) - LoadPlugin(assembly); + foreach (var assemblyFullPath in Directory.GetFiles("plugins", "*.dll")) + LoadPlugin(assemblyFullPath); if (automaticallyLoadPluginChanges) { - _pluginChangesWatcher = new SingleFireFileWatcher(".", "*.dll"); + _pluginChangesWatcher = new SingleFireFileWatcher("plugins", "*.dll"); _pluginChangesWatcher.Deleted += UnloadPlugin; _pluginChangesWatcher.Changed += ReloadPlugin; } @@ -49,10 +50,9 @@ public void UnloadPlugins() GC.Collect(); // triggers the unload of the assembly (after DoUnloadExtension we no longer have references to the instances) } - void LoadPlugin(string assemblyPath) + void LoadPlugin(string assemblyFullPath) { - assemblyPath = Path.GetFullPath(assemblyPath); - var (context, plugins) = Load(assemblyPath, plugingArgs); + var (context, plugins) = Load(assemblyFullPath, plugingArgs); var initializedPlugins = plugins.ToList(); if (initializedPlugins.Count == 0) { @@ -63,26 +63,25 @@ void LoadPlugin(string assemblyPath) foreach (var plugin in initializedPlugins) plugin.Initialize(new PluginsCommandHandler(handler), new PluginsLogger(plugin.Name)); - _runningPlugins[assemblyPath] = (context, initializedPlugins); - CALog.LogData(LogID.A, $"loaded plugins from {assemblyPath} - {string.Join(",", initializedPlugins.Select(e => e.GetType().Name))}"); + _runningPlugins[assemblyFullPath] = (context, initializedPlugins); + CALog.LogData(LogID.A, $"loaded plugins from {assemblyFullPath} - {string.Join(",", initializedPlugins.Select(e => e.GetType().Name))}"); } - void UnloadPlugin(string assemblyPath) + void UnloadPlugin(string assemblyFullPath) { - assemblyPath = Path.GetFullPath(assemblyPath); - if (!_runningPlugins.TryGetValue(assemblyPath, out var runningPluginEntry)) - CALog.LogData(LogID.A, $"running plugin not found: {Path.GetFileNameWithoutExtension(assemblyPath)}"); + if (!_runningPlugins.TryGetValue(assemblyFullPath, out var runningPluginEntry)) + CALog.LogData(LogID.A, $"running plugin not found: {Path.GetFileNameWithoutExtension(assemblyFullPath)}"); else - UnloadPlugin(assemblyPath, runningPluginEntry); + UnloadPlugin(assemblyFullPath, runningPluginEntry); } - void UnloadPlugin(string assemblyPath, (AssemblyLoadContext ctx, IEnumerable instances) entry) + void UnloadPlugin(string assemblyFullPath, (AssemblyLoadContext ctx, IEnumerable instances) entry) { foreach (var instance in entry.instances) instance.Dispose(); - _runningPlugins.Remove(assemblyPath); + _runningPlugins.Remove(assemblyFullPath); entry.ctx.Unload(); - CALog.LogData(LogID.A, $"unloaded plugins from {assemblyPath}"); + CALog.LogData(LogID.A, $"unloaded plugins from {assemblyFullPath}"); } /// @@ -94,16 +93,16 @@ void ReloadPlugin(string fullpath) LoadPlugin(fullpath); } - static (AssemblyLoadContext context, IEnumerable plugins) Load(string assemblyPath, params object[] args) + static (AssemblyLoadContext context, IEnumerable plugins) Load(string assemblyFullPath, params object[] args) { - var (context, assembly) = LoadAssembly(assemblyPath); + var (context, assembly) = LoadAssembly(assemblyFullPath); return (context, CreateInstances(assembly, args)); } - static (AssemblyLoadContext context, Assembly assembly) LoadAssembly(string assemblyPath) + static (AssemblyLoadContext context, Assembly assembly) LoadAssembly(string assemblyFullPath) { - PluginLoadContext context = new PluginLoadContext(assemblyPath); - using var fs = new FileStream(assemblyPath, FileMode.Open, FileAccess.Read); // force no file lock + PluginLoadContext context = new PluginLoadContext(assemblyFullPath); + using var fs = new FileStream(assemblyFullPath, FileMode.Open, FileAccess.Read); // force no file lock return (context, context.LoadFromStream(fs)); } @@ -121,8 +120,8 @@ private class PluginLoadContext : AssemblyLoadContext { private readonly AssemblyDependencyResolver _resolver; - public PluginLoadContext(string pluginPath) : base (true) => - _resolver = new AssemblyDependencyResolver(pluginPath); + public PluginLoadContext(string pluginFullPath) : base (true) => + _resolver = new AssemblyDependencyResolver(pluginFullPath); protected override Assembly Load(AssemblyName assemblyName) { @@ -201,7 +200,7 @@ private async Task UpdateAllPlugins() foreach (var pluginName in loader.GetRunningPluginsNames()) { CALog.LogInfoAndConsoleLn(LogID.A, $"downloading plugin: {pluginName}"); - await pluginDownloader((pluginName, ".")); + await pluginDownloader((pluginName, "plugins")); } CALog.LogInfoAndConsoleLn(LogID.A, $"unloading running plugins"); @@ -214,12 +213,12 @@ private async Task UpdateAllPlugins() private async Task UpdatePlugin(string pluginName) { CALog.LogInfoAndConsoleLn(LogID.A, $"downloading plugin: {pluginName}"); - await pluginDownloader((pluginName, ".")); - var assemblyPath = Path.GetFullPath(pluginName + ".dll"); + await pluginDownloader((pluginName, "plugins")); + var assemblyFullPath = Path.Combine("plugins", Path.GetFullPath(pluginName + ".dll")); CALog.LogInfoAndConsoleLn(LogID.A, $"unloading plugin: {pluginName}"); - loader.UnloadPlugin(assemblyPath); + loader.UnloadPlugin(assemblyFullPath); CALog.LogInfoAndConsoleLn(LogID.A, $"loading plugin: {pluginName}"); - loader.LoadPlugin(assemblyPath); + loader.LoadPlugin(assemblyFullPath); CALog.LogInfoAndConsoleLn(LogID.A, $"plugins updated"); } } From 774e87d2664e90bb11953497eeb58ae070beab35 Mon Sep 17 00:00:00 2001 From: freddyrios Date: Tue, 5 Oct 2021 15:01:28 +0200 Subject: [PATCH 2/3] fixed regression preventing all help messages from showing --- CA_DataUploaderLib/CommandHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CA_DataUploaderLib/CommandHandler.cs b/CA_DataUploaderLib/CommandHandler.cs index a48691e4..f4132677 100644 --- a/CA_DataUploaderLib/CommandHandler.cs +++ b/CA_DataUploaderLib/CommandHandler.cs @@ -181,7 +181,7 @@ private List RunCommandFunctions(string cmdString, bool addToCommandHistor isFirstAccepted = false; OnCommandAccepted(cmdString, addToCommandHistory); // track it in the history if at least one execution accepted the command } - else + else if (!accepted) break; // avoid running the command on another subsystem when it was already rejected } catch (ArgumentException ex) From a7421d43c93a23ecb2f8d4d8a799592b381cc7b4 Mon Sep 17 00:00:00 2001 From: freddyrios Date: Tue, 5 Oct 2021 15:36:36 +0200 Subject: [PATCH 3/3] allows specifying the target folder --- CA_DataUploaderLib/PluginsLoader.cs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/CA_DataUploaderLib/PluginsLoader.cs b/CA_DataUploaderLib/PluginsLoader.cs index da05e04d..425aafb6 100644 --- a/CA_DataUploaderLib/PluginsLoader.cs +++ b/CA_DataUploaderLib/PluginsLoader.cs @@ -12,6 +12,7 @@ namespace CA_DataUploaderLib { public class PluginsLoader { // see https://docs.microsoft.com/en-us/dotnet/core/tutorials/creating-app-with-plugin-support + private readonly string targetFolder = "plugins"; readonly Dictionary instances)> _runningPlugins = new Dictionary instances)>(); SingleFireFileWatcher _pluginChangesWatcher; @@ -19,25 +20,26 @@ public class PluginsLoader readonly CommandHandler handler; UpdatePluginsCommand updatePluginsCommand; - public PluginsLoader(CommandHandler handler, Func<(string pluginName, string targetFolder), Task> pluginDownloader = null) + public PluginsLoader(CommandHandler handler, Func<(string pluginName, string targetFolder), Task> pluginDownloader = null, string targetFolder = "plugins") { this.handler = handler; + this.targetFolder = targetFolder; if (pluginDownloader != null) - this.updatePluginsCommand = new UpdatePluginsCommand(handler, pluginDownloader, this); + this.updatePluginsCommand = new UpdatePluginsCommand(handler, pluginDownloader, this, targetFolder); } public IEnumerable GetRunningPluginsNames() => _runningPlugins.Keys.Select(v => Path.GetFileNameWithoutExtension(v)); public void LoadPlugins(bool automaticallyLoadPluginChanges = true) { - Directory.CreateDirectory("plugins"); + Directory.CreateDirectory(targetFolder); // load all - foreach (var assemblyFullPath in Directory.GetFiles("plugins", "*.dll")) + foreach (var assemblyFullPath in Directory.GetFiles(targetFolder, "*.dll")) LoadPlugin(assemblyFullPath); if (automaticallyLoadPluginChanges) { - _pluginChangesWatcher = new SingleFireFileWatcher("plugins", "*.dll"); + _pluginChangesWatcher = new SingleFireFileWatcher(targetFolder, "*.dll"); _pluginChangesWatcher.Deleted += UnloadPlugin; _pluginChangesWatcher.Changed += ReloadPlugin; } @@ -179,6 +181,7 @@ private class UpdatePluginsCommand : LoopControlCommand { private readonly Func<(string pluginName, string targetFolder), Task> pluginDownloader; private readonly PluginsLoader loader; + private readonly string targetFolder; public override string Name => "updateplugins"; @@ -186,11 +189,12 @@ private class UpdatePluginsCommand : LoopControlCommand public override bool IsHiddenCommand => true; - public UpdatePluginsCommand(CommandHandler cmd, Func<(string pluginName, string targetFolder), Task> pluginDownloader, PluginsLoader loader) + public UpdatePluginsCommand(CommandHandler cmd, Func<(string pluginName, string targetFolder), Task> pluginDownloader, PluginsLoader loader, string targetFolder) { Initialize(new PluginsCommandHandler(cmd), new PluginsLogger("PluginsUpdater")); this.pluginDownloader = pluginDownloader; this.loader = loader; + this.targetFolder = targetFolder; } protected override Task Command(List args) => args.Count > 1 ? UpdatePlugin(args[1]) : UpdateAllPlugins(); @@ -200,7 +204,7 @@ private async Task UpdateAllPlugins() foreach (var pluginName in loader.GetRunningPluginsNames()) { CALog.LogInfoAndConsoleLn(LogID.A, $"downloading plugin: {pluginName}"); - await pluginDownloader((pluginName, "plugins")); + await pluginDownloader((pluginName, targetFolder)); } CALog.LogInfoAndConsoleLn(LogID.A, $"unloading running plugins"); @@ -213,8 +217,8 @@ private async Task UpdateAllPlugins() private async Task UpdatePlugin(string pluginName) { CALog.LogInfoAndConsoleLn(LogID.A, $"downloading plugin: {pluginName}"); - await pluginDownloader((pluginName, "plugins")); - var assemblyFullPath = Path.Combine("plugins", Path.GetFullPath(pluginName + ".dll")); + await pluginDownloader((pluginName, targetFolder)); + var assemblyFullPath = Path.Combine(targetFolder, Path.GetFullPath(pluginName + ".dll")); CALog.LogInfoAndConsoleLn(LogID.A, $"unloading plugin: {pluginName}"); loader.UnloadPlugin(assemblyFullPath); CALog.LogInfoAndConsoleLn(LogID.A, $"loading plugin: {pluginName}");