From 06c8f669d4869b2a3da22199b4e670a17f3c56de Mon Sep 17 00:00:00 2001 From: William Chen Date: Fri, 12 Jun 2026 03:52:17 -0400 Subject: [PATCH 1/3] Start of only-option branch From 9701b3d975bf254a9043b22663a87abff92e8c12 Mon Sep 17 00:00:00 2001 From: William Chen Date: Fri, 12 Jun 2026 04:09:27 -0400 Subject: [PATCH 2/3] Change SilkTouch entrypoint to use an actual Program class I've always felt the Program-class-less entrypoint approach to be less readable. It's fine if it's only a few lines, but logic, methods, and constants start to blur together. --- sources/SilkTouch/SilkTouch/Program.cs | 199 ++++++++++++++----------- 1 file changed, 110 insertions(+), 89 deletions(-) diff --git a/sources/SilkTouch/SilkTouch/Program.cs b/sources/SilkTouch/SilkTouch/Program.cs index 5e4c0bf429..ceb75fa224 100644 --- a/sources/SilkTouch/SilkTouch/Program.cs +++ b/sources/SilkTouch/SilkTouch/Program.cs @@ -1,107 +1,128 @@ -using System; -using System.Collections.Concurrent; using System.CommandLine; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using ClangSharp; using Microsoft.Build.Locator; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Console; -using Silk.NET.SilkTouch; using Silk.NET.SilkTouch.Caching; -var logging = new Option(new[] { "--log-level", "-l" }, () => LogLevel.Information); -var skip = new Option( - new[] { "--skip", "-s" }, - Array.Empty, - "A list of job names to skip." -) -{ - Arity = ArgumentArity.ZeroOrMore, -}; -var configs = new Argument("configs", "Path(s) to JSON SilkTouch configuration(s)") -{ - Arity = ArgumentArity.OneOrMore, -}; -var configOverrides = new Argument( - "overrides", - Array.Empty, - "Arguments recognisable by Microsoft.Extensions.Configuration.CommandLine to override JSON configuration items." -) -{ - Arity = ArgumentArity.ZeroOrMore, -}; -var jobs = new Option( - new[] { "--max-jobs", "-j" }, - () => Environment.ProcessorCount, - "Maximum number of parallel ClangSharp executions." -); -var rootCommand = new RootCommand { logging, skip, configs, configOverrides, jobs }; -rootCommand.SetHandler(async ctx => +namespace Silk.NET.SilkTouch; + +internal class Program { - // Create the ConfigurationBuilder with support for env var & command line overrides - var cb = new ConfigurationBuilder() - .AddEnvironmentVariables(source => source.Prefix = "SILKDOTNET_") - .AddCommandLine(ctx.ParseResult.GetValueForArgument(configOverrides)); + private static Option LoggingOption { get; } = + new(["--log-level", "-l"], () => LogLevel.Information); - // Add the JSON files to the ConfigurationBuilder - var jsons = ctx.ParseResult.GetValueForArgument(configs); - cb = jsons.Aggregate(cb, (current, file) => current.AddJsonFile(Path.GetFullPath(file))); - var config = cb.Build(); - if (jsons.Length == 1) - { - FileSystemCacheProvider.CommonDir = Path.GetFullPath( - (Path.GetDirectoryName(jsons[0]) is (null or not "") and var path ? path : ".") - ?? Environment.CurrentDirectory + private static Option SkipOption { get; } = + new(["--skip", "-s"], Array.Empty, "A list of job names to skip.") + { + Arity = ArgumentArity.ZeroOrMore, + }; + + private static Argument ConfigsArgument { get; } = + new("configs", "Path(s) to JSON SilkTouch configuration(s)") + { + Arity = ArgumentArity.OneOrMore, + }; + + private static Argument ConfigOverridesArgument { get; } = + new( + "overrides", + Array.Empty, + "Arguments recognisable by Microsoft.Extensions.Configuration.CommandLine to override JSON configuration items." + ) + { + Arity = ArgumentArity.ZeroOrMore, + }; + + private static Option JobsOption { get; } = + new( + new[] { "--max-jobs", "-j" }, + () => Environment.ProcessorCount, + "Maximum number of parallel ClangSharp executions." ); - } - // Register MSBuild - MSBuildLocator.RegisterDefaults(); + public static async Task Main(string[] args) + { + var rootCommand = new RootCommand + { + LoggingOption, + SkipOption, + ConfigsArgument, + ConfigOverridesArgument, + JobsOption, + }; - var sp = new ServiceCollection() - .AddLogging(builder => + rootCommand.SetHandler(async ctx => { - builder.AddSimpleConsole(opts => + // Create the ConfigurationBuilder with support for env var & command line overrides + var cb = new ConfigurationBuilder() + .AddEnvironmentVariables(source => source.Prefix = "SILKDOTNET_") + .AddCommandLine(ctx.ParseResult.GetValueForArgument(ConfigOverridesArgument)); + + // Add the JSON files to the ConfigurationBuilder + var jsons = ctx.ParseResult.GetValueForArgument(ConfigsArgument); + cb = jsons.Aggregate( + cb, + (current, file) => current.AddJsonFile(Path.GetFullPath(file)) + ); + var config = cb.Build(); + if (jsons.Length == 1) { - opts.SingleLine = true; - opts.ColorBehavior = Console.IsOutputRedirected - ? LoggerColorBehavior.Disabled - : LoggerColorBehavior.Default; - opts.IncludeScopes = false; - }); - builder.SetMinimumLevel(ctx.ParseResult.GetValueForOption(logging)); - }) - .AddOptions() - .AddSilkTouch(config) - .BuildServiceProvider(); + FileSystemCacheProvider.CommonDir = Path.GetFullPath( + (Path.GetDirectoryName(jsons[0]) is (null or not "") and var path ? path : ".") + ?? Environment.CurrentDirectory + ); + } - var logger = sp.GetRequiredService>(); - var skipped = ctx.ParseResult.GetValueForOption(skip); - var generator = sp.GetRequiredService(); - await Parallel.ForEachAsync( - config - .GetSection("Jobs") - .GetChildren() - .Where(x => - skipped?.All(y => !x.Key.Equals(y, StringComparison.OrdinalIgnoreCase)) ?? true - ), - async (job, ct) => - { - await generator.RunAsync( - job.Key, - job, - // TODO parallelism configuration - // ctx.ParseResult.GetValueForOption(jobs), - ct + // Register MSBuild + MSBuildLocator.RegisterDefaults(); + + var sp = new ServiceCollection() + .AddLogging(builder => + { + builder.AddSimpleConsole(opts => + { + opts.SingleLine = true; + opts.ColorBehavior = Console.IsOutputRedirected + ? LoggerColorBehavior.Disabled + : LoggerColorBehavior.Default; + opts.IncludeScopes = false; + }); + builder.SetMinimumLevel(ctx.ParseResult.GetValueForOption(LoggingOption)); + }) + .AddOptions() + .AddSilkTouch(config) + .BuildServiceProvider(); + + var logger = sp.GetRequiredService>(); + var skipped = ctx.ParseResult.GetValueForOption(SkipOption); + var generator = sp.GetRequiredService(); + + await Parallel.ForEachAsync( + config + .GetSection("Jobs") + .GetChildren() + .Where(x => + skipped?.All(y => !x.Key.Equals(y, StringComparison.OrdinalIgnoreCase)) + ?? true + ), + async (job, ct) => + { + await generator.RunAsync( + job.Key, + job, + // TODO parallelism configuration + // ctx.ParseResult.GetValueForOption(jobs), + ct + ); + } ); - } - ); - // workaround for dangling logging/socket engine threads - Environment.Exit(0); -}); -await rootCommand.InvokeAsync(args); + // Workaround for dangling logging/socket engine threads + Environment.Exit(0); + }); + + await rootCommand.InvokeAsync(args); + } +} From febdf127393caed0cec08becd1f842f47ba44866 Mon Sep 17 00:00:00 2001 From: William Chen Date: Fri, 12 Jun 2026 04:22:12 -0400 Subject: [PATCH 3/3] Add back the --only option --- sources/SilkTouch/SilkTouch/Program.cs | 55 ++++++++++++++++++-------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/sources/SilkTouch/SilkTouch/Program.cs b/sources/SilkTouch/SilkTouch/Program.cs index ceb75fa224..bd498399a1 100644 --- a/sources/SilkTouch/SilkTouch/Program.cs +++ b/sources/SilkTouch/SilkTouch/Program.cs @@ -14,33 +14,39 @@ internal class Program new(["--log-level", "-l"], () => LogLevel.Information); private static Option SkipOption { get; } = - new(["--skip", "-s"], Array.Empty, "A list of job names to skip.") + new(["--skip", "-s"], Array.Empty) { + Description = "A list of job names to skip. Takes precedence over --only.", + Arity = ArgumentArity.ZeroOrMore, + }; + + private static Option OnlyOption { get; } = + new(new[] { "--only", "-o" }, Array.Empty) + { + Description = "A list of job names to run.", Arity = ArgumentArity.ZeroOrMore, }; private static Argument ConfigsArgument { get; } = - new("configs", "Path(s) to JSON SilkTouch configuration(s)") + new("configs") { + Description = "Path(s) to JSON SilkTouch configuration(s)", Arity = ArgumentArity.OneOrMore, }; private static Argument ConfigOverridesArgument { get; } = - new( - "overrides", - Array.Empty, - "Arguments recognisable by Microsoft.Extensions.Configuration.CommandLine to override JSON configuration items." - ) + new("overrides", Array.Empty) { + Description = + "Arguments recognisable by Microsoft.Extensions.Configuration.CommandLine to override JSON configuration items.", Arity = ArgumentArity.ZeroOrMore, }; private static Option JobsOption { get; } = - new( - new[] { "--max-jobs", "-j" }, - () => Environment.ProcessorCount, - "Maximum number of parallel ClangSharp executions." - ); + new(new[] { "--max-jobs", "-j" }, () => Environment.ProcessorCount) + { + Description = "Maximum number of parallel ClangSharp executions.", + }; public static async Task Main(string[] args) { @@ -48,6 +54,7 @@ public static async Task Main(string[] args) { LoggingOption, SkipOption, + OnlyOption, ConfigsArgument, ConfigOverridesArgument, JobsOption, @@ -95,18 +102,32 @@ public static async Task Main(string[] args) .AddSilkTouch(config) .BuildServiceProvider(); + var jobsToRun = new HashSet( + ctx.ParseResult.GetValueForOption(OnlyOption) ?? [], + StringComparer.OrdinalIgnoreCase + ); + + var jobsToSkip = new HashSet( + ctx.ParseResult.GetValueForOption(SkipOption) ?? [], + StringComparer.OrdinalIgnoreCase + ); + var logger = sp.GetRequiredService>(); - var skipped = ctx.ParseResult.GetValueForOption(SkipOption); var generator = sp.GetRequiredService(); await Parallel.ForEachAsync( config .GetSection("Jobs") .GetChildren() - .Where(x => - skipped?.All(y => !x.Key.Equals(y, StringComparison.OrdinalIgnoreCase)) - ?? true - ), + .Where(section => + { + if (jobsToSkip.Contains(section.Key)) + { + return false; + } + + return jobsToRun.Count == 0 || jobsToRun.Contains(section.Key); + }), async (job, ct) => { await generator.RunAsync(