Skip to content

Commit 242ecfa

Browse files
Configure IHostApplicationBuilder support for annotation library (#1999)
1 parent 8e9a889 commit 242ecfa

24 files changed

+633
-47
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"Projects": [
3+
{
4+
"Name": "Amazon.Lambda.Annotations",
5+
"Type": "Minor",
6+
"ChangelogMessages": [
7+
"Add ConfigureHostBuilder function to `Startup` class for allowing the Lambda function to configure an `IHostApplicationBuilder`."
8+
]
9+
}
10+
]
11+
}

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Amazon.Lambda.Annotations.SourceGenerator.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<TargetFrameworks>netstandard2.0;net6.0;net8.0</TargetFrameworks>

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Diagnostics/AnalyzerReleases.Shipped.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
; Shipped analyzer releases
22
; https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
33

4+
## Release 1.7.0
5+
### New Rules
6+
Rule ID | Category | Severity | Notes
7+
--------|----------|----------|-------
8+
AWSLambda0119 | AWSLambdaCSharpGenerator | Error | Conflicting Service Configuration Methods Detected
9+
410
## Release 1.5.1
511
### New Rules
612

@@ -79,4 +85,4 @@ Rule ID | Category | Severity | Notes
7985
AWSLambda0001 | AWSLambda | Error | Unhandled exception
8086
AWSLambda0101 | AWSLambdaCSharpGenerator | Error | Multiple LambdaStartup classes not allowed
8187
AWSLambda0102 | AWSLambdaCSharpGenerator | Error | Multiple events on Lambda function not supported
82-
AWSLambda0103 | AWSLambdaCSharpGenerator | Info | Generated code
88+
AWSLambda0103 | AWSLambdaCSharpGenerator | Info | Generated code

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Diagnostics/DiagnosticDescriptors.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,5 +145,13 @@ public static class DiagnosticDescriptors
145145
category: "AWSLambdaCSharpGenerator",
146146
DiagnosticSeverity.Error,
147147
isEnabledByDefault: true);
148+
149+
public static readonly DiagnosticDescriptor MultipleConfigureMethodsNotAllowed = new DiagnosticDescriptor(
150+
id: "AWSLambda0119",
151+
title: "Conflicting Service Configuration Methods Detected",
152+
messageFormat: "Both '{0}' and '{1}' methods are present. Only {1} will be used. Please consolidate your service configuration into {1}.",
153+
category: "AWSLambdaCSharpGenerator",
154+
DiagnosticSeverity.Error,
155+
isEnabledByDefault: true);
148156
}
149-
}
157+
}

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Generator.cs

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using Amazon.Lambda.Annotations.SourceGenerator.Diagnostics;
1+
using Amazon.Lambda.Annotations.SourceGenerator.Diagnostics;
22
using Amazon.Lambda.Annotations.SourceGenerator.Extensions;
33
using Amazon.Lambda.Annotations.SourceGenerator.FileIO;
44
using Amazon.Lambda.Annotations.SourceGenerator.Models;
@@ -93,9 +93,9 @@ public void Execute(GeneratorExecutionContext context)
9393
var isExecutable = false;
9494

9595
bool foundFatalError = false;
96-
96+
9797
var assemblyAttributes = context.Compilation.Assembly.GetAttributes();
98-
98+
9999
var globalPropertiesAttribute = assemblyAttributes
100100
.FirstOrDefault(attr => attr.AttributeClass.Name == nameof(LambdaGlobalPropertiesAttribute));
101101

@@ -109,7 +109,7 @@ public void Execute(GeneratorExecutionContext context)
109109
defaultRuntime = _targetFrameworksToRuntimes[targetFramework];
110110
}
111111
}
112-
112+
113113
// The runtime specified in the global property has precedence over the one we determined from the TFM (if we did)
114114
if (globalPropertiesAttribute != null)
115115
{
@@ -134,14 +134,26 @@ public void Execute(GeneratorExecutionContext context)
134134
}
135135
}
136136

137-
var configureMethodSymbol = semanticModelProvider.GetConfigureMethodModel(receiver.StartupClasses.FirstOrDefault());
137+
var configureHostBuilderMethodSymbol = semanticModelProvider.GetConfigureHostBuilderMethodModel(receiver.StartupClasses.FirstOrDefault());
138+
var configureServicesMethodSymbol = semanticModelProvider.GetConfigureServicesMethodModel(receiver.StartupClasses.FirstOrDefault());
139+
var configureMethodSymbol = configureServicesMethodSymbol;
140+
141+
if (configureServicesMethodSymbol != null && configureHostBuilderMethodSymbol != null)
142+
{
143+
diagnosticReporter.Report(Diagnostic.Create(DiagnosticDescriptors.MultipleConfigureMethodsNotAllowed, configureServicesMethodSymbol.Locations.FirstOrDefault(), configureServicesMethodSymbol.Name, configureHostBuilderMethodSymbol.Name));
144+
}
145+
146+
if (configureHostBuilderMethodSymbol != null)
147+
{
148+
configureMethodSymbol = configureHostBuilderMethodSymbol;
149+
}
138150

139151
var annotationReport = new AnnotationReport();
140152

141153
var templateHandler = new CloudFormationTemplateHandler(_fileManager, _directoryManager);
142154

143155
var lambdaModels = new List<LambdaFunctionModel>();
144-
156+
145157
foreach (var lambdaMethodDeclarationSyntax in receiver.LambdaMethods)
146158
{
147159
var lambdaMethodSymbol = semanticModelProvider.GetMethodSemanticModel(lambdaMethodDeclarationSyntax);
@@ -163,7 +175,7 @@ public void Execute(GeneratorExecutionContext context)
163175
sourceText = template.TransformText().ToEnvironmentLineEndings();
164176
context.AddSource($"{lambdaFunctionModel.GeneratedMethod.ContainingType.Name}.g.cs", SourceText.From(sourceText, Encoding.UTF8, SourceHashAlgorithm.Sha256));
165177
}
166-
catch (Exception e) when (e is NotSupportedException || e is InvalidOperationException)
178+
catch (Exception e) when (e is NotSupportedException || e is InvalidOperationException)
167179
{
168180
diagnosticReporter.Report(Diagnostic.Create(DiagnosticDescriptors.CodeGenerationFailed, Location.Create(lambdaMethodDeclarationSyntax.SyntaxTree, lambdaMethodDeclarationSyntax.Span), e.Message));
169181
return;
@@ -241,7 +253,7 @@ private static ExecutableAssembly GenerateExecutableAssemblySource(
241253
DiagnosticDescriptors.MissingDependencies,
242254
Location.None,
243255
"Amazon.Lambda.RuntimeSupport"));
244-
256+
245257
return null;
246258
}
247259

@@ -251,14 +263,14 @@ private static ExecutableAssembly GenerateExecutableAssemblySource(
251263
var symbol = model.GetDeclaredSymbol(methodDeclaration) as IMethodSymbol;
252264

253265
// Check to see if a static main method exists in the same namespace that has 0 or 1 parameters
254-
if (symbol.Name != "Main" || !symbol.IsStatic || symbol.ContainingNamespace.Name != lambdaModels[0].LambdaMethod.ContainingAssembly || (symbol.Parameters.Length > 1))
266+
if (symbol.Name != "Main" || !symbol.IsStatic || symbol.ContainingNamespace.Name != lambdaModels[0].LambdaMethod.ContainingAssembly || (symbol.Parameters.Length > 1))
255267
continue;
256-
268+
257269
diagnosticReporter.Report(
258270
Diagnostic.Create(
259271
DiagnosticDescriptors.MainMethodExists,
260272
Location.None));
261-
273+
262274
return null;
263275
}
264276

@@ -284,4 +296,4 @@ public void Initialize(GeneratorInitializationContext context)
284296
context.RegisterForSyntaxNotifications(() => new SyntaxReceiver(_fileManager, _directoryManager));
285297
}
286298
}
287-
}
299+
}

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/GeneratedMethodModelBuilder.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public static GeneratedMethodModel Build(IMethodSymbol lambdaMethodSymbol,
2727
return model;
2828
}
2929

30-
private static IList<string> BuildUsings(LambdaMethodModel lambdaMethodModel,
30+
private static IList<string> BuildUsings(LambdaMethodModel lambdaMethodModel,
3131
IMethodSymbol lambdaMethodSymbol,
3232
IMethodSymbol configureMethodSymbol,
3333
GeneratorExecutionContext context)
@@ -45,6 +45,12 @@ private static IList<string> BuildUsings(LambdaMethodModel lambdaMethodModel,
4545
if (configureMethodSymbol != null)
4646
{
4747
namespaces.Add("Microsoft.Extensions.DependencyInjection");
48+
49+
if (lambdaMethodModel.UsingHostBuilderForDependencyInjection)
50+
{
51+
namespaces.Add("Microsoft.Extensions.Hosting");
52+
}
53+
4854
}
4955

5056
namespaces.Add("Amazon.Lambda.Core");
@@ -73,7 +79,7 @@ private static TypeModel BuildResponseType(IMethodSymbol lambdaMethodSymbol,
7379
return TypeModelBuilder.Build(typeStream, context);
7480
}
7581

76-
82+
7783
if (lambdaMethodSymbol.HasAttribute(context, TypeFullNames.RestApiAttribute))
7884
{
7985
var symbol = lambdaMethodModel.ReturnsVoidOrGenericTask ?

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/LambdaFunctionModelBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,4 @@ private static LambdaSerializerInfo GetSerializerInfoAttribute(GeneratorExecutio
8383
return new LambdaSerializerInfo(serializerString);
8484
}
8585
}
86-
}
86+
}

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/LambdaMethodModel.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public bool ReturnsIHttpResults
5454
{
5555
return true;
5656
}
57-
if(ReturnsGenericTask && ReturnType.TypeArguments.Count == 1 && ReturnType.TypeArguments[0].FullName == TypeFullNames.IHttpResult)
57+
if(ReturnsGenericTask && ReturnType.TypeArguments.Count == 1 && ReturnType.TypeArguments[0].FullName == TypeFullNames.IHttpResult)
5858
{
5959
return true;
6060
}
@@ -100,6 +100,8 @@ public bool ReturnsVoidTaskOrSqsBatchResponse
100100
/// </summary>
101101
public bool UsingDependencyInjection { get; set; }
102102

103+
public bool UsingHostBuilderForDependencyInjection { get; set; }
104+
103105
/// <summary>
104106
/// Gets or sets the namespace for the nearest enclosing namespace. Returns null if the
105107
/// symbol isn't contained in a namespace.
@@ -151,4 +153,4 @@ public AttributeModel<LambdaFunctionAttribute> LambdaFunctionAttribute
151153
}
152154
}
153155
}
154-
}
156+
}

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/LambdaMethodModelBuilder.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ public static LambdaMethodModel Build(IMethodSymbol lambdaMethodSymbol,
2727
Events = EventTypeBuilder.Build(lambdaMethodSymbol, context),
2828
ContainingType = TypeModelBuilder.Build(lambdaMethodSymbol.ContainingType, context),
2929
UsingDependencyInjection = configureMethodSymbol != null,
30+
UsingHostBuilderForDependencyInjection = configureMethodSymbol != null && configureMethodSymbol.Name == "ConfigureHostBuilder",
3031
Attributes = lambdaMethodSymbol.GetAttributes().Select(att => AttributeModelBuilder.Build(att, context)).ToList()
3132
};
3233

3334
return model;
3435
}
3536
}
36-
}
37+
}

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/SemanticModelProvider.cs

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ public SemanticModelProvider(GeneratorExecutionContext context)
2020
/// If <see cref="startupSyntax"/> is null, returns null.
2121
/// </summary>
2222
/// <param name="startupSyntax">LambdaStartup attributed class syntax</param>
23-
public IMethodSymbol GetConfigureMethodModel(ClassDeclarationSyntax startupSyntax)
23+
public IMethodSymbol GetConfigureServicesMethodModel(ClassDeclarationSyntax startupSyntax)
2424
{
2525
if (startupSyntax == null)
2626
{
2727
return null;
2828
}
2929

30-
IMethodSymbol configureMethodSymbol = null;
30+
IMethodSymbol configureServicesMethodSymbol = null;
3131

3232
var iServiceCollectionSymbol = _context.Compilation.GetTypeByMetadataName("Microsoft.Extensions.DependencyInjection.IServiceCollection");
3333

@@ -36,7 +36,6 @@ public IMethodSymbol GetConfigureMethodModel(ClassDeclarationSyntax startupSynta
3636
// Filter the methods which can potentially have Configure(IServiceCollection) parameter signature
3737
var members = startupSyntax.Members.Where(member => member.Kind() == SyntaxKind.MethodDeclaration);
3838

39-
4039
foreach (var member in members)
4140
{
4241
var methodSyntax = (MethodDeclarationSyntax) member;
@@ -47,12 +46,37 @@ public IMethodSymbol GetConfigureMethodModel(ClassDeclarationSyntax startupSynta
4746
&& methodSymbol.Parameters[0].Type
4847
.Equals(iServiceCollectionSymbol, SymbolEqualityComparer.Default))
4948
{
50-
configureMethodSymbol = methodSymbol;
49+
configureServicesMethodSymbol = methodSymbol;
50+
break;
51+
}
52+
}
53+
54+
return configureServicesMethodSymbol;
55+
}
56+
57+
public IMethodSymbol GetConfigureHostBuilderMethodModel(ClassDeclarationSyntax startupSyntax)
58+
{
59+
if (startupSyntax == null)
60+
{
61+
return null;
62+
}
63+
64+
IMethodSymbol configureHostBuilderMethodSymbol = null;
65+
66+
var classModel = _context.Compilation.GetSemanticModel(startupSyntax.SyntaxTree);
67+
68+
foreach (var member in startupSyntax.Members.Where(member => member.Kind() == SyntaxKind.MethodDeclaration))
69+
{
70+
var methodSyntax = (MethodDeclarationSyntax)member;
71+
var methodSymbol = classModel.GetDeclaredSymbol(methodSyntax);
72+
if (methodSymbol != null && methodSymbol.Name == "ConfigureHostBuilder")
73+
{
74+
configureHostBuilderMethodSymbol = methodSymbol;
5175
break;
5276
}
5377
}
5478

55-
return configureMethodSymbol;
79+
return configureHostBuilderMethodSymbol;
5680
}
5781

5882
/// <summary>
@@ -65,4 +89,4 @@ public IMethodSymbol GetMethodSemanticModel(MethodDeclarationSyntax syntax)
6589
return methodModel.GetDeclaredSymbol(syntax);
6690
}
6791
}
68-
}
92+
}

0 commit comments

Comments
 (0)