Skip to content

Commit 20b137e

Browse files
committed
Merge branch 'feature/annotations-executable' into dev
2 parents b90a012 + 146ce67 commit 20b137e

File tree

109 files changed

+4469
-629
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

109 files changed

+4469
-629
lines changed

Diff for: Docs/lambda-annotations-design.md

+68
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,69 @@ public class LambdaFunctions
330330
}
331331
```
332332

333+
## Auto Generate Main
333334

335+
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.
336+
337+
To auto-generate the `static Main` method, first ensure the `OutputType` in your `csproj` file is set to `exe`.
338+
```xml
339+
<PropertyGroup>
340+
<!--Removed for brevity..-->
341+
<OutputType>exe</OutputType>
342+
</PropertyGroup>
343+
```
344+
345+
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.
346+
347+
```c#
348+
[assembly: LambdaGlobalProperties(GenerateMain = true, Runtime = "provided.al2")]
349+
```
350+
351+
### Behind The Scenes
352+
353+
Assuming the below Lambda function handler:
354+
355+
```c#
356+
public class Greeter
357+
{
358+
[LambdaFunction(ResourceName = "GreeterSayHello", MemorySize = 1024, PackageType = LambdaPackageType.Image)]
359+
[HttpApi(LambdaHttpMethod.Get, "/Greeter/SayHello", Version = HttpApiVersion.V1)]
360+
public void SayHello([FromQuery(Name = "names")]IEnumerable<string> firstNames, APIGatewayProxyRequest request, ILambdaContext context)
361+
{
362+
context.Logger.LogLine($"Request {JsonSerializer.Serialize(request)}");
363+
364+
if (firstNames == null)
365+
{
366+
return;
367+
}
368+
369+
foreach (var firstName in firstNames)
370+
{
371+
Console.WriteLine($"Hello {firstName}");
372+
}
373+
}
374+
}
375+
```
376+
377+
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.
378+
379+
The auto-generated CloudFormation template will include this as a default.
380+
381+
```c#
382+
public class GeneratedProgram
383+
{
384+
private static async Task Main(string[] args)
385+
{
386+
switch (Environment.GetEnvironmentVariable("ANNOTATIONS_HANDLER"))
387+
{
388+
case "ToUpper":
389+
Func<string, string> toupper_handler = new TestServerlessApp.Sub1.Functions_ToUpper_Generated().ToUpper;
390+
await Amazon.Lambda.RuntimeSupport.LambdaBootstrapBuilder.Create(toupper_handler, new Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer()).Build().RunAsync();
391+
break;
392+
}
393+
}
394+
}
395+
```
334396

335397
## Lambda .NET Attributes
336398

@@ -368,6 +430,12 @@ Here is a preliminary list of .NET attributes that will tell the source generato
368430
* 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.
369431
* FromServices
370432
* Map method parameter to registered service in IServiceProvider
433+
434+
### Global Attributes
435+
* GenerateMain
436+
* 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.
437+
* Runtime
438+
* Set the runtime in the generated CloudFormation template. Set to either `dotnet6` or `provided.al2`
371439

372440
Here is a list of features that are supported/planned in no particular priority order. The list will grow as we get deeper into implementation.
373441
- [x] LambdaFunction attribute triggers source generator and syncs with the CloudFormation template

Diff for: Libraries/Amazon.Lambda.Annotations.slnf

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
"solution": {
33
"path": "Libraries.sln",
44
"projects": [
5-
"src\\Amazon.Lambda.APIGatewayEvents\\Amazon.Lambda.APIGatewayEvents.csproj",
65
"src\\Amazon.Lambda.Annotations.SourceGenerator\\Amazon.Lambda.Annotations.SourceGenerator.csproj",
76
"src\\Amazon.Lambda.Annotations\\Amazon.Lambda.Annotations.csproj",
7+
"src\\Amazon.Lambda.APIGatewayEvents\\Amazon.Lambda.APIGatewayEvents.csproj",
8+
"src\\Amazon.Lambda.RuntimeSupport\\Amazon.Lambda.RuntimeSupport.csproj",
89
"src\\Amazon.Lambda.Core\\Amazon.Lambda.Core.csproj",
910
"src\\Amazon.Lambda.Serialization.SystemTextJson\\Amazon.Lambda.Serialization.SystemTextJson.csproj",
1011
"test\\Amazon.Lambda.Annotations.SourceGenerators.Tests\\Amazon.Lambda.Annotations.SourceGenerators.Tests.csproj",
1112
"test\\TestServerlessApp.IntegrationTests\\TestServerlessApp.IntegrationTests.csproj",
13+
"test\\TestExecutableServerlessApp\\TestExecutableServerlessApp.csproj",
1214
"test\\TestServerlessApp\\TestServerlessApp.csproj"
1315
]
1416
}

Diff for: Libraries/Libraries.sln

+7
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amazon.Lambda.LexV2Events",
130130
EndProject
131131
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CustomRuntimeAspNetCoreMinimalApiCustomSerializerTest", "test\Amazon.Lambda.RuntimeSupport.Tests\CustomRuntimeAspNetCoreMinimalApiCustomSerializerTest\CustomRuntimeAspNetCoreMinimalApiCustomSerializerTest.csproj", "{0BD83939-458C-4EF5-8663-7098AD1200F2}"
132132
EndProject
133+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestExecutableServerlessApp", "test\TestExecutableServerlessApp\TestExecutableServerlessApp.csproj", "{DD378063-C54A-44C7-9A6F-32A6A1AE94B3}"
134+
EndProject
133135
Global
134136
GlobalSection(SharedMSBuildProjectFiles) = preSolution
135137
test\EventsTests.Shared\EventsTests.Shared.projitems*{44e9d925-b61d-4234-97b7-61424c963ba6}*SharedItemsImports = 5
@@ -353,6 +355,10 @@ Global
353355
{0BD83939-458C-4EF5-8663-7098AD1200F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
354356
{0BD83939-458C-4EF5-8663-7098AD1200F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
355357
{0BD83939-458C-4EF5-8663-7098AD1200F2}.Release|Any CPU.Build.0 = Release|Any CPU
358+
{DD378063-C54A-44C7-9A6F-32A6A1AE94B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
359+
{DD378063-C54A-44C7-9A6F-32A6A1AE94B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
360+
{DD378063-C54A-44C7-9A6F-32A6A1AE94B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
361+
{DD378063-C54A-44C7-9A6F-32A6A1AE94B3}.Release|Any CPU.Build.0 = Release|Any CPU
356362
EndGlobalSection
357363
GlobalSection(SolutionProperties) = preSolution
358364
HideSolutionNode = FALSE
@@ -415,6 +421,7 @@ Global
415421
{BF85932E-2DFF-41CD-8090-A672468B8FBB} = {AAB54E74-20B1-42ED-BC3D-CE9F7BC7FD12}
416422
{3C6AABF5-0372-41E0-874F-DF18ECCC7FB6} = {AAB54E74-20B1-42ED-BC3D-CE9F7BC7FD12}
417423
{0BD83939-458C-4EF5-8663-7098AD1200F2} = {B5BD0336-7D08-492C-8489-42C987E29B39}
424+
{DD378063-C54A-44C7-9A6F-32A6A1AE94B3} = {1DE4EE60-45BA-4EF7-BE00-B9EB861E4C69}
418425
EndGlobalSection
419426
GlobalSection(ExtensibilityGlobals) = postSolution
420427
SolutionGuid = {503678A4-B8D1-4486-8915-405A3E9CF0EB}

Diff for: Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Amazon.Lambda.Annotations.SourceGenerator.csproj

+9
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@
5656
<Generator>TextTemplatingFilePreprocessor</Generator>
5757
<LastGenOutput>NoEventMethodBody.cs</LastGenOutput>
5858
</None>
59+
<None Update="Templates\ExecutableAssembly.tt">
60+
<Generator>TextTemplatingFilePreprocessor</Generator>
61+
<LastGenOutput>ExecutableAssembly.cs</LastGenOutput>
62+
</None>
5963
</ItemGroup>
6064

6165
<ItemGroup>
@@ -84,6 +88,11 @@
8488
<AutoGen>True</AutoGen>
8589
<DependentUpon>NoEventMethodBody.tt</DependentUpon>
8690
</Compile>
91+
<Compile Update="Templates\ExecutableAssembly.cs">
92+
<DesignTime>True</DesignTime>
93+
<AutoGen>True</AutoGen>
94+
<DependentUpon>ExecutableAssembly.tt</DependentUpon>
95+
</Compile>
8796
</ItemGroup>
8897

8998
<ItemGroup>

Diff for: Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Diagnostics/AnalyzerReleases.Unshipped.md

+4
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@
55

66
Rule ID | Category | Severity | Notes
77
--------|----------|----------|-------
8+
AWSLambda0111|AWSLambdaCSharpGenerator|Error|If the GenerateMain global property is set to true but the project OutputType is not set to 'exe'
9+
AWSLambda0112|AWSLambdaCSharpGenerator|Error|An invalid runtime is selected in the LambdaGlobalProperties attribute
10+
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
11+
AWSLambda0114|AWSLambdaCSharpGenerator|Error|The GenerateMain global property is set to true, but the project already contains a static Main method

Diff for: Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Diagnostics/DiagnosticDescriptors.cs

+30
Original file line numberDiff line numberDiff line change
@@ -87,5 +87,35 @@ public static class DiagnosticDescriptors
8787
category: "AWSLambdaCSharpGenerator",
8888
DiagnosticSeverity.Error,
8989
isEnabledByDefault: true);
90+
91+
public static readonly DiagnosticDescriptor SetOutputTypeExecutable = new DiagnosticDescriptor(id: "AWSLambda0111",
92+
title: "Output Type is not an executable",
93+
messageFormat: "AssemblyAttribute Amazon.Lambda.Annotations.LambdaGlobalPropertiesAttribute is configured to generate a static main method " +
94+
"but the assembly itself is not configured to output an executable. Set the 'OutputType' property in the .csproj file to be 'exe'.",
95+
category: "AWSLambdaCSharpGenerator",
96+
DiagnosticSeverity.Error,
97+
isEnabledByDefault: true);
98+
99+
public static readonly DiagnosticDescriptor InvalidRuntimeSelection = new DiagnosticDescriptor(id: "AWSLambda0112",
100+
title: "Invalid runtime selection",
101+
messageFormat: "The runtime selected in the Amazon.Lambda.Annotations.LambdaGlobalPropertiesAttribute is not a supported value. " +
102+
$"The valid values are: {string.Join(", ", Generator._allowdRuntimeValues.ToArray())}",
103+
category: "AWSLambdaCSharpGenerator",
104+
DiagnosticSeverity.Error,
105+
isEnabledByDefault: true);
106+
107+
public static readonly DiagnosticDescriptor ExecutableWithNoFunctions = new DiagnosticDescriptor(id: "AWSLambda0113",
108+
title: "Executable output with no LambdaFunction annotations",
109+
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",
110+
category: "AWSLambdaCSharpGenerator",
111+
DiagnosticSeverity.Error,
112+
isEnabledByDefault: true);
113+
114+
public static readonly DiagnosticDescriptor MainMethodExists = new DiagnosticDescriptor(id: "AWSLambda0114",
115+
title: "static Main method exists",
116+
messageFormat: "Failed to generate Main method for LambdaGenerateMainAttribute because project already contains Main method. Existing Main methods must be removed when using LambdaGenerateMainAttribute attribute.",
117+
category: "AWSLambdaCSharpGenerator",
118+
DiagnosticSeverity.Error,
119+
isEnabledByDefault: true);
90120
}
91121
}

0 commit comments

Comments
 (0)