Code Generator
Albatross.CommandLine.CodeGen is an incremental source generator that automatically creates command classes, service registration, and command registration methods from attributed parameters classes. This eliminates boilerplate code while maintaining full flexibility.
Unlike reflection-based approaches, the generator performs all wiring at compile time, so there is no runtime scanning, dynamic discovery, or Activator usage. This improves startup performance and makes the generated code AOT-friendly, since everything is statically known and compiled ahead of time. The result is predictable behavior, easier debugging, and better alignment with Native AOT and trimming scenarios.
How to Use it
Albatross.CommandLine.CodeGen is referenced automatically as a code generator when a project references the Albatross.CommandLine nuget package.
How It Works
The code generator uses Roslyn's incremental source generation capabilities to scan your code at compile time, find classes annotated with [VerbAttribute], and generate the necessary code structure.
It generates:
- One command class for each annotated
VerbAttribute. The namespace of the generated command is always the same as theVerbAttribute's targetParametersclass. - Services Registration method - A static service registration method that can be used to register the parameters class using scoped lifetime and the command handler by keyed scoped lifetime. Notice that the instance of the
parametersclass is created by the service registration code using initializers.
public static class CodeGenExtensions {
public static IServiceCollection RegisterCommands(this IServiceCollection services) {
services.AddKeyedScoped<IAsyncCommandHandler, Sample.CommandLine.ExampleBaseSpecificRegistrationsHandler>("example csharp web-client");
services.AddScoped<Sample.CommandLine.ExampleCommandSpecificRegistrationsParams>(provider => {
var context = provider.GetRequiredService<ICommandContext>();
var parameters = new Sample.CommandLine.ExampleCommandSpecificRegistrationsParams() {
OutputDirectory = context.Result.GetRequiredValue<System.IO.DirectoryInfo>("--output-directory"),
Project = context.Result.GetRequiredValue<System.IO.FileInfo>("--project"),
};
return parameters;
});
}
}
- Command Registration method - This generated method add the commands to a dictionary, which will be used to create a command tree right before Parsing.
public static CommandHost AddCommands(this CommandHost host) {
host.CommandBuilder.Add<ParentCommand>("test");
host.CommandBuilder.Add<ParentCommand1>("example");
host.CommandBuilder.Add<ExampleCommandSpecificRegistrationsCommand>("example csharp web-client");
host.CommandBuilder.Add<ExampleCommandSpecificRegistrationsCommand1>("example typescript web-client");
}
Put It Together
The genenerated code should be invoked during bootstrapping.
internal class Program {
static async Task<int> Main(string[] args) {
await using var host = new CommandHost("Sample Command Line Application")
.RegisterServices((_, services) => services.RegisterCommands())
// this method is generated by source generator
.AddCommands()
.Parse(args)
.WithDefaults()
.Build();
return await host.InvokeAsync();
}
}