| | | 1 | | // Copyright (c) NexusLabs. All rights reserved. |
| | | 2 | | // Licensed under the MIT License. |
| | | 3 | | |
| | | 4 | | using System.Collections.Generic; |
| | | 5 | | using System.Text; |
| | | 6 | | |
| | | 7 | | namespace NexusLabs.Needlr.Generators.CodeGen; |
| | | 8 | | |
| | | 9 | | /// <summary> |
| | | 10 | | /// Generates the <c>NeedlrSourceGenBootstrap.g.cs</c> module initializer file. |
| | | 11 | | /// </summary> |
| | | 12 | | internal static class BootstrapCodeGenerator |
| | | 13 | | { |
| | | 14 | | /// <summary> |
| | | 15 | | /// Emits the module-initializer bootstrap source that registers TypeRegistry |
| | | 16 | | /// callbacks and force-loads referenced assemblies. |
| | | 17 | | /// </summary> |
| | | 18 | | internal static string GenerateModuleInitializerBootstrapSource(string assemblyName, IReadOnlyList<string> reference |
| | | 19 | | { |
| | 455 | 20 | | var builder = new StringBuilder(); |
| | 455 | 21 | | var safeAssemblyName = GeneratorHelpers.SanitizeIdentifier(assemblyName); |
| | | 22 | | |
| | 455 | 23 | | breadcrumbs.WriteFileHeader(builder, assemblyName, "Needlr Source-Gen Bootstrap"); |
| | 455 | 24 | | builder.AppendLine("#nullable enable"); |
| | 455 | 25 | | builder.AppendLine(); |
| | 455 | 26 | | builder.AppendLine("using System.Runtime.CompilerServices;"); |
| | 455 | 27 | | builder.AppendLine(); |
| | 455 | 28 | | builder.AppendLine("using Microsoft.Extensions.Configuration;"); |
| | 455 | 29 | | builder.AppendLine("using Microsoft.Extensions.DependencyInjection;"); |
| | 455 | 30 | | builder.AppendLine(); |
| | 455 | 31 | | builder.AppendLine($"namespace {safeAssemblyName}.Generated;"); |
| | 455 | 32 | | builder.AppendLine(); |
| | 455 | 33 | | builder.AppendLine("internal static class NeedlrSourceGenModuleInitializer"); |
| | 455 | 34 | | builder.AppendLine("{"); |
| | 455 | 35 | | builder.AppendLine(" [global::System.Runtime.CompilerServices.ModuleInitializer]"); |
| | 455 | 36 | | builder.AppendLine(" internal static void Initialize()"); |
| | 455 | 37 | | builder.AppendLine(" {"); |
| | | 38 | | |
| | | 39 | | // Generate ForceLoadAssemblies call if there are referenced assemblies with [GenerateTypeRegistry] |
| | 455 | 40 | | if (referencedAssemblies.Count > 0) |
| | | 41 | | { |
| | 17 | 42 | | builder.AppendLine(" // Force-load referenced assemblies to ensure their module initializers run"); |
| | 17 | 43 | | builder.AppendLine(" ForceLoadReferencedAssemblies();"); |
| | 17 | 44 | | builder.AppendLine(); |
| | | 45 | | } |
| | | 46 | | |
| | 455 | 47 | | builder.AppendLine(" global::NexusLabs.Needlr.Generators.NeedlrSourceGenBootstrap.Register("); |
| | 455 | 48 | | builder.AppendLine($" global::{safeAssemblyName}.Generated.TypeRegistry.GetInjectableTypes,"); |
| | 455 | 49 | | builder.AppendLine($" global::{safeAssemblyName}.Generated.TypeRegistry.GetPluginTypes,"); |
| | | 50 | | |
| | | 51 | | // Generate the decorator/factory/provider applier lambda |
| | 455 | 52 | | if (hasFactories || hasProviders) |
| | | 53 | | { |
| | 41 | 54 | | builder.AppendLine(" services =>"); |
| | 41 | 55 | | builder.AppendLine(" {"); |
| | 41 | 56 | | builder.AppendLine($" global::{safeAssemblyName}.Generated.TypeRegistry.ApplyDecorators((ISer |
| | 41 | 57 | | if (hasFactories) |
| | | 58 | | { |
| | 24 | 59 | | builder.AppendLine($" global::{safeAssemblyName}.Generated.FactoryRegistrations.RegisterF |
| | | 60 | | } |
| | 41 | 61 | | if (hasProviders) |
| | | 62 | | { |
| | 17 | 63 | | builder.AppendLine($" global::{safeAssemblyName}.Generated.TypeRegistry.RegisterProviders |
| | | 64 | | } |
| | 41 | 65 | | builder.AppendLine(" },"); |
| | | 66 | | } |
| | | 67 | | else |
| | | 68 | | { |
| | 414 | 69 | | builder.AppendLine($" services => global::{safeAssemblyName}.Generated.TypeRegistry.ApplyDecorato |
| | | 70 | | } |
| | | 71 | | |
| | | 72 | | // Generate the options registrar lambda for NeedlrSourceGenBootstrap (for backward compat) |
| | 455 | 73 | | if (hasOptions) |
| | | 74 | | { |
| | 146 | 75 | | builder.AppendLine($" (services, config) => global::{safeAssemblyName}.Generated.TypeRegistry.Reg |
| | | 76 | | } |
| | | 77 | | else |
| | | 78 | | { |
| | 309 | 79 | | builder.AppendLine(" null);"); |
| | | 80 | | } |
| | | 81 | | |
| | | 82 | | // Also register with SourceGenRegistry (for ConfiguredSyringe without Generators.Attributes dependency) |
| | 455 | 83 | | if (hasOptions) |
| | | 84 | | { |
| | 146 | 85 | | builder.AppendLine(); |
| | 146 | 86 | | builder.AppendLine(" // Register options with core SourceGenRegistry for ConfiguredSyringe"); |
| | 146 | 87 | | builder.AppendLine($" global::NexusLabs.Needlr.SourceGenRegistry.RegisterOptionsRegistrar("); |
| | 146 | 88 | | builder.AppendLine($" (services, config) => global::{safeAssemblyName}.Generated.TypeRegistry.Reg |
| | | 89 | | } |
| | | 90 | | |
| | 455 | 91 | | builder.AppendLine(" }"); |
| | | 92 | | |
| | | 93 | | // Generate ForceLoadReferencedAssemblies method if needed |
| | 455 | 94 | | if (referencedAssemblies.Count > 0) |
| | | 95 | | { |
| | 17 | 96 | | builder.AppendLine(); |
| | 17 | 97 | | builder.AppendLine(" /// <summary>"); |
| | 17 | 98 | | builder.AppendLine(" /// Forces referenced assemblies with [GenerateTypeRegistry] to load,"); |
| | 17 | 99 | | builder.AppendLine(" /// ensuring their module initializers execute and register their types."); |
| | 17 | 100 | | builder.AppendLine(" /// </summary>"); |
| | 17 | 101 | | builder.AppendLine(" /// <remarks>"); |
| | 17 | 102 | | builder.AppendLine(" /// Without this, transitive dependencies that are never directly referenced"); |
| | 17 | 103 | | builder.AppendLine(" /// in code would not be loaded by the CLR, and their plugins would not be discovere |
| | 17 | 104 | | builder.AppendLine(" /// </remarks>"); |
| | 17 | 105 | | builder.AppendLine(" [MethodImpl(MethodImplOptions.NoInlining)]"); |
| | 17 | 106 | | builder.AppendLine(" private static void ForceLoadReferencedAssemblies()"); |
| | 17 | 107 | | builder.AppendLine(" {"); |
| | | 108 | | |
| | 70 | 109 | | foreach (var referencedAssembly in referencedAssemblies) |
| | | 110 | | { |
| | 18 | 111 | | var safeRefAssemblyName = GeneratorHelpers.SanitizeIdentifier(referencedAssembly); |
| | 18 | 112 | | builder.AppendLine($" _ = typeof(global::{safeRefAssemblyName}.Generated.TypeRegistry).Assembly;" |
| | | 113 | | } |
| | | 114 | | |
| | 17 | 115 | | builder.AppendLine(" }"); |
| | | 116 | | } |
| | | 117 | | |
| | 455 | 118 | | builder.AppendLine("}"); |
| | | 119 | | |
| | 455 | 120 | | return builder.ToString(); |
| | | 121 | | } |
| | | 122 | | } |