Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#1571 Add annotations support for executable assemblies #1573

Merged
merged 31 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
5c3927c
#1571 Add annotations support for executable assemblies
Aug 21, 2023
fe69112
Add support for serialization
jeastham1993 Aug 21, 2023
5ebbf64
Add support for serialization and update annotation name
Aug 22, 2023
c73b61c
Move serialization to constructor
Aug 22, 2023
2bb0c07
Merge branch 'aws:master' into master
jeastham1993 Aug 30, 2023
0093d53
Update existing static main error
Aug 30, 2023
e54ea0d
Use syntax receiver get declared symbol
Aug 30, 2023
9484ac1
Add check for method parameter count
Aug 30, 2023
68bebac
Update to use extension method
Aug 30, 2023
a90b6ce
Fix test unit tests
Aug 30, 2023
80cbcf9
Merge branch 'master' of https://github.com/jeastham1993/aws-lambda-d…
Sep 1, 2023
0b57758
Update attribute name and add Runtime option
jeastham1993 Sep 9, 2023
0207130
Update unit tests and documentation
Sep 11, 2023
d695a6c
Update tests and comments
jeastham1993 Oct 1, 2023
5604358
Update CfN writer to always set runtime
jeastham1993 Oct 1, 2023
47d87f9
Update serverless template
Oct 26, 2023
1ae33d3
Add diaganostic error and update SLN
Oct 26, 2023
01f2595
Merge branch 'master' of https://github.com/jeastham1993/aws-lambda-d…
Oct 26, 2023
ae09362
Fix issue with parameterless response
Oct 29, 2023
5034c30
Updates to handle responses with no parameters
Oct 29, 2023
9747536
Remove BAK file and update referenced libraries
Nov 2, 2023
2c71099
Add RuntimeSupport to solution filter
Nov 2, 2023
5692d98
Update HttpResults.cs (#1602)
t0mll Nov 7, 2023
c4a08ba
Update HttpResults.cs (#1603)
t0mll Nov 7, 2023
4092dbf
Update README
Nov 8, 2023
62e11c1
Add new diagnostics
Nov 9, 2023
1132f97
Update SourceGen tests to use static strings instead of files
Nov 9, 2023
d9d5b96
Update error messages
Nov 9, 2023
aaaa5a8
Update annotations design
Nov 9, 2023
c78c6d2
Resolve build issues
jeastham1993 Nov 11, 2023
c2e4304
Merge branch 'aws:master' into master
jeastham1993 Nov 11, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions Docs/lambda-annotations-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,69 @@ public class LambdaFunctions
}
```

## Auto Generate Main
ashovlin marked this conversation as resolved.
Show resolved Hide resolved

A `LambdaGlobalProperties` attribute is available to set global settings that the annotations framework uses when generating code at compile time. This simplifies the programming model when using custom runtimes or native ahead of time (AOT) compilation. It removes the need to manually bootstrap the Lambda runtime.

To auto-generate the `static Main` method, first ensure the `OutputType` in your `csproj` file is set to `exe`.
```xml
<PropertyGroup>
<!--Removed for brevity..-->
<OutputType>exe</OutputType>
</PropertyGroup>
```

Once the output type is set to executable, add the `LambdaGlobalProperties` assembly attribute and set the `GenerateMain` property to true. You can also configure the `Runtime` in the generated CloudFormation template.

```c#
[assembly: LambdaGlobalProperties(GenerateMain = true, Runtime = "provided.al2")]
```

### Behind The Scenes

Assuming the below Lambda function handler:

```c#
public class Greeter
{
[LambdaFunction(ResourceName = "GreeterSayHello", MemorySize = 1024, PackageType = LambdaPackageType.Image)]
[HttpApi(LambdaHttpMethod.Get, "/Greeter/SayHello", Version = HttpApiVersion.V1)]
public void SayHello([FromQuery(Name = "names")]IEnumerable<string> firstNames, APIGatewayProxyRequest request, ILambdaContext context)
{
context.Logger.LogLine($"Request {JsonSerializer.Serialize(request)}");

if (firstNames == null)
{
return;
}

foreach (var firstName in firstNames)
{
Console.WriteLine($"Hello {firstName}");
}
}
}
```

The generated `static Main` method would look like the below. To allow for multiple Lambda functions in the same executable an Environment variable is used to determine which handler is executed. When using the `GenerateMain` attribute, ensure you also set the `ANNOTATIONS_HANDLER` environment variable on the deployed resource.

The auto-generated CloudFormation template will include this as a default.

```c#
public class GeneratedProgram
{
private static async Task Main(string[] args)
{
switch (Environment.GetEnvironmentVariable("ANNOTATIONS_HANDLER"))
{
case "ToUpper":
Func<string, string> toupper_handler = new TestServerlessApp.Sub1.Functions_ToUpper_Generated().ToUpper;
await Amazon.Lambda.RuntimeSupport.LambdaBootstrapBuilder.Create(toupper_handler, new Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer()).Build().RunAsync();
break;
}
}
}
```

## Lambda .NET Attributes

Expand Down Expand Up @@ -368,3 +430,9 @@ Here is a preliminary list of .NET attributes that will tell the source generato
* Map method parameter to HTTP request body. If parameter is a complex type then request body will be assumed to be JSON and deserialized into the type.
* FromServices
* Map method parameter to registered service in IServiceProvider

### Global Attributes
* GenerateMain
* Generates a `static Program` class and a `static Main` method that bootstraps the Lambda runtime. Simplifies the programming model when building on a custom runtime or using native ahead of time (AOT) compilation.
* Runtime
* Set the runtime in the generated CloudFormation template. Set to either `dotnet6` or `provided.al2`
4 changes: 3 additions & 1 deletion Libraries/Amazon.Lambda.Annotations.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
"solution": {
"path": "Libraries.sln",
"projects": [
"src\\Amazon.Lambda.APIGatewayEvents\\Amazon.Lambda.APIGatewayEvents.csproj",
"src\\Amazon.Lambda.Annotations.SourceGenerator\\Amazon.Lambda.Annotations.SourceGenerator.csproj",
"src\\Amazon.Lambda.Annotations\\Amazon.Lambda.Annotations.csproj",
"src\\Amazon.Lambda.APIGatewayEvents\\Amazon.Lambda.APIGatewayEvents.csproj",
ashovlin marked this conversation as resolved.
Show resolved Hide resolved
"src\\Amazon.Lambda.RuntimeSupport\\Amazon.Lambda.RuntimeSupport.csproj",
"src\\Amazon.Lambda.Core\\Amazon.Lambda.Core.csproj",
"src\\Amazon.Lambda.Serialization.SystemTextJson\\Amazon.Lambda.Serialization.SystemTextJson.csproj",
"test\\Amazon.Lambda.Annotations.SourceGenerators.Tests\\Amazon.Lambda.Annotations.SourceGenerators.Tests.csproj",
"test\\TestServerlessApp.IntegrationTests\\TestServerlessApp.IntegrationTests.csproj",
"test\\TestExecutableServerlessApp\\TestExecutableServerlessApp.csproj",
"test\\TestServerlessApp\\TestServerlessApp.csproj"
]
}
Expand Down
7 changes: 7 additions & 0 deletions Libraries/Libraries.sln
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amazon.Lambda.LexV2Events",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CustomRuntimeAspNetCoreMinimalApiCustomSerializerTest", "test\Amazon.Lambda.RuntimeSupport.Tests\CustomRuntimeAspNetCoreMinimalApiCustomSerializerTest\CustomRuntimeAspNetCoreMinimalApiCustomSerializerTest.csproj", "{0BD83939-458C-4EF5-8663-7098AD1200F2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestExecutableServerlessApp", "test\TestExecutableServerlessApp\TestExecutableServerlessApp.csproj", "{DD378063-C54A-44C7-9A6F-32A6A1AE94B3}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
test\EventsTests.Shared\EventsTests.Shared.projitems*{44e9d925-b61d-4234-97b7-61424c963ba6}*SharedItemsImports = 5
Expand Down Expand Up @@ -353,6 +355,10 @@ Global
{0BD83939-458C-4EF5-8663-7098AD1200F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0BD83939-458C-4EF5-8663-7098AD1200F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0BD83939-458C-4EF5-8663-7098AD1200F2}.Release|Any CPU.Build.0 = Release|Any CPU
{DD378063-C54A-44C7-9A6F-32A6A1AE94B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DD378063-C54A-44C7-9A6F-32A6A1AE94B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DD378063-C54A-44C7-9A6F-32A6A1AE94B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DD378063-C54A-44C7-9A6F-32A6A1AE94B3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -415,6 +421,7 @@ Global
{BF85932E-2DFF-41CD-8090-A672468B8FBB} = {AAB54E74-20B1-42ED-BC3D-CE9F7BC7FD12}
{3C6AABF5-0372-41E0-874F-DF18ECCC7FB6} = {AAB54E74-20B1-42ED-BC3D-CE9F7BC7FD12}
{0BD83939-458C-4EF5-8663-7098AD1200F2} = {B5BD0336-7D08-492C-8489-42C987E29B39}
{DD378063-C54A-44C7-9A6F-32A6A1AE94B3} = {1DE4EE60-45BA-4EF7-BE00-B9EB861E4C69}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {503678A4-B8D1-4486-8915-405A3E9CF0EB}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@
<Generator>TextTemplatingFilePreprocessor</Generator>
<LastGenOutput>NoEventMethodBody.cs</LastGenOutput>
</None>
<None Update="Templates\ExecutableAssembly.tt">
<Generator>TextTemplatingFilePreprocessor</Generator>
<LastGenOutput>ExecutableAssembly.cs</LastGenOutput>
</None>
</ItemGroup>

<ItemGroup>
Expand Down Expand Up @@ -84,6 +88,11 @@
<AutoGen>True</AutoGen>
<DependentUpon>NoEventMethodBody.tt</DependentUpon>
</Compile>
<Compile Update="Templates\ExecutableAssembly.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>ExecutableAssembly.tt</DependentUpon>
</Compile>
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@

Rule ID | Category | Severity | Notes
--------|----------|----------|-------
AWSLambda0111|AWSLambdaCSharpGenerator|Error|If the GenerateMain global property is set to true but the project OutputType is not set to 'exe'
AWSLambda0112|AWSLambdaCSharpGenerator|Error|An invalid runtime is selected in the LambdaGlobalProperties attribute
AWSLambda0113|AWSLambdaCSharpGenerator|Error|The GenerateMain global property is set to true and the OutputType is set to 'exe', but no Lambda Function attributes are used
AWSLambda0114|AWSLambdaCSharpGenerator|Error|The GenerateMain global property is set to true, but the project already contains a static Main method
Original file line number Diff line number Diff line change
Expand Up @@ -87,5 +87,35 @@ public static class DiagnosticDescriptors
category: "AWSLambdaCSharpGenerator",
DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor SetOutputTypeExecutable = new DiagnosticDescriptor(id: "AWSLambda0111",
title: "Output Type is not an executable",
messageFormat: "AssemblyAttribute Amazon.Lambda.Annotations.LambdaGlobalPropertiesAttribute is configured to generate a static main method " +
"but the assembly itself is not configured to output an executable. Set the 'OutputType' property in the .csproj file to be 'exe'.",
category: "AWSLambdaCSharpGenerator",
DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor InvalidRuntimeSelection = new DiagnosticDescriptor(id: "AWSLambda0112",
title: "Invalid runtime selection",
messageFormat: "The runtime selected in the Amazon.Lambda.Annotations.LambdaGlobalPropertiesAttribute is not a supported value. " +
$"It should be set to one of {string.Join(" or ", Generator._allowdRuntimeValues.ToArray())}.",
category: "AWSLambdaCSharpGenerator",
DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor ExecutableWithNoFunctions = new DiagnosticDescriptor(id: "AWSLambda0113",
title: "Executable output with no LambdaFunction annotations",
messageFormat: "Your project is configured to output an executable and generate a static Main method, but you have not configured any methods with the 'LambdaFunction' attribute.",
category: "AWSLambdaCSharpGenerator",
DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor MainMethodExists = new DiagnosticDescriptor(id: "AWSLambda0114",
title: "static Main method exists",
messageFormat: "Failed to generate Main method for LambdaGenerateMainAttribute because project already contains Main method. Existing Main methods must be removed when using LambdaGenerateMainAttribute attribute.",
category: "AWSLambdaCSharpGenerator",
DiagnosticSeverity.Error,
isEnabledByDefault: true);
}
}
Loading
Loading