Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP Initial Trimming Support #1508

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
2 changes: 1 addition & 1 deletion examples/Cli/Demo/Utilities/SettingsDumper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public static void Dump(CommandSettings settings)
{
var value = property.GetValue(settings)
?.ToString()
?.Replace("[", "[[");
?.EscapeMarkup();

table.AddRow(
property.Name,
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;
}
}
24 changes: 24 additions & 0 deletions examples/Cli/DemoAot/DemoAot.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<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" />
<TrimmerRootAssembly Include="Spectre.Console" />
<TrimmerRootAssembly Include="Spectre.Console.Cli" />

<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
</ItemGroup>

</Project>
27 changes: 27 additions & 0 deletions examples/Cli/DemoAot/GreetingService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Spectre.Console;
using Spectre.Console.Cli;

namespace DemoAot;

public class InfoCommand(GreetingService greetingService) : Command<InfoCommand.Settings>
{
public override int Execute(CommandContext context, Settings settings)
{
greetingService.Greet("World");
return 0;
}

public class Settings : CommandSettings
{
[CommandOption("-v")]
public bool Verbose {get;set;}
}
}

public class GreetingService(IAnsiConsole ansiConsole)
{
public void Greet(string name)
{
ansiConsole.WriteLine($"Hello {name}!");
}
}
58 changes: 58 additions & 0 deletions examples/Cli/DemoAot/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System;
using DemoAot;
using DemoAot.Commands.Add;
using DemoAot.Commands.Run;
using DemoAot.Commands.Serve;
using DemoAot.Utilities;
using Microsoft.Extensions.DependencyInjection;
using Spectre.Console;
using Spectre.Console.Cli;

try
{
var services = new ServiceCollection();
services.AddSingleton<GreetingService>();

// add extra services to the container here
using var registrar = new DependencyInjectionRegistrar(services);

var app = new CommandApp(registrar);
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<InfoCommand, InfoCommand.Settings>("info");

// 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");
});

// Serve
config.AddCommand<ServeCommand, ServeCommand.Settings>("serve")
.WithExample("serve", "-o", "firefox")
.WithExample("serve", "--port", "80", "-o", "firefox");
});

app.Run(args);

return 0;
}
catch (Exception e)
{
// this will raise a warning because AnsiConsole.WriteException relies on reflection to generate the pretty formatted
// exception. When executed in a NativeAOT scenario it is the same as calling e.ToString()
#pragma warning disable IL2026
AnsiConsole.WriteException(e);
#pragma warning restore IL2026

return -1;
}
Loading
Loading