Skip to content
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.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
268 changes: 268 additions & 0 deletions plugins/dotnet/skills/dotnet-aot-compat/SKILL.md

Large diffs are not rendered by default.

43 changes: 43 additions & 0 deletions plugins/dotnet/skills/dotnet-aot-compat/references/polyfills.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Polyfills for Older TFMs

`DynamicallyAccessedMembersAttribute` shipped in .NET 5. For projects targeting netstandard2.0 or net472, you need a polyfill. The trimmer recognizes the attribute by name, so a local copy works:

```csharp
#if !NET
namespace System.Diagnostics.CodeAnalysis
{
[AttributeUsage(AttributeTargets.Field | AttributeTargets.ReturnValue |
AttributeTargets.GenericParameter | AttributeTargets.Parameter |
AttributeTargets.Property, Inherited = false)]
internal sealed class DynamicallyAccessedMembersAttribute : Attribute
{
public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes memberTypes)
=> MemberTypes = memberTypes;
public DynamicallyAccessedMemberTypes MemberTypes { get; }
}

[Flags]
internal enum DynamicallyAccessedMemberTypes
{
None = 0,
PublicParameterlessConstructor = 0x0001,
PublicConstructors = 0x0002 | PublicParameterlessConstructor,
NonPublicConstructors = 0x0004,
PublicMethods = 0x0008,
NonPublicMethods = 0x0010,
PublicFields = 0x0020,
NonPublicFields = 0x0040,
PublicNestedTypes = 0x0080,
NonPublicNestedTypes = 0x0100,
PublicProperties = 0x0200,
NonPublicProperties = 0x0400,
PublicEvents = 0x0800,
NonPublicEvents = 0x1000,
Interfaces = 0x2000,
All = ~None // Discouraged — prefer specific flags
}
}
#endif
```

Similarly for `RequiresUnreferencedCodeAttribute` and `UnconditionalSuppressMessageAttribute` if needed on older TFMs.
2 changes: 2 additions & 0 deletions tests/dotnet/dotnet-aot-compat/after/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bin/
obj/
315 changes: 315 additions & 0 deletions tests/dotnet/dotnet-aot-compat/after/ArmClient.cs

Large diffs are not rendered by default.

81 changes: 81 additions & 0 deletions tests/dotnet/dotnet-aot-compat/after/ArmClientOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text.Json;
using Azure.Core;
using Azure.ResourceManager.Resources;
using Azure.ResourceManager;

namespace Azure.ResourceManager
{
/// <summary>
/// A class representing Azure resource manager client options.
/// </summary>
#pragma warning disable AZC0008 // ClientOptions should have a nested enum called ServiceVersion
public sealed class ArmClientOptions : ClientOptions
#pragma warning restore AZC0008 // ClientOptions should have a nested enum called ServiceVersion
{
internal IDictionary<ResourceType, string> ResourceApiVersionOverrides { get; } = new Dictionary<ResourceType, string>();

/// <summary>
/// Gets or sets Azure cloud environment.
/// </summary>
public ArmEnvironment? Environment { get; set; }

/// <summary>
/// Sets the api version to use for a given resource type.
/// To find which API Versions are available in your environment you can use the <see cref="ResourceProviderResource.Get"/> method
/// for the provider namespace you are interested in.
/// </summary>
/// <param name="resourceType"> The resource type to set the version for. To determine the appropriate value, you can refer to the corresponding documentation or XML documentation comments of the API. Then, get its resource type from the Resource's ResourceType field.</param>
/// <param name="apiVersion"> The api version to use. </param>
public void SetApiVersion(ResourceType resourceType, string apiVersion)
{
Argument.AssertNotNullOrEmpty(apiVersion, nameof(apiVersion));

ResourceApiVersionOverrides[resourceType] = apiVersion;
}

/// <summary>
/// Sets the api versions from an Azure Stack profile.
/// </summary>
public void SetApiVersionsFromProfile(AzureStackProfile profile)
{
var assembly = Assembly.GetExecutingAssembly();
using (Stream stream = assembly.GetManifestResourceStream(profile.GetManifestName()))
{
var span = BinaryData.FromStream(stream).ToMemory().Span;
var allProfile = JsonSerializer.Deserialize(span, ResourceManagerJsonContext.Default.DictionaryStringDictionaryStringJsonElement);
var armProfile = allProfile["resource-manager"];
foreach (var keyValuePair in armProfile)
{
var namespaceName = keyValuePair.Key;
var element = keyValuePair.Value;

foreach (var apiVersionProperty in element.EnumerateObject())
{
var apiVersion = apiVersionProperty.Name;
foreach (var resourceTypeItem in apiVersionProperty.Value.EnumerateArray())
{
string resourceTypeName = default;
foreach (var property in resourceTypeItem.EnumerateObject())
{
if (property.NameEquals("resourceType"))
{
resourceTypeName = property.Value.GetString();
break;
}
}
var resourceType = $"{namespaceName}/{resourceTypeName}";
ResourceApiVersionOverrides[resourceType] = apiVersion;
}
}
}
}
}
}
}
84 changes: 84 additions & 0 deletions tests/dotnet/dotnet-aot-compat/after/ArmCollection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Concurrent;
using System.ComponentModel;
using Azure.Core;
using Azure.Core.Pipeline;

namespace Azure.ResourceManager
{
/// <summary>
/// Base class representing collection of resources.
/// </summary>
public abstract class ArmCollection
{
private readonly ConcurrentDictionary<Type, object> _clientCache = new ConcurrentDictionary<Type, object>();

/// <summary>
/// Initializes a new instance of the <see cref="ArmCollection"/> class for mocking.
/// </summary>
protected ArmCollection()
{
}

/// <summary>
/// Initializes a new instance of the <see cref="ArmCollection"/> class.
/// </summary>
/// <param name="client"> The client to copy settings from. </param>
/// <param name="id"> The id of the parent for the collection. </param>
protected ArmCollection(ArmClient client, ResourceIdentifier id)
{
Argument.AssertNotNull(id, nameof(id));
Argument.AssertNotNull(client, nameof(client));

Client = client;
Id = id;
}

/// <summary>
/// Gets the resource identifier.
/// </summary>
public virtual ResourceIdentifier Id { get; }

/// <summary>
/// Gets the <see cref="ArmClient"/> this resource client was created from.
/// </summary>
protected internal virtual ArmClient Client { get; }

/// <summary>
/// Gets the diagnostic options for this resource client.
/// </summary>
protected internal DiagnosticsOptions Diagnostics => Client.Diagnostics;

/// <summary>
/// Gets the pipeline for this resource client.
/// </summary>
protected internal HttpPipeline Pipeline => Client.Pipeline;

/// <summary>
/// Gets the base uri for this resource client.
/// </summary>
protected internal Uri Endpoint => Client.Endpoint;

/// <summary>
/// Gets the api version override if it has been set for the current client options.
/// </summary>
/// <param name="resourceType"> The resource type to get the version for. </param>
/// <param name="apiVersion"> The api version to variable to set. </param>
protected bool TryGetApiVersion(ResourceType resourceType, out string apiVersion) => Client.TryGetApiVersion(resourceType, out apiVersion);

/// <summary>
/// Gets a cached client to use for extension methods.
/// </summary>
/// <typeparam name="T"> The type of client to get. </typeparam>
/// <param name="clientFactory"> The constructor factory for the client. </param>
[EditorBrowsable(EditorBrowsableState.Never)]
public virtual T GetCachedClient<T>(Func<ArmClient, T> clientFactory)
where T : class
{
return _clientCache.GetOrAdd(typeof(T), (type) => { return clientFactory(Client); }) as T;
}
}
}
82 changes: 82 additions & 0 deletions tests/dotnet/dotnet-aot-compat/after/ArmEnvironment.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Text.Json;
using System.ComponentModel;
using Azure.Core;

namespace Azure.ResourceManager
{
/// <summary>
/// ArmEnvrionment represents the information of an Azure Cloud environment.
/// </summary>
public readonly struct ArmEnvironment : IEquatable<ArmEnvironment>
{
// name after the `name` property of returned audience from https://management.azure.com/metadata/endpoints?api-version=2019-11-01
/// <summary> Azure Public Cloud. </summary>
public static readonly ArmEnvironment AzurePublicCloud = new(new Uri("https://management.azure.com"), "https://management.azure.com/");

/// <summary> Azure China Cloud. </summary>
public static readonly ArmEnvironment AzureChina = new(new Uri("https://management.chinacloudapi.cn"), "https://management.chinacloudapi.cn");

/// <summary> Azure US Government. </summary>
public static readonly ArmEnvironment AzureGovernment = new(new Uri("https://management.usgovcloudapi.net"), "https://management.usgovcloudapi.net");

/// <summary> Azure German Cloud. </summary>
public static readonly ArmEnvironment AzureGermany = new(new Uri("https://management.microsoftazure.de"), "https://management.microsoftazure.de");

/// <summary>
/// Gets base URI of the management API endpoint.
/// </summary>
public readonly Uri Endpoint { get; }

/// <summary>
/// Gets authentication audience.
/// </summary>
public readonly string Audience { get; }

/// <summary>
/// Gets default authentication scope.
/// </summary>
public string DefaultScope { get; }

/// <summary>
/// Construct an <see cref="ArmEnvironment"/> using the given value.
/// </summary>
/// <param name="endpoint">Management API endpoint base URI.</param>
/// <param name="audience">Authentication audience.</param>
public ArmEnvironment(Uri endpoint, string audience)
{
Argument.AssertNotNull(endpoint, nameof(endpoint));
Argument.AssertNotNullOrWhiteSpace(audience, nameof(audience));

Endpoint = endpoint;
Audience = audience;
DefaultScope = $"{Audience}/.default";
}

/// <summary> Determines if two <see cref="ArmEnvironment"/> values are the same. </summary>
public static bool operator ==(ArmEnvironment left, ArmEnvironment right) => left.Equals(right);

/// <summary> Determines if two <see cref="ArmEnvironment"/> values are not the same. </summary>internal
public static bool operator !=(ArmEnvironment left, ArmEnvironment right) => !left.Equals(right);

/// <inheritdoc />
[EditorBrowsable(EditorBrowsableState.Never)]
public override bool Equals(object obj) => obj is ArmEnvironment other && Equals(other);

/// <inheritdoc />
public bool Equals(ArmEnvironment other) => string.Equals(Audience, other.Audience, StringComparison.Ordinal) && Endpoint.Equals(other.Endpoint);

/// <inheritdoc />
[EditorBrowsable(EditorBrowsableState.Never)]
public override int GetHashCode()
{
return HashCodeBuilder.Combine(Endpoint, Audience);
}

/// <inheritdoc />
public override string ToString() => JsonSerializer.Serialize(this, ResourceManagerJsonContext.Default.ArmEnvironment);
}
}
Loading