Skip to content

Commit

Permalink
Adding dedicated NativeAOT project
Browse files Browse the repository at this point in the history
Adding a nativeAot version of the demo application. While I was in here I fixed a few bugs in the original Demo though.
  • Loading branch information
phil-scott-78 committed Apr 10, 2024
1 parent 24a6cb6 commit e712ad0
Show file tree
Hide file tree
Showing 17 changed files with 389 additions and 35 deletions.
1 change: 0 additions & 1 deletion examples/Cli/Demo/Commands/Run/RunCommand.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using Demo.Utilities;
using Spectre.Console.Cli;

Expand Down
6 changes: 4 additions & 2 deletions examples/Cli/Demo/Commands/Serve/ServeCommand.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.ComponentModel;
using Demo.Utilities;
using Spectre.Console;
using Spectre.Console.Cli;

namespace Demo.Commands.Serve;
Expand All @@ -11,6 +12,7 @@ public sealed class ServeCommand : Command<ServeCommand.Settings>
public sealed class Settings : CommandSettings
{
[CommandOption("-p|--port <PORT>")]
[DefaultValue(8080)]
[Description("Port to use. Defaults to [grey]8080[/]. Use [grey]0[/] for a dynamic port.")]
public int Port { get; set; }

Expand All @@ -26,11 +28,11 @@ public override int Execute(CommandContext context, Settings settings)
var browser = settings.OpenBrowser.Value;
if (browser != null)
{
Console.WriteLine($"Open in {browser}");
AnsiConsole.WriteLine($"Open in {browser}");
}
else
{
Console.WriteLine($"Open in default browser.");
AnsiConsole.WriteLine($"Open in default browser.");
}
}

Expand Down
5 changes: 1 addition & 4 deletions examples/Cli/Demo/Demo.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@
<ExampleGroup>Cli</ExampleGroup>
<ExampleVisible>false</ExampleVisible>
</PropertyGroup>
<PropertyGroup>
<PublishAot>true</PublishAot>
<TrimmerSingleWarn>false</TrimmerSingleWarn>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console.Cli\Spectre.Console.Cli.csproj" />
</ItemGroup>
Expand Down
18 changes: 6 additions & 12 deletions examples/Cli/Demo/Program.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Demo.Commands;
using Demo.Commands.Add;
using Demo.Commands.Run;
Expand All @@ -8,38 +6,34 @@

namespace Demo;


public static class Program
{
public static int Main(string[] args)
{
var app = new CommandApp();
app.Configure(config =>
{
config.PropagateExceptions();
config.SetApplicationName("fake-dotnet");
config.ValidateExamples();
config.AddExample("run", "--no-build");
// Run
config.AddCommand<RunCommand, RunCommand.Settings>("run");
config.AddCommand<RunCommand>("run");
// Add
config.AddBranch<AddSettings>("add", add =>
{
add.SetDescription("Add a package or reference to a .NET project");
add.AddCommand<AddPackageCommand, AddPackageCommand.Settings>("package");
add.AddCommand<AddReferenceCommand, AddReferenceCommand.Settings>("reference");
add.AddCommand<AddPackageCommand>("package");
add.AddCommand<AddReferenceCommand>("reference");
});
// Serve
config.AddCommand<ServeCommand, ServeCommand.Settings>("serve")
config.AddCommand<ServeCommand>("serve")
.WithExample("serve", "-o", "firefox")
.WithExample("serve", "--port", "80", "-o", "firefox");
});

app.Run(args);
});

return 0;
return app.Run(args);
}
}
8 changes: 3 additions & 5 deletions examples/Cli/Demo/Utilities/SettingsDumper.cs
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
using System.Diagnostics.CodeAnalysis;
using Spectre.Console;
using Spectre.Console.Cli;

namespace Demo.Utilities;

public static class SettingsDumper
{
public static void Dump<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(T settings)
where T : CommandSettings
public static void Dump(CommandSettings settings)
{
var table = new Table().RoundedBorder();
table.AddColumn("[grey]Name[/]");
table.AddColumn("[grey]Value[/]");

var properties = typeof(T).GetProperties();
var properties = settings.GetType().GetProperties();
foreach (var property in properties)
{
var value = property.GetValue(settings)
?.ToString()
?.Replace("[", "[[");
?.EscapeMarkup();

table.AddRow(
property.Name,
Expand Down
7 changes: 0 additions & 7 deletions examples/Cli/Demo/Verbosity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,6 @@ public VerbosityConverter()

public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
// NativeAOT will pass an integer when using the DefaultValue and an enum, so we need to manually convert
// that back to the enum
if (value is int intValue && Enum.IsDefined(typeof(Verbosity), intValue))
{
return (Verbosity)intValue;
}

if (value is string stringValue)
{
var result = _lookup.TryGetValue(stringValue, out var verbosity);
Expand Down
46 changes: 46 additions & 0 deletions examples/Cli/DemoAot/Commands/Add/AddPackageCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.ComponentModel;
using DemoAot.Utilities;
using Spectre.Console.Cli;

namespace DemoAot.Commands.Add;

[Description("Add a NuGet package reference to the project.")]
public sealed class AddPackageCommand : Command<AddPackageCommand.Settings>
{
public sealed class Settings : AddSettings
{
[CommandArgument(0, "<PACKAGENAME>")]
[Description("The package reference to add.")]
public string PackageName { get; set; }

[CommandOption("-v|--version <VERSION>")]
[Description("The version of the package to add.")]
public string Version { get; set; }

[CommandOption("-f|--framework <FRAMEWORK>")]
[Description("Add the reference only when targeting a specific framework.")]
public string Framework { get; set; }

[CommandOption("--no-restore")]
[Description("Add the reference without performing restore preview and compatibility check.")]
public bool NoRestore { get; set; }

[CommandOption("--source <SOURCE>")]
[Description("The NuGet package source to use during the restore.")]
public string Source { get; set; }

[CommandOption("--package-directory <PACKAGEDIR>")]
[Description("The directory to restore packages to.")]
public string PackageDirectory { get; set; }

[CommandOption("--interactive")]
[Description("Allows the command to stop and wait for user input or action (for example to complete authentication).")]
public bool Interactive { get; set; }
}

public override int Execute(CommandContext context, Settings settings)
{
SettingsDumper.Dump(settings);
return 0;
}
}
35 changes: 35 additions & 0 deletions examples/Cli/DemoAot/Commands/Add/AddReferenceCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using DemoAot.Utilities;
using Spectre.Console;
using Spectre.Console.Cli;

namespace DemoAot.Commands.Add;

public sealed class AddReferenceCommand : Command<AddReferenceCommand.Settings>
{
public sealed class Settings : AddSettings
{
[CommandArgument(0, "<PROJECTPATH>")]
[Description("The package reference to add.")]
public DirectoryInfo ProjectPath { get; set; }

[CommandOption("-f|--framework <FRAMEWORK>")]
[Description("Add the reference only when targeting a specific framework.")]
public string Framework { get; set; }

[CommandOption("--interactive")]
[Description("Allows the command to stop and wait for user input or action (for example to complete authentication).")]
public bool Interactive { get; set; }
}

// In non-AOT scenarios, we can dynamically call the constructor to DirectoryInfo via reflection.
// With trimming enabled we need to be explicit about requiring that constructor.
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(DirectoryInfo))]
public override int Execute(CommandContext context, Settings settings)
{
SettingsDumper.Dump(settings);
return 0;
}
}
11 changes: 11 additions & 0 deletions examples/Cli/DemoAot/Commands/Add/AddSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.ComponentModel;
using Spectre.Console.Cli;

namespace DemoAot.Commands.Add;

public abstract class AddSettings : CommandSettings
{
[CommandArgument(0, "<PROJECT>")]
[Description("The project file to operate on. If a file is not specified, the command will search the current directory for one.")]
public string Project { get; set; }
}
69 changes: 69 additions & 0 deletions examples/Cli/DemoAot/Commands/Run/RunCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System.ComponentModel;
using DemoAot.Utilities;
using Spectre.Console.Cli;

namespace DemoAot.Commands.Run;

[Description("Build and run a .NET project output.")]
public sealed class RunCommand : Command<RunCommand.Settings>
{
public sealed class Settings : CommandSettings
{
[CommandOption("-c|--configuration <CONFIGURATION>")]
[Description("The configuration to run for. The default for most projects is '[grey]Debug[/]'.")]
[DefaultValue("Debug")]
public string Configuration { get; set; }

[CommandOption("-f|--framework <FRAMEWORK>")]
[Description("The target framework to run for. The target framework must also be specified in the project file.")]
public string Framework { get; set; }

[CommandOption("-r|--runtime <RUNTIMEIDENTIFIER>")]
[Description("The target runtime to run for.")]
public string RuntimeIdentifier { get; set; }

[CommandOption("-p|--project <PROJECTPATH>")]
[Description("The path to the project file to run (defaults to the current directory if there is only one project).")]
public string ProjectPath { get; set; }

[CommandOption("--launch-profile <LAUNCHPROFILE>")]
[Description("The name of the launch profile (if any) to use when launching the application.")]
public string LaunchProfile { get; set; }

[CommandOption("--no-launch-profile")]
[Description("Do not attempt to use [grey]launchSettings.json[/] to configure the application.")]
public bool NoLaunchProfile { get; set; }

[CommandOption("--no-build")]
[Description("Do not build the project before running. Implies [grey]--no-restore[/].")]
public bool NoBuild { get; set; }

[CommandOption("--interactive")]
[Description("Allows the command to stop and wait for user input or action (for example to complete authentication).")]
public string Interactive { get; set; }

[CommandOption("--no-restore")]
[Description("Do not restore the project before building.")]
public bool NoRestore { get; set; }

[CommandOption("--verbosity <VERBOSITY>")]
[Description("Set the MSBuild verbosity level. Allowed values are q[grey]uiet[/], m[grey]inimal[/], n[grey]ormal[/], d[grey]etailed[/], and diag[grey]nostic[/].")]
[TypeConverter(typeof(VerbosityConverter))]
[DefaultValue(Verbosity.Normal)]
public Verbosity Verbosity { get; set; }

[CommandOption("--no-dependencies")]
[Description("Do not restore project-to-project references and only restore the specified project.")]
public bool NoDependencies { get; set; }

[CommandOption("--force")]
[Description("Force all dependencies to be resolved even if the last restore was successful. This is equivalent to deleting [grey]project.assets.json[/].")]
public bool Force { get; set; }
}

public override int Execute(CommandContext context, Settings settings)
{
SettingsDumper.Dump(settings);
return 0;
}
}
42 changes: 42 additions & 0 deletions examples/Cli/DemoAot/Commands/Serve/ServeCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System;
using System.ComponentModel;
using DemoAot.Utilities;
using Spectre.Console;
using Spectre.Console.Cli;

namespace DemoAot.Commands.Serve;

[Description("Launches a web server in the current working directory and serves all files in it.")]
public sealed class ServeCommand : Command<ServeCommand.Settings>
{
public sealed class Settings : CommandSettings
{
[CommandOption("-p|--port <PORT>")]
[DefaultValue(8080)]
[Description("Port to use. Defaults to [grey]8080[/]. Use [grey]0[/] for a dynamic port.")]
public int Port { get; set; }

[CommandOption("-o|--open-browser [BROWSER]")]
[Description("Open a web browser when the server starts. You can also specify which browser to use. If none is specified, the default one will be used.")]
public FlagValue<string> OpenBrowser { get; set; }
}

public override int Execute(CommandContext context, Settings settings)
{
if (settings.OpenBrowser.IsSet)
{
var browser = settings.OpenBrowser.Value;
if (browser != null)
{
AnsiConsole.WriteLine($"Open in {browser}");
}
else
{
AnsiConsole.WriteLine($"Open in default browser.");
}
}

SettingsDumper.Dump(settings);
return 0;
}
}
20 changes: 20 additions & 0 deletions examples/Cli/DemoAot/DemoAot.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
<ExampleName>DemoAot</ExampleName>
<ExampleDescription>Demonstrates the most common use cases of Spectre.Cli, configured for NativeAOT.</ExampleDescription>
<ExampleGroup>Cli</ExampleGroup>
<ExampleVisible>false</ExampleVisible>
</PropertyGroup>
<PropertyGroup>
<PublishAot>true</PublishAot>
<TrimmerSingleWarn>false</TrimmerSingleWarn>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console.Cli\Spectre.Console.Cli.csproj" />
</ItemGroup>

</Project>

0 comments on commit e712ad0

Please sign in to comment.