fix: switch from RootCommand to Command to handle stripped executables

This commit is contained in:
Dylan R. E. Moonfire 2021-11-29 00:54:17 -06:00
parent 6b03ca4c28
commit e5e87770f9
6 changed files with 41 additions and 9 deletions

View file

@ -13,7 +13,15 @@ namespace MfGames.ToolBuilder
string[] arguments, string[] arguments,
TType defaultValue) TType defaultValue)
{ {
var rootCommand = new RootCommand // Normally, we should be using `RootCommand` here because it does
// the "right" thing with regards to picking up the executable name
// and path from the environment. However, it appears when the
// library/executable is stripped, it blows up. So we use Command
// directly and fake the RootCommand.
//
// We don't need a "real" executable name here, so we just hard-code
// a faked one.
var rootCommand = new Command("_executable", string.Empty)
{ {
option, option,
}; };

View file

@ -78,7 +78,7 @@ namespace MfGames.ToolBuilder.Globals
/// Adds the common options to the command. /// Adds the common options to the command.
/// </summary> /// </summary>
/// <param name="root"></param> /// <param name="root"></param>
public void AddOptions(RootCommand root) public void AddOptions(Command root)
{ {
root.AddGlobalOption(this.ConfigOption); root.AddGlobalOption(this.ConfigOption);
} }

View file

@ -34,7 +34,7 @@ namespace MfGames.ToolBuilder.Globals
/// Adds the common options to the command. /// Adds the common options to the command.
/// </summary> /// </summary>
/// <param name="root"></param> /// <param name="root"></param>
public void AddOptions(RootCommand root) public void AddOptions(Command root)
{ {
root.AddGlobalOption(this.LogLevelOption); root.AddGlobalOption(this.LogLevelOption);
} }

View file

@ -410,6 +410,8 @@ namespace MfGames.ToolBuilder.Tables
// Default is mostly like ConsoleTableExt but there is a separator // Default is mostly like ConsoleTableExt but there is a separator
// between the headers because it makes it harder to parse. // between the headers because it makes it harder to parse.
builder builder
.WithPaddingLeft(string.Empty)
.WithPaddingRight(string.Empty)
.WithCharMapDefinition( .WithCharMapDefinition(
new Dictionary<CharMapPositions, char> new Dictionary<CharMapPositions, char>
{ {
@ -418,7 +420,7 @@ namespace MfGames.ToolBuilder.Tables
.WithHeaderCharMapDefinition( .WithHeaderCharMapDefinition(
new Dictionary<HeaderCharMapPositions, char> new Dictionary<HeaderCharMapPositions, char>
{ {
{ HeaderCharMapPositions.BottomCenter, '+' }, { HeaderCharMapPositions.BottomCenter, ' ' },
{ HeaderCharMapPositions.Divider, ' ' }, { HeaderCharMapPositions.Divider, ' ' },
{ HeaderCharMapPositions.BorderBottom, '-' }, { HeaderCharMapPositions.BorderBottom, '-' },
}); });

View file

@ -165,6 +165,12 @@ namespace MfGames.ToolBuilder
.AsSelf() .AsSelf()
.SingleInstance(); .SingleInstance();
// Register the tool service since we have to use the factory to
// use it.
builder
.Register(this.CreateToolService)
.SingleInstance();
// Register the components required to make the CLI work. // Register the components required to make the CLI work.
builder.RegisterModule<ToolBuilderModule>(); builder.RegisterModule<ToolBuilderModule>();
} }
@ -174,7 +180,14 @@ namespace MfGames.ToolBuilder
IServiceCollection services) IServiceCollection services)
{ {
services.AddAutofac(); services.AddAutofac();
services.AddHostedService<ToolService>(); }
private ToolService CreateToolService(IComponentContext context)
{
var factory = context.Resolve<ToolService.Factory>();
var service = factory(this.InternalName);
return service;
} }
} }
} }

View file

@ -25,6 +25,8 @@ namespace MfGames.ToolBuilder
/// </summary> /// </summary>
public class ToolService : IHostedService public class ToolService : IHostedService
{ {
private readonly string cliName;
private readonly IList<ITopCommand> commands; private readonly IList<ITopCommand> commands;
private readonly ConfigToolGlobalService configService; private readonly ConfigToolGlobalService configService;
@ -36,12 +38,15 @@ namespace MfGames.ToolBuilder
private readonly LoggingToolGlobalService loggingService; private readonly LoggingToolGlobalService loggingService;
public ToolService( public ToolService(
string cliName,
ILogger logger, ILogger logger,
IHostApplicationLifetime lifetime, IHostApplicationLifetime lifetime,
IList<ITopCommand> commands, IList<ITopCommand> commands,
ConfigToolGlobalService configService, ConfigToolGlobalService configService,
LoggingToolGlobalService loggingService) LoggingToolGlobalService loggingService)
{ {
this.cliName = cliName
?? throw new ArgumentNullException(nameof(cliName));
this.lifetime = lifetime; this.lifetime = lifetime;
this.commands = commands; this.commands = commands;
this.configService = configService; this.configService = configService;
@ -49,6 +54,8 @@ namespace MfGames.ToolBuilder
this.logger = logger.ForContext<ToolService>(); this.logger = logger.ForContext<ToolService>();
} }
public delegate ToolService Factory(string cliName);
/// <inheritdoc /> /// <inheritdoc />
public Task StartAsync(CancellationToken cancellationToken) public Task StartAsync(CancellationToken cancellationToken)
{ {
@ -71,11 +78,13 @@ namespace MfGames.ToolBuilder
return Task.CompletedTask; return Task.CompletedTask;
} }
private RootCommand CreateRootCommand() private Command CreateRootCommand()
{ {
// Create the root command and add in the top-level commands // Create the root command and add in the top-level commands
// underneath it. // underneath it. We can't use the "real" `RootCommand` here because
var root = new RootCommand(); // it doesn't work in stripped executables (because this is a
// library) so we fake it with a "normal" command.
var root = new Command(this.cliName, string.Empty);
foreach (var command in this.commands) foreach (var command in this.commands)
{ {
@ -116,7 +125,7 @@ namespace MfGames.ToolBuilder
try try
{ {
// Build the command tree. // Build the command tree.
RootCommand root = this.CreateRootCommand(); Command root = this.CreateRootCommand();
string[] args = Environment.GetCommandLineArgs(); string[] args = Environment.GetCommandLineArgs();
// Execute the command. // Execute the command.