in Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Generator.cs [50:240]
public void Execute(GeneratorExecutionContext context)
{
var diagnosticReporter = new DiagnosticReporter(context);
try
{
// retrieve the populated receiver
if (!(context.SyntaxContextReceiver is SyntaxReceiver receiver))
{
return;
}
// Check to see if any of the current syntax trees has any error diagnostics. If so
// Skip generation. We only want to sync the CloudFormation template if the project
// can compile.
foreach (var syntaxTree in context.Compilation.SyntaxTrees)
{
if(syntaxTree.GetDiagnostics().Any(x => x.Severity == DiagnosticSeverity.Error))
{
return;
}
}
// If no project directory was detected then skip the generator.
// This is most likely to happen when the project is empty and doesn't have any classes in it yet.
if(string.IsNullOrEmpty(receiver.ProjectDirectory))
{
return;
}
var semanticModelProvider = new SemanticModelProvider(context);
if (receiver.StartupClasses.Count > 1)
{
foreach (var startup in receiver.StartupClasses)
{
// If there are more than one startup class, report them as errors
diagnosticReporter.Report(Diagnostic.Create(DiagnosticDescriptors.MultipleStartupNotAllowed,
Location.Create(startup.SyntaxTree, startup.Span),
startup.SyntaxTree.FilePath));
}
}
var isExecutable = false;
bool foundFatalError = false;
var assemblyAttributes = context.Compilation.Assembly.GetAttributes();
var globalPropertiesAttribute = assemblyAttributes
.FirstOrDefault(attr => attr.AttributeClass.Name == nameof(LambdaGlobalPropertiesAttribute));
var defaultRuntime = "dotnet6";
// Try to determine the target framework from the source generator context
if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.TargetFramework", out var targetFramework))
{
if (_targetFrameworksToRuntimes.ContainsKey(targetFramework))
{
defaultRuntime = _targetFrameworksToRuntimes[targetFramework];
}
}
// The runtime specified in the global property has precedence over the one we determined from the TFM (if we did)
if (globalPropertiesAttribute != null)
{
var generateMain = globalPropertiesAttribute.NamedArguments.FirstOrDefault(kvp => kvp.Key == "GenerateMain").Value;
var runtimeAttributeValue = globalPropertiesAttribute.NamedArguments.FirstOrDefault(kvp => kvp.Key == "Runtime").Value;
var runtime = runtimeAttributeValue.Value == null ? defaultRuntime : runtimeAttributeValue.Value.ToString();
if (!_allowedRuntimeValues.Contains(runtime))
{
diagnosticReporter.Report(Diagnostic.Create(DiagnosticDescriptors.InvalidRuntimeSelection, Location.None));
return;
}
defaultRuntime = runtime;
isExecutable = generateMain.Value != null && bool.Parse(generateMain.Value.ToString());
if (isExecutable && context.Compilation.Options.OutputKind != OutputKind.ConsoleApplication)
{
diagnosticReporter.Report(Diagnostic.Create(DiagnosticDescriptors.SetOutputTypeExecutable, Location.None));
return;
}
}
var configureHostBuilderMethodSymbol = semanticModelProvider.GetConfigureHostBuilderMethodModel(receiver.StartupClasses.FirstOrDefault());
var configureServicesMethodSymbol = semanticModelProvider.GetConfigureServicesMethodModel(receiver.StartupClasses.FirstOrDefault());
var configureMethodSymbol = configureServicesMethodSymbol;
if (configureServicesMethodSymbol != null && configureHostBuilderMethodSymbol != null)
{
diagnosticReporter.Report(Diagnostic.Create(DiagnosticDescriptors.MultipleConfigureMethodsNotAllowed, configureServicesMethodSymbol.Locations.FirstOrDefault(), configureServicesMethodSymbol.Name, configureHostBuilderMethodSymbol.Name));
}
if (configureHostBuilderMethodSymbol != null)
{
configureMethodSymbol = configureHostBuilderMethodSymbol;
}
var annotationReport = new AnnotationReport();
var templateHandler = new CloudFormationTemplateHandler(_fileManager, _directoryManager);
var lambdaModels = new List<LambdaFunctionModel>();
foreach (var lambdaMethodDeclarationSyntax in receiver.LambdaMethods)
{
var lambdaMethodSymbol = semanticModelProvider.GetMethodSemanticModel(lambdaMethodDeclarationSyntax);
var lambdaMethodLocation = lambdaMethodDeclarationSyntax.GetLocation();
var lambdaFunctionModel = LambdaFunctionModelBuilder.BuildAndValidate(lambdaMethodSymbol, lambdaMethodLocation, configureMethodSymbol, context, isExecutable, defaultRuntime, diagnosticReporter);
if (!lambdaFunctionModel.IsValid)
{
// If the model is not valid then skip it from further processing
foundFatalError = true;
continue;
}
var template = new LambdaFunctionTemplate(lambdaFunctionModel);
string sourceText;
try
{
sourceText = template.TransformText().ToEnvironmentLineEndings();
context.AddSource($"{lambdaFunctionModel.GeneratedMethod.ContainingType.Name}.g.cs", SourceText.From(sourceText, Encoding.UTF8, SourceHashAlgorithm.Sha256));
}
catch (Exception e) when (e is NotSupportedException || e is InvalidOperationException)
{
diagnosticReporter.Report(Diagnostic.Create(DiagnosticDescriptors.CodeGenerationFailed, Location.Create(lambdaMethodDeclarationSyntax.SyntaxTree, lambdaMethodDeclarationSyntax.Span), e.Message));
return;
}
// report every generated file to build output
diagnosticReporter.Report(Diagnostic.Create(DiagnosticDescriptors.CodeGeneration, Location.None, $"{lambdaFunctionModel.GeneratedMethod.ContainingType.Name}.g.cs", sourceText));
lambdaModels.Add(lambdaFunctionModel);
annotationReport.LambdaFunctions.Add(lambdaFunctionModel);
}
if (isExecutable)
{
var executableAssembly = GenerateExecutableAssemblySource(
context,
diagnosticReporter,
receiver,
lambdaModels);
if (executableAssembly == null)
{
foundFatalError = true;
return;
}
context.AddSource("Program.g.cs", SourceText.From(executableAssembly.TransformText().ToEnvironmentLineEndings(), Encoding.UTF8, SourceHashAlgorithm.Sha256));
}
// Run the CloudFormation sync if any LambdaMethods exists. Also run if no LambdaMethods exists but there is a
// CloudFormation template in case orphaned functions in the template need to be removed.
// Both checks are required because if there is no template but there are LambdaMethods the CF template the template will be created.
if (!foundFatalError && (receiver.LambdaMethods.Any() || templateHandler.DoesTemplateExist(receiver.ProjectDirectory)))
{
annotationReport.CloudFormationTemplatePath = templateHandler.FindTemplate(receiver.ProjectDirectory);
annotationReport.ProjectRootDirectory = receiver.ProjectDirectory;
annotationReport.IsTelemetrySuppressed = ProjectFileHandler.IsTelemetrySuppressed(receiver.ProjectPath, _fileManager);
var templateFormat = templateHandler.DetermineTemplateFormat(annotationReport.CloudFormationTemplatePath);
ITemplateWriter templateWriter;
if (templateFormat == CloudFormationTemplateFormat.Json)
{
templateWriter = new JsonWriter();
}
else
{
templateWriter = new YamlWriter();
}
var cloudFormationWriter = new CloudFormationWriter(_fileManager, _directoryManager, templateWriter, diagnosticReporter);
cloudFormationWriter.ApplyReport(annotationReport);
}
}
catch (Exception e)
{
// this is a generator failure, report this as error
diagnosticReporter.Report(Diagnostic.Create(DiagnosticDescriptors.UnhandledException, Location.None, e.PrettyPrint()));
#if DEBUG
throw;
#endif
}
}