winget-cli

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

PackageVersionSelection.cpp (12276B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "Public/winget/PackageVersionSelection.h"
      5 #include "Public/winget/RepositorySource.h"
      6 #include "Public/winget/PinningData.h"
      7 
      8 
      9 namespace AppInstaller::Repository
     10 {
     11     namespace
     12     {
     13         std::shared_ptr<IPackage> GetAvailablePackageFromSource(const std::vector<std::shared_ptr<IPackage>>& packages, const std::string_view sourceIdentifier)
     14         {
     15             for (const std::shared_ptr<IPackage>& package : packages)
     16             {
     17                 if (sourceIdentifier == package->GetSource().GetIdentifier())
     18                 {
     19                     return package;
     20                 }
     21             }
     22 
     23             return {};
     24         }
     25 
     26         struct AvailablePackageVersionCollection : public IPackageVersionCollection
     27         {
     28             AvailablePackageVersionCollection(const std::shared_ptr<ICompositePackage>& composite, const std::shared_ptr<IPackageVersion>& installedVersion) :
     29                 m_packages(composite->GetAvailable())
     30             {
     31                 if (!installedVersion)
     32                 {
     33                     return;
     34                 }
     35 
     36                 m_channel = installedVersion->GetProperty(PackageVersionProperty::Channel);
     37 
     38                 // Remove the packages that are not from the installed source.
     39                 Source installedVersionSource = installedVersion->GetSource();
     40                 if (installedVersionSource && installedVersionSource.ContainsAvailablePackages())
     41                 {
     42                     m_packages.erase(std::remove_if(m_packages.begin(), m_packages.end(), [&](const std::shared_ptr<IPackage>& p) { return installedVersionSource != p->GetSource(); }), m_packages.end());
     43                 }
     44             }
     45 
     46             std::vector<PackageVersionKey> GetVersionKeys() const override
     47             {
     48                 std::vector<PackageVersionKey> result;
     49 
     50                 for (const std::shared_ptr<IPackage>& package : m_packages)
     51                 {
     52                     std::vector<PackageVersionKey> versionKeys = package->GetVersionKeys();
     53                     std::copy(versionKeys.begin(), versionKeys.end(), std::back_inserter(result));
     54                 }
     55 
     56                 // Remove all elements whose channel does not match the installed package.
     57                 if (m_channel)
     58                 {
     59                     result.erase(
     60                         std::remove_if(result.begin(), result.end(), [&](const PackageVersionKey& pvk) { return !Utility::ICUCaseInsensitiveEquals(pvk.Channel, m_channel.value()); }),
     61                         result.end());
     62                 }
     63 
     64                 // Put latest versions at the front; for versions available from multiple sources maintain the order they were added in
     65                 std::stable_sort(result.begin(), result.end());
     66 
     67                 return result;
     68             }
     69 
     70             std::shared_ptr<IPackageVersion> GetVersion(const PackageVersionKey& versionKey) const override
     71             {
     72                 // If there is a specific source, just use that package's result
     73                 std::shared_ptr<IPackage> package;
     74 
     75                 if (!versionKey.SourceId.empty())
     76                 {
     77                     package = GetAvailablePackageFromSource(m_packages, versionKey.SourceId);
     78                 }
     79                 else if (m_packages.size() == 1)
     80                 {
     81                     package = m_packages[0];
     82                 }
     83                 else if (versionKey.IsDefaultLatest())
     84                 {
     85                     std::shared_ptr<IPackageVersion> result;
     86                     Utility::Version resultVersion;
     87 
     88                     for (const auto& p : m_packages)
     89                     {
     90                         std::shared_ptr<IPackageVersion> latest = p->GetLatestVersion();
     91                         Utility::Version version { latest->GetProperty(PackageVersionProperty::Version) };
     92 
     93                         if (!result || resultVersion < version)
     94                         {
     95                             result = std::move(latest);
     96                             resultVersion = std::move(version);
     97                         }
     98                     }
     99 
    100                     return result;
    101                 }
    102                 else
    103                 {
    104                     // Otherwise, find the first version that matches
    105                     std::vector<PackageVersionKey> versions = GetVersionKeys();
    106 
    107                     for (const PackageVersionKey& key : versions)
    108                     {
    109                         if (key.IsMatch(versionKey))
    110                         {
    111                             package = GetAvailablePackageFromSource(m_packages, key.SourceId);
    112                             break;
    113                         }
    114                     }
    115                 }
    116 
    117                 return package ? package->GetVersion(versionKey) : nullptr;
    118             }
    119 
    120             std::shared_ptr<IPackageVersion> GetLatestVersion() const override
    121             {
    122                 return GetVersion({ "", "", m_channel.value_or("") });
    123             }
    124 
    125         private:
    126             std::optional<std::string> m_channel;
    127             std::vector<std::shared_ptr<IPackage>> m_packages;
    128         };
    129     }
    130 
    131     std::shared_ptr<IPackageVersionCollection> GetAvailableVersionsForInstalledVersion(const std::shared_ptr<ICompositePackage>& composite)
    132     {
    133         return std::make_shared<AvailablePackageVersionCollection>(composite, GetInstalledVersion(composite));
    134     }
    135 
    136     std::shared_ptr<IPackageVersionCollection> GetAvailableVersionsForInstalledVersion(
    137         const std::shared_ptr<ICompositePackage>& composite,
    138         const std::shared_ptr<IPackageVersion>& installedVersion)
    139     {
    140         return std::make_shared<AvailablePackageVersionCollection>(composite, installedVersion);
    141     }
    142 
    143     std::shared_ptr<IPackageVersionCollection> GetAllAvailableVersions(const std::shared_ptr<ICompositePackage>& composite)
    144     {
    145         return GetAvailableVersionsForInstalledVersion(composite, nullptr);
    146     }
    147 
    148     std::shared_ptr<IPackageVersion> GetInstalledVersion(const std::shared_ptr<ICompositePackage>& composite)
    149     {
    150         auto installedPackage = composite->GetInstalled();
    151         return installedPackage ? installedPackage->GetLatestVersion() : nullptr;
    152     }
    153 
    154     std::shared_ptr<IPackage> GetAvailablePackageFromSource(const std::shared_ptr<ICompositePackage>& composite, const std::string_view sourceIdentifier)
    155     {
    156         return GetAvailablePackageFromSource(composite->GetAvailable(), sourceIdentifier);
    157     }
    158 
    159     LatestApplicableVersionData GetLatestApplicableVersion(const std::shared_ptr<ICompositePackage>& composite)
    160     {
    161         using namespace AppInstaller::Pinning;
    162 
    163         LatestApplicableVersionData result;
    164 
    165         auto installedVersion = AppInstaller::Repository::GetInstalledVersion(composite);
    166         auto availableVersions = AppInstaller::Repository::GetAvailableVersionsForInstalledVersion(composite, installedVersion);
    167 
    168         PinningData pinningData{ PinningData::Disposition::ReadOnly };
    169         auto evaluator = pinningData.CreatePinStateEvaluator(PinBehavior::ConsiderPins, installedVersion);
    170 
    171         AppInstaller::Manifest::ManifestComparator::Options options;
    172         if (installedVersion)
    173         {
    174             GetManifestComparatorOptionsFromMetadata(options, installedVersion->GetMetadata());
    175         }
    176         AppInstaller::Manifest::ManifestComparator manifestComparator{ options };
    177 
    178         auto availableVersionKeys = availableVersions->GetVersionKeys();
    179         for (const auto& availableVersionKey : availableVersionKeys)
    180         {
    181             auto availableVersion = availableVersions->GetVersion(availableVersionKey);
    182 
    183             if (installedVersion && !evaluator.IsUpdate(availableVersion))
    184             {
    185                 // Version too low or different channel for upgrade
    186                 continue;
    187             }
    188 
    189             if (evaluator.EvaluatePinType(availableVersion) != AppInstaller::Pinning::PinType::Unknown)
    190             {
    191                 // Pinned
    192                 continue;
    193             }
    194 
    195             auto manifestComparatorResult = manifestComparator.GetPreferredInstaller(availableVersion->GetManifest());
    196             if (!manifestComparatorResult.installer.has_value())
    197             {
    198                 // No applicable installer
    199                 continue;
    200             }
    201 
    202             result.LatestApplicableVersion = availableVersion;
    203             if (installedVersion)
    204             {
    205                 result.UpdateAvailable = true;
    206             }
    207 
    208             break;
    209         }
    210 
    211         return result;
    212     }
    213 
    214     void GetManifestComparatorOptionsFromMetadata(AppInstaller::Manifest::ManifestComparator::Options& options, const IPackageVersion::Metadata& metadata, bool includeAllowedArchitectures)
    215     {
    216         auto installedTypeItr = metadata.find(Repository::PackageVersionMetadata::InstalledType);
    217         if (installedTypeItr != metadata.end())
    218         {
    219             options.CurrentlyInstalledType = Manifest::ConvertToInstallerTypeEnum(installedTypeItr->second);
    220         }
    221 
    222         auto installedScopeItr = metadata.find(Repository::PackageVersionMetadata::InstalledScope);
    223         if (installedScopeItr != metadata.end())
    224         {
    225             options.CurrentlyInstalledScope = Manifest::ConvertToScopeEnum(installedScopeItr->second);
    226         }
    227 
    228         auto userIntentLocaleItr = metadata.find(Repository::PackageVersionMetadata::UserIntentLocale);
    229         if (userIntentLocaleItr != metadata.end())
    230         {
    231             options.PreviousUserIntentLocale = userIntentLocaleItr->second;
    232         }
    233 
    234         auto installedLocaleItr = metadata.find(Repository::PackageVersionMetadata::InstalledLocale);
    235         if (installedLocaleItr != metadata.end())
    236         {
    237             options.CurrentlyInstalledLocale = installedLocaleItr->second;
    238         }
    239 
    240         if (includeAllowedArchitectures)
    241         {
    242             auto userIntentItr = metadata.find(Repository::PackageVersionMetadata::UserIntentArchitecture);
    243             if (userIntentItr != metadata.end())
    244             {
    245                 // For upgrade, user intent from previous install is considered requirement
    246                 options.AllowedArchitectures.emplace_back(Utility::ConvertToArchitectureEnum(userIntentItr->second));
    247             }
    248             else
    249             {
    250                 auto installedItr = metadata.find(Repository::PackageVersionMetadata::InstalledArchitecture);
    251                 if (installedItr != metadata.end())
    252                 {
    253                     // For upgrade, previous installed architecture should be considered first preference and is always allowed.
    254                     // Then check settings requirements and preferences.
    255                     options.AllowedArchitectures.emplace_back(Utility::ConvertToArchitectureEnum(installedItr->second));
    256                 }
    257 
    258                 std::vector<Utility::Architecture> requiredArchitectures = Settings::User().Get<Settings::Setting::InstallArchitectureRequirement>();
    259                 std::vector<Utility::Architecture> optionalArchitectures = Settings::User().Get<Settings::Setting::InstallArchitecturePreference>();
    260 
    261                 if (!requiredArchitectures.empty())
    262                 {
    263                     // Required architecture list from settings if applicable
    264                     options.AllowedArchitectures.insert(options.AllowedArchitectures.end(), requiredArchitectures.begin(), requiredArchitectures.end());
    265                 }
    266                 else
    267                 {
    268                     // Preferred architecture list from settings if applicable, add Unknown to indicate allowing remaining applicable
    269                     if (!optionalArchitectures.empty())
    270                     {
    271                         options.AllowedArchitectures.insert(options.AllowedArchitectures.end(), optionalArchitectures.begin(), optionalArchitectures.end());
    272                     }
    273 
    274                     options.AllowedArchitectures.emplace_back(Utility::Architecture::Unknown);
    275                 }
    276             }
    277         }
    278     }
    279 }