From 3c06ebae9536376c4a088592762a1ccff5aa2253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Tue, 21 May 2024 15:21:43 +0200 Subject: [PATCH 1/2] Add support for C/C++ attribute annotate and emit custom attribute type if encountered. --- .../PInvokeGenerator.cs | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index 841aeb15..ffbace62 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -65,6 +65,8 @@ public sealed partial class PInvokeGenerator : IDisposable private readonly HashSet _usedRemappings; private readonly string _placeholderMacroType; + private bool _hasAnnotateAttr; + private string _filePath; private string[] _clangCommandLineArgs; private CXTranslationUnit_Flags _translationFlags; @@ -435,6 +437,7 @@ public void Close() GenerateNativeBitfieldAttribute(this, stream, leaveStreamOpen); GenerateNativeInheritanceAttribute(this, stream, leaveStreamOpen); GenerateNativeTypeNameAttribute(this, stream, leaveStreamOpen); + GenerateNativeAnnotationAttribute(this, stream, leaveStreamOpen); GenerateSetsLastSystemErrorAttribute(this, stream, leaveStreamOpen); GenerateVtblIndexAttribute(this, stream, leaveStreamOpen); GenerateTransparentStructs(this, stream, leaveStreamOpen); @@ -795,6 +798,94 @@ static void GenerateNativeTypeNameAttribute(PInvokeGenerator generator, Stream? } } + static void GenerateNativeAnnotationAttribute(PInvokeGenerator generator, Stream? stream, bool leaveStreamOpen) + { + var config = generator.Config; + + if (!generator._hasAnnotateAttr) + { + return; + } + + if (stream is null) + { + var outputPath = Path.Combine(config.OutputLocation, "NativeAnnotationAttribute.cs"); + stream = generator._outputStreamFactory(outputPath); + } + + using var sw = new StreamWriter(stream, s_defaultStreamWriterEncoding, DefaultStreamWriterBufferSize, leaveStreamOpen); + sw.NewLine = "\n"; + + if (!string.IsNullOrEmpty(config.HeaderText)) + { + sw.WriteLine(config.HeaderText); + } + + var indentString = " "; + + sw.WriteLine("using System;"); + sw.WriteLine("using System.Diagnostics;"); + sw.WriteLine(); + + sw.Write("namespace "); + sw.Write(generator.GetNamespace("NativeAnnotationAttribute")); + + if (generator.Config.GenerateFileScopedNamespaces) + { + sw.WriteLine(';'); + sw.WriteLine(); + indentString = ""; + } + else + { + sw.WriteLine(); + sw.WriteLine('{'); + } + + sw.Write(indentString); + sw.WriteLine("/// Defines the annotation found in a native declaration."); + sw.Write(indentString); + sw.WriteLine("[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)]"); + sw.Write(indentString); + sw.WriteLine("[Conditional(\"DEBUG\")]"); + sw.Write(indentString); + sw.WriteLine("internal sealed partial class NativeAnnotationAttribute : Attribute"); + sw.Write(indentString); + sw.WriteLine('{'); + sw.Write(indentString); + sw.WriteLine(" private readonly string _annotation;"); + sw.WriteLine(); + sw.Write(indentString); + sw.WriteLine(" /// Initializes a new instance of the class."); + sw.Write(indentString); + sw.WriteLine(" /// The annotation that was used in the native declaration."); + sw.Write(indentString); + sw.WriteLine(" public NativeAnnotationAttribute(string annotation)"); + sw.Write(indentString); + sw.WriteLine(" {"); + sw.Write(indentString); + sw.WriteLine(" _annotation = annotation;"); + sw.Write(indentString); + sw.WriteLine(" }"); + sw.WriteLine(); + sw.Write(indentString); + sw.WriteLine(" /// Gets the annotation that was used in the native declaration."); + sw.Write(indentString); + sw.WriteLine(" public string Annotation => _annotation;"); + sw.Write(indentString); + sw.WriteLine('}'); + + if (!generator.Config.GenerateFileScopedNamespaces) + { + sw.WriteLine('}'); + } + + if (!leaveStreamOpen) + { + stream = null; + } + } + static void GenerateSetsLastSystemErrorAttribute(PInvokeGenerator generator, Stream? stream, bool leaveStreamOpen) { var config = generator.Config; @@ -6724,6 +6815,14 @@ private void WithAttributes(NamedDecl namedDecl, bool onlySupportedOSPlatform = break; } + case CX_AttrKind_Annotate: + { + _hasAnnotateAttr = true; + var annotationText = attr.Spelling; + outputBuilder.WriteCustomAttribute($"""NativeAnnotation("{annotationText}")"""); + break; + } + case CX_AttrKind_Format: case CX_AttrKind_FormatArg: case CX_AttrKind_MSNoVTable: From c62fb6231888351dad7e8a95001f1be58421cf17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Sat, 25 May 2024 14:18:44 +0200 Subject: [PATCH 2/2] NativeAnnotationAttribute is generated when not included in config's name exclusion list. --- .../PInvokeGenerator.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index ffbace62..d3b9bb84 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -65,8 +65,6 @@ public sealed partial class PInvokeGenerator : IDisposable private readonly HashSet _usedRemappings; private readonly string _placeholderMacroType; - private bool _hasAnnotateAttr; - private string _filePath; private string[] _clangCommandLineArgs; private CXTranslationUnit_Flags _translationFlags; @@ -800,16 +798,18 @@ static void GenerateNativeTypeNameAttribute(PInvokeGenerator generator, Stream? static void GenerateNativeAnnotationAttribute(PInvokeGenerator generator, Stream? stream, bool leaveStreamOpen) { + const string AttributeName = "NativeAnnotationAttribute"; var config = generator.Config; - if (!generator._hasAnnotateAttr) + var ns = generator.GetNamespace(AttributeName); + if (config.ExcludedNames.Contains(AttributeName) || config.ExcludedNames.Contains($"{ns}.{AttributeName}")) { return; } if (stream is null) { - var outputPath = Path.Combine(config.OutputLocation, "NativeAnnotationAttribute.cs"); + var outputPath = Path.Combine(config.OutputLocation, $"{AttributeName}.cs"); stream = generator._outputStreamFactory(outputPath); } @@ -828,7 +828,7 @@ static void GenerateNativeAnnotationAttribute(PInvokeGenerator generator, Stream sw.WriteLine(); sw.Write("namespace "); - sw.Write(generator.GetNamespace("NativeAnnotationAttribute")); + sw.Write(ns); if (generator.Config.GenerateFileScopedNamespaces) { @@ -849,18 +849,18 @@ static void GenerateNativeAnnotationAttribute(PInvokeGenerator generator, Stream sw.Write(indentString); sw.WriteLine("[Conditional(\"DEBUG\")]"); sw.Write(indentString); - sw.WriteLine("internal sealed partial class NativeAnnotationAttribute : Attribute"); + sw.WriteLine($"internal sealed partial class {AttributeName} : Attribute"); sw.Write(indentString); sw.WriteLine('{'); sw.Write(indentString); sw.WriteLine(" private readonly string _annotation;"); sw.WriteLine(); sw.Write(indentString); - sw.WriteLine(" /// Initializes a new instance of the class."); + sw.WriteLine($" /// Initializes a new instance of the class."); sw.Write(indentString); sw.WriteLine(" /// The annotation that was used in the native declaration."); sw.Write(indentString); - sw.WriteLine(" public NativeAnnotationAttribute(string annotation)"); + sw.WriteLine($" public {AttributeName}(string annotation)"); sw.Write(indentString); sw.WriteLine(" {"); sw.Write(indentString); @@ -6817,7 +6817,6 @@ private void WithAttributes(NamedDecl namedDecl, bool onlySupportedOSPlatform = case CX_AttrKind_Annotate: { - _hasAnnotateAttr = true; var annotationText = attr.Spelling; outputBuilder.WriteCustomAttribute($"""NativeAnnotation("{annotationText}")"""); break;