From e8aa2cf8ebab26d20b7f5e13dc4c2ca64e85f0dd Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Mon, 8 Jan 2024 11:32:10 -0800 Subject: [PATCH] Update src/Authoring/WinRT.SourceGenerator/Generator.cs Co-authored-by: Vineeth Thomas Alex --- .../WinRT.SourceGenerator/Generator.cs | 528 +++++++++--------- 1 file changed, 264 insertions(+), 264 deletions(-) diff --git a/src/Authoring/WinRT.SourceGenerator/Generator.cs b/src/Authoring/WinRT.SourceGenerator/Generator.cs index 039760e0d..92ab4551f 100644 --- a/src/Authoring/WinRT.SourceGenerator/Generator.cs +++ b/src/Authoring/WinRT.SourceGenerator/Generator.cs @@ -1,266 +1,266 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection.Metadata; -using System.Reflection.Metadata.Ecma335; -using System.Reflection.PortableExecutable; -using System.Text; - -namespace Generator -{ - public class ComponentGenerator - { - private Logger Logger { get; } - private readonly GeneratorExecutionContext context; - private string tempFolder; - - public ComponentGenerator(GeneratorExecutionContext context) - { - this.context = context; - Logger = new Logger(context); - } - - private string GetTempFolder(bool clearSourceFilesFromFolder = false) - { - if (string.IsNullOrEmpty(tempFolder) || !File.Exists(tempFolder)) - { - string outputDir = Path.Combine(Path.GetTempPath(), "CsWinRT", Path.GetRandomFileName()).TrimEnd('\\'); - Directory.CreateDirectory(outputDir); - tempFolder = outputDir; - Logger.Log("Created temp folder: " + tempFolder); - } - - if (clearSourceFilesFromFolder) - { - foreach (var file in Directory.GetFiles(tempFolder, "*.cs", SearchOption.TopDirectoryOnly)) - { - Logger.Log("Clearing " + file); - File.Delete(file); - } - } - - return tempFolder; - } - - private void GenerateSources() - { - string cswinrtExe = context.GetCsWinRTExe(); - string assemblyName = context.GetAssemblyName(); - string winmdFile = context.GetWinmdOutputFile(); - string outputDir = GetTempFolder(true); - string windowsMetadata = context.GetCsWinRTWindowsMetadata(); - string winmds = context.GetCsWinRTDependentMetadata(); - - string arguments = string.Format( - "-component -input \"{0}\" -input {1} -include {2} -output \"{3}\" -input {4} -verbose", - winmdFile, - windowsMetadata, - assemblyName, - outputDir, - winmds); - Logger.Log("Running " + cswinrtExe + " " + arguments); - - var processInfo = new ProcessStartInfo - { - FileName = cswinrtExe, - Arguments = arguments, - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - WindowStyle = ProcessWindowStyle.Hidden, - CreateNoWindow = true - }; - - try - { - using var cswinrtProcess = Process.Start(processInfo); - Logger.Log(cswinrtProcess.StandardOutput.ReadToEnd()); - Logger.Log(cswinrtProcess.StandardError.ReadToEnd()); - cswinrtProcess.WaitForExit(); - - if (cswinrtProcess.ExitCode != 0) - { - throw new Win32Exception(cswinrtProcess.ExitCode); - } - - foreach (var file in Directory.GetFiles(outputDir, "*.cs", SearchOption.TopDirectoryOnly)) - { - Logger.Log("Adding " + file); - context.AddSource(Path.GetFileNameWithoutExtension(file), SourceText.From(File.ReadAllText(file), Encoding.UTF8)); - } - } - finally - { - if (!context.GetKeepGeneratedSources()) - { - Directory.Delete(outputDir, true); - } - } - } - - private void GenerateWinMD(MetadataBuilder metadataBuilder) - { - string outputFile = context.GetWinmdOutputFile(); - Logger.Log("Writing " + outputFile); - var managedPeBuilder = new ManagedPEBuilder( - new PEHeaderBuilder( - machine: Machine.I386, - imageCharacteristics: Characteristics.ExecutableImage | Characteristics.Dll | Characteristics.Bit32Machine), - new MetadataRootBuilder(metadataBuilder, "WindowsRuntime 1.4"), - new BlobBuilder(), - flags: CorFlags.ILOnly); - - var peBlob = new BlobBuilder(); - managedPeBuilder.Serialize(peBlob); - - using var fs = new FileStream(outputFile, FileMode.Create, FileAccess.Write); - peBlob.WriteContentTo(fs); - } - - private bool CatchWinRTDiagnostics() - { - string assemblyName = context.GetAssemblyName(); - WinRTComponentScanner winrtScanner = new(context, assemblyName); - winrtScanner.FindDiagnostics(); - return winrtScanner.Found(); - } - - public void Generate() - { - if (CatchWinRTDiagnostics()) - { - Logger.Log("Exiting early -- found errors in authored runtime component."); - Logger.Close(); - Environment.ExitCode = -1; - return; - } - - try - { - string assembly = context.GetAssemblyName(); - string version = context.GetAssemblyVersion(); - MetadataBuilder metadataBuilder = new MetadataBuilder(); - - var writer = new WinRTTypeWriter( - assembly, - version, - metadataBuilder, - Logger); - - WinRTSyntaxReceiver syntaxReceiver = (WinRTSyntaxReceiver)context.SyntaxReceiver; - Logger.Log("Found " + syntaxReceiver.Declarations.Count + " types"); - foreach (var declaration in syntaxReceiver.Declarations) - { - writer.Model = context.Compilation.GetSemanticModel(declaration.SyntaxTree); - writer.Visit(declaration); - } - writer.FinalizeGeneration(); - - GenerateWinMD(metadataBuilder); - if (!context.ShouldGenerateWinMDOnly()) - { - GenerateSources(); - } - } - catch (Exception e) - { - Logger.Log(e.ToString()); - if (e.InnerException != null) - { - Logger.Log(e.InnerException.ToString()); - } - Logger.Close(); - Environment.ExitCode = -2; - throw; - } - - Logger.Log("Done"); - Logger.Close(); - } - } - - [Generator] - public class SourceGenerator : ISourceGenerator - { - public void Execute(GeneratorExecutionContext context) - { - if (!context.IsCsWinRTComponent() && !context.ShouldGenerateWinMDOnly()) - { - System.Diagnostics.Debug.WriteLine($"Skipping component {context.GetAssemblyName()}"); - return; - } - - ComponentGenerator generator = new ComponentGenerator(context); - generator.Generate(); - } - - public void Initialize(GeneratorInitializationContext context) - { - context.RegisterForSyntaxNotifications(() => new WinRTSyntaxReceiver()); - } - } - - class WinRTSyntaxReceiver : ISyntaxReceiver - { - public List Declarations = new(); - public List Namespaces = new(); - - private bool HasSomePublicTypes(SyntaxNode syntaxNode) - { - return syntaxNode.ChildNodes().OfType().Any(IsPublic); - } - - public void OnVisitSyntaxNode(SyntaxNode syntaxNode) - { - // Store namespaces separately as we only need to look at them for diagnostics - // If we did store them in declarations, we would get duplicate entries in the WinMD, - // once from the namespace declaration and once from the member's declaration - if (syntaxNode is NamespaceDeclarationSyntax @namespace) - { - // We only include the namespace if it has a public type as otherwise it won't. - // be projected. For partial types, there would be one instance that we encounter - // which declares the accessibility and we will use that to determine the accessibility - // of the type for the purpose of determining whether to include the namespace. - if (HasSomePublicTypes(syntaxNode)) - { - Namespaces.Add(@namespace); - } - - // Subsequent checks will fail, small performance boost to return now. - return; - } - - if (syntaxNode is not MemberDeclarationSyntax declaration || !IsPublicOrPartial(declaration)) - { - return; - } - - if (syntaxNode is ClassDeclarationSyntax || - syntaxNode is InterfaceDeclarationSyntax || - syntaxNode is EnumDeclarationSyntax || - syntaxNode is DelegateDeclarationSyntax || - syntaxNode is StructDeclarationSyntax) - { - Declarations.Add(declaration); - } - } - - private bool IsPublic(MemberDeclarationSyntax member) - { - return member.Modifiers.Any(m => m.IsKind(SyntaxKind.PublicKeyword)); +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Reflection.PortableExecutable; +using System.Text; + +namespace Generator +{ + public class ComponentGenerator + { + private Logger Logger { get; } + private readonly GeneratorExecutionContext context; + private string tempFolder; + + public ComponentGenerator(GeneratorExecutionContext context) + { + this.context = context; + Logger = new Logger(context); + } + + private string GetTempFolder(bool clearSourceFilesFromFolder = false) + { + if (string.IsNullOrEmpty(tempFolder) || !File.Exists(tempFolder)) + { + string outputDir = Path.Combine(Path.GetTempPath(), "CsWinRT", Path.GetRandomFileName()).TrimEnd('\\'); + Directory.CreateDirectory(outputDir); + tempFolder = outputDir; + Logger.Log("Created temp folder: " + tempFolder); + } + + if (clearSourceFilesFromFolder) + { + foreach (var file in Directory.GetFiles(tempFolder, "*.cs", SearchOption.TopDirectoryOnly)) + { + Logger.Log("Clearing " + file); + File.Delete(file); + } + } + + return tempFolder; } - private bool IsPublicOrPartial(MemberDeclarationSyntax member) - { - // We detect whether partial types are public using symbol information later. - return member.Modifiers.Any(m => m.IsKind(SyntaxKind.PublicKeyword) || m.IsKind(SyntaxKind.PartialKeyword)); - } - } -} + private void GenerateSources() + { + string cswinrtExe = context.GetCsWinRTExe(); + string assemblyName = context.GetAssemblyName(); + string winmdFile = context.GetWinmdOutputFile(); + string outputDir = GetTempFolder(true); + string windowsMetadata = context.GetCsWinRTWindowsMetadata(); + string winmds = context.GetCsWinRTDependentMetadata(); + + string arguments = string.Format( + "-component -input \"{0}\" -input {1} -include {2} -output \"{3}\" -input {4} -verbose", + winmdFile, + windowsMetadata, + assemblyName, + outputDir, + winmds); + Logger.Log("Running " + cswinrtExe + " " + arguments); + + var processInfo = new ProcessStartInfo + { + FileName = cswinrtExe, + Arguments = arguments, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + WindowStyle = ProcessWindowStyle.Hidden, + CreateNoWindow = true + }; + + try + { + using var cswinrtProcess = Process.Start(processInfo); + Logger.Log(cswinrtProcess.StandardOutput.ReadToEnd()); + Logger.Log(cswinrtProcess.StandardError.ReadToEnd()); + cswinrtProcess.WaitForExit(); + + if (cswinrtProcess.ExitCode != 0) + { + throw new Win32Exception(cswinrtProcess.ExitCode); + } + + foreach (var file in Directory.GetFiles(outputDir, "*.cs", SearchOption.TopDirectoryOnly)) + { + Logger.Log("Adding " + file); + context.AddSource(Path.GetFileNameWithoutExtension(file), SourceText.From(File.ReadAllText(file), Encoding.UTF8)); + } + } + finally + { + if (!context.GetKeepGeneratedSources()) + { + Directory.Delete(outputDir, true); + } + } + } + + private void GenerateWinMD(MetadataBuilder metadataBuilder) + { + string outputFile = context.GetWinmdOutputFile(); + Logger.Log("Writing " + outputFile); + var managedPeBuilder = new ManagedPEBuilder( + new PEHeaderBuilder( + machine: Machine.I386, + imageCharacteristics: Characteristics.ExecutableImage | Characteristics.Dll | Characteristics.Bit32Machine), + new MetadataRootBuilder(metadataBuilder, "WindowsRuntime 1.4"), + new BlobBuilder(), + flags: CorFlags.ILOnly); + + var peBlob = new BlobBuilder(); + managedPeBuilder.Serialize(peBlob); + + using var fs = new FileStream(outputFile, FileMode.Create, FileAccess.Write); + peBlob.WriteContentTo(fs); + } + + private bool CatchWinRTDiagnostics() + { + string assemblyName = context.GetAssemblyName(); + WinRTComponentScanner winrtScanner = new(context, assemblyName); + winrtScanner.FindDiagnostics(); + return winrtScanner.Found(); + } + + public void Generate() + { + if (CatchWinRTDiagnostics()) + { + Logger.Log("Exiting early -- found errors in authored runtime component."); + Logger.Close(); + Environment.ExitCode = -1; + return; + } + + try + { + string assembly = context.GetAssemblyName(); + string version = context.GetAssemblyVersion(); + MetadataBuilder metadataBuilder = new MetadataBuilder(); + + var writer = new WinRTTypeWriter( + assembly, + version, + metadataBuilder, + Logger); + + WinRTSyntaxReceiver syntaxReceiver = (WinRTSyntaxReceiver)context.SyntaxReceiver; + Logger.Log("Found " + syntaxReceiver.Declarations.Count + " types"); + foreach (var declaration in syntaxReceiver.Declarations) + { + writer.Model = context.Compilation.GetSemanticModel(declaration.SyntaxTree); + writer.Visit(declaration); + } + writer.FinalizeGeneration(); + + GenerateWinMD(metadataBuilder); + if (!context.ShouldGenerateWinMDOnly()) + { + GenerateSources(); + } + } + catch (Exception e) + { + Logger.Log(e.ToString()); + if (e.InnerException != null) + { + Logger.Log(e.InnerException.ToString()); + } + Logger.Close(); + Environment.ExitCode = -2; + throw; + } + + Logger.Log("Done"); + Logger.Close(); + } + } + + [Generator] + public class SourceGenerator : ISourceGenerator + { + public void Execute(GeneratorExecutionContext context) + { + if (!context.IsCsWinRTComponent() && !context.ShouldGenerateWinMDOnly()) + { + System.Diagnostics.Debug.WriteLine($"Skipping component {context.GetAssemblyName()}"); + return; + } + + ComponentGenerator generator = new ComponentGenerator(context); + generator.Generate(); + } + + public void Initialize(GeneratorInitializationContext context) + { + context.RegisterForSyntaxNotifications(() => new WinRTSyntaxReceiver()); + } + } + + class WinRTSyntaxReceiver : ISyntaxReceiver + { + public List Declarations = new(); + public List Namespaces = new(); + + private bool HasSomePublicTypes(SyntaxNode syntaxNode) + { + return syntaxNode.ChildNodes().OfType().Any(IsPublic); + } + + public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + { + // Store namespaces separately as we only need to look at them for diagnostics + // If we did store them in declarations, we would get duplicate entries in the WinMD, + // once from the namespace declaration and once from the member's declaration + if (syntaxNode is NamespaceDeclarationSyntax @namespace) + { + // We only include the namespace if it has a public type as otherwise it won't + // be projected. For partial types, there would be one instance that we encounter + // which declares the accessibility and we will use that to determine the accessibility + // of the type for the purpose of determining whether to include the namespace. + if (HasSomePublicTypes(syntaxNode)) + { + Namespaces.Add(@namespace); + } + + // Subsequent checks will fail, small performance boost to return now. + return; + } + + if (syntaxNode is not MemberDeclarationSyntax declaration || !IsPublicOrPartial(declaration)) + { + return; + } + + if (syntaxNode is ClassDeclarationSyntax || + syntaxNode is InterfaceDeclarationSyntax || + syntaxNode is EnumDeclarationSyntax || + syntaxNode is DelegateDeclarationSyntax || + syntaxNode is StructDeclarationSyntax) + { + Declarations.Add(declaration); + } + } + + private bool IsPublic(MemberDeclarationSyntax member) + { + return member.Modifiers.Any(m => m.IsKind(SyntaxKind.PublicKeyword)); + } + + private bool IsPublicOrPartial(MemberDeclarationSyntax member) + { + // We detect whether partial types are public using symbol information later. + return member.Modifiers.Any(m => m.IsKind(SyntaxKind.PublicKeyword) || m.IsKind(SyntaxKind.PartialKeyword)); + } + } +}