Skip to content

Commit

Permalink
[Extensions] Add AOT compatibility attributes to Microsoft.Extensions…
Browse files Browse the repository at this point in the history
….Azure (#41936)

* [Extensions] Add AOT compatibility attributes

Annotate the Microsoft.Extensions.Azure library for trimming/AOT compatibility.

Also add a few suppressions to Azure.Identity's EventSource that showed up when I was using Microsoft.Extensions.Azure in a real-world app.

* Add expected AOT warnings for DiagnosticSource.Write

These warnings went away with dotnet/runtime#76109, which was backported to .NET 7.0.3, but the CI build machines are still using an older 7.0.x, which don't have these warnings fixed. Adding expected warnings entries for them until we take a new .NET version in CI.

* Fix CI errors.

* Add CheckAOTCompat to Azure.Identity

* Fix up Identity expected aot warnings for CI.
  • Loading branch information
eerhardt authored Feb 14, 2024
1 parent 89d810e commit cf3d2b8
Show file tree
Hide file tree
Showing 18 changed files with 143 additions and 25 deletions.
1 change: 1 addition & 0 deletions sdk/core/Azure.Core/api/Azure.Core.net6.0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,7 @@ public partial interface IAzureClientFactoryBuilder
}
public partial interface IAzureClientFactoryBuilderWithConfiguration<in TConfiguration> : Azure.Core.Extensions.IAzureClientFactoryBuilder
{
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Binding strongly typed objects to configuration values is not supported with trimming. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
Azure.Core.Extensions.IAzureClientBuilder<TClient, TOptions> RegisterClientFactory<TClient, TOptions>(TConfiguration configuration) where TOptions : class;
}
public partial interface IAzureClientFactoryBuilderWithCredential
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using System;
using System.Diagnostics.CodeAnalysis;

namespace Azure.Core.Extensions
{
Expand All @@ -17,6 +18,6 @@ public interface IAzureClientFactoryBuilder
/// <typeparam name="TOptions">The client options type used the client.</typeparam>
/// <param name="clientFactory">The factory, that given the instance of options, returns a client instance.</param>
/// <returns><see cref="IAzureClientBuilder{TClient,TOptions}"/> that allows customizing the client registration.</returns>
IAzureClientBuilder<TClient, TOptions> RegisterClientFactory<TClient, TOptions>(Func<TOptions, TClient> clientFactory) where TOptions : class;
IAzureClientBuilder<TClient, TOptions> RegisterClientFactory<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TOptions>(Func<TOptions, TClient> clientFactory) where TOptions : class;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Diagnostics.CodeAnalysis;

namespace Azure.Core.Extensions
{
/// <summary>
Expand All @@ -15,6 +17,8 @@ public interface IAzureClientFactoryBuilderWithConfiguration<in TConfiguration>
/// <typeparam name="TOptions">The client options type used the client.</typeparam>
/// <param name="configuration">Instance of <typeparamref name="TConfiguration"/> to use.</param>
/// <returns><see cref="IAzureClientBuilder{TClient,TOptions}"/> that allows customizing the client registration.</returns>
IAzureClientBuilder<TClient, TOptions> RegisterClientFactory<TClient, TOptions>(TConfiguration configuration) where TOptions : class;
[RequiresUnreferencedCode("Binding strongly typed objects to configuration values is not supported with trimming. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
[RequiresDynamicCode("Binding strongly typed objects to configuration values requires generating dynamic code at runtime, for example instantiating generic types. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
IAzureClientBuilder<TClient, TOptions> RegisterClientFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TOptions>(TConfiguration configuration) where TOptions : class;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using System;
using System.Diagnostics.CodeAnalysis;

namespace Azure.Core.Extensions
{
Expand All @@ -18,6 +19,6 @@ public interface IAzureClientFactoryBuilderWithCredential
/// <param name="clientFactory">The factory, that given the instance of options and credential, returns a client instance.</param>
/// <param name="requiresCredential">Specifies whether the credential is optional (client supports anonymous authentication).</param>
/// <returns><see cref="IAzureClientBuilder{TClient,TOptions}"/> that allows customizing the client registration.</returns>
IAzureClientBuilder<TClient, TOptions> RegisterClientFactory<TClient, TOptions>(Func<TOptions, TokenCredential, TClient> clientFactory, bool requiresCredential = true) where TOptions : class;
IAzureClientBuilder<TClient, TOptions> RegisterClientFactory<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TOptions>(Func<TOptions, TokenCredential, TClient> clientFactory, bool requiresCredential = true) where TOptions : class;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.Diagnostics.CodeAnalysis;

namespace Microsoft.Extensions.Azure
{
Expand Down Expand Up @@ -78,6 +79,8 @@ public static IAzureClientBuilder<TClient, TOptions> ConfigureOptions<TClient, T
/// <param name="builder">The client builder instance.</param>
/// <param name="configuration">The configuration instance to use.</param>
/// <returns>The client builder instance.</returns>
[RequiresUnreferencedCode("Binding strongly typed objects to configuration values is not supported with trimming. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
[RequiresDynamicCode("Binding strongly typed objects to configuration values requires generating dynamic code at runtime, for example instantiating generic types. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
public static IAzureClientBuilder<TClient, TOptions> ConfigureOptions<TClient, TOptions>(this IAzureClientBuilder<TClient, TOptions> builder, IConfiguration configuration) where TOptions : class
{
return builder.ConfigureOptions(options => configuration.Bind(options));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using System;
using System.Diagnostics.CodeAnalysis;
using Azure.Core;
using Azure.Core.Extensions;
using Microsoft.Extensions.Configuration;
Expand All @@ -25,12 +26,16 @@ internal AzureClientFactoryBuilder(IServiceCollection serviceCollection)
_serviceCollection = serviceCollection;
}

IAzureClientBuilder<TClient, TOptions> IAzureClientFactoryBuilder.RegisterClientFactory<TClient, TOptions>(Func<TOptions, TClient> clientFactory)
IAzureClientBuilder<TClient, TOptions> IAzureClientFactoryBuilder.RegisterClientFactory<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TOptions>(
Func<TOptions, TClient> clientFactory)
{
return ((IAzureClientFactoryBuilderWithCredential)this).RegisterClientFactory<TClient, TOptions>((options, _) => clientFactory(options));
}

IAzureClientBuilder<TClient, TOptions> IAzureClientFactoryBuilderWithConfiguration<IConfiguration>.RegisterClientFactory<TClient, TOptions>(IConfiguration configuration)
[RequiresUnreferencedCode("Binding strongly typed objects to configuration values is not supported with trimming. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
[RequiresDynamicCode("Binding strongly typed objects to configuration values requires generating dynamic code at runtime, for example instantiating generic types. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
IAzureClientBuilder<TClient, TOptions> IAzureClientFactoryBuilderWithConfiguration<IConfiguration>.RegisterClientFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TOptions>(
IConfiguration configuration)
{
var credentialsFromConfig = ClientFactory.CreateCredential(configuration);
var clientBuilder =((IAzureClientFactoryBuilderWithCredential)this).RegisterClientFactory<TClient, TOptions>(
Expand Down Expand Up @@ -73,6 +78,8 @@ public AzureClientFactoryBuilder ConfigureDefaults(Action<ClientOptions, IServic
/// </summary>
/// <param name="configuration">The configuration instance.</param>
/// <returns>This instance.</returns>
[RequiresUnreferencedCode("Binding strongly typed objects to configuration values is not supported with trimming. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
[RequiresDynamicCode("Binding strongly typed objects to configuration values requires generating dynamic code at runtime, for example instantiating generic types. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
public AzureClientFactoryBuilder ConfigureDefaults(IConfiguration configuration)
{
ConfigureDefaults(configuration.Bind);
Expand All @@ -87,7 +94,8 @@ public AzureClientFactoryBuilder ConfigureDefaults(IConfiguration configuration)
return this;
}

IAzureClientBuilder<TClient, TOptions> IAzureClientFactoryBuilderWithCredential.RegisterClientFactory<TClient, TOptions>(Func<TOptions, TokenCredential, TClient> clientFactory, bool requiresCredential)
IAzureClientBuilder<TClient, TOptions> IAzureClientFactoryBuilderWithCredential.RegisterClientFactory<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TOptions>(
Func<TOptions, TokenCredential, TClient> clientFactory, bool requiresCredential)
{
return RegisterClientFactory<TClient, TOptions>((options, credential, _) => clientFactory(options, credential), requiresCredential);
}
Expand Down Expand Up @@ -119,7 +127,9 @@ public AzureClientFactoryBuilder UseCredential(Func<IServiceProvider, TokenCrede
/// <typeparam name="TClient">The type of the client.</typeparam>
/// <typeparam name="TOptions">The type of the client options.</typeparam>
/// <returns>The <see cref="IAzureClientBuilder{TClient, TOptions}"/> to allow client configuration.</returns>
public IAzureClientBuilder<TClient, TOptions> AddClient<TClient, TOptions>(Func<TOptions, TClient> factory) where TOptions : class
public IAzureClientBuilder<TClient, TOptions> AddClient<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TOptions>(
Func<TOptions, TClient> factory)
where TOptions : class
{
return RegisterClientFactory<TClient, TOptions>((options, _, _) => factory(options), false);
}
Expand All @@ -130,7 +140,9 @@ public IAzureClientBuilder<TClient, TOptions> AddClient<TClient, TOptions>(Func<
/// <typeparam name="TClient">The type of the client.</typeparam>
/// <typeparam name="TOptions">The type of the client options.</typeparam>
/// <returns>The <see cref="IAzureClientBuilder{TClient, TOptions}"/> to allow client configuration.</returns>
public IAzureClientBuilder<TClient, TOptions> AddClient<TClient, TOptions>(Func<TOptions, TokenCredential, TClient> factory) where TOptions : class
public IAzureClientBuilder<TClient, TOptions> AddClient<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TOptions>(
Func<TOptions, TokenCredential, TClient> factory)
where TOptions : class
{
return RegisterClientFactory<TClient, TOptions>((options, credential, _) => factory(options, credential), true);
}
Expand All @@ -142,7 +154,9 @@ public IAzureClientBuilder<TClient, TOptions> AddClient<TClient, TOptions>(Func<
/// <typeparam name="TClient">The type of the client.</typeparam>
/// <typeparam name="TOptions">The type of the client options.</typeparam>
/// <returns>The <see cref="IAzureClientBuilder{TClient, TOptions}"/> to allow client configuration.</returns>
public IAzureClientBuilder<TClient, TOptions> AddClient<TClient, TOptions>(Func<TOptions, IServiceProvider, TClient> factory) where TOptions : class
public IAzureClientBuilder<TClient, TOptions> AddClient<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TOptions>(
Func<TOptions, IServiceProvider, TClient> factory)
where TOptions : class
{
return RegisterClientFactory<TClient, TOptions>((options, _, provider) => factory(options, provider), true);
}
Expand All @@ -154,12 +168,17 @@ public IAzureClientBuilder<TClient, TOptions> AddClient<TClient, TOptions>(Func<
/// <typeparam name="TClient">The type of the client.</typeparam>
/// <typeparam name="TOptions">The type of the client options.</typeparam>
/// <returns>The <see cref="IAzureClientBuilder{TClient, TOptions}"/> to allow client configuration.</returns>
public IAzureClientBuilder<TClient, TOptions> AddClient<TClient, TOptions>(Func<TOptions, TokenCredential, IServiceProvider, TClient> factory) where TOptions : class
public IAzureClientBuilder<TClient, TOptions> AddClient<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TOptions>(
Func<TOptions, TokenCredential, IServiceProvider, TClient> factory)
where TOptions : class
{
return RegisterClientFactory<TClient, TOptions>((options, credential, provider) => factory(options, credential, provider), true);
}

private IAzureClientBuilder<TClient, TOptions> RegisterClientFactory<TClient, TOptions>(Func<TOptions, TokenCredential, IServiceProvider, TClient> clientFactory, bool requiresCredential) where TOptions : class
private IAzureClientBuilder<TClient, TOptions> RegisterClientFactory<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TOptions>(
Func<TOptions, TokenCredential, IServiceProvider, TClient> clientFactory,
bool requiresCredential)
where TOptions : class
{
var clientRegistration = new ClientRegistration<TClient>(DefaultClientName, requiresCredential, (provider, options, credential) => clientFactory((TOptions) options, credential, provider));
_serviceCollection.AddSingleton(clientRegistration);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using System;
using System.Diagnostics.CodeAnalysis;
using Azure.Core;
using Microsoft.Extensions.Configuration;

Expand All @@ -24,7 +25,12 @@ public abstract class AzureComponentFactory
/// <param name="serviceVersion">The value of ServiceVersion enum to use, null to use the default.</param>
/// <param name="configuration">The <see cref="IConfiguration"/> instance to apply to options.</param>
/// <returns>A new instance of <paramref name="optionsType"/>.</returns>
public abstract object CreateClientOptions(Type optionsType, object serviceVersion, IConfiguration configuration);
[RequiresUnreferencedCode("Binding strongly typed objects to configuration values is not supported with trimming. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
[RequiresDynamicCode("Binding strongly typed objects to configuration values requires generating dynamic code at runtime, for example instantiating generic types. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
public abstract object CreateClientOptions(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type optionsType,
object serviceVersion,
IConfiguration configuration);

/// <summary>
/// Creates a new client instance using the provided configuration to map constructor parameters from.
Expand All @@ -35,6 +41,11 @@ public abstract class AzureComponentFactory
/// <param name="credential">The <see cref="TokenCredential"/> object to use if required by constructor, if null no .</param>
/// <param name="clientOptions">The client </param>
/// <returns></returns>
public abstract object CreateClient(Type clientType, IConfiguration configuration, TokenCredential credential, object clientOptions);
[RequiresUnreferencedCode("Binding strongly typed objects to configuration values is not supported with trimming. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
public abstract object CreateClient(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type clientType,
IConfiguration configuration,
TokenCredential credential,
object clientOptions);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;

namespace Microsoft.Extensions.Azure
{
internal class AzureClientFactory<TClient, TOptions>: IAzureClientFactory<TClient>, IDisposable, IAsyncDisposable
internal class AzureClientFactory<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TOptions>
: IAzureClientFactory<TClient>, IDisposable, IAsyncDisposable
{
private readonly Dictionary<string, ClientRegistration<TClient>> _clientRegistrations;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using System;
using System.Diagnostics.CodeAnalysis;
using Azure.Core;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
Expand All @@ -25,7 +26,12 @@ public override TokenCredential CreateTokenCredential(IConfiguration configurati
return ClientFactory.CreateCredential(configuration) ?? _globalOptions.CurrentValue.CredentialFactory(_serviceProvider);
}

public override object CreateClientOptions(Type optionsType, object serviceVersion, IConfiguration configuration)
[RequiresUnreferencedCode("Binding strongly typed objects to configuration values is not supported with trimming. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
[RequiresDynamicCode("Binding strongly typed objects to configuration values requires generating dynamic code at runtime, for example instantiating generic types. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
public override object CreateClientOptions(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type optionsType,
object serviceVersion,
IConfiguration configuration)
{
if (optionsType == null) throw new ArgumentNullException(nameof(optionsType));
var options = ClientFactory.CreateClientOptions(serviceVersion, optionsType);
Expand All @@ -41,7 +47,12 @@ public override object CreateClientOptions(Type optionsType, object serviceVersi
return options;
}

public override object CreateClient(Type clientType, IConfiguration configuration, TokenCredential credential, object clientOptions)
[RequiresUnreferencedCode("Binding strongly typed objects to configuration values is not supported with trimming. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
public override object CreateClient(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type clientType,
IConfiguration configuration,
TokenCredential credential,
object clientOptions)
{
if (clientType == null) throw new ArgumentNullException(nameof(clientType));
if (configuration == null) throw new ArgumentNullException(nameof(configuration));
Expand Down
Loading

0 comments on commit cf3d2b8

Please sign in to comment.