From a4779f60a3cd036289e37cc3f42df3d0fd47eea8 Mon Sep 17 00:00:00 2001 From: PieterjanDeClippel Date: Wed, 8 Oct 2025 14:01:41 +0200 Subject: [PATCH 1/4] Adding console app generator --- .dockerignore | 30 ++++++++++++ MintPlayer.Dotnet.Tools.sln | 24 ++++++++++ .../ConsoleAppAttribute.cs | 6 +++ ...intPlayer.CommandLineApp.Attributes.csproj | 10 ++++ ...LaunchSettingsSummaryGenerator.Producer.cs | 21 +++++++++ .../LaunchSettingsSummaryGenerator.cs | 46 +++++++++++++++++++ .../MintPlayer.CommandLineApp.csproj | 24 ++++++++++ .../Models/ConsoleApp.cs | 10 ++++ .../Properties/launchSettings.json | 8 ++++ .../CommandLineAppDebugging.csproj | 18 ++++++++ .../CommandLineAppDebugging/Program.cs | 8 ++++ .../Properties/launchSettings.json | 10 ++++ 12 files changed, 215 insertions(+) create mode 100644 .dockerignore create mode 100644 SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp.Attributes/ConsoleAppAttribute.cs create mode 100644 SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp.Attributes/MintPlayer.CommandLineApp.Attributes.csproj create mode 100644 SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.Producer.cs create mode 100644 SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.cs create mode 100644 SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/MintPlayer.CommandLineApp.csproj create mode 100644 SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Models/ConsoleApp.cs create mode 100644 SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Properties/launchSettings.json create mode 100644 SourceGenerators/TestProjects/CommandLineAppDebugging/CommandLineAppDebugging.csproj create mode 100644 SourceGenerators/TestProjects/CommandLineAppDebugging/Program.cs create mode 100644 SourceGenerators/TestProjects/CommandLineAppDebugging/Properties/launchSettings.json diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..fe1152bd --- /dev/null +++ b/.dockerignore @@ -0,0 +1,30 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md +!**/.gitignore +!.git/HEAD +!.git/config +!.git/packed-refs +!.git/refs/heads/** \ No newline at end of file diff --git a/MintPlayer.Dotnet.Tools.sln b/MintPlayer.Dotnet.Tools.sln index 9e0dd67a..2a23b933 100644 --- a/MintPlayer.Dotnet.Tools.sln +++ b/MintPlayer.Dotnet.Tools.sln @@ -176,6 +176,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MintPlayer.AdminHelper", "A EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdminTest", "AdminHelper\AdminTest\AdminTest.csproj", "{CC34556F-529A-4186-AD6C-5F986808D8A5}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CommandLineApp", "CommandLineApp", "{752F83A4-B5A2-431F-B7A9-871FD0BD1617}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MintPlayer.CommandLineApp", "SourceGenerators\CommandLineApp\MintPlayer.CommandLineApp\MintPlayer.CommandLineApp.csproj", "{83309DAC-700C-4A03-B7CD-EC1C4F77CC85}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MintPlayer.CommandLineApp.Attributes", "SourceGenerators\CommandLineApp\MintPlayer.CommandLineApp.Attributes\MintPlayer.CommandLineApp.Attributes.csproj", "{53451C47-4933-47BE-8B44-ED911D9163AB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommandLineAppDebugging", "SourceGenerators\TestProjects\CommandLineAppDebugging\CommandLineAppDebugging.csproj", "{4CA948F7-9118-4442-885F-CE8CFFE5E0A2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -390,6 +398,18 @@ Global {9BCC691D-02F6-4F52-BB14-BB99825959B9}.Debug|Any CPU.Build.0 = Debug|Any CPU {9BCC691D-02F6-4F52-BB14-BB99825959B9}.Release|Any CPU.ActiveCfg = Release|Any CPU {9BCC691D-02F6-4F52-BB14-BB99825959B9}.Release|Any CPU.Build.0 = Release|Any CPU + {83309DAC-700C-4A03-B7CD-EC1C4F77CC85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {83309DAC-700C-4A03-B7CD-EC1C4F77CC85}.Debug|Any CPU.Build.0 = Debug|Any CPU + {83309DAC-700C-4A03-B7CD-EC1C4F77CC85}.Release|Any CPU.ActiveCfg = Release|Any CPU + {83309DAC-700C-4A03-B7CD-EC1C4F77CC85}.Release|Any CPU.Build.0 = Release|Any CPU + {53451C47-4933-47BE-8B44-ED911D9163AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {53451C47-4933-47BE-8B44-ED911D9163AB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {53451C47-4933-47BE-8B44-ED911D9163AB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {53451C47-4933-47BE-8B44-ED911D9163AB}.Release|Any CPU.Build.0 = Release|Any CPU + {4CA948F7-9118-4442-885F-CE8CFFE5E0A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4CA948F7-9118-4442-885F-CE8CFFE5E0A2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4CA948F7-9118-4442-885F-CE8CFFE5E0A2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4CA948F7-9118-4442-885F-CE8CFFE5E0A2}.Release|Any CPU.Build.0 = Release|Any CPU {447355EB-ED1F-4701-955F-598D00E1E207}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {447355EB-ED1F-4701-955F-598D00E1E207}.Debug|Any CPU.Build.0 = Debug|Any CPU {447355EB-ED1F-4701-955F-598D00E1E207}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -466,6 +486,10 @@ Global {9BCC691D-02F6-4F52-BB14-BB99825959B9} = {BE967E23-E7A7-4A55-912D-38FD21068E1D} {447355EB-ED1F-4701-955F-598D00E1E207} = {41AC5E0E-BCB6-4F0F-A991-C5D19C66828C} {CC34556F-529A-4186-AD6C-5F986808D8A5} = {41AC5E0E-BCB6-4F0F-A991-C5D19C66828C} + {752F83A4-B5A2-431F-B7A9-871FD0BD1617} = {C65054D9-CAD1-4124-B474-D03C178D9D78} + {83309DAC-700C-4A03-B7CD-EC1C4F77CC85} = {752F83A4-B5A2-431F-B7A9-871FD0BD1617} + {53451C47-4933-47BE-8B44-ED911D9163AB} = {752F83A4-B5A2-431F-B7A9-871FD0BD1617} + {4CA948F7-9118-4442-885F-CE8CFFE5E0A2} = {1614A8B2-CA9A-4F94-B68A-BCC8ED244648} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D5378D43-9246-4355-8C4B-880DB15B07DA} diff --git a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp.Attributes/ConsoleAppAttribute.cs b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp.Attributes/ConsoleAppAttribute.cs new file mode 100644 index 00000000..17d8c04c --- /dev/null +++ b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp.Attributes/ConsoleAppAttribute.cs @@ -0,0 +1,6 @@ +namespace MintPlayer.CommandLineApp.Attributes; + +[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] +public class ConsoleAppAttribute : Attribute +{ +} diff --git a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp.Attributes/MintPlayer.CommandLineApp.Attributes.csproj b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp.Attributes/MintPlayer.CommandLineApp.Attributes.csproj new file mode 100644 index 00000000..90d9a8bc --- /dev/null +++ b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp.Attributes/MintPlayer.CommandLineApp.Attributes.csproj @@ -0,0 +1,10 @@ + + + + netstandard2.0 + 13 + enable + enable + + + diff --git a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.Producer.cs b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.Producer.cs new file mode 100644 index 00000000..8358a056 --- /dev/null +++ b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.Producer.cs @@ -0,0 +1,21 @@ +using MintPlayer.CommandLineApp.Models; +using MintPlayer.SourceGenerators.Tools; +using System.CodeDom.Compiler; + +namespace MintPlayer.CommandLineApp.Generators; + +public class LaunchSettingsSummaryProducer : Producer +{ + private readonly IEnumerable consoleApps; + public LaunchSettingsSummaryProducer(IEnumerable consoleApps, string rootNamespace) : base(rootNamespace, "LaunchSettingsSummary.g.cs") + { + this.consoleApps = consoleApps; + } + + protected override void ProduceSource(IndentedTextWriter writer, CancellationToken cancellationToken) + { + writer.WriteLine(""" + global::System.Console.WriteLine("Hello, World!"); + """); + } +} diff --git a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.cs b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.cs new file mode 100644 index 00000000..8044e3f3 --- /dev/null +++ b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.cs @@ -0,0 +1,46 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using MintPlayer.SourceGenerators.Tools; + +namespace MintPlayer.CommandLineApp.Generators +{ + [Generator(LanguageNames.CSharp)] + public sealed class LaunchSettingsSummaryGenerator : IncrementalGenerator + { + const string consoleAppAttribute = "MintPlayer.CommandLineApp.Attributes.ConsoleAppAttribute"; + + public override void Initialize(IncrementalGeneratorInitializationContext context, IncrementalValueProvider settingsProvider) + { + var consoleAppsProvider = context.SyntaxProvider.ForAttributeWithMetadataName( + consoleAppAttribute, + (node, ct) => node is ClassDeclarationSyntax { AttributeLists.Count: > 0 }, + (context, ct) => + { + if (context.TargetNode is ClassDeclarationSyntax classDeclaration && + context.SemanticModel.GetDeclaredSymbol(classDeclaration, ct) is INamedTypeSymbol classSymbol) + { + return new Models.ConsoleApp + { + ClassName = classSymbol.Name, + Namespace = classSymbol.ContainingNamespace?.ToDisplayString() ?? string.Empty, + }; + } + + return default; + }) + .WithNullableComparer() + .Collect(); + + var consoleAppsSourceProvider = consoleAppsProvider + .Join(settingsProvider) + .Select(Producer (prov, ct) => new LaunchSettingsSummaryProducer(prov.Item1, prov.Item2.RootNamespace!)); + + context.ProduceCode(consoleAppsSourceProvider); + } + + public override void RegisterComparers() + { + } + } +} diff --git a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/MintPlayer.CommandLineApp.csproj b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/MintPlayer.CommandLineApp.csproj new file mode 100644 index 00000000..33eea4be --- /dev/null +++ b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/MintPlayer.CommandLineApp.csproj @@ -0,0 +1,24 @@ + + + + $(GetTargetPathDependsOn);GetDependencyTargetPaths + + + + + + + + + + + + + + + + + + + + diff --git a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Models/ConsoleApp.cs b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Models/ConsoleApp.cs new file mode 100644 index 00000000..37396e7e --- /dev/null +++ b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Models/ConsoleApp.cs @@ -0,0 +1,10 @@ +using MintPlayer.ValueComparerGenerator.Attributes; + +namespace MintPlayer.CommandLineApp.Models; + +[AutoValueComparer] +public partial class ConsoleApp +{ + public string? Namespace { get; set; } + public string? ClassName { get; set; } +} diff --git a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Properties/launchSettings.json b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Properties/launchSettings.json new file mode 100644 index 00000000..129d5adb --- /dev/null +++ b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "Debug Mapper Generators on CommandLineAppDebugging": { + "commandName": "DebugRoslynComponent", + "targetProject": "..\\..\\TestProjects\\CommandLineAppDebugging\\CommandLineAppDebugging.csproj" + } + } +} \ No newline at end of file diff --git a/SourceGenerators/TestProjects/CommandLineAppDebugging/CommandLineAppDebugging.csproj b/SourceGenerators/TestProjects/CommandLineAppDebugging/CommandLineAppDebugging.csproj new file mode 100644 index 00000000..5be0e189 --- /dev/null +++ b/SourceGenerators/TestProjects/CommandLineAppDebugging/CommandLineAppDebugging.csproj @@ -0,0 +1,18 @@ + + + + Exe + net10.0 + enable + enable + false + true + true + + + + + + + + diff --git a/SourceGenerators/TestProjects/CommandLineAppDebugging/Program.cs b/SourceGenerators/TestProjects/CommandLineAppDebugging/Program.cs new file mode 100644 index 00000000..2f002eb8 --- /dev/null +++ b/SourceGenerators/TestProjects/CommandLineAppDebugging/Program.cs @@ -0,0 +1,8 @@ +// See https://aka.ms/new-console-template for more information +using MintPlayer.CommandLineApp.Attributes; + +[ConsoleApp] +public class ListAspnetcoreApps +{ + +} \ No newline at end of file diff --git a/SourceGenerators/TestProjects/CommandLineAppDebugging/Properties/launchSettings.json b/SourceGenerators/TestProjects/CommandLineAppDebugging/Properties/launchSettings.json new file mode 100644 index 00000000..71e133b2 --- /dev/null +++ b/SourceGenerators/TestProjects/CommandLineAppDebugging/Properties/launchSettings.json @@ -0,0 +1,10 @@ +{ + "profiles": { + "CommandLineAppDebugging": { + "commandName": "Project" + }, + "Container (Dockerfile)": { + "commandName": "Docker" + } + } +} \ No newline at end of file From 30a2d2ae3f0aeb286dd71bd465acc3d5ddefc337 Mon Sep 17 00:00:00 2001 From: PieterjanDeClippel Date: Wed, 8 Oct 2025 14:20:14 +0200 Subject: [PATCH 2/4] Temp commit --- .../LaunchSettingsSummaryGenerator.Producer.cs | 4 +++- .../Generators/LaunchSettingsSummaryGenerator.cs | 7 ++++++- .../Properties/launchSettings.json | 2 +- .../Extensions/ComparerExtensions.cs | 15 +++++++++++++++ .../CommandLineAppDebugging/Program.cs | 2 ++ 5 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 SourceGenerators/MintPlayer.SourceGenerators.Tools/Extensions/ComparerExtensions.cs diff --git a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.Producer.cs b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.Producer.cs index 8358a056..d6772a1f 100644 --- a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.Producer.cs +++ b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.Producer.cs @@ -7,9 +7,11 @@ namespace MintPlayer.CommandLineApp.Generators; public class LaunchSettingsSummaryProducer : Producer { private readonly IEnumerable consoleApps; - public LaunchSettingsSummaryProducer(IEnumerable consoleApps, string rootNamespace) : base(rootNamespace, "LaunchSettingsSummary.g.cs") + private readonly bool alreadyHasTopLevelStatements; + public LaunchSettingsSummaryProducer(IEnumerable consoleApps, bool alreadyHasTopLevelStatements, string rootNamespace) : base(rootNamespace, "LaunchSettingsSummary.g.cs") { this.consoleApps = consoleApps; + this.alreadyHasTopLevelStatements = alreadyHasTopLevelStatements; } protected override void ProduceSource(IndentedTextWriter writer, CancellationToken cancellationToken) diff --git a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.cs b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.cs index 8044e3f3..4c18e286 100644 --- a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.cs +++ b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.cs @@ -12,6 +12,10 @@ public sealed class LaunchSettingsSummaryGenerator : IncrementalGenerator public override void Initialize(IncrementalGeneratorInitializationContext context, IncrementalValueProvider settingsProvider) { + var alreadyHasTopLevelStatements = context.CompilationProvider + .Select((compilation, ct) => compilation.SyntaxTrees.Any(st => st.GetRoot(ct).DescendantNodesAndSelf().OfType().Any())) + .WithDefaultComparer(); + var consoleAppsProvider = context.SyntaxProvider.ForAttributeWithMetadataName( consoleAppAttribute, (node, ct) => node is ClassDeclarationSyntax { AttributeLists.Count: > 0 }, @@ -33,8 +37,9 @@ public override void Initialize(IncrementalGeneratorInitializationContext contex .Collect(); var consoleAppsSourceProvider = consoleAppsProvider + .Join(alreadyHasTopLevelStatements) .Join(settingsProvider) - .Select(Producer (prov, ct) => new LaunchSettingsSummaryProducer(prov.Item1, prov.Item2.RootNamespace!)); + .Select(Producer (prov, ct) => new LaunchSettingsSummaryProducer(prov.Item1, prov.Item2, prov.Item3.RootNamespace!)); context.ProduceCode(consoleAppsSourceProvider); } diff --git a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Properties/launchSettings.json b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Properties/launchSettings.json index 129d5adb..271fde0e 100644 --- a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Properties/launchSettings.json +++ b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Properties/launchSettings.json @@ -1,6 +1,6 @@ { "profiles": { - "Debug Mapper Generators on CommandLineAppDebugging": { + "Debug CommandLineApp Generators on CommandLineAppDebugging": { "commandName": "DebugRoslynComponent", "targetProject": "..\\..\\TestProjects\\CommandLineAppDebugging\\CommandLineAppDebugging.csproj" } diff --git a/SourceGenerators/MintPlayer.SourceGenerators.Tools/Extensions/ComparerExtensions.cs b/SourceGenerators/MintPlayer.SourceGenerators.Tools/Extensions/ComparerExtensions.cs new file mode 100644 index 00000000..50f8cf16 --- /dev/null +++ b/SourceGenerators/MintPlayer.SourceGenerators.Tools/Extensions/ComparerExtensions.cs @@ -0,0 +1,15 @@ +using Microsoft.CodeAnalysis; +using MintPlayer.SourceGenerators.Tools.ValueComparers; + +namespace MintPlayer.SourceGenerators.Tools; + +public static class ComparerExtensions +{ + /// + /// Only use this method on simple types, like bool + /// + public static IncrementalValueProvider WithDefaultComparer(this IncrementalValueProvider provider) where T : struct + { + return provider.WithComparer(DefaultValueComparer.Instance); + } +} diff --git a/SourceGenerators/TestProjects/CommandLineAppDebugging/Program.cs b/SourceGenerators/TestProjects/CommandLineAppDebugging/Program.cs index 2f002eb8..d61cbbcb 100644 --- a/SourceGenerators/TestProjects/CommandLineAppDebugging/Program.cs +++ b/SourceGenerators/TestProjects/CommandLineAppDebugging/Program.cs @@ -1,6 +1,8 @@ // See https://aka.ms/new-console-template for more information using MintPlayer.CommandLineApp.Attributes; +Console.WriteLine("test"); + [ConsoleApp] public class ListAspnetcoreApps { From 32617ea7ebe1d7eb58f7022f448af0d4eaf29937 Mon Sep 17 00:00:00 2001 From: PieterjanDeClippel Date: Thu, 9 Oct 2025 05:14:56 +0200 Subject: [PATCH 3/4] Temp commit --- .../AnalyzerReleases.Shipped.md | 3 + .../AnalyzerReleases.Unshipped.md | 8 +++ ...LaunchSettingsSummaryGenerator.Producer.cs | 57 +++++++++++++++++-- .../LaunchSettingsSummaryGenerator.Rules.cs | 14 +++++ .../LaunchSettingsSummaryGenerator.cs | 25 +++++--- .../CommandLineAppDebugging/Program.cs | 2 +- 6 files changed, 95 insertions(+), 14 deletions(-) create mode 100644 SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/AnalyzerReleases.Shipped.md create mode 100644 SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/AnalyzerReleases.Unshipped.md create mode 100644 SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.Rules.cs diff --git a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/AnalyzerReleases.Shipped.md b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/AnalyzerReleases.Shipped.md new file mode 100644 index 00000000..60b59dd9 --- /dev/null +++ b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/AnalyzerReleases.Shipped.md @@ -0,0 +1,3 @@ +; Shipped analyzer releases +; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md + diff --git a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/AnalyzerReleases.Unshipped.md b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/AnalyzerReleases.Unshipped.md new file mode 100644 index 00000000..dfb7955a --- /dev/null +++ b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/AnalyzerReleases.Unshipped.md @@ -0,0 +1,8 @@ +; Unshipped analyzer release +; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +COMLAPP001 | ConsoleAppGenerator | Error | DiagnosticRules \ No newline at end of file diff --git a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.Producer.cs b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.Producer.cs index d6772a1f..0372c2ca 100644 --- a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.Producer.cs +++ b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.Producer.cs @@ -1,20 +1,65 @@ -using MintPlayer.CommandLineApp.Models; +using Microsoft.CodeAnalysis; +using MintPlayer.CommandLineApp.Models; using MintPlayer.SourceGenerators.Tools; +using MintPlayer.SourceGenerators.Tools.Extensions; using System.CodeDom.Compiler; namespace MintPlayer.CommandLineApp.Generators; -public class LaunchSettingsSummaryProducer : Producer +public class LaunchSettingsSummaryProducer : Producer, IDiagnosticReporter { private readonly IEnumerable consoleApps; - private readonly bool alreadyHasTopLevelStatements; - public LaunchSettingsSummaryProducer(IEnumerable consoleApps, bool alreadyHasTopLevelStatements, string rootNamespace) : base(rootNamespace, "LaunchSettingsSummary.g.cs") + private readonly IEnumerable filesWithTopLevelStatements; + public LaunchSettingsSummaryProducer(IEnumerable consoleApps, IEnumerable filesWithTopLevelStatements, string rootNamespace) : base(rootNamespace, "LaunchSettingsSummary.g.cs") { this.consoleApps = consoleApps; - this.alreadyHasTopLevelStatements = alreadyHasTopLevelStatements; + this.filesWithTopLevelStatements = filesWithTopLevelStatements; } - + + public IEnumerable GetDiagnostics() + { + return filesWithTopLevelStatements.Select(f => DiagnosticRules.CannotHaveTopLevelStatements.Create(f)); + } + protected override void ProduceSource(IndentedTextWriter writer, CancellationToken cancellationToken) + { + if (filesWithTopLevelStatements.Any()) + { + writer.WriteLine($"namespace {RootNamespace}"); + writer.WriteLine("{"); + writer.Indent++; + + foreach (var consoleApp in consoleApps) + { + writer.WriteLine($"public class {consoleApp.ClassName}App"); + writer.WriteLine("{"); + writer.Indent++; + + writer.WriteLine("public static void Entrypoint(string[] args)"); + writer.WriteLine("{"); + writer.Indent++; + + ProduceMainMethod(writer, consoleApp); + + writer.Indent--; + writer.WriteLine("}"); + + writer.Indent--; + writer.WriteLine("}"); + } + + writer.Indent--; + writer.WriteLine("}"); + } + else + { + writer.WriteLine(""" + global::System.Console.WriteLine("Hello, World!"); + """); + } + } + + private void ProduceMainMethod(IndentedTextWriter writer, ConsoleApp consoleApp) { writer.WriteLine(""" global::System.Console.WriteLine("Hello, World!"); diff --git a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.Rules.cs b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.Rules.cs new file mode 100644 index 00000000..48c66f90 --- /dev/null +++ b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.Rules.cs @@ -0,0 +1,14 @@ +using Microsoft.CodeAnalysis; + +namespace MintPlayer.CommandLineApp.Generators; + +public static partial class DiagnosticRules +{ + public static readonly DiagnosticDescriptor CannotHaveTopLevelStatements = new( + id: "COMLAPP001", + title: "A console app that uses the [ConsoleApp] attribute cannot have top-level statements", + messageFormat: "A console app that uses the [ConsoleApp] attribute cannot have top-level statements", + category: "ConsoleAppGenerator", + defaultSeverity: DiagnosticSeverity.Error, + isEnabledByDefault: true); +} \ No newline at end of file diff --git a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.cs b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.cs index 4c18e286..810374f6 100644 --- a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.cs +++ b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.cs @@ -1,7 +1,9 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; using MintPlayer.SourceGenerators.Tools; +using MintPlayer.SourceGenerators.Tools.ValueComparers; namespace MintPlayer.CommandLineApp.Generators { @@ -12,14 +14,17 @@ public sealed class LaunchSettingsSummaryGenerator : IncrementalGenerator public override void Initialize(IncrementalGeneratorInitializationContext context, IncrementalValueProvider settingsProvider) { - var alreadyHasTopLevelStatements = context.CompilationProvider - .Select((compilation, ct) => compilation.SyntaxTrees.Any(st => st.GetRoot(ct).DescendantNodesAndSelf().OfType().Any())) - .WithDefaultComparer(); + var filesWithTopLevelStatements = context.CompilationProvider + .SelectMany(static (compilation, ct) => compilation.SyntaxTrees + .Where(st => st.GetRoot(ct).DescendantNodesAndSelf().OfType().Any()) + .Select(f => f.GetLocation(TextSpan.FromBounds(0, f.Length - 1)))) + .WithComparer(ValueComparer.Instance) + .Collect(); var consoleAppsProvider = context.SyntaxProvider.ForAttributeWithMetadataName( consoleAppAttribute, - (node, ct) => node is ClassDeclarationSyntax { AttributeLists.Count: > 0 }, - (context, ct) => + static (node, ct) => node is ClassDeclarationSyntax { AttributeLists.Count: > 0 }, + static (context, ct) => { if (context.TargetNode is ClassDeclarationSyntax classDeclaration && context.SemanticModel.GetDeclaredSymbol(classDeclaration, ct) is INamedTypeSymbol classSymbol) @@ -37,11 +42,17 @@ public override void Initialize(IncrementalGeneratorInitializationContext contex .Collect(); var consoleAppsSourceProvider = consoleAppsProvider - .Join(alreadyHasTopLevelStatements) + .Join(filesWithTopLevelStatements) + .Join(settingsProvider) + .Select(static Producer (prov, ct) => new LaunchSettingsSummaryProducer(prov.Item1, prov.Item2, prov.Item3.RootNamespace!)); + + var consoleAppsDiagnosticProvider = consoleAppsProvider + .Join(filesWithTopLevelStatements) .Join(settingsProvider) - .Select(Producer (prov, ct) => new LaunchSettingsSummaryProducer(prov.Item1, prov.Item2, prov.Item3.RootNamespace!)); + .Select(static IDiagnosticReporter (prov, ct) => new LaunchSettingsSummaryProducer(prov.Item1, prov.Item2, prov.Item3.RootNamespace!)); context.ProduceCode(consoleAppsSourceProvider); + context.ReportDiagnostics(consoleAppsDiagnosticProvider); } public override void RegisterComparers() diff --git a/SourceGenerators/TestProjects/CommandLineAppDebugging/Program.cs b/SourceGenerators/TestProjects/CommandLineAppDebugging/Program.cs index d61cbbcb..da7ee4fe 100644 --- a/SourceGenerators/TestProjects/CommandLineAppDebugging/Program.cs +++ b/SourceGenerators/TestProjects/CommandLineAppDebugging/Program.cs @@ -1,7 +1,7 @@ // See https://aka.ms/new-console-template for more information using MintPlayer.CommandLineApp.Attributes; -Console.WriteLine("test"); +Console.WriteLine("Hello, World!"); [ConsoleApp] public class ListAspnetcoreApps From dbe81671b51eb767a1388f37dcc4b4713f10f2ad Mon Sep 17 00:00:00 2001 From: PieterjanDeClippel Date: Sun, 12 Oct 2025 18:43:19 +0200 Subject: [PATCH 4/4] Basic generator --- .../ConsoleAppAttribute.cs | 3 + ...intPlayer.CommandLineApp.Attributes.csproj | 4 ++ ...LaunchSettingsSummaryGenerator.Producer.cs | 64 +++++++++++-------- .../LaunchSettingsSummaryGenerator.Rules.cs | 8 +++ .../LaunchSettingsSummaryGenerator.cs | 5 +- .../Models/ConsoleApp.cs | 2 + .../CommandLineAppDebugging/Program.cs | 7 +- 7 files changed, 61 insertions(+), 32 deletions(-) diff --git a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp.Attributes/ConsoleAppAttribute.cs b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp.Attributes/ConsoleAppAttribute.cs index 17d8c04c..6381cd7b 100644 --- a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp.Attributes/ConsoleAppAttribute.cs +++ b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp.Attributes/ConsoleAppAttribute.cs @@ -3,4 +3,7 @@ [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] public class ConsoleAppAttribute : Attribute { + public ConsoleAppAttribute(string description) + { + } } diff --git a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp.Attributes/MintPlayer.CommandLineApp.Attributes.csproj b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp.Attributes/MintPlayer.CommandLineApp.Attributes.csproj index 90d9a8bc..d9f9f7e9 100644 --- a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp.Attributes/MintPlayer.CommandLineApp.Attributes.csproj +++ b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp.Attributes/MintPlayer.CommandLineApp.Attributes.csproj @@ -7,4 +7,8 @@ enable + + + + diff --git a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.Producer.cs b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.Producer.cs index 0372c2ca..0ca65dcc 100644 --- a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.Producer.cs +++ b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.Producer.cs @@ -10,7 +10,7 @@ public class LaunchSettingsSummaryProducer : Producer, IDiagnosticReporter { private readonly IEnumerable consoleApps; private readonly IEnumerable filesWithTopLevelStatements; - public LaunchSettingsSummaryProducer(IEnumerable consoleApps, IEnumerable filesWithTopLevelStatements, string rootNamespace) : base(rootNamespace, "LaunchSettingsSummary.g.cs") + public LaunchSettingsSummaryProducer(IEnumerable consoleApps, IEnumerable filesWithTopLevelStatements, string rootNamespace) : base(rootNamespace, "LaunchSettingsSummary.g.cs") { this.consoleApps = consoleApps; this.filesWithTopLevelStatements = filesWithTopLevelStatements; @@ -18,51 +18,61 @@ public LaunchSettingsSummaryProducer(IEnumerable consoleApps, public IEnumerable GetDiagnostics() { - return filesWithTopLevelStatements.Select(f => DiagnosticRules.CannotHaveTopLevelStatements.Create(f)); + if (consoleApps.Count() > 1) + return consoleApps.Select(ca => DiagnosticRules.OnlyOneConsoleAppAllowed.Create(ca.ClassSymbolLocation)); + else + return Enumerable.Empty(); + + //return filesWithTopLevelStatements.Select(f => DiagnosticRules.CannotHaveTopLevelStatements.Create(f)); } protected override void ProduceSource(IndentedTextWriter writer, CancellationToken cancellationToken) { - if (filesWithTopLevelStatements.Any()) + foreach (var consoleApp in consoleApps) { - writer.WriteLine($"namespace {RootNamespace}"); - writer.WriteLine("{"); - writer.Indent++; - - foreach (var consoleApp in consoleApps) + if (!string.IsNullOrEmpty(consoleApp.Namespace)) { - writer.WriteLine($"public class {consoleApp.ClassName}App"); + writer.WriteLine($"namespace {consoleApp.Namespace}"); writer.WriteLine("{"); writer.Indent++; + } - writer.WriteLine("public static void Entrypoint(string[] args)"); - writer.WriteLine("{"); - writer.Indent++; + writer.WriteLine($"public static class {consoleApp.ClassName}App"); + writer.WriteLine("{"); + writer.Indent++; - ProduceMainMethod(writer, consoleApp); + writer.WriteLine("public static async global::System.Threading.Tasks.Task Run(string[] args)"); + writer.WriteLine("{"); + writer.Indent++; - writer.Indent--; - writer.WriteLine("}"); + ProduceMainMethod(writer, consoleApp); - writer.Indent--; - writer.WriteLine("}"); - } + writer.Indent--; + writer.WriteLine("}"); writer.Indent--; writer.WriteLine("}"); - } - else - { - writer.WriteLine(""" - global::System.Console.WriteLine("Hello, World!"); - """); + + + if (!string.IsNullOrEmpty(consoleApp.Namespace)) + { + writer.Indent--; + writer.WriteLine("}"); + } } } private void ProduceMainMethod(IndentedTextWriter writer, ConsoleApp consoleApp) { - writer.WriteLine(""" - global::System.Console.WriteLine("Hello, World!"); - """); + var description = consoleApp.Description ?? string.Empty; + writer.WriteLine($"""" + var rootCommand = new System.CommandLine.RootCommand("{description.Replace("\"", "\\\"")}"); + """"); + writer.WriteLine($"""" + var parsed = rootCommand.Parse(args); + """"); + writer.WriteLine($"""" + await parsed.InvokeAsync(); + """"); } } diff --git a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.Rules.cs b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.Rules.cs index 48c66f90..d7fcf42b 100644 --- a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.Rules.cs +++ b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.Rules.cs @@ -11,4 +11,12 @@ public static partial class DiagnosticRules category: "ConsoleAppGenerator", defaultSeverity: DiagnosticSeverity.Error, isEnabledByDefault: true); + + public static readonly DiagnosticDescriptor OnlyOneConsoleAppAllowed = new( + id: "COMLAPP002", + title: "Only one console app with the [ConsoleApp] attribute is allowed", + messageFormat: "Only one console app with the [ConsoleApp] attribute is allowed", + category: "ConsoleAppGenerator", + defaultSeverity: DiagnosticSeverity.Error, + isEnabledByDefault: true); } \ No newline at end of file diff --git a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.cs b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.cs index 810374f6..5a5261ad 100644 --- a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.cs +++ b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Generators/LaunchSettingsSummaryGenerator.cs @@ -31,8 +31,11 @@ public override void Initialize(IncrementalGeneratorInitializationContext contex { return new Models.ConsoleApp { + Description = context.Attributes.FirstOrDefault(a => a.AttributeClass?.ToDisplayString() == consoleAppAttribute) + ?.ConstructorArguments.FirstOrDefault().Value?.ToString() ?? string.Empty, ClassName = classSymbol.Name, - Namespace = classSymbol.ContainingNamespace?.ToDisplayString() ?? string.Empty, + Namespace = classSymbol.ContainingNamespace.IsGlobalNamespace ? string.Empty : classSymbol.ContainingNamespace?.ToDisplayString(), + ClassSymbolLocation = classSymbol.Locations.FirstOrDefault(), }; } diff --git a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Models/ConsoleApp.cs b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Models/ConsoleApp.cs index 37396e7e..60270fb1 100644 --- a/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Models/ConsoleApp.cs +++ b/SourceGenerators/CommandLineApp/MintPlayer.CommandLineApp/Models/ConsoleApp.cs @@ -5,6 +5,8 @@ namespace MintPlayer.CommandLineApp.Models; [AutoValueComparer] public partial class ConsoleApp { + public Microsoft.CodeAnalysis.Location? ClassSymbolLocation { get; set; } public string? Namespace { get; set; } public string? ClassName { get; set; } + public string? Description { get; set; } } diff --git a/SourceGenerators/TestProjects/CommandLineAppDebugging/Program.cs b/SourceGenerators/TestProjects/CommandLineAppDebugging/Program.cs index da7ee4fe..04e1ac8c 100644 --- a/SourceGenerators/TestProjects/CommandLineAppDebugging/Program.cs +++ b/SourceGenerators/TestProjects/CommandLineAppDebugging/Program.cs @@ -1,10 +1,9 @@ // See https://aka.ms/new-console-template for more information using MintPlayer.CommandLineApp.Attributes; -Console.WriteLine("Hello, World!"); +await ListAspnetcoreAppsApp.Run(args); -[ConsoleApp] +[ConsoleApp("Lists all ASP.NET Core apps under the current folder")] public class ListAspnetcoreApps { - -} \ No newline at end of file +}