Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[projmgr] List layers: introduce active connect handling #1628

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions tools/projmgr/include/ProjMgrUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,16 @@

#include "RteConstants.h"

/**
/**
* @brief vector of ConnectItem pointers
*/
*/
typedef std::vector<const ConnectItem*> ConnectPtrVec;

/**
* @brief map of ConnectItem active flags
*/
typedef std::map<const ConnectItem*, bool> ActiveConnectMap;

/**
* @brief connections collection item containing
* filename reference
Expand Down
7 changes: 4 additions & 3 deletions tools/projmgr/include/ProjMgrWorker.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@
* overflowed connections,
* incompatible connections,
* missed provided combined connections,
* provided connections
* active connect map,
*/
struct ConnectionsValidationResult {
bool valid;
StrVec conflicts;
StrPairVec overflows;
StrPairVec incompatibles;
std::vector<ConnectionsCollection> missedCollections;
StrPairPtrVec provides;
ActiveConnectMap activeConnectMap;
};

/**
Expand Down Expand Up @@ -781,7 +781,8 @@ class ProjMgrWorker {
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);
void GetActiveConnectMap(const ConnectionsCollectionVec& collection, ActiveConnectMap& activeConnectMap);
void SetActiveConnect(const ConnectItem* activeConnect, const ConnectionsCollectionVec& collection, ActiveConnectMap& activeConnectMap);
ConnectionsCollectionMap ClassifyConnections(const ConnectionsCollectionVec& connections, BoolMap optionalTypeFlags);
ConnectionsValidationResult ValidateConnections(ConnectionsCollectionVec combination);
void GetAllCombinations(const ConnectionsCollectionMap& src, const ConnectionsCollectionMap::iterator& it,
Expand Down
90 changes: 69 additions & 21 deletions tools/projmgr/src/ProjMgrWorker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,7 @@ bool ProjMgrWorker::ProcessLayerCombinations(ContextItem& context, LayersDiscove
if (!CheckBoardDeviceInLayer(context, clayerItem)) {
continue;
}
ConnectionsCollection collection = {clayerItem.path, type};
ConnectionsCollection collection = { clayerItem.path, type };
for (const auto& connect : clayerItem.connections) {
collection.connections.push_back(&connect);
}
Expand All @@ -850,7 +850,11 @@ bool ProjMgrWorker::ProcessLayerCombinations(ContextItem& context, LayersDiscove
}

// validate connections combinations
for (const auto& combination : combinations) {
for (auto& combination : combinations) {

// validate connections
ConnectionsValidationResult result = ValidateConnections(combination);

// debug message
if (m_debug) {
debugMsg += "\ncheck combined connections:";
Expand All @@ -859,16 +863,25 @@ bool ProjMgrWorker::ProcessLayerCombinations(ContextItem& context, LayersDiscove
debugMsg += "\n " + item.filename + (type.empty() ? "" : " (layer type: " + type + ")");
for (const auto& connect : item.connections) {
debugMsg += "\n " + (connect->set.empty() ? "" : "set: " + connect->set + " ") + "(" +
connect->connect + (connect->info.empty() ? "" : " - " + connect->info) + ")";
connect->connect + (connect->info.empty() ? "" : " - " + connect->info) + ")" + (result.activeConnectMap[connect] ? "" : " ignored");
}
}
debugMsg += "\n";
}
// validate connections
ConnectionsValidationResult result = ValidateConnections(combination);

// update list of compatible layers
if (result.valid) {
// remove inactive connects
for (auto& item : combination) {
for (auto it = item.connections.begin(); it != item.connections.end();) {
if (result.activeConnectMap[*it]) {
it++;
} else {
it = item.connections.erase(it);
}
}
}

// update list of compatible layers
context.validConnections.push_back(combination);
for (const auto& [type, _] : discover.candidateClayers) {
for (const auto& collection : combination) {
Expand Down Expand Up @@ -985,7 +998,7 @@ void ProjMgrWorker::PrintConnectionsValidation(ConnectionsValidationResult resul
}

if (!result.missedCollections.empty()) {
msg += "provided combined connections not consumed:";
msg += "no provided connections from this layer are consumed:";
for (const auto& missedCollection : result.missedCollections) {
msg += "\n " + missedCollection.filename + (missedCollection.type.empty() ? "" : " (layer type: " + missedCollection.type + ")");
for (const auto& connect : missedCollection.connections) {
Expand Down Expand Up @@ -1077,20 +1090,42 @@ ConnectionsCollectionMap ProjMgrWorker::ClassifyConnections(const ConnectionsCol
return classifiedConnections;
}

void ProjMgrWorker::GetConsumesProvides(const ConnectionsCollectionVec& collection, ConnectionsList& connections) {
// collect consumed and provided connections
ConnectPtrVec visitedConnect;
void ProjMgrWorker::GetActiveConnectMap(const ConnectionsCollectionVec& collection, ActiveConnectMap& activeConnectMap) {
// collect default active connects
for (const auto& item : collection) {
for (const auto& connect : item.connections) {
if (find(visitedConnect.begin(), visitedConnect.end(), connect) != visitedConnect.end()) {
continue;
if (regex_match(item.filename, regex(".*\\.cproject\\.(yml|yaml)"))) {
// the 'connect' is always active in cproject.yml
for (const auto& connect : item.connections) {
activeConnectMap[connect] = true;
}
visitedConnect.push_back(connect);
for (const auto& consumed : connect->consumes) {
connections.consumes.push_back(&consumed);
} else {
// the 'connect' is always active if it has no 'provides'
for (const auto& connect : item.connections) {
activeConnectMap[connect] = connect->provides.empty();
}
for (const auto& provided : connect->provides) {
connections.provides.push_back(&provided);
}
}
// the 'connect' is only active if one or more key listed under 'provides' is listed under 'consumes' in other active 'connect'
for (auto& [activeConnect, activeFlag] : activeConnectMap) {
if (activeFlag) {
// set active connect recursively
SetActiveConnect(activeConnect, collection, activeConnectMap);
}
}
}

void ProjMgrWorker::SetActiveConnect(const ConnectItem* activeConnect, const ConnectionsCollectionVec& collection, ActiveConnectMap& activeConnectMap) {
// the 'connect' is only active if one or more key listed under 'provides' is listed under 'consumes' in other active 'connect'
for (const auto& consumed : activeConnect->consumes) {
for (const auto& item : collection) {
for (const auto& connect : item.connections) {
for (const auto& provided : connect->provides) {
if (provided.first == consumed.first && !activeConnectMap[connect]) {
activeConnectMap[connect] = true;
SetActiveConnect(connect, collection, activeConnectMap);
break;
}
}
}
}
}
Expand All @@ -1117,9 +1152,22 @@ bool ProjMgrWorker::ProvidedConnectionsMatch(ConnectionsCollection collection, C
}

ConnectionsValidationResult ProjMgrWorker::ValidateConnections(ConnectionsCollectionVec combination) {
// get connections
// get active connects
ActiveConnectMap activeConnectMap;
GetActiveConnectMap(combination, activeConnectMap);

// collect active consumed and provided connections
ConnectionsList connections;
GetConsumesProvides(combination, connections);
for (const auto& [connect, active] : activeConnectMap) {
if (active) {
for (const auto& consumed : connect->consumes) {
connections.consumes.push_back(&consumed);
}
for (const auto& provided : connect->provides) {
connections.provides.push_back(&provided);
}
}
}

// elaborate provided list
StrMap providedValues;
Expand Down Expand Up @@ -1180,7 +1228,7 @@ ConnectionsValidationResult ProjMgrWorker::ValidateConnections(ConnectionsCollec

// set results
bool result = !conflicts.empty() || !overflows.empty() || !incompatibles.empty() || !missedCollections.empty() ? false : true;
return { result, conflicts, overflows, incompatibles, missedCollections, connections.provides };
return { result, conflicts, overflows, incompatibles, missedCollections, activeConnectMap };
}

bool ProjMgrWorker::ProcessDevice(ContextItem& context) {
Expand Down
2 changes: 2 additions & 0 deletions tools/projmgr/test/data/TestLayers/genericlayers.cproject.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ project:

connections:
- connect: Project Connections
consumes:
- MultipleProvided
provides:
# compatible connections
- ExactMatch: 42 # both key and value exact match
Expand Down
13 changes: 13 additions & 0 deletions tools/projmgr/test/data/TestLayers/no-layer-provides.cproject.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/cproject.schema.json

project:
compiler: AC6

layers:
- type: Incompatible
optional: false

connections:
- connect: Project Connections
provides:
- MultipleProvided
13 changes: 13 additions & 0 deletions tools/projmgr/test/data/TestLayers/no-layer-provides.csolution.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/csolution.schema.json

solution:

target-types:
- type: RteTest_ARMCM3
device: RteTest_ARMCM3

projects:
- project: no-layer-provides.cproject.yml

packs:
- pack: ARM::RteTest_DFP
27 changes: 12 additions & 15 deletions tools/projmgr/test/src/ProjMgrUnitTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1964,11 +1964,6 @@ required connections not provided:\n\
ProvidedEmpty: 123\n\
sum of required values exceed provided:\n\
AddedValueHigherThanProvided: 100 > 99\n\
provided combined connections not consumed:\n\
.*/ARM/RteTest_DFP/0.2.0/Layers/incompatible.clayer.yml \\(layer type: Incompatible\\)\n\
MultipleProvided\n\
MultipleProvidedNonIdentical0\n\
MultipleProvidedNonIdentical1\n\
connections are invalid\n\
\n\
check combined connections:\n\
Expand All @@ -1988,11 +1983,6 @@ required connections not provided:\n\
ProvidedEmpty: 123\n\
sum of required values exceed provided:\n\
AddedValueHigherThanProvided: 100 > 99\n\
provided combined connections not consumed:\n\
.*/ARM/RteTest_DFP/0.2.0/Layers/incompatible.clayer.yml \\(layer type: Incompatible\\)\n\
MultipleProvided\n\
MultipleProvidedNonIdentical0\n\
MultipleProvidedNonIdentical1\n\
connections are invalid\n\
\n\
check combined connections:\n\
Expand All @@ -2012,11 +2002,6 @@ required connections not provided:\n\
ProvidedEmpty: 123\n\
sum of required values exceed provided:\n\
AddedValueHigherThanProvided: 100 > 99\n\
provided combined connections not consumed:\n\
.*/ARM/RteTest_DFP/0.2.0/Layers/incompatible.clayer.yml \\(layer type: Incompatible\\)\n\
MultipleProvided\n\
MultipleProvidedNonIdentical0\n\
MultipleProvidedNonIdentical1\n\
connections are invalid\n\
\n\
no valid combination of clayers was found\n\
Expand All @@ -2032,6 +2017,18 @@ no valid combination of clayers was found\n\
EXPECT_TRUE(regex_match(outStr, regex(expectedOutStr)));
}

TEST_F(ProjMgrUnitTests, ListLayersIncompatibleNoLayerProvides) {
StdStreamRedirect streamRedirect;
char* argv[5];
const string& csolution = testinput_folder + "/TestLayers/no-layer-provides.csolution.yml";
argv[1] = (char*)"list";
argv[2] = (char*)"layers";
argv[3] = (char*)csolution.c_str();
argv[4] = (char*)"-d";
EXPECT_EQ(1, RunProjMgr(5, argv, m_envp));
EXPECT_NE(string::npos, streamRedirect.GetErrorString().find("no provided connections from this layer are consumed"));
}

TEST_F(ProjMgrUnitTests, ListLayersOptionalLayerType) {
StdStreamRedirect streamRedirect;
char* argv[8];
Expand Down
67 changes: 65 additions & 2 deletions tools/projmgr/test/src/ProjMgrWorkerUnitTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1012,7 +1012,7 @@ TEST_F(ProjMgrWorkerUnitTests, ValidateConnections) {
};

ConnectItem validConnectItem = { RteUtils::EMPTY_STRING, RteUtils::EMPTY_STRING, RteUtils::EMPTY_STRING, providedList, consumedList };
ConnectionsCollection validCollection = { RteUtils::EMPTY_STRING, RteUtils::EMPTY_STRING, {&validConnectItem} };
ConnectionsCollection validCollection = { ".cproject.yml", RteUtils::EMPTY_STRING, {&validConnectItem} };
result = ValidateConnections({ validCollection });
EXPECT_TRUE(result.valid);

Expand All @@ -1036,14 +1036,77 @@ TEST_F(ProjMgrWorkerUnitTests, ValidateConnections) {
StrPairVec expectedOverflow = {{"Lemon", "170 > 160"}};
StrPairVec expectedIncompatibles = {{"Ananas", "98"}, {"Grape Fruit", "1"}};
ConnectItem invalidConnectItem = { RteUtils::EMPTY_STRING, RteUtils::EMPTY_STRING, RteUtils::EMPTY_STRING, providedList, consumedList };
ConnectionsCollection invalidCollection = { RteUtils::EMPTY_STRING, RteUtils::EMPTY_STRING, {&invalidConnectItem} };
ConnectionsCollection invalidCollection = { ".cproject.yml", RteUtils::EMPTY_STRING, {&invalidConnectItem} };
result = ValidateConnections({ invalidCollection });
EXPECT_FALSE(result.valid);
EXPECT_EQ(result.conflicts, expectedConflicts);
EXPECT_EQ(result.overflows, expectedOverflow);
EXPECT_EQ(result.incompatibles, expectedIncompatibles);
}

TEST_F(ProjMgrWorkerUnitTests, ValidateActiveConnects) {
StrPairVec consumedList1 = {{ "Ananas", "" }};
StrPairVec consumedList2 = {{ "Banana", "" }};
StrPairVec consumedList3 = {{ "Cherry", "" }};
StrPairVec providedList1 = consumedList1;
StrPairVec providedList2 = consumedList2;
StrPairVec providedList3 = consumedList3;
ConnectItem empty = { RteUtils::EMPTY_STRING, RteUtils::EMPTY_STRING, RteUtils::EMPTY_STRING, StrPairVec() , StrPairVec() };
ConnectItem consumed1 = { RteUtils::EMPTY_STRING, RteUtils::EMPTY_STRING, RteUtils::EMPTY_STRING, StrPairVec() , consumedList1 };
ConnectItem provided1 = { RteUtils::EMPTY_STRING, RteUtils::EMPTY_STRING, RteUtils::EMPTY_STRING, providedList1, StrPairVec() };
ConnectItem consumed2 = { RteUtils::EMPTY_STRING, RteUtils::EMPTY_STRING, RteUtils::EMPTY_STRING, StrPairVec() , consumedList2 };
ConnectItem provided2 = { RteUtils::EMPTY_STRING, RteUtils::EMPTY_STRING, RteUtils::EMPTY_STRING, providedList2, StrPairVec() };
ConnectItem consumed2_provided1 = { RteUtils::EMPTY_STRING, RteUtils::EMPTY_STRING, RteUtils::EMPTY_STRING, providedList1, consumedList2 };
ConnectItem consumed2_provided3 = { RteUtils::EMPTY_STRING, RteUtils::EMPTY_STRING, RteUtils::EMPTY_STRING, providedList3, consumedList2 };

// test with active cproject connect
ConnectionsCollectionVec collectionVec1 = {
{ "0.cproject.yml", RteUtils::EMPTY_STRING, { &consumed1 }},
{ "1.clayer.yml" , RteUtils::EMPTY_STRING, { &consumed2_provided1 }},
{ "2.clayer.yml" , RteUtils::EMPTY_STRING, { &provided2 }},
};
ConnectionsValidationResult result1 = ValidateConnections(collectionVec1);
EXPECT_TRUE(result1.valid);
EXPECT_TRUE(result1.activeConnectMap[&consumed1]);
EXPECT_TRUE(result1.activeConnectMap[&consumed2_provided1]);
EXPECT_TRUE(result1.activeConnectMap[&provided2]);

// test with active cproject connect and inactive layer connect
ConnectionsCollectionVec collectionVec2 = {
{ "0.cproject.yml", RteUtils::EMPTY_STRING, { &consumed1 }},
{ "1.clayer.yml" , RteUtils::EMPTY_STRING, { &consumed2_provided3, &provided1 }},
};
ConnectionsValidationResult result2 = ValidateConnections(collectionVec2);
EXPECT_TRUE(result2.valid);
EXPECT_TRUE(result2.activeConnectMap[&consumed1]);
EXPECT_TRUE(result2.activeConnectMap[&provided1]);

// test with empty cproject connect and active layer and matching connects
ConnectionsCollectionVec collectionVec3 = {
{ "0.cproject.yml", RteUtils::EMPTY_STRING, { &empty }},
{ "1.clayer.yml" , RteUtils::EMPTY_STRING, { &consumed1 }},
{ "2.clayer.yml" , RteUtils::EMPTY_STRING, { &consumed2_provided1 }},
{ "3.clayer.yml" , RteUtils::EMPTY_STRING, { &provided2 }},
};
ConnectionsValidationResult result3 = ValidateConnections(collectionVec3);
EXPECT_TRUE(result3.valid);
EXPECT_TRUE(result3.activeConnectMap[&empty]);
EXPECT_TRUE(result3.activeConnectMap[&consumed1]);
EXPECT_TRUE(result3.activeConnectMap[&consumed2_provided1]);
EXPECT_TRUE(result3.activeConnectMap[&provided2]);

// test with empty cproject connect and mismatching provided vs consumed
ConnectionsCollectionVec collectionVec4 = {
{ "0.cproject.yml", RteUtils::EMPTY_STRING, { &empty }},
{ "1.clayer.yml" , RteUtils::EMPTY_STRING, { &consumed1 }},
{ "2.clayer.yml" , RteUtils::EMPTY_STRING, { &provided2 }},
};
ConnectionsValidationResult result4 = ValidateConnections(collectionVec4);
EXPECT_FALSE(result4.valid);
EXPECT_EQ(consumedList1.front(), result4.incompatibles.front());
EXPECT_EQ(providedList2.front(), result4.missedCollections.front().connections.front()->provides.front());
}

TEST_F(ProjMgrWorkerUnitTests, CollectLayersFromPacks) {
// test CollectLayersFromPacks with an non-existent clayer file
InitializeModel();
Expand Down
Loading