From 0b71733b5f51bd56767673f23e12de3a28dc4000 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Fri, 12 Jan 2024 13:56:48 -0700 Subject: [PATCH 1/2] Add switch for deactivating friendly overloads --- .../Generator.FriendlyOverloads.cs | 5 +++ .../GeneratorOptions.cs | 5 +++ .../settings.schema.json | 5 +++ .../FullGenerationTests.cs | 33 ++++++++++++------- 4 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/Microsoft.Windows.CsWin32/Generator.FriendlyOverloads.cs b/src/Microsoft.Windows.CsWin32/Generator.FriendlyOverloads.cs index d2e2c5dd..6034d536 100644 --- a/src/Microsoft.Windows.CsWin32/Generator.FriendlyOverloads.cs +++ b/src/Microsoft.Windows.CsWin32/Generator.FriendlyOverloads.cs @@ -14,6 +14,11 @@ private enum FriendlyOverloadOf private IEnumerable DeclareFriendlyOverloads(MethodDefinition methodDefinition, MethodDeclarationSyntax externMethodDeclaration, NameSyntax declaringTypeName, FriendlyOverloadOf overloadOf, HashSet helperMethodsAdded) { + if (!this.options.FriendlyOverloads) + { + yield break; + } + // If/when we ever need helper methods for the friendly overloads again, they can be added when used with code like this: ////if (helperMethodsAdded.Add(SomeHelperMethodName)) ////{ diff --git a/src/Microsoft.Windows.CsWin32/GeneratorOptions.cs b/src/Microsoft.Windows.CsWin32/GeneratorOptions.cs index 67ed23fb..75717c66 100644 --- a/src/Microsoft.Windows.CsWin32/GeneratorOptions.cs +++ b/src/Microsoft.Windows.CsWin32/GeneratorOptions.cs @@ -41,6 +41,11 @@ public record GeneratorOptions /// The default value is . public bool AllowMarshaling { get; init; } = true; + /// + /// Gets a value indicating whether to generate method overloads that may be easier to consume or be more idiomatic C#. + /// + public bool FriendlyOverloads { get; init; } = true; + /// /// Gets a value indicating whether to generate APIs judged to be unnecessary or redundant given the target framework /// because the project multi-targets to frameworks that need the APIs consistently for easier coding. diff --git a/src/Microsoft.Windows.CsWin32/settings.schema.json b/src/Microsoft.Windows.CsWin32/settings.schema.json index cca80f53..80411459 100644 --- a/src/Microsoft.Windows.CsWin32/settings.schema.json +++ b/src/Microsoft.Windows.CsWin32/settings.schema.json @@ -37,6 +37,11 @@ "type": "boolean", "default": true }, + "friendlyOverloads": { + "description": "A value indicating whether to generate method overloads that may be easier to consume or be more idiomatic C#. These may use fewer pointers, accept or return SafeHandles, etc.", + "type": "boolean", + "default": true + }, "multiTargetingFriendlyAPIs": { "description": "A value indicating whether to generate APIs judged to be unnecessary or redundant given the target framework. This is useful for multi-targeting projects that need a consistent set of APIs across target frameworks to avoid too many conditional compilation regions.", "type": "boolean", diff --git a/test/Microsoft.Windows.CsWin32.Tests/FullGenerationTests.cs b/test/Microsoft.Windows.CsWin32.Tests/FullGenerationTests.cs index 1c7ea47b..a3d711a1 100644 --- a/test/Microsoft.Windows.CsWin32.Tests/FullGenerationTests.cs +++ b/test/Microsoft.Windows.CsWin32.Tests/FullGenerationTests.cs @@ -18,6 +18,13 @@ public FullGenerationTests(ITestOutputHelper logger) { } + [Trait("TestCategory", "FailsInCloudTest")] // these take ~4GB of memory to run. + [Fact] + public void Everything_NoFriendlyOverloads() + { + this.TestHelper(new GeneratorOptions { FriendlyOverloads = false }, Platform.X64, "net7.0", generator => generator.GenerateAll(CancellationToken.None)); + } + [Trait("TestCategory", "FailsInCloudTest")] // these take ~4GB of memory to run. [Theory, PairwiseData] public void Everything( @@ -26,7 +33,7 @@ public FullGenerationTests(ITestOutputHelper logger) [CombinatorialMemberData(nameof(AnyCpuArchitectures))] Platform platform, [CombinatorialMemberData(nameof(TFMDataNoNetFx35))] string tfm) { - this.TestHelper(marshaling, useIntPtrForComOutPtr, platform, tfm, generator => generator.GenerateAll(CancellationToken.None)); + this.TestHelper(OptionsForMarshaling(marshaling, useIntPtrForComOutPtr), platform, tfm, generator => generator.GenerateAll(CancellationToken.None)); } [Trait("TestCategory", "FailsInCloudTest")] // these take ~4GB of memory to run. @@ -36,13 +43,13 @@ public FullGenerationTests(ITestOutputHelper logger) bool useIntPtrForComOutPtr, [CombinatorialMemberData(nameof(TFMDataNoNetFx35))] string tfm) { - this.TestHelper(marshaling, useIntPtrForComOutPtr, Platform.X64, tfm, generator => generator.GenerateAllInteropTypes(CancellationToken.None)); + this.TestHelper(OptionsForMarshaling(marshaling, useIntPtrForComOutPtr), Platform.X64, tfm, generator => generator.GenerateAllInteropTypes(CancellationToken.None)); } [Fact] public void Constants() { - this.TestHelper(marshaling: MarshalingOptions.FullMarshaling, useIntPtrForComOutPtr: false, Platform.X64, DefaultTFM, generator => generator.GenerateAllConstants(CancellationToken.None)); + this.TestHelper(new GeneratorOptions(), Platform.X64, DefaultTFM, generator => generator.GenerateAllConstants(CancellationToken.None)); } [Theory, PairwiseData] @@ -52,23 +59,27 @@ public void Constants() [CombinatorialMemberData(nameof(SpecificCpuArchitectures))] Platform platform, [CombinatorialMemberData(nameof(TFMDataNoNetFx35))] string tfm) { - this.TestHelper(marshaling, useIntPtrForComOutPtr, platform, tfm, generator => generator.GenerateAllExternMethods(CancellationToken.None)); + this.TestHelper(OptionsForMarshaling(marshaling, useIntPtrForComOutPtr), platform, tfm, generator => generator.GenerateAllExternMethods(CancellationToken.None)); } [Fact] public void Macros() { - this.TestHelper(marshaling: MarshalingOptions.FullMarshaling, useIntPtrForComOutPtr: false, Platform.X64, DefaultTFM, generator => generator.GenerateAllMacros(CancellationToken.None)); + this.TestHelper(new GeneratorOptions(), Platform.X64, DefaultTFM, generator => generator.GenerateAllMacros(CancellationToken.None)); } - private void TestHelper(MarshalingOptions marshaling, bool useIntPtrForComOutPtr, Platform platform, string targetFramework, Action generationCommands) + private static GeneratorOptions OptionsForMarshaling(MarshalingOptions marshaling, bool useIntPtrForComOutPtr) => new() { - var generatorOptions = new GeneratorOptions + AllowMarshaling = marshaling >= MarshalingOptions.MarshalingWithoutSafeHandles, + UseSafeHandles = marshaling == MarshalingOptions.FullMarshaling, + ComInterop = new() { - AllowMarshaling = marshaling >= MarshalingOptions.MarshalingWithoutSafeHandles, - UseSafeHandles = marshaling == MarshalingOptions.FullMarshaling, - ComInterop = new() { UseIntPtrForComOutPointers = useIntPtrForComOutPtr }, - }; + UseIntPtrForComOutPointers = useIntPtrForComOutPtr, + }, + }; + + private void TestHelper(GeneratorOptions generatorOptions, Platform platform, string targetFramework, Action generationCommands) + { this.compilation = this.starterCompilations[targetFramework]; this.compilation = this.compilation.WithOptions(this.compilation.Options.WithPlatform(platform)); From 429d8817f280bd3c37e23e63b400bcdb775fd594 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Fri, 12 Jan 2024 14:04:31 -0700 Subject: [PATCH 2/2] Expand friendly overload switch to an options object so we can add more options later --- .../Generator.FriendlyOverloads.cs | 2 +- .../GeneratorOptions.cs | 16 ++++++++++++++-- .../settings.schema.json | 12 +++++++++--- .../FullGenerationTests.cs | 2 +- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.Windows.CsWin32/Generator.FriendlyOverloads.cs b/src/Microsoft.Windows.CsWin32/Generator.FriendlyOverloads.cs index 6034d536..ed9c86c5 100644 --- a/src/Microsoft.Windows.CsWin32/Generator.FriendlyOverloads.cs +++ b/src/Microsoft.Windows.CsWin32/Generator.FriendlyOverloads.cs @@ -14,7 +14,7 @@ private enum FriendlyOverloadOf private IEnumerable DeclareFriendlyOverloads(MethodDefinition methodDefinition, MethodDeclarationSyntax externMethodDeclaration, NameSyntax declaringTypeName, FriendlyOverloadOf overloadOf, HashSet helperMethodsAdded) { - if (!this.options.FriendlyOverloads) + if (!this.options.FriendlyOverloads.Enabled) { yield break; } diff --git a/src/Microsoft.Windows.CsWin32/GeneratorOptions.cs b/src/Microsoft.Windows.CsWin32/GeneratorOptions.cs index 75717c66..a060e415 100644 --- a/src/Microsoft.Windows.CsWin32/GeneratorOptions.cs +++ b/src/Microsoft.Windows.CsWin32/GeneratorOptions.cs @@ -42,9 +42,9 @@ public record GeneratorOptions public bool AllowMarshaling { get; init; } = true; /// - /// Gets a value indicating whether to generate method overloads that may be easier to consume or be more idiomatic C#. + /// Gets options related to friendly overloads. /// - public bool FriendlyOverloads { get; init; } = true; + public FriendlyOverloadOptions FriendlyOverloads { get; init; } = new(); /// /// Gets a value indicating whether to generate APIs judged to be unnecessary or redundant given the target framework @@ -88,4 +88,16 @@ public record ComInteropOptions /// public bool UseIntPtrForComOutPointers { get; init; } } + + /// + /// Options for friendly overloads. + /// + public record FriendlyOverloadOptions + { + /// + /// Gets a value indicating whether to generate method overloads that may be easier to consume or be more idiomatic C#. + /// + /// The default value is . + public bool Enabled { get; init; } = true; + } } diff --git a/src/Microsoft.Windows.CsWin32/settings.schema.json b/src/Microsoft.Windows.CsWin32/settings.schema.json index 80411459..8ef51b15 100644 --- a/src/Microsoft.Windows.CsWin32/settings.schema.json +++ b/src/Microsoft.Windows.CsWin32/settings.schema.json @@ -38,9 +38,15 @@ "default": true }, "friendlyOverloads": { - "description": "A value indicating whether to generate method overloads that may be easier to consume or be more idiomatic C#. These may use fewer pointers, accept or return SafeHandles, etc.", - "type": "boolean", - "default": true + "description": "An object with properties that control generation of friendly overloads.", + "type": "object", + "properties": { + "enabled": { + "description": "A value indicating whether to generate method overloads that may be easier to consume or be more idiomatic C#. These may use fewer pointers, accept or return SafeHandles, etc.", + "type": "boolean", + "default": true + } + } }, "multiTargetingFriendlyAPIs": { "description": "A value indicating whether to generate APIs judged to be unnecessary or redundant given the target framework. This is useful for multi-targeting projects that need a consistent set of APIs across target frameworks to avoid too many conditional compilation regions.", diff --git a/test/Microsoft.Windows.CsWin32.Tests/FullGenerationTests.cs b/test/Microsoft.Windows.CsWin32.Tests/FullGenerationTests.cs index a3d711a1..d1a0cc71 100644 --- a/test/Microsoft.Windows.CsWin32.Tests/FullGenerationTests.cs +++ b/test/Microsoft.Windows.CsWin32.Tests/FullGenerationTests.cs @@ -22,7 +22,7 @@ public FullGenerationTests(ITestOutputHelper logger) [Fact] public void Everything_NoFriendlyOverloads() { - this.TestHelper(new GeneratorOptions { FriendlyOverloads = false }, Platform.X64, "net7.0", generator => generator.GenerateAll(CancellationToken.None)); + this.TestHelper(new GeneratorOptions { FriendlyOverloads = new() { Enabled = false } }, Platform.X64, "net7.0", generator => generator.GenerateAll(CancellationToken.None)); } [Trait("TestCategory", "FailsInCloudTest")] // these take ~4GB of memory to run.