Make default executable name resolution smarter

This commit is contained in:
Oleksii Holub
2022-04-24 23:45:10 +03:00
parent 9141092919
commit 016ec8b186
3 changed files with 49 additions and 20 deletions

View File

@@ -6,6 +6,7 @@ using System.Reflection;
using CliFx.Attributes; using CliFx.Attributes;
using CliFx.Infrastructure; using CliFx.Infrastructure;
using CliFx.Schema; using CliFx.Schema;
using CliFx.Utils;
using CliFx.Utils.Extensions; using CliFx.Utils.Extensions;
namespace CliFx; namespace CliFx;
@@ -21,7 +22,7 @@ public partial class CliApplicationBuilder
private bool _isPreviewModeAllowed = true; private bool _isPreviewModeAllowed = true;
private string? _title; private string? _title;
private string? _executableName; private string? _executableName;
private string? _versionText; private string? _version;
private string? _description; private string? _description;
private IConsole? _console; private IConsole? _console;
private ITypeActivator? _typeActivator; private ITypeActivator? _typeActivator;
@@ -144,7 +145,7 @@ public partial class CliApplicationBuilder
/// </remarks> /// </remarks>
public CliApplicationBuilder SetVersion(string version) public CliApplicationBuilder SetVersion(string version)
{ {
_versionText = version; _version = version;
return this; return this;
} }
@@ -189,7 +190,7 @@ public partial class CliApplicationBuilder
var metadata = new ApplicationMetadata( var metadata = new ApplicationMetadata(
_title ?? GetDefaultTitle(), _title ?? GetDefaultTitle(),
_executableName ?? GetDefaultExecutableName(), _executableName ?? GetDefaultExecutableName(),
_versionText ?? GetDefaultVersionText(), _version ?? GetDefaultVersionText(),
_description _description
); );
@@ -210,14 +211,9 @@ public partial class CliApplicationBuilder
public partial class CliApplicationBuilder public partial class CliApplicationBuilder
{ {
private static readonly Lazy<Assembly?> EntryAssemblyLazy = new(Assembly.GetEntryAssembly);
// Entry assembly can be null, for example in tests
private static Assembly? EntryAssembly => EntryAssemblyLazy.Value;
private static string GetDefaultTitle() private static string GetDefaultTitle()
{ {
var entryAssemblyName = EntryAssembly?.GetName().Name; var entryAssemblyName = EnvironmentEx.EntryAssembly?.GetName().Name;
if (string.IsNullOrWhiteSpace(entryAssemblyName)) if (string.IsNullOrWhiteSpace(entryAssemblyName))
return "App"; return "App";
@@ -226,26 +222,39 @@ public partial class CliApplicationBuilder
private static string GetDefaultExecutableName() private static string GetDefaultExecutableName()
{ {
var entryAssemblyLocation = EntryAssembly?.Location; var entryAssemblyLocation = EnvironmentEx.EntryAssembly?.Location;
if (string.IsNullOrWhiteSpace(entryAssemblyLocation)) if (string.IsNullOrWhiteSpace(entryAssemblyLocation))
return "app"; return "app";
// The assembly can be an .exe or a .dll, depending on how it was packaged // If the application was launched via matching EXE apphost, use that as the executable name
var isLaunchedViaAppHost = string.Equals(
EnvironmentEx.ProcessPath,
Path.ChangeExtension(entryAssemblyLocation, ".exe"),
StringComparison.OrdinalIgnoreCase
);
if (isLaunchedViaAppHost)
return Path.GetFileNameWithoutExtension(entryAssemblyLocation);
// Otherwise, use the entry assembly as the executable name.
// Prefix it with `dotnet` if it's a DLL file.
var isDll = string.Equals( var isDll = string.Equals(
Path.GetExtension(entryAssemblyLocation), Path.GetExtension(entryAssemblyLocation),
".dll", ".dll",
StringComparison.OrdinalIgnoreCase StringComparison.OrdinalIgnoreCase
); );
var name = isDll return isDll
? "dotnet " + Path.GetFileName(entryAssemblyLocation) ? "dotnet " + Path.GetFileName(entryAssemblyLocation)
: Path.GetFileNameWithoutExtension(entryAssemblyLocation); : Path.GetFileNameWithoutExtension(entryAssemblyLocation);
return name;
} }
private static string GetDefaultVersionText() => private static string GetDefaultVersionText()
EntryAssembly is not null {
? "v" + EntryAssembly.GetName().Version.ToSemanticString() var entryAssemblyVersion = EnvironmentEx.EntryAssembly?.GetName().Version;
: "v1.0"; if (entryAssemblyVersion is null)
return "v1.0";
return "v" + entryAssemblyVersion.ToSemanticString();
}
} }

View File

@@ -0,0 +1,20 @@
using System;
using System.Diagnostics;
using System.Reflection;
namespace CliFx.Utils;
internal static class EnvironmentEx
{
private static readonly Lazy<string?> ProcessPathLazy = new(() =>
{
using var process = Process.GetCurrentProcess();
return process.MainModule?.FileName;
});
public static string? ProcessPath => ProcessPathLazy.Value;
private static readonly Lazy<Assembly?> EntryAssemblyLazy = new(Assembly.GetEntryAssembly);
public static Assembly? EntryAssembly => EntryAssemblyLazy.Value;
}

View File

@@ -209,7 +209,7 @@ OPTIONS
Overall, parameters and options are both used to consume input from the command line, but they differ in a few important ways: Overall, parameters and options are both used to consume input from the command line, but they differ in a few important ways:
| | Parameter | Option | | | Parameter | Option |
| ------------------ | ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------- | |--------------------|--------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------|
| **Identification** | Positional (by relative order). | Nominal (by name or short name). | | **Identification** | Positional (by relative order). | Nominal (by name or short name). |
| **Requiredness** | Required by default. Only the last parameter can be configured to be optional. | Optional by default. Any option can be configured to be required without limitations. | | **Requiredness** | Required by default. Only the last parameter can be configured to be optional. | Optional by default. Any option can be configured to be required without limitations. |
| **Arity** | Only the last parameter can be bound to a non-scalar property (i.e. an array). | Any option can be bound to a non-scalar property without limitations. | | **Arity** | Only the last parameter can be bound to a non-scalar property (i.e. an array). | Any option can be bound to a non-scalar property without limitations. |