diff --git a/Libraries/src/Amazon.Lambda.RuntimeSupport/Amazon.Lambda.RuntimeSupport.csproj b/Libraries/src/Amazon.Lambda.RuntimeSupport/Amazon.Lambda.RuntimeSupport.csproj index e932d8823..f9e951ce8 100644 --- a/Libraries/src/Amazon.Lambda.RuntimeSupport/Amazon.Lambda.RuntimeSupport.csproj +++ b/Libraries/src/Amazon.Lambda.RuntimeSupport/Amazon.Lambda.RuntimeSupport.csproj @@ -24,7 +24,6 @@ IL2026,IL2067,IL2075 true true - true diff --git a/Libraries/src/Amazon.Lambda.RuntimeSupport/Helpers/Logging/JsonLogMessageFormatter.cs b/Libraries/src/Amazon.Lambda.RuntimeSupport/Helpers/Logging/JsonLogMessageFormatter.cs index 95e7d576e..e0e7f2b09 100644 --- a/Libraries/src/Amazon.Lambda.RuntimeSupport/Helpers/Logging/JsonLogMessageFormatter.cs +++ b/Libraries/src/Amazon.Lambda.RuntimeSupport/Helpers/Logging/JsonLogMessageFormatter.cs @@ -208,6 +208,8 @@ private void WriteException(Utf8JsonWriter writer, MessageState state) /// [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026", Justification = "If formatting an object using JSON serialization this will do its best attempt. If the object has trim errors formatting will fall back to ToString for the object.")] + [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("ReflectionAnalysis", "IL3050", + Justification = "If formatting an object using JSON serialization this will do its best attempt. If the object has trim errors formatting will fall back to ToString for the object.")] private void FormatJsonValue(Utf8JsonWriter writer, object value, string formatArguments, MessageProperty.Directive directive) { if(value == null) diff --git a/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/NativeAOTTests.cs b/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/NativeAOTTests.cs new file mode 100644 index 000000000..ae7f7aaf7 --- /dev/null +++ b/Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/Amazon.Lambda.RuntimeSupport.UnitTests/NativeAOTTests.cs @@ -0,0 +1,100 @@ +using System; +using System.Diagnostics; +using System.Text; +using System.Reflection; + +using Xunit; +using Xunit.Abstractions; +using System.IO; + +#if NET8_0_OR_GREATER +namespace Amazon.Lambda.RuntimeSupport.UnitTests +{ + public class NativeAOTTests + { + private ITestOutputHelper _output; + + public NativeAOTTests(ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void EnsureNoTrimWarningsDuringPublish() + { + var projectDirectory = FindProject("NativeAOTFunction"); + + _output.WriteLine("dotnet publish " + projectDirectory); + var output = ExecutePublish(projectDirectory); + _output.WriteLine(output.Log); + + Assert.True(output.ExitCode == 0); + Assert.DoesNotContain("AOT analysis warning", output.Log); + } + + private (int ExitCode, string Log) ExecutePublish(string projectDirectory) + { + var buffer = new StringBuilder(); + var handler = (DataReceivedEventHandler)((o, e) => + { + if (string.IsNullOrEmpty(e.Data)) + return; + + buffer.AppendLine(e.Data); + }); + + var startInfo = new ProcessStartInfo + { + FileName = "dotnet", + Arguments = $"publish", + WorkingDirectory = projectDirectory, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true + }; + + int exitCode; + using (var proc = new Process()) + { + proc.StartInfo = startInfo; + proc.Start(); + + if (startInfo.RedirectStandardOutput) + { + proc.ErrorDataReceived += handler; + proc.OutputDataReceived += handler; + proc.BeginOutputReadLine(); + proc.BeginErrorReadLine(); + + proc.EnableRaisingEvents = true; + } + + proc.WaitForExit(); + + + exitCode = proc.ExitCode; + } + + return (exitCode, buffer.ToString()); + } + + private string FindProject(string projectName) + { + var directory = Assembly.GetExecutingAssembly().Location; + while (directory != null) + { + var fullpath = Path.Combine(directory, projectName); + if (Directory.Exists(fullpath)) + { + return fullpath; + } + + directory = Directory.GetParent(directory)?.FullName; + } + + throw new Exception("Failed to find project directory " + projectName); + } + } +} +#endif \ No newline at end of file diff --git a/Libraries/test/NativeAOTFunction/Function.cs b/Libraries/test/NativeAOTFunction/Function.cs new file mode 100644 index 000000000..0baf56ce7 --- /dev/null +++ b/Libraries/test/NativeAOTFunction/Function.cs @@ -0,0 +1,60 @@ +using Amazon.Lambda.Core; +using Amazon.Lambda.RuntimeSupport; +using Amazon.Lambda.Serialization.SystemTextJson; +using System.Text.Json.Serialization; + +namespace StructureAOTTest; + +public class Function +{ + /// + /// The main entry point for the Lambda function. The main function is called once during the Lambda init phase. It + /// initializes the .NET Lambda runtime client passing in the function handler to invoke for each Lambda event and + /// the JSON serializer to use for converting Lambda JSON format to the .NET types. + /// + private static async Task Main() + { + Func handler = FunctionHandler; + await LambdaBootstrapBuilder.Create(handler, new SourceGeneratorLambdaJsonSerializer()) + .Build() + .RunAsync(); + } + + /// + /// A simple function that takes a string and does a ToUpper. + /// + /// To use this handler to respond to an AWS event, reference the appropriate package from + /// https://github.com/aws/aws-lambda-dotnet#events + /// and change the string input parameter to the desired event type. When the event type + /// is changed, the handler type registered in the main method needs to be updated and the LambdaFunctionJsonSerializerContext + /// defined below will need the JsonSerializable updated. If the return type and event type are different then the + /// LambdaFunctionJsonSerializerContext must have two JsonSerializable attributes, one for each type. + /// + // When using Native AOT extra testing with the deployed Lambda functions is required to ensure + // the libraries used in the Lambda function work correctly with Native AOT. If a runtime + // error occurs about missing types or methods the most likely solution will be to remove references to trim-unsafe + // code or configure trimming options. This sample defaults to partial TrimMode because currently the AWS + // SDK for .NET does not support trimming. This will result in a larger executable size, and still does not + // guarantee runtime trimming errors won't be hit. + /// + /// The event for the Lambda function handler to process. + /// The ILambdaContext that provides methods for logging and describing the Lambda environment. + /// + public static string FunctionHandler(string input, ILambdaContext context) + { + return input.ToUpper(); + } +} + +/// +/// This class is used to register the input event and return type for the FunctionHandler method with the System.Text.Json source generator. +/// There must be a JsonSerializable attribute for each type used as the input and return type or a runtime error will occur +/// from the JSON serializer unable to find the serialization information for unknown types. +/// +[JsonSerializable(typeof(string))] +public partial class LambdaFunctionJsonSerializerContext : JsonSerializerContext +{ + // By using this partial class derived from JsonSerializerContext, we can generate reflection free JSON Serializer code at compile time + // which can deserialize our class and properties. However, we must attribute this class to tell it what types to generate serialization code for. + // See https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-source-generation +} \ No newline at end of file diff --git a/Libraries/test/NativeAOTFunction/NativeAOTFunction.csproj b/Libraries/test/NativeAOTFunction/NativeAOTFunction.csproj new file mode 100644 index 000000000..bc83e5d2e --- /dev/null +++ b/Libraries/test/NativeAOTFunction/NativeAOTFunction.csproj @@ -0,0 +1,25 @@ + + + Exe + net8.0 + enable + enable + Lambda + + true + + true + + true + + partial + false + + + + + + + \ No newline at end of file diff --git a/Libraries/test/NativeAOTFunction/aws-lambda-tools-defaults.json b/Libraries/test/NativeAOTFunction/aws-lambda-tools-defaults.json new file mode 100644 index 000000000..f7e8c52a4 --- /dev/null +++ b/Libraries/test/NativeAOTFunction/aws-lambda-tools-defaults.json @@ -0,0 +1,16 @@ +{ + "Information": [ + "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", + "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.", + "dotnet lambda help", + "All the command line options for the Lambda command can be specified in this file." + ], + "profile": "default", + "region": "us-west-2", + "configuration": "Release", + "function-runtime": "dotnet8", + "function-memory-size": 512, + "function-timeout": 30, + "function-handler": "NativeAOTFunction", + "msbuild-parameters": "--self-contained true" +} \ No newline at end of file