Add AOT analyzer, implement fixes, add json source generation

This commit is contained in:
Caelan Sayler
2024-06-01 12:04:20 +01:00
parent 16bc19e967
commit f32d6f88ff
16 changed files with 144 additions and 44 deletions

View File

@@ -0,0 +1,36 @@
namespace Velopack.Json;
public class SimpleJson
{
#if NET6_0_OR_GREATER
private static readonly System.Text.Json.JsonSerializerOptions Options = new System.Text.Json.JsonSerializerOptions {
AllowTrailingCommas = true,
ReadCommentHandling = System.Text.Json.JsonCommentHandling.Skip,
PropertyNameCaseInsensitive = true,
WriteIndented = true,
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull,
Converters = {
new System.Text.Json.Serialization.JsonStringEnumConverter(),
new SemanticVersionConverter(),
},
};
#endif
public static T DeserializeObject<T>(string json)
{
#if NET6_0_OR_GREATER
return System.Text.Json.JsonSerializer.Deserialize<T>(json, Options);
#else
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(json, CompiledJson.Options);
#endif
}
public static string SerializeObject<T>(T obj)
{
#if NET6_0_OR_GREATER
return System.Text.Json.JsonSerializer.Serialize(obj, Options);
#else
return Newtonsoft.Json.JsonConvert.SerializeObject(obj, CompiledJson.Options);
#endif
}
}

View File

@@ -1,7 +1,9 @@
using System;
using System;
using NuGet.Versioning;
using Velopack.Sources;
using System.Collections.Generic;
#if NET5_0_OR_GREATER
#if NET6_0_OR_GREATER
using System.Text.Json;
using System.Text.Json.Serialization;
#else
@@ -11,7 +13,7 @@ using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
#endif
#if !NET5_0_OR_GREATER
#if !NET6_0_OR_GREATER
namespace System.Text.Json.Serialization
{
// this is just here so our code can "use" System.Text.Json.Serialization
@@ -22,26 +24,54 @@ namespace System.Text.Json.Serialization
namespace Velopack.Json
{
#if NET5_0_OR_GREATER
internal static class SimpleJson
#if NET6_0_OR_GREATER
[JsonSerializable(typeof(List<GithubRelease>))]
[JsonSerializable(typeof(List<GitlabRelease>))]
[JsonSerializable(typeof(VelopackAssetFeed))]
#if NET8_0_OR_GREATER
[JsonSourceGenerationOptions(UseStringEnumConverter = true)]
#endif
internal partial class CompiledJsonSourceGenerationContext : JsonSerializerContext
{
public static readonly JsonSerializerOptions Options = new JsonSerializerOptions {
}
internal static class CompiledJson
{
private static readonly JsonSerializerOptions Options = new JsonSerializerOptions {
AllowTrailingCommas = true,
ReadCommentHandling = JsonCommentHandling.Skip,
PropertyNameCaseInsensitive = true,
WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Converters = { new JsonStringEnumConverter(), new SemanticVersionConverter() },
Converters = {
#if !NET8_0_OR_GREATER
new JsonStringEnumConverter(),
#endif
new SemanticVersionConverter(),
},
};
public static T? DeserializeObject<T>(string json)
private static readonly CompiledJsonSourceGenerationContext Context = new CompiledJsonSourceGenerationContext(Options);
public static List<GithubRelease>? DeserializeGithubReleaseList(string json)
{
return JsonSerializer.Deserialize<T>(json, Options);
return JsonSerializer.Deserialize(json, Context.ListGithubRelease);
}
public static string SerializeObject<T>(T obj)
public static List<GitlabRelease>? DeserializeGitlabReleaseList(string json)
{
return JsonSerializer.Serialize(obj, Options);
return JsonSerializer.Deserialize(json, Context.ListGitlabRelease);
}
public static VelopackAsset[]? DeserializeVelopackAssetArray(string json)
{
return JsonSerializer.Deserialize(json, Context.VelopackAssetArray);
}
public static VelopackAssetFeed? DeserializeVelopackAssetFeed(string json)
{
return JsonSerializer.Deserialize(json, Context.VelopackAssetFeed);
}
}
@@ -70,22 +100,32 @@ namespace Velopack.Json
}
}
internal static class SimpleJson
internal static class CompiledJson
{
private static readonly JsonSerializerSettings Options = new JsonSerializerSettings {
public static readonly JsonSerializerSettings Options = new JsonSerializerSettings {
Converters = { new StringEnumConverter(), new SemanticVersionConverter() },
ContractResolver = new JsonNameContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
};
public static T? DeserializeObject<T>(string json)
public static List<GithubRelease>? DeserializeGithubReleaseList(string json)
{
return JsonConvert.DeserializeObject<T>(json, Options);
return JsonConvert.DeserializeObject<List<GithubRelease>>(json, Options);
}
public static string SerializeObject<T>(T obj)
public static List<GitlabRelease>? DeserializeGitlabReleaseList(string json)
{
return JsonConvert.SerializeObject(obj, Formatting.Indented, Options);
return JsonConvert.DeserializeObject<List<GitlabRelease>>(json, Options);
}
public static VelopackAsset[]? DeserializeVelopackAssetArray(string json)
{
return JsonConvert.DeserializeObject<VelopackAsset[]>(json, Options);
}
public static VelopackAssetFeed? DeserializeVelopackAssetFeed(string json)
{
return JsonConvert.DeserializeObject<VelopackAssetFeed>(json, Options);
}
}

View File

@@ -642,6 +642,15 @@ namespace Velopack
if (!File.Exists(source)) throw new FileNotFoundException("File not found", source);
if (overwrite) File.Delete(dest);
File.Move(source, dest);
#endif
}
public static TEnum[] GetEnumValues<TEnum>() where TEnum : struct, Enum
{
#if NET6_0_OR_GREATER
return Enum.GetValues<TEnum>();
#else
return Enum.GetValues(typeof(TEnum)).Cast<TEnum>().ToArray();
#endif
}
}

View File

@@ -92,7 +92,7 @@ namespace Velopack.Sources
var baseUri = GetApiBaseUrl(RepoUri);
var getReleasesUri = new Uri(baseUri, releasesPath);
var response = await Downloader.DownloadString(getReleasesUri.ToString(), Authorization, "application/vnd.github.v3+json").ConfigureAwait(false);
var releases = SimpleJson.DeserializeObject<List<GithubRelease>>(response);
var releases = CompiledJson.DeserializeGithubReleaseList(response);
if (releases == null) return new GithubRelease[0];
return releases.OrderByDescending(d => d.PublishedAt).Where(x => includePrereleases || !x.Prerelease).ToArray();
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
@@ -153,7 +152,7 @@ namespace Velopack.Sources
var baseUri = new Uri("https://gitlab.com");
var getReleasesUri = new Uri(baseUri, releasesPath);
var response = await Downloader.DownloadString(getReleasesUri.ToString(), Authorization).ConfigureAwait(false);
var releases = SimpleJson.DeserializeObject<List<GitlabRelease>>(response);
var releases = CompiledJson.DeserializeGitlabReleaseList(response);
if (releases == null) return new GitlabRelease[0];
return releases.OrderByDescending(d => d.ReleasedAt).Where(x => includePrereleases || !x.UpcomingRelease).ToArray();
}

View File

@@ -29,7 +29,7 @@ namespace Velopack.Sources
public IFileDownloader Downloader { get; }
/// <inheritdoc />
public async Task<VelopackAssetFeed> GetReleaseFeed(ILogger logger, string channel, Guid? stagingId = null,
public async Task<VelopackAssetFeed> GetReleaseFeed(ILogger logger, string channel, Guid? stagingId = null,
VelopackAsset? latestLocalRelease = null)
{
Uri baseUri = new(BaseUri, $"v1.0/manifest/");
@@ -58,7 +58,7 @@ namespace Velopack.Sources
var json = await Downloader.DownloadString(uriAndQuery.ToString()).ConfigureAwait(false);
var releaseAssets = SimpleJson.DeserializeObject<VelopackReleaseAsset[]>(json);
var releaseAssets = CompiledJson.DeserializeVelopackAssetArray(json);
return new VelopackAssetFeed() {
Assets = releaseAssets
};

View File

@@ -5,12 +5,16 @@
<TargetFrameworks>net462;net48;net6.0;net8.0</TargetFrameworks>
<Nullable>enable</Nullable>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<LangVersion>9</LangVersion>
<VelopackPackageId>Velopack</VelopackPackageId>
</PropertyGroup>
<PropertyGroup>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net7.0'))">true</IsAotCompatible>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NuGet.Versioning" Version="6.9.1" />
</ItemGroup>

View File

@@ -26,14 +26,14 @@ namespace Velopack
/// <summary>
/// A list of assets available in this feed.
/// </summary>
public VelopackAsset[] Assets { get; init; } = Array.Empty<VelopackAsset>();
public VelopackAsset[] Assets { get; set; } = Array.Empty<VelopackAsset>();
/// <summary>
/// Parse a json string into a <see cref="VelopackAssetFeed"/>.
/// </summary>
public static VelopackAssetFeed FromJson(string json)
{
return SimpleJson.DeserializeObject<VelopackAssetFeed>(json) ?? new VelopackAssetFeed();
return CompiledJson.DeserializeVelopackAssetFeed(json) ?? new VelopackAssetFeed();
}
}
@@ -43,28 +43,28 @@ namespace Velopack
public record VelopackAsset
{
/// <summary> The name or Id of the package containing this release. </summary>
public string PackageId { get; init; }
public string PackageId { get; set; }
/// <summary> The version of this release. </summary>
public SemanticVersion Version { get; init; }
public SemanticVersion Version { get; set; }
/// <summary> The type of asset (eg. full or delta). </summary>
public VelopackAssetType Type { get; init; }
public VelopackAssetType Type { get; set; }
/// <summary> The filename of the update package containing this release. </summary>
public string FileName { get; init; }
public string FileName { get; set; }
/// <summary> The SHA1 checksum of the update package containing this release. </summary>
public string SHA1 { get; init; }
public string SHA1 { get; set; }
/// <summary> The size in bytes of the update package containing this release. </summary>
public long Size { get; init; }
public long Size { get; set; }
/// <summary> The release notes in markdown format, as passed to Velopack when packaging the release. </summary>
public string NotesMarkdown { get; init; }
public string NotesMarkdown { get; set; }
/// <summary> The release notes in HTML format, transformed from Markdown when packaging the release. </summary>
public string NotesHTML { get; init; }
public string NotesHTML { get; set; }
/// <summary>
/// Convert a <see cref="ZipPackage"/> to a <see cref="VelopackAsset"/>.

View File

@@ -317,8 +317,10 @@ namespace Velopack.Windows
var typestr = match.Groups["type"].Value; // default is WindowsDesktop
var archValid = Enum.TryParse<RuntimeCpu>(String.IsNullOrWhiteSpace(archstr) ? "x64" : archstr, true, out var cpu);
if (!archValid)
throw new ArgumentException($"Invalid machine architecture '{archstr}'. Valid values: {String.Join(", ", Enum.GetValues(typeof(RuntimeCpu)))}");
if (!archValid) {
throw new ArgumentException($"Invalid machine architecture '{archstr}'. " +
$"Valid values: {String.Join(", ", Utility.GetEnumValues<RuntimeCpu>())}");
}
var type = DotnetRuntimeType.WindowsDesktop;
if (!String.IsNullOrEmpty(typestr)) {

View File

@@ -1066,7 +1066,7 @@ namespace Velopack.Windows
displayName = "";
SHFILEINFO shfi = new SHFILEINFO();
uint shfiSize = (uint) Marshal.SizeOf(shfi.GetType());
uint shfiSize = (uint) Marshal.SizeOf<SHFILEINFO>();
IntPtr ret = SHGetFileInfo(
fileName, 0, ref shfi, shfiSize, (uint) (flags));

View File

@@ -280,7 +280,7 @@ namespace Velopack.Windows
private ShortcutLocation[] GetLocations(ShortcutLocation flag)
{
var locations = (ShortcutLocation[]) Enum.GetValues(typeof(ShortcutLocation));
var locations = Utility.GetEnumValues<ShortcutLocation>();
return locations
.Where(x => x != ShortcutLocation.None)
.Where(x => flag.HasFlag(x))

View File

@@ -157,6 +157,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
}
},
"Microsoft.NET.ILLink.Tasks": {
"type": "Direct",
"requested": "[8.0.1, )",
"resolved": "8.0.1",
"contentHash": "ADdJXuKNjwZDfBmybMnpvwd5CK3gp92WkWqqeQhW4W+q4MO3Qaa9QyW2DcFLAvCDMcCWxT5hRXqGdv13oon7nA=="
},
"Microsoft.SourceLink.GitHub": {
"type": "Direct",
"requested": "[8.0.0, )",

View File

@@ -1,7 +1,7 @@
#pragma warning disable CS0618 // Type or member is obsolete
#pragma warning disable CS0612 // Type or member is obsolete
using System.Text;
using System.Text.Json;
using Velopack.Json;
using Velopack.Sources;
namespace Velopack.Tests.TestHelpers;
@@ -63,7 +63,7 @@ internal class FakeFixtureRepository : Sources.IFileDownloader
}
if (url.Contains($"/{_releasesNameNew}?")) {
var json = JsonSerializer.Serialize(_releasesNew, SimpleJsonTests.Options);
var json = SimpleJson.SerializeObject(_releasesNew);
return Task.FromResult(Encoding.UTF8.GetBytes(json));
}
@@ -104,7 +104,7 @@ internal class FakeFixtureRepository : Sources.IFileDownloader
}
if (url.Contains($"/{_releasesNameNew}?")) {
var json = JsonSerializer.Serialize(_releasesNew, SimpleJsonTests.Options);
var json = SimpleJson.SerializeObject(_releasesNew);
return Task.FromResult(json);
}

View File

@@ -1,6 +1,6 @@
using System.Text;
using System.Text.Json;
using NuGet.Versioning;
using Velopack.Json;
using Velopack.Locators;
using Velopack.Sources;
using Velopack.Tests.TestHelpers;
@@ -38,7 +38,7 @@ public class UpdateManagerTests
},
}
};
var json = JsonSerializer.Serialize(feed, SimpleJsonTests.Options);
var json = SimpleJson.SerializeObject(feed);
return new FakeDownloader() { MockedResponseBytes = Encoding.UTF8.GetBytes(json) };
}
@@ -96,7 +96,7 @@ public class UpdateManagerTests
},
}
};
var json = JsonSerializer.Serialize(feed, SimpleJsonTests.Options);
var json = SimpleJson.SerializeObject(feed);
return new FakeDownloader() { MockedResponseBytes = Encoding.UTF8.GetBytes(json) };
}

View File

@@ -14,6 +14,10 @@
</Otherwise>
</Choose>
<ItemGroup>
<Compile Include="..\..\src\Velopack.Packaging\SimpleJson.cs" Link="SimpleJson.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.IO.Packaging" Version="8.0.0" />
<PackageReference Include="System.Text.Json" Version="8.0.3" />