Skip to content

Commit b622f1e

Browse files
authored
[projmgr] Improve pack version range resolution: re-use resolved packs
1 parent b20824e commit b622f1e

File tree

7 files changed

+144
-38
lines changed

7 files changed

+144
-38
lines changed

tools/projmgr/include/ProjMgrUtils.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,18 @@ typedef std::vector<const ConnectItem*> ConnectPtrVec;
2222
*/
2323
typedef std::map<const ConnectItem*, bool> ActiveConnectMap;
2424

25+
/**
26+
* @brief version type
27+
*/
28+
enum class VersionType
29+
{
30+
FIXED = 0,
31+
EQUIVALENT,
32+
COMPATIBLE,
33+
MINIMUM,
34+
ANY
35+
};
36+
2537
/**
2638
* @brief connections collection item containing
2739
* filename reference
@@ -212,6 +224,13 @@ class ProjMgrUtils {
212224
*/
213225
static bool IsMatchingPackInfo(const PackInfo& exactPackInfo, const PackInfo& packInfoToMatch);
214226

227+
/**
228+
* @brief get type of version string
229+
* @param version string
230+
* @return type according to VersionType enum
231+
*/
232+
static VersionType GetVersionType(const std::string& version);
233+
215234
/**
216235
* @brief convert version in YML format to CPRJ range format
217236
* @param version version in YML format

tools/projmgr/include/ProjMgrWorker.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1222,6 +1222,7 @@ class ProjMgrWorker {
12221222
bool IsEnvironmentCompatible(const std::string& environment, const StrVec& filter);
12231223
bool HasCompatibleEnvironment(const Collection<RteItem*>& environments, const StrVec& filter);
12241224
template<class T> bool CheckFilter(const std::string& filter, const T& item);
1225+
void ResolvePackRequirement(ContextItem& context, const PackItem& packEntry);
12251226
};
12261227

12271228
#endif // PROJMGRWORKER_H

tools/projmgr/src/ProjMgrUtils.cpp

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -300,26 +300,47 @@ SemVer ProjMgrUtils::GetSemVer(const std::string version) {
300300
return semVer;
301301
}
302302

303+
VersionType ProjMgrUtils::GetVersionType(const string& version) {
304+
if (version.find(HIGHER_OR_EQUAL_OPERATOR) != string::npos) {
305+
return VersionType::MINIMUM;
306+
} else if (version.find(TILDE_OPERATOR) != string::npos) {
307+
return VersionType::EQUIVALENT;
308+
} else if (version.find(CARET_OPERATOR) != string::npos) {
309+
return VersionType::COMPATIBLE;
310+
} else if (version.empty()) {
311+
return VersionType::ANY;
312+
} else {
313+
return VersionType::FIXED;
314+
}
315+
}
316+
303317
string ProjMgrUtils::ConvertToVersionRange(const string& version) {
304-
string versionRange = version;
305-
if (!versionRange.empty()) {
306-
if (versionRange.find(HIGHER_OR_EQUAL_OPERATOR) != string::npos) {
307-
// Minimum version
308-
versionRange = RteUtils::StripPrefix(versionRange, HIGHER_OR_EQUAL_OPERATOR);
309-
} else if (versionRange.find(TILDE_OPERATOR) != string::npos) {
310-
// Equivalent version
311-
versionRange = RteUtils::StripPrefix(versionRange, TILDE_OPERATOR);
312-
SemVer semVer = GetSemVer(versionRange);
313-
versionRange = versionRange + ":" + to_string(semVer.major) + "." + to_string(semVer.minor + 1) + ".0-0";
314-
} else if (versionRange.find(CARET_OPERATOR) != string::npos) {
315-
// Compatible version
316-
versionRange = RteUtils::StripPrefix(versionRange, CARET_OPERATOR);
317-
SemVer semVer = GetSemVer(versionRange);
318-
versionRange = versionRange + ":" + to_string(semVer.major + 1) + ".0.0-0";
319-
} else {
320-
// Fixed version
321-
versionRange = versionRange + ":" + versionRange;
322-
}
318+
string versionRange;
319+
SemVer semVer;
320+
switch (GetVersionType(version)) {
321+
case VersionType::MINIMUM:
322+
// Minimum version
323+
versionRange = RteUtils::StripPrefix(version, HIGHER_OR_EQUAL_OPERATOR);
324+
break;
325+
case VersionType::EQUIVALENT:
326+
// Equivalent version
327+
versionRange = RteUtils::StripPrefix(version, TILDE_OPERATOR);
328+
semVer = GetSemVer(versionRange);
329+
versionRange = versionRange + ":" + to_string(semVer.major) + "." + to_string(semVer.minor + 1) + ".0-0";
330+
break;
331+
case VersionType::COMPATIBLE:
332+
// Compatible version
333+
versionRange = RteUtils::StripPrefix(version, CARET_OPERATOR);
334+
semVer = GetSemVer(versionRange);
335+
versionRange = versionRange + ":" + to_string(semVer.major + 1) + ".0.0-0";
336+
break;
337+
case VersionType::FIXED:
338+
// Fixed version
339+
versionRange = version + ":" + version;
340+
break;
341+
case VersionType::ANY:
342+
// Any version
343+
versionRange = RteUtils::EMPTY_STRING;
323344
}
324345
return versionRange;
325346
}

tools/projmgr/src/ProjMgrWorker.cpp

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1675,6 +1675,7 @@ bool ProjMgrWorker::AddPackRequirements(ContextItem& context, const vector<PackI
16751675
}
16761676

16771677
// Process packages
1678+
map<VersionType, vector<PackItem>> packEntries;
16781679
for (const auto& packageEntry : packages) {
16791680
if (packageEntry.path.empty()) {
16801681
// Store specified pack metadata
@@ -1696,28 +1697,11 @@ bool ProjMgrWorker::AddPackRequirements(ContextItem& context, const vector<PackI
16961697
} else {
16971698
// Not matching cbuild pack, add it unless a wildcard entry
16981699
PackageItem package;
1699-
package.origin = packageEntry.origin;
17001700
ProjMgrUtils::ConvertToPackInfo(packageEntry.pack, package.pack);
17011701

1702-
// Resolve version range using installed/local packs
1702+
// Store pack entries in separated vectors for later resolution in the right order
17031703
if (!package.pack.name.empty() && !WildCards::IsWildcardPattern(package.pack.name)) {
1704-
XmlItem attributes({
1705-
{"name", package.pack.name},
1706-
{"vendor", package.pack.vendor},
1707-
{"version", ProjMgrUtils::ConvertToVersionRange(package.pack.version)},
1708-
});
1709-
auto pdsc = m_kernel->GetEffectivePdscFile(attributes);
1710-
// Only remember the version of the pack if we had it installed or local
1711-
// Will be used when serializing the cbuild-pack.yml file later
1712-
if (!pdsc.first.empty()) {
1713-
context.userInputToResolvedPackIdMap[packageEntry.pack].insert(pdsc.first);
1714-
string installedVersion = RtePackage::VersionFromId(pdsc.first);
1715-
package.pack.version = VersionCmp::RemoveVersionMeta(installedVersion);
1716-
} else {
1717-
// Remember that we had the user input, but it does not match any installed pack
1718-
context.userInputToResolvedPackIdMap[packageEntry.pack] = {};
1719-
}
1720-
context.packRequirements.push_back(package);
1704+
packEntries[ProjMgrUtils::GetVersionType(package.pack.version)].push_back(packageEntry);
17211705
}
17221706
}
17231707
} else {
@@ -1742,6 +1726,14 @@ bool ProjMgrWorker::AddPackRequirements(ContextItem& context, const vector<PackI
17421726
}
17431727
}
17441728

1729+
// Resolve pack entries in the order according to the version type: fixed, equivalent, compatible, minimum, any
1730+
for (const auto& versionType : { VersionType::FIXED, VersionType::EQUIVALENT, VersionType::COMPATIBLE,
1731+
VersionType::MINIMUM, VersionType::ANY }) {
1732+
for (const auto& packEntry : packEntries[versionType]) {
1733+
ResolvePackRequirement(context, packEntry);
1734+
}
1735+
}
1736+
17451737
// Add wildcard entries last so that they can be re-expanded if needed
17461738
for (const auto& packageEntry : packages) {
17471739
PackageItem package;
@@ -1766,6 +1758,44 @@ bool ProjMgrWorker::AddPackRequirements(ContextItem& context, const vector<PackI
17661758
return true;
17671759
}
17681760

1761+
void ProjMgrWorker::ResolvePackRequirement(ContextItem& context, const PackItem& packageEntry) {
1762+
// Resolve version range using installed/local packs
1763+
// Reuse already resolved pack when possible
1764+
PackageItem package;
1765+
ProjMgrUtils::ConvertToPackInfo(packageEntry.pack, package.pack);
1766+
string versionRange = ProjMgrUtils::ConvertToVersionRange(package.pack.version);
1767+
string bestMatch = RteUtils::EMPTY_STRING;
1768+
for (const auto& resolved : context.packRequirements) {
1769+
if (resolved.pack.vendor == package.pack.vendor &&
1770+
resolved.pack.name == package.pack.name &&
1771+
VersionCmp::RangeCompare(resolved.pack.version, versionRange) == 0 &&
1772+
VersionCmp::Compare(resolved.pack.version, bestMatch) > 0) {
1773+
bestMatch = resolved.pack.version;
1774+
}
1775+
}
1776+
if (!bestMatch.empty()) {
1777+
versionRange = ProjMgrUtils::ConvertToVersionRange(bestMatch);
1778+
}
1779+
XmlItem attributes({
1780+
{"name", package.pack.name},
1781+
{"vendor", package.pack.vendor},
1782+
{"version", versionRange}
1783+
});
1784+
auto pdsc = m_kernel->GetEffectivePdscFile(attributes);
1785+
// Only remember the version of the pack if we had it installed or local
1786+
// Will be used when serializing the cbuild-pack.yml file later
1787+
if (!pdsc.first.empty()) {
1788+
context.userInputToResolvedPackIdMap[packageEntry.pack].insert(pdsc.first);
1789+
string installedVersion = RtePackage::VersionFromId(pdsc.first);
1790+
package.pack.version = VersionCmp::RemoveVersionMeta(installedVersion);
1791+
} else {
1792+
// Remember that we had the user input, but it does not match any installed pack
1793+
context.userInputToResolvedPackIdMap[packageEntry.pack] = {};
1794+
}
1795+
package.origin = packageEntry.origin;
1796+
context.packRequirements.push_back(package);
1797+
}
1798+
17691799
bool ProjMgrWorker::ProcessToolchain(ContextItem& context) {
17701800
if (context.compiler.empty()) {
17711801
// Use the default compiler if available
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/csolution.schema.json
2+
3+
solution:
4+
target-types:
5+
- type: CM0
6+
device: RteTest_ARMCM0
7+
packs:
8+
- pack: ARM::RteTest_DFP
9+
- pack: ARM::RteTest_DFP@>=0.0.9
10+
- pack: ARM::RteTest_DFP@^0.0.9
11+
- pack: ARM::RteTest_DFP@~0.1.0
12+
- pack: ARM::[email protected]
13+
projects:
14+
- project: ./project_with_dfp_components.cproject.yml
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
cbuild-pack:
2+
resolved-packs:
3+
- resolved-pack: ARM::[email protected]
4+
selected-by-pack:
5+
- ARM::RteTest_DFP
6+
7+
- ARM::RteTest_DFP@>=0.0.9
8+
- ARM::RteTest_DFP@^0.0.9
9+
- ARM::RteTest_DFP@~0.1.0

tools/projmgr/test/src/ProjMgrUnitTests.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -943,6 +943,18 @@ TEST_F(ProjMgrUnitTests, RunProjMgrSolution_LockPackVersion) {
943943
RteFsUtils::RemoveFile(cbuildPackBackup);
944944
}
945945

946+
TEST_F(ProjMgrUnitTests, RunProjMgrSolution_MultiplePackEntries) {
947+
char* argv[3];
948+
const string csolution = testinput_folder + "/TestSolution/PackLocking/multiple_pack_entries.csolution.yml";
949+
argv[1] = (char*)"convert";
950+
argv[2] = (char*)csolution.c_str();
951+
EXPECT_EQ(0, RunProjMgr(3, argv, m_envp));
952+
953+
// Check the generated cbuild-pack file
954+
ProjMgrTestEnv::CompareFile(testinput_folder + "/TestSolution/PackLocking/multiple_pack_entries.cbuild-pack.yml",
955+
testinput_folder + "/TestSolution/PackLocking/ref/multiple_pack_entries.cbuild-pack.yml");
956+
}
957+
946958
TEST_F(ProjMgrUnitTests, RunProjMgrSolution_LockPackKeepExistingForContextSelections) {
947959
char* argv[9];
948960
string buf1, buf2, buf3;

0 commit comments

Comments
 (0)