public void Execute(GeneratorExecutionContext context) { var receiver = context.SyntaxContextReceiver as ParserSyntaxReceiver; if (receiver?.Root?.Class is not null && receiver.Namespace is not null) { defaultLanguage = context.GetMSBuildProperty("DefaultLanguage"); defaultLanguage = string.IsNullOrWhiteSpace(defaultLanguage) ? "en" : defaultLanguage; var semanticModel = context.Compilation.GetSemanticModel(receiver.Root.Class.SyntaxTree); var cmds = GetCommands(receiver.Root.Class, semanticModel, out CommandInfoBase? defaultCommand); var rootDoc = GetXmlDocumentation(receiver.Root.Class); var rh = rootDoc.Descendants("summary").FirstOrDefault(); if (rh is not null) { receiver.Root.HelpText = HelpText.FromXElement(rh, defaultLanguage); } receiver.Root.Children = cmds; receiver.Root.Default = defaultCommand; var enums = GetEnumsInfo(defaultCommand is null ? cmds : cmds.Append(defaultCommand)) .Select(x => (x.type.GetMembers().Select(y => y.Name).ToArray(), x.typeName, x.displayTypeName)) .ToArray(); Queue <string> errors = new(); var ctx = CreateContext(CreateParserTemplateModel(receiver), errors); string ep = parserTemplate.Render(ctx); string completion = completionTemplate.Render(ctx); ctx = CreateContext(CreateHelpTextsModel(receiver), errors); string ht = helpTextsTemplate.Render(ctx); ctx = CreateContext(CreateEnumsModel(receiver.Namespace, enums), errors); string enp = enumParserTemplate.Render(ctx); var logPath = context.GetMSBuildProperty("LogGeneratedParser"); if (string.IsNullOrWhiteSpace(logPath) is not true) { File.WriteAllText(Path.Combine(logPath, "EntryPoint.cs"), ep); File.WriteAllText(Path.Combine(logPath, "HelpTexts.cs"), ht); File.WriteAllText(Path.Combine(logPath, "EnumParser.cs"), enp); File.WriteAllText(Path.Combine(logPath, "Completer.cs"), completion); } foreach (var item in errors) { context.ReportDiagnostic(Diagnostic.Create(GenerationError, null, item)); } context.AddSource("EntryPoint.cs", ep); context.AddSource("HelpTexts.cs", ht); context.AddSource("EnumParser.cs", enp); context.AddSource("Completer.cs", completion); } }
public void Execute(GeneratorExecutionContext context) { var isIos = context.IsiOS(); var isAndroid = context.IsAndroid(); if (!isIos && !isAndroid) { return; } var syntaxReceiver = (AppStartupSyntaxReceiver)context.SyntaxReceiver; var namespaceName = context.Compilation.GlobalNamespace.Name; if (string.IsNullOrEmpty(namespaceName)) { namespaceName = context.Compilation.AssemblyName ?? "GeneratedMauiApp"; } if (context.IsAppHead()) { // Look for a `[assembly: MauiStartup(typeof(StartupClass))]` attribute var startupAttributes = context.FindAttributes("MauiStartupAttribute")?.ToList(); // If we have multiple, throw an error if ((startupAttributes?.Count ?? 0) > 1) { context.ReportDiagnostic(Diagnostic.Create("MAUI1010", "Compiler", "More than one MauiStartupAttribute found.", DiagnosticSeverity.Error, DiagnosticSeverity.Error, true, 0)); return; } var startupAttribute = startupAttributes.FirstOrDefault(); // If we don't have any, if (startupAttribute != null) { // Get the constructor arg which we expect to be the startup class type name var ctorTypeArg = startupAttribute.ConstructorArguments.FirstOrDefault(); var startupClassName = ctorTypeArg.Value?.ToString(); if (!string.IsNullOrEmpty(startupClassName)) { // Prefix with global:: when we generate code to be safe if (!startupClassName.StartsWith("global::")) { startupClassName = $"global::{startupClassName}"; } if (isIos) { // Prefix with global:: when we generate code to be safe var appDelegateClassName = $"{namespaceName}.AppDelegate"; if (!appDelegateClassName.StartsWith("global::")) { appDelegateClassName = $"global::{appDelegateClassName}"; } // Create an app delegate // We check to see if there's already an app delegate // If there is, if it's partial and the same name as the one we generate, that's fine too if (!syntaxReceiver.HasiOSAppDelegateSubclass || syntaxReceiver.iOSPartialAppDelegateSubclassType.Equals(appDelegateClassName)) { context.AddSource("Maui_Generated_MauiGeneratedAppDelegate.cs", GenerateiOSAppDelegate(namespaceName, startupClassName)); } else { context.ReportDiagnostic(Diagnostic.Create("MAUI1020", "Compiler", "UIApplicationDelegate implementation already exists, not generating one.", DiagnosticSeverity.Warning, DiagnosticSeverity.Warning, true, 3)); } // Create a main method if (!syntaxReceiver.HasiOSMainMethod) { context.AddSource("Maui_Generated_MauiGeneratedMain.cs", GenerateiOSMain(namespaceName, appDelegateClassName)); } else { context.ReportDiagnostic(Diagnostic.Create("MAUI1021", "Compiler", "Main method implementation already exists, not generating one.", DiagnosticSeverity.Warning, DiagnosticSeverity.Warning, true, 3)); } } else if (isAndroid) { var appName = context.GetMSBuildProperty("ApplicationTitle") ?? "App1"; context.AddSource("Maui_Generated_MauiGeneratedAndroidActivity.cs", GenerateAndroidMainActivity(appName, namespaceName, startupClassName)); if (!syntaxReceiver.HasAndroidMainLauncher) { context.AddSource("Maui_Generated_MauiGeneratedAndroidApplication.cs", GenerateAndroidApplication(namespaceName, startupClassName)); } else { context.ReportDiagnostic(Diagnostic.Create("MAUI1021", "Compiler", "Activity with MainLauncher=true already exists, not generating one.", DiagnosticSeverity.Warning, DiagnosticSeverity.Warning, true, 3)); } } } } } }
public static bool IsAndroid(this GeneratorExecutionContext context) => (context.GetMSBuildProperty("TargetFrameworkIdentifier")?.Contains("MonoAndroid") ?? false) || (context.GetCompilationContextTFM()?.Contains("MonoAndroid") ?? false);
public static bool IsAppHead(this GeneratorExecutionContext context) => context.GetMSBuildProperty("OutputType")?.Equals("Exe", StringComparison.OrdinalIgnoreCase) ?? false;