From 12f1053ad9785a83d9b495995e4e1d99c9339d4d Mon Sep 17 00:00:00 2001 From: asherw83 Date: Thu, 27 Aug 2020 21:23:20 +1000 Subject: [PATCH 1/4] Part 2 in the move to IOCing the world. --- .../Extensions/AssemblyExtensions.cs | 15 ++ .../DaemonRunner/NetDaemonExtensions.cs | 28 +++- .../Service/App/DaemonAppCompiler.cs | 43 +++++ .../Service/App/DaemonCompiler.cs | 158 ++++++++---------- .../Service/App/IDaemonAppCompiler.cs | 17 ++ .../Service/App/LocalDaemonAppCompiler.cs | 37 ++++ .../DaemonRunner/Service/RunnerService.cs | 43 ++--- .../DaemonRunner/App/DaemonAppTests.cs | 2 +- 8 files changed, 219 insertions(+), 124 deletions(-) create mode 100644 src/DaemonRunner/DaemonRunner/Infrastructure/Extensions/AssemblyExtensions.cs create mode 100644 src/DaemonRunner/DaemonRunner/Service/App/DaemonAppCompiler.cs create mode 100644 src/DaemonRunner/DaemonRunner/Service/App/IDaemonAppCompiler.cs create mode 100644 src/DaemonRunner/DaemonRunner/Service/App/LocalDaemonAppCompiler.cs diff --git a/src/DaemonRunner/DaemonRunner/Infrastructure/Extensions/AssemblyExtensions.cs b/src/DaemonRunner/DaemonRunner/Infrastructure/Extensions/AssemblyExtensions.cs new file mode 100644 index 000000000..80c2a2fdc --- /dev/null +++ b/src/DaemonRunner/DaemonRunner/Infrastructure/Extensions/AssemblyExtensions.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace NetDaemon.Infrastructure.Extensions +{ + public static class AssemblyExtensions + { + public static IEnumerable GetTypesWhereSubclassOf(this Assembly assembly) + { + return assembly.GetTypes().Where(x => x.IsClass && x.IsSubclassOf(typeof(T))); + } + } +} \ No newline at end of file diff --git a/src/DaemonRunner/DaemonRunner/NetDaemonExtensions.cs b/src/DaemonRunner/DaemonRunner/NetDaemonExtensions.cs index de800a579..957c3f995 100644 --- a/src/DaemonRunner/DaemonRunner/NetDaemonExtensions.cs +++ b/src/DaemonRunner/DaemonRunner/NetDaemonExtensions.cs @@ -1,9 +1,11 @@ -using Microsoft.AspNetCore.Hosting; +using System; +using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using NetDaemon.Common.Configuration; using NetDaemon.Daemon.Config; using NetDaemon.Service; +using NetDaemon.Service.App; namespace NetDaemon { @@ -17,6 +19,9 @@ public static IHostBuilder UseNetDaemon(this IHostBuilder hostBuilder) services.Configure(context.Configuration.GetSection("HomeAssistant")); services.Configure(context.Configuration.GetSection("NetDaemon")); services.AddSingleton(); + + RegisterNetDaemonAssembly(services); + }) .ConfigureWebHostDefaults(webbuilder => { @@ -24,5 +29,26 @@ public static IHostBuilder UseNetDaemon(this IHostBuilder hostBuilder) webbuilder.UseStartup(); }); } + + private static void RegisterNetDaemonAssembly(IServiceCollection services) + { + if (BypassLocalAssemblyLoading()) + services.AddSingleton(); + else + services.AddSingleton(); + } + + private static bool BypassLocalAssemblyLoading() + { + var value = Environment.GetEnvironmentVariable("HASS_DISABLE_LOCAL_ASM"); + + if (string.IsNullOrWhiteSpace(value)) + return false; + + if (bool.TryParse(value, out var boolResult)) + return boolResult; + + return false; + } } } \ No newline at end of file diff --git a/src/DaemonRunner/DaemonRunner/Service/App/DaemonAppCompiler.cs b/src/DaemonRunner/DaemonRunner/Service/App/DaemonAppCompiler.cs new file mode 100644 index 000000000..da291b3fe --- /dev/null +++ b/src/DaemonRunner/DaemonRunner/Service/App/DaemonAppCompiler.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using NetDaemon.Common; +using NetDaemon.Common.Configuration; +using NetDaemon.Infrastructure.Extensions; + +namespace NetDaemon.Service.App +{ + public class DaemonAppCompiler : IDaemonAppCompiler + { + private readonly ILogger _logger; + private readonly IOptions _netDaemonSettings; + + public DaemonAppCompiler(ILogger logger, IOptions netDaemonSettings) + { + _logger = logger; + _netDaemonSettings = netDaemonSettings; + } + + public IEnumerable GetApps() + { + var assembly = Load(); + var apps = assembly.GetTypesWhereSubclassOf(); + + if (!apps.Any()) + _logger.LogWarning("No .cs files found, please add files to {sourceFolder}/apps", _netDaemonSettings.Value.SourceFolder); + + return apps; + } + + public Assembly Load() + { + CollectibleAssemblyLoadContext alc; + var appFolder = Path.Combine(_netDaemonSettings.Value.SourceFolder!, "apps"); + return DaemonCompiler.GetCompiledAppAssembly(out alc, appFolder!, _logger); + } + } +} \ No newline at end of file diff --git a/src/DaemonRunner/DaemonRunner/Service/App/DaemonCompiler.cs b/src/DaemonRunner/DaemonRunner/Service/App/DaemonCompiler.cs index 5c83338f9..6768614ee 100644 --- a/src/DaemonRunner/DaemonRunner/Service/App/DaemonCompiler.cs +++ b/src/DaemonRunner/DaemonRunner/Service/App/DaemonCompiler.cs @@ -15,13 +15,13 @@ using NetDaemon.Common; using NetDaemon.Common.Reactive; using NetDaemon.Daemon; +using NetDaemon.Infrastructure.Extensions; [assembly: InternalsVisibleTo("NetDaemon.Daemon.Tests")] namespace NetDaemon.Service.App { - - internal class CollectibleAssemblyLoadContext : AssemblyLoadContext + public class CollectibleAssemblyLoadContext : AssemblyLoadContext { public CollectibleAssemblyLoadContext() : base(isCollectible: true) { @@ -39,21 +39,6 @@ public static (IEnumerable, CollectibleAssemblyLoadContext?) GetDaemonApps { var loadedApps = new List(50); - // Load the internal apps (mainly for ) - var disableLoadLocalAssemblies = Environment.GetEnvironmentVariable("HASS_DISABLE_LOCAL_ASM"); - if (!(disableLoadLocalAssemblies is object && disableLoadLocalAssemblies == "true")) - { - var localApps = LoadLocalAssemblyApplicationsForDevelopment(); - if (localApps is object) - loadedApps.AddRange(localApps); - } - if (loadedApps.Count() > 0) - { - // We do not want to get and compile the apps if it is includer - // this is typically when in dev environment - logger.LogInformation("Loading compiled built-in apps"); - return (loadedApps, null); - } CollectibleAssemblyLoadContext alc; // Load the compiled apps var (compiledApps, compileErrorText) = GetCompiledApps(out alc, codeFolder, logger); @@ -68,29 +53,74 @@ public static (IEnumerable, CollectibleAssemblyLoadContext?) GetDaemonApps return (loadedApps, alc); } - private static IEnumerable? LoadLocalAssemblyApplicationsForDevelopment() + public static (IEnumerable?, string) GetCompiledApps(out CollectibleAssemblyLoadContext alc, string codeFolder, ILogger logger) { - // Get daemon apps in entry assembly (mainly for development) - return Assembly.GetEntryAssembly()?.GetTypes() - .Where(type => type.IsClass && type.IsSubclassOf(typeof(NetDaemonAppBase))); + var assembly = GetCompiledAppAssembly(out alc, codeFolder, logger); + + if (assembly == null) + return (null, "Compile error"); + + return (assembly.GetTypesWhereSubclassOf(), string.Empty); + } + + public static Assembly GetCompiledAppAssembly(out CollectibleAssemblyLoadContext alc, string codeFolder, ILogger logger) + { + alc = new CollectibleAssemblyLoadContext(); + + try + { + var compilation = GetCsCompilation(codeFolder); + + foreach (var syntaxTree in compilation.SyntaxTrees) + { + if (Path.GetFileName(syntaxTree.FilePath) != "_EntityExtensions.cs") + WarnIfExecuteIsMissing(syntaxTree, compilation, logger); + + InterceptAppInfo(syntaxTree, compilation); + } + + using (var peStream = new MemoryStream()) + { + var emitResult = compilation.Emit(peStream: peStream); + + if (emitResult.Success) + { + peStream.Seek(0, SeekOrigin.Begin); + + return alc!.LoadFromStream(peStream); + } + else + { + PrettyPrintCompileError(emitResult, logger); + + return null!; + } + } + } + finally + { + alc.Unload(); + // Finally do cleanup and release memory + GC.Collect(); + GC.WaitForPendingFinalizers(); + } } private static List LoadSyntaxTree(string codeFolder) { var result = new List(50); - // Get the paths for all .cs files recurcivlely in app folder + // Get the paths for all .cs files recursively in app folder var csFiles = Directory.EnumerateFiles(codeFolder, "*.cs", SearchOption.AllDirectories); - - var embeddedTexts = new List(); + //var embeddedTexts = new List(); foreach (var csFile in csFiles) { using (var fs = new FileStream(csFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { var sourceText = SourceText.From(fs, encoding: Encoding.UTF8, canBeEmbedded: true); - embeddedTexts.Add(EmbeddedText.FromSource(csFile, sourceText)); + //embeddedTexts.Add(EmbeddedText.FromSource(csFile, sourceText)); var syntaxTree = SyntaxFactory.ParseSyntaxTree(sourceText, path: csFile); result.Add(syntaxTree); @@ -140,70 +170,19 @@ private static CSharpCompilation GetCsCompilation(string codeFolder) var metaDataReference = GetDefaultReferences(); - return CSharpCompilation.Create($"net_{Path.GetRandomFileName()}.dll", + return CSharpCompilation.Create( + $"net_{Path.GetRandomFileName()}.dll", syntaxTrees.ToArray(), references: metaDataReference.ToArray(), - options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, + options: new CSharpCompilationOptions( + OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release, - assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default)); + assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default + ) + ); } - public static (IEnumerable?, string) GetCompiledApps(out CollectibleAssemblyLoadContext alc, string codeFolder, ILogger logger) - { - - alc = new CollectibleAssemblyLoadContext(); - - - try - { - var compilation = GetCsCompilation(codeFolder); - - foreach (var syntaxTree in compilation.SyntaxTrees) - { - if (Path.GetFileName(syntaxTree.FilePath) != "_EntityExtensions.cs") - WarnIfExecuteIsMissing(syntaxTree, compilation, logger); - - InterceptAppInfo(syntaxTree, compilation, logger); - } - - // var emitOptions = new EmitOptions( - // debugInformationFormat: DebugInformationFormat.PortablePdb, - // pdbFilePath: "netdaemondynamic.pdb"); - - using (var peStream = new MemoryStream()) - // using (var symbolsStream = new MemoryStream()) - { - var emitResult = compilation.Emit( - peStream: peStream - // pdbStream: symbolsStream, - // embeddedTexts: embeddedTexts, - /*options: emitOptions*/); - - if (emitResult.Success) - { - peStream.Seek(0, SeekOrigin.Begin); - - var asm = alc!.LoadFromStream(peStream); - return (asm.GetTypes() // Get all types - .Where(type => type.IsClass && type.IsSubclassOf(typeof(NetDaemonAppBase))) // That is a app - , ""); // And return a list apps - } - else - { - return (null, PrettyPrintCompileError(emitResult)); - } - } - } - finally - { - alc.Unload(); - // Finally do cleanup and release memory - GC.Collect(); - GC.WaitForPendingFinalizers(); - } - } - - private static string PrettyPrintCompileError(EmitResult emitResult) + private static void PrettyPrintCompileError(EmitResult emitResult, ILogger logger) { var msg = new StringBuilder(); msg.AppendLine($"Compiler error!"); @@ -215,13 +194,13 @@ private static string PrettyPrintCompileError(EmitResult emitResult) msg.AppendLine(emitResultDiagnostic.ToString()); } } - return msg.ToString(); + logger.LogError(msg.ToString()); } /// /// All NetDaemonApp methods that needs to be closed with Execute or ExecuteAsync /// - private static string[] _executeWarningOnInvocationNames = new string[] + private static readonly string[] ExecuteWarningOnInvocationNames = new string[] { "Entity", "Entities", @@ -236,7 +215,7 @@ private static string PrettyPrintCompileError(EmitResult emitResult) "RunScript" }; - private static void InterceptAppInfo(SyntaxTree syntaxTree, CSharpCompilation compilation, ILogger logger) + private static void InterceptAppInfo(SyntaxTree syntaxTree, CSharpCompilation compilation) { var semModel = compilation.GetSemanticModel(syntaxTree); @@ -285,7 +264,6 @@ private static void InterceptAppInfo(SyntaxTree syntaxTree, CSharpCompilation co } } - var linesReported = new List(); } /// /// Warn user if fluent command chain not ending with Execute or ExecuteAsync @@ -307,7 +285,7 @@ private static void WarnIfExecuteIsMissing(SyntaxTree syntaxTree, CSharpCompilat continue; if (string.IsNullOrEmpty(symbol?.Name) || - _executeWarningOnInvocationNames.Contains(symbol?.Name) == false) + ExecuteWarningOnInvocationNames.Contains(symbol?.Name) == false) // The invocation name is empty or not in list of invocations // that needs to be closed with Execute or ExecuteAsync continue; @@ -317,8 +295,6 @@ private static void WarnIfExecuteIsMissing(SyntaxTree syntaxTree, CSharpCompilat if (symbol is object && symbol.ContainingType.Name == "NetDaemonApp") { - var comment = symbol.GetDocumentationCommentXml(); - System.Console.WriteLine("HELLO COMMENT: " + comment); var disableLogging = false; var symbolName = symbol.Name; diff --git a/src/DaemonRunner/DaemonRunner/Service/App/IDaemonAppCompiler.cs b/src/DaemonRunner/DaemonRunner/Service/App/IDaemonAppCompiler.cs new file mode 100644 index 000000000..a5728e449 --- /dev/null +++ b/src/DaemonRunner/DaemonRunner/Service/App/IDaemonAppCompiler.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace NetDaemon.Service.App +{ + public interface IDaemonAppCompiler + { + /// + /// Temporary + /// + /// + [Obsolete("Only exists while migrating the world to IOC.")] + IEnumerable GetApps(); + Assembly Load(); + } +} \ No newline at end of file diff --git a/src/DaemonRunner/DaemonRunner/Service/App/LocalDaemonAppCompiler.cs b/src/DaemonRunner/DaemonRunner/Service/App/LocalDaemonAppCompiler.cs new file mode 100644 index 000000000..c782a6fa4 --- /dev/null +++ b/src/DaemonRunner/DaemonRunner/Service/App/LocalDaemonAppCompiler.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Microsoft.Extensions.Logging; +using NetDaemon.Common; +using NetDaemon.Infrastructure.Extensions; + +namespace NetDaemon.Service.App +{ + public class LocalDaemonAppCompiler : IDaemonAppCompiler + { + private readonly ILogger _logger; + + public LocalDaemonAppCompiler(ILogger logger) + { + _logger = logger; + } + + public IEnumerable GetApps() + { + var assembly = Load(); + + var apps = assembly.GetTypesWhereSubclassOf(); + + if (!apps.Any()) + _logger.LogWarning("No local daemon apps found."); + + return apps; + } + + public Assembly Load() + { + return Assembly.GetEntryAssembly()!; + } + } +} \ No newline at end of file diff --git a/src/DaemonRunner/DaemonRunner/Service/RunnerService.cs b/src/DaemonRunner/DaemonRunner/Service/RunnerService.cs index 66ec4ec06..44bf4ecae 100644 --- a/src/DaemonRunner/DaemonRunner/Service/RunnerService.cs +++ b/src/DaemonRunner/DaemonRunner/Service/RunnerService.cs @@ -30,15 +30,19 @@ public class RunnerService : BackgroundService private readonly IServiceProvider _serviceProvider; private readonly IYamlConfig _yamlConfig; + private readonly IDaemonAppCompiler _daemonAppCompiler; private bool _entitiesGenerated; + private IEnumerable? _loadedDaemonApps; + public RunnerService( ILoggerFactory loggerFactory, IOptions netDaemonSettings, IOptions homeAssistantSettings, IServiceProvider serviceProvider, - IYamlConfig yamlConfig + IYamlConfig yamlConfig, + IDaemonAppCompiler daemonAppCompiler ) { _logger = loggerFactory.CreateLogger(); @@ -46,13 +50,13 @@ IYamlConfig yamlConfig _netDaemonSettings = netDaemonSettings.Value; _serviceProvider = serviceProvider; _yamlConfig = yamlConfig; + _daemonAppCompiler = daemonAppCompiler; } public override async Task StopAsync(CancellationToken cancellationToken) { _logger.LogInformation("Stopping NetDaemon..."); await base.StopAsync(cancellationToken).ConfigureAwait(false); - } protected override async Task ExecuteAsync(CancellationToken stoppingToken) @@ -67,7 +71,6 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) EnsureApplicationDirectoryExists(_netDaemonSettings); - var storageFolder = Path.Combine(_netDaemonSettings.SourceFolder!, ".storage"); var sourceFolder = Path.Combine(_netDaemonSettings.SourceFolder!, "apps"); // Automatically create source directories @@ -76,11 +79,9 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) var hasConnectedBefore = false; - CollectibleAssemblyLoadContext? alc = null; - IEnumerable? loadedDaemonApps = null; + _loadedDaemonApps = null; - await using var daemonHost = - _serviceProvider.GetService(); + await using var daemonHost = _serviceProvider.GetService(); while (!stoppingToken.IsCancellationRequested) { @@ -94,8 +95,6 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) _logger.LogInformation($"Restarting NeDaemon (version {Version})..."); } - - var daemonHostTask = daemonHost.Run( _homeAssistantSettings.Host, _homeAssistantSettings.Port, @@ -118,18 +117,13 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) // Generate code if requested await GenerateEntities(daemonHost, sourceFolder); - if (loadedDaemonApps is null) - { - (loadedDaemonApps, alc) = DaemonCompiler.GetDaemonApps(sourceFolder!, _logger); - } + if (_loadedDaemonApps is null) + _loadedDaemonApps = _daemonAppCompiler.GetApps(); - if (loadedDaemonApps is null || !loadedDaemonApps.Any()) - { - _logger.LogWarning("No .cs files files found, please add files to {sourceFolder}/apps", sourceFolder); + if (_loadedDaemonApps is null || !_loadedDaemonApps.Any()) return; - } - IInstanceDaemonApp? codeManager = new CodeManager(loadedDaemonApps, _logger, _yamlConfig); + IInstanceDaemonApp? codeManager = new CodeManager(_loadedDaemonApps, _logger, _yamlConfig); await daemonHost.Initialize(codeManager).ConfigureAwait(false); // Wait until daemon stops @@ -175,19 +169,6 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) hasConnectedBefore = true; } - if (alc is object) - { - loadedDaemonApps = null; - var alcWeakRef = new WeakReference(alc, trackResurrection: true); - alc.Unload(); - alc = null; - - for (int i = 0; alcWeakRef.IsAlive && (i < 100); i++) - { - GC.Collect(); - GC.WaitForPendingFinalizers(); - } - } } catch (OperationCanceledException) { diff --git a/tests/NetDaemon.Daemon.Tests/DaemonRunner/App/DaemonAppTests.cs b/tests/NetDaemon.Daemon.Tests/DaemonRunner/App/DaemonAppTests.cs index 52d5c2dd0..f934d9eff 100644 --- a/tests/NetDaemon.Daemon.Tests/DaemonRunner/App/DaemonAppTests.cs +++ b/tests/NetDaemon.Daemon.Tests/DaemonRunner/App/DaemonAppTests.cs @@ -46,7 +46,7 @@ public void FaultyApplicationShouldLogError() var cm = new CodeManager(daemonApps, loggerMock.Logger, new YamlConfig(netDaemonSettings)); // ASSERT - loggerMock.AssertLogged(LogLevel.Error, Times.Once()); + loggerMock.AssertLogged(LogLevel.Error, Times.AtLeastOnce()); } [Fact] From 974d808266961b99404598df0ad7c44b2fa86efd Mon Sep 17 00:00:00 2001 From: asherw83 Date: Sat, 29 Aug 2020 20:37:15 +1000 Subject: [PATCH 2/4] Remove unused code and reduce if statement nesting. --- .../Service/App/DaemonCompiler.cs | 65 +++++++++---------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/src/DaemonRunner/DaemonRunner/Service/App/DaemonCompiler.cs b/src/DaemonRunner/DaemonRunner/Service/App/DaemonCompiler.cs index 6768614ee..0eb37a2e6 100644 --- a/src/DaemonRunner/DaemonRunner/Service/App/DaemonCompiler.cs +++ b/src/DaemonRunner/DaemonRunner/Service/App/DaemonCompiler.cs @@ -113,19 +113,16 @@ private static List LoadSyntaxTree(string codeFolder) // Get the paths for all .cs files recursively in app folder var csFiles = Directory.EnumerateFiles(codeFolder, "*.cs", SearchOption.AllDirectories); - //var embeddedTexts = new List(); - foreach (var csFile in csFiles) { using (var fs = new FileStream(csFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { var sourceText = SourceText.From(fs, encoding: Encoding.UTF8, canBeEmbedded: true); - //embeddedTexts.Add(EmbeddedText.FromSource(csFile, sourceText)); - var syntaxTree = SyntaxFactory.ParseSyntaxTree(sourceText, path: csFile); result.Add(syntaxTree); } } + return result; } @@ -223,46 +220,47 @@ private static void InterceptAppInfo(SyntaxTree syntaxTree, CSharpCompilation co foreach (var classDeclaration in classDeclarationExpressions) { - //(IMethodSymbol?) var symbol = semModel?.GetDeclaredSymbol(classDeclaration); + if (symbol is null) continue; - if (symbol.BaseType?.Name == "NetDaemonApp" || symbol.BaseType?.Name == "NetDaemonRxApp" || symbol.BaseType?.Name == "GeneratedAppBase") + if (symbol.BaseType?.Name != "NetDaemonApp" && symbol.BaseType?.Name != "NetDaemonRxApp" && symbol.BaseType?.Name != "GeneratedAppBase") + continue; + + if (!classDeclaration.HasStructuredTrivia) + continue; + + foreach (var kind in classDeclaration.GetLeadingTrivia()) { - if (classDeclaration.HasStructuredTrivia) + switch (kind.Kind()) { - foreach (var kind in classDeclaration.GetLeadingTrivia()) - { - switch (kind.Kind()) + case SyntaxKind.SingleLineDocumentationCommentTrivia: + var doc = kind.ToString(); + doc = System.Text.RegularExpressions.Regex.Replace(doc, @"(?i)s*<\s*(summary)\s*>", string.Empty); + doc = System.Text.RegularExpressions.Regex.Replace(doc, @"(?i)s*<\s*(\/summary)\s*>", string.Empty); + doc = System.Text.RegularExpressions.Regex.Replace(doc, @"(?i)\/\/\/", ""); + string comment = string.Empty; + + foreach (var row in doc.Split('\n')) { - case SyntaxKind.SingleLineDocumentationCommentTrivia: - var doc = kind.ToString(); - doc = System.Text.RegularExpressions.Regex.Replace(doc, @"(?i)s*<\s*(summary)\s*>", ""); - doc = System.Text.RegularExpressions.Regex.Replace(doc, @"(?i)s*<\s*(\/summary)\s*>", ""); - doc = System.Text.RegularExpressions.Regex.Replace(doc, @"(?i)\/\/\/", ""); - string comment = ""; - foreach (var row in doc.Split('\n')) - { - var commentRow = row.Trim(); - if (commentRow.Length > 0) - comment += commentRow + "\n"; - } - var app_key = symbol.ContainingNamespace.Name == "" ? symbol.Name : symbol.ContainingNamespace.Name + "." + symbol.Name; - - if (!NetDaemonAppBase.CompileTimeProperties.ContainsKey(app_key)) - { - NetDaemonAppBase.CompileTimeProperties[app_key] = new Dictionary(); - } - NetDaemonAppBase.CompileTimeProperties[app_key]["description"] = comment; - - break; + var commentRow = row.Trim(); + if (commentRow.Length > 0) + comment += commentRow + "\n"; } - } + var app_key = symbol.ContainingNamespace.Name == "" ? symbol.Name : symbol.ContainingNamespace.Name + "." + symbol.Name; + + if (!NetDaemonAppBase.CompileTimeProperties.ContainsKey(app_key)) + { + NetDaemonAppBase.CompileTimeProperties[app_key] = new Dictionary(); + } + + NetDaemonAppBase.CompileTimeProperties[app_key]["description"] = comment; + + break; } } - } } /// @@ -279,7 +277,6 @@ private static void WarnIfExecuteIsMissing(SyntaxTree syntaxTree, CSharpCompilat foreach (var invocationExpression in invocationExpressions) { - var symbol = (IMethodSymbol?)semModel?.GetSymbolInfo(invocationExpression).Symbol; if (symbol is null) continue; From cb1af585dea78a1e297b9b340513b3e860b1e8da Mon Sep 17 00:00:00 2001 From: Lesley Vente Date: Sun, 13 Sep 2020 14:03:21 +0200 Subject: [PATCH 3/4] fix: ensure types are instantiable --- .../Infrastructure/Extensions/AssemblyExtensions.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/DaemonRunner/DaemonRunner/Infrastructure/Extensions/AssemblyExtensions.cs b/src/DaemonRunner/DaemonRunner/Infrastructure/Extensions/AssemblyExtensions.cs index 80c2a2fdc..388b0552b 100644 --- a/src/DaemonRunner/DaemonRunner/Infrastructure/Extensions/AssemblyExtensions.cs +++ b/src/DaemonRunner/DaemonRunner/Infrastructure/Extensions/AssemblyExtensions.cs @@ -5,11 +5,15 @@ namespace NetDaemon.Infrastructure.Extensions { - public static class AssemblyExtensions + internal static class AssemblyExtensions { public static IEnumerable GetTypesWhereSubclassOf(this Assembly assembly) { - return assembly.GetTypes().Where(x => x.IsClass && x.IsSubclassOf(typeof(T))); + return assembly.GetTypes() + .Where(type => type.IsClass) + .Where(type => !type.IsGenericType) + .Where(type => !type.IsAbstract) + .Where(type => type.IsSubclassOf(typeof(T))); } } } \ No newline at end of file From f009e3ff44d1f8acb253cf4212e0ff250fd3a6c4 Mon Sep 17 00:00:00 2001 From: Lesley Vente Date: Sun, 13 Sep 2020 14:03:43 +0200 Subject: [PATCH 4/4] refactor: use bool parse as return value --- src/DaemonRunner/DaemonRunner/NetDaemonExtensions.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/DaemonRunner/DaemonRunner/NetDaemonExtensions.cs b/src/DaemonRunner/DaemonRunner/NetDaemonExtensions.cs index 957c3f995..5073914cc 100644 --- a/src/DaemonRunner/DaemonRunner/NetDaemonExtensions.cs +++ b/src/DaemonRunner/DaemonRunner/NetDaemonExtensions.cs @@ -41,14 +41,7 @@ private static void RegisterNetDaemonAssembly(IServiceCollection services) private static bool BypassLocalAssemblyLoading() { var value = Environment.GetEnvironmentVariable("HASS_DISABLE_LOCAL_ASM"); - - if (string.IsNullOrWhiteSpace(value)) - return false; - - if (bool.TryParse(value, out var boolResult)) - return boolResult; - - return false; + return bool.TryParse(value, out var result) && result; } } } \ No newline at end of file