From a1eed293a9af700a6998dc55867e7ef14f105069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Wed, 7 Feb 2024 08:44:27 +0100 Subject: [PATCH 01/13] Add strip-enum-member-type-name config option. --- README.md | 4 ++++ .../PInvokeGenerator.VisitDecl.cs | 10 +++++++++- .../PInvokeGeneratorConfiguration.cs | 2 ++ .../PInvokeGeneratorConfigurationOptions.cs | 2 ++ sources/ClangSharpPInvokeGenerator/Program.cs | 14 ++++++++++++++ 5 files changed, 31 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8d8df0c9..3a36108e 100644 --- a/README.md +++ b/README.md @@ -256,6 +256,10 @@ Options: generate-unmanaged-constants Unmanaged constants should be generated using static ref readonly properties. This is currently experimental. generate-vtbl-index-attribute [VtblIndex(#)] attribute should be generated to document the underlying VTBL index for a helper method. + # Stripping Options + + strip-enum-member-type-name Strips the enum type name from the beginning of its member names. + # Logging Options log-exclusions A list of excluded declaration types should be generated. This will also log if the exclusion was due to an exact or partial match. diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs index 10a6b486..a8b4b4cc 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs @@ -19,6 +19,7 @@ using static ClangSharp.Interop.CXUnaryOperatorKind; using static ClangSharp.Interop.CXEvalResultKind; using static ClangSharp.Interop.CXTypeKind; +using System.Text.RegularExpressions; namespace ClangSharp; @@ -294,6 +295,13 @@ private void VisitEnumConstantDecl(EnumConstantDecl enumConstantDecl) parentName = _outputBuilder.Name; } + var strippedName = escapedName; + if (Config.StripEnumMemberTypeName) + { + Regex stripParentNameRegex = new($"^{Regex.Escape(parentName)}_*"); + strippedName = stripParentNameRegex.Replace(escapedName, string.Empty); + } + var kind = isAnonymousEnum ? ValueKind.Primitive : ValueKind.Enumerator; var flags = ValueFlags.Constant; @@ -305,7 +313,7 @@ private void VisitEnumConstantDecl(EnumConstantDecl enumConstantDecl) var desc = new ValueDesc { AccessSpecifier = accessSpecifier, TypeName = typeName, - EscapedName = escapedName, + EscapedName = Config.StripEnumMemberTypeName ? strippedName : escapedName, NativeTypeName = null, ParentName = parentName, Kind = kind, diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs index 3dfb786b..e36133e0 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs @@ -265,6 +265,8 @@ public IReadOnlyCollection ExcludedNames public bool GenerateVtblIndexAttribute => _options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateVtblIndexAttribute); + public bool StripEnumMemberTypeName => _options.HasFlag(PInvokeGeneratorConfigurationOptions.StripEnumMemberTypeName); + public string HeaderText => _headerText; [AllowNull] diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfigurationOptions.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfigurationOptions.cs index db91ccd4..c2a34293 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfigurationOptions.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfigurationOptions.cs @@ -86,4 +86,6 @@ public enum PInvokeGeneratorConfigurationOptions : long GenerateCallConvMemberFunction = 1L << 37, GenerateGenericPointerWrapper = 1L << 38, + + StripEnumMemberTypeName = 1L << 39, } diff --git a/sources/ClangSharpPInvokeGenerator/Program.cs b/sources/ClangSharpPInvokeGenerator/Program.cs index 6dd06fee..69eb17ee 100644 --- a/sources/ClangSharpPInvokeGenerator/Program.cs +++ b/sources/ClangSharpPInvokeGenerator/Program.cs @@ -177,6 +177,12 @@ public static class Program new TwoColumnHelpRow("generate-unmanaged-constants", "Unmanaged constants should be generated using static ref readonly properties. This is currently experimental."), new TwoColumnHelpRow("generate-vtbl-index-attribute", "[VtblIndex(#)] attribute should be generated to document the underlying VTBL index for a helper method."), + new TwoColumnHelpRow("", ""), + new TwoColumnHelpRow("# Stripping Options", ""), + new TwoColumnHelpRow("", ""), + + new TwoColumnHelpRow("strip-enum-member-type-name", "Strips the enum type name from the beginning of its member names."), + new TwoColumnHelpRow("", ""), new TwoColumnHelpRow("# Logging Options", ""), new TwoColumnHelpRow("", ""), @@ -617,6 +623,14 @@ public static void Run(InvocationContext context) break; } + // Strip Options + + case "strip-enum-member-type-name": + { + configOptions |= PInvokeGeneratorConfigurationOptions.StripEnumMemberTypeName; + break; + } + // Legacy Options case "default-remappings": From da4255fc77d3c079736837d411ba65a4284bbeb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Wed, 7 Feb 2024 09:04:08 +0100 Subject: [PATCH 02/13] Match enum type name case insensitively --- .../ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs index a8b4b4cc..ed4c93b4 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs @@ -298,7 +298,7 @@ private void VisitEnumConstantDecl(EnumConstantDecl enumConstantDecl) var strippedName = escapedName; if (Config.StripEnumMemberTypeName) { - Regex stripParentNameRegex = new($"^{Regex.Escape(parentName)}_*"); + Regex stripParentNameRegex = new($"^{Regex.Escape(parentName)}_*", RegexOptions.IgnoreCase); strippedName = stripParentNameRegex.Replace(escapedName, string.Empty); } From 44af1626a997d9ab24765b93ae732857d680da42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Wed, 7 Feb 2024 09:04:59 +0100 Subject: [PATCH 03/13] Contain StripEnumMemberTypeName config option logic in a single location --- .../PInvokeGenerator.VisitDecl.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs index ed4c93b4..6ddf4af4 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs @@ -295,11 +295,11 @@ private void VisitEnumConstantDecl(EnumConstantDecl enumConstantDecl) parentName = _outputBuilder.Name; } - var strippedName = escapedName; if (Config.StripEnumMemberTypeName) { Regex stripParentNameRegex = new($"^{Regex.Escape(parentName)}_*", RegexOptions.IgnoreCase); - strippedName = stripParentNameRegex.Replace(escapedName, string.Empty); + var strippedName = stripParentNameRegex.Replace(escapedName, string.Empty); + escapedName = strippedName; } var kind = isAnonymousEnum ? ValueKind.Primitive : ValueKind.Enumerator; @@ -313,7 +313,7 @@ private void VisitEnumConstantDecl(EnumConstantDecl enumConstantDecl) var desc = new ValueDesc { AccessSpecifier = accessSpecifier, TypeName = typeName, - EscapedName = Config.StripEnumMemberTypeName ? strippedName : escapedName, + EscapedName = escapedName, NativeTypeName = null, ParentName = parentName, Kind = kind, From fa6653d54abe4206fb8231755fda408e67b068bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Wed, 7 Feb 2024 09:06:34 +0100 Subject: [PATCH 04/13] Move Regex using next to related usings --- .../ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs index 6ddf4af4..393d23f3 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Runtime.InteropServices; using System.Text; +using System.Text.RegularExpressions; using ClangSharp.Abstractions; using ClangSharp.CSharp; using static ClangSharp.Interop.CX_CastKind; @@ -19,7 +20,6 @@ using static ClangSharp.Interop.CXUnaryOperatorKind; using static ClangSharp.Interop.CXEvalResultKind; using static ClangSharp.Interop.CXTypeKind; -using System.Text.RegularExpressions; namespace ClangSharp; From 6703d6a4cbbcd82557057c6443724bd929b0ba05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Sun, 19 May 2024 15:20:22 +0200 Subject: [PATCH 05/13] Simplify prefix stripping logic and consolidate --- .../PInvokeGenerator.VisitDecl.cs | 18 +++++----- .../PInvokeGenerator.VisitStmt.cs | 2 +- .../PInvokeGenerator.cs | 33 ++++++++++++++----- 3 files changed, 33 insertions(+), 20 deletions(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs index 393d23f3..974b98da 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs @@ -297,9 +297,7 @@ private void VisitEnumConstantDecl(EnumConstantDecl enumConstantDecl) if (Config.StripEnumMemberTypeName) { - Regex stripParentNameRegex = new($"^{Regex.Escape(parentName)}_*", RegexOptions.IgnoreCase); - var strippedName = stripParentNameRegex.Replace(escapedName, string.Empty); - escapedName = strippedName; + escapedName = PrefixAndStrip(escapedName, parentName, trimChar: '_'); } var kind = isAnonymousEnum ? ValueKind.Primitive : ValueKind.Enumerator; @@ -543,12 +541,12 @@ private void VisitFunctionDecl(FunctionDecl functionDecl) if ((cxxMethodDecl is not null) && cxxMethodDecl.IsVirtual) { isVirtual = true; - escapedName = PrefixAndStripName(name, GetOverloadIndex(cxxMethodDecl)); + escapedName = PrefixAndStripMethodName(name, GetOverloadIndex(cxxMethodDecl)); } else { isVirtual = false; - escapedName = EscapeAndStripName(name); + escapedName = EscapeAndStripMethodName(name); } var returnType = functionDecl.ReturnType; @@ -2032,7 +2030,7 @@ void OutputMarkerInterface(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethodD var desc = new FunctionOrDelegateDesc { AccessSpecifier = AccessSpecifier.Public, - EscapedName = EscapeAndStripName(name), + EscapedName = EscapeAndStripMethodName(name), IsMemberFunction = true, NativeTypeName = nativeTypeName, HasFnPtrCodeGen = !_config.ExcludeFnptrCodegen, @@ -2120,7 +2118,7 @@ void OutputVtblEntry(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethodDecl) } var remappedName = FixupNameForMultipleHits(cxxMethodDecl); - var escapedName = EscapeAndStripName(remappedName); + var escapedName = EscapeAndStripMethodName(remappedName); var desc = new FieldDesc { @@ -2189,7 +2187,7 @@ void OutputVtblHelperMethod(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethod var desc = new FunctionOrDelegateDesc { AccessSpecifier = AccessSpecifier.Public, IsAggressivelyInlined = _config.GenerateAggressiveInlining, - EscapedName = EscapeAndStripName(name), + EscapedName = EscapeAndStripMethodName(name), ParentName = parentName, IsMemberFunction = true, IsInherited = isInherited, @@ -2269,7 +2267,7 @@ void OutputVtblHelperMethod(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethod { body.Write("Marshal.GetDelegateForFunctionPointer<"); body.BeginMarker("delegate"); - body.Write(PrefixAndStripName(name, GetOverloadIndex(cxxMethodDecl))); + body.Write(PrefixAndStripMethodName(name, GetOverloadIndex(cxxMethodDecl))); body.EndMarker("delegate"); body.Write(">("); } @@ -2278,7 +2276,7 @@ void OutputVtblHelperMethod(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethod { body.Write("lpVtbl->"); body.BeginMarker("vtbl", new KeyValuePair("explicit", true)); - body.Write(EscapeAndStripName(remappedName)); + body.Write(EscapeAndStripMethodName(remappedName)); body.EndMarker("vtbl"); } else diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs index e216d889..3fd39a09 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs @@ -970,7 +970,7 @@ private void VisitDeclRefExpr(DeclRefExpr declRefExpr) if (declRefExpr.Decl is FunctionDecl) { - escapedName = EscapeAndStripName(name); + escapedName = EscapeAndStripMethodName(name); } } diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index 6edb8098..76ee2b96 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -2326,13 +2326,9 @@ private static string EscapeName(string name) } } - private string EscapeAndStripName(string name) + private string EscapeAndStripMethodName(string name) { - if (name.StartsWith(_config.MethodPrefixToStrip, StringComparison.Ordinal)) - { - name = name[_config.MethodPrefixToStrip.Length..]; - } - + name = PrefixAndStrip(name, _config.MethodPrefixToStrip); return EscapeName(name); } @@ -6198,13 +6194,32 @@ private void ParenthesizeStmt(Stmt stmt) } } - private string PrefixAndStripName(string name, uint overloadIndex) + /// + /// Checks whether the specified name starts with a prefix, optionally trims a separator character following the prefix and returns the remainder. + /// + /// The name to strip. + /// The prefix to strip from . + /// Additional separator that may follow which should also be trimmed away. + /// + /// if it does not start with ; + /// otherwise, + /// the remainder of after stripping and all immediate following occurences of from it beginning. + /// + private static string PrefixAndStrip(string name, string prefix, char trimChar = '\0') { - if (name.StartsWith(_config.MethodPrefixToStrip, StringComparison.Ordinal)) + var nameSpan = name.AsSpan(); + if (nameSpan.StartsWith(prefix, StringComparison.Ordinal)) { - name = name[_config.MethodPrefixToStrip.Length..]; + nameSpan = nameSpan[prefix.Length..]; + nameSpan = nameSpan.TrimStart(trimChar); + return nameSpan.ToString(); + } + return name; } + private string PrefixAndStripMethodName(string name, uint overloadIndex) + { + name = PrefixAndStrip(name, _config.MethodPrefixToStrip); return $"_{name}{((overloadIndex != 0) ? overloadIndex.ToString(CultureInfo.InvariantCulture) : "")}"; } From c246b4161de63a4dbb49b948391700245c8768b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Sun, 19 May 2024 15:21:05 +0200 Subject: [PATCH 06/13] Automatic Whitespace fixup made by Visual Studio --- .../PInvokeGenerator.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index 76ee2b96..c6cbcc7c 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -1454,18 +1454,18 @@ static void GenerateTransparentStructs(PInvokeGenerator generator, Stream? strea return type.Contains('*', StringComparison.Ordinal) ? (8, 4, +1) : type switch { - "sbyte" => (1, 1, -1), - "byte" => (1, 1, +1), - "short" => (2, 2, -1), - "ushort" => (2, 2, +1), - "int" => (4, 4, -1), - "uint" => (4, 4, +1), - "nint" => (8, 4, -1), - "nuint" => (8, 4, +1), - "long" => (8, 8, -1), - "ulong" => (8, 8, +1), - _ => (0, 0, 0), - }; + "sbyte" => (1, 1, -1), + "byte" => (1, 1, +1), + "short" => (2, 2, -1), + "ushort" => (2, 2, +1), + "int" => (4, 4, -1), + "uint" => (4, 4, +1), + "nint" => (8, 4, -1), + "nuint" => (8, 4, +1), + "long" => (8, 8, -1), + "ulong" => (8, 8, +1), + _ => (0, 0, 0), + }; } static void OutputConversions(StreamWriter sw, string indentString, string name, string type, PInvokeGeneratorTransparentStructKind kind, string target) @@ -5403,7 +5403,7 @@ private static bool IsTransparentStructBoolean(PInvokeGeneratorTransparentStruct => kind is PInvokeGeneratorTransparentStructKind.Boolean; private static bool IsTransparentStructHandle(PInvokeGeneratorTransparentStructKind kind) - => kind is PInvokeGeneratorTransparentStructKind.Handle + => kind is PInvokeGeneratorTransparentStructKind.Handle or PInvokeGeneratorTransparentStructKind.HandleWin32; private static bool IsTransparentStructHexBased(PInvokeGeneratorTransparentStructKind kind) @@ -6149,7 +6149,7 @@ private bool NeedsReturnFixup(CXXMethodDecl cxxMethodDecl) private static bool NeedsNewKeyword(string name) { - return name.Equals("Equals",StringComparison.Ordinal) + return name.Equals("Equals", StringComparison.Ordinal) || name.Equals("GetHashCode", StringComparison.Ordinal) || name.Equals("GetType", StringComparison.Ordinal) || name.Equals("MemberwiseClone", StringComparison.Ordinal) @@ -6215,7 +6215,7 @@ private static string PrefixAndStrip(string name, string prefix, char trimChar = return nameSpan.ToString(); } return name; - } + } private string PrefixAndStripMethodName(string name, uint overloadIndex) { From f3237de8200e1ab0376dbadadb0438870ff27c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Fri, 24 May 2024 19:48:10 +0200 Subject: [PATCH 07/13] Refactor Enum member name escaping --- .../PInvokeGenerator.VisitDecl.cs | 6 +----- .../PInvokeGenerator.cs | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs index 974b98da..c538e421 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs @@ -273,7 +273,6 @@ private void VisitEnumConstantDecl(EnumConstantDecl enumConstantDecl) var accessSpecifier = AccessSpecifier.None; var name = GetRemappedCursorName(enumConstantDecl); - var escapedName = EscapeName(name); var typeName = GetTargetTypeName(enumConstantDecl, out _); var isAnonymousEnum = false; var parentName = ""; @@ -295,10 +294,7 @@ private void VisitEnumConstantDecl(EnumConstantDecl enumConstantDecl) parentName = _outputBuilder.Name; } - if (Config.StripEnumMemberTypeName) - { - escapedName = PrefixAndStrip(escapedName, parentName, trimChar: '_'); - } + var escapedName = EscapeAndStripEnumMemberName(name, parentName); var kind = isAnonymousEnum ? ValueKind.Primitive : ValueKind.Enumerator; var flags = ValueFlags.Constant; diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index c6cbcc7c..841aeb15 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -2332,6 +2332,20 @@ private string EscapeAndStripMethodName(string name) return EscapeName(name); } + private string EscapeAndStripEnumMemberName(string name, string enumTypeName) + { + if (Config.StripEnumMemberTypeName) + { + var escapedName = PrefixAndStrip(name, enumTypeName, trimChar: '_'); + if (escapedName.Length > 0 && char.IsAsciiDigit(escapedName[0])) + { + escapedName = '_' + escapedName; + } + return escapedName; + } + return EscapeName(name); + } + internal static string EscapeCharacter(char value) => value switch { '\0' => @"\0", '\\' => @"\\", 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 08/13] 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 09/13] 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; From 604d448f5a46b035befc4dc6ae90409561423701 Mon Sep 17 00:00:00 2001 From: myd7349 Date: Tue, 16 Jul 2024 18:01:06 +0800 Subject: [PATCH 10/13] Eliminate the ambiguity of "-h" option Currently, the "-h" option serves as an alias for both "--headerFile" and "--help" long options, causing ambiguity. --- sources/ClangSharpPInvokeGenerator/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/ClangSharpPInvokeGenerator/Program.cs b/sources/ClangSharpPInvokeGenerator/Program.cs index 69eb17ee..322ef1e4 100644 --- a/sources/ClangSharpPInvokeGenerator/Program.cs +++ b/sources/ClangSharpPInvokeGenerator/Program.cs @@ -28,7 +28,7 @@ public static class Program private static readonly string[] s_excludeOptionAliases = ["--exclude", "-e"]; private static readonly string[] s_fileOptionAliases = ["--file", "-f"]; private static readonly string[] s_fileDirectionOptionAliases = ["--file-directory", "-F"]; - private static readonly string[] s_headerOptionAliases = ["--headerFile", "-h"]; + private static readonly string[] s_headerOptionAliases = ["--headerFile", "-hf"]; private static readonly string[] s_includeOptionAliases = ["--include", "-i"]; private static readonly string[] s_includeDirectoryOptionAliases = ["--include-directory", "-I"]; private static readonly string[] s_languageOptionAliases = ["--language", "-x"]; From 6dad84b26c24b3518a32e28b972febbaa2cd58d8 Mon Sep 17 00:00:00 2001 From: myd7349 Date: Thu, 18 Jul 2024 18:51:54 +0800 Subject: [PATCH 11/13] Update ClangSharpPInvokeGenerator usage --- README.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 3a36108e..795e325a 100644 --- a/README.md +++ b/README.md @@ -155,32 +155,33 @@ Options: -e, --exclude A declaration name to exclude from binding generation. [] -f, --file A file to parse and generate bindings for. [] -F, --file-directory The base path for files to parse. [] - -h, --headerFile A file which contains the header to prefix every generated file with. [] + -hf, --headerFile A file which contains the header to prefix every generated file with. [] -i, --include A declaration name to include in binding generation. [] -I, --include-directory Add directory to include search path. [] - -x, --language Treat subsequent input files as having type . [default: c++] + -x, --language Treat subsequent input files as having type . [default: c++] -l, --libraryPath The string to use in the DllImport attribute used when generating bindings. [] -m, --methodClassName The name of the static class that will contain the generated method bindings. [default: Methods] -n, --namespace The namespace in which to place the generated bindings. [] - --nativeTypeNamesToStrip The contents to strip from the generated NativeTypeName attributes. -om, --output-mode The mode describing how the information collected from the headers are presented in the resultant bindings. [default: CSharp] - -o, --output The output location to write the generated bindings to. [] + -o, --output The output location to write the generated bindings to. [] -p, --prefixStrip The prefix to strip from the generated method bindings. [] + --nativeTypeNamesToStrip The contents to strip from the generated NativeTypeName attributes. [] -r, --remap A declaration name to be remapped to another name during binding generation. [] - -std, --std Language standard to compile for. [] + -std Language standard to compile for. [] -to, --test-output The output location to write the generated tests to. [] -t, --traverse A file name included either directly or indirectly by -f that should be traversed during binding generation. [] - -v, --version Prints the current version information for the tool and its native dependencies. + -v, --version Prints the current version information for the tool and its native dependencies. -was, --with-access-specifier An access specifier to be used with the given qualified or remapped declaration name during binding generation. Supports wildcards. [] -wa, --with-attribute An attribute to be added to the given remapped declaration name during binding generation. Supports wildcards. [] -wcc, --with-callconv A calling convention to be used for the given declaration during binding generation. Supports wildcards. [] -wc, --with-class A class to be used for the given remapped constant or function declaration name during binding generation. Supports wildcards. [] -wg, --with-guid A GUID to be used for the given declaration during binding generation. Supports wildcards. [] + -wl, --with-length A length to be used for the given declaration during binding generation. Supports wildcards. [] -wlb, --with-librarypath A library path to be used for the given declaration during binding generation. Supports wildcards. [] -wmi, --with-manual-import A remapped function name to be treated as a manual import during binding generation. Supports wildcards. [] -wn, --with-namespace A namespace to be used for the given remapped declaration name during binding generation. Supports wildcards. [] -wp, --with-packing Overrides the StructLayoutAttribute.Pack property for the given type. Supports wildcards. [] - -wsle, --with-setlasterror Add the SetLastError=true modifier to a given DllImport or UnmanagedFunctionPointer. Supports wildcards. [] + -wsle, --with-setlasterror Add the SetLastError=true modifier or SetsSystemLastError attribute to a given DllImport or UnmanagedFunctionPointer. Supports wildcards. [] -wsgct, --with-suppressgctransition Add the SuppressGCTransition calling convention to a given DllImport or UnmanagedFunctionPointer. Supports wildcards. [] -wts, --with-transparent-struct A remapped type name to be treated as a transparent wrapper during binding generation. Supports wildcards. [] -wt, --with-type A type to be used for the given enum declaration during binding generation. Supports wildcards. [] @@ -218,7 +219,7 @@ Options: # Exclusion Options exclude-anonymous-field-helpers The helper ref properties generated for fields in nested anonymous structs and unions should not be generated. - exclude-com-proxies Types recognized as COM proxies should not have bindings generated. Thes are currently function declarations ending with _UserFree, _UserMarshal, _UserSize, _UserUnmarshal, _Proxy, or _Stub. + exclude-com-proxies Types recognized as COM proxies should not have bindings generated. These are currently function declarations ending with _UserFree, _UserMarshal, _UserSize, _UserUnmarshal, _Proxy, or _Stub. exclude-default-remappings Default remappings for well known types should not be added. This currently includes intptr_t, ptrdiff_t, size_t, and uintptr_t exclude-empty-records Bindings for records that contain no members should not be generated. These are commonly encountered for opaque handle like types such as HWND. exclude-enum-operators Bindings for operators over enum types should not be generated. These are largely unnecessary in C# as the operators are available by default. @@ -243,14 +244,15 @@ Options: generate-callconv-member-function Instance function pointers should use [CallConvMemberFunction] where applicable. generate-cpp-attributes [CppAttributeList("")] should be generated to document the encountered C++ attributes. generate-disable-runtime-marshalling [assembly: DisableRuntimeMarshalling] should be generated. - generate-doc-includes <include> xml documentation tags should be generated for declarations. + generate-doc-includes xml documentation tags should be generated for declarations. generate-file-scoped-namespaces Namespaces should be scoped to the file to reduce nesting. generate-guid-member Types with an associated GUID should have a corresponding member generated. generate-helper-types Code files should be generated for various helper attributes and declared transparent structs. generate-macro-bindings Bindings for macro-definitions should be generated. This currently only works with value like macros and not function-like ones. generate-marker-interfaces Bindings for marker interfaces representing native inheritance hierarchies should be generated. - generate-native-bitfield-attribute [NativeBitfield(\"\", offset: #, length: #)] attribute should be generated to document the encountered bitfield layout. + generate-native-bitfield-attribute [NativeBitfield("", offset: #, length: #)] attribute should be generated to document the encountered bitfield layout. generate-native-inheritance-attribute [NativeInheritance("")] attribute should be generated to document the encountered C++ base type. + generate-generic-pointer-wrapper Pointer should be used for limited generic type support. generate-setslastsystemerror-attribute [SetsLastSystemError] attribute should be generated rather than using SetLastError = true. generate-template-bindings Bindings for template-definitions should be generated. This is currently experimental. generate-unmanaged-constants Unmanaged constants should be generated using static ref readonly properties. This is currently experimental. From e28f2d6d73e3ab681e88882e28c93a9f3ea9e720 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 21 Jul 2024 09:51:22 -0700 Subject: [PATCH 12/13] Roll back the version of System.CommandLine being used --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 7430ca20..6704befd 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -17,7 +17,7 @@ - + From fd868d6a4126bbc4079d430f576c33a96d52dd31 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 21 Jul 2024 10:16:35 -0700 Subject: [PATCH 13/13] Bump the build version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 7b102047..148ea050 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -47,7 +47,7 @@ 17.0.0 ClangSharp ClangSharp - 18.1.0 + 18.1.0.1 rc1 pr