From 4e7432dfa9653b4a9bb9e3717ff5b813cf0c6f7f Mon Sep 17 00:00:00 2001 From: neuecc Date: Tue, 17 Dec 2024 19:34:43 +0900 Subject: [PATCH] remove dependency --- sandbox/GeneratorSandbox/Program.cs | 2 +- .../GeneratorCore/CodeGenerator.cs | 265 +----------------- .../MasterMemory.SourceGenerator.csproj | 5 - .../MasterMemoryGenerator.cs | 24 +- .../Polyfill/System.CodeDom.cs | 21 ++ 5 files changed, 45 insertions(+), 272 deletions(-) create mode 100644 src/MasterMemory.SourceGenerator/Polyfill/System.CodeDom.cs diff --git a/sandbox/GeneratorSandbox/Program.cs b/sandbox/GeneratorSandbox/Program.cs index 0cb5147..f00d4a1 100644 --- a/sandbox/GeneratorSandbox/Program.cs +++ b/sandbox/GeneratorSandbox/Program.cs @@ -49,7 +49,7 @@ public override string ToString() } [MemoryTable("monster"), MessagePackObject(true)] -public class Monster +public partial class Monster { [PrimaryKey] public int MonsterId { get; private set; } diff --git a/src/MasterMemory.SourceGenerator/GeneratorCore/CodeGenerator.cs b/src/MasterMemory.SourceGenerator/GeneratorCore/CodeGenerator.cs index 4b126d2..cf3c646 100644 --- a/src/MasterMemory.SourceGenerator/GeneratorCore/CodeGenerator.cs +++ b/src/MasterMemory.SourceGenerator/GeneratorCore/CodeGenerator.cs @@ -3,200 +3,12 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Editing; -using Microsoft.CodeAnalysis.Formatting; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace MasterMemory.GeneratorCore { - public class CodeGenerator + public static class CodeGenerator { - static readonly Encoding NoBomUtf8 = new UTF8Encoding(false); - - public void GenerateFile(string usingNamespace, string inputDirectory, string outputDirectory, string prefixClassName, bool addImmutableConstructor, bool throwIfKeyNotFound, bool forceOverwrite, Action logger) - { - prefixClassName ??= ""; - var list = new List(); - - // Collect - if (inputDirectory.EndsWith(".csproj")) - { - throw new InvalidOperationException("Path must be directory but it is csproj. inputDirectory:" + inputDirectory); - } - - foreach (var item in Directory.GetFiles(inputDirectory, "*.cs", SearchOption.AllDirectories)) - { - list.AddRange(CreateGenerationContext(item)); - } - - list.Sort((a, b) => string.Compare(a.ClassName, b.ClassName, StringComparison.Ordinal)); - - if (list.Count == 0) - { - throw new InvalidOperationException("Not found MemoryTable files, inputDir:" + inputDirectory); - } - - // Output - { - if (!Directory.Exists(outputDirectory)) - { - Directory.CreateDirectory(outputDirectory); - } - - var usingStrings = string.Join(Environment.NewLine, list.SelectMany(x => x.UsingStrings).Distinct().OrderBy(x => x, StringComparer.Ordinal)); - - var builderTemplate = new DatabaseBuilderTemplate(); - var databaseTemplate = new MemoryDatabaseTemplate(); - var immutableBuilderTemplate = new ImmutableBuilderTemplate(); - var resolverTemplate = new MessagePackResolverTemplate(); - builderTemplate.Namespace = databaseTemplate.Namespace = immutableBuilderTemplate.Namespace = resolverTemplate.Namespace = usingNamespace; - builderTemplate.PrefixClassName = databaseTemplate.PrefixClassName = immutableBuilderTemplate.PrefixClassName = resolverTemplate.PrefixClassName = prefixClassName; - builderTemplate.Using = databaseTemplate.Using = immutableBuilderTemplate.Using = resolverTemplate.Using = (usingStrings + Environment.NewLine + ("using " + usingNamespace + ".Tables;")); - builderTemplate.GenerationContexts = databaseTemplate.GenerationContexts = immutableBuilderTemplate.GenerationContexts = resolverTemplate.GenerationContexts = list.ToArray(); - - logger(WriteToFile(outputDirectory, builderTemplate.ClassName, builderTemplate.TransformText(), forceOverwrite)); - logger(WriteToFile(outputDirectory, immutableBuilderTemplate.ClassName, immutableBuilderTemplate.TransformText(), forceOverwrite)); - logger(WriteToFile(outputDirectory, databaseTemplate.ClassName, databaseTemplate.TransformText(), forceOverwrite)); - logger(WriteToFile(outputDirectory, resolverTemplate.ClassName, resolverTemplate.TransformText(), forceOverwrite)); - } - { - var tableDir = Path.Combine(outputDirectory, "Tables"); - if (!Directory.Exists(tableDir)) - { - Directory.CreateDirectory(tableDir); - } - - foreach (var context in list) - { - var template = new TableTemplate() - { - Namespace = usingNamespace, - GenerationContext = context, - Using = string.Join(Environment.NewLine, context.UsingStrings), - ThrowKeyIfNotFound = throwIfKeyNotFound - }; - - logger(WriteToFile(tableDir, context.ClassName + "Table", template.TransformText(), forceOverwrite)); - } - } - // Modify - { - if (addImmutableConstructor) - { - var workspace = new AdhocWorkspace(); - var byFilePath = list.GroupBy(x => x.InputFilePath); - - foreach (var context in byFilePath) - { - var newFile = BuildRecordConstructorFile(workspace, context.Select(x => x.OriginalClassDeclaration)); - if (newFile != null) - { - File.WriteAllText(context.Key, newFile, NoBomUtf8); - logger("Modified " + context.Key); - } - } - } - } - } - - static string NormalizeNewLines(string content) - { - // The T4 generated code may be text with mixed line ending types. (CR + CRLF) - // We need to normalize the line ending type in each Operating Systems. (e.g. Windows=CRLF, Linux/macOS=LF) - return content.Replace("\r\n", "\n").Replace("\n", Environment.NewLine); - } - - static string WriteToFile(string directory, string fileName, string content, bool forceOverwrite) - { - var path = Path.Combine(directory, fileName + ".cs"); - var contentBytes = Encoding.UTF8.GetBytes(NormalizeNewLines(content)); - - // If the generated content is unchanged, skip the write. - if (!forceOverwrite && File.Exists(path)) - { - if (new FileInfo(path).Length == contentBytes.Length && contentBytes.AsSpan().SequenceEqual(File.ReadAllBytes(path))) - { - return $"Generate {fileName} to: {path} (Skipped)"; - } - } - - File.WriteAllBytes(path, contentBytes); - return $"Generate {fileName} to: {path}"; - } - - IEnumerable CreateGenerationContext(string filePath) - { - var syntax = Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.ParseText(File.ReadAllText(filePath)); - var root = syntax.GetRoot(); - - var classDeclarations = root.DescendantNodes().OfType().ToArray(); - if (classDeclarations.Length == 0) yield break; - - var ns = root.DescendantNodes().OfType() - .Select(x => "using " + x.Name.ToFullStringTrim() + ";") - .ToArray(); - - var usingStrings = root.DescendantNodes() - .OfType() - .Select(x => x.ToFullString().Trim()) - .Concat(new[] { "using MasterMemory", "using MasterMemory.Validation", "using System", "using System.Collections.Generic" }) - .Concat(ns) - .Select(x => x.Trim(';') + ";") - .Distinct() - .OrderBy(x => x, StringComparer.Ordinal) - .ToArray(); - - foreach (var classDecl in classDeclarations) - { - var context = new GenerationContext(); - - foreach (var attr in classDecl.AttributeLists.SelectMany(x => x.Attributes)) - { - var attrName = attr.Name.ToFullString().Trim(); - if (attrName == "MemoryTable" || attrName == "MasterMemory.Annotations.MemoryTable") - { - context.ClassName = classDecl.Identifier.ToFullString().Trim(); - context.MemoryTableName = AttributeExpressionToString(attr.ArgumentList.Arguments[0].Expression) ?? context.ClassName; - - var members = classDecl.Members.OfType() - .Select(x => ExtractPropertyAttribute(x)) - .ToArray(); - - var primaryKey = AggregatePrimaryKey(members.Where(x => x.Item1 != null).Select(x => x.Item1)); - if (primaryKey.Properties.Length == 0) - { - throw new InvalidOperationException("MemoryTable does not found PrimaryKey property, Type:" + context.ClassName); - } - - var secondaryKeys = members.SelectMany(x => x.Item2).GroupBy(x => x.IndexNo).Select(x => AggregateSecondaryKey(x)).ToArray(); - var properties = members.Where(x => x.Item3 != null).Select(x => new Property - { - Type = x.Item3.Type.ToFullStringTrim(), - Name = x.Item3.Identifier.Text, - }).ToArray(); - - context.PrimaryKey = primaryKey; - context.SecondaryKeys = secondaryKeys; - context.Properties = properties; - } - } - - if (context.PrimaryKey != null) - { - context.UsingStrings = usingStrings; - context.OriginalClassDeclaration = classDecl; - context.InputFilePath = filePath; - yield return context; - } - } - } - - public static GenerationContext CreateGenerationContext2(ClassDeclarationSyntax classDecl) + public static GenerationContext CreateGenerationContext(ClassDeclarationSyntax classDecl) { var root = classDecl.SyntaxTree.GetRoot(); @@ -410,79 +222,6 @@ static SecondaryKey AggregateSecondaryKey(IGrouping secondary return secondaryKey; } - string BuildRecordConstructorFile(AdhocWorkspace workspace, IEnumerable classDeclarations) - { - // using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; - - var modify = false; - var editor = new SyntaxEditor(classDeclarations.First().SyntaxTree.GetRoot(), workspace); - foreach (var classDeclaration in classDeclarations) - { - var members = classDeclaration.Members.OfType() - .Where(x => x.Modifiers.Any(y => y.IsKind(SyntaxKind.PublicKeyword))) - .Where(x => - { - foreach (var attr in x.AttributeLists.SelectMany(y => y.Attributes)) - { - var attrName = attr.Name.ToFullString().Trim(); - if (attrName == "IgnoreMember" || attrName == "MessagePack.IgnoreMember") - { - return false; - } - if (attrName == "IgnoreDataMember" || attrName == "System.Runtime.Serialization.IgnoreDataMember") - { - return false; - } - } - return true; - }) - .ToArray(); - - var parameters = ParameterList(SeparatedList(members.Select(x => Parameter(attributeLists: default, modifiers: default, type: x.Type, x.Identifier, @default: null)))); - - var parameterStrings = parameters.Parameters.Select(x => x.Type.ToFullStringTrim()).ToArray(); - - // check existing constructor - var matchedConstructor = classDeclaration.Members.OfType() - .Where(x => x.ParameterList.Parameters.Select(y => y.Type.ToFullStringTrim()).SequenceEqual(parameterStrings)) - .ToArray(); - - if (matchedConstructor.Length != 0) - { - continue; - } - - var body = members.Select(x => AssignmentExpression( - SyntaxKind.SimpleAssignmentExpression, MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, ThisExpression(), IdentifierName(x.Identifier)), - Token(SyntaxKind.EqualsToken), - IdentifierName(x.Identifier))) - .Select(x => ExpressionStatement(x)); - - var recordCtor = ConstructorDeclaration(classDeclaration.Identifier) - .WithModifiers(TokenList(Token(Sy‌​ntaxKind.PublicKeywo‌​rd))) - .WithParameterList(parameters) - .WithBody(Block(body)) - .NormalizeWhitespace() - .WithLeadingTrivia(LineFeed) - .WithTrailingTrivia(LineFeed); - - var newClassDecl = classDeclaration.AddMembers(recordCtor); - - modify = true; - editor.ReplaceNode(classDeclaration, newClassDecl); - } - - if (modify) - { - var newCodeString = Microsoft.CodeAnalysis.Formatting.Formatter.Format(editor.GetChangedRoot(), workspace).ToFullString(); - return newCodeString; - } - else - { - return null; - } - } - static string AttributeExpressionToString(ExpressionSyntax expression) { if (expression is InvocationExpressionSyntax ie) diff --git a/src/MasterMemory.SourceGenerator/MasterMemory.SourceGenerator.csproj b/src/MasterMemory.SourceGenerator/MasterMemory.SourceGenerator.csproj index 3eb1e15..3513dbd 100644 --- a/src/MasterMemory.SourceGenerator/MasterMemory.SourceGenerator.csproj +++ b/src/MasterMemory.SourceGenerator/MasterMemory.SourceGenerator.csproj @@ -17,11 +17,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - diff --git a/src/MasterMemory.SourceGenerator/MasterMemoryGenerator.cs b/src/MasterMemory.SourceGenerator/MasterMemoryGenerator.cs index 89b6c3c..1095ecc 100644 --- a/src/MasterMemory.SourceGenerator/MasterMemoryGenerator.cs +++ b/src/MasterMemory.SourceGenerator/MasterMemoryGenerator.cs @@ -41,7 +41,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) // TODO:... // Input and Output is no needed. // prefix should be configurable?(to assemblyattribute) - // immutableconstructor + // remove immutableconstructor feature // returnnull // new CodeGenerator().GenerateFile(UsingNamespace, InputDirectory, OutputDirectory, PrefixClassName, AddImmutableConstructor, !ReturnNullIfKeyNotFound, ForceOverwrite, x => Console.WriteLine(x)); @@ -63,7 +63,7 @@ void EmitMemoryTable(SourceProductionContext context, ImmutableArray