aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2022-08-11 17:40:06 -0700
committerRob Mensching <rob@firegiant.com>2022-08-15 06:49:36 -0700
commita6f8b6fa3903d846cdc2fbe715ca951d83af3107 (patch)
treee1c554fc6c386c5dc881d288661aa0d1ba0a84a4
parent4a21abbfc4d3b18bda3547a6c792be9f21df356e (diff)
downloadwix-a6f8b6fa3903d846cdc2fbe715ca951d83af3107.tar.gz
wix-a6f8b6fa3903d846cdc2fbe715ca951d83af3107.tar.bz2
wix-a6f8b6fa3903d846cdc2fbe715ca951d83af3107.zip
Redesign command-line help to meet the needs of WiX v4
-rw-r--r--src/api/wix/WixToolset.Extensibility/BaseCommandLineCommand.cs40
-rw-r--r--src/api/wix/WixToolset.Extensibility/BaseExtensionCommandLine.cs9
-rw-r--r--src/api/wix/WixToolset.Extensibility/Data/CommandLineHelp.cs52
-rw-r--r--src/api/wix/WixToolset.Extensibility/Data/CommandLineHelpCommand.cs31
-rw-r--r--src/api/wix/WixToolset.Extensibility/Data/CommandLineHelpSwitch.cs47
-rw-r--r--src/api/wix/WixToolset.Extensibility/Data/ExtensionCommandLineSwitch.cs20
-rw-r--r--src/api/wix/WixToolset.Extensibility/Data/ICommandLineCommand.cs12
-rw-r--r--src/api/wix/WixToolset.Extensibility/IExtensionCommandLine.cs6
-rw-r--r--src/tools/heat/HeatCommand.cs22
-rw-r--r--src/tools/heat/HeatCommandLine.cs6
-rw-r--r--src/tools/heat/HelpCommand.cs17
-rw-r--r--src/wix/WixToolset.Converters/ConvertCommand.cs44
-rw-r--r--src/wix/WixToolset.Converters/ConverterExtensionCommandLine.cs15
-rw-r--r--src/wix/WixToolset.Converters/FixupCommandBase.cs39
-rw-r--r--src/wix/WixToolset.Converters/FormatCommand.cs46
-rw-r--r--src/wix/WixToolset.Core.Burn/BurnExtensionCommandLine.cs10
-rw-r--r--src/wix/WixToolset.Core.Burn/CommandLine/BurnCommand.cs46
-rw-r--r--src/wix/WixToolset.Core.Burn/CommandLine/BurnSubcommandBase.cs3
-rw-r--r--src/wix/WixToolset.Core.Burn/CommandLine/DetachSubcommand.cs10
-rw-r--r--src/wix/WixToolset.Core.Burn/CommandLine/ExtractSubcommand.cs11
-rw-r--r--src/wix/WixToolset.Core.Burn/CommandLine/ReattachSubcommand.cs10
-rw-r--r--src/wix/WixToolset.Core.Burn/CommandLine/RemotePayloadSubcommand.cs31
-rw-r--r--src/wix/WixToolset.Core.ExtensionCache/ExtensionCacheManagerCommand.cs69
-rw-r--r--src/wix/WixToolset.Core.ExtensionCache/ExtensionCacheManagerExtensionCommandLine.cs10
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/CommandLine/DecompilerSubcommand.cs16
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/CommandLine/InscribeSubcommand.cs10
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/CommandLine/TransformSubcommand.cs47
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/CommandLine/ValidateSubcommand.cs13
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/CommandLine/WindowsInstallerCommand.cs47
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/CommandLine/WindowsInstallerSubcommandBase.cs3
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerExtensionCommandLine.cs13
-rw-r--r--src/wix/WixToolset.Core/CommandLine/BuildCommand.cs66
-rw-r--r--src/wix/WixToolset.Core/CommandLine/CommandLine.cs50
-rw-r--r--src/wix/WixToolset.Core/CommandLine/CommandLineParser.cs21
-rw-r--r--src/wix/WixToolset.Core/CommandLine/HelpCommand.cs126
-rw-r--r--src/wix/WixToolset.Core/CommandLine/VersionCommand.cs16
-rw-r--r--src/wix/test/Example.Extension/ExamplePreprocessorExtensionAndCommandLine.cs9
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/CommandLineFixture.cs5
38 files changed, 685 insertions, 363 deletions
diff --git a/src/api/wix/WixToolset.Extensibility/BaseCommandLineCommand.cs b/src/api/wix/WixToolset.Extensibility/BaseCommandLineCommand.cs
new file mode 100644
index 00000000..69a4f49f
--- /dev/null
+++ b/src/api/wix/WixToolset.Extensibility/BaseCommandLineCommand.cs
@@ -0,0 +1,40 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Extensibility
4{
5 using System.Threading;
6 using System.Threading.Tasks;
7 using WixToolset.Extensibility.Data;
8 using WixToolset.Extensibility.Services;
9
10 /// <summary>
11 /// Base class for a command-line command.
12 /// </summary>
13 public abstract class BaseCommandLineCommand : ICommandLineCommand
14 {
15 /// <summary>
16 /// See <see cref="ICommandLineCommand.ShowLogo" />
17 /// </summary>
18 public virtual bool ShowLogo => false;
19
20 /// <summary>
21 /// See <see cref="ICommandLineCommand.StopParsing" />
22 /// </summary>
23 public bool StopParsing { get; protected set; }
24
25 /// <summary>
26 /// See <see cref="ICommandLineCommand.ExecuteAsync" />
27 /// </summary>
28 public abstract Task<int> ExecuteAsync(CancellationToken cancellationToken);
29
30 /// <summary>
31 /// See <see cref="ICommandLineCommand.GetCommandLineHelp" />
32 /// </summary>
33 public abstract CommandLineHelp GetCommandLineHelp();
34
35 /// <summary>
36 /// See <see cref="ICommandLineCommand.TryParseArgument" />
37 /// </summary>
38 public abstract bool TryParseArgument(ICommandLineParser parser, string argument);
39 }
40}
diff --git a/src/api/wix/WixToolset.Extensibility/BaseExtensionCommandLine.cs b/src/api/wix/WixToolset.Extensibility/BaseExtensionCommandLine.cs
index c716ac7e..2690788b 100644
--- a/src/api/wix/WixToolset.Extensibility/BaseExtensionCommandLine.cs
+++ b/src/api/wix/WixToolset.Extensibility/BaseExtensionCommandLine.cs
@@ -2,8 +2,6 @@
2 2
3namespace WixToolset.Extensibility 3namespace WixToolset.Extensibility
4{ 4{
5 using System;
6 using System.Collections.Generic;
7 using WixToolset.Extensibility.Data; 5 using WixToolset.Extensibility.Data;
8 using WixToolset.Extensibility.Services; 6 using WixToolset.Extensibility.Services;
9 7
@@ -13,9 +11,12 @@ namespace WixToolset.Extensibility
13 public abstract class BaseExtensionCommandLine : IExtensionCommandLine 11 public abstract class BaseExtensionCommandLine : IExtensionCommandLine
14 { 12 {
15 /// <summary> 13 /// <summary>
16 /// See <see cref="IExtensionCommandLine.CommandLineSwitches" /> 14 /// See <see cref="IExtensionCommandLine.GetCommandLineHelp" />
17 /// </summary> 15 /// </summary>
18 public virtual IReadOnlyCollection<ExtensionCommandLineSwitch> CommandLineSwitches => Array.Empty<ExtensionCommandLineSwitch>(); 16 public virtual CommandLineHelp GetCommandLineHelp()
17 {
18 return null;
19 }
19 20
20 /// <summary> 21 /// <summary>
21 /// See <see cref="IExtensionCommandLine.PostParse" /> 22 /// See <see cref="IExtensionCommandLine.PostParse" />
diff --git a/src/api/wix/WixToolset.Extensibility/Data/CommandLineHelp.cs b/src/api/wix/WixToolset.Extensibility/Data/CommandLineHelp.cs
new file mode 100644
index 00000000..52291947
--- /dev/null
+++ b/src/api/wix/WixToolset.Extensibility/Data/CommandLineHelp.cs
@@ -0,0 +1,52 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Extensibility.Data
4{
5 using System.Collections.Generic;
6
7 /// <summary>
8 /// A command line option (switch or command) description.
9 /// </summary>
10 public class CommandLineHelp
11 {
12 /// <summary>
13 /// Creates command line help.
14 /// </summary>
15 /// <param name="description">Description for the command line option.</param>
16 /// <param name="usage">Optional usage for the command line option.</param>
17 /// <param name="switches">Optional list of switches.</param>
18 /// <param name="commands">Optional list of commands.</param>
19 public CommandLineHelp(string description, string usage = null, IReadOnlyCollection<CommandLineHelpSwitch> switches = null, IReadOnlyCollection<CommandLineHelpCommand> commands = null)
20 {
21 this.Description = description;
22 this.Usage = usage;
23 this.Switches = switches;
24 this.Commands = commands;
25 }
26
27 /// <summary>
28 /// Description for the command line option.
29 /// </summary>
30 public string Description { get; set; }
31
32 /// <summary>
33 /// Usage for the command line option.
34 /// </summary>
35 public string Usage { get; set; }
36
37 /// <summary>
38 /// Optional additional notes for the command line option.
39 /// </summary>
40 public string Notes { get; set; }
41
42 /// <summary>
43 /// Optional list of command line switches.
44 /// </summary>
45 public IReadOnlyCollection<CommandLineHelpSwitch> Switches { get; set; }
46
47 /// <summary>
48 /// Optional list of command line commands.
49 /// </summary>
50 public IReadOnlyCollection<CommandLineHelpCommand> Commands { get; set; }
51 }
52}
diff --git a/src/api/wix/WixToolset.Extensibility/Data/CommandLineHelpCommand.cs b/src/api/wix/WixToolset.Extensibility/Data/CommandLineHelpCommand.cs
new file mode 100644
index 00000000..a9fc70f9
--- /dev/null
+++ b/src/api/wix/WixToolset.Extensibility/Data/CommandLineHelpCommand.cs
@@ -0,0 +1,31 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Extensibility.Data
4{
5 /// <summary>
6 /// A command line command description.
7 /// </summary>
8 public class CommandLineHelpCommand
9 {
10 /// <summary>
11 /// Creates help for command line command.
12 /// </summary>
13 /// <param name="name">Name of command.</param>
14 /// <param name="description">Description for command.</param>
15 public CommandLineHelpCommand(string name, string description)
16 {
17 Name = name;
18 Description = description;
19 }
20
21 /// <summary>
22 /// Name of command.
23 /// </summary>
24 public string Name { get; set; }
25
26 /// <summary>
27 /// Description of the command.
28 /// </summary>
29 public string Description { get; set; }
30 }
31}
diff --git a/src/api/wix/WixToolset.Extensibility/Data/CommandLineHelpSwitch.cs b/src/api/wix/WixToolset.Extensibility/Data/CommandLineHelpSwitch.cs
new file mode 100644
index 00000000..19c014ad
--- /dev/null
+++ b/src/api/wix/WixToolset.Extensibility/Data/CommandLineHelpSwitch.cs
@@ -0,0 +1,47 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Extensibility.Data
4{
5 /// <summary>
6 /// A command line switch description.
7 /// </summary>
8 public class CommandLineHelpSwitch
9 {
10 /// <summary>
11 /// Creates help for command line switch.
12 /// </summary>
13 /// <param name="name">Name of switch.</param>
14 /// <param name="description">Description for switch.</param>
15 public CommandLineHelpSwitch(string name, string description) : this(name, null, description)
16 {
17 }
18
19 /// <summary>
20 /// Creates help for command line switch.
21 /// </summary>
22 /// <param name="name">Name of switch.</param>
23 /// <param name="shortName">Optional short name of switch.</param>
24 /// <param name="description">Description for switch.</param>
25 public CommandLineHelpSwitch(string name, string shortName, string description)
26 {
27 Name = name;
28 ShortName = shortName;
29 Description = description;
30 }
31
32 /// <summary>
33 /// Name for switch.
34 /// </summary>
35 public string Name { get; set; }
36
37 /// <summary>
38 /// Optional short name for switch.
39 /// </summary>
40 public string ShortName { get; set; }
41
42 /// <summary>
43 /// Description of the switch.
44 /// </summary>
45 public string Description { get; set; }
46 }
47}
diff --git a/src/api/wix/WixToolset.Extensibility/Data/ExtensionCommandLineSwitch.cs b/src/api/wix/WixToolset.Extensibility/Data/ExtensionCommandLineSwitch.cs
deleted file mode 100644
index 14b5dabb..00000000
--- a/src/api/wix/WixToolset.Extensibility/Data/ExtensionCommandLineSwitch.cs
+++ /dev/null
@@ -1,20 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Extensibility.Data
4{
5 /// <summary>
6 /// A command line option.
7 /// </summary>
8 public struct ExtensionCommandLineSwitch
9 {
10 /// <summary>
11 ///
12 /// </summary>
13 public string Switch { get; set; }
14
15 /// <summary>
16 ///
17 /// </summary>
18 public string Description { get; set; }
19 }
20}
diff --git a/src/api/wix/WixToolset.Extensibility/Data/ICommandLineCommand.cs b/src/api/wix/WixToolset.Extensibility/Data/ICommandLineCommand.cs
index b6c9ef3e..1f58bfb5 100644
--- a/src/api/wix/WixToolset.Extensibility/Data/ICommandLineCommand.cs
+++ b/src/api/wix/WixToolset.Extensibility/Data/ICommandLineCommand.cs
@@ -12,19 +12,19 @@ namespace WixToolset.Extensibility.Data
12 public interface ICommandLineCommand 12 public interface ICommandLineCommand
13 { 13 {
14 /// <summary> 14 /// <summary>
15 /// Indicates the command-line should show help for the command. 15 /// Indicates the command-line should show the logo.
16 /// </summary> 16 /// </summary>
17 bool ShowHelp { get; set; } 17 bool ShowLogo { get; }
18 18
19 /// <summary> 19 /// <summary>
20 /// Indicates the command-line should show the command-line logo. 20 /// Indicates the command-line parsing can stop.
21 /// </summary> 21 /// </summary>
22 bool ShowLogo { get; set; } 22 bool StopParsing { get; }
23 23
24 /// <summary> 24 /// <summary>
25 /// Indicates the command-line parsing can stop. 25 /// Gets the help for this command.
26 /// </summary> 26 /// </summary>
27 bool StopParsing { get; } 27 CommandLineHelp GetCommandLineHelp();
28 28
29 /// <summary> 29 /// <summary>
30 /// Executes the command. 30 /// Executes the command.
diff --git a/src/api/wix/WixToolset.Extensibility/IExtensionCommandLine.cs b/src/api/wix/WixToolset.Extensibility/IExtensionCommandLine.cs
index f7b19955..304c30bb 100644
--- a/src/api/wix/WixToolset.Extensibility/IExtensionCommandLine.cs
+++ b/src/api/wix/WixToolset.Extensibility/IExtensionCommandLine.cs
@@ -2,7 +2,6 @@
2 2
3namespace WixToolset.Extensibility 3namespace WixToolset.Extensibility
4{ 4{
5 using System.Collections.Generic;
6 using WixToolset.Extensibility.Data; 5 using WixToolset.Extensibility.Data;
7 using WixToolset.Extensibility.Services; 6 using WixToolset.Extensibility.Services;
8 7
@@ -12,10 +11,9 @@ namespace WixToolset.Extensibility
12 public interface IExtensionCommandLine 11 public interface IExtensionCommandLine
13 { 12 {
14 /// <summary> 13 /// <summary>
15 /// Gets the supported command line types for this extension. 14 /// Gets the help for this extension.
16 /// </summary> 15 /// </summary>
17 /// <value>The supported command line types for this extension.</value> 16 CommandLineHelp GetCommandLineHelp();
18 IReadOnlyCollection<ExtensionCommandLineSwitch> CommandLineSwitches { get; }
19 17
20 /// <summary> 18 /// <summary>
21 /// Called before the command-line is parsed. 19 /// Called before the command-line is parsed.
diff --git a/src/tools/heat/HeatCommand.cs b/src/tools/heat/HeatCommand.cs
index 56277004..6815acd6 100644
--- a/src/tools/heat/HeatCommand.cs
+++ b/src/tools/heat/HeatCommand.cs
@@ -11,13 +11,16 @@ namespace WixToolset.Harvesters
11 using System.Threading.Tasks; 11 using System.Threading.Tasks;
12 using System.Xml; 12 using System.Xml;
13 using WixToolset.Data; 13 using WixToolset.Data;
14 using WixToolset.Extensibility;
14 using WixToolset.Extensibility.Data; 15 using WixToolset.Extensibility.Data;
15 using WixToolset.Extensibility.Services; 16 using WixToolset.Extensibility.Services;
16 using WixToolset.Harvesters.Extensibility; 17 using WixToolset.Harvesters.Extensibility;
17 using Wix = WixToolset.Harvesters.Serialize; 18 using Wix = WixToolset.Harvesters.Serialize;
18 19
19 internal class HeatCommand : ICommandLineCommand 20 internal class HeatCommand : BaseCommandLineCommand
20 { 21 {
22 private bool showLogo;
23
21 public HeatCommand(string harvestType, IList<IHeatExtension> extensions, IServiceProvider serviceProvider) 24 public HeatCommand(string harvestType, IList<IHeatExtension> extensions, IServiceProvider serviceProvider)
22 { 25 {
23 this.Extensions = extensions; 26 this.Extensions = extensions;
@@ -28,11 +31,7 @@ namespace WixToolset.Harvesters
28 this.ExtensionOptions.Add(harvestType); 31 this.ExtensionOptions.Add(harvestType);
29 } 32 }
30 33
31 public bool ShowHelp { get; set; } 34 public override bool ShowLogo => this.showLogo;
32
33 public bool ShowLogo { get; set; }
34
35 public bool StopParsing { get; private set; }
36 35
37 private string ExtensionArgument { get; set; } 36 private string ExtensionArgument { get; set; }
38 37
@@ -50,13 +49,18 @@ namespace WixToolset.Harvesters
50 49
51 private IServiceProvider ServiceProvider { get; } 50 private IServiceProvider ServiceProvider { get; }
52 51
53 public Task<int> ExecuteAsync(CancellationToken cancellationToken) 52 public override CommandLineHelp GetCommandLineHelp()
53 {
54 return null;
55 }
56
57 public override Task<int> ExecuteAsync(CancellationToken cancellationToken)
54 { 58 {
55 var exitCode = this.Harvest(); 59 var exitCode = this.Harvest();
56 return Task.FromResult(exitCode); 60 return Task.FromResult(exitCode);
57 } 61 }
58 62
59 public bool TryParseArgument(ICommandLineParser parser, string arg) 63 public override bool TryParseArgument(ICommandLineParser parser, string arg)
60 { 64 {
61 if (this.ExtensionArgument == null) 65 if (this.ExtensionArgument == null)
62 { 66 {
@@ -67,7 +71,7 @@ namespace WixToolset.Harvesters
67 string parameter = arg.Substring(1); 71 string parameter = arg.Substring(1);
68 if ("nologo" == parameter) 72 if ("nologo" == parameter)
69 { 73 {
70 this.ShowLogo = false; 74 this.showLogo = false;
71 } 75 }
72 else if ("o" == parameter || "out" == parameter) 76 else if ("o" == parameter || "out" == parameter)
73 { 77 {
diff --git a/src/tools/heat/HeatCommandLine.cs b/src/tools/heat/HeatCommandLine.cs
index b11dda4e..f299266d 100644
--- a/src/tools/heat/HeatCommandLine.cs
+++ b/src/tools/heat/HeatCommandLine.cs
@@ -13,19 +13,17 @@ namespace WixToolset.Harvesters
13 13
14 internal class HeatCommandLine : IHeatCommandLine 14 internal class HeatCommandLine : IHeatCommandLine
15 { 15 {
16 private readonly List<IHeatExtension> extensions;
17 private readonly IMessaging messaging;
18 private readonly IServiceProvider serviceProvider; 16 private readonly IServiceProvider serviceProvider;
17 private readonly List<IHeatExtension> extensions;
19 18
20 public HeatCommandLine(IServiceProvider serviceProvider, IEnumerable<IHeatExtension> heatExtensions) 19 public HeatCommandLine(IServiceProvider serviceProvider, IEnumerable<IHeatExtension> heatExtensions)
21 { 20 {
21 this.serviceProvider = serviceProvider;
22 this.extensions = new List<IHeatExtension> { new IIsHeatExtension(), new UtilHeatExtension(serviceProvider), new VSHeatExtension() }; 22 this.extensions = new List<IHeatExtension> { new IIsHeatExtension(), new UtilHeatExtension(serviceProvider), new VSHeatExtension() };
23 if (heatExtensions != null) 23 if (heatExtensions != null)
24 { 24 {
25 this.extensions.AddRange(heatExtensions); 25 this.extensions.AddRange(heatExtensions);
26 } 26 }
27 this.messaging = serviceProvider.GetService<IMessaging>();
28 this.serviceProvider = serviceProvider;
29 } 27 }
30 28
31 public ICommandLineCommand ParseStandardCommandLine(ICommandLineArguments arguments) 29 public ICommandLineCommand ParseStandardCommandLine(ICommandLineArguments arguments)
diff --git a/src/tools/heat/HelpCommand.cs b/src/tools/heat/HelpCommand.cs
index d991b4fa..25d8cd87 100644
--- a/src/tools/heat/HelpCommand.cs
+++ b/src/tools/heat/HelpCommand.cs
@@ -8,12 +8,13 @@ namespace WixToolset.Harvesters
8 using System.Diagnostics; 8 using System.Diagnostics;
9 using System.Threading; 9 using System.Threading;
10 using System.Threading.Tasks; 10 using System.Threading.Tasks;
11 using WixToolset.Extensibility;
11 using WixToolset.Extensibility.Data; 12 using WixToolset.Extensibility.Data;
12 using WixToolset.Extensibility.Services; 13 using WixToolset.Extensibility.Services;
13 using WixToolset.Harvesters.Data; 14 using WixToolset.Harvesters.Data;
14 using WixToolset.Harvesters.Extensibility; 15 using WixToolset.Harvesters.Extensibility;
15 16
16 internal class HelpCommand : ICommandLineCommand 17 internal class HelpCommand : BaseCommandLineCommand
17 { 18 {
18 const string HelpMessageOptionFormat = " {0,-7} {1}"; 19 const string HelpMessageOptionFormat = " {0,-7} {1}";
19 20
@@ -24,17 +25,12 @@ namespace WixToolset.Harvesters
24 25
25 private IList<IHeatExtension> Extensions { get; } 26 private IList<IHeatExtension> Extensions { get; }
26 27
27 public bool ShowHelp { get; set; } 28 public override CommandLineHelp GetCommandLineHelp()
28
29 public bool ShowLogo
30 { 29 {
31 get => false; 30 return null;
32 set { }
33 } 31 }
34 32
35 public bool StopParsing => true; 33 public override Task<int> ExecuteAsync(CancellationToken cancellationToken)
36
37 public Task<int> ExecuteAsync(CancellationToken cancellationToken)
38 { 34 {
39 var exitCode = this.DisplayHelp(); 35 var exitCode = this.DisplayHelp();
40 return Task.FromResult(exitCode); 36 return Task.FromResult(exitCode);
@@ -50,8 +46,9 @@ namespace WixToolset.Harvesters
50 Console.WriteLine(); 46 Console.WriteLine();
51 } 47 }
52 48
53 public bool TryParseArgument(ICommandLineParser parser, string argument) 49 public override bool TryParseArgument(ICommandLineParser parser, string argument)
54 { 50 {
51 this.StopParsing = true;
55 return true; 52 return true;
56 } 53 }
57 54
diff --git a/src/wix/WixToolset.Converters/ConvertCommand.cs b/src/wix/WixToolset.Converters/ConvertCommand.cs
index b6826f43..bec6cd19 100644
--- a/src/wix/WixToolset.Converters/ConvertCommand.cs
+++ b/src/wix/WixToolset.Converters/ConvertCommand.cs
@@ -5,27 +5,34 @@ namespace WixToolset.Converters
5 using System; 5 using System;
6 using System.Threading; 6 using System.Threading;
7 using System.Threading.Tasks; 7 using System.Threading.Tasks;
8 using WixToolset.Extensibility.Services; 8 using WixToolset.Extensibility.Data;
9 9
10 internal class ConvertCommand : FixupCommandBase 10 internal class ConvertCommand : FixupCommandBase
11 { 11 {
12 private const string SettingsFileDefault = "wix.convert.settings.xml"; 12 private const string SettingsFileDefault = "wix.convert.settings.xml";
13 13
14 public ConvertCommand(IServiceProvider serviceProvider) 14 public ConvertCommand(IServiceProvider serviceProvider) : base(serviceProvider)
15 { 15 {
16 this.Messaging = serviceProvider.GetService<IMessaging>();
17 } 16 }
18 17
19 private IMessaging Messaging { get; } 18 public override CommandLineHelp GetCommandLineHelp()
20
21 public override Task<int> ExecuteAsync(CancellationToken cancellationToken)
22 { 19 {
23 if (this.ShowHelp) 20 return new CommandLineHelp("Convert v3 source code to v4 source code.", "convert [options] sourceFile [sourceFile ...]")
24 { 21 {
25 DisplayHelp(); 22 Switches = new[]
26 return Task.FromResult(-1); 23 {
27 } 24 new CommandLineHelpSwitch("--dry-run", "-n", "Only display errors, do not update files."),
25 new CommandLineHelpSwitch("--recurse", "-r", "Search for matching files in current dir and subdirs."),
26 new CommandLineHelpSwitch("-set1<file>", "Primary settings file."),
27 new CommandLineHelpSwitch("-set2<file>", "Secondary settings file (overrides primary)."),
28 new CommandLineHelpSwitch("-indent:<n>", "Indentation multiple (overrides default of 4)."),
29 },
30 Notes = " sourceFile may use wildcards like *.wxs"
31 };
32 }
28 33
34 public override Task<int> ExecuteAsync(CancellationToken cancellationToken)
35 {
29 this.ParseSettings(SettingsFileDefault); 36 this.ParseSettings(SettingsFileDefault);
30 37
31 var converter = new WixConverter(this.Messaging, this.IndentationAmount, this.ErrorsAsWarnings, this.IgnoreErrors); 38 var converter = new WixConverter(this.Messaging, this.IndentationAmount, this.ErrorsAsWarnings, this.IgnoreErrors);
@@ -39,22 +46,5 @@ namespace WixToolset.Converters
39 return converter.ConvertFile(file, fix); 46 return converter.ConvertFile(file, fix);
40 } 47 }
41 } 48 }
42
43 private static void DisplayHelp()
44 {
45 Console.WriteLine();
46 Console.WriteLine("Usage: wix convert [options] sourceFile [sourceFile ...]");
47 Console.WriteLine();
48 Console.WriteLine("Options:");
49 Console.WriteLine(" -h|--help Show command line help.");
50 Console.WriteLine(" --nologo Suppress displaying the logo information.");
51 Console.WriteLine(" -n|--dry-run Only display errors, do not update files.");
52 Console.WriteLine(" -r|--recurse Search for matching files in current dir and subdirs.");
53 Console.WriteLine(" -set1<file> Primary settings file.");
54 Console.WriteLine(" -set2<file> Secondary settings file (overrides primary).");
55 Console.WriteLine(" -indent:<n> Indentation multiple (overrides default of 4).");
56 Console.WriteLine();
57 Console.WriteLine(" sourceFile may use wildcards like *.wxs");
58 }
59 } 49 }
60} 50}
diff --git a/src/wix/WixToolset.Converters/ConverterExtensionCommandLine.cs b/src/wix/WixToolset.Converters/ConverterExtensionCommandLine.cs
index 06d3658c..f9facf99 100644
--- a/src/wix/WixToolset.Converters/ConverterExtensionCommandLine.cs
+++ b/src/wix/WixToolset.Converters/ConverterExtensionCommandLine.cs
@@ -3,7 +3,6 @@
3namespace WixToolset.Converters 3namespace WixToolset.Converters
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic;
7 using WixToolset.Extensibility; 6 using WixToolset.Extensibility;
8 using WixToolset.Extensibility.Data; 7 using WixToolset.Extensibility.Data;
9 using WixToolset.Extensibility.Services; 8 using WixToolset.Extensibility.Services;
@@ -21,11 +20,17 @@ namespace WixToolset.Converters
21 20
22 private IServiceProvider ServiceProvider { get; } 21 private IServiceProvider ServiceProvider { get; }
23 22
24 public override IReadOnlyCollection<ExtensionCommandLineSwitch> CommandLineSwitches => new ExtensionCommandLineSwitch[] 23 public override CommandLineHelp GetCommandLineHelp()
25 { 24 {
26 new ExtensionCommandLineSwitch { Switch = "convert", Description = "Convert v3 source code to v4 source code." }, 25 return new CommandLineHelp("Source code converter and formatter.")
27 new ExtensionCommandLineSwitch { Switch = "format", Description = "Ensures consistent formatting of source code." }, 26 {
28 }; 27 Commands = new[]
28 {
29 new CommandLineHelpCommand("convert", "Convert v3 source code to v4 source code."),
30 new CommandLineHelpCommand("format", "Ensures consistent formatting of source code."),
31 }
32 };
33 }
29 34
30 public override bool TryParseCommand(ICommandLineParser parser, string argument, out ICommandLineCommand command) 35 public override bool TryParseCommand(ICommandLineParser parser, string argument, out ICommandLineCommand command)
31 { 36 {
diff --git a/src/wix/WixToolset.Converters/FixupCommandBase.cs b/src/wix/WixToolset.Converters/FixupCommandBase.cs
index 71d4dad7..5dc96a51 100644
--- a/src/wix/WixToolset.Converters/FixupCommandBase.cs
+++ b/src/wix/WixToolset.Converters/FixupCommandBase.cs
@@ -6,15 +6,17 @@ namespace WixToolset.Converters
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.IO; 7 using System.IO;
8 using System.Threading; 8 using System.Threading;
9 using System.Threading.Tasks;
10 using System.Xml; 9 using System.Xml;
11 using WixToolset.Extensibility.Data; 10 using WixToolset.Data;
11 using WixToolset.Extensibility;
12 using WixToolset.Extensibility.Services; 12 using WixToolset.Extensibility.Services;
13 13
14 internal abstract class FixupCommandBase : ICommandLineCommand 14 internal abstract class FixupCommandBase : BaseCommandLineCommand
15 { 15 {
16 protected FixupCommandBase() 16 protected FixupCommandBase(IServiceProvider serviceProvider)
17 { 17 {
18 this.Messaging = serviceProvider.GetService<IMessaging>();
19
18 this.IndentationAmount = 4; // default indentation amount 20 this.IndentationAmount = 4; // default indentation amount
19 this.ErrorsAsWarnings = new HashSet<string>(); 21 this.ErrorsAsWarnings = new HashSet<string>();
20 this.ExemptFiles = new HashSet<string>(); 22 this.ExemptFiles = new HashSet<string>();
@@ -23,11 +25,7 @@ namespace WixToolset.Converters
23 this.SearchPatterns = new List<string>(); 25 this.SearchPatterns = new List<string>();
24 } 26 }
25 27
26 public bool ShowHelp { get; set; } 28 protected IMessaging Messaging { get; }
27
28 public bool ShowLogo { get; set; }
29
30 public bool StopParsing { get; set; }
31 29
32 protected CustomTableTarget CustomTableSetting { get; set; } 30 protected CustomTableTarget CustomTableSetting { get; set; }
33 31
@@ -43,15 +41,15 @@ namespace WixToolset.Converters
43 41
44 protected bool Recurse { get; set; } 42 protected bool Recurse { get; set; }
45 43
46 private HashSet<string> SearchPatternResults { get; } 44 private HashSet<string> SearchPatternResults { get; }
47 45
48 private List<string> SearchPatterns { get; } 46 private List<string> SearchPatterns { get; }
49 47
50 private string SettingsFile1 { get; set; } 48 private string SettingsFile1 { get; set; }
51 49
52 private string SettingsFile2 { get; set; } 50 private string SettingsFile2 { get; set; }
53 51
54 public bool TryParseArgument(ICommandLineParser parser, string argument) 52 public override bool TryParseArgument(ICommandLineParser parser, string argument)
55 { 53 {
56 if (!parser.IsSwitch(argument)) 54 if (!parser.IsSwitch(argument))
57 { 55 {
@@ -62,14 +60,6 @@ namespace WixToolset.Converters
62 var parameter = argument.Substring(1); 60 var parameter = argument.Substring(1);
63 switch (parameter.ToLowerInvariant()) 61 switch (parameter.ToLowerInvariant())
64 { 62 {
65 case "?":
66 case "h":
67 case "-help":
68 this.ShowHelp = true;
69 this.ShowLogo = true;
70 this.StopParsing = true;
71 return true;
72
73 case "-custom-table": 63 case "-custom-table":
74 var customTableSetting = parser.GetNextArgumentOrError(argument); 64 var customTableSetting = parser.GetNextArgumentOrError(argument);
75 switch (customTableSetting) 65 switch (customTableSetting)
@@ -91,11 +81,6 @@ namespace WixToolset.Converters
91 this.DryRun = true; 81 this.DryRun = true;
92 return true; 82 return true;
93 83
94 case "nologo":
95 case "-nologo":
96 this.ShowLogo = false;
97 return true;
98
99 case "s": 84 case "s":
100 case "r": 85 case "r":
101 case "-recurse": 86 case "-recurse":
@@ -131,8 +116,6 @@ namespace WixToolset.Converters
131 } 116 }
132 } 117 }
133 118
134 public abstract Task<int> ExecuteAsync(CancellationToken cancellationToken);
135
136 protected void ParseSettings(string defaultSettingsFile) 119 protected void ParseSettings(string defaultSettingsFile)
137 { 120 {
138 // parse the settings if any were specified 121 // parse the settings if any were specified
@@ -157,7 +140,7 @@ namespace WixToolset.Converters
157 { 140 {
158 if (!this.SearchPatternResults.Contains(searchPattern)) 141 if (!this.SearchPatternResults.Contains(searchPattern))
159 { 142 {
160 Console.Error.WriteLine("Could not find file \"{0}\"", searchPattern); 143 this.Messaging.Write(ErrorMessages.FileNotFound(null, searchPattern));
161 errors++; 144 errors++;
162 } 145 }
163 } 146 }
diff --git a/src/wix/WixToolset.Converters/FormatCommand.cs b/src/wix/WixToolset.Converters/FormatCommand.cs
index 0861fc51..f3c8efdd 100644
--- a/src/wix/WixToolset.Converters/FormatCommand.cs
+++ b/src/wix/WixToolset.Converters/FormatCommand.cs
@@ -5,31 +5,38 @@ namespace WixToolset.Converters
5 using System; 5 using System;
6 using System.Threading; 6 using System.Threading;
7 using System.Threading.Tasks; 7 using System.Threading.Tasks;
8 using WixToolset.Extensibility.Services; 8 using WixToolset.Extensibility.Data;
9 9
10 internal class FormatCommand : FixupCommandBase 10 internal class FormatCommand : FixupCommandBase
11 { 11 {
12 private const string SettingsFileDefault = "wix.format.settings.xml"; 12 private const string SettingsFileDefault = "wix.format.settings.xml";
13 13
14 public FormatCommand(IServiceProvider serviceProvider) 14 public FormatCommand(IServiceProvider serviceProvider) : base(serviceProvider)
15 { 15 {
16 this.Messaging = serviceProvider.GetService<IMessaging>();
17 } 16 }
18 17
19 private IMessaging Messaging { get; } 18 public override CommandLineHelp GetCommandLineHelp()
19 {
20 return new CommandLineHelp("Ensures consistent formatting of source code.", "format [options] sourceFile [sourceFile ...]")
21 {
22 Switches = new[]
23 {
24 new CommandLineHelpSwitch("--dry-run", "-n", "Only display errors, do not update files."),
25 new CommandLineHelpSwitch("--recurse", "-r", "Search for matching files in current dir and subdirs."),
26 new CommandLineHelpSwitch("-set1<file>", "Primary settings file."),
27 new CommandLineHelpSwitch("-set2<file>", "Secondary settings file (overrides primary)."),
28 new CommandLineHelpSwitch("-indent:<n>", "Indentation multiple (overrides default of 4)."),
29 },
30 Notes = " sourceFile may use wildcards like *.wxs"
31 };
32 }
20 33
21 public override Task<int> ExecuteAsync(CancellationToken cancellationToken) 34 public override Task<int> ExecuteAsync(CancellationToken cancellationToken)
22 { 35 {
23 if (this.ShowHelp) 36 this.ParseSettings(SettingsFileDefault);
24 {
25 DisplayHelp();
26 return Task.FromResult(-1);
27 }
28 37
29 var converter = new WixConverter(this.Messaging, this.IndentationAmount, this.ErrorsAsWarnings, this.IgnoreErrors); 38 var converter = new WixConverter(this.Messaging, this.IndentationAmount, this.ErrorsAsWarnings, this.IgnoreErrors);
30 39
31 this.ParseSettings(SettingsFileDefault);
32
33 var errors = base.Inspect(Inspector, cancellationToken); 40 var errors = base.Inspect(Inspector, cancellationToken);
34 41
35 return Task.FromResult(errors); 42 return Task.FromResult(errors);
@@ -39,22 +46,5 @@ namespace WixToolset.Converters
39 return converter.FormatFile(file, fix); 46 return converter.FormatFile(file, fix);
40 } 47 }
41 } 48 }
42
43 private static void DisplayHelp()
44 {
45 Console.WriteLine();
46 Console.WriteLine("Usage: wix format [options] sourceFile [sourceFile ...]");
47 Console.WriteLine();
48 Console.WriteLine("Options:");
49 Console.WriteLine(" -h|--help Show command line help.");
50 Console.WriteLine(" --nologo Suppress displaying the logo information.");
51 Console.WriteLine(" -n|--dry-run Only display errors, do not update files.");
52 Console.WriteLine(" -r|--recurse Search for matching files in current dir and subdirs.");
53 Console.WriteLine(" -set1<file> Primary settings file.");
54 Console.WriteLine(" -set2<file> Secondary settings file (overrides primary).");
55 Console.WriteLine(" -indent:<n> Indentation multiple (overrides default of 4).");
56 Console.WriteLine();
57 Console.WriteLine(" sourceFile may use wildcards like *.wxs");
58 }
59 } 49 }
60} 50}
diff --git a/src/wix/WixToolset.Core.Burn/BurnExtensionCommandLine.cs b/src/wix/WixToolset.Core.Burn/BurnExtensionCommandLine.cs
index b306199d..f8a0b7fc 100644
--- a/src/wix/WixToolset.Core.Burn/BurnExtensionCommandLine.cs
+++ b/src/wix/WixToolset.Core.Burn/BurnExtensionCommandLine.cs
@@ -3,7 +3,6 @@
3namespace WixToolset.Core.Burn 3namespace WixToolset.Core.Burn
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic;
7 using WixToolset.Core.Burn.CommandLine; 6 using WixToolset.Core.Burn.CommandLine;
8 using WixToolset.Extensibility; 7 using WixToolset.Extensibility;
9 using WixToolset.Extensibility.Data; 8 using WixToolset.Extensibility.Data;
@@ -22,10 +21,13 @@ namespace WixToolset.Core.Burn
22 21
23 private IServiceProvider ServiceProvider { get; } 22 private IServiceProvider ServiceProvider { get; }
24 23
25 public override IReadOnlyCollection<ExtensionCommandLineSwitch> CommandLineSwitches => new ExtensionCommandLineSwitch[] 24 public override CommandLineHelp GetCommandLineHelp()
26 { 25 {
27 new ExtensionCommandLineSwitch { Switch = "burn", Description = "Burn specialized operations." }, 26 return new CommandLineHelp(null)
28 }; 27 {
28 Commands = new[] { new CommandLineHelpCommand("burn", "Specialized operations for manipulating Burn-based bundles.") }
29 };
30 }
29 31
30 public override bool TryParseCommand(ICommandLineParser parser, string argument, out ICommandLineCommand command) 32 public override bool TryParseCommand(ICommandLineParser parser, string argument, out ICommandLineCommand command)
31 { 33 {
diff --git a/src/wix/WixToolset.Core.Burn/CommandLine/BurnCommand.cs b/src/wix/WixToolset.Core.Burn/CommandLine/BurnCommand.cs
index c9c0c657..886551e3 100644
--- a/src/wix/WixToolset.Core.Burn/CommandLine/BurnCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/CommandLine/BurnCommand.cs
@@ -5,41 +5,50 @@ namespace WixToolset.Core.Burn.CommandLine
5 using System; 5 using System;
6 using System.Threading; 6 using System.Threading;
7 using System.Threading.Tasks; 7 using System.Threading.Tasks;
8 using WixToolset.Extensibility;
8 using WixToolset.Extensibility.Data; 9 using WixToolset.Extensibility.Data;
9 using WixToolset.Extensibility.Services; 10 using WixToolset.Extensibility.Services;
10 11
11 /// <summary> 12 /// <summary>
12 /// Burn specialized command. 13 /// Burn specialized command.
13 /// </summary> 14 /// </summary>
14 internal class BurnCommand : ICommandLineCommand 15 internal class BurnCommand : BaseCommandLineCommand
15 { 16 {
16 public BurnCommand(IServiceProvider serviceProvider) 17 public BurnCommand(IServiceProvider serviceProvider)
17 { 18 {
18 this.ServiceProvider = serviceProvider; 19 this.ServiceProvider = serviceProvider;
19 } 20 }
20 21
21 public bool ShowHelp { get; set; }
22
23 public bool ShowLogo { get; set; }
24
25 public bool StopParsing { get; set; }
26
27 private IServiceProvider ServiceProvider { get; } 22 private IServiceProvider ServiceProvider { get; }
28 23
29 private BurnSubcommandBase Subcommand { get; set; } 24 private BurnSubcommandBase Subcommand { get; set; }
30 25
31 public Task<int> ExecuteAsync(CancellationToken cancellationToken) 26 public override CommandLineHelp GetCommandLineHelp()
32 { 27 {
33 if (this.ShowHelp || this.Subcommand is null) 28 return this.Subcommand?.GetCommandLineHelp() ?? new CommandLineHelp("Specialized operations for manipulating Burn-based bundles.", "burn detach|extract|reattach|remotepayload")
34 { 29 {
35 DisplayHelp(); 30 Commands = new[]
31 {
32 new CommandLineHelpCommand("detach", "Detaches the burn engine from a bundle so it can be signed."),
33 new CommandLineHelpCommand("extract", "Extracts the internals of a bundle to a folder."),
34 new CommandLineHelpCommand("reattach", "Reattaches a signed burn engine to a bundle."),
35 new CommandLineHelpCommand("remotepayload", "Extracts the internals of a bundle."),
36 }
37 };
38 }
39
40 public override Task<int> ExecuteAsync(CancellationToken cancellationToken)
41 {
42 if (this.Subcommand is null)
43 {
44 Console.Error.WriteLine("A subcommand is required for the \"burn\" command. Add -h to for help.");
36 return Task.FromResult(1); 45 return Task.FromResult(1);
37 } 46 }
38 47
39 return this.Subcommand.ExecuteAsync(cancellationToken); 48 return this.Subcommand.ExecuteAsync(cancellationToken);
40 } 49 }
41 50
42 public bool TryParseArgument(ICommandLineParser parser, string argument) 51 public override bool TryParseArgument(ICommandLineParser parser, string argument)
43 { 52 {
44 if (this.Subcommand is null) 53 if (this.Subcommand is null)
45 { 54 {
@@ -67,20 +76,5 @@ namespace WixToolset.Core.Burn.CommandLine
67 76
68 return this.Subcommand.TryParseArgument(parser, argument); 77 return this.Subcommand.TryParseArgument(parser, argument);
69 } 78 }
70
71 private static void DisplayHelp()
72 {
73 Console.WriteLine();
74 Console.WriteLine("Usage: wix burn detach|reattach bundle.exe -out engine.exe");
75 Console.WriteLine();
76 Console.WriteLine("Options:");
77 Console.WriteLine(" -h|--help Show command line help.");
78 Console.WriteLine(" --nologo Suppress displaying the logo information.");
79 Console.WriteLine();
80 Console.WriteLine("Commands:");
81 Console.WriteLine();
82 Console.WriteLine(" detach Detaches the burn engine from a bundle so it can be signed.");
83 Console.WriteLine(" reattach Reattaches a signed burn engine to a bundle.");
84 }
85 } 79 }
86} 80}
diff --git a/src/wix/WixToolset.Core.Burn/CommandLine/BurnSubcommandBase.cs b/src/wix/WixToolset.Core.Burn/CommandLine/BurnSubcommandBase.cs
index cf2da708..50bb6741 100644
--- a/src/wix/WixToolset.Core.Burn/CommandLine/BurnSubcommandBase.cs
+++ b/src/wix/WixToolset.Core.Burn/CommandLine/BurnSubcommandBase.cs
@@ -4,10 +4,13 @@ namespace WixToolset.Core.Burn.CommandLine
4{ 4{
5 using System.Threading; 5 using System.Threading;
6 using System.Threading.Tasks; 6 using System.Threading.Tasks;
7 using WixToolset.Extensibility.Data;
7 using WixToolset.Extensibility.Services; 8 using WixToolset.Extensibility.Services;
8 9
9 internal abstract class BurnSubcommandBase 10 internal abstract class BurnSubcommandBase
10 { 11 {
12 public abstract CommandLineHelp GetCommandLineHelp();
13
11 public abstract bool TryParseArgument(ICommandLineParser parser, string argument); 14 public abstract bool TryParseArgument(ICommandLineParser parser, string argument);
12 15
13 public abstract Task<int> ExecuteAsync(CancellationToken cancellationToken); 16 public abstract Task<int> ExecuteAsync(CancellationToken cancellationToken);
diff --git a/src/wix/WixToolset.Core.Burn/CommandLine/DetachSubcommand.cs b/src/wix/WixToolset.Core.Burn/CommandLine/DetachSubcommand.cs
index 3f958c81..e737af0b 100644
--- a/src/wix/WixToolset.Core.Burn/CommandLine/DetachSubcommand.cs
+++ b/src/wix/WixToolset.Core.Burn/CommandLine/DetachSubcommand.cs
@@ -7,6 +7,7 @@ namespace WixToolset.Core.Burn.CommandLine
7 using System.Threading; 7 using System.Threading;
8 using System.Threading.Tasks; 8 using System.Threading.Tasks;
9 using WixToolset.Core.Burn.Inscribe; 9 using WixToolset.Core.Burn.Inscribe;
10 using WixToolset.Extensibility.Data;
10 using WixToolset.Extensibility.Services; 11 using WixToolset.Extensibility.Services;
11 12
12 internal class DetachSubcommand : BurnSubcommandBase 13 internal class DetachSubcommand : BurnSubcommandBase
@@ -27,6 +28,15 @@ namespace WixToolset.Core.Burn.CommandLine
27 28
28 private string EngineOutputPath { get; set; } 29 private string EngineOutputPath { get; set; }
29 30
31 public override CommandLineHelp GetCommandLineHelp()
32 {
33 return new CommandLineHelp("Detaches the burn engine from a bundle so it can be signed.", "burn detach [options] original.exe -engine engine.exe", new[]
34 {
35 new CommandLineHelpSwitch("-intermediateFolder", "Optional working folder. If not specified %TMP% will be used."),
36 new CommandLineHelpSwitch("-engine", "Path to extract bundle's engine file to."),
37 });
38 }
39
30 public override Task<int> ExecuteAsync(CancellationToken cancellationToken) 40 public override Task<int> ExecuteAsync(CancellationToken cancellationToken)
31 { 41 {
32 if (String.IsNullOrEmpty(this.InputPath)) 42 if (String.IsNullOrEmpty(this.InputPath))
diff --git a/src/wix/WixToolset.Core.Burn/CommandLine/ExtractSubcommand.cs b/src/wix/WixToolset.Core.Burn/CommandLine/ExtractSubcommand.cs
index 5d6edc33..19aec14e 100644
--- a/src/wix/WixToolset.Core.Burn/CommandLine/ExtractSubcommand.cs
+++ b/src/wix/WixToolset.Core.Burn/CommandLine/ExtractSubcommand.cs
@@ -7,6 +7,7 @@ namespace WixToolset.Core.Burn.CommandLine
7 using System.Threading; 7 using System.Threading;
8 using System.Threading.Tasks; 8 using System.Threading.Tasks;
9 using WixToolset.Core.Burn.Bundles; 9 using WixToolset.Core.Burn.Bundles;
10 using WixToolset.Extensibility.Data;
10 using WixToolset.Extensibility.Services; 11 using WixToolset.Extensibility.Services;
11 12
12 internal class ExtractSubcommand : BurnSubcommandBase 13 internal class ExtractSubcommand : BurnSubcommandBase
@@ -27,6 +28,15 @@ namespace WixToolset.Core.Burn.CommandLine
27 28
28 private string ExtractPath { get; set; } 29 private string ExtractPath { get; set; }
29 30
31 public override CommandLineHelp GetCommandLineHelp()
32 {
33 return new CommandLineHelp("Extracts the internals of a bundle to a folder.", "burn extract [options] bundle.exe -o outputfolder ", new[]
34 {
35 new CommandLineHelpSwitch("-intermediateFolder", "Optional working folder. If not specified %TMP% will be used."),
36 new CommandLineHelpSwitch("-out", "-o", "Folder to extract the bundle contents to."),
37 });
38 }
39
30 public override Task<int> ExecuteAsync(CancellationToken cancellationToken) 40 public override Task<int> ExecuteAsync(CancellationToken cancellationToken)
31 { 41 {
32 if (String.IsNullOrEmpty(this.InputPath)) 42 if (String.IsNullOrEmpty(this.InputPath))
@@ -77,6 +87,7 @@ namespace WixToolset.Core.Burn.CommandLine
77 return true; 87 return true;
78 88
79 case "o": 89 case "o":
90 case "out":
80 this.ExtractPath = parser.GetNextArgumentAsDirectoryOrError(argument); 91 this.ExtractPath = parser.GetNextArgumentAsDirectoryOrError(argument);
81 return true; 92 return true;
82 } 93 }
diff --git a/src/wix/WixToolset.Core.Burn/CommandLine/ReattachSubcommand.cs b/src/wix/WixToolset.Core.Burn/CommandLine/ReattachSubcommand.cs
index ba034973..da6cd9b4 100644
--- a/src/wix/WixToolset.Core.Burn/CommandLine/ReattachSubcommand.cs
+++ b/src/wix/WixToolset.Core.Burn/CommandLine/ReattachSubcommand.cs
@@ -7,6 +7,7 @@ namespace WixToolset.Core.Burn.CommandLine
7 using System.Threading; 7 using System.Threading;
8 using System.Threading.Tasks; 8 using System.Threading.Tasks;
9 using WixToolset.Core.Burn.Inscribe; 9 using WixToolset.Core.Burn.Inscribe;
10 using WixToolset.Extensibility.Data;
10 using WixToolset.Extensibility.Services; 11 using WixToolset.Extensibility.Services;
11 12
12 internal class ReattachSubcommand : BurnSubcommandBase 13 internal class ReattachSubcommand : BurnSubcommandBase
@@ -29,6 +30,15 @@ namespace WixToolset.Core.Burn.CommandLine
29 30
30 private string OutputPath { get; set; } 31 private string OutputPath { get; set; }
31 32
33 public override CommandLineHelp GetCommandLineHelp()
34 {
35 return new CommandLineHelp("Reattaches a signed burn engine to a bundle.", "burn reattach [options] original.exe signed.exe -o final.exe", new[]
36 {
37 new CommandLineHelpSwitch("-intermediateFolder", "Optional working folder. If not specified %TMP% will be used."),
38 new CommandLineHelpSwitch("-out", "-o", "Output bundle with signed engine attached."),
39 });
40 }
41
32 public override Task<int> ExecuteAsync(CancellationToken cancellationToken) 42 public override Task<int> ExecuteAsync(CancellationToken cancellationToken)
33 { 43 {
34 if (String.IsNullOrEmpty(this.InputPath)) 44 if (String.IsNullOrEmpty(this.InputPath))
diff --git a/src/wix/WixToolset.Core.Burn/CommandLine/RemotePayloadSubcommand.cs b/src/wix/WixToolset.Core.Burn/CommandLine/RemotePayloadSubcommand.cs
index d152504d..ae98ccca 100644
--- a/src/wix/WixToolset.Core.Burn/CommandLine/RemotePayloadSubcommand.cs
+++ b/src/wix/WixToolset.Core.Burn/CommandLine/RemotePayloadSubcommand.cs
@@ -15,6 +15,7 @@ namespace WixToolset.Core.Burn.CommandLine
15 using WixToolset.Data; 15 using WixToolset.Data;
16 using WixToolset.Data.Symbols; 16 using WixToolset.Data.Symbols;
17 using WixToolset.Extensibility; 17 using WixToolset.Extensibility;
18 using WixToolset.Extensibility.Data;
18 using WixToolset.Extensibility.Services; 19 using WixToolset.Extensibility.Services;
19 20
20 internal class RemotePayloadSubcommand : BurnSubcommandBase 21 internal class RemotePayloadSubcommand : BurnSubcommandBase
@@ -66,6 +67,36 @@ namespace WixToolset.Core.Burn.CommandLine
66 67
67 private bool UseCertificate { get; set; } 68 private bool UseCertificate { get; set; }
68 69
70 public override CommandLineHelp GetCommandLineHelp()
71 {
72 return new CommandLineHelp("Generate source code for a remote payload", "burn remotepayload [options] payloadfile [payloadfile ...]", new[]
73 {
74 new CommandLineHelpSwitch("-basepath", "-bp", "Folder as base to make payloads relative."),
75 new CommandLineHelpSwitch("-bundlepayloadgeneration", "Sets the package payload generation option; see the Payload Generation Types below."),
76 new CommandLineHelpSwitch("-downloadurl", "-du", "Set the DownloadUrl attribute on the generated payloads."),
77 new CommandLineHelpSwitch("-out", "-o", "Path to output the source code file."),
78 new CommandLineHelpSwitch("-recurse", "-r", "Indicates to add all payloads in directory recursively."),
79 new CommandLineHelpSwitch("-intermediatefolder", "Optional working folder. If not specified %TMP% folder will be created."),
80 new CommandLineHelpSwitch("-packagetype", "Explicitly set package type; see the Pacakge Types below."),
81 new CommandLineHelpSwitch("-usecertificate", "Use certificate to validate signed payloads. This option is not recommended."),
82 })
83 {
84 Notes = String.Join(Environment.NewLine,
85 "Payload Generation Types:",
86 " none",
87 " externalwithoutdownloadurl",
88 " external",
89 " all",
90 String.Empty,
91 "Package Types:",
92 " bundle",
93 " exe",
94 " msi",
95 " msp",
96 " msu")
97 };
98 }
99
69 public override Task<int> ExecuteAsync(CancellationToken cancellationToken) 100 public override Task<int> ExecuteAsync(CancellationToken cancellationToken)
70 { 101 {
71 var inputPaths = this.ExpandInputPaths(); 102 var inputPaths = this.ExpandInputPaths();
diff --git a/src/wix/WixToolset.Core.ExtensionCache/ExtensionCacheManagerCommand.cs b/src/wix/WixToolset.Core.ExtensionCache/ExtensionCacheManagerCommand.cs
index 008fcebd..f7a93a6d 100644
--- a/src/wix/WixToolset.Core.ExtensionCache/ExtensionCacheManagerCommand.cs
+++ b/src/wix/WixToolset.Core.ExtensionCache/ExtensionCacheManagerCommand.cs
@@ -7,13 +7,14 @@ namespace WixToolset.Core.ExtensionCache
7 using System.Linq; 7 using System.Linq;
8 using System.Threading; 8 using System.Threading;
9 using System.Threading.Tasks; 9 using System.Threading.Tasks;
10 using WixToolset.Extensibility;
10 using WixToolset.Extensibility.Data; 11 using WixToolset.Extensibility.Data;
11 using WixToolset.Extensibility.Services; 12 using WixToolset.Extensibility.Services;
12 13
13 /// <summary> 14 /// <summary>
14 /// Extension cache manager command. 15 /// Extension cache manager command.
15 /// </summary> 16 /// </summary>
16 internal class ExtensionCacheManagerCommand : ICommandLineCommand 17 internal class ExtensionCacheManagerCommand : BaseCommandLineCommand
17 { 18 {
18 private enum CacheSubcommand 19 private enum CacheSubcommand
19 { 20 {
@@ -29,12 +30,6 @@ namespace WixToolset.Core.ExtensionCache
29 this.ExtensionReferences = new List<string>(); 30 this.ExtensionReferences = new List<string>();
30 } 31 }
31 32
32 public bool ShowHelp { get; set; }
33
34 public bool ShowLogo { get; set; }
35
36 public bool StopParsing { get; set; }
37
38 private IMessaging Messaging { get; } 33 private IMessaging Messaging { get; }
39 34
40 private IExtensionManager ExtensionManager { get; } 35 private IExtensionManager ExtensionManager { get; }
@@ -45,12 +40,30 @@ namespace WixToolset.Core.ExtensionCache
45 40
46 private List<string> ExtensionReferences { get; } 41 private List<string> ExtensionReferences { get; }
47 42
48 public async Task<int> ExecuteAsync(CancellationToken cancellationToken) 43 public override CommandLineHelp GetCommandLineHelp()
44 {
45 return new CommandLineHelp("Manage the extension cache.", "extension add|remove|list [options] [extensionRef]")
46 {
47 Switches = new[]
48 {
49 new CommandLineHelpSwitch("--global", "-g", "Add/remove the extension for the current user."),
50 },
51 Commands = new[]
52 {
53 new CommandLineHelpCommand("add", "Add extension to the cache."),
54 new CommandLineHelpCommand("list", "List extensions in the cache."),
55 new CommandLineHelpCommand("remove", "Remove extension from the cache."),
56 },
57 Notes = " extensionRef format: extensionId/version (the version is optional)"
58 };
59 }
60
61 public override async Task<int> ExecuteAsync(CancellationToken cancellationToken)
49 { 62 {
50 if (this.ShowHelp || !this.Subcommand.HasValue) 63 if (!this.Subcommand.HasValue)
51 { 64 {
52 DisplayHelp(); 65 Console.Error.WriteLine("A subcommand is required for the \"extension\" command. Use -h to for help.");
53 return 1; 66 return -1;
54 } 67 }
55 68
56 var success = false; 69 var success = false;
@@ -74,7 +87,7 @@ namespace WixToolset.Core.ExtensionCache
74 return success ? 0 : 2; 87 return success ? 0 : 2;
75 } 88 }
76 89
77 public bool TryParseArgument(ICommandLineParser parser, string argument) 90 public override bool TryParseArgument(ICommandLineParser parser, string argument)
78 { 91 {
79 if (!parser.IsSwitch(argument)) 92 if (!parser.IsSwitch(argument))
80 { 93 {
@@ -98,19 +111,6 @@ namespace WixToolset.Core.ExtensionCache
98 var parameter = argument.Substring(1); 111 var parameter = argument.Substring(1);
99 switch (parameter.ToLowerInvariant()) 112 switch (parameter.ToLowerInvariant())
100 { 113 {
101 case "?":
102 case "h":
103 case "-help":
104 this.ShowHelp = true;
105 this.ShowLogo = true;
106 this.StopParsing = true;
107 return true;
108
109 case "nologo":
110 case "-nologo":
111 this.ShowLogo = false;
112 return true;
113
114 case "g": 114 case "g":
115 case "-global": 115 case "-global":
116 this.Global = true; 116 this.Global = true;
@@ -161,24 +161,5 @@ namespace WixToolset.Core.ExtensionCache
161 161
162 return found; 162 return found;
163 } 163 }
164
165 private static void DisplayHelp()
166 {
167 Console.WriteLine();
168 Console.WriteLine("Usage: wix extension add|remove|list [extensionRef]");
169 Console.WriteLine();
170 Console.WriteLine("Options:");
171 Console.WriteLine(" -h|--help Show command line help.");
172 Console.WriteLine(" -g|--global Add/remove the extension for the current user.");
173 Console.WriteLine(" --nologo Suppress displaying the logo information.");
174 Console.WriteLine();
175 Console.WriteLine("Commands:");
176 Console.WriteLine();
177 Console.WriteLine(" add Add extension to the cache.");
178 Console.WriteLine(" list List extensions in the cache.");
179 Console.WriteLine(" remove Remove extension from the cache.");
180 Console.WriteLine();
181 Console.WriteLine(" extensionRef format: extensionId/version (the version is optional)");
182 }
183 } 164 }
184} 165}
diff --git a/src/wix/WixToolset.Core.ExtensionCache/ExtensionCacheManagerExtensionCommandLine.cs b/src/wix/WixToolset.Core.ExtensionCache/ExtensionCacheManagerExtensionCommandLine.cs
index 2a603adf..788b986c 100644
--- a/src/wix/WixToolset.Core.ExtensionCache/ExtensionCacheManagerExtensionCommandLine.cs
+++ b/src/wix/WixToolset.Core.ExtensionCache/ExtensionCacheManagerExtensionCommandLine.cs
@@ -3,7 +3,6 @@
3namespace WixToolset.Core.ExtensionCache 3namespace WixToolset.Core.ExtensionCache
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic;
7 using WixToolset.Extensibility; 6 using WixToolset.Extensibility;
8 using WixToolset.Extensibility.Data; 7 using WixToolset.Extensibility.Data;
9 using WixToolset.Extensibility.Services; 8 using WixToolset.Extensibility.Services;
@@ -21,10 +20,13 @@ namespace WixToolset.Core.ExtensionCache
21 20
22 private IServiceProvider ServiceProvider { get; } 21 private IServiceProvider ServiceProvider { get; }
23 22
24 public override IReadOnlyCollection<ExtensionCommandLineSwitch> CommandLineSwitches => new ExtensionCommandLineSwitch[] 23 public override CommandLineHelp GetCommandLineHelp()
25 { 24 {
26 new ExtensionCommandLineSwitch { Switch = "extension", Description = "Manage extension cache." }, 25 return new CommandLineHelp("Manage the extension cache.")
27 }; 26 {
27 Commands = new[] { new CommandLineHelpCommand("extension", "Manage extension cache.") }
28 };
29 }
28 30
29 public override bool TryParseCommand(ICommandLineParser parser, string argument, out ICommandLineCommand command) 31 public override bool TryParseCommand(ICommandLineParser parser, string argument, out ICommandLineCommand command)
30 { 32 {
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/DecompilerSubcommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/DecompilerSubcommand.cs
index 0f806c95..97da7a9e 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/DecompilerSubcommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/DecompilerSubcommand.cs
@@ -42,6 +42,22 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
42 42
43 private bool SuppressUI { get; set; } 43 private bool SuppressUI { get; set; }
44 44
45 public override CommandLineHelp GetCommandLineHelp()
46 {
47 return new CommandLineHelp("Converts a Windows Installer database back into source code.", "msi decompile [options] inputfile", new[]
48 {
49 new CommandLineHelpSwitch("-cub", "Optional path to a custom validation .CUBe file."),
50 new CommandLineHelpSwitch("-sct", "Suppress decompiling custom tables."),
51 new CommandLineHelpSwitch("-sdet", "Suppress dropping empty tables."),
52 new CommandLineHelpSwitch("-sras", "Suppress relative action sequencing."),
53 new CommandLineHelpSwitch("-sui", "Suppress decompiling UI tables."),
54 new CommandLineHelpSwitch("-type", "Optional specify the input file type: msi or msm. If not specified, type will be inferred by file extension."),
55 new CommandLineHelpSwitch("-intermediateFolder", "Optional working folder. If not specified %TMP% will be used."),
56 new CommandLineHelpSwitch("-out", "-o", "Path to output the decompiled .wxs file. If not specified, outputs next to inputfile"),
57 new CommandLineHelpSwitch("-x", "Folder to export embedded binaries and icons to."),
58 });
59 }
60
45 public override Task<int> ExecuteAsync(CancellationToken cancellationToken) 61 public override Task<int> ExecuteAsync(CancellationToken cancellationToken)
46 { 62 {
47 if (String.IsNullOrEmpty(this.InputPath)) 63 if (String.IsNullOrEmpty(this.InputPath))
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/InscribeSubcommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/InscribeSubcommand.cs
index ebc4673d..dd80b8e2 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/InscribeSubcommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/InscribeSubcommand.cs
@@ -7,6 +7,7 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
7 using System.Threading; 7 using System.Threading;
8 using System.Threading.Tasks; 8 using System.Threading.Tasks;
9 using WixToolset.Core.WindowsInstaller.Inscribe; 9 using WixToolset.Core.WindowsInstaller.Inscribe;
10 using WixToolset.Extensibility.Data;
10 using WixToolset.Extensibility.Services; 11 using WixToolset.Extensibility.Services;
11 12
12 internal class InscribeSubcommand : WindowsInstallerSubcommandBase 13 internal class InscribeSubcommand : WindowsInstallerSubcommandBase
@@ -27,6 +28,15 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
27 28
28 private string OutputPath { get; set; } 29 private string OutputPath { get; set; }
29 30
31 public override CommandLineHelp GetCommandLineHelp()
32 {
33 return new CommandLineHelp("Updates MSI database with cabinet signature information.", "msi inscribe [options] input.msi [-out inscribed.msi]", new[]
34 {
35 new CommandLineHelpSwitch("-intermediateFolder", "Optional working folder. If not specified %TMP% will be used."),
36 new CommandLineHelpSwitch("-out", "-o", "Path to output the inscribed MSI. If not provided, the input MSI is updated in place."),
37 });
38 }
39
30 public override Task<int> ExecuteAsync(CancellationToken cancellationToken) 40 public override Task<int> ExecuteAsync(CancellationToken cancellationToken)
31 { 41 {
32 if (String.IsNullOrEmpty(this.InputPath)) 42 if (String.IsNullOrEmpty(this.InputPath))
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/TransformSubcommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/TransformSubcommand.cs
index 7ed41d1a..77f29723 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/TransformSubcommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/TransformSubcommand.cs
@@ -12,6 +12,7 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
12 using WixToolset.Data.Symbols; 12 using WixToolset.Data.Symbols;
13 using WixToolset.Data.WindowsInstaller; 13 using WixToolset.Data.WindowsInstaller;
14 using WixToolset.Extensibility; 14 using WixToolset.Extensibility;
15 using WixToolset.Extensibility.Data;
15 using WixToolset.Extensibility.Services; 16 using WixToolset.Extensibility.Services;
16 17
17 internal class TransformSubcommand : WindowsInstallerSubcommandBase 18 internal class TransformSubcommand : WindowsInstallerSubcommandBase
@@ -51,6 +52,52 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
51 52
52 private TransformFlags ValidationFlags { get; set; } 53 private TransformFlags ValidationFlags { get; set; }
53 54
55 public override CommandLineHelp GetCommandLineHelp()
56 {
57 return new CommandLineHelp("Creates an MST transform file.", "msi transform [options] target.msi [updated.msi] -out output.mst")
58 {
59 Switches = new[]
60 {
61 new CommandLineHelpSwitch("-a", "Admin image, generates source file information in the transform."),
62 new CommandLineHelpSwitch("-intermediateFolder", "Optional working folder. If not specified %TMP% will be used."),
63 new CommandLineHelpSwitch("-p", "Preserve unchanged rows."),
64 new CommandLineHelpSwitch("-pedantic", "Show pedantic messages."),
65 new CommandLineHelpSwitch("-serr <flags>", "Suppress error when applying transform; see Error flags below"),
66 new CommandLineHelpSwitch("-t <type>", "Use default validation flags for the transform type; see Transform types below"),
67 new CommandLineHelpSwitch("-val <flags>", "Validation flags for the transform; see Validation flags below"),
68 new CommandLineHelpSwitch("-x", "Folder to extract binaries."),
69 new CommandLineHelpSwitch("-xo", "Output transfrom as a WiX output instead of an MST file."),
70 new CommandLineHelpSwitch("-out", "-o", "Path to output the transform file."),
71 },
72 Notes = String.Join(Environment.NewLine,
73 "Error flags:",
74 " a Ignore errors when adding an existing row",
75 " b Ignore errors when deleting a missing row",
76 " c Ignore errors when adding an existing table",
77 " d Ignore errors when deleting a missing table",
78 " e Ignore errors when modifying a missing row",
79 " f Ignore errors when changing the code page",
80 String.Empty,
81 "Validation flags:",
82 " g UpgradeCode must match",
83 " l Language must match",
84 " r Product ID must match",
85 " s Check major version only",
86 " t Check major and minor versions",
87 " u Check major, minor, and upgrade versions",
88 " v Upgrade version < target version",
89 " w Upgrade version <= target version",
90 " x Upgrade version = target version",
91 " y Upgrade version > target version",
92 " z Upgrade version >= target version",
93 String.Empty,
94 "Transform types:",
95 " language Default flags for a language transform",
96 " instance Default flags for an instance transform",
97 " patch Default flags for a patch transform")
98 };
99 }
100
54 public override Task<int> ExecuteAsync(CancellationToken cancellationToken) 101 public override Task<int> ExecuteAsync(CancellationToken cancellationToken)
55 { 102 {
56 if (String.IsNullOrEmpty(this.TargetPath)) 103 if (String.IsNullOrEmpty(this.TargetPath))
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/ValidateSubcommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/ValidateSubcommand.cs
index eb014086..f00002e3 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/ValidateSubcommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/ValidateSubcommand.cs
@@ -9,6 +9,7 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
9 using System.Threading.Tasks; 9 using System.Threading.Tasks;
10 using WixToolset.Core.WindowsInstaller.Validate; 10 using WixToolset.Core.WindowsInstaller.Validate;
11 using WixToolset.Data.WindowsInstaller; 11 using WixToolset.Data.WindowsInstaller;
12 using WixToolset.Extensibility.Data;
12 using WixToolset.Extensibility.Services; 13 using WixToolset.Extensibility.Services;
13 14
14 internal class ValidateSubcommand : WindowsInstallerSubcommandBase 15 internal class ValidateSubcommand : WindowsInstallerSubcommandBase
@@ -35,6 +36,18 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
35 36
36 private List<string> SuppressIces { get; } = new List<string>(); 37 private List<string> SuppressIces { get; } = new List<string>();
37 38
39 public override CommandLineHelp GetCommandLineHelp()
40 {
41 return new CommandLineHelp("Validates MSI database using standard or custom ICEs.", "msi validate [options] inputfile", new[]
42 {
43 new CommandLineHelpSwitch("-cub", "Optional path to a custom validation .CUBe file."),
44 new CommandLineHelpSwitch("-ice", "Validates only with the specified ICE. May be provided multiple times."),
45 new CommandLineHelpSwitch("-intermediateFolder", "Optional working folder. If not specified %TMP% will be used."),
46 new CommandLineHelpSwitch("-pdb", "Optional path to .wixpdb for source line information. If not provided, will check next to the input file."),
47 new CommandLineHelpSwitch("-sice", "Suppresses an ICE validator."),
48 });
49 }
50
38 public override Task<int> ExecuteAsync(CancellationToken cancellationToken) 51 public override Task<int> ExecuteAsync(CancellationToken cancellationToken)
39 { 52 {
40 WindowsInstallerData data = null; 53 WindowsInstallerData data = null;
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/WindowsInstallerCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/WindowsInstallerCommand.cs
index f11020c8..964aab71 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/WindowsInstallerCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/WindowsInstallerCommand.cs
@@ -5,41 +5,50 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
5 using System; 5 using System;
6 using System.Threading; 6 using System.Threading;
7 using System.Threading.Tasks; 7 using System.Threading.Tasks;
8 using WixToolset.Extensibility;
8 using WixToolset.Extensibility.Data; 9 using WixToolset.Extensibility.Data;
9 using WixToolset.Extensibility.Services; 10 using WixToolset.Extensibility.Services;
10 11
11 /// <summary> 12 /// <summary>
12 /// Windows Installer specialized command. 13 /// Windows Installer specialized command.
13 /// </summary> 14 /// </summary>
14 internal class WindowsInstallerCommand : ICommandLineCommand 15 internal class WindowsInstallerCommand : BaseCommandLineCommand
15 { 16 {
16 public WindowsInstallerCommand(IServiceProvider serviceProvider) 17 public WindowsInstallerCommand(IServiceProvider serviceProvider)
17 { 18 {
18 this.ServiceProvider = serviceProvider; 19 this.ServiceProvider = serviceProvider;
19 } 20 }
20 21
21 public bool ShowHelp { get; set; }
22
23 public bool ShowLogo { get; set; }
24
25 public bool StopParsing { get; set; }
26
27 private IServiceProvider ServiceProvider { get; } 22 private IServiceProvider ServiceProvider { get; }
28 23
29 private WindowsInstallerSubcommandBase Subcommand { get; set; } 24 private WindowsInstallerSubcommandBase Subcommand { get; set; }
30 25
31 public Task<int> ExecuteAsync(CancellationToken cancellationToken) 26 public override CommandLineHelp GetCommandLineHelp()
32 { 27 {
33 if (this.ShowHelp || this.Subcommand is null) 28 return this.Subcommand?.GetCommandLineHelp() ?? new CommandLineHelp("Specialized operations for manipulating Windows Installer databases.", "msi decompile|inscribe|transform|validate")
34 { 29 {
35 DisplayHelp(); 30 Commands = new[]
31 {
32 new CommandLineHelpCommand("decompile", "Converts a Windows Installer database back into source code."),
33 new CommandLineHelpCommand("inscribe", "Updates MSI database with cabinet signature information."),
34 new CommandLineHelpCommand("transform", "Creates an MST transform file."),
35 new CommandLineHelpCommand("validate", "Validates MSI database using standard or custom ICEs."),
36 }
37 };
38 }
39
40 public override Task<int> ExecuteAsync(CancellationToken cancellationToken)
41 {
42 if (this.Subcommand is null)
43 {
44 Console.Error.WriteLine("A subcommand is required for the \"msi\" command. Add -h to for help.");
36 return Task.FromResult(1); 45 return Task.FromResult(1);
37 } 46 }
38 47
39 return this.Subcommand.ExecuteAsync(cancellationToken); 48 return this.Subcommand.ExecuteAsync(cancellationToken);
40 } 49 }
41 50
42 public bool TryParseArgument(ICommandLineParser parser, string argument) 51 public override bool TryParseArgument(ICommandLineParser parser, string argument)
43 { 52 {
44 if (this.Subcommand is null) 53 if (this.Subcommand is null)
45 { 54 {
@@ -67,21 +76,5 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
67 76
68 return this.Subcommand.TryParseArgument(parser, argument); 77 return this.Subcommand.TryParseArgument(parser, argument);
69 } 78 }
70
71 private static void DisplayHelp()
72 {
73 Console.WriteLine();
74 Console.WriteLine("Usage: wix msi inscribe input.msi [-intermedidateFolder folder] [-out output.msi]");
75 Console.WriteLine();
76 Console.WriteLine("Options:");
77 Console.WriteLine(" -h|--help Show command line help.");
78 Console.WriteLine(" --nologo Suppress displaying the logo information.");
79 Console.WriteLine();
80 Console.WriteLine("Commands:");
81 Console.WriteLine();
82 Console.WriteLine(" inscribe Updates MSI database with cabinet signature information.");
83 Console.WriteLine(" transform Creates an MST transform file.");
84 Console.WriteLine(" validate Validates MSI database using standard or custom ICEs.");
85 }
86 } 79 }
87} 80}
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/WindowsInstallerSubcommandBase.cs b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/WindowsInstallerSubcommandBase.cs
index e3f26296..cbe389e2 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/WindowsInstallerSubcommandBase.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/WindowsInstallerSubcommandBase.cs
@@ -4,10 +4,13 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
4{ 4{
5 using System.Threading; 5 using System.Threading;
6 using System.Threading.Tasks; 6 using System.Threading.Tasks;
7 using WixToolset.Extensibility.Data;
7 using WixToolset.Extensibility.Services; 8 using WixToolset.Extensibility.Services;
8 9
9 internal abstract class WindowsInstallerSubcommandBase 10 internal abstract class WindowsInstallerSubcommandBase
10 { 11 {
12 public abstract CommandLineHelp GetCommandLineHelp();
13
11 public abstract bool TryParseArgument(ICommandLineParser parser, string argument); 14 public abstract bool TryParseArgument(ICommandLineParser parser, string argument);
12 15
13 public abstract Task<int> ExecuteAsync(CancellationToken cancellationToken); 16 public abstract Task<int> ExecuteAsync(CancellationToken cancellationToken);
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerExtensionCommandLine.cs b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerExtensionCommandLine.cs
index ae8f0a94..2919451c 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerExtensionCommandLine.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerExtensionCommandLine.cs
@@ -3,7 +3,6 @@
3namespace WixToolset.Core.WindowsInstaller 3namespace WixToolset.Core.WindowsInstaller
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic;
7 using WixToolset.Core.WindowsInstaller.CommandLine; 6 using WixToolset.Core.WindowsInstaller.CommandLine;
8 using WixToolset.Extensibility; 7 using WixToolset.Extensibility;
9 using WixToolset.Extensibility.Data; 8 using WixToolset.Extensibility.Data;
@@ -22,10 +21,16 @@ namespace WixToolset.Core.WindowsInstaller
22 21
23 private IServiceProvider ServiceProvider { get; } 22 private IServiceProvider ServiceProvider { get; }
24 23
25 public override IReadOnlyCollection<ExtensionCommandLineSwitch> CommandLineSwitches => new ExtensionCommandLineSwitch[] 24 public override CommandLineHelp GetCommandLineHelp()
26 { 25 {
27 new ExtensionCommandLineSwitch { Switch = "msi", Description = "Windows Installer specialized operations." }, 26 return new CommandLineHelp("Specialized operations for manipulating Windows Installer databases.")
28 }; 27 {
28 Commands = new[]
29 {
30 new CommandLineHelpCommand("msi", "Windows Installer specialized operations."),
31 }
32 };
33 }
29 34
30 public override bool TryParseCommand(ICommandLineParser parser, string argument, out ICommandLineCommand command) 35 public override bool TryParseCommand(ICommandLineParser parser, string argument, out ICommandLineCommand command)
31 { 36 {
diff --git a/src/wix/WixToolset.Core/CommandLine/BuildCommand.cs b/src/wix/WixToolset.Core/CommandLine/BuildCommand.cs
index a00b4b12..51101d01 100644
--- a/src/wix/WixToolset.Core/CommandLine/BuildCommand.cs
+++ b/src/wix/WixToolset.Core/CommandLine/BuildCommand.cs
@@ -14,7 +14,7 @@ namespace WixToolset.Core.CommandLine
14 using WixToolset.Extensibility.Data; 14 using WixToolset.Extensibility.Data;
15 using WixToolset.Extensibility.Services; 15 using WixToolset.Extensibility.Services;
16 16
17 internal class BuildCommand : ICommandLineCommand 17 internal class BuildCommand : BaseCommandLineCommand
18 { 18 {
19 private readonly CommandLine commandLine; 19 private readonly CommandLine commandLine;
20 20
@@ -26,21 +26,6 @@ namespace WixToolset.Core.CommandLine
26 this.commandLine = new CommandLine(this.ServiceProvider, this.Messaging); 26 this.commandLine = new CommandLine(this.ServiceProvider, this.Messaging);
27 } 27 }
28 28
29 public bool ShowHelp
30 {
31 get { return this.commandLine.ShowHelp; }
32 set { this.commandLine.ShowHelp = value; }
33 }
34
35 public bool ShowLogo
36 {
37 get { return this.commandLine.ShowLogo; }
38 set { this.commandLine.ShowLogo = value; }
39 }
40
41 // Stop parsing when we've decided to show help.
42 public bool StopParsing => this.commandLine.ShowHelp;
43
44 private IServiceProvider ServiceProvider { get; } 29 private IServiceProvider ServiceProvider { get; }
45 30
46 private IMessaging Messaging { get; } 31 private IMessaging Messaging { get; }
@@ -57,14 +42,40 @@ namespace WixToolset.Core.CommandLine
57 42
58 private string TrackingFile { get; set; } 43 private string TrackingFile { get; set; }
59 44
60 public Task<int> ExecuteAsync(CancellationToken cancellationToken) 45 public override CommandLineHelp GetCommandLineHelp()
61 { 46 {
62 if (this.commandLine.ShowHelp) 47 return new CommandLineHelp("", "build [options] sourcevile [sourcefile ...] -out output.ext", new[]
63 { 48 {
64 Console.WriteLine("TODO: Show build command help"); 49 new CommandLineHelpSwitch("-arch", "Set the architecture of the output."),
65 return Task.FromResult(-1); 50 new CommandLineHelpSwitch("-bindfiles", "-bf", "Bind files into an output .wixlib. Ignored if not building a .wixlib."),
66 } 51 new CommandLineHelpSwitch("-bindpath", "Bind path to search for content files."),
52 new CommandLineHelpSwitch("-cabcache", "-cc", "Set a folder to cache cabinets across builds."),
53 new CommandLineHelpSwitch("-culture", "Adds a culture to filter localization files."),
54 new CommandLineHelpSwitch("-define", "-d", "Sets a preprocessor variable."),
55 new CommandLineHelpSwitch("-defaultcompressionlevel", "-dcl", "Default compression level; see Compression levels below."),
56 new CommandLineHelpSwitch("-include", "-i", "Folder to search for include files."),
57 new CommandLineHelpSwitch("-intermediatefolder", "Optional working folder. If not specified a folder in %TMP% will be created."),
58 new CommandLineHelpSwitch("-loc", "Localization file to use in the build. By default, .wxl files are recognized as localization."),
59 new CommandLineHelpSwitch("-lib", "Library file to use in the build. By default, .wixlb files are recognized as libraries."),
60 new CommandLineHelpSwitch("-src", "Source file to use in the build. By default, .wxs files are recognized as source code."),
61 new CommandLineHelpSwitch("-out", "-o", "Path to output the build to."),
62 new CommandLineHelpSwitch("-outputtype", "Explicitly set the output type if it cannot be determined from the output."),
63 new CommandLineHelpSwitch("-pdb", "Optional path to output .wixpdb. Default will write .wixpdb beside output path."),
64 new CommandLineHelpSwitch("-pdbtype", "Switch to disable creation of .wixpdb. Types: full or none."),
65 })
66 {
67 Notes = String.Join(Environment.NewLine,
68 "Compression levels:",
69 " none Use no compression",
70 " low Use low compression",
71 " medium Use medium compression",
72 " high Use high compression",
73 " mszip Use ms-zip compression")
74 };
75 }
67 76
77 public override Task<int> ExecuteAsync(CancellationToken cancellationToken)
78 {
68 this.IntermediateFolder = this.commandLine.CalculateIntermedateFolder(); 79 this.IntermediateFolder = this.commandLine.CalculateIntermedateFolder();
69 80
70 this.Platform = this.commandLine.Platform; 81 this.Platform = this.commandLine.Platform;
@@ -144,7 +155,7 @@ namespace WixToolset.Core.CommandLine
144 return Task.FromResult(this.Messaging.LastErrorNumber); 155 return Task.FromResult(this.Messaging.LastErrorNumber);
145 } 156 }
146 157
147 public bool TryParseArgument(ICommandLineParser parser, string argument) 158 public override bool TryParseArgument(ICommandLineParser parser, string argument)
148 { 159 {
149 return this.commandLine.TryParseArgument(argument, parser); 160 return this.commandLine.TryParseArgument(argument, parser);
150 } 161 }
@@ -465,7 +476,7 @@ namespace WixToolset.Core.CommandLine
465 476
466 public List<string> SourceFilePaths { get; } = new List<string>(); 477 public List<string> SourceFilePaths { get; } = new List<string>();
467 478
468 public List<string> UnevaluatedInputFilePaths { get; } = new List<string>(); 479 public List<string> UnclassifiedInputFilePaths { get; } = new List<string>();
469 480
470 public Platform Platform { get; private set; } 481 public Platform Platform { get; private set; }
471 482
@@ -473,10 +484,6 @@ namespace WixToolset.Core.CommandLine
473 484
474 public PdbType PdbType { get; private set; } 485 public PdbType PdbType { get; private set; }
475 486
476 public bool ShowLogo { get; set; }
477
478 public bool ShowHelp { get; set; }
479
480 public string IntermediateFolder { get; private set; } 487 public string IntermediateFolder { get; private set; }
481 488
482 public string OutputFile { get; private set; } 489 public string OutputFile { get; private set; }
@@ -535,6 +542,7 @@ namespace WixToolset.Core.CommandLine
535 } 542 }
536 543
537 case "cc": 544 case "cc":
545 case "cabcache":
538 this.CabCachePath = parser.GetNextArgumentOrError(arg); 546 this.CabCachePath = parser.GetNextArgumentOrError(arg);
539 return true; 547 return true;
540 548
@@ -617,7 +625,7 @@ namespace WixToolset.Core.CommandLine
617 } 625 }
618 else 626 else
619 { 627 {
620 parser.GetArgumentAsFilePathOrError(arg, "input file", this.UnevaluatedInputFilePaths); 628 parser.GetArgumentAsFilePathOrError(arg, "input file", this.UnclassifiedInputFilePaths);
621 return true; 629 return true;
622 } 630 }
623 } 631 }
@@ -737,7 +745,7 @@ namespace WixToolset.Core.CommandLine
737 var outputPath = this.OutputFile; 745 var outputPath = this.OutputFile;
738 var outputType = this.CalculateOutputType(); 746 var outputType = this.CalculateOutputType();
739 747
740 foreach (var path in this.UnevaluatedInputFilePaths) 748 foreach (var path in this.UnclassifiedInputFilePaths)
741 { 749 {
742 var extension = Path.GetExtension(path); 750 var extension = Path.GetExtension(path);
743 751
diff --git a/src/wix/WixToolset.Core/CommandLine/CommandLine.cs b/src/wix/WixToolset.Core/CommandLine/CommandLine.cs
index 07dcc2d2..bbe985f8 100644
--- a/src/wix/WixToolset.Core/CommandLine/CommandLine.cs
+++ b/src/wix/WixToolset.Core/CommandLine/CommandLine.cs
@@ -21,6 +21,10 @@ namespace WixToolset.Core.CommandLine
21 21
22 private IMessaging Messaging { get; } 22 private IMessaging Messaging { get; }
23 23
24 private bool ShowHelp { get; set; }
25
26 private bool SuppressLogo { get; set; }
27
24 public ICommandLineCommand CreateCommand(string[] args) 28 public ICommandLineCommand CreateCommand(string[] args)
25 { 29 {
26 var arguments = this.ServiceProvider.GetService<ICommandLineArguments>(); 30 var arguments = this.ServiceProvider.GetService<ICommandLineArguments>();
@@ -49,11 +53,12 @@ namespace WixToolset.Core.CommandLine
49 53
50 var command = this.Parse(context); 54 var command = this.Parse(context);
51 55
52 if (command.ShowLogo) 56 if (!this.SuppressLogo && command?.ShowLogo == true)
53 { 57 {
54 var branding = this.ServiceProvider.GetService<IWixBranding>(); 58 var branding = this.ServiceProvider.GetService<IWixBranding>();
55 Console.WriteLine(branding.ReplacePlaceholders("[AssemblyProduct] [AssemblyDescription] version [ProductVersion]")); 59 Console.WriteLine(branding.ReplacePlaceholders("[AssemblyProduct] [AssemblyDescription] version [ProductVersion]"));
56 Console.WriteLine(branding.ReplacePlaceholders("[AssemblyCopyright]")); 60 Console.WriteLine(branding.ReplacePlaceholders("[AssemblyCopyright]"));
61 Console.WriteLine();
57 } 62 }
58 63
59 return command; 64 return command;
@@ -71,7 +76,6 @@ namespace WixToolset.Core.CommandLine
71 76
72 private ICommandLineCommand Parse(ICommandLineContext context) 77 private ICommandLineCommand Parse(ICommandLineContext context)
73 { 78 {
74 var branding = context.ServiceProvider.GetService<IWixBranding>();
75 var extensions = context.ExtensionManager.GetServices<IExtensionCommandLine>(); 79 var extensions = context.ExtensionManager.GetServices<IExtensionCommandLine>();
76 80
77 foreach (var extension in extensions) 81 foreach (var extension in extensions)
@@ -86,28 +90,31 @@ namespace WixToolset.Core.CommandLine
86 String.IsNullOrEmpty(parser.ErrorArgument) && 90 String.IsNullOrEmpty(parser.ErrorArgument) &&
87 parser.TryGetNextSwitchOrArgument(out var arg)) 91 parser.TryGetNextSwitchOrArgument(out var arg))
88 { 92 {
89 if (String.IsNullOrWhiteSpace(arg)) // skip blank arguments. 93 // If we don't have a command yet, try to parse for a command or a global switch.
90 {
91 continue;
92 }
93
94 // First argument must be the command or global switch (that creates a command).
95 if (command == null) 94 if (command == null)
96 { 95 {
97 if (!this.TryParseCommand(arg, parser, extensions, out command)) 96 if (this.TryParseCommand(arg, parser, extensions, out command))
98 { 97 {
98 // Found our command, all good.
99 }
100 else if (!parser.IsSwitch(arg) || !TryParseCommandLineArgumentWithExtension(arg, parser, extensions))
101 {
102 // Not a global switch handled by an extension, so failure.
99 parser.ReportErrorArgument(arg); 103 parser.ReportErrorArgument(arg);
100 } 104 }
101 } 105 }
102 else if (parser.IsSwitch(arg)) 106 else if (parser.IsSwitch(arg))
103 { 107 {
104 if (!command.TryParseArgument(parser, arg) && !TryParseCommandLineArgumentWithExtension(arg, parser, extensions) && 108 // Commands get first crack at parsing switches then extensions then the standard.
105 !this.TryParseStandardCommandLineSwitch(command, parser, arg)) 109 if (!command.TryParseArgument(parser, arg) &&
110 !TryParseCommandLineArgumentWithExtension(arg, parser, extensions) &&
111 !this.TryParseStandardCommandLineSwitch(parser, arg))
106 { 112 {
107 parser.ReportErrorArgument(arg); 113 parser.ReportErrorArgument(arg);
108 } 114 }
109 } 115 }
110 else if (!TryParseCommandLineArgumentWithExtension(arg, parser, extensions) && !command.TryParseArgument(parser, arg)) 116 else if (!TryParseCommandLineArgumentWithExtension(arg, parser, extensions) &&
117 !command.TryParseArgument(parser, arg))
111 { 118 {
112 parser.ReportErrorArgument(arg); 119 parser.ReportErrorArgument(arg);
113 } 120 }
@@ -121,10 +128,15 @@ namespace WixToolset.Core.CommandLine
121 // If we hit an error, do not return a command. 128 // If we hit an error, do not return a command.
122 if (!String.IsNullOrEmpty(parser.ErrorArgument)) 129 if (!String.IsNullOrEmpty(parser.ErrorArgument))
123 { 130 {
124 return null; 131 command = null;
132 }
133 else if (this.ShowHelp || command == null)
134 {
135 var branding = context.ServiceProvider.GetService<IWixBranding>();
136 command = new HelpCommand(extensions, branding, command);
125 } 137 }
126 138
127 return command ?? new HelpCommand(extensions, branding); 139 return command;
128 } 140 }
129 141
130 private bool TryParseCommand(string arg, ICommandLineParser parser, IEnumerable<IExtensionCommandLine> extensions, out ICommandLineCommand command) 142 private bool TryParseCommand(string arg, ICommandLineParser parser, IEnumerable<IExtensionCommandLine> extensions, out ICommandLineCommand command)
@@ -141,7 +153,7 @@ namespace WixToolset.Core.CommandLine
141 case "help": 153 case "help":
142 case "-help": 154 case "-help":
143 var branding = this.ServiceProvider.GetService<IWixBranding>(); 155 var branding = this.ServiceProvider.GetService<IWixBranding>();
144 command = new HelpCommand(extensions, branding); 156 command = new HelpCommand(extensions, branding, null);
145 break; 157 break;
146 158
147 case "version": 159 case "version":
@@ -184,7 +196,7 @@ namespace WixToolset.Core.CommandLine
184 return false; 196 return false;
185 } 197 }
186 198
187 private bool TryParseStandardCommandLineSwitch(ICommandLineCommand command, ICommandLineParser parser, string arg) 199 private bool TryParseStandardCommandLineSwitch(ICommandLineParser parser, string arg)
188 { 200 {
189 var parameter = arg.Substring(1).ToLowerInvariant(); 201 var parameter = arg.Substring(1).ToLowerInvariant();
190 202
@@ -193,11 +205,13 @@ namespace WixToolset.Core.CommandLine
193 case "?": 205 case "?":
194 case "h": 206 case "h":
195 case "help": 207 case "help":
196 command.ShowHelp = true; 208 case "-help":
209 this.ShowHelp = true;
197 return true; 210 return true;
198 211
199 case "nologo": 212 case "nologo":
200 command.ShowLogo = false; 213 case "-nologo":
214 this.SuppressLogo = true;
201 return true; 215 return true;
202 216
203 case "v": 217 case "v":
diff --git a/src/wix/WixToolset.Core/CommandLine/CommandLineParser.cs b/src/wix/WixToolset.Core/CommandLine/CommandLineParser.cs
index 7e6e7cac..52350929 100644
--- a/src/wix/WixToolset.Core/CommandLine/CommandLineParser.cs
+++ b/src/wix/WixToolset.Core/CommandLine/CommandLineParser.cs
@@ -5,6 +5,7 @@ namespace WixToolset.Core.CommandLine
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.IO; 7 using System.IO;
8 using System.Linq;
8 using WixToolset.Data; 9 using WixToolset.Data;
9 using WixToolset.Extensibility.Services; 10 using WixToolset.Extensibility.Services;
10 11
@@ -12,19 +13,19 @@ namespace WixToolset.Core.CommandLine
12 { 13 {
13 private const string ExpectedArgument = "expected argument"; 14 private const string ExpectedArgument = "expected argument";
14 15
15 public string ErrorArgument { get; private set; }
16
17 private Queue<string> RemainingArguments { get; }
18
19 private IMessaging Messaging { get; }
20
21 public CommandLineParser(IMessaging messaging, string[] arguments, string errorArgument) 16 public CommandLineParser(IMessaging messaging, string[] arguments, string errorArgument)
22 { 17 {
23 this.Messaging = messaging; 18 this.Messaging = messaging;
24 this.RemainingArguments = new Queue<string>(arguments); 19 this.RemainingArguments = new Queue<string>(arguments.Where(a => !String.IsNullOrWhiteSpace(a))); // skip blank arguments.
25 this.ErrorArgument = errorArgument; 20 this.ErrorArgument = errorArgument;
26 } 21 }
27 22
23 public string ErrorArgument { get; private set; }
24
25 private Queue<string> RemainingArguments { get; }
26
27 private IMessaging Messaging { get; }
28
28 public bool IsSwitch(string arg) 29 public bool IsSwitch(string arg)
29 { 30 {
30 return !String.IsNullOrEmpty(arg) && '-' == arg[0]; 31 return !String.IsNullOrEmpty(arg) && '-' == arg[0];
@@ -132,15 +133,15 @@ namespace WixToolset.Core.CommandLine
132 this.ErrorArgument = argument; 133 this.ErrorArgument = argument;
133 } 134 }
134 135
135 public bool TryGetNextSwitchOrArgument(out string arg) 136 public bool TryGetNextSwitchOrArgument(out string argument)
136 { 137 {
137 if (this.RemainingArguments.Count > 0) 138 if (this.RemainingArguments.Count > 0)
138 { 139 {
139 arg = this.RemainingArguments.Dequeue(); 140 argument = this.RemainingArguments.Dequeue();
140 return true; 141 return true;
141 } 142 }
142 143
143 arg = null; 144 argument = null;
144 return false; 145 return false;
145 } 146 }
146 147
diff --git a/src/wix/WixToolset.Core/CommandLine/HelpCommand.cs b/src/wix/WixToolset.Core/CommandLine/HelpCommand.cs
index 9a879cc8..f6d3d776 100644
--- a/src/wix/WixToolset.Core/CommandLine/HelpCommand.cs
+++ b/src/wix/WixToolset.Core/CommandLine/HelpCommand.cs
@@ -11,67 +11,115 @@ namespace WixToolset.Core.CommandLine
11 using WixToolset.Extensibility.Data; 11 using WixToolset.Extensibility.Data;
12 using WixToolset.Extensibility.Services; 12 using WixToolset.Extensibility.Services;
13 13
14 internal class HelpCommand : ICommandLineCommand 14 internal class HelpCommand : BaseCommandLineCommand
15 { 15 {
16 private static readonly ExtensionCommandLineSwitch[] BuiltInSwitches = new ExtensionCommandLineSwitch[] 16 public HelpCommand(IEnumerable<IExtensionCommandLine> extensions, IWixBranding branding, ICommandLineCommand command)
17 {
18 new ExtensionCommandLineSwitch { Switch = "build", Description = "Build a wixlib, package or bundle." },
19 new ExtensionCommandLineSwitch { Switch = "decompile", Description = "Decompile a package or bundle into source code." },
20 };
21
22 public HelpCommand(IEnumerable<IExtensionCommandLine> extensions, IWixBranding branding)
23 { 17 {
24 this.Extensions = extensions; 18 this.Extensions = extensions;
25 this.Branding = branding; 19 this.Branding = branding;
20 this.Command = command;
26 } 21 }
27 22
28 public bool ShowHelp 23 public override bool ShowLogo => true;
24
25 private IEnumerable<IExtensionCommandLine> Extensions { get; }
26
27 private IWixBranding Branding { get; }
28
29 private ICommandLineCommand Command { get; }
30
31 public override CommandLineHelp GetCommandLineHelp()
29 { 32 {
30 get => true; 33 return new CommandLineHelp(null, "[command] [options]")
31 set { } 34 {
35 Switches = new[]
36 {
37 new CommandLineHelpSwitch("--help", "-h", "Show command line help."),
38 new CommandLineHelpSwitch("--version", "-v", "Display WiX Toolset version in use."),
39 new CommandLineHelpSwitch("--nologo", "Suppress displaying the logo information."),
40 },
41 Commands = new[]
42 {
43 new CommandLineHelpCommand("build", "Build a wixlib, package or bundle.")
44 },
45 Notes = "Run 'wix [command] -h' for more information on a command."
46 };
32 } 47 }
33 48
34 public bool ShowLogo 49 public override Task<int> ExecuteAsync(CancellationToken _)
35 { 50 {
36 get => true; 51 var extensionsHelp = this.Extensions.Select(e => e.GetCommandLineHelp()).Where(h => h != null).ToList();
37 set { }
38 }
39 52
40 public bool StopParsing => true; 53 var help = this.Command?.GetCommandLineHelp() ?? this.GetCommandLineHelp();
41 54
42 private IEnumerable<IExtensionCommandLine> Extensions { get; } 55 var switches = new List<CommandLineHelpSwitch>();
56 if (help.Switches != null)
57 {
58 switches.AddRange(help.Switches);
59 }
43 60
44 private IWixBranding Branding { get; } 61 switches.AddRange(extensionsHelp.Where(e => e.Switches != null).SelectMany(e => e.Switches));
45 62
46 public Task<int> ExecuteAsync(CancellationToken _) 63 var commands = new List<CommandLineHelpCommand>();
47 { 64 if (help.Commands != null)
48 var commandLineSwitches = new List<ExtensionCommandLineSwitch>(BuiltInSwitches); 65 {
49 commandLineSwitches.AddRange(this.Extensions.SelectMany(e => e.CommandLineSwitches).OrderBy(s => s.Switch, StringComparer.Ordinal)); 66 commands.AddRange(help.Commands);
50 67 }
51 Console.WriteLine(); 68
52 Console.WriteLine("Usage: wix [option]"); 69 if (this.Command == null)
53 Console.WriteLine("Usage: wix [command]"); 70 {
54 Console.WriteLine(); 71 commands.AddRange(extensionsHelp.Where(e => e.Commands != null).SelectMany(e => e.Commands));
55 Console.WriteLine("Options:"); 72 }
56 Console.WriteLine(" -h|--help Show command line help."); 73
57 Console.WriteLine(" --version Display WiX Toolset version in use."); 74 if (!String.IsNullOrEmpty(help.Description))
58 Console.WriteLine(); 75 {
59 76 Console.WriteLine("Description:");
60 Console.WriteLine("Commands:"); 77 Console.WriteLine(" {0}", help.Description);
61 foreach (var commandLineSwitch in commandLineSwitches) 78 Console.WriteLine();
79 }
80
81 if (!String.IsNullOrEmpty(help.Usage))
82 {
83 Console.WriteLine("Usage:");
84 Console.WriteLine(" wix {0}", help.Usage);
85 Console.WriteLine();
86 }
87
88 if (switches.Count > 0)
89 {
90 Console.WriteLine("Options:");
91 foreach (var commandLineSwitch in help.Switches)
92 {
93 var switchName = String.IsNullOrEmpty(commandLineSwitch.ShortName) ? commandLineSwitch.Name : $"{commandLineSwitch.ShortName}|{commandLineSwitch.Name}";
94 Console.WriteLine(" {0,-17} {1}", switchName, commandLineSwitch.Description);
95 }
96
97 Console.WriteLine();
98 }
99
100 if (commands.Count > 0)
101 {
102 Console.WriteLine("Commands:");
103 foreach (var command in commands)
104 {
105 Console.WriteLine(" {0,-17} {1}", command.Name, command.Description);
106 }
107
108 Console.WriteLine();
109 }
110
111 if (!String.IsNullOrEmpty(help.Notes))
62 { 112 {
63 Console.WriteLine(" {0,-17} {1}", commandLineSwitch.Switch, commandLineSwitch.Description); 113 Console.WriteLine(help.Notes);
114 Console.WriteLine();
64 } 115 }
65 116
66 Console.WriteLine();
67 Console.WriteLine("Run 'wix [command] -h[elp]' for more information on a command.");
68 Console.WriteLine();
69 Console.WriteLine(this.Branding.ReplacePlaceholders("For more information see: [SupportUrl]")); 117 Console.WriteLine(this.Branding.ReplacePlaceholders("For more information see: [SupportUrl]"));
70 118
71 return Task.FromResult(-1); 119 return Task.FromResult(-1);
72 } 120 }
73 121
74 public bool TryParseArgument(ICommandLineParser parseHelper, string argument) 122 public override bool TryParseArgument(ICommandLineParser parseHelper, string argument)
75 { 123 {
76 return true; // eat any arguments 124 return true; // eat any arguments
77 } 125 }
diff --git a/src/wix/WixToolset.Core/CommandLine/VersionCommand.cs b/src/wix/WixToolset.Core/CommandLine/VersionCommand.cs
index 7e08b66e..2fb13967 100644
--- a/src/wix/WixToolset.Core/CommandLine/VersionCommand.cs
+++ b/src/wix/WixToolset.Core/CommandLine/VersionCommand.cs
@@ -5,18 +5,18 @@ namespace WixToolset.Core.CommandLine
5 using System; 5 using System;
6 using System.Threading; 6 using System.Threading;
7 using System.Threading.Tasks; 7 using System.Threading.Tasks;
8 using WixToolset.Extensibility;
8 using WixToolset.Extensibility.Data; 9 using WixToolset.Extensibility.Data;
9 using WixToolset.Extensibility.Services; 10 using WixToolset.Extensibility.Services;
10 11
11 internal class VersionCommand : ICommandLineCommand 12 internal class VersionCommand : BaseCommandLineCommand
12 { 13 {
13 public bool ShowHelp { get; set; } 14 public override CommandLineHelp GetCommandLineHelp()
14 15 {
15 public bool ShowLogo { get; set; } 16 return null;
16 17 }
17 public bool StopParsing => true;
18 18
19 public Task<int> ExecuteAsync(CancellationToken cancellationToken) 19 public override Task<int> ExecuteAsync(CancellationToken cancellationToken)
20 { 20 {
21 // $(GitBaseVersionMajor).$(GitBaseVersionMinor).$(GitBaseVersionPatch)$(GitSemVerDashLabel)+$(Commit) 21 // $(GitBaseVersionMajor).$(GitBaseVersionMinor).$(GitBaseVersionPatch)$(GitSemVerDashLabel)+$(Commit)
22 Console.WriteLine("{0}.{1}.{2}{3}+{4}", ThisAssembly.Git.BaseVersion.Major 22 Console.WriteLine("{0}.{1}.{2}{3}+{4}", ThisAssembly.Git.BaseVersion.Major
@@ -27,7 +27,7 @@ namespace WixToolset.Core.CommandLine
27 return Task.FromResult(0); 27 return Task.FromResult(0);
28 } 28 }
29 29
30 public bool TryParseArgument(ICommandLineParser parseHelper, string argument) 30 public override bool TryParseArgument(ICommandLineParser parseHelper, string argument)
31 { 31 {
32 return true; // eat any arguments 32 return true; // eat any arguments
33 } 33 }
diff --git a/src/wix/test/Example.Extension/ExamplePreprocessorExtensionAndCommandLine.cs b/src/wix/test/Example.Extension/ExamplePreprocessorExtensionAndCommandLine.cs
index 7244798a..37834826 100644
--- a/src/wix/test/Example.Extension/ExamplePreprocessorExtensionAndCommandLine.cs
+++ b/src/wix/test/Example.Extension/ExamplePreprocessorExtensionAndCommandLine.cs
@@ -3,7 +3,6 @@
3namespace Example.Extension 3namespace Example.Extension
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic;
7 using WixToolset.Extensibility; 6 using WixToolset.Extensibility;
8 using WixToolset.Extensibility.Data; 7 using WixToolset.Extensibility.Data;
9 using WixToolset.Extensibility.Services; 8 using WixToolset.Extensibility.Services;
@@ -12,7 +11,13 @@ namespace Example.Extension
12 { 11 {
13 private string exampleValueFromCommandLine; 12 private string exampleValueFromCommandLine;
14 13
15 public IReadOnlyCollection<ExtensionCommandLineSwitch> CommandLineSwitches => throw new NotImplementedException(); 14 public CommandLineHelp GetCommandLineHelp()
15 {
16 return new CommandLineHelp("Example command line extension", "example value", new[]
17 {
18 new CommandLineHelpSwitch("-example", "Sets an example value")
19 });
20 }
16 21
17 public ExamplePreprocessorExtensionAndCommandLine() 22 public ExamplePreprocessorExtensionAndCommandLine()
18 { 23 {
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/CommandLineFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/CommandLineFixture.cs
index 1b0f9633..c1c98252 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/CommandLineFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/CommandLineFixture.cs
@@ -22,10 +22,9 @@ namespace WixToolsetTest.CoreIntegration
22 WixAssert.CompareLineByLine(new[] 22 WixAssert.CompareLineByLine(new[]
23 { 23 {
24 "-bindpath is expected to be followed by a value. See -? for additional detail.", 24 "-bindpath is expected to be followed by a value. See -? for additional detail.",
25 "Additional argument '-bindpath' was unexpected. Remove the argument and add the '-?' switch for more information.", 25 "Additional argument '-bindpath' was unexpected. Remove the argument and add the '-?' switch for more information."
26 "No source files specified."
27 }, result.Messages.Select(m => m.ToString()).ToArray()); 26 }, result.Messages.Select(m => m.ToString()).ToArray());
28 Assert.Equal(391, result.ExitCode); 27 Assert.Equal(1, result.ExitCode);
29 } 28 }
30 29
31 [Fact] 30 [Fact]