| | | 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 | | /// identity and callbacks and runs referenced TypeRegistry module constructors. |
| | | 17 | | /// </summary> |
| | | 18 | | internal static string GenerateModuleInitializerBootstrapSource(string assemblyName, IReadOnlyList<string> reference |
| | | 19 | | { |
| | 560 | 20 | | var builder = new StringBuilder(); |
| | 560 | 21 | | var safeAssemblyName = GeneratorHelpers.SanitizeIdentifier(assemblyName); |
| | | 22 | | |
| | 560 | 23 | | breadcrumbs.WriteFileHeader(builder, assemblyName, "Needlr Source-Gen Bootstrap"); |
| | 560 | 24 | | builder.AppendLine("#nullable enable"); |
| | 560 | 25 | | builder.AppendLine(); |
| | 560 | 26 | | builder.AppendLine("using Microsoft.Extensions.Configuration;"); |
| | 560 | 27 | | builder.AppendLine("using Microsoft.Extensions.DependencyInjection;"); |
| | 560 | 28 | | builder.AppendLine(); |
| | 560 | 29 | | builder.AppendLine($"namespace {safeAssemblyName}.Generated;"); |
| | 560 | 30 | | builder.AppendLine(); |
| | 560 | 31 | | builder.AppendLine("internal static class NeedlrSourceGenModuleInitializer"); |
| | 560 | 32 | | builder.AppendLine("{"); |
| | 560 | 33 | | builder.AppendLine(" [global::System.Runtime.CompilerServices.ModuleInitializer]"); |
| | 560 | 34 | | builder.AppendLine(" internal static void Initialize()"); |
| | 560 | 35 | | builder.AppendLine(" {"); |
| | | 36 | | |
| | | 37 | | // Generate the referenced-module initialization call when dependencies have registries. |
| | 560 | 38 | | if (referencedAssemblies.Count > 0) |
| | | 39 | | { |
| | 18 | 40 | | builder.AppendLine(" // Run referenced module constructors so their registries register"); |
| | 18 | 41 | | builder.AppendLine(" ForceLoadReferencedAssemblies();"); |
| | 18 | 42 | | builder.AppendLine(); |
| | | 43 | | } |
| | | 44 | | |
| | 560 | 45 | | builder.AppendLine(" global::NexusLabs.Needlr.Generators.NeedlrSourceGenBootstrap.Register("); |
| | 560 | 46 | | builder.AppendLine($" typeof(global::{safeAssemblyName}.Generated.TypeRegistry),"); |
| | 560 | 47 | | builder.AppendLine($" global::{safeAssemblyName}.Generated.TypeRegistry.GetInjectableTypes,"); |
| | 560 | 48 | | builder.AppendLine($" global::{safeAssemblyName}.Generated.TypeRegistry.GetPluginTypes,"); |
| | | 49 | | |
| | | 50 | | // Generate the decorator/factory/provider applier lambda |
| | 560 | 51 | | if (hasFactories || hasProviders) |
| | | 52 | | { |
| | 55 | 53 | | builder.AppendLine(" services =>"); |
| | 55 | 54 | | builder.AppendLine(" {"); |
| | 55 | 55 | | builder.AppendLine($" global::{safeAssemblyName}.Generated.TypeRegistry.ApplyDecorators((ISer |
| | 55 | 56 | | if (hasFactories) |
| | | 57 | | { |
| | 38 | 58 | | builder.AppendLine($" global::{safeAssemblyName}.Generated.FactoryRegistrations.RegisterF |
| | | 59 | | } |
| | 55 | 60 | | if (hasProviders) |
| | | 61 | | { |
| | 17 | 62 | | builder.AppendLine($" global::{safeAssemblyName}.Generated.TypeRegistry.RegisterProviders |
| | | 63 | | } |
| | 55 | 64 | | builder.AppendLine(" },"); |
| | | 65 | | } |
| | | 66 | | else |
| | | 67 | | { |
| | 505 | 68 | | builder.AppendLine($" services => global::{safeAssemblyName}.Generated.TypeRegistry.ApplyDecorato |
| | | 69 | | } |
| | | 70 | | |
| | | 71 | | // Generate the options registrar lambda for NeedlrSourceGenBootstrap (for backward compat) |
| | 560 | 72 | | if (hasOptions) |
| | | 73 | | { |
| | 160 | 74 | | builder.AppendLine($" (services, config) => global::{safeAssemblyName}.Generated.TypeRegistry.Reg |
| | | 75 | | } |
| | | 76 | | else |
| | | 77 | | { |
| | 400 | 78 | | builder.AppendLine(" null);"); |
| | | 79 | | } |
| | | 80 | | |
| | | 81 | | // Also register with SourceGenRegistry (for ConfiguredSyringe without Generators.Attributes dependency) |
| | 560 | 82 | | if (hasOptions) |
| | | 83 | | { |
| | 160 | 84 | | builder.AppendLine(); |
| | 160 | 85 | | builder.AppendLine(" // Register options with core SourceGenRegistry for ConfiguredSyringe"); |
| | 160 | 86 | | builder.AppendLine($" global::NexusLabs.Needlr.SourceGenRegistry.RegisterOptionsRegistrar("); |
| | 160 | 87 | | builder.AppendLine($" (services, config) => global::{safeAssemblyName}.Generated.TypeRegistry.Reg |
| | | 88 | | } |
| | | 89 | | |
| | 560 | 90 | | builder.AppendLine(" }"); |
| | | 91 | | |
| | | 92 | | // Generate ForceLoadReferencedAssemblies method if needed |
| | 560 | 93 | | if (referencedAssemblies.Count > 0) |
| | | 94 | | { |
| | 18 | 95 | | builder.AppendLine(); |
| | 18 | 96 | | builder.AppendLine(" /// <summary>"); |
| | 18 | 97 | | builder.AppendLine(" /// Runs module constructors for referenced assemblies with"); |
| | 18 | 98 | | builder.AppendLine(" /// [GenerateTypeRegistry], ensuring their registries register."); |
| | 18 | 99 | | builder.AppendLine(" /// </summary>"); |
| | 18 | 100 | | builder.AppendLine(" /// <remarks>"); |
| | 18 | 101 | | builder.AppendLine(" /// A typeof expression alone does not guarantee module initialization."); |
| | 18 | 102 | | builder.AppendLine(" /// RunModuleConstructor is idempotent when a dependency was already initialized."); |
| | 18 | 103 | | builder.AppendLine(" /// </remarks>"); |
| | 18 | 104 | | builder.AppendLine(" private static void ForceLoadReferencedAssemblies()"); |
| | 18 | 105 | | builder.AppendLine(" {"); |
| | | 106 | | |
| | 74 | 107 | | foreach (var referencedAssembly in referencedAssemblies) |
| | | 108 | | { |
| | 19 | 109 | | var safeRefAssemblyName = GeneratorHelpers.SanitizeIdentifier(referencedAssembly); |
| | 19 | 110 | | builder.AppendLine(" global::System.Runtime.CompilerServices.RuntimeHelpers.RunModuleConstructor( |
| | 19 | 111 | | builder.AppendLine($" typeof(global::{safeRefAssemblyName}.Generated.TypeRegistry).Module.Mod |
| | | 112 | | } |
| | | 113 | | |
| | 18 | 114 | | builder.AppendLine(" }"); |
| | | 115 | | } |
| | | 116 | | |
| | 560 | 117 | | builder.AppendLine("}"); |
| | | 118 | | |
| | 560 | 119 | | return builder.ToString(); |
| | | 120 | | } |
| | | 121 | | } |