From 89a2e353a48c1c6f8dd80196b2ee672b0c2b0d14 Mon Sep 17 00:00:00 2001 From: pableess <8421069+pableess@users.noreply.github.com> Date: Sun, 13 Sep 2026 22:29:03 -0500 Subject: [PATCH] Add NativeAOT support (#1) * Add NativeAOT support Use a source-generated TomlSerializerContext for the internal TomlTable deserialization instead of the reflection-based overload. Reflection-based TOML serialization is disabled by default under PublishAot=true, which made the previous call fail at runtime and emit IL2026/IL3050 warnings. - Add internal TomlModelSerializerContext with [TomlSerializable(typeof(TomlTable))] - Parse via TomlSerializer.Deserialize(text, context) - Mark the library IsAotCompatible and bump Tomlyn to 2.10.1 - Add a NativeAOT smoke-test project and CI workflow (test + AOT publish/run) * removed test projects * remove test project and ci build --------- Co-authored-by: Paul --- README.md | 22 +++++++++++++++++++ .../TomlConfigurationFileParser.cs | 2 +- .../TomlModelSerializerContext.cs | 10 +++++++++ .../Tomlyn.Extensions.Configuration.csproj | 3 ++- 4 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 Tomlyn.Extensions.Configuration/TomlModelSerializerContext.cs diff --git a/README.md b/README.md index 31afbac..aa951bf 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,28 @@ config.Bind(sample); If using 'snake_case' keys, YOU MUST make sure that your C# variables are 'PascalCase' to ensure proper binding. +## NativeAOT + +This library is annotated as NativeAOT compatible (`IsAotCompatible`) and has no runtime reflection dependencies. TOML parsing goes through a source-generated Tomlyn `TomlSerializerContext`, so it works when reflection-based serialization is disabled (`PublishAot=true`). + +When publishing with `PublishAot=true`, avoid reflection-based configuration binding: + +```cs +// Not AOT safe +configuration.Bind(options); + +// AOT safe +var value = configuration["Server:Port"]; +``` + +To bind values to strongly typed options under NativeAOT, opt into the configuration binder source generator: + +```xml + + true + +``` + ## Alternatives and benchmarks Another [possibly slightly faster](https://github.com/bugproof/TomlLibrariesBenchmark) alternative is [Tommy.Extensions.Configuration](https://github.com/dezhidki/Tommy/tree/master/Tommy.Extensions.Configuration). diff --git a/Tomlyn.Extensions.Configuration/TomlConfigurationFileParser.cs b/Tomlyn.Extensions.Configuration/TomlConfigurationFileParser.cs index 23f2659..e859680 100644 --- a/Tomlyn.Extensions.Configuration/TomlConfigurationFileParser.cs +++ b/Tomlyn.Extensions.Configuration/TomlConfigurationFileParser.cs @@ -20,7 +20,7 @@ public static IDictionary Parse(Stream input) private IDictionary ParseStream(Stream input) { using var reader = new StreamReader(input); - var model = TomlSerializer.Deserialize(reader.ReadToEnd()) + var model = TomlSerializer.Deserialize(reader.ReadToEnd(), TomlModelSerializerContext.Default) ?? throw new FormatException("TOML deserialization returned null"); VisitObject(model); return _data; diff --git a/Tomlyn.Extensions.Configuration/TomlModelSerializerContext.cs b/Tomlyn.Extensions.Configuration/TomlModelSerializerContext.cs new file mode 100644 index 0000000..4fdbf88 --- /dev/null +++ b/Tomlyn.Extensions.Configuration/TomlModelSerializerContext.cs @@ -0,0 +1,10 @@ +using Tomlyn.Model; +using Tomlyn.Serialization; + +namespace Tomlyn.Extensions.Configuration +{ + [TomlSerializable(typeof(TomlTable))] + internal partial class TomlModelSerializerContext : TomlSerializerContext + { + } +} diff --git a/Tomlyn.Extensions.Configuration/Tomlyn.Extensions.Configuration.csproj b/Tomlyn.Extensions.Configuration/Tomlyn.Extensions.Configuration.csproj index 3ca6822..99915a4 100644 --- a/Tomlyn.Extensions.Configuration/Tomlyn.Extensions.Configuration.csproj +++ b/Tomlyn.Extensions.Configuration/Tomlyn.Extensions.Configuration.csproj @@ -11,11 +11,12 @@ MIT true default + true - +