Skip to content

Commit 7de12a1

Browse files
[build] rework command line argument parsing
1 parent e4c4f6f commit 7de12a1

File tree

16 files changed

+277
-118
lines changed

16 files changed

+277
-118
lines changed

.github/workflows/generate-changelog.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,8 @@ jobs:
2020
ref: master
2121

2222
- name: Download changelog
23-
run: ./build.cmd DocsUpdate /p:Depth=1
23+
run: ./build.cmd DocsUpdate --depth 1 --preview
2424
env:
25-
GITHUB_PRODUCT: ChangelogBuilder
2625
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2726

2827
- name: Push changelog

.github/workflows/generate-gh-pages.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,8 @@ jobs:
2323
run: ./build.cmd Build
2424

2525
- name: Download changelog
26-
run: ./build.cmd DocsUpdate /p:Depth=1
26+
run: ./build.cmd DocsUpdate --depth 1 --preview
2727
env:
28-
GITHUB_PRODUCT: ChangelogBuilder
2928
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3029

3130
- name: Build documentation

build/BenchmarkDotNet.Build/BuildContext.cs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,13 @@
1616
using Cake.Core.IO;
1717
using Cake.FileHelpers;
1818
using Cake.Frosting;
19-
using Cake.Git;
2019

2120
namespace BenchmarkDotNet.Build;
2221

2322
public class BuildContext : FrostingContext
2423
{
2524
public string BuildConfiguration { get; set; } = "Release";
2625
public DotNetVerbosity BuildVerbosity { get; set; } = DotNetVerbosity.Minimal;
27-
public int Depth { get; set; }
2826
public bool VersionStable { get; }
2927
public string NextVersion { get; }
3028
public bool PushMode { get; }
@@ -85,7 +83,6 @@ public BuildContext(ICakeContext context)
8583
MsBuildSettingsBuild.WithProperty("UseSharedCompilation", "false");
8684
}
8785

88-
Depth = -1;
8986
VersionStable = false;
9087
NextVersion = "";
9188
PushMode = false;
@@ -113,9 +110,6 @@ public BuildContext(ICakeContext context)
113110
BuildVerbosity = parsedVerbosity.Value;
114111
}
115112

116-
if (name.Equals("depth", StringComparison.OrdinalIgnoreCase))
117-
Depth = int.Parse(value);
118-
119113
if (name.Equals("VersionStable", StringComparison.OrdinalIgnoreCase) && value != "")
120114
VersionStable = true;
121115

@@ -160,14 +154,18 @@ public void GenerateFile(FilePath filePath, StringBuilder content)
160154
GenerateFile(filePath, content.ToString());
161155
}
162156

163-
public void GenerateFile(FilePath filePath, string content)
157+
public void GenerateFile(FilePath filePath, string content, bool reportNoChanges = false)
164158
{
165159
var relativePath = RootDirectory.GetRelativePath(filePath);
166160
if (this.FileExists(filePath))
167161
{
168162
var oldContent = this.FileReadText(filePath);
169163
if (content == oldContent)
164+
{
165+
if (reportNoChanges)
166+
this.Information("[NoChanges] " + relativePath);
170167
return;
168+
}
171169

172170
this.FileWriteText(filePath, content);
173171
this.Information("[Updated] " + relativePath);

build/BenchmarkDotNet.Build/ChangeLogBuilder.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Collections.Immutable;
4-
using System.IO;
54
using System.Linq;
65
using System.Text;
76
using System.Threading.Tasks;
87
using BenchmarkDotNet.Build.Helpers;
98
using BenchmarkDotNet.Build.Meta;
9+
using Cake.Common.Diagnostics;
1010
using Cake.Core.IO;
1111
using Octokit;
1212

@@ -174,17 +174,18 @@ private void AppendList<T>(string title, IReadOnlyList<T> items, Func<T, string>
174174
}
175175
}
176176

177-
public static async Task Run(DirectoryPath path, string currentVersion, string previousVersion, string lastCommit)
177+
public static void Run(BuildContext context, DirectoryPath path,
178+
string currentVersion, string previousVersion, string lastCommit)
178179
{
179180
try
180181
{
181182
var config = new Config(currentVersion, previousVersion, lastCommit);
182-
var releaseNotes = await MarkdownBuilder.Build(config);
183-
await File.WriteAllTextAsync(path.Combine($"v{config.CurrentVersion}.md").FullPath, releaseNotes);
183+
var releaseNotes = MarkdownBuilder.Build(config).Result;
184+
context.GenerateFile(path.Combine($"v{config.CurrentVersion}.md").FullPath, releaseNotes, true);
184185
}
185186
catch (Exception e)
186187
{
187-
await Console.Error.WriteLineAsync(e.ToString());
188+
context.Error(e.ToString());
188189
}
189190
}
190191
}

build/BenchmarkDotNet.Build/CommandLineParser.cs

Lines changed: 83 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
using System.Collections.Generic;
33
using System.Linq;
44
using System.Reflection;
5+
using System.Runtime.InteropServices;
6+
using BenchmarkDotNet.Build.Options;
57
using Cake.Frosting;
68

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

15+
private static readonly string CallScriptName =
16+
(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? ScriptName : "./" + ScriptName;
17+
1318
public static readonly CommandLineParser Instance = new();
1419

1520
public string[]? Parse(string[]? args)
@@ -53,63 +58,32 @@ public class CommandLineParser
5358
{
5459
var arg = argsToProcess.Dequeue();
5560

56-
var matched = false;
57-
foreach (var option in options)
58-
{
59-
if (Is(arg, option.ShortName, option.FullName))
60-
{
61-
matched = true;
62-
cakeArgs.Add(option.CakeOption);
63-
if (option.Arg != "")
64-
{
65-
if (!argsToProcess.Any())
66-
{
67-
PrintError(option.FullName + " is not specified");
68-
return null;
69-
}
70-
71-
cakeArgs.Add(argsToProcess.Dequeue());
72-
}
73-
}
74-
}
75-
7661
if (arg.StartsWith("/p:"))
7762
{
78-
matched = true;
7963
cakeArgs.Add("--msbuild");
8064
cakeArgs.Add(arg[3..]);
65+
continue;
8166
}
8267

83-
if (!matched)
68+
if (arg.StartsWith('-'))
8469
{
85-
PrintError("Unknown option: " + arg);
86-
return null;
70+
cakeArgs.Add(arg);
71+
if (argsToProcess.Any() && !argsToProcess.Peek().StartsWith('-'))
72+
cakeArgs.Add(argsToProcess.Dequeue());
73+
continue;
8774
}
75+
76+
PrintError("Unknown option: " + arg);
77+
return null;
8878
}
8979

9080
return cakeArgs.ToArray();
9181
}
9282

9383

94-
private record Option(string ShortName, string FullName, string Arg, string Description, string CakeOption);
95-
96-
private readonly Option[] options =
84+
private readonly IOption[] baseOptions =
9785
{
98-
new("-v",
99-
"--verbosity",
100-
"<LEVEL>",
101-
"Specifies the amount of information to be displayed\n(Quiet, Minimal, Normal, Verbose, Diagnostic)",
102-
"--verbosity"),
103-
new("-e",
104-
"--exclusive",
105-
"",
106-
"Executes the target task without any dependencies",
107-
"--exclusive"),
108-
new("-h",
109-
"--help",
110-
"",
111-
"Prints help information for the target task",
112-
"")
86+
KnownOptions.Verbosity, KnownOptions.Exclusive, KnownOptions.Help
11387
};
11488

11589
private void PrintHelp()
@@ -127,7 +101,7 @@ private void PrintHelp()
127101
WriteHeader("Usage:");
128102

129103
WritePrefix();
130-
Write(ScriptName + " ");
104+
Write(CallScriptName + " ");
131105
WriteTask("<TASK> ");
132106
WriteOption("[OPTIONS]");
133107
WriteLine();
@@ -137,12 +111,12 @@ private void PrintHelp()
137111
WriteHeader("Examples:");
138112

139113
WritePrefix();
140-
Write(ScriptName + " ");
114+
Write(CallScriptName + " ");
141115
WriteTask("restore");
142116
WriteLine();
143117

144118
WritePrefix();
145-
Write(ScriptName + " ");
119+
Write(CallScriptName + " ");
146120
WriteTask("build ");
147121
WriteOption("/p:");
148122
WriteArg("Configuration");
@@ -151,7 +125,7 @@ private void PrintHelp()
151125
WriteLine();
152126

153127
WritePrefix();
154-
Write(ScriptName + " ");
128+
Write(CallScriptName + " ");
155129
WriteTask("pack ");
156130
WriteOption("/p:");
157131
WriteArg("VersionPrefix");
@@ -164,27 +138,29 @@ private void PrintHelp()
164138
WriteLine();
165139

166140
WritePrefix();
167-
Write(ScriptName + " ");
141+
Write(CallScriptName + " ");
168142
WriteTask("unittests ");
169143
WriteOption("--exclusive --verbosity ");
170144
WriteArg("Diagnostic");
171145
WriteLine();
172146

173147
WritePrefix();
174-
Write(ScriptName + " ");
148+
Write(CallScriptName + " ");
175149
WriteTask("docs-update ");
176-
WriteOption("/p:");
177-
WriteArg("Depth");
178-
WriteOption("=");
150+
WriteOption("--depth ");
179151
WriteArg("3");
180152
WriteLine();
181153

154+
WritePrefix();
155+
Write(CallScriptName + " ");
156+
WriteTask("docs-build ");
157+
WriteOption("--preview ");
182158
WriteLine();
183159

184-
PrintCommonOptions();
185-
186160
WriteLine();
187161

162+
PrintOptions(baseOptions);
163+
188164
WriteHeader("Tasks:");
189165
var taskWidth = GetTaskNames().Max(name => name.Length) + 3;
190166
foreach (var (taskName, taskDescription) in GetTasks())
@@ -207,29 +183,47 @@ private void PrintHelp()
207183
}
208184
}
209185

210-
private void PrintCommonOptions()
186+
private void PrintOptions(IOption[] options)
211187
{
188+
const string valuePlaceholder = "<VALUE>";
189+
212190
WriteLine("Options:", ConsoleColor.DarkCyan);
213191

214-
var shortNameWidth = options.Max(it => it.ShortName.Length);
215-
var targetWidth = options.Max(it => it.FullName.Length + it.Arg.Length);
192+
int GetWidth(IOption option)
193+
{
194+
int width = option.CommandLineName.Length;
195+
foreach (var alias in option.Aliases)
196+
width += 1 + alias.Length;
197+
if (option is StringOption)
198+
width += 1 + valuePlaceholder.Length;
199+
return width;
200+
}
216201

217-
foreach (var (shortName, fullName, arg, description, _) in options)
202+
const int descriptionGap = 3;
203+
var maxWidth = options.Max(GetWidth) + descriptionGap;
204+
205+
foreach (var option in options)
218206
{
207+
var allNames = option.Aliases.Append(option.CommandLineName).OrderBy(name => name.Length);
208+
var joinName = string.Join(',', allNames);
209+
219210
WritePrefix();
220-
WriteOption(shortName.PadRight(shortNameWidth));
221-
WriteOption(shortName != "" ? "," : " ");
222-
WriteOption(fullName);
223-
Write(" ");
224-
WriteArg(arg);
225-
Write(new string(' ', targetWidth - fullName.Length - arg.Length + 3));
226-
var descriptionLines = description.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
211+
WriteOption(joinName);
212+
if (option is StringOption)
213+
{
214+
Write(" ");
215+
WriteArg(valuePlaceholder);
216+
}
217+
218+
Write(new string(' ',
219+
maxWidth - joinName.Length - (option is StringOption ? valuePlaceholder.Length + 1 : 0)));
220+
var descriptionLines = option.Description.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
227221
Write(descriptionLines.FirstOrDefault() ?? "");
228222
for (int i = 1; i < descriptionLines.Length; i++)
229223
{
230224
WriteLine();
231225
WritePrefix();
232-
Write(new string(' ', shortNameWidth + 2 + targetWidth + 3));
226+
Write(new string(' ', maxWidth));
233227
Write(descriptionLines[i]);
234228
}
235229

@@ -240,10 +234,12 @@ private void PrintCommonOptions()
240234
WriteOption("/p:");
241235
WriteArg("<KEY>");
242236
WriteOption("=");
243-
WriteArg("<VALUE>");
244-
Write(new string(' ', targetWidth + shortNameWidth - 11));
237+
WriteArg(valuePlaceholder);
238+
Write(new string(' ', maxWidth - "/p:<KEY>=".Length - valuePlaceholder.Length));
245239
Write("Passes custom properties to MSBuild");
246240
WriteLine();
241+
242+
WriteLine();
247243
}
248244

249245
private void PrintTaskHelp(string taskName)
@@ -267,18 +263,19 @@ private void PrintTaskHelp(string taskName)
267263
WriteLine(taskDescription);
268264
}
269265

270-
foreach (var line in helpInfo.Description)
271-
{
272-
WritePrefix();
273-
WriteLine(line);
274-
}
266+
if (string.IsNullOrWhiteSpace(helpInfo.Description))
267+
foreach (var line in helpInfo.Description.Split('\n', StringSplitOptions.RemoveEmptyEntries))
268+
{
269+
WritePrefix();
270+
WriteLine(line.Trim());
271+
}
275272

276273
WriteLine();
277274

278275
WriteHeader("Usage:");
279276

280277
WritePrefix();
281-
Write(ScriptName + " ");
278+
Write(CallScriptName + " ");
282279
WriteTask(taskName + " ");
283280
WriteOption("[OPTIONS]");
284281
WriteLine();
@@ -309,7 +306,19 @@ private void PrintTaskHelp(string taskName)
309306

310307
WriteLine();
311308

312-
PrintCommonOptions();
309+
PrintOptions(helpInfo.Options.Concat(baseOptions).ToArray());
310+
311+
if (helpInfo.EnvironmentVariables.Any())
312+
{
313+
WriteHeader("Environment variables:");
314+
foreach (var variable in helpInfo.EnvironmentVariables)
315+
{
316+
WritePrefix();
317+
WriteOption(variable);
318+
}
319+
320+
WriteLine();
321+
}
313322
}
314323

315324
private static HashSet<string> GetTaskNames()

0 commit comments

Comments
 (0)