diff --git a/libs/rtemodel/test/src/RteModelTest.cpp b/libs/rtemodel/test/src/RteModelTest.cpp
index 9b0f42166..71766a319 100644
--- a/libs/rtemodel/test/src/RteModelTest.cpp
+++ b/libs/rtemodel/test/src/RteModelTest.cpp
@@ -323,9 +323,9 @@ TEST_F(RteModelPrjTest, LoadCprj) {
EXPECT_EQ(boardName, "RteTest Test board");
// get layers
auto& allLayerDescriptors = rteKernel.GetGlobalModel()->GetLayerDescriptors();
- EXPECT_EQ(allLayerDescriptors.size(), 8);
+ EXPECT_EQ(allLayerDescriptors.size(), 9);
auto& filteredLayerDescriptors = activeTarget->GetFilteredModel()->GetLayerDescriptors();
- EXPECT_EQ(filteredLayerDescriptors.size(), 6);
+ EXPECT_EQ(filteredLayerDescriptors.size(), 7);
const string rteDir = RteUtils::ExtractFilePath(RteTestM3_cprj, true) + "RTE/";
const string CompConfig_0_Base_Version = rteDir + "RteTest/" + "ComponentLevelConfig_0.h.base@0.0.1";
@@ -804,9 +804,9 @@ TEST_F(RteModelPrjTest, LoadCprjM4) {
EXPECT_TRUE(boardName.empty());
// get layers
auto& allLayerDescriptors = rteKernel.GetGlobalModel()->GetLayerDescriptors();
- EXPECT_EQ(allLayerDescriptors.size(), 8);
+ EXPECT_EQ(allLayerDescriptors.size(), 9);
auto& filteredLayerDescriptors = activeTarget->GetFilteredModel()->GetLayerDescriptors();
- EXPECT_EQ(filteredLayerDescriptors.size(), 8);
+ EXPECT_EQ(filteredLayerDescriptors.size(), 9);
const string projDir = RteUtils::ExtractFilePath(RteTestM4_cprj, true);
const string rteDir = projDir + "RTE/";
@@ -872,9 +872,9 @@ TEST_F(RteModelPrjTest, LoadCprjM4_Board) {
EXPECT_EQ(boardName, "RteTest CM4 board");
// get layers
auto& allLayerDescriptors = rteKernel.GetGlobalModel()->GetLayerDescriptors();
- EXPECT_EQ(allLayerDescriptors.size(), 8);
+ EXPECT_EQ(allLayerDescriptors.size(), 9);
auto& filteredLayerDescriptors = activeTarget->GetFilteredModel()->GetLayerDescriptors();
- EXPECT_EQ(filteredLayerDescriptors.size(), 5);
+ EXPECT_EQ(filteredLayerDescriptors.size(), 6);
const string projDir = RteUtils::ExtractFilePath(RteTestM4_Board_cprj, true);
const string rteDir = projDir + "RTE_BOARD/";
diff --git a/test/packs/ARM/RteTest_DFP/0.2.0/ARM.RteTest_DFP.pdsc b/test/packs/ARM/RteTest_DFP/0.2.0/ARM.RteTest_DFP.pdsc
index a6c08d9de..cb6d9a993 100644
--- a/test/packs/ARM/RteTest_DFP/0.2.0/ARM.RteTest_DFP.pdsc
+++ b/test/packs/ARM/RteTest_DFP/0.2.0/ARM.RteTest_DFP.pdsc
@@ -518,6 +518,10 @@
"Test board layer with shield"
+
+ "Test board layer with board specific pack"
+
+
diff --git a/test/packs/ARM/RteTest_DFP/0.2.0/Layers/board-specific.clayer.yml b/test/packs/ARM/RteTest_DFP/0.2.0/Layers/board-specific.clayer.yml
new file mode 100644
index 000000000..d39fe19c5
--- /dev/null
+++ b/test/packs/ARM/RteTest_DFP/0.2.0/Layers/board-specific.clayer.yml
@@ -0,0 +1,22 @@
+# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/clayer.schema.json
+
+layer:
+
+ packs:
+ - pack: ARM::RteTestBoard@0.1.0
+
+ type: BoardSpecific
+
+ for-board: RteTest board listing
+
+ components:
+ - component: Board:Test:Rev1
+
+ connections:
+ - connect: Board1 Connections
+ consumes:
+ - ExactMatch: 42 # both key and value exact match
+ - EmptyConsumedValue # key exact match, consumed value is empty
+ - EmptyValues # key exact match, both values empty
+ - AddedValueLessThanProvided: +49 # added consumed values are less than provided
+ - AddedValueEqualToProvided: +499 # added consumed values are equal to provided
diff --git a/tools/projmgr/include/ProjMgrUtils.h b/tools/projmgr/include/ProjMgrUtils.h
index 0ad97913c..2d69f685f 100644
--- a/tools/projmgr/include/ProjMgrUtils.h
+++ b/tools/projmgr/include/ProjMgrUtils.h
@@ -102,6 +102,11 @@ typedef std::map StrVecMap;
*/
typedef std::map IntMap;
+/**
+ * @brief map of bool
+*/
+typedef std::map BoolMap;
+
/**
* @brief map of string
*/
@@ -201,6 +206,14 @@ class ProjMgrUtils {
static void PushBackUniquely(std::list& lst, const std::string& value);
static void PushBackUniquely(StrPairVec& vec, const StrPair& value);
+ /**
+ * @brief merge two string vector maps
+ * @param map1 first string vector map
+ * @param map2 second string vector map
+ * @return StrVecMap merged map
+ */
+ static StrVecMap MergeStrVecMap(const StrVecMap& map1, const StrVecMap& map2);
+
/**
* @brief convert string to int, return 0 if it's empty or not convertible
* @param string
diff --git a/tools/projmgr/include/ProjMgrWorker.h b/tools/projmgr/include/ProjMgrWorker.h
index a06a7ab45..4c5b05d16 100644
--- a/tools/projmgr/include/ProjMgrWorker.h
+++ b/tools/projmgr/include/ProjMgrWorker.h
@@ -29,6 +29,24 @@ struct ConnectionsValidationResult {
StrPairPtrVec provides;
};
+/**
+ * @brief layers discovering variables
+ * required layer types,
+ * missed required types,
+ * optional type flags,
+ * generic clayers from search path,
+ * generic clayers from packs,
+ * candidate layers,
+*/
+struct LayersDiscovering {
+ StrVec requiredLayerTypes;
+ StrVec missedRequiredTypes;
+ BoolMap optionalTypeFlags;
+ StrVecMap genericClayersFromSearchPath;
+ StrVecMap genericClayersFromPacks;
+ StrVecMap candidateClayers;
+};
+
/**
* @brief connections lists
* list of consumes
@@ -646,10 +664,14 @@ class ProjMgrWorker {
void PrintConnectionsValidation(ConnectionsValidationResult result, std::string& msg);
bool CollectLayersFromPacks(ContextItem& context, StrVecMap& clayers);
bool CollectLayersFromSearchPath(const std::string& clayerSearchPath, StrVecMap& clayers);
- bool DiscoverMatchingLayers(ContextItem& context, const std::string& clayerSearchPath);
+ void GetRequiredLayerTypes(ContextItem& context, LayersDiscovering& discover);
+ bool GetCandidateLayers(LayersDiscovering& discover);
+ bool ProcessCandidateLayers(ContextItem& context, LayersDiscovering& discover);
+ bool ProcessLayerCombinations(ContextItem& context, LayersDiscovering& discover);
+ bool DiscoverMatchingLayers(ContextItem& context, std::string clayerSearchPath);
void CollectConnections(ContextItem& context, ConnectionsCollectionVec& connections);
void GetConsumesProvides(const ConnectionsCollectionVec& collection, ConnectionsList& connections);
- ConnectionsCollectionMap ClassifyConnections(const ConnectionsCollectionVec& connections, std::map optionalTypeFlags);
+ ConnectionsCollectionMap ClassifyConnections(const ConnectionsCollectionVec& connections, BoolMap optionalTypeFlags);
ConnectionsValidationResult ValidateConnections(ConnectionsCollectionVec combination);
void GetAllCombinations(const ConnectionsCollectionMap& src, const ConnectionsCollectionMap::iterator& it,
std::vector& combinations, const ConnectionsCollectionVec& previous = ConnectionsCollectionVec());
@@ -673,6 +695,7 @@ class ProjMgrWorker {
void ExpandAccessSequence(const ContextItem& context, const ContextItem& refContext, const std::string& sequence, std::string& item, bool withHeadingDot);
bool GetGeneratorDir(const RteGenerator* generator, ContextItem& context, const std::string& layer, std::string& genDir);
bool ParseContextLayers(ContextItem& context);
+ bool AddPackRequirements(ContextItem& context, const std::vector packRequirements);
};
#endif // PROJMGRWORKER_H
diff --git a/tools/projmgr/src/ProjMgrUtils.cpp b/tools/projmgr/src/ProjMgrUtils.cpp
index ea3d6d83f..478a1b1a7 100644
--- a/tools/projmgr/src/ProjMgrUtils.cpp
+++ b/tools/projmgr/src/ProjMgrUtils.cpp
@@ -105,6 +105,12 @@ void ProjMgrUtils::PushBackUniquely(StrPairVec& vec, const StrPair& value) {
vec.push_back(value);
}
+StrVecMap ProjMgrUtils::MergeStrVecMap(const StrVecMap& map1, const StrVecMap& map2) {
+ StrVecMap mergedMap(map1);
+ mergedMap.insert(map2.begin(), map2.end());
+ return mergedMap;
+}
+
int ProjMgrUtils::StringToInt(const string& value) {
int intValue = 0;
smatch sm;
diff --git a/tools/projmgr/src/ProjMgrWorker.cpp b/tools/projmgr/src/ProjMgrWorker.cpp
index 6929e6549..f91973c11 100644
--- a/tools/projmgr/src/ProjMgrWorker.cpp
+++ b/tools/projmgr/src/ProjMgrWorker.cpp
@@ -587,53 +587,118 @@ bool ProjMgrWorker::CollectLayersFromSearchPath(const string& clayerSearchPath,
return true;
}
-bool ProjMgrWorker::DiscoverMatchingLayers(ContextItem& context, const string& clayerSearchPath) {
- // required layer types
- StrVec requiredLayerTypes;
- map optionalTypeFlags;
+void ProjMgrWorker::GetRequiredLayerTypes(ContextItem& context, LayersDiscovering& discover) {
for (const auto& clayer : context.cproject->clayers) {
if (clayer.type.empty() || !CheckContextFilters(clayer.typeFilter, context) ||
(ExpandString(clayer.layer, context.variables) != clayer.layer)) {
continue;
}
- requiredLayerTypes.push_back(clayer.type);
- optionalTypeFlags[clayer.type] = clayer.optional;
+ discover.requiredLayerTypes.push_back(clayer.type);
+ discover.optionalTypeFlags[clayer.type] = clayer.optional;
}
+}
+
+bool ProjMgrWorker::ProcessCandidateLayers(ContextItem& context, LayersDiscovering& discover) {
+ // get all candidate layers
+ if (!GetCandidateLayers(discover)) {
+ return false;
+ }
+ // load device/board specific packs specified in candidate layers
+ vector packRequirements;
+ for (const auto& [type, clayers] : discover.candidateClayers) {
+ for (const auto& clayer : clayers) {
+ const ClayerItem& clayerItem = m_parser->GetGenericClayers()[clayer];
+ if (!clayerItem.forBoard.empty() || !clayerItem.forDevice.empty()) {
+ packRequirements.insert(packRequirements.end(), clayerItem.packs.begin(), clayerItem.packs.end());
+ }
+ }
+ }
+ if (packRequirements.size() > 0) {
+ AddPackRequirements(context, packRequirements);
+ if (!LoadAllRelevantPacks() || !LoadPacks(context)) {
+ return false;
+ }
+ }
+ // process board/device filtering
+ if (!ProcessDevice(context)) {
+ return false;
+ }
+ if (!SetTargetAttributes(context, context.targetAttributes)) {
+ return false;
+ }
+ // recollect layers from packs after filtering
+ discover.genericClayersFromPacks.clear();
+ if (!CollectLayersFromPacks(context, discover.genericClayersFromPacks)) {
+ return false;
+ }
+ discover.candidateClayers.clear();
+ if (!GetCandidateLayers(discover)) {
+ return false;
+ }
+ return true;
+}
+
+bool ProjMgrWorker::GetCandidateLayers(LayersDiscovering& discover) {
+ // clayers matching required types
+ StrVecMap genericClayers = ProjMgrUtils::MergeStrVecMap(discover.genericClayersFromSearchPath, discover.genericClayersFromPacks);
+ for (const auto& requiredType : discover.requiredLayerTypes) {
+ if (genericClayers.find(requiredType) != genericClayers.end()) {
+ for (const auto& clayer : genericClayers.at(requiredType)) {
+ discover.candidateClayers[requiredType].push_back(clayer);
+ }
+ } else {
+ ProjMgrUtils::PushBackUniquely(discover.missedRequiredTypes, requiredType);
+ }
+ }
+ // parse matched type layers
+ for (const auto& [type, clayers] : discover.candidateClayers) {
+ for (const auto& clayer : clayers) {
+ if (!m_parser->ParseGenericClayer(clayer, m_checkSchema)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool ProjMgrWorker::DiscoverMatchingLayers(ContextItem& context, string clayerSearchPath) {
+ // get all layers from packs and from search path
+ LayersDiscovering discover;
+ if (!CollectLayersFromPacks(context, discover.genericClayersFromPacks)) {
+ return false;
+ }
+ if (!CollectLayersFromSearchPath(clayerSearchPath, discover.genericClayersFromSearchPath)) {
+ return false;
+ }
+ // get required layer types
+ GetRequiredLayerTypes(context, discover);
+ // process candidate layers
+ if (!ProcessCandidateLayers(context, discover)) {
+ return false;
+ }
+ // process layer combinations
+ if (!ProcessLayerCombinations(context, discover)) {
+ return false;
+ }
+ return true;
+}
+bool ProjMgrWorker::ProcessLayerCombinations(ContextItem& context, LayersDiscovering& discover) {
// debug message
string debugMsg;
if (m_debug) {
debugMsg = "check for context '" + context.name + "'\n";
+
+ for (const auto& missedRequiredType : discover.missedRequiredTypes) {
+ debugMsg += "no clayer matches type '" + missedRequiredType + "'\n";
+ }
}
+ // collect connections from candidate layers
ConnectionsCollectionVec allConnections;
- StrVecMap matchedTypeClayers;
-
- if (!requiredLayerTypes.empty()) {
- // collect generic clayers from loaded packs and from search path
- StrVecMap genericClayers;
- if (!CollectLayersFromPacks(context, genericClayers) ||
- !CollectLayersFromSearchPath(clayerSearchPath, genericClayers)) {
- return false;
- }
- // clayers matching required types
- for (const auto& requiredType : requiredLayerTypes) {
- if (genericClayers.find(requiredType) != genericClayers.end()) {
- for (const auto& clayer : genericClayers.at(requiredType)) {
- matchedTypeClayers[requiredType].push_back(clayer);
- }
- } else {
- if (m_debug) {
- debugMsg += "no clayer matches type '" + requiredType + "'\n";
- }
- }
- }
- // parse matched type layers and collect connections
- for (const auto& [type, clayers] : matchedTypeClayers) {
+ if (!discover.requiredLayerTypes.empty()) {
+ for (const auto& [type, clayers] : discover.candidateClayers) {
for (const auto& clayer : clayers) {
- if (!m_parser->ParseGenericClayer(clayer, m_checkSchema)) {
- return false;
- }
const ClayerItem& clayerItem = m_parser->GetGenericClayers()[clayer];
if (type != clayerItem.type) {
if (m_debug) {
@@ -657,7 +722,7 @@ bool ProjMgrWorker::DiscoverMatchingLayers(ContextItem& context, const string& c
CollectConnections(context, allConnections);
// classify connections according to layer types and set config-ids
- ConnectionsCollectionMap classifiedConnections = ClassifyConnections(allConnections, optionalTypeFlags);
+ ConnectionsCollectionMap classifiedConnections = ClassifyConnections(allConnections, discover.optionalTypeFlags);
// cross classified connections to get all combinations to be validated
vector combinations;
@@ -686,7 +751,7 @@ bool ProjMgrWorker::DiscoverMatchingLayers(ContextItem& context, const string& c
// update list of compatible layers
if (result.valid) {
context.validConnections.push_back(combination);
- for (const auto& [type, _] : matchedTypeClayers) {
+ for (const auto& [type, _] : discover.candidateClayers) {
for (const auto& collection : combination) {
if (collection.type == type) {
ProjMgrUtils::PushBackUniquely(context.compatibleLayers[type], collection.filename);
@@ -703,9 +768,9 @@ bool ProjMgrWorker::DiscoverMatchingLayers(ContextItem& context, const string& c
}
// assess generic layers validation results
- if (!matchedTypeClayers.empty()) {
+ if (!discover.candidateClayers.empty()) {
if (!context.compatibleLayers.empty()) {
- for (const auto& [type, _] : matchedTypeClayers) {
+ for (const auto& [type, _] : discover.candidateClayers) {
if (context.compatibleLayers[type].size() == 1) {
// unique match
const auto& clayer = context.compatibleLayers[type].front();
@@ -735,7 +800,7 @@ bool ProjMgrWorker::DiscoverMatchingLayers(ContextItem& context, const string& c
ProjMgrLogger::Debug(debugMsg);
}
- if (!matchedTypeClayers.empty() && context.compatibleLayers.empty()) {
+ if (!discover.candidateClayers.empty() && context.compatibleLayers.empty()) {
return false;
}
@@ -1229,7 +1294,11 @@ bool ProjMgrWorker::ProcessPackages(ContextItem& context) {
for (const auto& [_, clayer] : context.clayers) {
packRequirements.insert(packRequirements.end(), clayer->packs.begin(), clayer->packs.end());
}
+ AddPackRequirements(context, packRequirements);
+ return true;
+}
+bool ProjMgrWorker::AddPackRequirements(ContextItem& context, const vector packRequirements) {
// Filter context specific package requirements
vector packages;
for (const auto& packItem : packRequirements) {
@@ -3049,16 +3118,10 @@ bool ProjMgrWorker::ListLayers(vector& layers, const string& clayerSearc
}
}
} else {
- // process board/device filtering
+ // process precedences
if (!ProcessPrecedences(context)) {
return false;
}
- if (!ProcessDevice(context)) {
- return false;
- }
- if (!SetTargetAttributes(context, context.targetAttributes)) {
- return false;
- }
// get matching layers for selected context
if (!DiscoverMatchingLayers(context, clayerSearchPath)) {
return false;
diff --git a/tools/projmgr/test/data/TestLayers/genericlayers.cproject.yml b/tools/projmgr/test/data/TestLayers/genericlayers.cproject.yml
index 4a1c3812c..ef2bb77bd 100644
--- a/tools/projmgr/test/data/TestLayers/genericlayers.cproject.yml
+++ b/tools/projmgr/test/data/TestLayers/genericlayers.cproject.yml
@@ -22,6 +22,9 @@ project:
- type: Board
optional: true
for-context: .OptionalLayerType
+ - type: BoardSpecific
+ for-context: +BoardSpecific
+
connections:
- connect: Project Connections
diff --git a/tools/projmgr/test/data/TestLayers/genericlayers.csolution.yml b/tools/projmgr/test/data/TestLayers/genericlayers.csolution.yml
index 350809c7d..43fe22b88 100644
--- a/tools/projmgr/test/data/TestLayers/genericlayers.csolution.yml
+++ b/tools/projmgr/test/data/TestLayers/genericlayers.csolution.yml
@@ -13,6 +13,8 @@ solution:
device: :cm0_core0
- type: AnyBoard
device: RteTest_ARMCM3
+ - type: BoardSpecific
+ board: RteTest board listing
projects:
- project: genericlayers.cproject.yml
diff --git a/tools/projmgr/test/src/ProjMgrUnitTests.cpp b/tools/projmgr/test/src/ProjMgrUnitTests.cpp
index d64821ba8..b59dafd66 100644
--- a/tools/projmgr/test/src/ProjMgrUnitTests.cpp
+++ b/tools/projmgr/test/src/ProjMgrUnitTests.cpp
@@ -673,6 +673,7 @@ TEST_F(ProjMgrUnitTests, ListLayersAll) {
EXPECT_EQ(0, RunProjMgr(3, argv, 0));
const string& expected = "\
+.*/ARM/RteTest_DFP/0.2.0/Layers/board-specific.clayer.yml \\(layer type: BoardSpecific\\)\n\
.*/ARM/RteTest_DFP/0.2.0/Layers/board1.clayer.yml \\(layer type: Board\\)\n\
.*/ARM/RteTest_DFP/0.2.0/Layers/board2.clayer.yml \\(layer type: Board\\)\n\
.*/ARM/RteTest_DFP/0.2.0/Layers/board3.clayer.yml \\(layer type: Board\\)\n\
@@ -1135,6 +1136,29 @@ connections are invalid\n\
EXPECT_TRUE(regex_search(errStr, regex(expected)));
}
+TEST_F(ProjMgrUnitTests, ListLayersWithBoardSpecificPack) {
+ StdStreamRedirect streamRedirect;
+ char* argv[8];
+ const string& csolution = testinput_folder + "/TestLayers/genericlayers.csolution.yml";
+ const string& context = "genericlayers.OptionalLayerType+BoardSpecific";
+ argv[1] = (char*)"list";
+ argv[2] = (char*)"layers";
+ argv[3] = (char*)"--solution";
+ argv[4] = (char*)csolution.c_str();
+ argv[5] = (char*)"-c";
+ argv[6] = (char*)context.c_str();
+ argv[7] = (char*)"-d";
+ EXPECT_EQ(0, RunProjMgr(8, argv, 0));
+
+ const string& expected = "\
+clayer of type 'BoardSpecific' was uniquely found:\n\
+ .*/ARM/RteTest_DFP/0.2.0/Layers/board-specific.clayer.yml\n\
+";
+
+ const string& errStr = streamRedirect.GetErrorString();
+ EXPECT_TRUE(regex_search(errStr, regex(expected)));
+}
+
TEST_F(ProjMgrUnitTests, ListLayersInvalidContext) {
char* argv[7];
const string& csolution = testinput_folder + "/TestLayers/genericlayers.csolution.yml";