winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 58402d4a0b4dd47fbbe61f8ffb6f29d799fd9f3e
parent 9a15df45d0b2deae578aee957bdab52ab69fe11e
Author: JohnMcPMS <johnmcp@microsoft.com>
Date:   Fri, 27 Aug 2021 15:18:58 -0700

Add version comparison and architecture control to COM API (#1395)

This change adds an API, `CompareToVersion`, to compare a version string against a `PackageVersionInfo`.  The result is a value that indicates how the `PackageVersionInfo` object's version relates to the given value.  Thus if you want to check if the installed version is at least some value:
```C++
if (packageVersionInfo.CompareToVersion(targetVersion) == CompareResult::Lesser)
{
// Time to upgrade
}
```

It also adds a property to `InstallOptions`; `AllowedArchitectures`.  This vector of `ProcessorArchitecture` values contains both the allowed installer architectures that may be chosen when installing, as well as the order in which to prioritize the choice.  The vector starts with the default values for the current system.  Duplicate instances are ignored after the first, so in addition to completely replacing the set, one can prepend values as preferred without needing to understand all of the fallback options.

This change also fixes the default scope for `InstallOptions` to be `Any` as it is for the command line.

Finally, a new well known source is added that will contain a controlled set of "framework" packages for future use.  The packages within this source are not yet stabilized, so taking a dependency on it is not supported yet.
Diffstat:
M.github/actions/spelling/allow.txt | 2++
Msrc/AppInstallerCLICore/COMContext.cpp | 4++--
Msrc/AppInstallerCLICore/COMContext.h | 4++--
Msrc/AppInstallerCLICore/ExecutionContextData.h | 7+++++++
Msrc/AppInstallerCLICore/Workflows/ManifestComparator.cpp | 110+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/AppInstallerCLICore/Workflows/ManifestComparator.h | 2+-
Msrc/AppInstallerCLICore/Workflows/UpdateFlow.cpp | 2+-
Msrc/AppInstallerCLICore/Workflows/WorkflowBase.cpp | 2+-
Msrc/AppInstallerCLITests/ManifestComparator.cpp | 141++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msrc/AppInstallerCommonCore/Architecture.cpp | 12++++++++----
Msrc/AppInstallerCommonCore/Public/AppInstallerArchitecture.h | 5+++++
Msrc/AppInstallerRepositoryCore/Microsoft/PredefinedInstalledSourceFactory.cpp | 4++--
Msrc/AppInstallerRepositoryCore/Public/AppInstallerRepositorySource.h | 1+
Msrc/AppInstallerRepositoryCore/RepositorySource.cpp | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Msrc/Microsoft.Management.Deployment/Converters.cpp | 41+++++++++++++++++++++++++++++++++++++++--
Msrc/Microsoft.Management.Deployment/Converters.h | 9+++++++--
Msrc/Microsoft.Management.Deployment/InstallOptions.cpp | 19+++++++++++++++++++
Msrc/Microsoft.Management.Deployment/InstallOptions.h | 7+++++--
Msrc/Microsoft.Management.Deployment/PackageManager.cpp | 38++++++++++++++++++++++++++++----------
Msrc/Microsoft.Management.Deployment/PackageManager.idl | 36++++++++++++++++++++++++++++++++++++
Msrc/Microsoft.Management.Deployment/PackageVersionInfo.cpp | 25+++++++++++++++++++++++++
Msrc/Microsoft.Management.Deployment/PackageVersionInfo.h | 1+
Msrc/WinGetServer/WinMain.cpp | 2+-
23 files changed, 481 insertions(+), 110 deletions(-)

diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt @@ -363,6 +363,8 @@ realloc REALTIME REBOOTPROMPT Redistributable +Refactor +refactoring REFCLSID regex regexp diff --git a/src/AppInstallerCLICore/COMContext.cpp b/src/AppInstallerCLICore/COMContext.cpp @@ -3,7 +3,7 @@ #include "pch.h" #include "COMContext.h" -namespace AppInstaller +namespace AppInstaller::CLI::Execution { static constexpr std::string_view s_comLogFileNamePrefix = "WPM"sv; @@ -19,7 +19,7 @@ namespace AppInstaller m_comProgressCallbacks.push_back(std::move(f)); } - void COMContext::FireCallbacks(::AppInstaller::ReportType reportType, uint64_t current, uint64_t maximum, ProgressType progressType, ::AppInstaller::CLI::Workflow::ExecutionStage executionPhase) + void COMContext::FireCallbacks(ReportType reportType, uint64_t current, uint64_t maximum, ProgressType progressType, ::AppInstaller::CLI::Workflow::ExecutionStage executionPhase) { // Lock around iterating through the list. Callbacks should not do long running tasks. std::lock_guard<std::mutex> lock{ m_callbackLock }; diff --git a/src/AppInstallerCLICore/COMContext.h b/src/AppInstallerCLICore/COMContext.h @@ -5,7 +5,7 @@ #include "ExecutionContext.h" #include "Workflows/WorkflowBase.h" -namespace AppInstaller +namespace AppInstaller::CLI::Execution { enum class ReportType: uint32_t { @@ -71,7 +71,7 @@ namespace AppInstaller static void SetLoggers(); private: - void FireCallbacks(::AppInstaller::ReportType reportType, uint64_t current, uint64_t maximum, ProgressType progressType, ::AppInstaller::CLI::Workflow::ExecutionStage executionPhase); + void FireCallbacks(ReportType reportType, uint64_t current, uint64_t maximum, ProgressType progressType, ::AppInstaller::CLI::Workflow::ExecutionStage executionPhase); std::vector<ProgressCallBackFunction> GetCallbacks(); CLI::Workflow::ExecutionStage m_executionStage = CLI::Workflow::ExecutionStage::Initial; diff --git a/src/AppInstallerCLICore/ExecutionContextData.h b/src/AppInstallerCLICore/ExecutionContextData.h @@ -48,6 +48,7 @@ namespace AppInstaller::CLI::Execution Sources, ARPSnapshot, Dependencies, + AllowedArchitectures, Max }; @@ -211,5 +212,11 @@ namespace AppInstaller::CLI::Execution { using value_t = Manifest::DependencyList; }; + + template <> + struct DataMapping<Data::AllowedArchitectures> + { + using value_t = std::vector<Utility::Architecture>; + }; } } diff --git a/src/AppInstallerCLICore/Workflows/ManifestComparator.cpp b/src/AppInstallerCLICore/Workflows/ManifestComparator.cpp @@ -2,6 +2,7 @@ // Licensed under the MIT License. #include "pch.h" #include "WorkflowBase.h" +#include "ExecutionContext.h" #include "ManifestComparator.h" #include <winget/UserSettings.h> @@ -42,22 +43,84 @@ namespace AppInstaller::CLI::Workflow { MachineArchitectureComparator() : details::ComparisonField("Machine Architecture") {} + MachineArchitectureComparator(std::vector<Utility::Architecture> allowedArchitectures) : + details::ComparisonField("Machine Architecture"), m_allowedArchitectures(std::move(allowedArchitectures)) + { + AICLI_LOG(CLI, Verbose, << "Architecture Comparator created with allowed architectures: " << GetAllowedArchitecturesString()); + } + + // TODO: At some point we can do better about matching the currently installed architecture + static std::unique_ptr<MachineArchitectureComparator> Create(const Execution::Context& context, const Repository::IPackageVersion::Metadata&) + { + if (context.Contains(Execution::Data::AllowedArchitectures)) + { + const std::vector<Utility::Architecture>& allowedArchitectures = context.Get<Execution::Data::AllowedArchitectures>(); + if (!allowedArchitectures.empty()) + { + // If the incoming data contains elements, we will use them to construct a final allowed list. + // The algorithm is to take elements until we find Unknown, which indicates that any architecture is + // acceptable at this point. The system supported set of architectures will then be placed at the end. + std::vector<Utility::Architecture> result; + bool addRemainingApplicableArchitectures = false; + + for (Utility::Architecture architecture : allowedArchitectures) + { + if (architecture == Utility::Architecture::Unknown) + { + addRemainingApplicableArchitectures = true; + break; + } + + // If the architecture is applicable and not already in our result set... + if (Utility::IsApplicableArchitecture(architecture) != Utility::InapplicableArchitecture && + Utility::IsApplicableArchitecture(architecture, result) == Utility::InapplicableArchitecture) + { + result.push_back(architecture); + } + } + + if (addRemainingApplicableArchitectures) + { + for (Utility::Architecture architecture : Utility::GetApplicableArchitectures()) + { + if (Utility::IsApplicableArchitecture(architecture, result) == Utility::InapplicableArchitecture) + { + result.push_back(architecture); + } + } + } + + return std::make_unique<MachineArchitectureComparator>(std::move(result)); + } + } + + return std::make_unique<MachineArchitectureComparator>(); + } + bool IsApplicable(const Manifest::ManifestInstaller& installer) override { - return Utility::IsApplicableArchitecture(installer.Arch) != Utility::InapplicableArchitecture; + return CheckAllowedArchitecture(installer.Arch) != Utility::InapplicableArchitecture; } std::string ExplainInapplicable(const Manifest::ManifestInstaller& installer) override { - std::string result = "Machine is not compatible with "; + std::string result; + if (Utility::IsApplicableArchitecture(installer.Arch) == Utility::InapplicableArchitecture) + { + result = "Machine is not compatible with "; + } + else + { + result = "Architecture was excluded by caller : "; + } result += Utility::ToString(installer.Arch); return result; } bool IsFirstBetter(const Manifest::ManifestInstaller& first, const Manifest::ManifestInstaller& second) override { - auto arch1 = Utility::IsApplicableArchitecture(first.Arch); - auto arch2 = Utility::IsApplicableArchitecture(second.Arch); + auto arch1 = CheckAllowedArchitecture(first.Arch); + auto arch2 = CheckAllowedArchitecture(second.Arch); if (arch1 > arch2) { @@ -66,6 +129,37 @@ namespace AppInstaller::CLI::Workflow return false; } + + private: + int CheckAllowedArchitecture(Utility::Architecture architecture) + { + if (m_allowedArchitectures.empty()) + { + return Utility::IsApplicableArchitecture(architecture); + } + else + { + return Utility::IsApplicableArchitecture(architecture, m_allowedArchitectures); + } + } + + std::string GetAllowedArchitecturesString() + { + std::stringstream result; + bool prependComma = false; + for (Utility::Architecture architecture : m_allowedArchitectures) + { + if (prependComma) + { + result << ", "; + } + result << Utility::ToString(architecture); + prependComma = true; + } + return result.str(); + } + + std::vector<Utility::Architecture> m_allowedArchitectures; }; struct InstalledTypeComparator : public details::ComparisonField @@ -385,7 +479,7 @@ namespace AppInstaller::CLI::Workflow }; } - ManifestComparator::ManifestComparator(const Execution::Args& args, const Repository::IPackageVersion::Metadata& installationMetadata) + ManifestComparator::ManifestComparator(const Execution::Context& context, const Repository::IPackageVersion::Metadata& installationMetadata) { AddFilter(std::make_unique<OSVersionFilter>()); AddFilter(InstalledScopeFilter::Create(installationMetadata)); @@ -401,11 +495,11 @@ namespace AppInstaller::CLI::Workflow } else { - AddComparator(LocaleComparator::Create(args)); + AddComparator(LocaleComparator::Create(context.Args)); } - AddComparator(ScopeComparator::Create(args)); - AddComparator(std::make_unique<MachineArchitectureComparator>()); + AddComparator(ScopeComparator::Create(context.Args)); + AddComparator(MachineArchitectureComparator::Create(context, installationMetadata)); } std::optional<Manifest::ManifestInstaller> ManifestComparator::GetPreferredInstaller(const Manifest::Manifest& manifest) diff --git a/src/AppInstallerCLICore/Workflows/ManifestComparator.h b/src/AppInstallerCLICore/Workflows/ManifestComparator.h @@ -50,7 +50,7 @@ namespace AppInstaller::CLI::Workflow // Class in charge of comparing manifest entries struct ManifestComparator { - ManifestComparator(const Execution::Args&, const Repository::IPackageVersion::Metadata& installationMetadata); + ManifestComparator(const Execution::Context& context, const Repository::IPackageVersion::Metadata& installationMetadata); // Gets the best installer from the manifest, if at least one is applicable. std::optional<Manifest::ManifestInstaller> GetPreferredInstaller(const Manifest::Manifest& manifest); diff --git a/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp b/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp @@ -25,7 +25,7 @@ namespace AppInstaller::CLI::Workflow auto package = context.Get<Execution::Data::Package>(); auto installedPackage = context.Get<Execution::Data::InstalledPackageVersion>(); Utility::Version installedVersion = Utility::Version(installedPackage->GetProperty(PackageVersionProperty::Version)); - ManifestComparator manifestComparator(context.Args, installedPackage->GetMetadata()); + ManifestComparator manifestComparator(context, installedPackage->GetMetadata()); bool updateFound = false; // The version keys should have already been sorted by version diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -685,7 +685,7 @@ namespace AppInstaller::CLI::Workflow installationMetadata = context.Get<Execution::Data::InstalledPackageVersion>()->GetMetadata(); } - ManifestComparator manifestComparator(context.Args, installationMetadata); + ManifestComparator manifestComparator(context, installationMetadata); context.Add<Execution::Data::Installer>(manifestComparator.GetPreferredInstaller(context.Get<Execution::Data::Manifest>())); } diff --git a/src/AppInstallerCLITests/ManifestComparator.cpp b/src/AppInstallerCLITests/ManifestComparator.cpp @@ -2,6 +2,8 @@ // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" +#include <ExecutionContext.h> +#include <COMContext.h> #include <Workflows/ManifestComparator.h> #include <winget/UserSettings.h> @@ -17,6 +19,11 @@ using namespace AppInstaller::Utility; using Manifest = ::AppInstaller::Manifest::Manifest; +struct ManifestComparatorTestContext : public NullStream, Context +{ + ManifestComparatorTestContext() : NullStream(), Context(*m_nullOut, *m_nullIn) {} +}; + const ManifestInstaller& AddInstaller(Manifest& manifest, Architecture architecture, InstallerTypeEnum installerType, ScopeEnum scope = ScopeEnum::Unknown, std::string minOSVersion = {}, std::string locale = {}) { ManifestInstaller toAdd; @@ -46,7 +53,7 @@ TEST_CASE("ManifestComparator_OSFilter_Low", "[manifest_comparator]") Manifest manifest; AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Exe, ScopeEnum::Unknown, "10.0.99999.0"); - ManifestComparator mc({}, {}); + ManifestComparator mc(ManifestComparatorTestContext{}, {}); auto result = mc.GetPreferredInstaller(manifest); REQUIRE(!result); @@ -57,7 +64,7 @@ TEST_CASE("ManifestComparator_OSFilter_High", "[manifest_comparator]") Manifest manifest; ManifestInstaller expected = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Exe, ScopeEnum::Unknown, "10.0.0.0"); - ManifestComparator mc({}, {}); + ManifestComparator mc(ManifestComparatorTestContext{}, {}); auto result = mc.GetPreferredInstaller(manifest); RequireInstaller(result, expected); @@ -70,7 +77,7 @@ TEST_CASE("ManifestComparator_InstalledScopeFilter_Uknown", "[manifest_comparato SECTION("Nothing Installed") { - ManifestComparator mc({}, {}); + ManifestComparator mc(ManifestComparatorTestContext{}, {}); auto result = mc.GetPreferredInstaller(manifest); // Only because it is first @@ -81,7 +88,7 @@ TEST_CASE("ManifestComparator_InstalledScopeFilter_Uknown", "[manifest_comparato IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledScope] = ScopeToString(ScopeEnum::User); - ManifestComparator mc({}, metadata); + ManifestComparator mc(ManifestComparatorTestContext{}, metadata); auto result = mc.GetPreferredInstaller(manifest); RequireInstaller(result, unknown); @@ -91,7 +98,7 @@ TEST_CASE("ManifestComparator_InstalledScopeFilter_Uknown", "[manifest_comparato IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledScope] = ScopeToString(ScopeEnum::Machine); - ManifestComparator mc({}, metadata); + ManifestComparator mc(ManifestComparatorTestContext{}, metadata); auto result = mc.GetPreferredInstaller(manifest); RequireInstaller(result, unknown); @@ -106,7 +113,7 @@ TEST_CASE("ManifestComparator_InstalledScopeFilter", "[manifest_comparator]") SECTION("Nothing Installed") { - ManifestComparator mc({}, {}); + ManifestComparator mc(ManifestComparatorTestContext{}, {}); auto result = mc.GetPreferredInstaller(manifest); // Only because it is first @@ -117,7 +124,7 @@ TEST_CASE("ManifestComparator_InstalledScopeFilter", "[manifest_comparator]") IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledScope] = ScopeToString(ScopeEnum::User); - ManifestComparator mc({}, metadata); + ManifestComparator mc(ManifestComparatorTestContext{}, metadata); auto result = mc.GetPreferredInstaller(manifest); RequireInstaller(result, user); @@ -127,7 +134,7 @@ TEST_CASE("ManifestComparator_InstalledScopeFilter", "[manifest_comparator]") IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledScope] = ScopeToString(ScopeEnum::Machine); - ManifestComparator mc({}, metadata); + ManifestComparator mc(ManifestComparatorTestContext{}, metadata); auto result = mc.GetPreferredInstaller(manifest); RequireInstaller(result, machine); @@ -142,7 +149,7 @@ TEST_CASE("ManifestComparator_InstalledTypeFilter", "[manifest_comparator]") SECTION("Nothing Installed") { - ManifestComparator mc({}, {}); + ManifestComparator mc(ManifestComparatorTestContext{}, {}); auto result = mc.GetPreferredInstaller(manifest); // Only because it is first @@ -153,7 +160,7 @@ TEST_CASE("ManifestComparator_InstalledTypeFilter", "[manifest_comparator]") IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledType] = InstallerTypeToString(InstallerTypeEnum::Msi); - ManifestComparator mc({}, metadata); + ManifestComparator mc(ManifestComparatorTestContext{}, metadata); auto result = mc.GetPreferredInstaller(manifest); RequireInstaller(result, msi); @@ -163,7 +170,7 @@ TEST_CASE("ManifestComparator_InstalledTypeFilter", "[manifest_comparator]") IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledType] = InstallerTypeToString(InstallerTypeEnum::Msix); - ManifestComparator mc({}, metadata); + ManifestComparator mc(ManifestComparatorTestContext{}, metadata); auto result = mc.GetPreferredInstaller(manifest); RequireInstaller(result, msix); @@ -178,7 +185,7 @@ TEST_CASE("ManifestComparator_InstalledTypeCompare", "[manifest_comparator]") SECTION("Nothing Installed") { - ManifestComparator mc({}, {}); + ManifestComparator mc(ManifestComparatorTestContext{}, {}); auto result = mc.GetPreferredInstaller(manifest); // Only because it is first @@ -189,7 +196,7 @@ TEST_CASE("ManifestComparator_InstalledTypeCompare", "[manifest_comparator]") IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledType] = InstallerTypeToString(InstallerTypeEnum::Exe); - ManifestComparator mc({}, metadata); + ManifestComparator mc(ManifestComparatorTestContext{}, metadata); auto result = mc.GetPreferredInstaller(manifest); RequireInstaller(result, exe); @@ -199,7 +206,7 @@ TEST_CASE("ManifestComparator_InstalledTypeCompare", "[manifest_comparator]") IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledType] = InstallerTypeToString(InstallerTypeEnum::Inno); - ManifestComparator mc({}, metadata); + ManifestComparator mc(ManifestComparatorTestContext{}, metadata); auto result = mc.GetPreferredInstaller(manifest); RequireInstaller(result, burn); @@ -214,7 +221,7 @@ TEST_CASE("ManifestComparator_ScopeFilter", "[manifest_comparator]") SECTION("Nothing Required") { - ManifestComparator mc({}, {}); + ManifestComparator mc(ManifestComparatorTestContext{}, {}); auto result = mc.GetPreferredInstaller(manifest); // Only because it is first @@ -222,20 +229,20 @@ TEST_CASE("ManifestComparator_ScopeFilter", "[manifest_comparator]") } SECTION("User Required") { - Args args; - args.AddArg(Args::Type::InstallScope, ScopeToString(ScopeEnum::User)); + ManifestComparatorTestContext context; + context.Args.AddArg(Args::Type::InstallScope, ScopeToString(ScopeEnum::User)); - ManifestComparator mc(args, {}); + ManifestComparator mc(context, {}); auto result = mc.GetPreferredInstaller(manifest); RequireInstaller(result, user); } SECTION("Machine Required") { - Args args; - args.AddArg(Args::Type::InstallScope, ScopeToString(ScopeEnum::Machine)); + ManifestComparatorTestContext context; + context.Args.AddArg(Args::Type::InstallScope, ScopeToString(ScopeEnum::Machine)); - ManifestComparator mc(args, {}); + ManifestComparator mc(context, {}); auto result = mc.GetPreferredInstaller(manifest); RequireInstaller(result, machine); @@ -250,7 +257,7 @@ TEST_CASE("ManifestComparator_ScopeCompare", "[manifest_comparator]") SECTION("No Preference") { - ManifestComparator mc({}, {}); + ManifestComparator mc(ManifestComparatorTestContext{}, {}); auto result = mc.GetPreferredInstaller(manifest); // The default preference is user @@ -261,7 +268,7 @@ TEST_CASE("ManifestComparator_ScopeCompare", "[manifest_comparator]") TestUserSettings settings; settings.Set<Setting::InstallScopePreference>(ScopePreference::User); - ManifestComparator mc({}, {}); + ManifestComparator mc(ManifestComparatorTestContext{}, {}); auto result = mc.GetPreferredInstaller(manifest); RequireInstaller(result, user); @@ -271,7 +278,7 @@ TEST_CASE("ManifestComparator_ScopeCompare", "[manifest_comparator]") TestUserSettings settings; settings.Set<Setting::InstallScopePreference>(ScopePreference::Machine); - ManifestComparator mc({}, {}); + ManifestComparator mc(ManifestComparatorTestContext{}, {}); auto result = mc.GetPreferredInstaller(manifest); RequireInstaller(result, machine); @@ -289,7 +296,7 @@ TEST_CASE("ManifestComparator_InstalledLocaleComparator_Uknown", "[manifest_comp TestUserSettings settings; settings.Set<Setting::InstallLocalePreference>({ "en-US" }); - ManifestComparator mc({}, {}); + ManifestComparator mc(ManifestComparatorTestContext{}, {}); auto result = mc.GetPreferredInstaller(manifest); // Only because it is first @@ -300,7 +307,7 @@ TEST_CASE("ManifestComparator_InstalledLocaleComparator_Uknown", "[manifest_comp IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledLocale] = "en-US"; - ManifestComparator mc({}, metadata); + ManifestComparator mc(ManifestComparatorTestContext{}, metadata); auto result = mc.GetPreferredInstaller(manifest); RequireInstaller(result, enGB); @@ -310,7 +317,7 @@ TEST_CASE("ManifestComparator_InstalledLocaleComparator_Uknown", "[manifest_comp IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledLocale] = "zh-CN"; - ManifestComparator mc({}, metadata); + ManifestComparator mc(ManifestComparatorTestContext{}, metadata); auto result = mc.GetPreferredInstaller(manifest); RequireInstaller(result, unknown); @@ -328,7 +335,7 @@ TEST_CASE("ManifestComparator_InstalledLocaleComparator", "[manifest_comparator] TestUserSettings settings; settings.Set<Setting::InstallLocalePreference>({ "en-US" }); - ManifestComparator mc({}, {}); + ManifestComparator mc(ManifestComparatorTestContext{}, {}); auto result = mc.GetPreferredInstaller(manifest); // Only because it is first @@ -339,7 +346,7 @@ TEST_CASE("ManifestComparator_InstalledLocaleComparator", "[manifest_comparator] IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledLocale] = "en-US"; - ManifestComparator mc({}, metadata); + ManifestComparator mc(ManifestComparatorTestContext{}, metadata); auto result = mc.GetPreferredInstaller(manifest); RequireInstaller(result, enGB); @@ -349,7 +356,7 @@ TEST_CASE("ManifestComparator_InstalledLocaleComparator", "[manifest_comparator] IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledLocale] = "zh-CN"; - ManifestComparator mc({}, metadata); + ManifestComparator mc(ManifestComparatorTestContext{}, metadata); auto result = mc.GetPreferredInstaller(manifest); REQUIRE(!result); @@ -365,20 +372,20 @@ TEST_CASE("ManifestComparator_LocaleComparator", "[manifest_comparator]") SECTION("en-GB Required") { - Args args; - args.AddArg(Args::Type::Locale, "en-GB"s); + ManifestComparatorTestContext context; + context.Args.AddArg(Args::Type::Locale, "en-GB"s); - ManifestComparator mc(args, {}); + ManifestComparator mc(context, {}); auto result = mc.GetPreferredInstaller(manifest); RequireInstaller(result, enGB); } SECTION("zh-CN Required") { - Args args; - args.AddArg(Args::Type::Locale, "zh-CN"s); + ManifestComparatorTestContext context; + context.Args.AddArg(Args::Type::Locale, "zh-CN"s); - ManifestComparator mc(args, {}); + ManifestComparator mc(context, {}); auto result = mc.GetPreferredInstaller(manifest); REQUIRE(!result); @@ -388,7 +395,7 @@ TEST_CASE("ManifestComparator_LocaleComparator", "[manifest_comparator]") TestUserSettings settings; settings.Set<Setting::InstallLocalePreference>({ "en-US" }); - ManifestComparator mc({}, {}); + ManifestComparator mc(ManifestComparatorTestContext{}, {}); auto result = mc.GetPreferredInstaller(manifest); RequireInstaller(result, enGB); @@ -398,9 +405,64 @@ TEST_CASE("ManifestComparator_LocaleComparator", "[manifest_comparator]") TestUserSettings settings; settings.Set<Setting::InstallLocalePreference>({ "zh-CN" }); - ManifestComparator mc({}, {}); + ManifestComparator mc(ManifestComparatorTestContext{}, {}); auto result = mc.GetPreferredInstaller(manifest); RequireInstaller(result, unknown); } -}- \ No newline at end of file +} + +TEST_CASE("ManifestComparator_AllowedArchitecture_x64_only", "[manifest_comparator]") +{ + Manifest manifest; + ManifestInstaller x86 = AddInstaller(manifest, Architecture::X86, InstallerTypeEnum::Msi, ScopeEnum::User, "", ""); + + ManifestComparatorTestContext context; + context.Add<Data::AllowedArchitectures>({ Architecture::X64 }); + + ManifestComparator mc(context, {}); + auto result = mc.GetPreferredInstaller(manifest); + + REQUIRE(!result); +} + +TEST_CASE("ManifestComparator_AllowedArchitecture", "[manifest_comparator]") +{ + Manifest manifest; + ManifestInstaller x86 = AddInstaller(manifest, Architecture::X86, InstallerTypeEnum::Msi, ScopeEnum::User, "", ""); + ManifestInstaller x64 = AddInstaller(manifest, Architecture::X64, InstallerTypeEnum::Msi, ScopeEnum::User, "", ""); + ManifestInstaller arm = AddInstaller(manifest, Architecture::Arm, InstallerTypeEnum::Msi, ScopeEnum::User, "", ""); + ManifestInstaller arm64 = AddInstaller(manifest, Architecture::Arm64, InstallerTypeEnum::Msi, ScopeEnum::User, "", ""); + + SECTION("x86 Preference") + { + ManifestComparatorTestContext context; + context.Add<Data::AllowedArchitectures>({ Architecture::X86, Architecture::X64 }); + + ManifestComparator mc(context, {}); + auto result = mc.GetPreferredInstaller(manifest); + + RequireInstaller(result, x86); + } + SECTION("Unknown") + { + ManifestComparatorTestContext context; + context.Add<Data::AllowedArchitectures>({ Architecture::Unknown }); + + ManifestComparator mc(context, {}); + auto result = mc.GetPreferredInstaller(manifest); + + REQUIRE(result); + REQUIRE(result->Arch == GetApplicableArchitectures()[0]); + } + SECTION("x86 and Unknown") + { + ManifestComparatorTestContext context; + context.Add<Data::AllowedArchitectures>({ Architecture::X86, Architecture::Unknown }); + + ManifestComparator mc(context, {}); + auto result = mc.GetPreferredInstaller(manifest); + + RequireInstaller(result, x86); + } +} diff --git a/src/AppInstallerCommonCore/Architecture.cpp b/src/AppInstallerCommonCore/Architecture.cpp @@ -137,12 +137,16 @@ namespace AppInstaller::Utility int IsApplicableArchitecture(Architecture arch) { - const std::vector<Architecture>& applicableArchs = GetApplicableArchitectures(); - auto it = std::find(applicableArchs.begin(), applicableArchs.end(), arch); + return IsApplicableArchitecture(arch, GetApplicableArchitectures()); + } + + int IsApplicableArchitecture(Architecture arch, const std::vector<Architecture>& allowedArchitectures) + { + auto it = std::find(allowedArchitectures.begin(), allowedArchitectures.end(), arch); - if (it != applicableArchs.end()) + if (it != allowedArchitectures.end()) { - return static_cast<int>(std::distance(it, applicableArchs.end())); + return static_cast<int>(std::distance(it, allowedArchitectures.end())); } else { diff --git a/src/AppInstallerCommonCore/Public/AppInstallerArchitecture.h b/src/AppInstallerCommonCore/Public/AppInstallerArchitecture.h @@ -34,4 +34,9 @@ namespace AppInstaller::Utility // Returns the priority in the applicable architecture list if the architecture is applicable. 0 has lowest priority. // Returns -1 if the architecture is not applicable. int IsApplicableArchitecture(Architecture arch); + + // Gets if an architecture is applicable to the given list + // Returns the priority in the applicable architecture list if the architecture is applicable. 0 has lowest priority. + // Returns -1 if the architecture is not applicable. + int IsApplicableArchitecture(Architecture arch, const std::vector<Architecture>& allowedArchitectures); } diff --git a/src/AppInstallerRepositoryCore/Microsoft/PredefinedInstalledSourceFactory.cpp b/src/AppInstallerRepositoryCore/Microsoft/PredefinedInstalledSourceFactory.cpp @@ -89,8 +89,8 @@ namespace AppInstaller::Repository::Microsoft manifest.Installers[0].PackageFamilyName = familyName; - // Use the family name as a unique key for the path - auto manifestId = index.AddManifest(manifest, std::filesystem::path{ packageId.FamilyName().c_str() }); + // Use the full name as a unique key for the path + auto manifestId = index.AddManifest(manifest, std::filesystem::path{ packageId.FullName().c_str() }); index.SetMetadataByManifestId(manifestId, PackageVersionMetadata::InstalledType, Manifest::InstallerTypeToString(Manifest::InstallerTypeEnum::Msix)); diff --git a/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySource.h b/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySource.h @@ -142,6 +142,7 @@ namespace AppInstaller::Repository { WinGet, MicrosoftStore, + DesktopFrameworks, }; SourceDetails GetPredefinedSourceDetails(PredefinedSource source); diff --git a/src/AppInstallerRepositoryCore/RepositorySource.cpp b/src/AppInstallerRepositoryCore/RepositorySource.cpp @@ -45,6 +45,11 @@ namespace AppInstaller::Repository constexpr std::string_view s_Source_MSStoreDefault_Arg = "https://storeedgefd.dsx.mp.microsoft.com/v9.0"sv; constexpr std::string_view s_Source_MSStoreDefault_Identifier = "StoreEdgeFD"sv; + constexpr std::string_view s_Source_DesktopFrameworks_Name = "microsoft.builtin.desktop.frameworks"sv; + constexpr std::string_view s_Source_DesktopFrameworks_Arg = "https://winget.azureedge.net/platform"sv; + constexpr std::string_view s_Source_DesktopFrameworks_Data = "Microsoft.Winget.Platform.Source_8wekyb3d8bbwe"sv; + constexpr std::string_view s_Source_DesktopFrameworks_Identifier = "Microsoft.Winget.Platform.Source_8wekyb3d8bbwe"sv; + namespace { // SourceDetails with additional data. @@ -58,6 +63,56 @@ namespace AppInstaller::Repository SourceDetailsInternal(const SourceDetails& details) : SourceDetails(details) {}; }; + SourceDetailsInternal GetWellKnownSourceDetailsInternal(WellKnownSource source) + { + switch (source) + { + case WellKnownSource::WinGet: + { + SourceDetailsInternal details; + details.Origin = SourceOrigin::Default; + details.Name = s_Source_WingetCommunityDefault_Name; + details.Type = Microsoft::PreIndexedPackageSourceFactory::Type(); + details.Arg = s_Source_WingetCommunityDefault_Arg; + details.Data = s_Source_WingetCommunityDefault_Data; + details.Identifier = s_Source_WingetCommunityDefault_Identifier; + details.TrustLevel = SourceTrustLevel::Trusted | SourceTrustLevel::StoreOrigin; + return details; + } + case WellKnownSource::MicrosoftStore: + { + SourceDetailsInternal details; + details.Origin = SourceOrigin::Default; + details.Name = s_Source_MSStoreDefault_Name; + details.Type = Rest::RestSourceFactory::Type(); + details.Arg = s_Source_MSStoreDefault_Arg; + details.Identifier = s_Source_MSStoreDefault_Identifier; + details.TrustLevel = SourceTrustLevel::Trusted; + details.Restricted = true; + return details; + } + case WellKnownSource::DesktopFrameworks: + { + SourceDetailsInternal details; + details.Origin = SourceOrigin::Default; + details.Name = s_Source_DesktopFrameworks_Name; + details.Type = Microsoft::PreIndexedPackageSourceFactory::Type(); + details.Arg = s_Source_DesktopFrameworks_Arg; + details.Data = s_Source_DesktopFrameworks_Data; + details.Identifier = s_Source_DesktopFrameworks_Identifier; + details.TrustLevel = SourceTrustLevel::Trusted | SourceTrustLevel::StoreOrigin; + // Cheat the system and call this a tombstone. This effectively hides it from everything outside + // of this file, while still allowing it to properly save metadata. There might be problems + // if someone chooses the exact same name as this, which is why its name is very long. + // TODO: When refactoring the source interface, handle this with Visibility or similar. + details.IsTombstone = true; + return details; + } + } + + THROW_HR(E_UNEXPECTED); + } + // Checks whether a default source is enabled with the current settings. // onlyExplicit determines whether we consider the not-configured state to be enabled or not. bool IsDefaultSourceEnabled(std::string_view sourceToLog, ExperimentalFeature::Feature feature, bool onlyExplicit, TogglePolicy::Policy policy) @@ -161,6 +216,11 @@ namespace AppInstaller::Repository return TogglePolicy::Policy::MSStoreSource; } + if (name == s_Source_MSStoreDefault_Name && IsMSStoreDefaultSourceEnabled(true)) + { + return TogglePolicy::Policy::MSStoreSource; + } + // Any other tombstone is allowed return TogglePolicy::Policy::None; } @@ -182,6 +242,12 @@ namespace AppInstaller::Repository return IsWingetMSStoreDefaultSourceEnabled(false) ? TogglePolicy::Policy::None : TogglePolicy::Policy::MSStoreSource; } + if (Utility::CaseInsensitiveEquals(arg, s_Source_MSStoreDefault_Arg) && + Utility::CaseInsensitiveEquals(type, Rest::RestSourceFactory::Type())) + { + return IsMSStoreDefaultSourceEnabled(false) ? TogglePolicy::Policy::None : TogglePolicy::Policy::MSStoreSource; + } + // Case 3: // If the source has the same name as a default source, it is shadowing with a different argument // (as it didn't match above). We only care if Group Policy requires the default source. @@ -193,7 +259,13 @@ namespace AppInstaller::Repository if (name == s_Source_WingetMSStoreDefault_Name && IsWingetMSStoreDefaultSourceEnabled(true)) { - AICLI_LOG(Repo, Warning, << "User source is not allowed as it shadows the default MS Store source. Name [" << name << "]. Arg [" << arg << "] Type [" << type << ']'); + AICLI_LOG(Repo, Warning, << "User source is not allowed as it shadows a default MS Store source. Name [" << name << "]. Arg [" << arg << "] Type [" << type << ']'); + return TogglePolicy::Policy::MSStoreSource; + } + + if (name == s_Source_MSStoreDefault_Name && IsMSStoreDefaultSourceEnabled(true)) + { + AICLI_LOG(Repo, Warning, << "User source is not allowed as it shadows a default MS Store source. Name [" << name << "]. Arg [" << arg << "] Type [" << type << ']'); return TogglePolicy::Policy::MSStoreSource; } @@ -410,7 +482,7 @@ namespace AppInstaller::Repository { if (IsWingetCommunityDefaultSourceEnabled()) { - result.emplace_back(GetWellKnownSourceDetails(WellKnownSource::WinGet)); + result.emplace_back(GetWellKnownSourceDetailsInternal(WellKnownSource::WinGet)); } if (IsWingetMSStoreDefaultSourceEnabled()) @@ -427,8 +499,12 @@ namespace AppInstaller::Repository if (IsMSStoreDefaultSourceEnabled()) { - result.emplace_back(GetWellKnownSourceDetails(WellKnownSource::MicrosoftStore)); + result.emplace_back(GetWellKnownSourceDetailsInternal(WellKnownSource::MicrosoftStore)); } + + // Since we are using the tombstone trick, this is added just to have the source in the internal + // list for tracking updates. Thus there is no need to check a policy. + result.emplace_back(GetWellKnownSourceDetailsInternal(WellKnownSource::DesktopFrameworks)); } break; case SourceOrigin::User: @@ -891,6 +967,11 @@ namespace AppInstaller::Repository auto source = sourceList.GetCurrentSource(name); THROW_HR_IF(APPINSTALLER_CLI_ERROR_SOURCE_NAME_ALREADY_EXISTS, source != nullptr); + // Check for a non-user tombstone; hidden source data that we don't want to collide. + // TODO: Refactor the source interface so that we don't do this + auto tombstoneSource = sourceList.GetSource(name); + THROW_HR_IF(APPINSTALLER_CLI_ERROR_SOURCE_NAME_ALREADY_EXISTS, tombstoneSource && tombstoneSource->Origin != SourceOrigin::User); + // Check sources allowed by group policy auto blockingPolicy = GetPolicyBlockingUserSource(name, type, arg, false); if (blockingPolicy != TogglePolicy::Policy::None) @@ -1102,35 +1183,7 @@ namespace AppInstaller::Repository SourceDetails GetWellKnownSourceDetails(WellKnownSource source) { - switch (source) - { - case WellKnownSource::WinGet: - { - SourceDetailsInternal details; - details.Origin = SourceOrigin::Default; - details.Name = s_Source_WingetCommunityDefault_Name; - details.Type = Microsoft::PreIndexedPackageSourceFactory::Type(); - details.Arg = s_Source_WingetCommunityDefault_Arg; - details.Data = s_Source_WingetCommunityDefault_Data; - details.Identifier = s_Source_WingetCommunityDefault_Identifier; - details.TrustLevel = SourceTrustLevel::Trusted | SourceTrustLevel::StoreOrigin; - return details; - } - case WellKnownSource::MicrosoftStore: - { - SourceDetailsInternal details; - details.Origin = SourceOrigin::Default; - details.Name = s_Source_MSStoreDefault_Name; - details.Type = Rest::RestSourceFactory::Type(); - details.Arg = s_Source_MSStoreDefault_Arg; - details.Identifier = s_Source_MSStoreDefault_Identifier; - details.TrustLevel = SourceTrustLevel::Trusted; - details.Restricted = true; - return details; - } - } - - THROW_HR(E_UNEXPECTED); + return GetWellKnownSourceDetailsInternal(source); } std::shared_ptr<ISource> CreateCompositeSource(const std::shared_ptr<ISource>& installedSource, const std::shared_ptr<ISource>& availableSource, CompositeSearchBehavior searchBehavior) diff --git a/src/Microsoft.Management.Deployment/Converters.cpp b/src/Microsoft.Management.Deployment/Converters.cpp @@ -265,4 +265,42 @@ namespace winrt::Microsoft::Management::Deployment::implementation } return resultStatus; } -}- \ No newline at end of file + + std::optional<::AppInstaller::Utility::Architecture> GetUtilityArchitecture(winrt::Windows::System::ProcessorArchitecture architecture) + { + switch (architecture) + { + case winrt::Windows::System::ProcessorArchitecture::X86: + return ::AppInstaller::Utility::Architecture::X86; + case winrt::Windows::System::ProcessorArchitecture::Arm: + return ::AppInstaller::Utility::Architecture::Arm; + case winrt::Windows::System::ProcessorArchitecture::X64: + return ::AppInstaller::Utility::Architecture::X64; + case winrt::Windows::System::ProcessorArchitecture::Neutral: + return ::AppInstaller::Utility::Architecture::Neutral; + case winrt::Windows::System::ProcessorArchitecture::Arm64: + return ::AppInstaller::Utility::Architecture::Arm64; + } + + return {}; + } + + std::optional<winrt::Windows::System::ProcessorArchitecture> GetWindowsSystemProcessorArchitecture(::AppInstaller::Utility::Architecture architecture) + { + switch (architecture) + { + case ::AppInstaller::Utility::Architecture::X86: + return winrt::Windows::System::ProcessorArchitecture::X86; + case ::AppInstaller::Utility::Architecture::Arm: + return winrt::Windows::System::ProcessorArchitecture::Arm; + case ::AppInstaller::Utility::Architecture::X64: + return winrt::Windows::System::ProcessorArchitecture::X64; + case ::AppInstaller::Utility::Architecture::Neutral: + return winrt::Windows::System::ProcessorArchitecture::Neutral; + case ::AppInstaller::Utility::Architecture::Arm64: + return winrt::Windows::System::ProcessorArchitecture::Arm64; + } + + return {}; + } +} diff --git a/src/Microsoft.Management.Deployment/Converters.h b/src/Microsoft.Management.Deployment/Converters.h @@ -2,6 +2,10 @@ // Licensed under the MIT License. #pragma once #include "PackageMatchFilter.g.h" +#include <AppInstallerArchitecture.h> +#include <AppInstallerRepositorySearch.h> +#include <AppInstallerRepositorySource.h> +#include <Workflows/WorkflowBase.h> namespace winrt::Microsoft::Management::Deployment::implementation { @@ -13,4 +17,6 @@ namespace winrt::Microsoft::Management::Deployment::implementation ::AppInstaller::Repository::PackageVersionMetadata GetRepositoryPackageVersionMetadata(winrt::Microsoft::Management::Deployment::PackageVersionMetadataField packageVersionMetadataField); winrt::Microsoft::Management::Deployment::InstallResultStatus GetInstallResultStatus(::AppInstaller::CLI::Workflow::ExecutionStage executionStage, winrt::hresult hresult); winrt::Microsoft::Management::Deployment::FindPackagesResultStatus FindPackagesResultStatus(winrt::hresult hresult); -}- \ No newline at end of file + std::optional<::AppInstaller::Utility::Architecture> GetUtilityArchitecture(winrt::Windows::System::ProcessorArchitecture architecture); + std::optional<winrt::Windows::System::ProcessorArchitecture> GetWindowsSystemProcessorArchitecture(::AppInstaller::Utility::Architecture architecture); +} diff --git a/src/Microsoft.Management.Deployment/InstallOptions.cpp b/src/Microsoft.Management.Deployment/InstallOptions.cpp @@ -9,9 +9,24 @@ #include "InstallOptions.h" #pragma warning( pop ) #include "InstallOptions.g.cpp" +#include "Converters.h" + +#include <AppInstallerArchitecture.h> namespace winrt::Microsoft::Management::Deployment::implementation { + InstallOptions::InstallOptions() + { + // Populate the allowed architectures with the default values for the machine + for (AppInstaller::Utility::Architecture architecture : AppInstaller::Utility::GetApplicableArchitectures()) + { + auto convertedArchitecture = GetWindowsSystemProcessorArchitecture(architecture); + if (convertedArchitecture) + { + m_allowedArchitectures.Append(convertedArchitecture.value()); + } + } + } winrt::Microsoft::Management::Deployment::PackageVersionId InstallOptions::PackageVersionId() { return m_packageVersionId; @@ -84,5 +99,9 @@ namespace winrt::Microsoft::Management::Deployment::implementation { m_additionalPackageCatalogArguments = value; } + winrt::Windows::Foundation::Collections::IVector<winrt::Windows::System::ProcessorArchitecture> InstallOptions::AllowedArchitectures() + { + return m_allowedArchitectures; + } CoCreatableCppWinRtClass(InstallOptions); } diff --git a/src/Microsoft.Management.Deployment/InstallOptions.h b/src/Microsoft.Management.Deployment/InstallOptions.h @@ -12,7 +12,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation #endif struct InstallOptions : InstallOptionsT<InstallOptions> { - InstallOptions() = default; + InstallOptions(); winrt::Microsoft::Management::Deployment::PackageVersionId PackageVersionId(); void PackageVersionId(winrt::Microsoft::Management::Deployment::PackageVersionId const& value); @@ -32,16 +32,19 @@ namespace winrt::Microsoft::Management::Deployment::implementation void CorrelationData(hstring const& value); hstring AdditionalPackageCatalogArguments(); void AdditionalPackageCatalogArguments(hstring const& value); + winrt::Windows::Foundation::Collections::IVector<winrt::Windows::System::ProcessorArchitecture> AllowedArchitectures(); private: winrt::Microsoft::Management::Deployment::PackageVersionId m_packageVersionId{ nullptr }; std::wstring m_preferredInstallLocation = L""; - winrt::Microsoft::Management::Deployment::PackageInstallScope m_packageInstallScope = winrt::Microsoft::Management::Deployment::PackageInstallScope::User; + winrt::Microsoft::Management::Deployment::PackageInstallScope m_packageInstallScope = winrt::Microsoft::Management::Deployment::PackageInstallScope::Any; winrt::Microsoft::Management::Deployment::PackageInstallMode m_packageInstallMode = winrt::Microsoft::Management::Deployment::PackageInstallMode::Default; std::wstring m_logOutputPath = L""; bool m_allowHashMismatch = false; std::wstring m_replacementInstallerArguments = L""; std::wstring m_correlationData = L""; std::wstring m_additionalPackageCatalogArguments = L""; + Windows::Foundation::Collections::IVector<Windows::System::ProcessorArchitecture> m_allowedArchitectures{ + winrt::single_threaded_vector<winrt::Windows::System::ProcessorArchitecture>() }; }; } namespace winrt::Microsoft::Management::Deployment::factory_implementation diff --git a/src/Microsoft.Management.Deployment/PackageManager.cpp b/src/Microsoft.Management.Deployment/PackageManager.cpp @@ -32,6 +32,7 @@ using namespace std::literals::chrono_literals; using namespace ::AppInstaller::CLI; +using namespace ::AppInstaller::CLI::Execution; namespace winrt::Microsoft::Management::Deployment::implementation { @@ -60,6 +61,9 @@ namespace winrt::Microsoft::Management::Deployment::implementation case winrt::Microsoft::Management::Deployment::PredefinedPackageCatalog::MicrosoftStore: sourceDetails = GetWellKnownSourceDetails(::AppInstaller::Repository::WellKnownSource::MicrosoftStore); break; + case winrt::Microsoft::Management::Deployment::PredefinedPackageCatalog::DesktopFrameworks: + sourceDetails = GetWellKnownSourceDetails(::AppInstaller::Repository::WellKnownSource::DesktopFrameworks); + break; default: throw hresult_invalid_argument(); } @@ -149,7 +153,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation } std::optional<winrt::Microsoft::Management::Deployment::InstallProgress> GetProgress( - ::AppInstaller::ReportType reportType, + ReportType reportType, uint64_t current, uint64_t maximum, ::AppInstaller::ProgressType progressType, @@ -170,7 +174,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation break; case ::Workflow::ExecutionStage::Download: progressState = PackageInstallProgressState::Downloading; - if (reportType == ::AppInstaller::ReportType::BeginProgress) + if (reportType == ReportType::BeginProgress) { reportProgress = true; } @@ -191,12 +195,12 @@ namespace winrt::Microsoft::Management::Deployment::implementation case ::Workflow::ExecutionStage::Execution: progressState = PackageInstallProgressState::Installing; downloadProgress = 1; - if (reportType == ::AppInstaller::ReportType::ExecutionPhaseUpdate) + if (reportType == ReportType::ExecutionPhaseUpdate) { // Install is starting. Send progress so callers know the AsyncOperation can't be cancelled. reportProgress = true; } - else if (reportType == ::AppInstaller::ReportType::EndProgress) + else if (reportType == ReportType::EndProgress) { // Install is "finished". May not have succeeded. reportProgress = true; @@ -213,7 +217,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation } break; case ::Workflow::ExecutionStage::PostExecution: - if (reportType == ::AppInstaller::ReportType::ExecutionPhaseUpdate) + if (reportType == ReportType::ExecutionPhaseUpdate) { // Send PostInstall progress when it switches to PostExecution phase. reportProgress = true; @@ -254,12 +258,12 @@ namespace winrt::Microsoft::Management::Deployment::implementation return packageVersionInfo; } - std::unique_ptr<::AppInstaller::COMContext> CreateContextFromInstallOptions( + std::unique_ptr<COMContext> CreateContextFromInstallOptions( winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::InstallOptions options, std::wstring callerProcessInfoString) { - std::unique_ptr<::AppInstaller::COMContext> context = std::make_unique<::AppInstaller::COMContext>(); + std::unique_ptr<COMContext> context = std::make_unique<COMContext>(); hstring correlationData = (options) ? options.CorrelationData() : L""; context->SetLoggerContext(correlationData, ::AppInstaller::Utility::ConvertToUTF8(callerProcessInfoString)); @@ -304,6 +308,20 @@ namespace winrt::Microsoft::Management::Deployment::implementation { context->Args.AddArg(Execution::Args::Type::Override, ::AppInstaller::Utility::ConvertToUTF8(options.ReplacementInstallerArguments())); } + + if (options.AllowedArchitectures().Size() != 0) + { + std::vector<AppInstaller::Utility::Architecture> allowedArchitectures; + for (auto architecture : options.AllowedArchitectures()) + { + auto convertedArchitecture = GetUtilityArchitecture(architecture); + if (convertedArchitecture) + { + allowedArchitectures.push_back(convertedArchitecture.value()); + } + } + context->Add<Data::AllowedArchitectures>(std::move(allowedArchitectures)); + } } // If the version of the package is specified use that, otherwise use the default. @@ -317,7 +335,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation std::shared_ptr<Execution::OrchestratorQueueItem> GetExistingQueueItemForPackage(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::PackageCatalogInfo catalogInfo) { std::shared_ptr<Execution::OrchestratorQueueItem> queueItem = nullptr; - std::unique_ptr<::AppInstaller::COMContext> context = std::make_unique<::AppInstaller::COMContext>(); + std::unique_ptr<COMContext> context = std::make_unique<COMContext>(); if (catalogInfo) { // If the caller has passed in the catalog they expect the package to have come from, then only look for an install from that catalog. @@ -390,7 +408,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation if (queueItem == nullptr) { Microsoft::Management::Deployment::PackageVersionInfo packageVersionInfo = GetPackageVersionInfo(package, options); - std::unique_ptr<::AppInstaller::COMContext> comContext = CreateContextFromInstallOptions(package, options, callerProcessInfoString); + std::unique_ptr<COMContext> comContext = CreateContextFromInstallOptions(package, options, callerProcessInfoString); queueItem = Execution::OrchestratorQueueItemFactory::CreateItemForInstall(std::wstring{ package.Id() }, std::wstring{ packageVersionInfo.PackageCatalog().Info().Id() }, std::move(comContext)); Execution::ContextOrchestrator::Instance().EnqueueAndRunItem(queueItem); @@ -407,7 +425,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation std::atomic<winrt::Microsoft::Management::Deployment::InstallProgress> installProgress; queueItem->GetContext().AddProgressCallbackFunction([&installProgress, &progressEvent]( - ::AppInstaller::ReportType reportType, + ReportType reportType, uint64_t current, uint64_t maximum, ::AppInstaller::ProgressType progressType, diff --git a/src/Microsoft.Management.Deployment/PackageManager.idl b/src/Microsoft.Management.Deployment/PackageManager.idl @@ -144,6 +144,20 @@ namespace Microsoft.Management.Deployment PublisherDisplayName, }; + /// The result of a comparison. + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 2)] + enum CompareResult + { + /// The comparison did not result in a succesful ordering. + Unknown, + /// The object value is lesser than the given value. + Lesser, + /// The object value is equal to the given value. + Equal, + /// The object value is greater than the given value. + Greater, + }; + /// IMPLEMENTATION NOTE: IPackageVersion from AppInstallerRepositorySearch.h /// A single package version. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] @@ -170,6 +184,17 @@ namespace Microsoft.Management.Deployment /// Gets the package catalog where this package version is from. PackageCatalog PackageCatalog { get; }; + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 2)] + { + /// Compares the given value against the package version of this object, with the result being + /// the enum value that represents where PackageVersionInfo::Version is ordered relative to the + /// versionString. "if (this.CompareToVersion(that) == Greater)" can be thought of as reading + /// the sentence "If this is compared to version that and is found to be greater". + /// IE if PackageVersionInfo::Version returns "2", then CompareToVersion("1") will return Greater. + /// Passing in an empty string will result in Unknown. + CompareResult CompareToVersion(String versionString); + } + /// DESIGN NOTE: /// GetManifest from IPackageVersion in AppInstallerRepositorySearch is not implemented in V1. That class has /// a lot of fields and no one requesting it. @@ -402,6 +427,7 @@ namespace Microsoft.Management.Deployment { OpenWindowsCatalog, MicrosoftStore, + DesktopFrameworks, }; /// Local Catalogs with PackageCatalogOrigin Predefined @@ -480,6 +506,16 @@ namespace Microsoft.Management.Deployment String CorrelationData; /// A string that will be passed to the source server if using a REST source String AdditionalPackageCatalogArguments; + + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 2)] + { + // The set of allowed Architectures, in preference order, that will be considered for + // the install operation. Initially the vector contains the default allowed architectures + // in the default preference order for the current system. It is allowed to have repeated + // values in the list, to make prepending a preference override easier. Instances of an + // architecture after the first will simply be ignored. + Windows.Foundation.Collections.IVector<Windows.System.ProcessorArchitecture> AllowedArchitectures { get; }; + } } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] diff --git a/src/Microsoft.Management.Deployment/PackageVersionInfo.cpp b/src/Microsoft.Management.Deployment/PackageVersionInfo.cpp @@ -9,6 +9,7 @@ #include "PackageCatalog.h" #include "CatalogPackage.h" #include "Workflows/WorkflowBase.h" +#include "AppInstallerVersions.h" #include "Converters.h" #include <wil\cppwinrt_wrl.h> @@ -87,4 +88,28 @@ namespace winrt::Microsoft::Management::Deployment::implementation } return m_packageCatalog; } + + winrt::Microsoft::Management::Deployment::CompareResult PackageVersionInfo::CompareToVersion(hstring versionString) + { + if (versionString.empty()) + { + return CompareResult::Unknown; + } + + AppInstaller::Utility::Version thisVersion{ m_packageVersion->GetProperty(::AppInstaller::Repository::PackageVersionProperty::Version).get() }; + AppInstaller::Utility::Version otherVersion{ AppInstaller::Utility::ConvertToUTF8(versionString) }; + + if (thisVersion < otherVersion) + { + return CompareResult::Lesser; + } + else if (otherVersion < thisVersion) + { + return CompareResult::Greater; + } + else + { + return CompareResult::Equal; + } + } } diff --git a/src/Microsoft.Management.Deployment/PackageVersionInfo.h b/src/Microsoft.Management.Deployment/PackageVersionInfo.h @@ -19,6 +19,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation winrt::Windows::Foundation::Collections::IVectorView<hstring> ProductCodes(); winrt::Microsoft::Management::Deployment::PackageCatalog PackageCatalog(); std::shared_ptr<::AppInstaller::Repository::IPackageVersion> GetRepositoryPackageVersion() { return m_packageVersion; } + winrt::Microsoft::Management::Deployment::CompareResult CompareToVersion(hstring versionString); private: winrt::Microsoft::Management::Deployment::PackageCatalog m_packageCatalog{ nullptr }; std::shared_ptr<::AppInstaller::Repository::IPackageVersion> m_packageVersion; diff --git a/src/WinGetServer/WinMain.cpp b/src/WinGetServer/WinMain.cpp @@ -56,7 +56,7 @@ int __stdcall wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ LPWSTR, _In_ int winrt::check_hresult(globalOptions->Set(COMGLB_RO_SETTINGS, COMGLB_FAST_RUNDOWN)); } - AppInstaller::COMContext::SetLoggers(); + AppInstaller::CLI::Execution::COMContext::SetLoggers(); _comServerExitEvent.create(); auto& module = ::Microsoft::WRL::Module<::Microsoft::WRL::ModuleType::OutOfProc>::Create(&_releaseNotifier);