Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow disabling of friendly overload generation #1117

Merged
merged 2 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Microsoft.Windows.CsWin32/Generator.FriendlyOverloads.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ private enum FriendlyOverloadOf

private IEnumerable<MethodDeclarationSyntax> DeclareFriendlyOverloads(MethodDefinition methodDefinition, MethodDeclarationSyntax externMethodDeclaration, NameSyntax declaringTypeName, FriendlyOverloadOf overloadOf, HashSet<string> helperMethodsAdded)
{
if (!this.options.FriendlyOverloads.Enabled)
{
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))
////{
Expand Down
17 changes: 17 additions & 0 deletions src/Microsoft.Windows.CsWin32/GeneratorOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ public record GeneratorOptions
/// <value>The default value is <see langword="true"/>.</value>
public bool AllowMarshaling { get; init; } = true;

/// <summary>
/// Gets options related to friendly overloads.
/// </summary>
public FriendlyOverloadOptions FriendlyOverloads { get; init; } = new();

/// <summary>
/// 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.
Expand Down Expand Up @@ -83,4 +88,16 @@ public record ComInteropOptions
/// </remarks>
public bool UseIntPtrForComOutPointers { get; init; }
}

/// <summary>
/// Options for friendly overloads.
/// </summary>
public record FriendlyOverloadOptions
{
/// <summary>
/// Gets a value indicating whether to generate method overloads that may be easier to consume or be more idiomatic C#.
/// </summary>
/// <value>The default value is <see langword="true" />.</value>
public bool Enabled { get; init; } = true;
}
}
11 changes: 11 additions & 0 deletions src/Microsoft.Windows.CsWin32/settings.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@
"type": "boolean",
"default": true
},
"friendlyOverloads": {
"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.",
"type": "boolean",
Expand Down
33 changes: 22 additions & 11 deletions test/Microsoft.Windows.CsWin32.Tests/FullGenerationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 = new() { Enabled = 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(
Expand All @@ -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.
Expand All @@ -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]
Expand All @@ -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<IGenerator> 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<IGenerator> generationCommands)
{
this.compilation = this.starterCompilations[targetFramework];
this.compilation = this.compilation.WithOptions(this.compilation.Options.WithPlatform(platform));

Expand Down