Skip to content

Commit

Permalink
[build] rework command line argument parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreyAkinshin committed Jul 12, 2023
1 parent 86a3417 commit c97d158
Show file tree
Hide file tree
Showing 16 changed files with 277 additions and 118 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/generate-changelog.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ jobs:
ref: master

- name: Download changelog
run: ./build.cmd DocsUpdate /p:Depth=1
run: ./build.cmd DocsUpdate --depth 1 --preview
env:
GITHUB_PRODUCT: ChangelogBuilder
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Push changelog
Expand Down
3 changes: 1 addition & 2 deletions .github/workflows/generate-gh-pages.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@ jobs:
run: ./build.cmd Build

- name: Download changelog
run: ./build.cmd DocsUpdate /p:Depth=1
run: ./build.cmd DocsUpdate --depth 1 --preview
env:
GITHUB_PRODUCT: ChangelogBuilder
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Build documentation
Expand Down
12 changes: 5 additions & 7 deletions build/BenchmarkDotNet.Build/BuildContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,13 @@
using Cake.Core.IO;
using Cake.FileHelpers;
using Cake.Frosting;
using Cake.Git;

namespace BenchmarkDotNet.Build;

public class BuildContext : FrostingContext
{
public string BuildConfiguration { get; set; } = "Release";
public DotNetVerbosity BuildVerbosity { get; set; } = DotNetVerbosity.Minimal;
public int Depth { get; set; }
public bool VersionStable { get; }
public string NextVersion { get; }
public bool PushMode { get; }
Expand Down Expand Up @@ -85,7 +83,6 @@ public BuildContext(ICakeContext context)
MsBuildSettingsBuild.WithProperty("UseSharedCompilation", "false");
}

Depth = -1;
VersionStable = false;
NextVersion = "";
PushMode = false;
Expand Down Expand Up @@ -113,9 +110,6 @@ public BuildContext(ICakeContext context)
BuildVerbosity = parsedVerbosity.Value;
}

if (name.Equals("depth", StringComparison.OrdinalIgnoreCase))
Depth = int.Parse(value);

if (name.Equals("VersionStable", StringComparison.OrdinalIgnoreCase) && value != "")
VersionStable = true;

Expand Down Expand Up @@ -160,14 +154,18 @@ public void GenerateFile(FilePath filePath, StringBuilder content)
GenerateFile(filePath, content.ToString());
}

public void GenerateFile(FilePath filePath, string content)
public void GenerateFile(FilePath filePath, string content, bool reportNoChanges = false)
{
var relativePath = RootDirectory.GetRelativePath(filePath);
if (this.FileExists(filePath))
{
var oldContent = this.FileReadText(filePath);
if (content == oldContent)
{
if (reportNoChanges)
this.Information("[NoChanges] " + relativePath);
return;
}

this.FileWriteText(filePath, content);
this.Information("[Updated] " + relativePath);
Expand Down
11 changes: 6 additions & 5 deletions build/BenchmarkDotNet.Build/ChangeLogBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BenchmarkDotNet.Build.Helpers;
using BenchmarkDotNet.Build.Meta;
using Cake.Common.Diagnostics;
using Cake.Core.IO;
using Octokit;

Expand Down Expand Up @@ -174,17 +174,18 @@ private void AppendList<T>(string title, IReadOnlyList<T> items, Func<T, string>
}
}

public static async Task Run(DirectoryPath path, string currentVersion, string previousVersion, string lastCommit)
public static void Run(BuildContext context, DirectoryPath path,
string currentVersion, string previousVersion, string lastCommit)
{
try
{
var config = new Config(currentVersion, previousVersion, lastCommit);
var releaseNotes = await MarkdownBuilder.Build(config);
await File.WriteAllTextAsync(path.Combine($"v{config.CurrentVersion}.md").FullPath, releaseNotes);
var releaseNotes = MarkdownBuilder.Build(config).Result;
context.GenerateFile(path.Combine($"v{config.CurrentVersion}.md").FullPath, releaseNotes, true);
}
catch (Exception e)
{
await Console.Error.WriteLineAsync(e.ToString());
context.Error(e.ToString());
}
}
}
157 changes: 83 additions & 74 deletions build/BenchmarkDotNet.Build/CommandLineParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using BenchmarkDotNet.Build.Options;
using Cake.Frosting;

namespace BenchmarkDotNet.Build;
Expand All @@ -10,6 +12,9 @@ public class CommandLineParser
{
private const string ScriptName = "build.cmd";

private static readonly string CallScriptName =
(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? ScriptName : "./" + ScriptName;

public static readonly CommandLineParser Instance = new();

public string[]? Parse(string[]? args)
Expand Down Expand Up @@ -53,63 +58,32 @@ public class CommandLineParser
{
var arg = argsToProcess.Dequeue();

var matched = false;
foreach (var option in options)
{
if (Is(arg, option.ShortName, option.FullName))
{
matched = true;
cakeArgs.Add(option.CakeOption);
if (option.Arg != "")
{
if (!argsToProcess.Any())
{
PrintError(option.FullName + " is not specified");
return null;
}

cakeArgs.Add(argsToProcess.Dequeue());
}
}
}

if (arg.StartsWith("/p:"))
{
matched = true;
cakeArgs.Add("--msbuild");
cakeArgs.Add(arg[3..]);
continue;
}

if (!matched)
if (arg.StartsWith('-'))
{
PrintError("Unknown option: " + arg);
return null;
cakeArgs.Add(arg);
if (argsToProcess.Any() && !argsToProcess.Peek().StartsWith('-'))
cakeArgs.Add(argsToProcess.Dequeue());
continue;
}

PrintError("Unknown option: " + arg);
return null;
}

return cakeArgs.ToArray();
}


private record Option(string ShortName, string FullName, string Arg, string Description, string CakeOption);

private readonly Option[] options =
private readonly IOption[] baseOptions =
{
new("-v",
"--verbosity",
"<LEVEL>",
"Specifies the amount of information to be displayed\n(Quiet, Minimal, Normal, Verbose, Diagnostic)",
"--verbosity"),
new("-e",
"--exclusive",
"",
"Executes the target task without any dependencies",
"--exclusive"),
new("-h",
"--help",
"",
"Prints help information for the target task",
"")
KnownOptions.Verbosity, KnownOptions.Exclusive, KnownOptions.Help
};

private void PrintHelp()
Expand All @@ -127,7 +101,7 @@ private void PrintHelp()
WriteHeader("Usage:");

WritePrefix();
Write(ScriptName + " ");
Write(CallScriptName + " ");
WriteTask("<TASK> ");
WriteOption("[OPTIONS]");
WriteLine();
Expand All @@ -137,12 +111,12 @@ private void PrintHelp()
WriteHeader("Examples:");

WritePrefix();
Write(ScriptName + " ");
Write(CallScriptName + " ");
WriteTask("restore");
WriteLine();

WritePrefix();
Write(ScriptName + " ");
Write(CallScriptName + " ");
WriteTask("build ");
WriteOption("/p:");
WriteArg("Configuration");
Expand All @@ -151,7 +125,7 @@ private void PrintHelp()
WriteLine();

WritePrefix();
Write(ScriptName + " ");
Write(CallScriptName + " ");
WriteTask("pack ");
WriteOption("/p:");
WriteArg("VersionPrefix");
Expand All @@ -164,27 +138,29 @@ private void PrintHelp()
WriteLine();

WritePrefix();
Write(ScriptName + " ");
Write(CallScriptName + " ");
WriteTask("unittests ");
WriteOption("--exclusive --verbosity ");
WriteArg("Diagnostic");
WriteLine();

WritePrefix();
Write(ScriptName + " ");
Write(CallScriptName + " ");
WriteTask("docs-update ");
WriteOption("/p:");
WriteArg("Depth");
WriteOption("=");
WriteOption("--depth ");
WriteArg("3");
WriteLine();

WritePrefix();
Write(CallScriptName + " ");
WriteTask("docs-build ");
WriteOption("--preview ");
WriteLine();

PrintCommonOptions();

WriteLine();

PrintOptions(baseOptions);

WriteHeader("Tasks:");
var taskWidth = GetTaskNames().Max(name => name.Length) + 3;
foreach (var (taskName, taskDescription) in GetTasks())
Expand All @@ -207,29 +183,47 @@ private void PrintHelp()
}
}

private void PrintCommonOptions()
private void PrintOptions(IOption[] options)
{
const string valuePlaceholder = "<VALUE>";

WriteLine("Options:", ConsoleColor.DarkCyan);

var shortNameWidth = options.Max(it => it.ShortName.Length);
var targetWidth = options.Max(it => it.FullName.Length + it.Arg.Length);
int GetWidth(IOption option)
{
int width = option.CommandLineName.Length;
foreach (var alias in option.Aliases)
width += 1 + alias.Length;
if (option is StringOption)
width += 1 + valuePlaceholder.Length;
return width;
}

foreach (var (shortName, fullName, arg, description, _) in options)
const int descriptionGap = 3;
var maxWidth = options.Max(GetWidth) + descriptionGap;

foreach (var option in options)
{
var allNames = option.Aliases.Append(option.CommandLineName).OrderBy(name => name.Length);
var joinName = string.Join(',', allNames);

WritePrefix();
WriteOption(shortName.PadRight(shortNameWidth));
WriteOption(shortName != "" ? "," : " ");
WriteOption(fullName);
Write(" ");
WriteArg(arg);
Write(new string(' ', targetWidth - fullName.Length - arg.Length + 3));
var descriptionLines = description.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
WriteOption(joinName);
if (option is StringOption)
{
Write(" ");
WriteArg(valuePlaceholder);
}

Write(new string(' ',
maxWidth - joinName.Length - (option is StringOption ? valuePlaceholder.Length + 1 : 0)));
var descriptionLines = option.Description.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
Write(descriptionLines.FirstOrDefault() ?? "");
for (int i = 1; i < descriptionLines.Length; i++)
{
WriteLine();
WritePrefix();
Write(new string(' ', shortNameWidth + 2 + targetWidth + 3));
Write(new string(' ', maxWidth));
Write(descriptionLines[i]);
}

Expand All @@ -240,10 +234,12 @@ private void PrintCommonOptions()
WriteOption("/p:");
WriteArg("<KEY>");
WriteOption("=");
WriteArg("<VALUE>");
Write(new string(' ', targetWidth + shortNameWidth - 11));
WriteArg(valuePlaceholder);
Write(new string(' ', maxWidth - "/p:<KEY>=".Length - valuePlaceholder.Length));
Write("Passes custom properties to MSBuild");
WriteLine();

WriteLine();
}

private void PrintTaskHelp(string taskName)
Expand All @@ -267,18 +263,19 @@ private void PrintTaskHelp(string taskName)
WriteLine(taskDescription);
}

foreach (var line in helpInfo.Description)
{
WritePrefix();
WriteLine(line);
}
if (string.IsNullOrWhiteSpace(helpInfo.Description))
foreach (var line in helpInfo.Description.Split('\n', StringSplitOptions.RemoveEmptyEntries))
{
WritePrefix();
WriteLine(line.Trim());
}

WriteLine();

WriteHeader("Usage:");

WritePrefix();
Write(ScriptName + " ");
Write(CallScriptName + " ");
WriteTask(taskName + " ");
WriteOption("[OPTIONS]");
WriteLine();
Expand Down Expand Up @@ -309,7 +306,19 @@ private void PrintTaskHelp(string taskName)

WriteLine();

PrintCommonOptions();
PrintOptions(helpInfo.Options.Concat(baseOptions).ToArray());

if (helpInfo.EnvironmentVariables.Any())
{
WriteHeader("Environment variables:");
foreach (var variable in helpInfo.EnvironmentVariables)
{
WritePrefix();
WriteOption(variable);
}

WriteLine();
}
}

private static HashSet<string> GetTaskNames()
Expand Down
Loading

0 comments on commit c97d158

Please sign in to comment.