From 8097c03b161cefce118b3fd0a77ab79cd3e602ed Mon Sep 17 00:00:00 2001 From: Nikola Milosavljevic Date: Tue, 17 Mar 2026 14:57:14 -0700 Subject: [PATCH] Make projects AOT compatible --- ...crosoft.TemplateEngine.Abstractions.csproj | 5 ++++ ...osoft.TemplateEngine.Core.Contracts.csproj | 5 ++++ .../Microsoft.TemplateEngine.Core.csproj | 5 ++++ .../BuiltInManagedProvider/GlobalSettings.cs | 2 +- .../GlobalSettingsJsonSerializerContext.cs | 11 ++++++++ .../Microsoft.TemplateEngine.Edge.csproj | 5 ++++ .../ReflectionLoadProbingPath.cs | 3 +++ .../Settings/ComponentManager.cs | 25 ++++++++++++++++++- .../Settings/Scanner.cs | 7 ++++++ .../SettingsStoreJsonSerializerContext.cs | 11 ++++++++ .../TemplateCacheJsonSerializerContext.cs | 11 ++++++++ .../Settings/TemplatePackageManager.cs | 2 +- .../Bootstrapper.cs | 6 +++++ .../Microsoft.TemplateEngine.IDE.csproj | 5 ++++ ...ngine.Orchestrator.RunnableProjects.csproj | 5 ++++ .../ValueForms/JsonEncodeValueFormFactory.cs | 10 +++++--- .../Microsoft.TemplateEngine.Utils.csproj | 5 ++++ .../Microsoft.TemplateSearch.Common.csproj | 5 ++++ .../TemplateDiscoveryMetadata.cs | 7 ++++++ .../TemplatePackageSearchData.Json.cs | 7 ++++++ .../TemplateSearchCache.Json.cs | 7 ++++++ .../TemplateSearchData.Json.cs | 7 ++++++ src/Shared/JExtensions.cs | 16 ++++++++++-- 23 files changed, 164 insertions(+), 8 deletions(-) create mode 100644 src/Microsoft.TemplateEngine.Edge/BuiltInManagedProvider/GlobalSettingsJsonSerializerContext.cs create mode 100644 src/Microsoft.TemplateEngine.Edge/Settings/SettingsStoreJsonSerializerContext.cs create mode 100644 src/Microsoft.TemplateEngine.Edge/Settings/TemplateCacheJsonSerializerContext.cs diff --git a/src/Microsoft.TemplateEngine.Abstractions/Microsoft.TemplateEngine.Abstractions.csproj b/src/Microsoft.TemplateEngine.Abstractions/Microsoft.TemplateEngine.Abstractions.csproj index 9a14342e2ba..64da04377b6 100644 --- a/src/Microsoft.TemplateEngine.Abstractions/Microsoft.TemplateEngine.Abstractions.csproj +++ b/src/Microsoft.TemplateEngine.Abstractions/Microsoft.TemplateEngine.Abstractions.csproj @@ -6,6 +6,11 @@ true true true + + true diff --git a/src/Microsoft.TemplateEngine.Core.Contracts/Microsoft.TemplateEngine.Core.Contracts.csproj b/src/Microsoft.TemplateEngine.Core.Contracts/Microsoft.TemplateEngine.Core.Contracts.csproj index 4466bcbda47..b6355219337 100644 --- a/src/Microsoft.TemplateEngine.Core.Contracts/Microsoft.TemplateEngine.Core.Contracts.csproj +++ b/src/Microsoft.TemplateEngine.Core.Contracts/Microsoft.TemplateEngine.Core.Contracts.csproj @@ -5,6 +5,11 @@ Contracts for extending Microsoft.TemplateEngine.Core true true + + true diff --git a/src/Microsoft.TemplateEngine.Core/Microsoft.TemplateEngine.Core.csproj b/src/Microsoft.TemplateEngine.Core/Microsoft.TemplateEngine.Core.csproj index 7bad7442b19..fc34c94724c 100644 --- a/src/Microsoft.TemplateEngine.Core/Microsoft.TemplateEngine.Core.csproj +++ b/src/Microsoft.TemplateEngine.Core/Microsoft.TemplateEngine.Core.csproj @@ -5,6 +5,11 @@ Common operations for instantiating templates using forward-only input stream operations true true + + true diff --git a/src/Microsoft.TemplateEngine.Edge/BuiltInManagedProvider/GlobalSettings.cs b/src/Microsoft.TemplateEngine.Edge/BuiltInManagedProvider/GlobalSettings.cs index 6850f15d73c..a2266f218c7 100644 --- a/src/Microsoft.TemplateEngine.Edge/BuiltInManagedProvider/GlobalSettings.cs +++ b/src/Microsoft.TemplateEngine.Edge/BuiltInManagedProvider/GlobalSettings.cs @@ -133,7 +133,7 @@ public async Task SetInstalledTemplatePackagesAsync(IReadOnlyListtrue true true + + true diff --git a/src/Microsoft.TemplateEngine.Edge/ReflectionLoadProbingPath.cs b/src/Microsoft.TemplateEngine.Edge/ReflectionLoadProbingPath.cs index 75806ba5e79..82c7a71da38 100644 --- a/src/Microsoft.TemplateEngine.Edge/ReflectionLoadProbingPath.cs +++ b/src/Microsoft.TemplateEngine.Edge/ReflectionLoadProbingPath.cs @@ -4,6 +4,7 @@ using System.Collections.Concurrent; using System.Reflection; #if NET +using System.Diagnostics.CodeAnalysis; using System.Runtime.Loader; #endif @@ -43,6 +44,7 @@ internal static void Reset() } #if NET + [UnconditionalSuppressMessage("AOT", "IL2026:RequiresUnreferencedCode", Justification = "Assembly probing loads assemblies by path at runtime.")] private static Assembly? SelectBestMatch(AssemblyLoadContext loadContext, AssemblyName match, IEnumerable candidates) #else private static Assembly? SelectBestMatch(object sender, AssemblyName match, IEnumerable candidates) @@ -195,6 +197,7 @@ internal static void Reset() } #if NET + [UnconditionalSuppressMessage("AOT", "IL2026:RequiresUnreferencedCode", Justification = "Assembly resolution probes and loads assemblies at runtime.")] private static Assembly? Resolving(AssemblyLoadContext assemblyLoadContext, AssemblyName assemblyName) #else private static Assembly? Resolving(object sender, ResolveEventArgs resolveEventArgs) diff --git a/src/Microsoft.TemplateEngine.Edge/Settings/ComponentManager.cs b/src/Microsoft.TemplateEngine.Edge/Settings/ComponentManager.cs index 0802c5a2194..42d3712b050 100644 --- a/src/Microsoft.TemplateEngine.Edge/Settings/ComponentManager.cs +++ b/src/Microsoft.TemplateEngine.Edge/Settings/ComponentManager.cs @@ -1,6 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if NET7_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif using System.Reflection; using Microsoft.TemplateEngine.Abstractions; #if NET @@ -18,6 +21,9 @@ internal class ComponentManager : IComponentManager private readonly SettingsFilePaths _paths; private readonly IEngineEnvironmentSettings _engineEnvironmentSettings; +#if NET7_0_OR_GREATER + [UnconditionalSuppressMessage("AOT", "IL2057", Justification = "Component types are resolved by assembly-qualified name from the settings store.")] +#endif public ComponentManager(IEngineEnvironmentSettings engineEnvironmentSettings) { _engineEnvironmentSettings = engineEnvironmentSettings; @@ -119,6 +125,12 @@ public void RegisterMany(IEnumerable typeList) } } +#if NET7_0_OR_GREATER + [UnconditionalSuppressMessage("AOT", "IL2026:RequiresUnreferencedCode", Justification = "Components are resolved by name and instantiated via reflection.")] + [UnconditionalSuppressMessage("AOT", "IL2067", Justification = "Component type is resolved at runtime from stored assembly-qualified name.")] + [UnconditionalSuppressMessage("AOT", "IL2072", Justification = "Component type is resolved at runtime from stored assembly-qualified name.")] + [UnconditionalSuppressMessage("AOT", "IL3050:RequiresDynamicCode", Justification = "Component instantiation uses Activator.CreateInstance with runtime-resolved types.")] +#endif public bool TryGetComponent(Guid id, out T? component) where T : class, IIdentifiedComponent { @@ -173,6 +185,13 @@ internal void AddProbingPath(string probeIn) } // This method does not save the settings, it just registers into the memory cache. +#if NET7_0_OR_GREATER + [UnconditionalSuppressMessage("AOT", "IL2026:RequiresUnreferencedCode", Justification = "Component registration inspects and instantiates types via reflection.")] + [UnconditionalSuppressMessage("AOT", "IL2067", Justification = "Component type metadata is inspected at runtime.")] + [UnconditionalSuppressMessage("AOT", "IL2070", Justification = "Component type interfaces are enumerated at runtime.")] + [UnconditionalSuppressMessage("AOT", "IL3000", Justification = "Assembly.Location is used to register probing paths for component resolution.")] + [UnconditionalSuppressMessage("AOT", "IL3050:RequiresDynamicCode", Justification = "Component instantiation uses Activator.CreateInstance with runtime-resolved types.")] +#endif private bool RegisterType(Type type) { if (!typeof(IIdentifiedComponent).IsAssignableFrom(type) || type.GetConstructor(Type.EmptyTypes) == null || !type.IsClass) @@ -239,7 +258,7 @@ internal void Save() { try { - _engineEnvironmentSettings.Host.FileSystem.WriteObject(_paths.SettingsFile, _settings); + _engineEnvironmentSettings.Host.FileSystem.WriteObject(_paths.SettingsFile, _settings, SettingsStoreJsonSerializerContext.Default.SettingsStore); successfulWrite = true; } catch (IOException) @@ -273,6 +292,10 @@ public void AddComponent(Type type, IIdentifiedComponent component) ids.Add(component.Id); } +#if NET7_0_OR_GREATER + [UnconditionalSuppressMessage("AOT", "IL2026:RequiresUnreferencedCode", Justification = "Assembly loading resolves assemblies by name at runtime.")] + [UnconditionalSuppressMessage("AOT", "IL2057", Justification = "Component type is resolved by stored assembly-qualified name.")] +#endif private Type GetType(string typeName) { int commaIndex = typeName.IndexOf(','); diff --git a/src/Microsoft.TemplateEngine.Edge/Settings/Scanner.cs b/src/Microsoft.TemplateEngine.Edge/Settings/Scanner.cs index 1147b4b578d..58d2c20ba2d 100644 --- a/src/Microsoft.TemplateEngine.Edge/Settings/Scanner.cs +++ b/src/Microsoft.TemplateEngine.Edge/Settings/Scanner.cs @@ -3,6 +3,7 @@ using System.Reflection; #if NET +using System.Diagnostics.CodeAnalysis; using System.Runtime.Loader; #endif using Microsoft.Extensions.Logging; @@ -128,6 +129,9 @@ private MountPointScanSource GetOrCreateMountPointScanInfoForInstallSource(strin throw new Exception(string.Format(LocalizableStrings.Scanner_Error_TemplatePackageLocationIsNotSupported, sourceLocation)); } +#if NET + [UnconditionalSuppressMessage("AOT", "IL2026:RequiresUnreferencedCode", Justification = "Component scanning uses Assembly.GetTypes() on dynamically loaded assemblies.")] +#endif private void ScanForComponents(MountPointScanSource source) { _ = source ?? throw new ArgumentNullException(nameof(source)); @@ -258,6 +262,9 @@ private async Task ScanMountPointForTemplatesAsync( /// Filename pattern to use when searching for files. /// to use when searching for files. /// The list of loaded assemblies in format (filename, loaded assembly). +#if NET + [UnconditionalSuppressMessage("AOT", "IL2026:RequiresUnreferencedCode", Justification = "Assembly loading via AssemblyLoadContext.LoadFromStream() is inherently reflection-based.")] +#endif private IEnumerable> LoadAllFromPath( out IEnumerable loadFailures, string path, diff --git a/src/Microsoft.TemplateEngine.Edge/Settings/SettingsStoreJsonSerializerContext.cs b/src/Microsoft.TemplateEngine.Edge/Settings/SettingsStoreJsonSerializerContext.cs new file mode 100644 index 00000000000..b3d7753e4ee --- /dev/null +++ b/src/Microsoft.TemplateEngine.Edge/Settings/SettingsStoreJsonSerializerContext.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text.Json.Serialization; + +namespace Microsoft.TemplateEngine.Edge.Settings +{ + [JsonSourceGenerationOptions(DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] + [JsonSerializable(typeof(SettingsStore))] + internal partial class SettingsStoreJsonSerializerContext : JsonSerializerContext; +} diff --git a/src/Microsoft.TemplateEngine.Edge/Settings/TemplateCacheJsonSerializerContext.cs b/src/Microsoft.TemplateEngine.Edge/Settings/TemplateCacheJsonSerializerContext.cs new file mode 100644 index 00000000000..c429de2e4f0 --- /dev/null +++ b/src/Microsoft.TemplateEngine.Edge/Settings/TemplateCacheJsonSerializerContext.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text.Json.Serialization; + +namespace Microsoft.TemplateEngine.Edge.Settings +{ + [JsonSourceGenerationOptions(DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] + [JsonSerializable(typeof(TemplateCache))] + internal partial class TemplateCacheJsonSerializerContext : JsonSerializerContext; +} diff --git a/src/Microsoft.TemplateEngine.Edge/Settings/TemplatePackageManager.cs b/src/Microsoft.TemplateEngine.Edge/Settings/TemplatePackageManager.cs index df730356f1a..6bb1999d0c9 100644 --- a/src/Microsoft.TemplateEngine.Edge/Settings/TemplatePackageManager.cs +++ b/src/Microsoft.TemplateEngine.Edge/Settings/TemplatePackageManager.cs @@ -373,7 +373,7 @@ await Task.WhenAll(Enumerable.Range(0, allTemplatePackages.Count).Select(async i try { - _environmentSettings.Host.FileSystem.WriteObject(_paths.TemplateCacheFile, cache); + _environmentSettings.Host.FileSystem.WriteObject(_paths.TemplateCacheFile, cache, TemplateCacheJsonSerializerContext.Default.TemplateCache); } catch (Exception ex) { diff --git a/src/Microsoft.TemplateEngine.IDE/Bootstrapper.cs b/src/Microsoft.TemplateEngine.IDE/Bootstrapper.cs index c1416631f20..dac2e89bff0 100644 --- a/src/Microsoft.TemplateEngine.IDE/Bootstrapper.cs +++ b/src/Microsoft.TemplateEngine.IDE/Bootstrapper.cs @@ -2,6 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Reflection; +#if NET7_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif using Microsoft.TemplateEngine.Abstractions; using Microsoft.TemplateEngine.Abstractions.Installer; using Microsoft.TemplateEngine.Abstractions.TemplatePackage; @@ -371,6 +374,9 @@ public void Register(Type type) } [Obsolete("Use ITemplateEngineHost.BuiltInComponents or AddComponent to add components.")] +#if NET7_0_OR_GREATER + [UnconditionalSuppressMessage("AOT", "IL2026:RequiresUnreferencedCode", Justification = "Obsolete method; Assembly.GetTypes() is inherently reflection-based.")] +#endif public void Register(Assembly assembly) { _engineEnvironmentSettings.Components.RegisterMany(assembly.GetTypes()); diff --git a/src/Microsoft.TemplateEngine.IDE/Microsoft.TemplateEngine.IDE.csproj b/src/Microsoft.TemplateEngine.IDE/Microsoft.TemplateEngine.IDE.csproj index b3e2dc3f7e4..7c17ac1e474 100644 --- a/src/Microsoft.TemplateEngine.IDE/Microsoft.TemplateEngine.IDE.csproj +++ b/src/Microsoft.TemplateEngine.IDE/Microsoft.TemplateEngine.IDE.csproj @@ -6,6 +6,11 @@ true true true + + true diff --git a/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/Microsoft.TemplateEngine.Orchestrator.RunnableProjects.csproj b/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/Microsoft.TemplateEngine.Orchestrator.RunnableProjects.csproj index 79dab3bba05..537f201c6c3 100644 --- a/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/Microsoft.TemplateEngine.Orchestrator.RunnableProjects.csproj +++ b/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/Microsoft.TemplateEngine.Orchestrator.RunnableProjects.csproj @@ -5,6 +5,11 @@ An extension for Template Engine that allows projects that still run to be used as templates true true + + true diff --git a/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/ValueForms/JsonEncodeValueFormFactory.cs b/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/ValueForms/JsonEncodeValueFormFactory.cs index 95de03692b9..5ff5a77d4ed 100644 --- a/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/ValueForms/JsonEncodeValueFormFactory.cs +++ b/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/ValueForms/JsonEncodeValueFormFactory.cs @@ -3,23 +3,27 @@ using System.Text.Encodings.Web; using System.Text.Json; +using System.Text.Json.Serialization; namespace Microsoft.TemplateEngine.Orchestrator.RunnableProjects.ValueForms { + [JsonSerializable(typeof(string))] + internal partial class JsonEncodeSerializerContext : JsonSerializerContext; + internal class JsonEncodeValueFormFactory : ActionableValueFormFactory { internal const string FormIdentifier = "jsonEncode"; - private static readonly JsonSerializerOptions JsonEncodeOptions = new() + private static readonly JsonEncodeSerializerContext JsonEncodeContext = new(new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping - }; + }); internal JsonEncodeValueFormFactory() : base(FormIdentifier) { } protected override string Process(string value) { - return JsonSerializer.Serialize(value, JsonEncodeOptions); + return JsonSerializer.Serialize(value, JsonEncodeContext.String); } } } diff --git a/src/Microsoft.TemplateEngine.Utils/Microsoft.TemplateEngine.Utils.csproj b/src/Microsoft.TemplateEngine.Utils/Microsoft.TemplateEngine.Utils.csproj index 7bfb6cc9f07..59c4595003b 100644 --- a/src/Microsoft.TemplateEngine.Utils/Microsoft.TemplateEngine.Utils.csproj +++ b/src/Microsoft.TemplateEngine.Utils/Microsoft.TemplateEngine.Utils.csproj @@ -6,6 +6,11 @@ true true true + + true diff --git a/src/Microsoft.TemplateSearch.Common/Microsoft.TemplateSearch.Common.csproj b/src/Microsoft.TemplateSearch.Common/Microsoft.TemplateSearch.Common.csproj index 80d70443282..670d6633b99 100644 --- a/src/Microsoft.TemplateSearch.Common/Microsoft.TemplateSearch.Common.csproj +++ b/src/Microsoft.TemplateSearch.Common/Microsoft.TemplateSearch.Common.csproj @@ -6,6 +6,11 @@ true true true + + true diff --git a/src/Microsoft.TemplateSearch.Common/TemplateDiscoveryMetadata/TemplateDiscoveryMetadata.cs b/src/Microsoft.TemplateSearch.Common/TemplateDiscoveryMetadata/TemplateDiscoveryMetadata.cs index b5e92825580..2b66b2e4887 100644 --- a/src/Microsoft.TemplateSearch.Common/TemplateDiscoveryMetadata/TemplateDiscoveryMetadata.cs +++ b/src/Microsoft.TemplateSearch.Common/TemplateDiscoveryMetadata/TemplateDiscoveryMetadata.cs @@ -1,6 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if NET7_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif using System.Text.Json.Nodes; using System.Text.Json.Serialization; using Microsoft.TemplateEngine; @@ -31,6 +34,10 @@ internal TemplateDiscoveryMetadata(string version, IReadOnlyList [JsonInclude] internal IReadOnlyDictionary AdditionalData { get; } +#if NET7_0_OR_GREATER + [UnconditionalSuppressMessage("AOT", "IL2026:RequiresUnreferencedCode", Justification = "Serializes a known internal type (TemplateDiscoveryMetadata).")] + [UnconditionalSuppressMessage("AOT", "IL3050:RequiresDynamicCode", Justification = "Serializes a known internal type (TemplateDiscoveryMetadata).")] +#endif internal JsonObject ToJObject() { return JExtensions.FromObject(this); diff --git a/src/Microsoft.TemplateSearch.Common/TemplateSearchCache/TemplatePackageSearchData.Json.cs b/src/Microsoft.TemplateSearch.Common/TemplateSearchCache/TemplatePackageSearchData.Json.cs index c2f1e14101e..8c9aac05b66 100644 --- a/src/Microsoft.TemplateSearch.Common/TemplateSearchCache/TemplatePackageSearchData.Json.cs +++ b/src/Microsoft.TemplateSearch.Common/TemplateSearchCache/TemplatePackageSearchData.Json.cs @@ -1,6 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if NET7_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif using System.Text.Json; using System.Text.Json.Nodes; using Microsoft.Extensions.Logging; @@ -67,6 +70,10 @@ private class TemplatePackageSearchDataJsonConverter : System.Text.Json.Serializ public override TemplatePackageSearchData Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => throw new NotImplementedException(); +#if NET7_0_OR_GREATER + [UnconditionalSuppressMessage("AOT", "IL2026:RequiresUnreferencedCode", Justification = "Templates and AdditionalData are serialized with known types.")] + [UnconditionalSuppressMessage("AOT", "IL3050:RequiresDynamicCode", Justification = "Templates and AdditionalData are serialized with known types.")] +#endif public override void Write(Utf8JsonWriter writer, TemplatePackageSearchData value, JsonSerializerOptions options) { if (value == null) diff --git a/src/Microsoft.TemplateSearch.Common/TemplateSearchCache/TemplateSearchCache.Json.cs b/src/Microsoft.TemplateSearch.Common/TemplateSearchCache/TemplateSearchCache.Json.cs index 1bb26f57551..aef541af28b 100644 --- a/src/Microsoft.TemplateSearch.Common/TemplateSearchCache/TemplateSearchCache.Json.cs +++ b/src/Microsoft.TemplateSearch.Common/TemplateSearchCache/TemplateSearchCache.Json.cs @@ -1,6 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if NET7_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif using System.Text.Json.Nodes; using Microsoft.Extensions.Logging; using Microsoft.TemplateEngine; @@ -96,6 +99,10 @@ internal static IDictionary ReadAdditionalData( return additionalData; } +#if NET7_0_OR_GREATER + [UnconditionalSuppressMessage("AOT", "IL2026:RequiresUnreferencedCode", Justification = "Serializes a known internal type (TemplateSearchCache).")] + [UnconditionalSuppressMessage("AOT", "IL3050:RequiresDynamicCode", Justification = "Serializes a known internal type (TemplateSearchCache).")] +#endif internal JsonObject ToJObject() { return JExtensions.FromObject(this); diff --git a/src/Microsoft.TemplateSearch.Common/TemplateSearchCache/TemplateSearchData.Json.cs b/src/Microsoft.TemplateSearch.Common/TemplateSearchCache/TemplateSearchData.Json.cs index 1c9d0a82bea..6d7bec690df 100644 --- a/src/Microsoft.TemplateSearch.Common/TemplateSearchCache/TemplateSearchData.Json.cs +++ b/src/Microsoft.TemplateSearch.Common/TemplateSearchCache/TemplateSearchData.Json.cs @@ -1,6 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if NET7_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif using System.Text.Json; using System.Text.Json.Nodes; using Microsoft.Extensions.Logging; @@ -40,6 +43,10 @@ private class TemplateSearchDataJsonConverter : System.Text.Json.Serialization.J public override TemplateSearchData Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => throw new NotImplementedException(); +#if NET7_0_OR_GREATER + [UnconditionalSuppressMessage("AOT", "IL2026:RequiresUnreferencedCode", Justification = "BaselineInfo and AdditionalData are serialized with known types.")] + [UnconditionalSuppressMessage("AOT", "IL3050:RequiresDynamicCode", Justification = "BaselineInfo and AdditionalData are serialized with known types.")] +#endif public override void Write(Utf8JsonWriter writer, TemplateSearchData value, JsonSerializerOptions options) { if (value == null) diff --git a/src/Shared/JExtensions.cs b/src/Shared/JExtensions.cs index 4ecbd81a7bc..131d3f11560 100644 --- a/src/Shared/JExtensions.cs +++ b/src/Shared/JExtensions.cs @@ -1,9 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if NET7_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; using Microsoft.TemplateEngine.Abstractions; using Microsoft.TemplateEngine.Abstractions.Mount; using Microsoft.TemplateEngine.Abstractions.PhysicalFileSystem; @@ -396,10 +400,10 @@ internal static JsonObject ReadObject(this IPhysicalFileSystem fileSystem, strin ?? throw new InvalidOperationException($"Failed to parse JSON from '{path}'."); } - internal static void WriteObject(this IPhysicalFileSystem fileSystem, string path, object obj) + internal static void WriteObject(this IPhysicalFileSystem fileSystem, string path, T obj, JsonTypeInfo jsonTypeInfo) { using Stream fileStream = fileSystem.CreateFile(path); - JsonSerializer.Serialize(fileStream, obj, SerializerOptions); + JsonSerializer.Serialize(fileStream, obj, jsonTypeInfo); } internal static IReadOnlyList JTokenStringOrArrayToCollection(this JsonNode? token, IReadOnlyList defaultSet) @@ -421,6 +425,10 @@ internal static IReadOnlyList JTokenStringOrArrayToCollection(this JsonN /// /// Converts to valid JSON string. /// +#if NET7_0_OR_GREATER + [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] + [RequiresDynamicCode("JSON serialization and deserialization might require runtime code generation.")] +#endif internal static string ToJsonString(object obj) { return JsonSerializer.Serialize(obj, SerializerOptions); @@ -488,6 +496,10 @@ internal static JsonObject ParseJsonObject(string json) /// Serializes an object to a JsonObject via JSON round-trip. /// Equivalent to Newtonsoft's JObject.FromObject(). /// +#if NET7_0_OR_GREATER + [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")] + [RequiresDynamicCode("JSON serialization and deserialization might require runtime code generation.")] +#endif internal static JsonObject FromObject(object obj) { string json = JsonSerializer.Serialize(obj, SerializerOptions);