1717#include < AppInstallerStrings.h>
1818#include < winget/ExperimentalFeature.h>
1919#include < winget/SelfManagement.h>
20+ #include < winget/PathTree.h>
2021#include < winrt/Microsoft.Management.Configuration.h>
2122
2223using namespace AppInstaller ::CLI::Execution;
@@ -1678,6 +1679,80 @@ namespace AppInstaller::CLI::Workflow
16781679 }
16791680 }
16801681
1682+ // Contains a tree of all unit processors by their path.
1683+ struct UnitProcessorTree
1684+ {
1685+ private:
1686+ struct SourceAndPackage
1687+ {
1688+ PackageCollection::Source Source;
1689+ PackageCollection::Package Package;
1690+ };
1691+
1692+ struct Node
1693+ {
1694+ // Packages whose installed location is at this node
1695+ std::vector<SourceAndPackage> Packages;
1696+
1697+ // Units whose location is at this node.
1698+ std::vector<IConfigurationUnitProcessorDetails> Units;
1699+ };
1700+
1701+ Filesystem::PathTree<Node> m_pathTree;
1702+
1703+ Node& FindNodeForFilePath (const winrt::hstring& filePath)
1704+ {
1705+ std::filesystem::path path{ std::wstring{ filePath } };
1706+ return m_pathTree.FindOrInsert (path.parent_path ());
1707+ }
1708+
1709+ public:
1710+ UnitProcessorTree (std::vector<IConfigurationUnitProcessorDetails>&& unitProcessors)
1711+ {
1712+ for (auto && unit : unitProcessors)
1713+ {
1714+ IConfigurationUnitProcessorDetails3 unitProcessor3;
1715+ if (unit.try_as (unitProcessor3))
1716+ {
1717+ winrt::hstring unitPath = unitProcessor3.Path ();
1718+ AICLI_LOG (Config, Verbose, << " Found unit `" << Utility::ConvertToUTF8 (unit.UnitType ()) << " ` at: " << Utility::ConvertToUTF8 (unitPath));
1719+ Node& node = FindNodeForFilePath (unitPath);
1720+ node.Units .emplace_back (std::move (unit));
1721+ }
1722+ }
1723+ }
1724+
1725+ void PlacePackage (const PackageCollection::Source& source, const PackageCollection::Package& package)
1726+ {
1727+ Node* node = m_pathTree.Find (package.InstalledLocation );
1728+ if (node)
1729+ {
1730+ node->Packages .emplace_back (SourceAndPackage{ source, package });
1731+ }
1732+ }
1733+
1734+ std::vector<IConfigurationUnitProcessorDetails> GetResourcesForPackage (const PackageCollection::Package& package) const
1735+ {
1736+ std::vector<IConfigurationUnitProcessorDetails> result;
1737+
1738+ m_pathTree.VisitIf (
1739+ package.InstalledLocation ,
1740+ [&](const Node& node)
1741+ {
1742+ for (const auto & unit : node.Units )
1743+ {
1744+ result.emplace_back (unit);
1745+ }
1746+ },
1747+ [](const Node& node)
1748+ {
1749+ return node.Packages .empty ();
1750+ });
1751+
1752+ return result;
1753+ }
1754+ };
1755+
16811756 void ProcessPackagesForConfigurationExportAll (Execution::Context& context)
16821757 {
16831758 ConfigurationContext& configContext = context.Get <Data::ConfigurationContext>();
@@ -1718,6 +1793,17 @@ namespace AppInstaller::CLI::Workflow
17181793 }
17191794 }
17201795
1796+ // Build a tree of the unit processors and place packages onto it to indicate nearest ownership.
1797+ UnitProcessorTree unitProcessorTree{ std::move (unitProcessors) };
1798+
1799+ for (const auto & source : context.Get <Execution::Data::PackageCollection>().Sources )
1800+ {
1801+ for (const auto & package : source.Packages )
1802+ {
1803+ unitProcessorTree.PlacePackage (source, package);
1804+ }
1805+ }
1806+
17211807 for (const auto & source : context.Get <Execution::Data::PackageCollection>().Sources )
17221808 {
17231809 // Create WinGetSource unit for non well known source.
@@ -1730,34 +1816,28 @@ namespace AppInstaller::CLI::Workflow
17301816
17311817 for (const auto & package : source.Packages )
17321818 {
1819+ AICLI_LOG (Config, Verbose, << " Exporting package `" << package.Id << " ` at: " << package.InstalledLocation );
1820+
17331821 auto packageUnit = anon::CreateWinGetPackageUnit (package, source, context.Args .Contains (Args::Type::IncludeVersions), sourceUnit, packageUnitType);
17341822 configContext.Set ().Units ().Append (packageUnit);
17351823
17361824 // Try package settings export.
1737- for (auto itr = unitProcessors.begin (); itr != unitProcessors.end (); /* itr incremented in the logic */ )
1825+ auto unitsForPackage = unitProcessorTree.GetResourcesForPackage (package);
1826+ for (const auto & unit : unitsForPackage)
17381827 {
1739- IConfigurationUnitProcessorDetails3 unitProcessor3;
1740- itr->try_as (unitProcessor3);
1741- if (Filesystem::IsParentPath (std::filesystem::path{ std::wstring{ unitProcessor3.Path () } }, package.InstalledLocation ))
1742- {
1743- ConfigurationUnit configUnit = anon::CreateConfigurationUnitFromUnitType (
1744- unitProcessor3.UnitType (),
1745- Utility::ConvertToUTF8 (packageUnit.Identifier ()));
1828+ winrt::hstring unitType = unit.UnitType ();
1829+ AICLI_LOG (Config, Verbose, << " exporting unit `" << Utility::ConvertToUTF8 (unitType));
17461830
1747- auto exportedUnits = anon::ExportUnit (context, configUnit);
1748- anon::AddDependentUnit (exportedUnits, packageUnit);
1831+ ConfigurationUnit configUnit = anon::CreateConfigurationUnitFromUnitType (
1832+ unitType,
1833+ Utility::ConvertToUTF8 (packageUnit.Identifier ()));
17491834
1750- for (auto exportedUnit : exportedUnits)
1751- {
1752- configContext.Set ().Units ().Append (exportedUnit);
1753- }
1835+ auto exportedUnits = anon::ExportUnit (context, configUnit);
1836+ anon::AddDependentUnit (exportedUnits, packageUnit);
17541837
1755- // Remove the unit processor from the list after export.
1756- itr = unitProcessors.erase (itr);
1757- }
1758- else
1838+ for (const auto & exportedUnit : exportedUnits)
17591839 {
1760- itr++ ;
1840+ configContext. Set (). Units (). Append (exportedUnit) ;
17611841 }
17621842 }
17631843 }
0 commit comments