Skip to content

Commit

Permalink
Add --custom argument for passing additional installer arguments (#2832)
Browse files Browse the repository at this point in the history
  • Loading branch information
Trenly authored Jan 12, 2023
1 parent 25857a8 commit bf9914a
Show file tree
Hide file tree
Showing 13 changed files with 94 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/AppInstallerCLICore/Argument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ namespace AppInstaller::CLI
return Argument{ "architecture"_liv, 'a', Args::Type::InstallArchitecture, Resource::String::InstallArchitectureArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help };
case Args::Type::Log:
return Argument{ "log"_liv, 'o', Args::Type::Log, Resource::String::LogArgumentDescription, ArgumentType::Standard };
case Args::Type::CustomSwitches:
return Argument{ "custom"_liv, NoAlias, Args::Type::CustomSwitches, Resource::String::CustomSwitchesArgumentDescription, ArgumentType::Standard};
case Args::Type::Override:
return Argument{ "override"_liv, NoAlias, Args::Type::Override, Resource::String::OverrideArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help };
case Args::Type::InstallLocation:
Expand Down
1 change: 1 addition & 0 deletions src/AppInstallerCLICore/Commands/InstallCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ namespace AppInstaller::CLI
Argument::ForType(Args::Type::Silent),
Argument::ForType(Args::Type::Locale),
Argument::ForType(Args::Type::Log),
Argument::ForType(Args::Type::CustomSwitches),
Argument::ForType(Args::Type::Override),
Argument::ForType(Args::Type::InstallLocation),
Argument::ForType(Args::Type::HashOverride),
Expand Down
1 change: 1 addition & 0 deletions src/AppInstallerCLICore/Commands/UpgradeCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ namespace AppInstaller::CLI
Argument::ForType(Args::Type::Silent), // -h
Argument::ForType(Args::Type::Purge),
Argument::ForType(Args::Type::Log), // -o
Argument::ForType(Args::Type::CustomSwitches),
Argument::ForType(Args::Type::Override),
Argument::ForType(Args::Type::InstallLocation), // -l
Argument{ s_ArgumentName_Scope, Argument::NoAlias, Execution::Args::Type::InstallScope, Resource::String::InstalledScopeArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help },
Expand Down
1 change: 1 addition & 0 deletions src/AppInstallerCLICore/ExecutionArgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ namespace AppInstaller::CLI::Execution
Silent,
Locale,
Log,
CustomSwitches, // CustomSwitches args are args passed to the installer in addition to any defined in the manifest
Override, // Override args are (and the only args) directly passed to installer
InstallLocation,
InstallScope,
Expand Down
1 change: 1 addition & 0 deletions src/AppInstallerCLICore/Resources.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ namespace AppInstaller::CLI::Resource
WINGET_DEFINE_RESOURCE_STRINGID(ConvertInstallFlowToUpgrade);
WINGET_DEFINE_RESOURCE_STRINGID(CountArgumentDescription);
WINGET_DEFINE_RESOURCE_STRINGID(CountOutOfBoundsError);
WINGET_DEFINE_RESOURCE_STRINGID(CustomSwitchesArgumentDescription);
WINGET_DEFINE_RESOURCE_STRINGID(DependenciesFlowInstall);
WINGET_DEFINE_RESOURCE_STRINGID(DependenciesFlowSourceNotFound);
WINGET_DEFINE_RESOURCE_STRINGID(DependenciesFlowSourceTooManyMatches);
Expand Down
11 changes: 11 additions & 0 deletions src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,17 @@ namespace AppInstaller::CLI::Workflow
installerArgs += ' ' + installerSwitches.at(InstallerSwitchType::Custom);
}

// Construct custom arg passed in by cli arg
if (context.Args.Contains(Execution::Args::Type::CustomSwitches))
{
std::string_view customSwitches = context.Args.GetArg(Execution::Args::Type::CustomSwitches);
// Since these arguments are appended to the installer at runtime, it doesn't make sense to append them if empty or whitespace
if (!Utility::IsEmptyOrWhitespace(customSwitches))
{
installerArgs += ' ' + std::string{ customSwitches };
}
}

// Construct update arg if applicable
if (isUpdate && installerSwitches.find(InstallerSwitchType::Update) != installerSwitches.end())
{
Expand Down
3 changes: 3 additions & 0 deletions src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw
Original file line number Diff line number Diff line change
Expand Up @@ -1322,6 +1322,9 @@ Please specify one of them using the --source option to proceed.</value>
<data name="CountOutOfBoundsError" xml:space="preserve">
<value>The requested number of results must be between 1 and 1000.</value>
</data>
<data name="CustomSwitchesArgumentDescription" xml:space="preserve">
<value>Arguments to be passed on to the installer in addition to the defaults</value>
</data>
<data name="UpgradeDifferentInstallTechnologyInNewerVersions" xml:space="preserve">
<value>A newer version was found, but the install technology is different from the current version installed. Please uninstall the package and install the newer version.</value>
</data>
Expand Down
37 changes: 37 additions & 0 deletions src/AppInstallerCLITests/InstallFlow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,43 @@ TEST_CASE("ShellExecuteHandlerInstallerArgs", "[InstallFlow][workflow]")
REQUIRE(installerArgs.find("/myinstalldir=\"MyDir\"") != std::string::npos); // Use declaration in manifest
}

{
std::ostringstream installOutput;
TestContext context{ installOutput, std::cin };
auto previousThreadGlobals = context.SetForCurrentThread();
// Inno type with /silent and /log and /custom and /installlocation, switches specified in manifest and --custom argument used in cli
auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallerArgTest_Inno_WithSwitches.yaml"));
context.Args.AddArg(Execution::Args::Type::Silent);
context.Args.AddArg(Execution::Args::Type::Log, "MyLog.log"sv);
context.Args.AddArg(Execution::Args::Type::InstallLocation, "MyDir"sv);
context.Args.AddArg(Execution::Args::Type::CustomSwitches, "/MyAppendedSwitch"sv);
context.Add<Data::Manifest>(manifest);
context.Add<Data::Installer>(manifest.Installers.at(0));
context << GetInstallerArgs;
std::string installerArgs = context.Get<Data::InstallerArgs>();
REQUIRE(installerArgs.find("/mysilent") != std::string::npos); // Use declaration in manifest
REQUIRE(installerArgs.find("/mylog=\"MyLog.log\"") != std::string::npos); // Use declaration in manifest
REQUIRE(installerArgs.find("/mycustom") != std::string::npos); // Use declaration in manifest
REQUIRE(installerArgs.find("/myinstalldir=\"MyDir\"") != std::string::npos); // Use declaration in manifest
REQUIRE(installerArgs.find("/MyAppendedSwitch") != std::string::npos); // Use declaration from argument
}

{
std::ostringstream installOutput;
TestContext context{ installOutput, std::cin };
auto previousThreadGlobals = context.SetForCurrentThread();
// Inno type with /silent and /log and /custom and /installlocation, switches specified in manifest and whitespace-only --custom argument used in cli
auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallerArgTest_Inno_WithSwitches.yaml"));
context.Args.AddArg(Execution::Args::Type::Silent);
context.Args.AddArg(Execution::Args::Type::CustomSwitches, "\t"sv);
context.Add<Data::Manifest>(manifest);
context.Add<Data::Installer>(manifest.Installers.at(0));
context << GetInstallerArgs;
std::string installerArgs = context.Get<Data::InstallerArgs>();
REQUIRE(installerArgs.find("/mysilent") != std::string::npos); // Use declaration in manifest
REQUIRE(installerArgs.find("\t") == std::string::npos); // Whitespace only Custom switches should not be appended
}

{
std::ostringstream installOutput;
TestContext context{ installOutput, std::cin };
Expand Down
8 changes: 8 additions & 0 deletions src/Microsoft.Management.Deployment/InstallOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ namespace winrt::Microsoft::Management::Deployment::implementation
{
m_replacementInstallerArguments = value;
}
hstring InstallOptions::AdditionalInstallerArguments()
{
return hstring(m_additionalInstallerArguments);
}
void InstallOptions::AdditionalInstallerArguments(hstring const& value)
{
m_additionalInstallerArguments = value;
}
hstring InstallOptions::CorrelationData()
{
return hstring(m_correlationData);
Expand Down
3 changes: 3 additions & 0 deletions src/Microsoft.Management.Deployment/InstallOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ namespace winrt::Microsoft::Management::Deployment::implementation
void AllowHashMismatch(bool value);
hstring ReplacementInstallerArguments();
void ReplacementInstallerArguments(hstring const& value);
hstring AdditionalInstallerArguments();
void AdditionalInstallerArguments(hstring const& value);
hstring CorrelationData();
void CorrelationData(hstring const& value);
hstring AdditionalPackageCatalogArguments();
Expand All @@ -44,6 +46,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation
std::wstring m_logOutputPath = L"";
bool m_allowHashMismatch = false;
std::wstring m_replacementInstallerArguments = L"";
std::wstring m_additionalInstallerArguments = L"";
std::wstring m_correlationData = L"";
std::wstring m_additionalPackageCatalogArguments = L"";
Windows::Foundation::Collections::IVector<Windows::System::ProcessorArchitecture> m_allowedArchitectures{
Expand Down
5 changes: 5 additions & 0 deletions src/Microsoft.Management.Deployment/PackageManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,11 @@ namespace winrt::Microsoft::Management::Deployment::implementation
context->Args.AddArg(Execution::Args::Type::Override, ::AppInstaller::Utility::ConvertToUTF8(options.ReplacementInstallerArguments()));
}

if (!options.AdditionalInstallerArguments().empty())
{
context->Args.AddArg(Execution::Args::Type::CustomSwitches, ::AppInstaller::Utility::ConvertToUTF8(options.AdditionalInstallerArguments()));
}

if (options.AllowedArchitectures().Size() != 0)
{
std::vector<AppInstaller::Utility::Architecture> allowedArchitectures;
Expand Down
9 changes: 8 additions & 1 deletion src/Microsoft.Management.Deployment/PackageManager.idl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Licensed under the MIT License.
namespace Microsoft.Management.Deployment
{
[contractversion(5)]
[contractversion(6)]
apicontract WindowsPackageManagerContract{};

/// State of the install
Expand Down Expand Up @@ -765,6 +765,13 @@ namespace Microsoft.Management.Deployment
/// Force the operation to continue upon non security related failures.
Boolean Force;
}

[contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 6)]
{
/// A string that will be passed to the installer
/// IMPLEMENTATION NOTE: maps to "--custom" in the winget cmd line
String AdditionalInstallerArguments;
}
}

[contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 4)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Microsoft.WinGet.Client.Commands.Common
{
using System.IO;
using System.Management.Automation;
using Microsoft.Management.Deployment;
using Microsoft.Management.Deployment;
using Microsoft.WinGet.Client.Helpers;
using Windows.Foundation;

Expand All @@ -32,6 +32,12 @@ public abstract class BaseInstallCommand : BasePackageCommand
[Parameter(ValueFromPipelineByPropertyName = true)]
public string Override { get; set; }

/// <summary>
/// Gets or sets the arguments to be passed on to the installer in addition to the defaults.
/// </summary>
[Parameter(ValueFromPipelineByPropertyName = true)]
public string Custom { get; set; }

/// <summary>
/// Gets or sets the installation location.
/// </summary>
Expand Down Expand Up @@ -91,6 +97,12 @@ protected virtual InstallOptions GetInstallOptions(PackageVersionId version)
options.ReplacementInstallerArguments = this.Override;
}

// Since these arguments are appended to the installer at runtime, it doesn't make sense to append them if they are whitespace
if (!string.IsNullOrWhiteSpace(this.Custom))
{
options.AdditionalInstallerArguments = this.Custom;
}

if (this.Location != null)
{
options.PreferredInstallLocation = this.Location;
Expand Down

0 comments on commit bf9914a

Please sign in to comment.